aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Hennebert <vhennebert@apache.org>2012-08-02 11:22:32 +0000
committerVincent Hennebert <vhennebert@apache.org>2012-08-02 11:22:32 +0000
commit2c2c292121674e5e67fc2f5762be66e1528d5161 (patch)
treede57fd0d1c94b6c9f9e245c2fa8f27b524bf2425
parent72362e49d9a0edf953e54b032bae42657edf67a5 (diff)
downloadxmlgraphics-fop-2c2c292121674e5e67fc2f5762be66e1528d5161.tar.gz
xmlgraphics-fop-2c2c292121674e5e67fc2f5762be66e1528d5161.zip
Bugzilla #53639: When PDF accessibility is enabled, the Scope attribute must be present in the structure tree for table header elements.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1368420 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java20
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructElem.java29
-rw-r--r--src/java/org/apache/fop/pdf/StandardStructureAttributes.java69
-rw-r--r--src/java/org/apache/fop/pdf/StandardStructureTypes.java132
-rw-r--r--src/java/org/apache/fop/pdf/StructureType.java34
-rw-r--r--src/java/org/apache/fop/pdf/VersionController.java17
-rw-r--r--src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java180
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java2
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java22
-rw-r--r--status.xml4
-rw-r--r--test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java131
-rw-r--r--test/pdf/1.5/test.pdfbin92661 -> 92694 bytes
-rw-r--r--test/pdf/accessibility/fop.xconf1
13 files changed, 485 insertions, 156 deletions
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index dad404d11..e46a22c4a 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -37,6 +37,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
import org.apache.fop.pdf.xref.CrossReferenceStream;
import org.apache.fop.pdf.xref.CrossReferenceTable;
import org.apache.fop.pdf.xref.TrailerDictionary;
@@ -362,7 +363,7 @@ public class PDFDocument {
* hierarchy
* @return a dictionary of type StructElem
*/
- public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
+ public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent) {
PDFStructElem structElem = new PDFStructElem(parent, structureType);
assignObjectNumber(structElem);
structureTreeElements.add(structElem);
@@ -370,6 +371,23 @@ public class PDFDocument {
}
/**
+ * Creates and returns a structure element.
+ *
+ * @param structureType the structure type of the new element (value for the
+ * S entry)
+ * @param parent the parent of the new structure element in the structure
+ * hierarchy
+ * @param scope the scope of the given table header element
+ * @return a dictionary of type StructElem
+ */
+ public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent,
+ Scope scope) {
+ PDFStructElem structElem = makeStructureElement(structureType, parent);
+ versionController.addTableHeaderScopeAttribute(structElem, scope);
+ return structElem;
+ }
+
+ /**
* Get the {@link PDFInfo} object for this document.
*
* @return the {@link PDFInfo} object
diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java
index 2a756fe9b..28cebb3ee 100644
--- a/src/java/org/apache/fop/pdf/PDFStructElem.java
+++ b/src/java/org/apache/fop/pdf/PDFStructElem.java
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Locale;
import org.apache.fop.accessibility.StructureTreeElement;
+import org.apache.fop.pdf.StandardStructureAttributes.Table;
import org.apache.fop.util.LanguageTags;
/**
@@ -33,7 +34,7 @@ import org.apache.fop.util.LanguageTags;
*/
public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject {
- private static final PDFName TABLE = new PDFName("Table");
+ private StructureType structureType;
private PDFStructElem parentElement;
@@ -50,12 +51,17 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
* @param parent parent of this element
* @param structureType the structure type of this element
*/
- PDFStructElem(PDFObject parent, PDFName structureType) {
+ PDFStructElem(PDFObject parent, StructureType structureType) {
+ this(parent);
+ this.structureType = structureType;
+ put("S", structureType.getName());
+ setParent(parent);
+ }
+
+ private PDFStructElem(PDFObject parent) {
if (parent instanceof PDFStructElem) {
parentElement = (PDFStructElem) parent;
}
- put("S", structureType);
- setParent(parent);
}
/**
@@ -113,8 +119,8 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
*
* @return the value of the S entry
*/
- public PDFName getStructureType() {
- return (PDFName) get("S");
+ public StructureType getStructureType() {
+ return structureType;
}
/**
@@ -201,7 +207,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
private void setTableAttributeRowColumnSpan(String typeSpan, int span) {
PDFDictionary attribute = new PDFDictionary();
- attribute.put("O", TABLE);
+ attribute.put("O", Table.NAME);
attribute.put(typeSpan, span);
if (attributes == null) {
attributes = new ArrayList<PDFDictionary>(2);
@@ -228,13 +234,8 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
}
}
- /**
- * Constructor
- * @param parent -
- * @param name -
- */
- public Placeholder(PDFObject parent, String name) {
- super(parent, new PDFName(name));
+ public Placeholder(PDFObject parent) {
+ super(parent);
}
}
diff --git a/src/java/org/apache/fop/pdf/StandardStructureAttributes.java b/src/java/org/apache/fop/pdf/StandardStructureAttributes.java
new file mode 100644
index 000000000..0a93d46bb
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/StandardStructureAttributes.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Standard attributes, as defined in section 10.7.5 of the PDF Reference, Fourth edition (PDF 1.5).
+ */
+public final class StandardStructureAttributes {
+
+ public static final class Table {
+
+ /**
+ * The name to use as an attribute owner. This is the value of the 'O' entry in
+ * the attribute's dictionary.
+ */
+ public static final PDFName NAME = new PDFName("Table");
+
+ public static enum Scope {
+ ROW("Row"),
+ COLUMN("Column"),
+ BOTH("Both");
+
+ private final PDFName name;
+
+ private Scope(String name) {
+ this.name = new PDFName(name);
+ }
+
+ /**
+ * Returns the name of this attribute.
+ *
+ * @return a name suitable for use as a value in the attribute's dictionary
+ */
+ public PDFName getName() {
+ return name;
+ }
+
+ /**
+ * Sets the given scope on the given table header element.
+ */
+ static void addScopeAttribute(PDFStructElem th, Scope scope) {
+ PDFDictionary scopeAttribute = new PDFDictionary();
+ scopeAttribute.put("O", Table.NAME);
+ scopeAttribute.put("Scope", scope.getName());
+ th.put("A", scopeAttribute);
+ }
+ }
+ }
+
+ private StandardStructureAttributes() { }
+
+}
diff --git a/src/java/org/apache/fop/pdf/StandardStructureTypes.java b/src/java/org/apache/fop/pdf/StandardStructureTypes.java
new file mode 100644
index 000000000..dc045e180
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/StandardStructureTypes.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Standard structure types, as defined in section 10.7.4 of the PDF Reference, Fourth Edition (PDF 1.5).
+ */
+public final class StandardStructureTypes {
+
+ public static final class Grouping {
+
+ public static final StructureType DOCUMENT = new StructureTypeImpl("Document");
+ public static final StructureType PART = new StructureTypeImpl("Part");
+ public static final StructureType ART = new StructureTypeImpl("Art");
+ public static final StructureType SECT = new StructureTypeImpl("Sect");
+ public static final StructureType DIV = new StructureTypeImpl("Div");
+ public static final StructureType BLOCK_QUOTE = new StructureTypeImpl("BlockQuote");
+ public static final StructureType CAPTION = new StructureTypeImpl("Caption");
+ public static final StructureType TOC = new StructureTypeImpl("TOC");
+ public static final StructureType TOCI = new StructureTypeImpl("TOCI");
+ public static final StructureType INDEX = new StructureTypeImpl("Index");
+ public static final StructureType NON_STRUCT = new StructureTypeImpl("NonStruct");
+ public static final StructureType PRIVATE = new StructureTypeImpl("Private");
+ }
+
+ public static final class Paragraphlike {
+ public static final StructureType H = new StructureTypeImpl("H");
+ public static final StructureType H1 = new StructureTypeImpl("H1");
+ public static final StructureType H2 = new StructureTypeImpl("H2");
+ public static final StructureType H3 = new StructureTypeImpl("H3");
+ public static final StructureType H4 = new StructureTypeImpl("H4");
+ public static final StructureType H5 = new StructureTypeImpl("H5");
+ public static final StructureType H6 = new StructureTypeImpl("H6");
+ public static final StructureType P = new StructureTypeImpl("P");
+ }
+
+ public static final class List {
+ public static final StructureType L = new StructureTypeImpl("L");
+ public static final StructureType LI = new StructureTypeImpl("LI");
+ public static final StructureType LBL = new StructureTypeImpl("Lbl");
+ public static final StructureType LBODY = new StructureTypeImpl("LBody");
+ }
+
+ public static final class Table {
+ public static final StructureType TABLE = new StructureTypeImpl("Table");
+ public static final StructureType TR = new StructureTypeImpl("TR");
+ public static final StructureType TH = new StructureTypeImpl("TH");
+ public static final StructureType TD = new StructureTypeImpl("TD");
+ public static final StructureType THEAD = new StructureTypeImpl("THead");
+ public static final StructureType TBODY = new StructureTypeImpl("TBody");
+ public static final StructureType TFOOT = new StructureTypeImpl("TFoot");
+ }
+
+ public static final class InlineLevelStructure {
+ public static final StructureType SPAN = new StructureTypeImpl("Span");
+ public static final StructureType QUOTE = new StructureTypeImpl("Quote");
+ public static final StructureType NOTE = new StructureTypeImpl("Note");
+ public static final StructureType REFERENCE = new StructureTypeImpl("Reference");
+ public static final StructureType BIB_ENTRY = new StructureTypeImpl("BibEntry");
+ public static final StructureType CODE = new StructureTypeImpl("Code");
+ public static final StructureType LINK = new StructureTypeImpl("Link");
+ public static final StructureType ANNOT = new StructureTypeImpl("Annot");
+ }
+
+ public static final class RubyOrWarichu {
+ public static final StructureType RUBY = new StructureTypeImpl("Ruby");
+ public static final StructureType RB = new StructureTypeImpl("RB");
+ public static final StructureType RT = new StructureTypeImpl("RT");
+ public static final StructureType RP = new StructureTypeImpl("RP");
+ public static final StructureType WARICHU = new StructureTypeImpl("Warichu");
+ public static final StructureType WT = new StructureTypeImpl("WT");
+ public static final StructureType WP = new StructureTypeImpl("WP");
+ }
+
+ public static final class Illustration {
+ public static final StructureType FIGURE = new StructureTypeImpl("Figure");
+ public static final StructureType FORMULA = new StructureTypeImpl("Formula");
+ public static final StructureType FORM = new StructureTypeImpl("Form");
+ }
+
+ private static class StructureTypeImpl implements StructureType {
+
+ private final PDFName name;
+
+ protected StructureTypeImpl(String name) {
+ this.name = new PDFName(name);
+ StandardStructureTypes.STRUCTURE_TYPES.put(name, this);
+ }
+
+ public PDFName getName() {
+ return name;
+ }
+
+ }
+
+ private static final Map<String, StructureType> STRUCTURE_TYPES = new HashMap<String, StructureType>();
+
+ private StandardStructureTypes() { }
+
+ /**
+ * Returns the standard structure type of the given name.
+ *
+ * @param name the name of a structure type, case sensitive. For example, Document,
+ * Sect, H1, etc.
+ * @return the corresponding {@code StructureType} instance, or {@code null} if the given
+ * name does not correspond to a standard structure type
+ */
+ public static StructureType get(String name) {
+ return STRUCTURE_TYPES.get(name);
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/StructureType.java b/src/java/org/apache/fop/pdf/StructureType.java
new file mode 100644
index 000000000..7b9b4c169
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/StructureType.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * A structure type, as defined in Section 10.6.2 of the PDF Reference, fourth edition (PDF 1.5).
+ */
+public interface StructureType {
+
+ /**
+ * Returns the name of this structure type.
+ *
+ * @return the name object identifying this structure type
+ */
+ PDFName getName();
+
+}
diff --git a/src/java/org/apache/fop/pdf/VersionController.java b/src/java/org/apache/fop/pdf/VersionController.java
index 4a92f4994..bd5e4d20f 100644
--- a/src/java/org/apache/fop/pdf/VersionController.java
+++ b/src/java/org/apache/fop/pdf/VersionController.java
@@ -19,6 +19,8 @@
package org.apache.fop.pdf;
+import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
+
/**
* An abstraction that controls the mutability of the PDF version for a document.
*/
@@ -47,6 +49,8 @@ public abstract class VersionController {
*/
public abstract void setPDFVersion(Version version);
+ abstract void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope);
+
@Override
public String toString() {
return version.toString();
@@ -67,6 +71,13 @@ public abstract class VersionController {
public void setPDFVersion(Version version) {
throw new IllegalStateException("Cannot change the version of this PDF document.");
}
+
+ @Override
+ void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope) {
+ if (super.version.compareTo(Version.V1_4) > 0) {
+ Scope.addScopeAttribute(th, scope);
+ }
+ }
}
/**
@@ -91,6 +102,12 @@ public abstract class VersionController {
doc.getRoot().setVersion(version);
}
}
+
+ @Override
+ void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope) {
+ setPDFVersion(Version.V1_5);
+ Scope.addScopeAttribute(th, scope);
+ }
}
/**
diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
index 0f752e886..68bfd8686 100644
--- a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
+++ b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
@@ -19,145 +19,66 @@
package org.apache.fop.render.pdf;
-import java.util.HashMap;
import java.util.Map;
import org.apache.fop.events.EventBroadcaster;
-import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFStructElem;
+import org.apache.fop.pdf.StandardStructureTypes;
+import org.apache.fop.pdf.StructureType;
/**
* This class provides the standard mappings from Formatting Objects to PDF structure types.
*/
final class FOToPDFRoleMap {
- /**
- * Standard structure types defined by the PDF Reference, Fourth Edition (PDF 1.5).
- */
- private static final Map<String, PDFName> STANDARD_STRUCTURE_TYPES
- = new HashMap<String, PDFName>();
-
- private static final Map<String, Mapper> DEFAULT_MAPPINGS
- = new java.util.HashMap<String, Mapper>();
-
- private static final PDFName THEAD;
- private static final PDFName NON_STRUCT;
+ private static final Map<String, Mapper> DEFAULT_MAPPINGS = new java.util.HashMap<String, Mapper>();
static {
- // Create PDFNames for the standard structure types
- // Table 10.18: Grouping elements
- addStructureType("Document");
- addStructureType("Part");
- addStructureType("Art");
- addStructureType("Sect");
- addStructureType("Div");
- addStructureType("BlockQuote");
- addStructureType("Caption");
- addStructureType("TOC");
- addStructureType("TOCI");
- addStructureType("Index");
- addStructureType("NonStruct");
- addStructureType("Private");
- // Table 10.20: Paragraphlike elements
- addStructureType("H");
- addStructureType("H1");
- addStructureType("H2");
- addStructureType("H3");
- addStructureType("H4");
- addStructureType("H5");
- addStructureType("H6");
- addStructureType("P");
- // Table 10.21: List elements
- addStructureType("L");
- addStructureType("LI");
- addStructureType("Lbl");
- addStructureType("LBody");
- // Table 10.22: Table elements
- addStructureType("Table");
- addStructureType("TR");
- addStructureType("TH");
- addStructureType("TD");
- addStructureType("THead");
- addStructureType("TBody");
- addStructureType("TFoot");
- // Table 10.23: Inline-level structure elements
- addStructureType("Span");
- addStructureType("Quote");
- addStructureType("Note");
- addStructureType("Reference");
- addStructureType("BibEntry");
- addStructureType("Code");
- addStructureType("Link");
- addStructureType("Annot");
- // Table 10.24: Ruby and Warichu elements
- addStructureType("Ruby");
- addStructureType("RB");
- addStructureType("RT");
- addStructureType("RP");
- addStructureType("Warichu");
- addStructureType("WT");
- addStructureType("WP");
- // Table 10.25: Illustration elements
- addStructureType("Figure");
- addStructureType("Formula");
- addStructureType("Form");
-
- NON_STRUCT = STANDARD_STRUCTURE_TYPES.get("NonStruct");
- assert NON_STRUCT != null;
- THEAD = STANDARD_STRUCTURE_TYPES.get("THead");
- assert THEAD != null;
-
// Create the standard mappings
// Declarations and Pagination and Layout Formatting Objects
- addMapping("root", "Document");
- addMapping("page-sequence", "Part");
- addMapping("flow", "Sect");
- addMapping("static-content", "Sect");
+ addMapping("root", StandardStructureTypes.Grouping.DOCUMENT);
+ addMapping("page-sequence", StandardStructureTypes.Grouping.PART);
+ addMapping("flow", StandardStructureTypes.Grouping.SECT);
+ addMapping("static-content", StandardStructureTypes.Grouping.SECT);
// Block-level Formatting Objects
- addMapping("block", "P");
- addMapping("block-container", "Div");
+ addMapping("block", StandardStructureTypes.Paragraphlike.P);
+ addMapping("block-container", StandardStructureTypes.Grouping.DIV);
// Inline-level Formatting Objects
- addMapping("character", "Span");
- addMapping("external-graphic", "Figure");
- addMapping("instream-foreign-object", "Figure");
- addMapping("inline", "Span");
- addMapping("inline-container", "Div");
- addMapping("page-number", "Quote");
- addMapping("page-number-citation", "Quote");
- addMapping("page-number-citation-last", "Quote");
+ addMapping("character", StandardStructureTypes.InlineLevelStructure.SPAN);
+ addMapping("external-graphic", StandardStructureTypes.Illustration.FIGURE);
+ addMapping("instream-foreign-object", StandardStructureTypes.Illustration.FIGURE);
+ addMapping("inline", StandardStructureTypes.InlineLevelStructure.SPAN);
+ addMapping("inline-container", StandardStructureTypes.Grouping.DIV);
+ addMapping("page-number", StandardStructureTypes.InlineLevelStructure.QUOTE);
+ addMapping("page-number-citation", StandardStructureTypes.InlineLevelStructure.QUOTE);
+ addMapping("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE);
// Formatting Objects for Tables
- addMapping("table-and-caption", "Div");
- addMapping("table", "Table");
- addMapping("table-caption", "Caption");
- addMapping("table-header", "THead");
- addMapping("table-footer", "TFoot");
- addMapping("table-body", "TBody");
- addMapping("table-row", "TR");
+ addMapping("table-and-caption", StandardStructureTypes.Grouping.DIV);
+ addMapping("table", StandardStructureTypes.Table.TABLE);
+ addMapping("table-caption", StandardStructureTypes.Grouping.CAPTION);
+ addMapping("table-header", StandardStructureTypes.Table.THEAD);
+ addMapping("table-footer", StandardStructureTypes.Table.TFOOT);
+ addMapping("table-body", StandardStructureTypes.Table.TBODY);
+ addMapping("table-row", StandardStructureTypes.Table.TR);
addMapping("table-cell", new TableCellMapper());
// Formatting Objects for Lists
- addMapping("list-block", "L");
- addMapping("list-item", "LI");
- addMapping("list-item-body", "LBody");
- addMapping("list-item-label", "Lbl");
+ addMapping("list-block", StandardStructureTypes.List.L);
+ addMapping("list-item", StandardStructureTypes.List.LI);
+ addMapping("list-item-body", StandardStructureTypes.List.LBODY);
+ addMapping("list-item-label", StandardStructureTypes.List.LBL);
// Dynamic Effects: Link and Multi Formatting Objects
- addMapping("basic-link", "Link");
+ addMapping("basic-link", StandardStructureTypes.InlineLevelStructure.LINK);
// Out-of-Line Formatting Objects
- addMapping("float", "Div");
- addMapping("footnote", "Note");
- addMapping("footnote-body", "Sect");
- addMapping("wrapper", "Span");
- addMapping("marker", "Private");
- }
-
- private static void addStructureType(String structureType) {
- STANDARD_STRUCTURE_TYPES.put(structureType, new PDFName(structureType));
+ addMapping("float", StandardStructureTypes.Grouping.DIV);
+ addMapping("footnote", StandardStructureTypes.InlineLevelStructure.NOTE);
+ addMapping("footnote-body", StandardStructureTypes.Grouping.SECT);
+ addMapping("wrapper", StandardStructureTypes.InlineLevelStructure.SPAN);
+ addMapping("marker", StandardStructureTypes.Grouping.PRIVATE);
}
- private static void addMapping(String fo, String structureType) {
- PDFName type = STANDARD_STRUCTURE_TYPES.get(structureType);
- assert type != null;
- addMapping(fo, new SimpleMapper(type));
+ private static void addMapping(String fo, StructureType structureType) {
+ addMapping(fo, new SimpleMapper(structureType));
}
private static void addMapping(String fo, Mapper mapper) {
@@ -173,13 +94,13 @@ final class FOToPDFRoleMap {
* @param eventBroadcaster the event broadcaster
* @return the structure type or null if no match could be found
*/
- public static PDFName mapFormattingObject(String fo, String role,
+ public static StructureType mapFormattingObject(String fo, String role,
PDFObject parent, EventBroadcaster eventBroadcaster) {
- PDFName type = null;
+ StructureType type = null;
if (role == null) {
type = getDefaultMappingFor(fo, parent);
} else {
- type = STANDARD_STRUCTURE_TYPES.get(role);
+ type = StandardStructureTypes.get(role);
if (type == null) {
type = getDefaultMappingFor(fo, parent);
PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo,
@@ -196,28 +117,28 @@ final class FOToPDFRoleMap {
* @param parent the parent of the structure element to be mapped
* @return the structure type or NonStruct if no match could be found
*/
- private static PDFName getDefaultMappingFor(String fo, PDFObject parent) {
+ private static StructureType getDefaultMappingFor(String fo, PDFObject parent) {
Mapper mapper = DEFAULT_MAPPINGS.get(fo);
if (mapper != null) {
return mapper.getStructureType(parent);
} else {
- return NON_STRUCT;
+ return StandardStructureTypes.Grouping.NON_STRUCT;
}
}
private interface Mapper {
- PDFName getStructureType(PDFObject parent);
+ StructureType getStructureType(PDFObject parent);
}
private static class SimpleMapper implements Mapper {
- private PDFName structureType;
+ private StructureType structureType;
- public SimpleMapper(PDFName structureType) {
+ public SimpleMapper(StructureType structureType) {
this.structureType = structureType;
}
- public PDFName getStructureType(PDFObject parent) {
+ public StructureType getStructureType(PDFObject parent) {
return structureType;
}
@@ -225,17 +146,14 @@ final class FOToPDFRoleMap {
private static class TableCellMapper implements Mapper {
- public PDFName getStructureType(PDFObject parent) {
+ public StructureType getStructureType(PDFObject parent) {
PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem();
//TODO What to do with cells from table-footer? Currently they are mapped on TD.
- PDFName type;
- if (THEAD.equals(grandParent.getStructureType())) {
- type = STANDARD_STRUCTURE_TYPES.get("TH");
+ if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) {
+ return StandardStructureTypes.Table.TH;
} else {
- type = STANDARD_STRUCTURE_TYPES.get("TD");
+ return StandardStructureTypes.Table.TD;
}
- assert type != null;
- return type;
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
index 6559e8d56..ee00d2401 100644
--- a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
@@ -132,7 +132,7 @@ class PDFLogicalStructureHandler {
? structureTreeElement.getParentStructElem()
: structureTreeElement;
pageParentTreeArray.add(parent);
- String type = parent.getStructureType().toString();
+ String type = parent.getStructureType().getName().toString();
int mcid = pageParentTreeArray.length() - 1;
return new MarkedContentInfo(type, mcid, structureTreeElement);
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
index 0f8e74515..1377bbfce 100644
--- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
+++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
@@ -29,18 +29,18 @@ import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.pdf.PDFFactory;
-import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.PDFStructTreeRoot;
+import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
+import org.apache.fop.pdf.StandardStructureTypes.Table;
+import org.apache.fop.pdf.StructureType;
class PDFStructureTreeBuilder implements StructureTreeEventHandler {
private PDFFactory pdfFactory;
- private PDFLogicalStructureHandler logicalStructureHandler;
-
private EventBroadcaster eventBroadcaster;
private LinkedList<PDFStructElem> ancestors = new LinkedList<PDFStructElem>();
@@ -52,11 +52,10 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
}
void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) {
- this.logicalStructureHandler = logicalStructureHandler;
- createRootStructureElement();
+ createRootStructureElement(logicalStructureHandler);
}
- private void createRootStructureElement() {
+ private void createRootStructureElement(PDFLogicalStructureHandler logicalStructureHandler) {
assert rootStructureElement == null;
PDFParentTree parentTree = logicalStructureHandler.getParentTree();
PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree);
@@ -79,8 +78,13 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
}
private PDFStructElem createStructureElement(String name, PDFObject parent, String role) {
- PDFName structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster);
- return pdfFactory.getDocument().makeStructureElement(structureType, parent);
+ StructureType structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent,
+ eventBroadcaster);
+ if (structureType == Table.TH) {
+ return pdfFactory.getDocument().makeStructureElement(structureType, parent, Scope.COLUMN);
+ } else {
+ return pdfFactory.getDocument().makeStructureElement(structureType, parent);
+ }
}
public void endPageSequence() {
@@ -135,7 +139,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
String role = attributes.getValue("role");
PDFStructElem structElem;
if ("#PCDATA".equals(name)) {
- structElem = new PDFStructElem.Placeholder(parent, name);
+ structElem = new PDFStructElem.Placeholder(parent);
} else {
structElem = createStructureElement(name, parent, role);
}
diff --git a/status.xml b/status.xml
index 055d40dcc..b1635fea9 100644
--- a/status.xml
+++ b/status.xml
@@ -62,6 +62,10 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="VH" type="add" fixes-bug="53639">
+ When PDF accessibility is enabled, the Scope attribute must be present in the structure tree
+ for table header elements.
+ </action>
<action context="Fonts" dev="MH" type="add" fixes-bug="53600" due-to="Robert Meyer">
Added an event if a glyph and its metric information does not exist in the character set
</action>
diff --git a/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java
new file mode 100644
index 000000000..89682628d
--- /dev/null
+++ b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.verification.VerificationMode;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
+import org.apache.fop.pdf.StandardStructureTypes.Table;
+
+public class TableHeaderScopeTestCase {
+
+ private static final String ATTRIBUTE_ENTRY = "A";
+
+ private VersionController controller;
+
+ @Test
+ public void pdfDocumentDelegatesToVersionController() {
+ for (Scope scope : Scope.values()) {
+ testMakeStructureElementWithScope(scope);
+ }
+ }
+
+ private void testMakeStructureElementWithScope(Scope scope) {
+ VersionController controller = mock(VersionController.class);
+ PDFDocument document = new PDFDocument("Test", controller);
+ document.makeStructTreeRoot(null);
+ document.makeStructureElement(Table.TH, null, scope);
+ verify(controller).addTableHeaderScopeAttribute(any(PDFStructElem.class), eq(scope));
+ }
+
+ @Test
+ public void versionControllerMayDelegateToScope() {
+ fixedController14doesNotAddAttribute();
+ fixedController15addsAttribute();
+ dynamicControllerAddsAttribute();
+ }
+
+ private void fixedController14doesNotAddAttribute() {
+ controller = VersionController.getFixedVersionController(Version.V1_4);
+ scopeMustNotBeAdded();
+ }
+
+ private void fixedController15addsAttribute() {
+ controller = VersionController.getFixedVersionController(Version.V1_5);
+ scopeMustBeAdded();
+ }
+
+ private void dynamicControllerAddsAttribute() {
+ PDFDocument document = new PDFDocument("Test");
+ controller = VersionController.getDynamicVersionController(Version.V1_4, document);
+ scopeMustBeAdded();
+ assertEquals(Version.V1_5, controller.getPDFVersion());
+ }
+
+ private void scopeMustBeAdded() {
+ scopeMustBeAdded(times(1));
+ }
+
+ private void scopeMustNotBeAdded() {
+ scopeMustBeAdded(never());
+ }
+
+ private void scopeMustBeAdded(VerificationMode nTimes) {
+ PDFStructElem structElem = mock(PDFStructElem.class);
+ controller.addTableHeaderScopeAttribute(structElem, Scope.COLUMN);
+ verify(structElem, nTimes).put(eq(ATTRIBUTE_ENTRY), any());
+ }
+
+ @Test
+ public void scopeAddsTheAttribute() {
+ for (Scope scope : Scope.values()) {
+ scopeAttributeMustBeAdded(scope);
+ }
+ }
+
+ private void scopeAttributeMustBeAdded(Scope scope) {
+ PDFStructElem structElem = mock(PDFStructElem.class);
+ Scope.addScopeAttribute(structElem, scope);
+ verify(structElem).put(eq(ATTRIBUTE_ENTRY), scopeAttribute(scope));
+ }
+
+ private PDFDictionary scopeAttribute(Scope scope) {
+ return argThat(new isScopeAttribute(scope));
+ }
+
+ private static class isScopeAttribute extends ArgumentMatcher<PDFDictionary> {
+
+ private final Scope expectedScope;
+
+ public isScopeAttribute(Scope expectedScope) {
+ this.expectedScope = expectedScope;
+ }
+
+ @Override
+ public boolean matches(Object argument) {
+ PDFDictionary attribute = (PDFDictionary) argument;
+ return "/Table".equals(attribute.get("O").toString())
+ && expectedScope.getName().toString().equals(attribute.get("Scope").toString());
+ }
+
+ }
+
+}
diff --git a/test/pdf/1.5/test.pdf b/test/pdf/1.5/test.pdf
index 6175a270f..d97648e1e 100644
--- a/test/pdf/1.5/test.pdf
+++ b/test/pdf/1.5/test.pdf
Binary files differ
diff --git a/test/pdf/accessibility/fop.xconf b/test/pdf/accessibility/fop.xconf
index adfccd2cc..24346a16a 100644
--- a/test/pdf/accessibility/fop.xconf
+++ b/test/pdf/accessibility/fop.xconf
@@ -6,6 +6,7 @@
<font-base>../../resources/fonts/ttf/</font-base>
<renderers>
<renderer mime="application/pdf">
+ <version>1.4</version>
<filterList>
<value>null</value>
</filterList>