]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #53639: When PDF accessibility is enabled, the Scope attribute must be prese...
authorVincent Hennebert <vhennebert@apache.org>
Thu, 2 Aug 2012 11:22:32 +0000 (11:22 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Thu, 2 Aug 2012 11:22:32 +0000 (11:22 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1368420 13f79535-47bb-0310-9956-ffa450edef68

13 files changed:
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFStructElem.java
src/java/org/apache/fop/pdf/StandardStructureAttributes.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/StandardStructureTypes.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/StructureType.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/VersionController.java
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
status.xml
test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java [new file with mode: 0644]
test/pdf/1.5/test.pdf
test/pdf/accessibility/fop.xconf

index dad404d1148fd6ece0ae5540cd4f5442c1c39b2a..e46a22c4a724e72eda2857ae6e5eecfd30453763 100644 (file)
@@ -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,13 +363,30 @@ 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);
         return structElem;
     }
 
+    /**
+     * 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.
      *
index 2a756fe9b9ba85f576994ad80d7b7995b3fa4ed6..28cebb3ee82635ff0bf0c8d77119e645e66e349c 100644 (file)
@@ -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 (file)
index 0000000..0a93d46
--- /dev/null
@@ -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 (file)
index 0000000..dc045e1
--- /dev/null
@@ -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 (file)
index 0000000..7b9b4c1
--- /dev/null
@@ -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();
+
+}
index 4a92f4994b204bc1cfb482fcbbd63eb4c811ae99..bd5e4d20f05687be01b5f42951efd3676675eac1 100644 (file)
@@ -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);
+        }
     }
 
     /**
index 0f752e886d65ca8a85b0b779a9507162a6789108..68bfd8686557360f39ac71f794dfd45f1289279d 100644 (file)
 
 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;
         }
 
     }
index 6559e8d56f2b4fa71d3195676f9bdc296804effa..ee00d2401e40cd912a26707453d71c6029e9fde4 100644 (file)
@@ -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);
     }
index 0f8e74515046ac74a744fb513892b2a09619305b..1377bbfcebf6c371a60a0983df0a6358229c1077 100644 (file)
@@ -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);
         }
index 055d40dcc68fc83b19355556ad0e199ded0205f8..b1635fea993ec210f3fbcfe546da6a020b3982ab 100644 (file)
       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 (file)
index 0000000..8968262
--- /dev/null
@@ -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());
+        }
+
+    }
+
+}
index 6175a270fc9b29702dc3397c8815d9eb06eea5b7..d97648e1edca50f561957fa6a1b04593adb0e8e0 100644 (file)
Binary files a/test/pdf/1.5/test.pdf and b/test/pdf/1.5/test.pdf differ
index adfccd2cc810c8e393858c8d05aa5fc12841366f..24346a16a1d35559e2f7ea6d495453b5353a3425 100644 (file)
@@ -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>