Browse Source

Removed the DOM representation of the structure tree.

The structure tree is now directly converted into corresponding PDF objects.
When going the IF route, the structure tree is stored in the form of SAX events that will be re-played when it's time to stream them into the output. This may still change.

* Extracted RFC3066 methods from XMLUtil into new LanguageTags class that can be re-used in a non-XML context.
* Dropped support for accessibility in the old Area Tree XML.
* Added support for the xml:lang property on fo:root, so that the global language can be set for the document without retrieving the language from the first page-sequence.
* Renamed StructureTreeBuilder into more appropriate StructureTreeEventHandler (same for applicable sub-classes)
* Renamed StructureTreeBuildingFOEventHandler into FO2StructureTreeConverter and added test case
* Added test cases for classes in the StructureTreeEventHandler hierarchy


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ImproveAccessibility@1205935 13f79535-47bb-0310-9956-ffa450edef68
pull/24/head
Vincent Hennebert 12 years ago
parent
commit
b251762630
72 changed files with 1708 additions and 689 deletions
  1. 11
    4
      src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd
  2. 52
    0
      src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java
  3. 105
    122
      src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java
  4. 0
    102
      src/java/org/apache/fop/accessibility/StructureTree.java
  5. 104
    0
      src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java
  6. 0
    95
      src/java/org/apache/fop/accessibility/StructureTreeBuilder.java
  7. 56
    0
      src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java
  8. 16
    14
      src/java/org/apache/fop/apps/FOUserAgent.java
  9. 9
    0
      src/java/org/apache/fop/area/AreaTreeHandler.java
  10. 8
    0
      src/java/org/apache/fop/area/AreaTreeModel.java
  11. 6
    50
      src/java/org/apache/fop/area/AreaTreeParser.java
  12. 6
    0
      src/java/org/apache/fop/area/RenderPagesModel.java
  13. 11
    0
      src/java/org/apache/fop/fo/DelegatingFOEventHandler.java
  14. 4
    1
      src/java/org/apache/fop/fo/FOElementMapping.java
  15. 7
    0
      src/java/org/apache/fop/fo/FOEventHandler.java
  16. 3
    5
      src/java/org/apache/fop/fo/FOTreeBuilder.java
  17. 26
    0
      src/java/org/apache/fop/fo/pagination/Root.java
  18. 0
    19
      src/java/org/apache/fop/pdf/PDFDocument.java
  19. 5
    1
      src/java/org/apache/fop/pdf/PDFProfile.java
  20. 11
    3
      src/java/org/apache/fop/pdf/PDFRoot.java
  21. 2
    2
      src/java/org/apache/fop/pdf/PDFStructElem.java
  22. 6
    1
      src/java/org/apache/fop/render/AbstractRenderer.java
  23. 7
    0
      src/java/org/apache/fop/render/Renderer.java
  24. 16
    1
      src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java
  25. 1
    0
      src/java/org/apache/fop/render/intermediate/IFConstants.java
  26. 13
    3
      src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
  27. 47
    21
      src/java/org/apache/fop/render/intermediate/IFParser.java
  28. 12
    2
      src/java/org/apache/fop/render/intermediate/IFRenderer.java
  29. 29
    19
      src/java/org/apache/fop/render/intermediate/IFSerializer.java
  30. 3
    0
      src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java
  31. 190
    0
      src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java
  32. 15
    1
      src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java
  33. 19
    16
      src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
  34. 33
    26
      src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
  35. 3
    0
      src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java
  36. 9
    63
      src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
  37. 93
    0
      src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
  38. 1
    30
      src/java/org/apache/fop/render/xml/XMLRenderer.java
  39. 66
    0
      src/java/org/apache/fop/util/LanguageTags.java
  40. 0
    36
      src/java/org/apache/fop/util/XMLUtil.java
  41. 0
    24
      test/accessibility/config-renderer.xconf
  42. 0
    0
      test/accessibility/fop.xconf
  43. BIN
      test/accessibility/pdf/background-image_jpg_repeat.pdf
  44. BIN
      test/accessibility/pdf/background-image_jpg_single.pdf
  45. BIN
      test/accessibility/pdf/background-image_png_repeat.pdf
  46. BIN
      test/accessibility/pdf/background-image_png_single.pdf
  47. BIN
      test/accessibility/pdf/background-image_svg_repeat.pdf
  48. BIN
      test/accessibility/pdf/background-image_svg_single.pdf
  49. BIN
      test/accessibility/pdf/complete.pdf
  50. BIN
      test/accessibility/pdf/image_jpg.pdf
  51. BIN
      test/accessibility/pdf/image_png.pdf
  52. BIN
      test/accessibility/pdf/image_svg.pdf
  53. BIN
      test/accessibility/pdf/image_wmf.pdf
  54. BIN
      test/accessibility/pdf/leader.pdf
  55. BIN
      test/accessibility/pdf/links.pdf
  56. BIN
      test/accessibility/pdf/role.pdf
  57. BIN
      test/accessibility/pdf/role_non-standard.pdf
  58. BIN
      test/accessibility/pdf/text_1.pdf
  59. BIN
      test/accessibility/pdf/text_2.pdf
  60. BIN
      test/accessibility/pdf/text_font-embedding.pdf
  61. 2
    1
      test/java/org/apache/fop/StandardTestSuite.java
  62. 202
    0
      test/java/org/apache/fop/accessibility/FO2StructureTreeConverterTestCase.java
  63. 116
    0
      test/java/org/apache/fop/accessibility/fo2StructureTree.xsl
  64. 14
    5
      test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java
  65. 18
    2
      test/java/org/apache/fop/fo/FODocumentParser.java
  66. 2
    1
      test/java/org/apache/fop/fo/complete_document.fo
  67. 10
    4
      test/java/org/apache/fop/fo/extract-events.xsl
  68. 2
    1
      test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java
  69. 16
    1
      test/java/org/apache/fop/intermediate/IFParserTestCase.java
  70. 169
    0
      test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java
  71. 131
    0
      test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java
  72. 21
    13
      test/java/org/apache/fop/util/LanguageTagsTestCase.java

+ 11
- 4
src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd View File

@@ -40,10 +40,17 @@
</xs:element>
<xs:element name="header">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<!--xs:element ref="x:xmpmeta" xmlns:x="adobe:ns:meta/"/-->
<xs:any namespace="##other" processContents="lax"/>
</xs:choice>
<xs:sequence>
<xs:element name="locale" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attributeGroup ref="mf:foreignAtts"/>
</xs:complexType>
</xs:element>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<!--xs:element ref="x:xmpmeta" xmlns:x="adobe:ns:meta/"/-->
<xs:any namespace="##other" processContents="lax"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="trailer">

+ 52
- 0
src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java View File

@@ -0,0 +1,52 @@
/*
* 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.accessibility;

import java.util.Locale;

import org.xml.sax.Attributes;

/**
* This implementation ignores all structure tree events.
*/
public final class DummyStructureTreeEventHandler implements StructureTreeEventHandler {

/** The singleton instance of this class. */
public static final StructureTreeEventHandler INSTANCE = new DummyStructureTreeEventHandler();

private DummyStructureTreeEventHandler() { }

/** {@inheritDoc} */
public void startPageSequence(Locale locale) {
}

/** {@inheritDoc} */
public void startNode(String name, Attributes attributes) {
}

/** {@inheritDoc} */
public void endNode(String name) {
}

/** {@inheritDoc} */
public void endPageSequence() {
}

}

src/java/org/apache/fop/accessibility/StructureTreeBuildingFOEventHandler.java → src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java View File

@@ -19,11 +19,7 @@

package org.apache.fop.accessibility;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import java.util.Locale;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
@@ -63,34 +59,32 @@ import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fo.properties.CommonAccessibilityHolder;
import org.apache.fop.util.XMLUtil;

/**
* A class that builds the document's structure tree.
* Allows to create the structure tree of an FO document, by converting FO
* events into appropriate structure tree events.
*/
public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandler {
public class FO2StructureTreeConverter extends DelegatingFOEventHandler {

private int idCounter;

private final StructureTree structureTree;
/** Delegates to either {@link #foToStructureTreeEventAdapter} or {@link #eventSwallower}. */
private FOEventHandler converter;

private TransformerHandler structureTreeDOMBuilder;

private DOMResult result;

/** Delegates to either {@link #actualStructureTreeBuilder} or {@link #eventSwallower}. */
private FOEventHandler structureTreeBuilder;

private FOEventHandler actualStructureTreeBuilder;
private final FOEventHandler foToStructureTreeEventAdapter;

/** The descendants of some elements like fo:leader must be ignored. */
private final FOEventHandler eventSwallower;

private final class StructureTreeBuilder extends FOEventHandler {
private final StructureTreeEventHandler structureTreeEventHandler;

private final class FOToStructureTreeEventAdapter extends FOEventHandler {

public StructureTreeBuilder(FOUserAgent foUserAgent) {
public FOToStructureTreeEventAdapter(FOUserAgent foUserAgent) {
super(foUserAgent);
}

@@ -104,33 +98,20 @@ public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandle

@Override
public void startPageSequence(PageSequence pageSeq) {
SAXTransformerFactory transformerFactory =
(SAXTransformerFactory) TransformerFactory.newInstance();
try {
structureTreeDOMBuilder = transformerFactory.newTransformerHandler();
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
Locale locale = null;
if (pageSeq.getLanguage() != null) {
if (pageSeq.getCountry() != null) {
locale = new Locale(pageSeq.getLanguage(), pageSeq.getCountry());
} else {
locale = new Locale(pageSeq.getLanguage());
}
}
result = new DOMResult();
structureTreeDOMBuilder.setResult(result);
try {
structureTreeDOMBuilder.startDocument();
} catch (SAXException e) {
throw new RuntimeException(e);
}
startElement(pageSeq);
structureTreeEventHandler.startPageSequence(locale);
}

@Override
public void endPageSequence(PageSequence pageSeq) {
endElement(pageSeq);
try {
structureTreeDOMBuilder.endDocument();
} catch (SAXException e) {
throw new RuntimeException(e);
}
structureTree.addPageSequenceStructure(
result.getNode().getFirstChild().getChildNodes());
structureTreeEventHandler.endPageSequence();
}

@Override
@@ -381,6 +362,7 @@ public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandle
endElement(c);
}


private void startElement(FONode node) {
startElement(node, new AttributesImpl());
}
@@ -409,13 +391,7 @@ public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandle
if (node instanceof CommonAccessibilityHolder) {
addRole((CommonAccessibilityHolder) node, attributes);
}
try {
structureTreeDOMBuilder.startElement(node.getNamespaceURI(), localName,
node.getNormalNamespacePrefix() + ":" + localName,
attributes);
} catch (SAXException e) {
throw new RuntimeException(e);
}
structureTreeEventHandler.startNode(localName, attributes);
}

private void addNoNamespaceAttribute(AttributesImpl attributes, String name, String value) {
@@ -438,12 +414,7 @@ public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandle

private void endElement(FONode node) {
String localName = node.getLocalName();
try {
structureTreeDOMBuilder.endElement(node.getNamespaceURI(), localName,
node.getNormalNamespacePrefix() + ":" + localName);
} catch (SAXException e) {
throw new RuntimeException(e);
}
structureTreeEventHandler.endNode(localName);
}

}
@@ -451,389 +422,401 @@ public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandle
/**
* Creates a new instance.
*
* @param structureTree the object that will hold the structure tree
* @param structureTreeEventHandler the object that will hold the structure tree
* @param delegate the FO event handler that must be wrapped by this instance
*/
public StructureTreeBuildingFOEventHandler(StructureTree structureTree,
public FO2StructureTreeConverter(StructureTreeEventHandler structureTreeEventHandler,
FOEventHandler delegate) {
super(delegate);
this.structureTree = structureTree;
this.actualStructureTreeBuilder = new StructureTreeBuilder(foUserAgent);
this.structureTreeBuilder = actualStructureTreeBuilder;
this.structureTreeEventHandler = structureTreeEventHandler;
this.foToStructureTreeEventAdapter = new FOToStructureTreeEventAdapter(foUserAgent);
this.converter = foToStructureTreeEventAdapter;
this.eventSwallower = new FOEventHandler(foUserAgent) { };
}

@Override
public void startDocument() throws SAXException {
structureTreeBuilder.startDocument();
converter.startDocument();
super.startDocument();
}

@Override
public void endDocument() throws SAXException {
structureTreeBuilder.endDocument();
converter.endDocument();
super.endDocument();
}

@Override
public void startRoot(Root root) {
converter.startRoot(root);
super.startRoot(root);
}

@Override
public void endRoot(Root root) {
converter.endRoot(root);
super.endRoot(root);
}

@Override
public void startPageSequence(PageSequence pageSeq) {
structureTreeBuilder.startPageSequence(pageSeq);
converter.startPageSequence(pageSeq);
super.startPageSequence(pageSeq);
}

@Override
public void endPageSequence(PageSequence pageSeq) {
structureTreeBuilder.endPageSequence(pageSeq);
converter.endPageSequence(pageSeq);
super.endPageSequence(pageSeq);
}

@Override
public void startPageNumber(PageNumber pagenum) {
structureTreeBuilder.startPageNumber(pagenum);
converter.startPageNumber(pagenum);
super.startPageNumber(pagenum);
}

@Override
public void endPageNumber(PageNumber pagenum) {
structureTreeBuilder.endPageNumber(pagenum);
converter.endPageNumber(pagenum);
super.endPageNumber(pagenum);
}

@Override
public void startPageNumberCitation(PageNumberCitation pageCite) {
structureTreeBuilder.startPageNumberCitation(pageCite);
converter.startPageNumberCitation(pageCite);
super.startPageNumberCitation(pageCite);
}

@Override
public void endPageNumberCitation(PageNumberCitation pageCite) {
structureTreeBuilder.endPageNumberCitation(pageCite);
converter.endPageNumberCitation(pageCite);
super.endPageNumberCitation(pageCite);
}

@Override
public void startPageNumberCitationLast(PageNumberCitationLast pageLast) {
structureTreeBuilder.startPageNumberCitationLast(pageLast);
converter.startPageNumberCitationLast(pageLast);
super.startPageNumberCitationLast(pageLast);
}

@Override
public void endPageNumberCitationLast(PageNumberCitationLast pageLast) {
structureTreeBuilder.endPageNumberCitationLast(pageLast);
converter.endPageNumberCitationLast(pageLast);
super.endPageNumberCitationLast(pageLast);
}

@Override
public void startFlow(Flow fl) {
structureTreeBuilder.startFlow(fl);
converter.startFlow(fl);
super.startFlow(fl);
}

@Override
public void endFlow(Flow fl) {
structureTreeBuilder.endFlow(fl);
converter.endFlow(fl);
super.endFlow(fl);
}

@Override
public void startBlock(Block bl) {
structureTreeBuilder.startBlock(bl);
converter.startBlock(bl);
super.startBlock(bl);
}

@Override
public void endBlock(Block bl) {
structureTreeBuilder.endBlock(bl);
converter.endBlock(bl);
super.endBlock(bl);
}

@Override
public void startBlockContainer(BlockContainer blc) {
structureTreeBuilder.startBlockContainer(blc);
converter.startBlockContainer(blc);
super.startBlockContainer(blc);
}

@Override
public void endBlockContainer(BlockContainer blc) {
structureTreeBuilder.endBlockContainer(blc);
converter.endBlockContainer(blc);
super.endBlockContainer(blc);
}

@Override
public void startInline(Inline inl) {
structureTreeBuilder.startInline(inl);
converter.startInline(inl);
super.startInline(inl);
}

@Override
public void endInline(Inline inl) {
structureTreeBuilder.endInline(inl);
converter.endInline(inl);
super.endInline(inl);
}

@Override
public void startTable(Table tbl) {
structureTreeBuilder.startTable(tbl);
converter.startTable(tbl);
super.startTable(tbl);
}

@Override
public void endTable(Table tbl) {
structureTreeBuilder.endTable(tbl);
converter.endTable(tbl);
super.endTable(tbl);
}

@Override
public void startColumn(TableColumn tc) {
structureTreeBuilder.startColumn(tc);
converter.startColumn(tc);
super.startColumn(tc);
}

@Override
public void endColumn(TableColumn tc) {
structureTreeBuilder.endColumn(tc);
converter.endColumn(tc);
super.endColumn(tc);
}

@Override
public void startHeader(TableHeader header) {
structureTreeBuilder.startHeader(header);
converter.startHeader(header);
super.startHeader(header);
}

@Override
public void endHeader(TableHeader header) {
structureTreeBuilder.endHeader(header);
converter.endHeader(header);
super.endHeader(header);
}

@Override
public void startFooter(TableFooter footer) {
structureTreeBuilder.startFooter(footer);
converter.startFooter(footer);
super.startFooter(footer);
}

@Override
public void endFooter(TableFooter footer) {
structureTreeBuilder.endFooter(footer);
converter.endFooter(footer);
super.endFooter(footer);
}

@Override
public void startBody(TableBody body) {
structureTreeBuilder.startBody(body);
converter.startBody(body);
super.startBody(body);
}

@Override
public void endBody(TableBody body) {
structureTreeBuilder.endBody(body);
converter.endBody(body);
super.endBody(body);
}

@Override
public void startRow(TableRow tr) {
structureTreeBuilder.startRow(tr);
converter.startRow(tr);
super.startRow(tr);
}

@Override
public void endRow(TableRow tr) {
structureTreeBuilder.endRow(tr);
converter.endRow(tr);
super.endRow(tr);
}

@Override
public void startCell(TableCell tc) {
structureTreeBuilder.startCell(tc);
converter.startCell(tc);
super.startCell(tc);
}

@Override
public void endCell(TableCell tc) {
structureTreeBuilder.endCell(tc);
converter.endCell(tc);
super.endCell(tc);
}

@Override
public void startList(ListBlock lb) {
structureTreeBuilder.startList(lb);
converter.startList(lb);
super.startList(lb);
}

@Override
public void endList(ListBlock lb) {
structureTreeBuilder.endList(lb);
converter.endList(lb);
super.endList(lb);
}

@Override
public void startListItem(ListItem li) {
structureTreeBuilder.startListItem(li);
converter.startListItem(li);
super.startListItem(li);
}

@Override
public void endListItem(ListItem li) {
structureTreeBuilder.endListItem(li);
converter.endListItem(li);
super.endListItem(li);
}

@Override
public void startListLabel(ListItemLabel listItemLabel) {
structureTreeBuilder.startListLabel(listItemLabel);
converter.startListLabel(listItemLabel);
super.startListLabel(listItemLabel);
}

@Override
public void endListLabel(ListItemLabel listItemLabel) {
structureTreeBuilder.endListLabel(listItemLabel);
converter.endListLabel(listItemLabel);
super.endListLabel(listItemLabel);
}

@Override
public void startListBody(ListItemBody listItemBody) {
structureTreeBuilder.startListBody(listItemBody);
converter.startListBody(listItemBody);
super.startListBody(listItemBody);
}

@Override
public void endListBody(ListItemBody listItemBody) {
structureTreeBuilder.endListBody(listItemBody);
converter.endListBody(listItemBody);
super.endListBody(listItemBody);
}

@Override
public void startStatic(StaticContent staticContent) {
structureTreeBuilder.startStatic(staticContent);
converter.startStatic(staticContent);
super.startStatic(staticContent);
}

@Override
public void endStatic(StaticContent statisContent) {
structureTreeBuilder.endStatic(statisContent);
converter.endStatic(statisContent);
super.endStatic(statisContent);
}

@Override
public void startMarkup() {
structureTreeBuilder.startMarkup();
converter.startMarkup();
super.startMarkup();
}

@Override
public void endMarkup() {
structureTreeBuilder.endMarkup();
converter.endMarkup();
super.endMarkup();
}

@Override
public void startLink(BasicLink basicLink) {
structureTreeBuilder.startLink(basicLink);
converter.startLink(basicLink);
super.startLink(basicLink);
}

@Override
public void endLink(BasicLink basicLink) {
structureTreeBuilder.endLink(basicLink);
converter.endLink(basicLink);
super.endLink(basicLink);
}

@Override
public void image(ExternalGraphic eg) {
structureTreeBuilder.image(eg);
converter.image(eg);
super.image(eg);
}

@Override
public void pageRef() {
structureTreeBuilder.pageRef();
converter.pageRef();
super.pageRef();
}

@Override
public void startInstreamForeignObject(InstreamForeignObject ifo) {
structureTreeBuilder.startInstreamForeignObject(ifo);
converter.startInstreamForeignObject(ifo);
super.startInstreamForeignObject(ifo);
}

@Override
public void endInstreamForeignObject(InstreamForeignObject ifo) {
structureTreeBuilder.endInstreamForeignObject(ifo);
converter.endInstreamForeignObject(ifo);
super.endInstreamForeignObject(ifo);
}

@Override
public void startFootnote(Footnote footnote) {
structureTreeBuilder.startFootnote(footnote);
converter.startFootnote(footnote);
super.startFootnote(footnote);
}

@Override
public void endFootnote(Footnote footnote) {
structureTreeBuilder.endFootnote(footnote);
converter.endFootnote(footnote);
super.endFootnote(footnote);
}

@Override
public void startFootnoteBody(FootnoteBody body) {
structureTreeBuilder.startFootnoteBody(body);
converter.startFootnoteBody(body);
super.startFootnoteBody(body);
}

@Override
public void endFootnoteBody(FootnoteBody body) {
structureTreeBuilder.endFootnoteBody(body);
converter.endFootnoteBody(body);
super.endFootnoteBody(body);
}

@Override
public void startLeader(Leader l) {
structureTreeBuilder = eventSwallower;
structureTreeBuilder.startLeader(l);
converter = eventSwallower;
converter.startLeader(l);
super.startLeader(l);
}

@Override
public void endLeader(Leader l) {
structureTreeBuilder.endLeader(l);
structureTreeBuilder = actualStructureTreeBuilder;
converter.endLeader(l);
converter = foToStructureTreeEventAdapter;
super.endLeader(l);
}

@Override
public void startWrapper(Wrapper wrapper) {
structureTreeBuilder.startWrapper(wrapper);
converter.startWrapper(wrapper);
super.startWrapper(wrapper);
}

@Override
public void endWrapper(Wrapper wrapper) {
structureTreeBuilder.endWrapper(wrapper);
converter.endWrapper(wrapper);
super.endWrapper(wrapper);
}

@Override
public void character(Character c) {
structureTreeBuilder.character(c);
converter.character(c);
super.character(c);
}

@Override
public void characters(char[] data, int start, int length) {
structureTreeBuilder.characters(data, start, length);
converter.characters(data, start, length);
super.characters(data, start, length);
}

@Override
public void startExternalDocument(ExternalDocument document) {
structureTreeBuilder.startExternalDocument(document);
converter.startExternalDocument(document);
super.startExternalDocument(document);
}

@Override
public void endExternalDocument(ExternalDocument document) {
structureTreeBuilder.endExternalDocument(document);
converter.endExternalDocument(document);
super.endExternalDocument(document);
}


+ 0
- 102
src/java/org/apache/fop/accessibility/StructureTree.java View File

@@ -1,102 +0,0 @@
/*
* 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.accessibility;

import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* A reduced version of the document's FO tree, containing only its logical
* structure. Used by accessible output formats.
*/
public final class StructureTree {

private final List pageSequenceStructures = new ArrayList();

/**
* Package-private default constructor.
*/
public StructureTree() { }

private static boolean flowOrStaticContentNodes(NodeList nodes) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE) {
return false;
}
String name = node.getLocalName();
if (!(name.equals("flow") || name.equals("static-content"))) {
return false;
}
}
return true;
}

void addPageSequenceStructure(NodeList structureTree) {
assert flowOrStaticContentNodes(structureTree);
pageSequenceStructures.add(structureTree);
}

/**
* Returns the list of nodes that are the children of the given page sequence.
*
* @param index index of the page sequence, 0-based
* @return its children nodes
*/
public NodeList getPageSequence(int index) {
return (NodeList) pageSequenceStructures.get(index);
}

/**
* Returns an XML-like representation of the structure trees.
* <p>
* <strong>Note:</strong> use only for debugging purpose, as this method
* performs non-trivial operations.
* </p>
* @return a string representation of this object
*/
public String toString() {
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
Writer str = new StringWriter();
for (Iterator iter = pageSequenceStructures.iterator(); iter.hasNext();) {
NodeList nodes = (NodeList) iter.next();
for (int i = 0, c = nodes.getLength(); i < c; i++) {
t.transform(new DOMSource(nodes.item(i)), new StreamResult(str));
}
}
return str.toString();
} catch (Exception e) {
return e.toString();
}
}

}

+ 104
- 0
src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java View File

@@ -0,0 +1,104 @@
/*
* 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.accessibility;

import java.util.Locale;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.fop.fo.FOElementMapping;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.render.intermediate.IFConstants;

/**
* Converts structure tree events to SAX events.
*/
public final class StructureTree2SAXEventAdapter implements StructureTreeEventHandler {

private final ContentHandler contentHandler;

private StructureTree2SAXEventAdapter(ContentHandler currentContentHandler) {
this.contentHandler = currentContentHandler;
}

/**
* Factory method that creates a new instance.
* @param contentHandler The handler that receives SAX events
*/
public static StructureTreeEventHandler newInstance(ContentHandler contentHandler) {
return new StructureTree2SAXEventAdapter(contentHandler);
}

/** {@inheritDoc} */
public void startPageSequence(Locale locale) {
try {

contentHandler.startPrefixMapping(
InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI);
contentHandler.startPrefixMapping(
ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI);
contentHandler.startElement(IFConstants.NAMESPACE,
IFConstants.EL_STRUCTURE_TREE, IFConstants.EL_STRUCTURE_TREE,
new AttributesImpl());
} catch (SAXException e) {
throw new RuntimeException(e);
}
}

/** {@inheritDoc} */
public void endPageSequence() {
try {
contentHandler.endElement(IFConstants.NAMESPACE, IFConstants.EL_STRUCTURE_TREE,
IFConstants.EL_STRUCTURE_TREE);
contentHandler.endPrefixMapping(
ExtensionElementMapping.STANDARD_PREFIX);
contentHandler.endPrefixMapping(
InternalElementMapping.STANDARD_PREFIX);

} catch (SAXException e) {
throw new RuntimeException(e);
}
}

/** {@inheritDoc} */
public void startNode(String name, Attributes attributes) {
try {
contentHandler.startElement(FOElementMapping.URI, name,
FOElementMapping.STANDARD_PREFIX + ":" + name,
attributes);
} catch (SAXException e) {
throw new RuntimeException(e);
}
}

/** {@inheritDoc} */
public void endNode(String name) {
try {
contentHandler.endElement(FOElementMapping.URI, name,
FOElementMapping.STANDARD_PREFIX + ":" + name);
} catch (SAXException e) {
throw new RuntimeException(e);
}
}
}

+ 0
- 95
src/java/org/apache/fop/accessibility/StructureTreeBuilder.java View File

@@ -1,95 +0,0 @@
/*
* 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.accessibility;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

import org.apache.fop.util.DelegatingContentHandler;

/**
* Helper class that re-builds a structure tree from what is stored in an
* intermediate XML file (IF XML or Area Tree XML).
*/
public final class StructureTreeBuilder {

private final SAXTransformerFactory factory;

private final StructureTree structureTree = new StructureTree();

/**
* Creates a new instance.
*
* @param factory a factory internally used to build the structures of page
* sequences
*/
public StructureTreeBuilder(SAXTransformerFactory factory) {
this.factory = factory;
}

/**
* Returns the structure tree that will result from the parsing.
*
* @return the structure tree built by this object
*/
public StructureTree getStructureTree() {
return structureTree;
}

/**
* Returns a ContenHandler for parsing the structure of a new page sequence.
* It is assumed that page sequences are being parsed in the document order.
*
* @return a handler for parsing the &lt;structure-tree&gt; or
* &lt;structureTree&gt; element and its descendants
* @throws SAXException if there is an error when creating the handler
*/
public ContentHandler getHandlerForNextPageSequence() throws SAXException {
TransformerHandler structureTreeBuilder;
try {
structureTreeBuilder = factory.newTransformerHandler();
} catch (TransformerConfigurationException e) {
throw new SAXException(e);
}
final DOMResult domResult = new DOMResult();
structureTreeBuilder.setResult(domResult);
return new DelegatingContentHandler(structureTreeBuilder) {

public void characters(char[] ch, int start, int length) throws SAXException {
/*
* There's no text node in the structure tree. This is just
* whitespace => ignore
*/
}

public void endDocument() throws SAXException {
super.endDocument();
structureTree.addPageSequenceStructure(domResult.getNode().getFirstChild()
.getChildNodes());
}
};
}

}

+ 56
- 0
src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java View File

@@ -0,0 +1,56 @@
/*
* 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.accessibility;

import java.util.Locale;

import org.xml.sax.Attributes;

/**
* Receive notifications relating to the structure tree of an FO document.
* A structure tree is a reduced version of the document's FO tree, containing only the logical
* structure that is used by accessible output formats.
*/
public interface StructureTreeEventHandler {

/**
* Starts a page sequence structure tree node.
* @param locale The locale of the page sequence
*/
void startPageSequence(Locale locale);

/**
* Starts a structure tree node.
* @param name The name of the structure tree node
* @param attributes Map of node properties
*/
void startNode(String name, Attributes attributes);

/**
* Ends a structure tree node.
* @param name The name of the structure tree node
*/
void endNode(String name);

/**
* Ends a page sequence structure tree node.
*/
void endPageSequence();
}

+ 16
- 14
src/java/org/apache/fop/apps/FOUserAgent.java View File

@@ -39,7 +39,8 @@ import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.Version;
import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.accessibility.StructureTree;
import org.apache.fop.accessibility.DummyStructureTreeEventHandler;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.events.DefaultEventBroadcaster;
import org.apache.fop.events.Event;
import org.apache.fop.events.EventBroadcaster;
@@ -101,8 +102,7 @@ public class FOUserAgent {
private boolean locatorEnabled = true; // true by default (for error messages).
private boolean conserveMemoryPolicy = false;
private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster();

private StructureTree structureTree;
private StructureTreeEventHandler structureTreeEventHandler = DummyStructureTreeEventHandler.INSTANCE;

/** Producer: Metadata element for the system/software that produces
* the document. (Some renderers can store this in the document.)
@@ -173,6 +173,9 @@ public class FOUserAgent {
* @param documentHandler the document handler instance to use
*/
public void setDocumentHandlerOverride(IFDocumentHandler documentHandler) {
if (isAccessibilityEnabled()) {
setStructureTreeEventHandler(documentHandler.getStructureTreeEventHandler());
}
this.documentHandlerOverride = documentHandler;

}
@@ -674,24 +677,23 @@ public class FOUserAgent {
}

/**
* Sets the document's structure tree, for use by accessible output formats.
* Sets the document's structure tree event handler, for use by accessible
* output formats.
*
* @param structureTree a simplified version of the FO tree, retaining only
* its logical structure
* @param structureTreeEventHandler The structure tree event handler to set
*/
public void setStructureTree(StructureTree structureTree) {
this.structureTree = structureTree;
public void setStructureTreeEventHandler(StructureTreeEventHandler structureTreeEventHandler) {
this.structureTreeEventHandler = structureTreeEventHandler;
}

/**
* Returns the document's structure tree, for use by accessible output
* formats.
* Returns the document's structure tree event handler, for use by
* accessible output formats.
*
* @return a simplified version of the FO tree, retaining only its logical
* structure
* @return The structure tree event handler
*/
public StructureTree getStructureTree() {
return this.structureTree;
public StructureTreeEventHandler getStructureTreeEventHandler() {
return this.structureTreeEventHandler;
}
}


+ 9
- 0
src/java/org/apache/fop/area/AreaTreeHandler.java View File

@@ -22,6 +22,7 @@ package org.apache.fop.area;
// Java
import java.io.OutputStream;
import java.util.List;
import java.util.Locale;

import org.xml.sax.SAXException;

@@ -182,6 +183,14 @@ public class AreaTreeHandler extends FOEventHandler {
}
}

@Override
public void startRoot(Root root) {
Locale locale = root.getLocale();
if (locale != null) {
model.setDocumentLocale(locale);
}
}

/**
* finish the previous pageSequence
*/

+ 8
- 0
src/java/org/apache/fop/area/AreaTreeModel.java View File

@@ -21,6 +21,7 @@ package org.apache.fop.area;

// Java
import java.util.List;
import java.util.Locale;

import org.xml.sax.SAXException;

@@ -123,4 +124,11 @@ public class AreaTreeModel {
public PageViewport getPage(int seq, int count) {
return pageSequenceList.get(seq - 1).getPage(count);
}

/**
*
* @param locale The locale of the document
*/
public void setDocumentLocale(Locale locale) {
}
}

+ 6
- 50
src/java/org/apache/fop/area/AreaTreeParser.java View File

@@ -58,8 +58,6 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.util.QName;

import org.apache.fop.ResourceEventProducer;
import org.apache.fop.accessibility.AccessibilityEventProducer;
import org.apache.fop.accessibility.StructureTreeBuilder;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
import org.apache.fop.area.Trait.InternalLink;
@@ -69,11 +67,11 @@ import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fo.expr.PropertyException;
@@ -86,7 +84,6 @@ import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.ConversionUtils;
import org.apache.fop.util.DefaultErrorListener;
import org.apache.fop.util.DelegatingContentHandler;
import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;

@@ -166,27 +163,8 @@ public class AreaTreeParser {
private DOMImplementation domImplementation;
private Locator locator;


private StructureTreeBuilder structureTreeBuilder;

private ContentHandler structureTreeBuilderWrapper;

private Attributes pageSequenceAttributes;

private final class StructureTreeBuilderWrapper extends DelegatingContentHandler {

private StructureTreeBuilderWrapper()
throws SAXException {
super(structureTreeBuilder.getHandlerForNextPageSequence());
}

public void endDocument() throws SAXException {
super.endDocument();
startAreaTreeElement("pageSequence", pageSequenceAttributes);
pageSequenceAttributes = null;
}
}

public Handler(AreaTreeModel treeModel, FOUserAgent userAgent,
ElementMappingRegistry elementMappingRegistry) {
this.treeModel = treeModel;
@@ -223,11 +201,6 @@ public class AreaTreeParser {
makers.put("bookmarkTree", new BookmarkTreeMaker());
makers.put("bookmark", new BookmarkMaker());
makers.put("destination", new DestinationMaker());

if (userAgent.isAccessibilityEnabled()) {
structureTreeBuilder = new StructureTreeBuilder(tFactory);
userAgent.setStructureTree(structureTreeBuilder.getStructureTree());
}
}

private Area findAreaType(Class clazz) {
@@ -308,32 +281,15 @@ public class AreaTreeParser {
} else {
boolean handled = true;
if ("".equals(uri)) {
if (localName.equals("pageSequence") && userAgent.isAccessibilityEnabled()) {
structureTreeBuilderWrapper = new StructureTreeBuilderWrapper();
pageSequenceAttributes = new AttributesImpl(attributes);
} else if (localName.equals("structureTree")) {
if (userAgent.isAccessibilityEnabled()) {
delegate = structureTreeBuilderWrapper;
} else {
/* Delegate to a handler that does nothing */
delegate = new DefaultHandler();
}
if (localName.equals("structureTree")) {

/* The area tree parser no longer supports the structure tree. */
delegate = new DefaultHandler();

delegateStack.push(qName);
delegate.startDocument();
delegate.startElement(uri, localName, qName, attributes);
} else {
if (pageSequenceAttributes != null) {
/*
* This means that no structure-element tag was
* found in the XML, otherwise a
* StructureTreeBuilderWrapper object would have
* been created, which would have reset the
* pageSequenceAttributes field.
*/
AccessibilityEventProducer.Provider
.get(userAgent.getEventBroadcaster())
.noStructureTreeInXML(this);
}
handled = startAreaTreeElement(localName, attributes);
}
} else {

+ 6
- 0
src/java/org/apache/fop/area/RenderPagesModel.java View File

@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.xml.sax.SAXException;

@@ -83,6 +84,11 @@ public class RenderPagesModel extends AreaTreeModel {
}
}

@Override
public void setDocumentLocale(Locale locale) {
renderer.setDocumentLocale(locale);
}

/** {@inheritDoc} */
@Override
public void startPageSequence(PageSequence pageSequence) {

+ 11
- 0
src/java/org/apache/fop/fo/DelegatingFOEventHandler.java View File

@@ -50,6 +50,7 @@ import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fonts.FontInfo;

@@ -90,6 +91,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler {
delegate.endDocument();
}

@Override
public void startRoot(Root root) {
delegate.startRoot(root);
}

@Override
public void endRoot(Root root) {
delegate.endRoot(root);
}

@Override
public void startPageSequence(PageSequence pageSeq) {
delegate.startPageSequence(pageSeq);

+ 4
- 1
src/java/org/apache/fop/fo/FOElementMapping.java View File

@@ -32,6 +32,9 @@ public class FOElementMapping extends ElementMapping {
/** The XSL-FO namespace URI */
public static final String URI = "http://www.w3.org/1999/XSL/Format";

/** Standard prefix */
public static final String STANDARD_PREFIX = "fo";

/**
* Basic constructor; inititializes the namespace URI for the fo: namespace
*/
@@ -141,7 +144,7 @@ public class FOElementMapping extends ElementMapping {

/** {@inheritDoc} */
public String getStandardPrefix() {
return "fo";
return STANDARD_PREFIX;
}

/** {@inheritDoc} */

+ 7
- 0
src/java/org/apache/fop/fo/FOEventHandler.java View File

@@ -50,6 +50,7 @@ import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fonts.FontEventAdapter;
import org.apache.fop.fonts.FontInfo;
@@ -117,6 +118,12 @@ public abstract class FOEventHandler {
public void endDocument() throws SAXException {
}

public void startRoot(Root root) {
}

public void endRoot(Root root) {
}

/**
*
* @param pageSeq PageSequence that is starting.

+ 3
- 5
src/java/org/apache/fop/fo/FOTreeBuilder.java View File

@@ -33,8 +33,7 @@ import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.util.QName;

import org.apache.fop.accessibility.StructureTree;
import org.apache.fop.accessibility.StructureTreeBuildingFOEventHandler;
import org.apache.fop.accessibility.FO2StructureTreeConverter;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FormattingResults;
@@ -107,9 +106,8 @@ public class FOTreeBuilder extends DefaultHandler {
foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
foUserAgent, outputFormat, stream);
if (userAgent.isAccessibilityEnabled()) {
StructureTree structureTree = new StructureTree();
foEventHandler = new StructureTreeBuildingFOEventHandler(structureTree, foEventHandler);
userAgent.setStructureTree(structureTree);
foEventHandler = new FO2StructureTreeConverter(
foUserAgent.getStructureTreeEventHandler(), foEventHandler);
}
builderContext = new FOTreeBuilderContext();
builderContext.setPropertyListMaker(new PropertyListMaker() {

+ 26
- 0
src/java/org/apache/fop/fo/pagination/Root.java View File

@@ -21,6 +21,7 @@ package org.apache.fop.fo.pagination;

// java
import java.util.List;
import java.util.Locale;

import org.xml.sax.Locator;

@@ -52,6 +53,7 @@ public class Root extends FObj implements CommonAccessibilityHolder {
private BookmarkTree bookmarkTree = null;
private List<Destination> destinationList;
private List<PageSequence> pageSequences;
private Locale locale;

// temporary until above list populated
private boolean pageSequenceFound = false;
@@ -88,6 +90,24 @@ public class Root extends FObj implements CommonAccessibilityHolder {
super.bind(pList);
commonAccessibility = CommonAccessibility.getInstance(pList);
mediaUsage = pList.get(PR_MEDIA_USAGE).getEnum();
String language = pList.get(PR_LANGUAGE).getString();
String country = pList.get(PR_COUNTRY).getString();
if (isLocalePropertySet(language)) {
if (isLocalePropertySet(country)) {
locale = new Locale(language, country);
} else {
locale = new Locale(language);
}
}
}

private boolean isLocalePropertySet(String property) {
return property != null && !property.equals("none");
}

/** {@inheritDoc} */
protected void startOfNode() throws FOPException {
foEventHandler.startRoot(this);
}

/** {@inheritDoc} */
@@ -96,6 +116,7 @@ public class Root extends FObj implements CommonAccessibilityHolder {
missingChildElementError("(layout-master-set, declarations?, "
+ "bookmark-tree?, (page-sequence|fox:external-document)+)");
}
foEventHandler.endRoot(this);
}

/**
@@ -343,4 +364,9 @@ public class Root extends FObj implements CommonAccessibilityHolder {
return FO_ROOT;
}


public Locale getLocale() {
return locale;
}

}

+ 0
- 19
src/java/org/apache/fop/pdf/PDFDocument.java View File

@@ -353,25 +353,6 @@ public class PDFDocument {
return this.root;
}

/**
* Makes sure a Lang entry has been set on the document catalog, setting it
* to a default value if necessary. When accessibility is enabled the
* language must be specified for any text element in the document.
*/
public void enforceLanguageOnRoot() {
if (root.getLanguage() == null) {
String fallbackLanguage;
if (getProfile().getPDFAMode().isPDFA1LevelA()) {
//According to Annex B of ISO-19005-1:2005(E), section B.2
fallbackLanguage = "x-unknown";
} else {
//No language has been set on the first page-sequence, so fall back to "en".
fallbackLanguage = "en";
}
root.setLanguage(fallbackLanguage);
}
}

/**
* Get the {@link PDFInfo} object for this document.
*

+ 5
- 1
src/java/org/apache/fop/pdf/PDFProfile.java View File

@@ -133,8 +133,12 @@ public class PDFProfile {

//---------=== Info and validation methods ===---------

private String format(String pattern, Object[] args) {
return MessageFormat.format(pattern, args);
}

private String format(String pattern, Object arg) {
return MessageFormat.format(pattern, new Object[] {arg});
return format(pattern, new Object[] {arg});
}

/** Checks if encryption is allowed. */

+ 11
- 3
src/java/org/apache/fop/pdf/PDFRoot.java View File

@@ -21,6 +21,9 @@ package org.apache.fop.pdf;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;

import org.apache.fop.util.LanguageTags;

/**
* Class representing a Root (/Catalog) object.
@@ -69,6 +72,7 @@ public class PDFRoot extends PDFDictionary {
setObjectNumber(objnum);
put("Type", new PDFName("Catalog"));
setRootPages(pages);
setLanguage("x-unknown");
}

/** {@inheritDoc} */
@@ -254,10 +258,14 @@ public class PDFRoot extends PDFDictionary {
* Sets the language identifier of the document.
* @param lang the language identifier of the document.
*/
public void setLanguage(String lang) {
if (lang == null) {
throw new NullPointerException("lang must not be null");
public void setLanguage(Locale locale) {
if (locale == null) {
throw new NullPointerException("locale must not be null");
}
setLanguage(LanguageTags.toLanguageTag(locale));
}

private void setLanguage(String lang) {
put("Lang", lang);
}


+ 2
- 2
src/java/org/apache/fop/pdf/PDFStructElem.java View File

@@ -21,7 +21,7 @@ package org.apache.fop.pdf;

import java.util.Locale;

import org.apache.fop.util.XMLUtil;
import org.apache.fop.util.LanguageTags;

/**
* Class representing a PDF Structure Element.
@@ -145,7 +145,7 @@ public class PDFStructElem extends PDFDictionary {
* @param language a value for the Lang entry
*/
public void setLanguage(Locale language) {
setLanguage(XMLUtil.toRFC3066(language));
setLanguage(LanguageTags.toLanguageTag(language));
}

/**

+ 6
- 1
src/java/org/apache/fop/render/AbstractRenderer.java View File

@@ -27,6 +27,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.w3c.dom.Document;
@@ -61,11 +62,11 @@ import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.FontInfo;
@@ -152,6 +153,10 @@ public abstract class AbstractRenderer
return false;
}

/** {@inheritDoc} */
public void setDocumentLocale(Locale locale) {
}

/**
* {@inheritDoc}
*/

+ 7
- 0
src/java/org/apache/fop/render/Renderer.java View File

@@ -22,6 +22,7 @@ package org.apache.fop.render;
// Java
import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
@@ -115,6 +116,12 @@ public interface Renderer {
*/
boolean supportsOutOfOrder();

/**
*
* @param locale Locale of the language
*/
void setDocumentLocale(Locale locale);

/**
* Tells the renderer to process an item not explicitly placed on the
* document (e.g., PDF bookmarks). Note - not all renderers will process

+ 16
- 1
src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java View File

@@ -19,6 +19,13 @@

package org.apache.fop.render.intermediate;

import java.util.Locale;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.accessibility.DummyStructureTreeEventHandler;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.FOUserAgent;

/**
@@ -52,6 +59,11 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler {
return getContext().getUserAgent();
}

/** {@inheritDoc} */
public StructureTreeEventHandler getStructureTreeEventHandler() {
return DummyStructureTreeEventHandler.INSTANCE;
}

/** {@inheritDoc} */
public IFDocumentNavigationHandler getDocumentNavigationHandler() {
return null; //By default, this is not supported
@@ -65,6 +77,10 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler {
}
}

/** {@inheritDoc} */
public void setDocumentLocale(Locale locale) {
}

/** {@inheritDoc} */
public void startDocumentHeader() throws IFException {
//nop
@@ -104,5 +120,4 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler {
public void endPageTrailer() throws IFException {
//nop
}

}

+ 1
- 0
src/java/org/apache/fop/render/intermediate/IFConstants.java View File

@@ -39,6 +39,7 @@ public interface IFConstants extends XMLConstants {
String EL_HEADER = "header";
/** element name trailer */
String EL_TRAILER = "trailer";
String EL_LOCALE = "locale";
/** element name page-sequence */
String EL_PAGE_SEQUENCE = "page-sequence";
/** element name page */

+ 13
- 3
src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java View File

@@ -20,9 +20,11 @@
package org.apache.fop.render.intermediate;

import java.awt.Dimension;
import java.util.Locale;

import javax.xml.transform.Result;

import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fonts.FontInfo;

/**
@@ -32,6 +34,7 @@ import org.apache.fop.fonts.FontInfo;
* <p>
* <pre>
* startDocument()
* [setDocumentLocale()]
* startDocumentHeader()
* [handleExtension()]*
* endDocumentHeader()
@@ -117,6 +120,11 @@ public interface IFDocumentHandler {
*/
IFDocumentHandlerConfigurator getConfigurator();

/**
* @return the structure tree builder
*/
StructureTreeEventHandler getStructureTreeEventHandler();

/**
* Returns a document navigation handler if this feature is supported.
* @return the document navigation handler or null if not supported
@@ -151,6 +159,11 @@ public interface IFDocumentHandler {
*/
void endDocument() throws IFException;

/**
* @param locale Locale of the document.
*/
void setDocumentLocale(Locale locale);

/**
* Indicates the start of the document header. This method is called right after the
* {@link #startDocument()} method. Extensions sent to this painter between
@@ -261,7 +274,4 @@ public interface IFDocumentHandler {
* @throws IFException if an error occurs while handling this event
*/
void handleExtensionObject(Object extension) throws IFException;

//TODO Prototype the following:
//ContentHandler handleExtension() throws Exception
}

+ 47
- 21
src/java/org/apache/fop/render/intermediate/IFParser.java View File

@@ -25,6 +25,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

@@ -48,7 +49,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.util.QName;

import org.apache.fop.accessibility.AccessibilityEventProducer;
import org.apache.fop.accessibility.StructureTreeBuilder;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
@@ -62,7 +63,7 @@ import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.DOMBuilderContentHandlerFactory;
import org.apache.fop.util.DefaultErrorListener;
import org.apache.fop.util.DelegatingContentHandler;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLUtil;

/**
@@ -153,24 +154,40 @@ public class IFParser implements IFConstants {

private ContentHandler navParser;

private StructureTreeBuilder structureTreeBuilder;

private ContentHandler structureTreeBuilderWrapper;
private ContentHandler structureTreeHandler;

private Attributes pageSequenceAttributes;

private final class StructureTreeBuilderWrapper extends DelegatingContentHandler {
private final class StructureTreeHandler extends DefaultHandler {

private StructureTreeBuilderWrapper()
throws SAXException {
super(structureTreeBuilder.getHandlerForNextPageSequence());
private final StructureTreeEventHandler structureTreeEventHandler;

private StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler,
Locale pageSequenceLanguage) throws SAXException {
this.structureTreeEventHandler = structureTreeEventHandler;
structureTreeEventHandler.startPageSequence(pageSequenceLanguage);
}

public void endDocument() throws SAXException {
super.endDocument();
startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes);
pageSequenceAttributes = null;
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (!"structure-tree".equals(localName)) {
structureTreeEventHandler.startNode(localName, attributes);
}
}

@Override
public void endElement(String uri, String localName, String arqNameg2)
throws SAXException {
if (!"structure-tree".equals(localName)) {
structureTreeEventHandler.endNode(localName);
}
}
}

public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent,
@@ -180,6 +197,7 @@ public class IFParser implements IFConstants {
this.elementMappingRegistry = elementMappingRegistry;
elementHandlers.put(EL_DOCUMENT, new DocumentHandler());
elementHandlers.put(EL_HEADER, new DocumentHeaderHandler());
elementHandlers.put(EL_LOCALE, new LocaleHandler());
elementHandlers.put(EL_TRAILER, new DocumentTrailerHandler());
elementHandlers.put(EL_PAGE_SEQUENCE, new PageSequenceHandler());
elementHandlers.put(EL_PAGE, new PageHandler());
@@ -197,11 +215,6 @@ public class IFParser implements IFConstants {
elementHandlers.put(EL_LINE, new LineHandler());
elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler());
elementHandlers.put(EL_IMAGE, new ImageHandler());

if (userAgent.isAccessibilityEnabled()) {
structureTreeBuilder = new StructureTreeBuilder(tFactory);
userAgent.setStructureTree(structureTreeBuilder.getStructureTree());
}
}

private void establishForeignAttributes(Map<QName, String> foreignAttributes) {
@@ -231,10 +244,13 @@ public class IFParser implements IFConstants {
if (NAMESPACE.equals(uri)) {
if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) {
pageSequenceAttributes = new AttributesImpl(attributes);
structureTreeBuilderWrapper = new StructureTreeBuilderWrapper();
Locale language = getLanguage(attributes);
structureTreeHandler = new StructureTreeHandler(
userAgent.getStructureTreeEventHandler(), language);

} else if (localName.equals(EL_STRUCTURE_TREE)) {
if (userAgent.isAccessibilityEnabled()) {
delegate = structureTreeBuilderWrapper;
delegate = structureTreeHandler;
} else {
/* Delegate to a handler that does nothing */
delegate = new DefaultHandler();
@@ -299,6 +315,11 @@ public class IFParser implements IFConstants {
}
}

private static Locale getLanguage(Attributes attributes) {
String xmllang = attributes.getValue(XML_NAMESPACE, "lang");
return (xmllang == null) ? null : LanguageTags.toLocale(xmllang);
}

private boolean startIFElement(String localName, Attributes attributes)
throws SAXException {
lastAttributes = new AttributesImpl(attributes);
@@ -413,6 +434,12 @@ public class IFParser implements IFConstants {

}

private class LocaleHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.setDocumentLocale(getLanguage(attributes));
}
}

private class DocumentTrailerHandler extends AbstractElementHandler {

public void startElement(Attributes attributes) throws IFException {
@@ -429,10 +456,9 @@ public class IFParser implements IFConstants {

public void startElement(Attributes attributes) throws IFException {
String id = attributes.getValue("id");
String xmllang = attributes.getValue(XML_NAMESPACE, "lang");
if (xmllang != null) {
documentHandler.getContext().setLanguage(
XMLUtil.convertRFC3066ToLocale(xmllang));
Locale language = getLanguage(attributes);
if (language != null) {
documentHandler.getContext().setLanguage(language);
}
Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
establishForeignAttributes(foreignAttributes);

+ 12
- 2
src/java/org/apache/fop/render/intermediate/IFRenderer.java View File

@@ -51,6 +51,7 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;

import org.apache.fop.Version;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.Area;
import org.apache.fop.area.AreaTreeObject;
@@ -70,10 +71,10 @@ import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.extensions.ExtensionAttachment;
@@ -227,7 +228,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
*/
protected IFDocumentHandler createDefaultDocumentHandler() {
IFSerializer serializer = new IFSerializer();
serializer.setContext(new IFContext(getUserAgent()));
FOUserAgent userAgent = getUserAgent();
serializer.setContext(new IFContext(userAgent));
if (userAgent.isAccessibilityEnabled()) {
userAgent.setStructureTreeEventHandler(serializer.getStructureTreeEventHandler());
}
return serializer;
}

@@ -294,6 +299,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
log.debug("Rendering finished.");
}

@Override
public void setDocumentLocale(Locale locale) {
documentHandler.setDocumentLocale(locale);
}

/** {@inheritDoc} */
public void processOffDocumentItem(OffDocumentItem odi) {
if (odi instanceof DestinationData) {

+ 29
- 19
src/java/org/apache/fop/render/intermediate/IFSerializer.java View File

@@ -31,16 +31,13 @@ import java.util.Locale;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.xmlgraphics.util.QName;
import org.apache.xmlgraphics.util.XMLizable;

import org.apache.fop.accessibility.StructureTree;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.PrintRendererConfigurator;
import org.apache.fop.render.RenderingContext;
@@ -54,9 +51,11 @@ import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.DOM2SAX;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;


/**
* IFPainter implementation that serializes the intermediate format to XML.
*/
@@ -71,11 +70,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler

private String currentID = "";

/**
* Default constructor.
*/
public IFSerializer() {
}
private IFStructureTreeBuilder structureTreeBuilder;

/** {@inheritDoc} */
@Override
@@ -150,6 +145,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
}

@Override
public StructureTreeEventHandler getStructureTreeEventHandler() {
if (structureTreeBuilder == null) {
structureTreeBuilder = new IFStructureTreeBuilder();
}
return structureTreeBuilder;
}

/** {@inheritDoc} */
@Override
public void startDocument() throws IFException {
@@ -166,6 +169,19 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
}

@Override
public void setDocumentLocale(Locale locale) {
AttributesImpl atts = new AttributesImpl();
atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
LanguageTags.toLanguageTag(locale));
try {
handler.startElement(EL_LOCALE, atts);
handler.endElement(EL_LOCALE);
} catch (SAXException e) {
throw new RuntimeException("Unable to create the " + EL_LOCALE + " element.", e);
}
}

/** {@inheritDoc} */
@Override
public void startDocumentHeader() throws IFException {
@@ -227,20 +243,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
Locale lang = getContext().getLanguage();
if (lang != null) {
atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
XMLUtil.toRFC3066(lang));
LanguageTags.toLanguageTag(lang));
}
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
addForeignAttributes(atts);
handler.startElement(EL_PAGE_SEQUENCE, atts);
if (this.getUserAgent().isAccessibilityEnabled()) {
StructureTree structureTree = getUserAgent().getStructureTree();
handler.startElement(EL_STRUCTURE_TREE); // add structure tree
NodeList nodes = structureTree.getPageSequence(pageSequenceIndex++);
for (int i = 0, n = nodes.getLength(); i < n; i++) {
Node node = nodes.item(i);
new DOM2SAX(handler).writeFragment(node);
}
handler.endElement(EL_STRUCTURE_TREE);
assert (structureTreeBuilder != null);
structureTreeBuilder.replayEventsForPageSequence(handler, pageSequenceIndex++);
}
} catch (SAXException e) {
throw new IFException("SAX error in startPageSequence()", e);
@@ -250,6 +260,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
/** {@inheritDoc} */
public void endPageSequence() throws IFException {
try {

handler.endElement(EL_PAGE_SEQUENCE);
} catch (SAXException e) {
throw new IFException("SAX error in endPageSequence()", e);
@@ -806,5 +817,4 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
throw new IFException("SAX error serializing object", e);
}
}

}

+ 3
- 0
src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java View File

@@ -31,6 +31,9 @@ public class IFSerializerMaker extends AbstractIFDocumentHandlerMaker {
public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
IFSerializer handler = new IFSerializer();
handler.setContext(new IFContext(ua));
if (ua.isAccessibilityEnabled()) {
ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler());
}
return handler;
}


+ 190
- 0
src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java View File

@@ -0,0 +1,190 @@
/*
* 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.render.intermediate;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import org.apache.fop.accessibility.StructureTree2SAXEventAdapter;
import org.apache.fop.accessibility.StructureTreeEventHandler;

/**
* Saves structure tree events as SAX events in order to replay them when it's
* time to stream the structure tree to the output.
*/
final class IFStructureTreeBuilder implements StructureTreeEventHandler {

private StructureTreeEventHandler delegate;

private final List<SAXEventRecorder> pageSequenceEventRecorders = new ArrayList<SAXEventRecorder>();

/**
* Replay SAX events for a page sequence.
* @param handler The handler that receives SAX events
* @param pageSequenceIndex The index of the page sequence
* @throws SAXException
*/
public void replayEventsForPageSequence(ContentHandler handler,
int pageSequenceIndex) throws SAXException {
pageSequenceEventRecorders.get(pageSequenceIndex).replay(handler);
}

/** {@inheritDoc} */
public void startPageSequence(Locale locale) {
SAXEventRecorder eventRecorder = new SAXEventRecorder();
pageSequenceEventRecorders.add(eventRecorder);
delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder);
delegate.startPageSequence(locale);
}

/** {@inheritDoc} */
public void endPageSequence() {
delegate.endPageSequence();
}

/** {@inheritDoc} */
public void startNode(String name, Attributes attributes) {
delegate.startNode(name, attributes);
}

/** {@inheritDoc} */
public void endNode(String name) {
delegate.endNode(name);
}

/** A SAX handler that records events to replay them later. */
static class SAXEventRecorder extends DefaultHandler {

private final List<SAXEventRecorder.Event> events = new ArrayList<SAXEventRecorder.Event>();

private abstract static class Event {
abstract void replay(ContentHandler handler) throws SAXException;
}

private abstract static class Element extends SAXEventRecorder.Event {

protected final String uri;
protected final String localName;
protected final String qName;

private Element(String uri, String localName, String qName) {
this.uri = uri;
this.localName = localName;
this.qName = qName;
}
}

private static final class StartElement extends SAXEventRecorder.Element {

private final Attributes attributes;

private StartElement(String uri, String localName, String qName,
Attributes attributes) {
super(uri, localName, qName);
this.attributes = attributes;
}

@Override
void replay(ContentHandler handler) throws SAXException {
handler.startElement(uri, localName, qName, attributes);
}
}

private static final class EndElement extends SAXEventRecorder.Element {

private EndElement(String uri, String localName, String qName) {
super(uri, localName, qName);
}

@Override
void replay(ContentHandler handler) throws SAXException {
handler.endElement(uri, localName, qName);
}
}

private static final class StartPrefixMapping extends SAXEventRecorder.Event {

protected final String prefix;
protected final String uri;

private StartPrefixMapping(String prefix, String uri) {
this.prefix = prefix;
this.uri = uri;
}

@Override
void replay(ContentHandler handler) throws SAXException {
handler.startPrefixMapping(prefix, uri);
}
}

private static final class EndPrefixMapping extends SAXEventRecorder.Event {

protected final String prefix;

private EndPrefixMapping(String prefix) {
this.prefix = prefix;
}

@Override
void replay(ContentHandler handler) throws SAXException {
handler.endPrefixMapping(prefix);
}
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
events.add(new StartElement(uri, localName, qName, attributes));
};

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
events.add(new EndElement(uri, localName, qName));
};

@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
events.add(new StartPrefixMapping(prefix, uri));
};

@Override
public void endPrefixMapping(String prefix) throws SAXException {
events.add(new EndPrefixMapping(prefix));
};

/**
* Replays the recorded events.
*
* @param handler {@code ContentHandler} to replay events on
*/
public void replay(ContentHandler handler) throws SAXException {
for (SAXEventRecorder.Event e : events) {
e.replay(handler);
}
}
}
}

+ 15
- 1
src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java View File

@@ -20,9 +20,12 @@
package org.apache.fop.render.intermediate.util;

import java.awt.Dimension;
import java.util.Locale;

import javax.xml.transform.Result;

import org.apache.fop.accessibility.DummyStructureTreeEventHandler;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandler;
@@ -93,6 +96,11 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler {
return this.delegate.getDocumentNavigationHandler();
}

/** {@inheritDoc} */
public StructureTreeEventHandler getStructureTreeEventHandler() {
return DummyStructureTreeEventHandler.INSTANCE;
}

/** {@inheritDoc} */
public void setResult(Result result) throws IFException {
this.delegate.setResult(result);
@@ -103,6 +111,12 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler {
this.delegate.startDocument();
}

/** {@inheritDoc} */
public void setDocumentLocale(Locale locale) {
this.delegate.setDocumentLocale(locale);

}

/** {@inheritDoc} */
public void startDocumentHeader() throws IFException {
this.delegate.startDocumentHeader();
@@ -184,4 +198,4 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler {
this.delegate.handleExtensionObject(extension);
}

}
}

+ 19
- 16
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java View File

@@ -22,8 +22,6 @@ package org.apache.fop.render.pdf;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Node;

import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
@@ -37,9 +35,9 @@ final class FOToPDFRoleMap {
/**
* Standard structure types defined by the PDF Reference, Fourth Edition (PDF 1.5).
*/
private static final Map STANDARD_STRUCTURE_TYPES = new HashMap();
private static final Map<String, PDFName> STANDARD_STRUCTURE_TYPES = new HashMap<String, PDFName>();

private static final Map DEFAULT_MAPPINGS = new java.util.HashMap();
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;
@@ -172,7 +170,7 @@ final class FOToPDFRoleMap {
* @return the structure type or null if no match could be found
*/
public static PDFName mapFormattingObject(String fo, PDFObject parent) {
Mapper mapper = (Mapper)DEFAULT_MAPPINGS.get(fo);
Mapper mapper = (Mapper) DEFAULT_MAPPINGS.get(fo);
if (mapper != null) {
return mapper.getStructureType(parent);
} else {
@@ -180,27 +178,32 @@ final class FOToPDFRoleMap {
}
}

public static PDFName mapFormattingObject(Node fo, PDFObject parent,
EventBroadcaster eventBroadcaster) {
/**
* Maps a Formatting Object to a PDFName representing the associated structure type.
* @param fo the formatting object's local name
* @param role the value of the formatting object's role property
* @param parent the parent of the structure element to be mapped
* @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,
PDFObject parent, EventBroadcaster eventBroadcaster) {
PDFName type = null;
Node role = fo.getAttributes().getNamedItemNS(null, "role");
if (role == null) {
type = mapFormattingObject(fo.getLocalName(), parent);
type = mapFormattingObject(fo, parent);
} else {
String customType = role.getNodeValue();
type = (PDFName) STANDARD_STRUCTURE_TYPES.get(customType);
type = (PDFName) STANDARD_STRUCTURE_TYPES.get(role);
if (type == null) {
String foName = fo.getLocalName();
type = mapFormattingObject(foName, parent);
type = mapFormattingObject(fo, parent);
PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo,
foName, customType, type.toString().substring(1));
fo, role, type.toString().substring(1));
}
}
assert type != null;
return type;
}

private static interface Mapper {
private interface Mapper {
PDFName getStructureType(PDFObject parent);
}

@@ -222,7 +225,7 @@ final class FOToPDFRoleMap {

public PDFName getStructureType(PDFObject parent) {
PDFStructElem grandParent = (PDFStructElem)
((PDFStructElem)parent).getParentStructElem();
((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())) {

+ 33
- 26
src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java View File

@@ -25,15 +25,16 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.w3c.dom.NodeList;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.xmp.Metadata;

import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.pdf.PDFAnnotList;
@@ -45,28 +46,26 @@ import org.apache.fop.render.extensions.prepress.PageBoundaries;
import org.apache.fop.render.extensions.prepress.PageScale;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
import org.apache.fop.util.XMLUtil;

/**
* {@link IFDocumentHandler} implementation that produces PDF.
* {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF.
*/
public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {

/** logging instance */
private static Log log = LogFactory.getLog(PDFDocumentHandler.class);

private int pageSequenceIndex;

private boolean accessEnabled;

private PDFLogicalStructureHandler logicalStructureHandler;

private PDFStructureTreeBuilder structureTreeBuilder;

/** the PDF Document being created */
protected PDFDocument pdfDoc;

@@ -92,8 +91,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
protected PageReference currentPageRef;

/** Used for bookmarks/outlines. */
protected Map<Integer, PageReference> pageReferences
= new java.util.HashMap<Integer, PageReference>();
protected Map<Integer, PageReference> pageReferences = new HashMap<Integer, PageReference>();

private final PDFDocumentNavigationHandler documentNavigationHandler
= new PDFDocumentNavigationHandler(this);
@@ -145,15 +143,23 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream);
this.accessEnabled = getUserAgent().isAccessibilityEnabled();
if (accessEnabled) {
pdfDoc.getRoot().makeTagged();
logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc,
getUserAgent().getEventBroadcaster());
setupAccessibility();
}
} catch (IOException e) {
throw new IFException("I/O error in startDocument()", e);
}
}

private void setupAccessibility() {
pdfDoc.getRoot().makeTagged();
logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
// TODO this is ugly. All the necessary information should be available
// at creation time in order to enforce immutability
structureTreeBuilder.setPdfFactory(pdfDoc.getFactory());
structureTreeBuilder.setLogicalStructureHandler(logicalStructureHandler);
structureTreeBuilder.setEventBroadcaster(getUserAgent().getEventBroadcaster());
}

/** {@inheritDoc} */
public void endDocumentHeader() throws IFException {
pdfUtil.generateDefaultXMPMetadata();
@@ -178,18 +184,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {

/** {@inheritDoc} */
public void startPageSequence(String id) throws IFException {
//TODO page sequence title

if (this.pdfDoc.getRoot().getLanguage() == null
&& getContext().getLanguage() != null) {
//No document-level language set, so we use the first page-sequence's language
this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage()));
}

if (accessEnabled) {
NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++);
logicalStructureHandler.processStructureTree(nodes, getContext().getLanguage());
}
//nop
}

/** {@inheritDoc} */
@@ -289,9 +284,9 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void handleExtensionObject(Object extension) throws IFException {
if (extension instanceof XMPMetadata) {
pdfUtil.renderXMPMetadata((XMPMetadata)extension);
pdfUtil.renderXMPMetadata((XMPMetadata) extension);
} else if (extension instanceof Metadata) {
XMPMetadata wrapper = new XMPMetadata(((Metadata)extension));
XMPMetadata wrapper = new XMPMetadata(((Metadata) extension));
pdfUtil.renderXMPMetadata(wrapper);
} else if (extension instanceof PDFEmbeddedFileExtensionAttachment) {
PDFEmbeddedFileExtensionAttachment embeddedFile
@@ -307,6 +302,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
}
}

/** {@inheritDoc} */
public void setDocumentLocale(Locale locale) {
pdfDoc.getRoot().setLanguage(locale);
}

PageReference getPageReference(int pageIndex) {
return this.pageReferences.get(Integer.valueOf(pageIndex));
}
@@ -332,4 +332,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
}
}

@Override
public StructureTreeEventHandler getStructureTreeEventHandler() {
if (structureTreeBuilder == null) {
structureTreeBuilder = new PDFStructureTreeBuilder();
}
return structureTreeBuilder;
}
}

+ 3
- 0
src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java View File

@@ -36,6 +36,9 @@ public class PDFDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
PDFDocumentHandler handler = new PDFDocumentHandler();
handler.setContext(new IFContext(ua));
if (ua.isAccessibilityEnabled()) {
ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler());
}
return handler;
}


+ 9
- 63
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java View File

@@ -23,12 +23,6 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
@@ -53,12 +47,10 @@ class PDFLogicalStructureHandler {

private final PDFDocument pdfDoc;

private final EventBroadcaster eventBroadcaster;

/**
* Map of references to the corresponding structure elements.
*/
private final Map structTreeMap = new HashMap();
private final Map<String, PDFStructElem> structTreeMap = new HashMap<String, PDFStructElem>();

private final PDFParentTree parentTree = new PDFParentTree();

@@ -108,23 +100,16 @@ class PDFLogicalStructureHandler {
*
* @param pdfDoc a document
*/
PDFLogicalStructureHandler(PDFDocument pdfDoc, EventBroadcaster eventBroadcaster) {
PDFLogicalStructureHandler(PDFDocument pdfDoc) {
this.pdfDoc = pdfDoc;
this.eventBroadcaster = eventBroadcaster;
PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree);
rootStructureElement = pdfDoc.getFactory().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot);
structTreeRoot.addKid(rootStructureElement);
}

/**
* Converts the given structure tree into PDF.
*
* @param structureTree the structure tree of the current page sequence
* @param language language set on the page sequence
*/
void processStructureTree(NodeList structureTree, Locale language) {
pdfDoc.enforceLanguageOnRoot();

PDFStructElem createPageSequence(Locale language) {
PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement),
rootStructureElement);
@@ -132,50 +117,7 @@ class PDFLogicalStructureHandler {
if (language != null) {
structElemPart.setLanguage(language);
}

for (int i = 0, n = structureTree.getLength(); i < n; i++) {
Node node = structureTree.item(i);
assert node.getLocalName().equals("flow")
|| node.getLocalName().equals("static-content");
PDFStructElem structElemSect = pdfDoc.getFactory().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(node.getLocalName(), structElemPart),
structElemPart);
structElemPart.addKid(structElemSect);
NodeList childNodes = node.getChildNodes();
for (int j = 0, m = childNodes.getLength(); j < m; j++) {
processNode(childNodes.item(j), structElemSect, true);
}
}
}

private void processNode(Node node, PDFStructElem parent, boolean addKid) {
PDFStructElem structElem = pdfDoc.getFactory().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(node, parent, eventBroadcaster), parent);
// TODO necessary? If a page-sequence is empty (e.g., contains a single
// empty fo:block), should the block still be added to the structure
// tree? This is not being done for descendant empty elements...
if (addKid) {
parent.addKid(structElem);
}
String nodeName = node.getLocalName();
if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) {
Node altTextNode = node.getAttributes().getNamedItemNS(
ExtensionElementMapping.URI, "alt-text");
if (altTextNode != null) {
structElem.put("Alt", altTextNode.getNodeValue());
} else {
structElem.put("Alt", "No alternate text specified");
}
}
Node attr = node.getAttributes().getNamedItemNS(InternalElementMapping.URI, "ptr");
if (attr != null) {
String ptr = attr.getNodeValue();
structTreeMap.put(ptr, structElem);
}
NodeList nodes = node.getChildNodes();
for (int i = 0, n = nodes.getLength(); i < n; i++) {
processNode(nodes.item(i), structElem, false);
}
return structElemPart;
}

private int getNextParentTreeKey() {
@@ -301,4 +243,8 @@ class PDFLogicalStructureHandler {
parent.addKid(contentItem);
}

void addStructurePointer(String ptr, PDFStructElem structElem) {
structTreeMap.put(ptr, structElem);
}

}

+ 93
- 0
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java View File

@@ -0,0 +1,93 @@
/*
* 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.render.pdf;

import java.util.LinkedList;
import java.util.Locale;

import org.xml.sax.Attributes;

import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.pdf.PDFFactory;
import org.apache.fop.pdf.PDFStructElem;

class PDFStructureTreeBuilder implements StructureTreeEventHandler {

private PDFFactory pdfFactory;

private PDFLogicalStructureHandler logicalStructureHandler;

private EventBroadcaster eventBroadcaster;

private LinkedList<PDFStructElem> ancestors = new LinkedList<PDFStructElem>();

void setPdfFactory(PDFFactory pdfFactory) {
this.pdfFactory = pdfFactory;
}

void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) {
this.logicalStructureHandler = logicalStructureHandler;
}

void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
this.eventBroadcaster = eventBroadcaster;
}

public void startPageSequence(Locale locale) {
ancestors = new LinkedList<PDFStructElem>();
ancestors.add(logicalStructureHandler.createPageSequence(locale));
}

public void endPageSequence() {
}

public void startNode(String name, Attributes attributes) {
PDFStructElem parent = ancestors.getFirst();
String role = attributes.getValue("role");
PDFStructElem created = pdfFactory.makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(name, role, parent,
eventBroadcaster), parent);
if (ancestors.size() <= 2) { // TODO remove
parent.addKid(created);
}
String ptr = attributes.getValue(InternalElementMapping.URI, "ptr");
if (ptr != null) {
logicalStructureHandler.addStructurePointer(ptr, created);
}

if (name.equals("external-graphic") || name.equals("instream-foreign-object")) {
String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text");
if (altTextNode != null) {
created.put("Alt", altTextNode);
} else {
created.put("Alt", "No alternate text specified");
}
}
ancestors.addFirst(created);
}

public void endNode(String name) {
ancestors.removeFirst();
}

}

+ 1
- 30
src/java/org/apache/fop/render/xml/XMLRenderer.java View File

@@ -35,9 +35,6 @@ import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;

import org.apache.xmlgraphics.util.QName;
@@ -75,23 +72,20 @@ import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.XMLHandler;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.DOM2SAX;

/**
* Renderer that renders areas to XML for debugging purposes.
@@ -461,29 +455,6 @@ public class XMLRenderer extends AbstractXMLRenderer {
}
transferForeignObjects(pageSequence);
startElement("pageSequence", atts);
if (this.getUserAgent().isAccessibilityEnabled()) {
String structureTreeElement = "structureTree";
startElement(structureTreeElement);
try {
this.handler.startPrefixMapping("foi", InternalElementMapping.URI);
this.handler.startPrefixMapping("fox", ExtensionElementMapping.URI);
NodeList nodes = getUserAgent().getStructureTree().getPageSequence(
pageSequenceIndex++);
for (int i = 0, n = nodes.getLength(); i < n; i++) {
Node node = nodes.item(i);
try {
new DOM2SAX(handler).writeFragment(node);
} catch (SAXException e) {
handleSAXException(e);
}
}
this.handler.endPrefixMapping("fox");
this.handler.endPrefixMapping("foi");
} catch (SAXException se) {
handleSAXException(se);
}
endElement(structureTreeElement);
}
handleExtensionAttachments(pageSequence.getExtensionAttachments());
LineArea seqTitle = pageSequence.getTitle();
if (seqTitle != null) {

+ 66
- 0
src/java/org/apache/fop/util/LanguageTags.java View File

@@ -0,0 +1,66 @@
/*
* 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.util;

import java.util.Locale;

/**
* Provides utility methods for manipulating language tags compliant with the
* RFC 3066 specification available at http://www.ietf.org/rfc/rfc3066.txt. A
* typical language tag is a 2-letter language code sometimes followed by a country
* code. For example: en, en-US.
*/
public final class LanguageTags {

private LanguageTags() {
}

/**
* Converts the given locale to an RFC 3066 compliant language tag.
*
* @param locale a locale
* @return the corresponding language tag
* @throws NullPointerException if the specified locale is null
*/
public static String toLanguageTag(Locale locale) {
StringBuffer sb = new StringBuffer(5);
sb.append(locale.getLanguage());
String country = locale.getCountry();
if (country.length() > 0) {
sb.append('-');
sb.append(country);
}
return sb.toString();
}

/**
* Converts an RFC 3066 compliant language tag to a locale.
*
* @throws NullPointerException if the specified language tag is null
*/
public static Locale toLocale(String languageTag) {
String[] parts = languageTag.split("-");
if (parts.length == 1) {
return new Locale(parts[0]);
} else {
return new Locale(parts[0], parts[1]);
}
}
}

+ 0
- 36
src/java/org/apache/fop/util/XMLUtil.java View File

@@ -21,7 +21,6 @@ package org.apache.fop.util;

import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.Locale;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@@ -174,39 +173,4 @@ public final class XMLUtil implements XMLConstants {
atts.addAttribute("", localName, localName, XMLUtil.CDATA, value);
}

/**
* Converts a {@link Locale} instance to an RFC 3066 compliant language identifier.
* @param language the language
* @return the formatted language identifier
*/
public static String toRFC3066(Locale language) {
if (language == null || language.getLanguage().length() == 0) {
return null;
}
StringBuffer sb = new StringBuffer();
sb.append(language.getLanguage());
if (language.getCountry().length() > 0) {
sb.append('-');
sb.append(language.getCountry());
}
return sb.toString();
}

/**
* Converts an RFC 3066 compliant language identifier to a {@link Locale} instance.
* @param lang the language string
* @return the converted locale instance
*/
public static Locale convertRFC3066ToLocale(String lang) {
if (lang == null || lang.length() == 0) {
return null;
}
String[] parts = lang.split("-");
if (parts.length == 1) {
return new Locale(parts[0]);
} else {
return new Locale(parts[0], parts[1]);
}
}

}

+ 0
- 24
test/accessibility/config-renderer.xconf View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<fop version="1.0">
<prefer-renderer>true</prefer-renderer>
<accessibility>true</accessibility>
<source-resolution>144</source-resolution>
<use-cache>false</use-cache>
<font-base>../resources/fonts/</font-base>
<renderers>
<renderer mime="application/pdf">
<filterList>
<value>null</value>
</filterList>
<filterList type="image">
<value>flate</value>
<value>ascii-85</value>
</filterList>
<fonts>
<font embed-url="DejaVuLGCSerif.ttf">
<font-triplet name="DejaVu" style="normal" weight="normal"/>
</font>
</fonts>
</renderer>
</renderers>
</fop>

test/accessibility/config-painter.xconf → test/accessibility/fop.xconf View File


BIN
test/accessibility/pdf/background-image_jpg_repeat.pdf View File


BIN
test/accessibility/pdf/background-image_jpg_single.pdf View File


BIN
test/accessibility/pdf/background-image_png_repeat.pdf View File


BIN
test/accessibility/pdf/background-image_png_single.pdf View File


BIN
test/accessibility/pdf/background-image_svg_repeat.pdf View File


BIN
test/accessibility/pdf/background-image_svg_single.pdf View File


BIN
test/accessibility/pdf/complete.pdf View File


BIN
test/accessibility/pdf/image_jpg.pdf View File


BIN
test/accessibility/pdf/image_png.pdf View File


BIN
test/accessibility/pdf/image_svg.pdf View File


BIN
test/accessibility/pdf/image_wmf.pdf View File


BIN
test/accessibility/pdf/leader.pdf View File


BIN
test/accessibility/pdf/links.pdf View File


BIN
test/accessibility/pdf/role.pdf View File


BIN
test/accessibility/pdf/role_non-standard.pdf View File


BIN
test/accessibility/pdf/text_1.pdf View File


BIN
test/accessibility/pdf/text_2.pdf View File


BIN
test/accessibility/pdf/text_font-embedding.pdf View File


+ 2
- 1
test/java/org/apache/fop/StandardTestSuite.java View File

@@ -68,7 +68,8 @@ import org.apache.fop.traits.MinOptMaxTest;
CharactersetEncoderTest.class,
org.apache.fop.render.afp.AFPTestSuite.class,
PSTestSuite.class,
MinOptMaxTest.class
MinOptMaxTest.class,
org.apache.fop.render.intermediate.IFStructureTreeBuilderTestCase.class
})
public class StandardTestSuite {
}

+ 202
- 0
test/java/org/apache/fop/accessibility/FO2StructureTreeConverterTestCase.java View File

@@ -0,0 +1,202 @@
/*
* 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.accessibility;

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;

import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.DifferenceConstants;
import org.custommonkey.xmlunit.DifferenceListener;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.FODocumentParser;
import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.LoadingException;
import org.apache.fop.fotreetest.DummyFOEventHandler;

public class FO2StructureTreeConverterTestCase {

private static class IgnoringPtrDifferenceListener implements DifferenceListener {

public int differenceFound(Difference difference) {
switch (difference.getId()) {
case DifferenceConstants.ELEMENT_NUM_ATTRIBUTES_ID:
return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
case DifferenceConstants.ATTR_NAME_NOT_FOUND_ID:
String additionalAttribute = difference.getTestNodeDetail().getValue();
if (additionalAttribute.equals("ptr")) {
return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
} else {
return RETURN_ACCEPT_DIFFERENCE;
}
default:
return RETURN_ACCEPT_DIFFERENCE;
}
}

public void skippedComparison(Node control, Node test) {
throw new UnsupportedOperationException("Not implemented");
}
}

private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence";

@Test
public void testConverter() throws Exception {
DOMResult expectedStructureTree = loadExpectedStructureTree();
DOMResult actualStructureTree = buildActualStructureTree();
final Diff diff = createDiff(expectedStructureTree, actualStructureTree);
assertTrue(diff.toString(), diff.similar());
}

private static DOMResult loadExpectedStructureTree() {
DOMResult expectedStructureTree = new DOMResult();
runXSLT(getXsltInputStream(), getFoInputStream(), expectedStructureTree);
return expectedStructureTree;
}

private static InputStream getXsltInputStream() {
return FO2StructureTreeConverterTestCase.class.getResourceAsStream("foToIfStructureTree.xsl");
}

private static InputStream getFoInputStream() {
return FO2StructureTreeConverterTestCase.class.getResourceAsStream(
"/org/apache/fop/fo/complete_document.fo");
}

private static void runXSLT(InputStream xslt, InputStream doc, Result result) {
Source fo = new StreamSource(doc);
try {
Transformer transformer = TransformerFactory.newInstance()
.newTransformer(new StreamSource(xslt));
transformer.transform(fo, result);
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
} catch (TransformerException e) {
throw new RuntimeException(e);
} finally {
closeStream(xslt);
closeStream(doc);
}
}

private static void closeStream(InputStream stream) {
try {
stream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static DOMResult buildActualStructureTree() throws Exception {
DOMResult actualStructureTree = new DOMResult();
createStructureTreeFromDocument(getFoInputStream(), actualStructureTree);
return actualStructureTree;
}

private static void createStructureTreeFromDocument(InputStream foInputStream,
DOMResult domResult) throws Exception {
TransformerHandler tHandler = createTransformerHandler(domResult);
startStructureTreeSequence(tHandler);
StructureTreeEventHandler structureTreeEventHandler
= StructureTree2SAXEventAdapter.newInstance(tHandler);
FODocumentParser documentParser = createDocumentParser(structureTreeEventHandler);
FOUserAgent userAgent = createFOUserAgent(documentParser);
parseDocument(foInputStream, documentParser, userAgent);
endStructureTreeSequence(tHandler);
}

private static TransformerHandler createTransformerHandler(DOMResult domResult)
throws TransformerConfigurationException, TransformerFactoryConfigurationError {
SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler transformerHandler = factory.newTransformerHandler();
transformerHandler.setResult(domResult);
return transformerHandler;
}

private static void startStructureTreeSequence(TransformerHandler tHandler) throws SAXException {
tHandler.startDocument();
tHandler.startElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME,
new AttributesImpl());
}

private static FODocumentParser createDocumentParser(
final StructureTreeEventHandler structureTreeEventHandler) {
return FODocumentParser.newInstance(new FOEventHandlerFactory() {
public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) {
return new FO2StructureTreeConverter(structureTreeEventHandler,
new DummyFOEventHandler(foUserAgent));
}
});
}

private static FOUserAgent createFOUserAgent(FODocumentParser documentParser) {
FOUserAgent userAgent = documentParser.createFOUserAgent();
userAgent.setAccessibility(true);
return userAgent;
}

private static void parseDocument(InputStream foInputStream, FODocumentParser documentParser,
FOUserAgent userAgent) throws FOPException, LoadingException {
try {
documentParser.parse(foInputStream, userAgent);
} finally {
closeStream(foInputStream);
}
}

private static void endStructureTreeSequence(TransformerHandler tHandler) throws SAXException {
tHandler.endElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME);
tHandler.endDocument();
}

private static Diff createDiff(DOMResult expected, DOMResult actual) {
Diff diff = new Diff(getDocument(expected), getDocument(actual));
diff.overrideDifferenceListener(new IgnoringPtrDifferenceListener());
return diff;
}

private static Document getDocument(DOMResult result) {
return (Document) result.getNode();
}
}

+ 116
- 0
test/java/org/apache/fop/accessibility/fo2StructureTree.xsl View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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$ -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
xmlns:foi="http://xmlgraphics.apache.org/fop/internal">

<xsl:output method="xml" indent="no"/>

<xsl:template name="copy">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>


<!-- Ignore fo:root -->
<xsl:template match="fo:root">
<structure-tree-sequence>
<xsl:apply-templates/>
</structure-tree-sequence>
</xsl:template>

<!-- fo:page-sequence maps to structure-tree -->
<xsl:template match="fo:page-sequence">
<structure-tree xmlns="http://xmlgraphics.apache.org/fop/intermediate">
<xsl:apply-templates/>
</structure-tree>
</xsl:template>


<!-- Declarations and Pagination and Layout Formatting Objects -->
<xsl:template match="fo:static-content|fo:flow">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Block-level Formatting Objects -->
<xsl:template match="fo:block|fo:block-container">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Inline-level Formatting Objects -->
<xsl:template match="fo:character|fo:inline|fo:inline-container">
<xsl:call-template name="copy"/>
</xsl:template>

<xsl:template match="fo:external-graphic|fo:instream-foreign-object">
<xsl:call-template name="copy"/>
</xsl:template>

<xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Formatting Objects for Tables -->
<xsl:template match="fo:table-and-caption|fo:table-caption|fo:table">
<xsl:call-template name="copy"/>
</xsl:template>

<xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Formatting Objects for Lists -->
<xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Dynamic Effects: Link and Multi Formatting Objects -->
<xsl:template match="fo:basic-link">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Out-of-Line Formatting Objects -->
<xsl:template match="fo:float|fo:footnote|fo:footnote-body">
<xsl:call-template name="copy"/>
</xsl:template>

<!-- Other Formatting Objects -->
<xsl:template match="fo:wrapper|fo:marker">
<xsl:call-template name="copy"/>
</xsl:template>


<!-- Discard descendants of fo:leader -->
<xsl:template match="fo:leader"/>


<!-- Keep fox:alt-text and role attributes, discard everything else -->
<xsl:template match="@fox:alt-text|@role">
<xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="@*"/>


<!-- Discard text -->
<xsl:template match="text()"/>

</xsl:stylesheet>

+ 14
- 5
test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java View File

@@ -71,6 +71,7 @@ import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;

/**
@@ -97,12 +98,22 @@ public class DelegatingFOEventHandlerTestCase {

@Override
public void startDocument() throws SAXException {
actualEvents.add("start root");
actualEvents.add("start document");
}

@Override
public void endDocument() throws SAXException {
actualEvents.add("end root");
actualEvents.add("end document");
}

@Override
public void startRoot(Root root) {
startElement(root);
}

@Override
public void endRoot(Root root) {
endElement(root);
}

@Override
@@ -417,9 +428,7 @@ public class DelegatingFOEventHandlerTestCase {
}

private void loadDocument() {
Class<?> clazz = getClass();
String documentName = clazz.getSimpleName() + ".fo";
document = clazz.getResourceAsStream(documentName);
document = getClass().getResourceAsStream("complete_document.fo");
}

private void loadExpectedEvents() throws IOException {

+ 18
- 2
test/java/org/apache/fop/fo/FODocumentParser.java View File

@@ -108,13 +108,29 @@ public final class FODocumentParser {
* @throws LoadingException if an error occurs when parsing the document
*/
public void parse(InputStream document) throws FOPException, LoadingException {
FOUserAgent foUserAgent = createFOUserAgent();
parse(document, createFOUserAgent());
}

/**
* Runs FOP on the given document with the supplied {@link FOUserAgent}.
*
* @param document XSL-FO document to parse
* @param foUserAgent The user agent
* @throws FOPException if an error occurs when initializing FOP
* @throws LoadingException if an error occurs when parsing the document
*/
public void parse(InputStream document, FOUserAgent foUserAgent)
throws FOPException, LoadingException {
fop = FOP_FACTORY.newFop(foUserAgent);
createTransformer();
runTransformer(document);
}

private FOUserAgent createFOUserAgent() {
/**
* Creates a new {@link FOUserAgent}.
* @return It
*/
public FOUserAgent createFOUserAgent() {
FOUserAgent userAgent = FOP_FACTORY.newFOUserAgent();
FOEventHandler foEventHandler = foEventHandlerFactory.newFOEventHandler(userAgent);
userAgent.setFOEventHandlerOverride(foEventHandler);

test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.fo → test/java/org/apache/fop/fo/complete_document.fo View File

@@ -147,7 +147,8 @@
</fo:list-item>
</fo:list-block>
<fo:block id="70" text-align="center"><fo:instream-foreign-object id="71"
inline-progression-dimension.maximum="50%" content-width="scale-to-fit">
inline-progression-dimension.maximum="50%" content-width="scale-to-fit"
fox:alt-text="An inline SVG">
<svg xmlns="http://www.w3.org/2000/svg" width="319" height="286.6">
<g style="fill-opacity:0.7; stroke:black; stroke-width:3"
transform="translate(0, 286.6) scale(1, -1) translate(100, 100)">

+ 10
- 4
test/java/org/apache/fop/fo/extract-events.xsl View File

@@ -6,16 +6,22 @@

<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:template match="fo:root">
<xsl:template match="/">
<event>
<xsl:text>start root</xsl:text>
<xsl:text>start document</xsl:text>
</event>
<xsl:apply-templates select="fo:page-sequence"/>
<xsl:apply-templates/>
<event>
<xsl:text>end root</xsl:text>
<xsl:text>end document</xsl:text>
</event>
</xsl:template>

<xsl:template match="fo:root">
<event>start root</event>
<xsl:apply-templates select="fo:page-sequence"/>
<event>end root</event>
</xsl:template>

<xsl:template match="fo:*">
<xsl:call-template name="process.node">
<xsl:with-param name="id">

+ 2
- 1
test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java View File

@@ -34,6 +34,7 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FONodeMocks;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.flow.table.UnimplementedWarningNeutralizer;

/**
* This tests that all the FONodes that implement CommonAccessibilityHolder correctly configure
@@ -51,7 +52,7 @@ public class CommonAccessibilityHolderTestCase {
static {
/* This triggers 'unimplemented feature' FO validation events so that the event system is
* not triggered when testing, avoiding extra convoluted dependency stubbing. */
// UnimplementedWarningNeutralizer.neutralizeUnimplementedWarning();
UnimplementedWarningNeutralizer.neutralizeUnimplementedWarning();

IMPLEMENTATIONS.add(org.apache.fop.fo.flow.BasicLink.class);
IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Block.class);

+ 16
- 1
test/java/org/apache/fop/intermediate/IFParserTestCase.java View File

@@ -48,6 +48,9 @@ import org.apache.fop.render.intermediate.IFSerializer;
@RunWith(Parameterized.class)
public class IFParserTestCase extends AbstractIFTestCase {

/** Set this to true to get the correspondence between test number and test file. */
private static final boolean DEBUG = false;

/**
* Gets the parameters for this test
*
@@ -56,7 +59,19 @@ public class IFParserTestCase extends AbstractIFTestCase {
*/
@Parameters
public static Collection<File[]> getParameters() throws IOException {
return LayoutEngineTestUtils.getLayoutTestFiles();
Collection<File[]> testFiles = LayoutEngineTestUtils.getLayoutTestFiles();
if (DEBUG) {
printFiles(testFiles);
}
return testFiles;
}

private static void printFiles(Collection<File[]> files) {
int index = 0;
for (File[] file : files) {
assert file.length == 1;
System.out.println(String.format("%3d %s", index++, file[0]));
}
}

/**

+ 169
- 0
test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java View File

@@ -0,0 +1,169 @@
/*
* 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.render.intermediate;

import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.fop.fo.FOElementMapping;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.util.XMLUtil;

public class IFStructureTreeBuilderTestCase {

private IFStructureTreeBuilder sut;

@Before
public void setUp() {
sut = new IFStructureTreeBuilder();
}

@Test
public void startAndEndPageSequence() throws SAXException {
final ContentHandler handler = mock(ContentHandler.class);

try {
sut.replayEventsForPageSequence(handler, 0);
fail("No page sequences created");
} catch (IndexOutOfBoundsException e) {
// Expected
}

sut.startPageSequence(null);
sut.endPageSequence();

sut.replayEventsForPageSequence(handler, 0);

InOrder inOrder = inOrder(handler);

inOrder.verify(handler).startPrefixMapping(
InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI);
inOrder.verify(handler).startPrefixMapping(
ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI);
inOrder.verify(handler).startElement(eq(IFConstants.NAMESPACE),
eq(IFConstants.EL_STRUCTURE_TREE),
eq(IFConstants.EL_STRUCTURE_TREE),
any(Attributes.class));
inOrder.verify(handler).endElement(eq(IFConstants.NAMESPACE),
eq(IFConstants.EL_STRUCTURE_TREE),
eq(IFConstants.EL_STRUCTURE_TREE));
inOrder.verify(handler).endPrefixMapping(ExtensionElementMapping.STANDARD_PREFIX);
inOrder.verify(handler).endPrefixMapping(InternalElementMapping.STANDARD_PREFIX);
}

@Test
public void startNode() throws Exception {
final String[] attributes = {"ptr", "1"};
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);

sut.startPageSequence(null);
sut.startNode(nodeName, createSimpleAttributes(attributes));
sut.endPageSequence();

sut.replayEventsForPageSequence(handler, 0);

verify(handler).startElement(eq(FOElementMapping.URI), eq(nodeName),
eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName),
AttributesMatcher.match(createSimpleAttributes(attributes)));
}

@Test
public void endNode() throws Exception {
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);

sut.startPageSequence(null);
sut.endNode(nodeName);
sut.endPageSequence();

sut.replayEventsForPageSequence(handler, 0);

verify(handler).endElement(eq(FOElementMapping.URI), eq(nodeName),
eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName));
}

private static Attributes createSimpleAttributes(String... attributes) {
assert (attributes.length % 2 == 0);
final AttributesImpl atts = new AttributesImpl();
for (int i = 0; i < attributes.length; i += 2) {
String key = attributes[i];
String value = attributes[i + 1];
atts.addAttribute("", key, key, XMLUtil.CDATA, value);
}
return atts;
}

private static final class AttributesMatcher extends ArgumentMatcher<Attributes> {

private final Attributes expected;

private AttributesMatcher(Attributes expected) {
this.expected = expected;
}

public static Attributes match(Attributes expected) {
return argThat(new AttributesMatcher(expected));
}

public boolean matches(Object attributes) {
return attributesEqual(expected, (Attributes) attributes);
}

private static boolean attributesEqual(Attributes attributes1, Attributes attributes2) {
if (attributes1.getLength() != attributes2.getLength()) {
return false;
}
for (int i = 0; i < attributes1.getLength(); i++) {
if (attributes1.getLocalName(i) != attributes2.getLocalName(i)) {
return false;
}
if (attributes1.getQName(i) != attributes2.getQName(i)) {
return false;
}
if (attributes1.getType(i) != attributes2.getType(i)) {
return false;
}
if (attributes1.getURI(i) != attributes2.getURI(i)) {
return false;
}
if (attributes1.getValue(i) != attributes2.getValue(i)) {
return false;
}
}
return true;
}
}
}

+ 131
- 0
test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.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.render.intermediate;


import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.fop.render.intermediate.IFStructureTreeBuilder.SAXEventRecorder;
import org.apache.fop.util.XMLUtil;

/**
* Tests {@link SAXEventRecorder}.
*/
public class SAXEventRecorderTestCase {

private static final String URI = "http://www.example.com/";

private SAXEventRecorder sut;

@Before
public void setUp() {
sut = new SAXEventRecorder();
}

@Test
public void testStartEvent() throws SAXException {
final String localName = "element";
final String qName = "prefix:" + localName;
final Attributes attributes = new AttributesImpl();

sut.startElement(URI, localName, qName, attributes);
ContentHandler handler = mock(ContentHandler.class);
sut.replay(handler);
verify(handler).startElement(URI, localName, qName, attributes);
}

@Test
public void testEndEvent() throws SAXException {
final String localName = "element";
final String qName = "prefix:" + localName;
sut.endElement(URI, localName, qName);
ContentHandler handler = mock(ContentHandler.class);
sut.replay(handler);
verify(handler).endElement(URI, localName, qName);
}

@Test
public void testStartPrefixMapping() throws SAXException {
final String prefix = "prefix";

sut.startPrefixMapping(URI, prefix);
ContentHandler handler = mock(ContentHandler.class);
sut.replay(handler);
verify(handler).startPrefixMapping(URI, prefix);
}

@Test
public void testEndPrefixMapping() throws SAXException {
final String prefix = "prefix";

sut.endPrefixMapping(prefix);
ContentHandler handler = mock(ContentHandler.class);
sut.replay(handler);
verify(handler).endPrefixMapping(prefix);
}

@Test
public void completeTest() throws SAXException {
final String localName1 = "element";
final String qName1 = "prefix:" + localName1;
final Attributes attributes1 = createAttributes(URI, localName1, qName1, "value-1");
final String localName2 = "element2";
final String qName2 = "prefix:" + localName2;
final Attributes attributes2 = createAttributes(URI, localName2, qName2, "value-2");
final ContentHandler handler = mock(ContentHandler.class);
final String extensionUrl = "http://www.example.com/extension";
final String extensionPrefix = "ext";

sut.startPrefixMapping(extensionPrefix, extensionUrl);
sut.startElement(URI, localName1, qName1, attributes1);
sut.startElement(URI, localName2, qName2, attributes2);
sut.endElement(URI, localName2, qName2);
sut.endElement(URI, localName1, qName1);
sut.endPrefixMapping(extensionPrefix);

sut.replay(handler);

InOrder inOrder = inOrder(handler);
inOrder.verify(handler).startPrefixMapping(extensionPrefix, extensionUrl);
inOrder.verify(handler).startElement(URI, localName1, qName1, attributes1);
inOrder.verify(handler).startElement(URI, localName2, qName2, attributes2);
inOrder.verify(handler).endElement(URI, localName2, qName2);
inOrder.verify(handler).endElement(URI, localName1, qName1);
inOrder.verify(handler).endPrefixMapping(extensionPrefix);
}

private static Attributes createAttributes(String uri, String localName,
String qName, String value) {
final AttributesImpl atts = new AttributesImpl();
atts.addAttribute(uri, localName, qName, XMLUtil.CDATA, value);
return atts;
}

}

test/java/org/apache/fop/util/XMLUtilTestCase.java → test/java/org/apache/fop/util/LanguageTagsTestCase.java View File

@@ -20,31 +20,39 @@
package org.apache.fop.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.util.Locale;

import org.junit.Test;

/**
* Tests {@link XMLUtil}.
* Tests {@link LanguageTags}.
*/
public class XMLUtilTestCase {
public class LanguageTagsTestCase {

@Test(expected = NullPointerException.class)
public void toLanguageTagRejectsNull() {
LanguageTags.toLanguageTag(null);
}

@Test
public void testLocaleToRFC3066() throws Exception {
assertNull(XMLUtil.toRFC3066(null));
assertEquals("en", XMLUtil.toRFC3066(new Locale("en")));
assertEquals("en-US", XMLUtil.toRFC3066(new Locale("en", "US")));
assertEquals("en-US", XMLUtil.toRFC3066(new Locale("EN", "us")));
public void testToLanguageTag() throws Exception {
assertEquals("", LanguageTags.toLanguageTag(new Locale("")));
assertEquals("en", LanguageTags.toLanguageTag(new Locale("en")));
assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("en", "US")));
assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("EN", "us")));
}

@Test(expected = NullPointerException.class)
public void toLocaleRejectsNull() {
LanguageTags.toLocale(null);
}

@Test
public void testRFC3066ToLocale() throws Exception {
assertNull(XMLUtil.convertRFC3066ToLocale(null));
assertNull(XMLUtil.convertRFC3066ToLocale(""));
assertEquals(new Locale("en"), XMLUtil.convertRFC3066ToLocale("en"));
assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("en-US"));
assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("EN-us"));
assertEquals(new Locale(""), LanguageTags.toLocale(""));
assertEquals(new Locale("en"), LanguageTags.toLocale("en"));
assertEquals(new Locale("en", "US"), LanguageTags.toLocale("en-US"));
assertEquals(new Locale("en", "US"), LanguageTags.toLocale("EN-us"));
}
}

Loading…
Cancel
Save