diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2012-08-02 11:22:32 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2012-08-02 11:22:32 +0000 |
commit | 2c2c292121674e5e67fc2f5762be66e1528d5161 (patch) | |
tree | de57fd0d1c94b6c9f9e245c2fa8f27b524bf2425 | |
parent | 72362e49d9a0edf953e54b032bae42657edf67a5 (diff) | |
download | xmlgraphics-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.java | 20 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFStructElem.java | 29 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/StandardStructureAttributes.java | 69 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/StandardStructureTypes.java | 132 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/StructureType.java | 34 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/VersionController.java | 17 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java | 180 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java | 2 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java | 22 | ||||
-rw-r--r-- | status.xml | 4 | ||||
-rw-r--r-- | test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java | 131 | ||||
-rw-r--r-- | test/pdf/1.5/test.pdf | bin | 92661 -> 92694 bytes | |||
-rw-r--r-- | test/pdf/accessibility/fop.xconf | 1 |
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 Binary files differindex 6175a270f..d97648e1e 100644 --- a/test/pdf/1.5/test.pdf +++ b/test/pdf/1.5/test.pdf 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> |