Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
addHeaderFooterAreas(headerElements, tableLM.getTable().getTableHeader(), painter,
false);
if (!ancestorTreatAsArtifact) {
headerIsBeingRepeated = true;
headerIsBeingRepeated = !tableLM.getFObj().getUserAgent().isStaticRegionsPerPageForAccessibility();
}
layoutContext.setTreatAsArtifact(ancestorTreatAsArtifact);
}
Expand All @@ -487,7 +487,8 @@ void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {

if (footerElements != null && !footerElements.isEmpty()) {
boolean ancestorTreatAsArtifact = layoutContext.treatAsArtifact();
layoutContext.setTreatAsArtifact(treatFooterAsArtifact);
layoutContext.setTreatAsArtifact(treatFooterAsArtifact
&& !tableLM.getFObj().getUserAgent().isStaticRegionsPerPageForAccessibility());
//Positions for footers are simply added at the end
addHeaderFooterAreas(footerElements, tableLM.getTable().getTableFooter(), painter, true);
if (lastPos instanceof TableHFPenaltyPosition && !tableLM.getFooterFootnotes().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,6 @@ public PDFStructElem build(StructureHierarchyMember parent, Attributes attribute

private PDFStructElem rootStructureElement;

private boolean staticContent;

void setPdfFactory(PDFFactory pdfFactory) {
this.pdfFactory = pdfFactory;
}
Expand Down Expand Up @@ -403,9 +401,6 @@ public void endPageSequence() {
}

public StructureTreeElement startNode(String name, Attributes attributes, StructureTreeElement parent) {
if ("static-content".equals(name)) {
staticContent = true;
}
if (!isPDFA1Safe(name)) {
return null;
}
Expand All @@ -416,7 +411,7 @@ public StructureTreeElement startNode(String name, Attributes attributes, Struct
parentElem = parent;
}
StructureTreeElement structElem;
if (staticContent && pdfFactory.getDocument().isStaticRegionsPerPageForAccessibility()) {
if (pdfFactory.getDocument().isStaticRegionsPerPageForAccessibility()) {
structElem = new Factory(name, parentElem, attributes);
} else {
structElem = createStructureElement(
Expand Down Expand Up @@ -464,10 +459,6 @@ public PDFStructElem createStructureElement(int pageNumber) {
}

public void endNode(String name) {
if ("static-content".equals(name)) {
staticContent = false;
}

if (isPDFA1Safe(name)) {
ancestors.removeFirst();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

Expand All @@ -35,12 +38,27 @@
import org.xml.sax.helpers.AttributesImpl;
import static org.junit.Assert.assertEquals;

import org.apache.fop.accessibility.StructureTreeElement;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.pdf.PDFLinearizationTestCase;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.StandardStructureTypes;
import org.apache.fop.pdf.StructureType;

import static org.apache.fop.pdf.StandardStructureTypes.Grouping.DIV;
import static org.apache.fop.pdf.StandardStructureTypes.Grouping.DOCUMENT;
import static org.apache.fop.pdf.StandardStructureTypes.Grouping.PART;
import static org.apache.fop.pdf.StandardStructureTypes.Grouping.SECT;
import static org.apache.fop.pdf.StandardStructureTypes.Paragraphlike.P;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TABLE;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TBODY;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TD;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TFOOT;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TH;
import static org.apache.fop.pdf.StandardStructureTypes.Table.THEAD;
import static org.apache.fop.pdf.StandardStructureTypes.Table.TR;

public class PDFStructureTreeTestCase {
@Test
Expand Down Expand Up @@ -77,111 +95,137 @@ public void testStaticRegionPerPage() throws Exception {
}

@Test
public void testTableHeaderDuplicatedIfStaticRegionsPerPageTrue() throws Exception {
List<PDFStructElem> elems = getPDFStructElems("<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" "
+ "font-family=\"arial\" font-size=\"16pt\" xml:lang=\"en\">\n"
+ " <fo:layout-master-set>\n"
+ " <fo:simple-page-master master-name=\"A5-Page\" page-width=\"148mm\" page-height=\"210mm\">\n"
+ " <fo:region-body background-color=\"#efefef\" margin=\"10mm\"/>\n"
+ " </fo:simple-page-master>\n"
+ " <fo:page-sequence-master master-name=\"A5\">\n"
+ " <fo:repeatable-page-master-alternatives>\n"
+ " <fo:conditional-page-master-reference master-reference=\"A5-Page\"/>\n"
+ " </fo:repeatable-page-master-alternatives>\n"
+ " </fo:page-sequence-master>\n"
+ " </fo:layout-master-set>\n"
+ " <fo:page-sequence master-reference=\"A5\">\n"
+ " <fo:flow flow-name=\"xsl-region-body\">\n"
+ " <fo:block>\n"
+ " <fo:block padding-bottom=\"160mm\">Table overflow</fo:block>\n"
+ " <fo:table table-layout=\"fixed\" width=\"100%\">\n"
+ " <fo:table-header>\n"
+ " <fo:table-row>\n"
+ " <fo:table-cell number-columns-spanned=\"2\">\n"
+ " <fo:block font-weight=\"bold\" font-style=\"italic\" text-align=\"left\" "
+ "text-align-last=\"center\"> Table Title </fo:block>\n"
+ " </fo:table-cell>\n"
+ " </fo:table-row>\n"
+ " </fo:table-header>\n"
+ " <fo:table-body>\n"
+ " <fo:table-row>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 1 Column A</fo:block>\n"
+ " </fo:table-cell>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 1 Column B</fo:block>\n"
+ " </fo:table-cell>\n"
+ " </fo:table-row>\n"
+ " <fo:table-row>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column A</fo:block>\n"
+ " </fo:table-cell>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column B</fo:block>\n"
+ " </fo:table-cell>\n"
+ " </fo:table-row>\n"
+ " <fo:table-row>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column A</fo:block>\n"
+ " </fo:table-cell>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column B</fo:block>\n"
+ " </fo:table-cell>\n"
+ " </fo:table-row>\n"
+ " <fo:table-row>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column A</fo:block>\n"
+ " </fo:table-cell>\n"
+ " <fo:table-cell>\n"
+ " <fo:block>Row 2 Column B</fo:block>\n"
+ " </fo:table-cell>\n"
+ " </fo:table-row>\n"
+ " </fo:table-body>\n"
+ " </fo:table>\n"
+ " </fo:block>\n"
+ " </fo:flow>\n"
+ " </fo:page-sequence>\n"
+ "</fo:root>");
public void testReadingOrder() throws Exception {
checkReadingOrder(Arrays.asList(DOCUMENT, PART, SECT, DIV, P, P, DIV, P, DIV, P, P, DIV, P),
"test/fo/reading_order.fo");
}

@Test
public void testReadingOrderWithTableInHeader() throws Exception {
checkReadingOrder(Arrays.asList(DOCUMENT, PART, SECT, DIV, P, TABLE, THEAD, TR, TH, P, TBODY, TR, TD, P, TFOOT,
TR, TD, P, P, DIV, P, DIV, P, TABLE, THEAD, TR, TH, P, TBODY, TR, TD, P, TFOOT,
TR, TD, P, P, DIV, P),
"test/fo/reading_order_table_in_header.fo");
}

@Test
public void testReadingOrderBlockSpannedOverPage() throws Exception {
checkReadingOrder(Arrays.asList(DOCUMENT, PART, SECT, DIV, P, P, DIV, P, DIV, P, P, DIV, P, DIV, P, P, DIV, P),
"test/fo/reading_order_block_spanned_over_page.fo");
}

@Test
public void testReadingOrderTableInBody() throws Exception {
checkReadingOrder(Arrays.asList(DOCUMENT, PART, SECT, DIV, P, P, TABLE, THEAD, TR, TH, P, TBODY, TR, TD, P, TD,
P, TFOOT, TR, TD, P, DIV, P, DIV, P, P, TABLE, THEAD, TR, TH, P, TBODY, TR, TD, P, TD, P, TFOOT,
TR, TD, P, DIV, P),
"test/fo/reading_order_table_in_body.fo");
}

private int countTableElements(List<PDFStructElem> elems, StructureType elementType) {
int count = 0;
for (PDFStructElem elem : elems) {
if (elem.getStructureType().equals(StandardStructureTypes.Table.THEAD)) {
if (elem.getStructureType().equals(elementType)) {
count++;
}
}

assertEquals("The static region per page conf must apply to static regions only", 1, count);
return count;
}

private List<PDFStructElem> getPDFStructElems(String fo) throws Exception {
FopFactory fopFactory = getFopFactory();
FOUserAgent userAgent = fopFactory.newFOUserAgent();
foToOutput(fo, fopFactory, userAgent);
private void checkReadingOrder(List<StructureType> orderedTypes, String filePath) throws Exception {
List<PDFStructElem> elems = getPDFStructElems(filePath, true);

PDFStructElem block = (PDFStructElem) userAgent
.getStructureTreeEventHandler().startNode("block", new AttributesImpl(), null);
int index = 0;
for (PDFStructElem elem : elems) {
assertEquals("Reading order must be preserved when static-region-per-page is true",
orderedTypes.get(index), elem.getStructureType());
index++;
}

return block.getDocument().getStructureTreeElements();
assertEquals("Must verify all the PDFStructElements", orderedTypes.size(), elems.size());
}

@Test
public void testTableDuplicatedIfStaticRegionsPerPageTrue() throws Exception {
checkTableBodyCount(true, "test/fo/reading_order_table_in_body.fo",
"A table element should only have one respective structure element", 2);
}

@Test
public void testTableBodyNotDuplicatedIfStaticRegionsPerPageFalse() throws Exception {
checkTableBodyCount(false, "test/fo/reading_order_table_in_body.fo",
"A table element should only have one respective structure element", 1);
}

@Test
public void testTableBodyDuplicatedIfInsideStaticContent() throws Exception {
checkTableBodyCount(true, "test/fo/reading_order_table_in_header.fo",
"If the conf is set to true, a table element must be duplicated like any other fo element", 2);
}

private void checkTableBodyCount(boolean staticRegionPerPage, String filePath, String assertionMessage,
int expectedCount) throws Exception {
List<PDFStructElem> elems = getPDFStructElems(filePath, staticRegionPerPage);

int count = countTableElements(elems, TABLE);
assertEquals(assertionMessage, expectedCount, count);

count = countTableElements(elems, StandardStructureTypes.Table.TBODY);
assertEquals(assertionMessage, expectedCount, count);

count = countTableElements(elems, THEAD);
assertEquals(assertionMessage, expectedCount, count);

count = countTableElements(elems, StandardStructureTypes.Table.TFOOT);
assertEquals(assertionMessage, expectedCount, count);
}

private List<PDFStructElem> getPDFStructElems(String foFileName, boolean staticRegionPerPage) throws Exception {
FopFactory fopFactory = getFopFactory(staticRegionPerPage, true);
FOUserAgent userAgent = fopFactory.newFOUserAgent();
foToOutput(new FileInputStream(foFileName), fopFactory, userAgent);

StructureTreeElement block = userAgent.getStructureTreeEventHandler()
.startNode("#PCDATA", new AttributesImpl(), null);

PDFStructElem blockElem;
if (block instanceof PDFStructElem) {
blockElem = (PDFStructElem) block;
} else {
blockElem = ((PDFStructureTreeBuilder.Factory) block).createStructureElement(1);
}

return blockElem.getDocument().getStructureTreeElements();
}

private ByteArrayOutputStream foToOutput(String fo) throws Exception {
FopFactory fopFactory = getFopFactory();
return foToOutput(fo, fopFactory, fopFactory.newFOUserAgent());
FopFactory fopFactory = getFopFactory(true, false);
return foToOutput(new ByteArrayInputStream(fo.getBytes()), fopFactory, fopFactory.newFOUserAgent());
}

private ByteArrayOutputStream foToOutput(String fo, FopFactory fopFactory, FOUserAgent userAgent) throws Exception {

private ByteArrayOutputStream foToOutput(InputStream inputStream, FopFactory fopFactory, FOUserAgent userAgent)
throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Fop fop = fopFactory.newFop("application/pdf", userAgent, bos);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Source src = new StreamSource(new ByteArrayInputStream(fo.getBytes()));

Source src = new StreamSource(inputStream);
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
return bos;
}

private FopFactory getFopFactory() throws Exception {
String fopxconf =
"<fop version=\"1.0\"><accessibility static-region-per-page=\"true\">true</accessibility></fop>";
private FopFactory getFopFactory(boolean staticRegionPerPage, boolean useObjectsStreams) throws Exception {
String fopxconf = "<fop version=\"1.0\">"
+ " <accessibility static-region-per-page=\"" + staticRegionPerPage + "\">true</accessibility>"
+ " <renderers>\n"
+ " <renderer mime=\"application/pdf\">\n"
+ " <use-object-streams>" + useObjectsStreams + "</use-object-streams>"
+ " </renderer>\n"
+ " </renderers>\n"
+ "</fop>";
return FopFactory.newInstance(new File(".").toURI(), new ByteArrayInputStream(fopxconf.getBytes()));
}
}
30 changes: 30 additions & 0 deletions fop/test/fo/reading_order.fo
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="Pages" page-height="297mm" page-width="210mm">
<fo:region-body region-name="Body"/>
<fo:region-before region-name="Header" extent="70mm"/>
<fo:region-after region-name="Footer" extent="27mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence format="1" id="th_default_sequence1" master-reference="Pages">
<fo:static-content flow-name="Header">
<fo:block-container height="27mm" width="68mm" top="5mm" left="14.5mm" absolute-position="fixed">
<fo:block margin-top="5mm">
rest header
</fo:block>
</fo:block-container>
</fo:static-content>
<fo:static-content flow-name="Footer">
<fo:block-container height="27mm" width="68mm" top="100mm" left="14.5mm" absolute-position="fixed">
<fo:block margin-top="5mm">
rest footer
</fo:block>
</fo:block-container>
</fo:static-content>
<fo:flow flow-name="Body">
<fo:block padding-bottom="27cm" page-break-after="always">test</fo:block>
<fo:block padding-bottom="27cm" page-break-after="always">test1</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
30 changes: 30 additions & 0 deletions fop/test/fo/reading_order_block_spanned_over_page.fo
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="Pages" page-height="297mm" page-width="210mm">
<fo:region-body region-name="Body"/>
<fo:region-before region-name="Header" extent="70mm"/>
<fo:region-after region-name="Footer" extent="27mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence format="1" id="th_default_sequence1" master-reference="Pages">
<fo:static-content flow-name="Header">
<fo:block-container height="27mm" width="68mm" top="5mm" left="14.5mm" absolute-position="fixed">
<fo:block margin-top="5mm">
rest header
</fo:block>
</fo:block-container>
</fo:static-content>
<fo:static-content flow-name="Footer">
<fo:block-container height="27mm" width="68mm" top="100mm" left="14.5mm" absolute-position="fixed">
<fo:block margin-top="5mm">
rest footer
</fo:block>
</fo:block-container>
</fo:static-content>
<fo:flow flow-name="Body">
<fo:block padding-bottom="27cm">test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test </fo:block>
<fo:block padding-bottom="27cm" page-break-after="always">test1</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
Loading
Loading