Skip to content

Commit fb908e1

Browse files
authored
FOP-3273: PDF accessibility tagging stops after first fo/page-sequence
1 parent 36c3a39 commit fb908e1

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

fop-core/src/main/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.fop.accessibility.StructureTreeEventHandler;
3030
import org.apache.fop.fo.DelegatingFOEventHandler;
3131
import org.apache.fop.fo.FOEventHandler;
32+
import org.apache.fop.fo.FONode;
3233
import org.apache.fop.fo.FOText;
3334
import org.apache.fop.fo.extensions.ExternalDocument;
3435
import org.apache.fop.fo.flow.AbstractRetrieveMarker;
@@ -786,6 +787,22 @@ public void run() {
786787
super.restoreState(retrieveMarker);
787788
}
788789

790+
@Override
791+
public void endRestoreState(RetrieveMarker retrieveMarker) {
792+
boolean isInsideArtifact = false;
793+
FONode obj = retrieveMarker.getParent();
794+
while (obj != null && !isInsideArtifact) {
795+
if (obj instanceof CommonAccessibilityHolder && isArtifact((CommonAccessibilityHolder)obj)) {
796+
isInsideArtifact = true;
797+
} else {
798+
obj = obj.getParent();
799+
}
800+
}
801+
if (isInsideArtifact && !converters.isEmpty()) {
802+
converter = converters.pop();
803+
}
804+
}
805+
789806
@SuppressWarnings("unchecked")
790807
private void restoreRetrieveMarkerState(AbstractRetrieveMarker retrieveMarker) {
791808
State state = states.get(retrieveMarker);

fop-core/src/main/java/org/apache/fop/fo/FOEventHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@ public void endRetrieveMarker(RetrieveMarker retrieveMarker) {
571571
public void restoreState(RetrieveMarker retrieveMarker) {
572572
}
573573

574+
public void endRestoreState(RetrieveMarker retrieveMarker) {
575+
}
576+
574577
/**
575578
* Process the start of a retrieve-table-marker.
576579
*

fop-core/src/main/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ public void bindMarker(Marker marker) {
212212
try {
213213
restoreFOEventHandlerState();
214214
cloneFromMarker(marker);
215+
endRestoreFOEventHandlerState();
215216
} catch (FOPException exc) {
216217
getFOValidationEventProducer().markerCloningFailed(this,
217218
marker.getMarkerClassName(), exc, getLocator());
@@ -223,6 +224,8 @@ public void bindMarker(Marker marker) {
223224

224225
protected abstract void restoreFOEventHandlerState();
225226

227+
protected abstract void endRestoreFOEventHandlerState();
228+
226229
/**
227230
* Return the value for the <code>retrieve-class-name</code>
228231
* property

fop-core/src/main/java/org/apache/fop/fo/flow/RetrieveMarker.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,8 @@ protected void restoreFOEventHandlerState() {
126126
getFOEventHandler().restoreState(this);
127127
}
128128

129+
@Override
130+
protected void endRestoreFOEventHandlerState() {
131+
getFOEventHandler().endRestoreState(this);
132+
}
129133
}

fop-core/src/main/java/org/apache/fop/fo/flow/RetrieveTableMarker.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,8 @@ protected void restoreFOEventHandlerState() {
158158
getFOEventHandler().restoreState(this);
159159
}
160160

161+
@Override
162+
protected void endRestoreFOEventHandlerState() {
163+
164+
}
161165
}

fop-core/src/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
package org.apache.fop.accessibility.fo;
2121

2222
import java.io.ByteArrayInputStream;
23+
import java.io.ByteArrayOutputStream;
24+
import java.io.File;
2325
import java.io.IOException;
2426
import java.io.InputStream;
2527
import java.io.StringWriter;
2628
import java.nio.charset.StandardCharsets;
29+
import java.util.List;
2730

2831
import javax.xml.transform.Result;
2932
import javax.xml.transform.Source;
@@ -34,6 +37,7 @@
3437
import javax.xml.transform.TransformerFactoryConfigurationError;
3538
import javax.xml.transform.dom.DOMResult;
3639
import javax.xml.transform.dom.DOMSource;
40+
import javax.xml.transform.sax.SAXResult;
3741
import javax.xml.transform.sax.SAXTransformerFactory;
3842
import javax.xml.transform.sax.TransformerHandler;
3943
import javax.xml.transform.stream.StreamResult;
@@ -46,15 +50,22 @@
4650
import org.w3c.dom.Document;
4751
import org.xml.sax.SAXException;
4852
import org.xml.sax.helpers.AttributesImpl;
49-
53+
import static org.junit.Assert.assertEquals;
5054
import static org.junit.Assert.assertTrue;
5155

5256
import org.apache.commons.io.IOUtils;
57+
import org.apache.pdfbox.Loader;
58+
import org.apache.pdfbox.pdmodel.PDDocument;
59+
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent;
60+
import org.apache.pdfbox.text.PDFMarkedContentExtractor;
5361

5462
import org.apache.fop.accessibility.StructureTree2SAXEventAdapter;
5563
import org.apache.fop.accessibility.StructureTreeEventHandler;
5664
import org.apache.fop.apps.FOPException;
5765
import org.apache.fop.apps.FOUserAgent;
66+
import org.apache.fop.apps.Fop;
67+
import org.apache.fop.apps.FopFactory;
68+
import org.apache.fop.apps.MimeConstants;
5869
import org.apache.fop.fo.FODocumentParser;
5970
import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory;
6071
import org.apache.fop.fo.FOEventHandler;
@@ -242,6 +253,83 @@ public void testSVGArtifact() throws Exception {
242253
+ "</structure-tree-sequence>");
243254
}
244255

256+
@Test
257+
public void testMultipleStaticContentArtifact() throws FOPException,
258+
TransformerException, IOException {
259+
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
260+
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
261+
foUserAgent.setAccessibility(true);
262+
ByteArrayOutputStream out = new ByteArrayOutputStream();
263+
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
264+
TransformerFactory factory = TransformerFactory.newInstance();
265+
Transformer transformer = factory.newTransformer();
266+
267+
String fo = "<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" xmlns:svg=\"http://www.w3.org/2000/svg\">"
268+
+ " <fo:layout-master-set>\n"
269+
+ " <fo:simple-page-master page-width=\"8.5in\" page-height=\"11in\" master-name=\"Page\">\n"
270+
+ " <fo:region-body region-name=\"Body\"/>\n"
271+
+ " <fo:region-before region-name=\"xsl-region-before\" extent=\"0.5in\"/>\n"
272+
+ " </fo:simple-page-master>\n"
273+
+ " </fo:layout-master-set>\n"
274+
+ " <fo:page-sequence master-reference=\"Page\">\n"
275+
+ " <fo:static-content flow-name=\"xsl-region-before\" role=\"artifact\">\n"
276+
+ " <fo:block>\n"
277+
+ " <fo:retrieve-marker retrieve-class-name=\"headline-current\"/>\n"
278+
+ " </fo:block>\n"
279+
+ " </fo:static-content>\n"
280+
+ " <fo:flow flow-name=\"Body\">\n"
281+
+ " <fo:block>\n"
282+
+ " <fo:marker marker-class-name=\"headline-current\">1</fo:marker>\n"
283+
+ " <fo:block>A</fo:block>\n"
284+
+ " </fo:block>\n"
285+
+ " </fo:flow>\n"
286+
+ " </fo:page-sequence>\n"
287+
+ " <fo:page-sequence master-reference=\"Page\">\n"
288+
+ " <fo:static-content flow-name=\"xsl-region-before\" role=\"artifact\">\n"
289+
+ " <fo:block>\n"
290+
+ " <fo:retrieve-marker retrieve-class-name=\"headline-current\"/>\n"
291+
+ " </fo:block>\n"
292+
+ " </fo:static-content>\n"
293+
+ " <fo:flow flow-name=\"Body\">\n"
294+
+ " <fo:block>\n"
295+
+ " <fo:marker marker-class-name=\"headline-current\">2</fo:marker>\n"
296+
+ " <fo:block>B</fo:block>\n"
297+
+ " </fo:block>\n"
298+
+ " </fo:flow>\n"
299+
+ " </fo:page-sequence>\n"
300+
+ "</fo:root>";
301+
Source src = new StreamSource(new ByteArrayInputStream(fo.getBytes()));
302+
Result res = new SAXResult(fop.getDefaultHandler());
303+
304+
try {
305+
transformer.transform(src, res);
306+
} finally {
307+
out.close();
308+
}
309+
310+
try (PDDocument pdfDocument = Loader.loadPDF(out.toByteArray())) {
311+
PDFMarkedContentExtractor extractor = new PDFMarkedContentExtractor();
312+
assertEquals(2, pdfDocument.getPages().getCount());
313+
extractor.processPage(pdfDocument.getPages().get(0));
314+
extractor.processPage(pdfDocument.getPages().get(1));
315+
316+
List<PDMarkedContent> markedContents = extractor.getMarkedContents();
317+
assertEquals(4, markedContents.size());
318+
319+
assertEquals("Artifact", markedContents.get(0).getTag());
320+
assertEquals("1", markedContents.get(0).getContents().get(0).toString());
321+
322+
assertEquals("P", markedContents.get(1).getTag());
323+
assertEquals("A", markedContents.get(1).getContents().get(0).toString());
324+
325+
assertEquals("Artifact", markedContents.get(2).getTag());
326+
assertEquals("2", markedContents.get(2).getContents().get(0).toString());
327+
328+
assertEquals("P", markedContents.get(3).getTag());
329+
assertEquals("B", markedContents.get(3).getContents().get(0).toString());
330+
}
331+
}
332+
245333
private void compare(final String fo, String tree) throws Exception {
246334
foLoader = new FOLoader("") {
247335
public InputStream getFoInputStream() {

0 commit comments

Comments
 (0)