Browse Source

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
tags/fop-2_0
Vincent Hennebert 11 years ago
parent
commit
2c2c292121

+ 19
- 1
src/java/org/apache/fop/pdf/PDFDocument.java View 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.
*

+ 15
- 14
src/java/org/apache/fop/pdf/PDFStructElem.java View 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);
}
}


+ 69
- 0
src/java/org/apache/fop/pdf/StandardStructureAttributes.java View File

@@ -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() { }

}

+ 132
- 0
src/java/org/apache/fop/pdf/StandardStructureTypes.java View File

@@ -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);
}

}

+ 34
- 0
src/java/org/apache/fop/pdf/StructureType.java View File

@@ -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();

}

+ 17
- 0
src/java/org/apache/fop/pdf/VersionController.java View 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);
}
}

/**

+ 49
- 131
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java View File

@@ -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;
}

}

+ 1
- 1
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java View 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);
}

+ 13
- 9
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java View 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);
}

+ 4
- 0
status.xml View File

@@ -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>

+ 131
- 0
test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java View File

@@ -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());
}

}

}

BIN
test/pdf/1.5/test.pdf View File


+ 1
- 0
test/pdf/accessibility/fop.xconf View 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>

Loading…
Cancel
Save