diff options
Diffstat (limited to 'src/java/org/apache/fop')
45 files changed, 1528 insertions, 761 deletions
diff --git a/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java index 80a011bab..07daf2f73 100644 --- a/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java +++ b/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java @@ -33,20 +33,31 @@ public final class DummyStructureTreeEventHandler implements StructureTreeEventH private DummyStructureTreeEventHandler() { } - /** {@inheritDoc} */ public void startPageSequence(Locale locale) { } - /** {@inheritDoc} */ - public void startNode(String name, Attributes attributes) { + public void endPageSequence() { + } + + public StructureTreeElement startNode(String name, Attributes attributes) { + return null; } - /** {@inheritDoc} */ public void endNode(String name) { } - /** {@inheritDoc} */ - public void endPageSequence() { + public StructureTreeElement startImageNode(String name, Attributes attributes) { + return null; + } + + public void endImageNode(String name) { + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + return null; + } + + public void endReferencedNode(String name) { } } diff --git a/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java b/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java index 240c384d6..2cd92a5ce 100644 --- a/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java +++ b/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java @@ -50,7 +50,6 @@ public final class StructureTree2SAXEventAdapter implements StructureTreeEventHa return new StructureTree2SAXEventAdapter(contentHandler); } - /** {@inheritDoc} */ public void startPageSequence(Locale locale) { try { @@ -66,7 +65,6 @@ public final class StructureTree2SAXEventAdapter implements StructureTreeEventHa } } - /** {@inheritDoc} */ public void endPageSequence() { try { contentHandler.endElement(IFConstants.NAMESPACE, IFConstants.EL_STRUCTURE_TREE, @@ -81,18 +79,23 @@ public final class StructureTree2SAXEventAdapter implements StructureTreeEventHa } } - /** {@inheritDoc} */ - public void startNode(String name, Attributes attributes) { + public StructureTreeElement startNode(String name, Attributes attributes) { try { - contentHandler.startElement(FOElementMapping.URI, name, - FOElementMapping.STANDARD_PREFIX + ":" + name, - attributes); + if (name.equals("#PCDATA")) { + name = "marked-content"; + contentHandler.startElement(IFConstants.NAMESPACE, name, + name, attributes); + } else { + contentHandler.startElement(FOElementMapping.URI, name, + FOElementMapping.STANDARD_PREFIX + ":" + name, + attributes); + } + return null; } catch (SAXException e) { throw new RuntimeException(e); } } - /** {@inheritDoc} */ public void endNode(String name) { try { contentHandler.endElement(FOElementMapping.URI, name, @@ -101,4 +104,20 @@ public final class StructureTree2SAXEventAdapter implements StructureTreeEventHa throw new RuntimeException(e); } } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + return startNode(name, attributes); + } + + public void endImageNode(String name) { + endNode(name); + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + return startNode(name, attributes); + } + + public void endReferencedNode(String name) { + endNode(name); + } } diff --git a/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java b/src/java/org/apache/fop/accessibility/StructureTreeElement.java index 5cce2822e..3a691c42c 100644 --- a/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java +++ b/src/java/org/apache/fop/accessibility/StructureTreeElement.java @@ -17,18 +17,15 @@ /* $Id$ */ -package org.apache.fop.fo.properties; - /** - * Defines property access methods for internal structure pointer extension properties. + * */ -public interface StructurePointerPropertySet { - - /** - * Returns the value of the "foi:ptr" property, the internal structure pointer used - * for tagged PDF and other formats that support a structure tree in addition to paged content. - * @return the "foi:ptr" property - */ - String getPtr(); +package org.apache.fop.accessibility; +/** + * An object that represents the structure of the document in the output format. + * In PDF, an implementation of this interface will typically result into the + * creation of a structure element dictionary (a dictionary of type StructElem). + */ +public interface StructureTreeElement { } diff --git a/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java index d84d07870..4b94d61f1 100644 --- a/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java +++ b/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java @@ -32,24 +32,47 @@ 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 + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element */ - void startNode(String name, Attributes attributes); + StructureTreeElement startNode(String name, Attributes attributes); /** * Ends a structure tree node. - * @param name The name of the structure tree node + * + * @param name the name of the structure tree node */ void endNode(String name); /** + * Starts an image node. + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element + */ + StructureTreeElement startImageNode(String name, Attributes attributes); + + /** + * Starts a node that can be referenced by other nodes. This is usually a + * node that can have Marked Content References as children. + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element + */ + StructureTreeElement startReferencedNode(String name, Attributes attributes); + + /** * Ends a page sequence structure tree node. */ void endPageSequence(); diff --git a/src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java index e6d0193b9..47c227e9a 100644 --- a/src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java +++ b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java @@ -17,21 +17,17 @@ /* $Id$ */ -package org.apache.fop.accessibility; +package org.apache.fop.accessibility.fo; -import java.util.Locale; +import java.util.Stack; import org.xml.sax.SAXException; -import org.xml.sax.helpers.AttributesImpl; -import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.fo.DelegatingFOEventHandler; import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.fo.FONode; -import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.FOText; import org.apache.fop.fo.extensions.ExternalDocument; -import org.apache.fop.fo.extensions.InternalElementMapping; -import org.apache.fop.fo.flow.AbstractGraphics; import org.apache.fop.fo.flow.BasicLink; import org.apache.fop.fo.flow.Block; import org.apache.fop.fo.flow.BlockContainer; @@ -61,8 +57,6 @@ 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; /** * Allows to create the structure tree of an FO document, by converting FO @@ -70,354 +64,18 @@ import org.apache.fop.util.XMLUtil; */ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { - private int idCounter; - - /** Delegates to either {@link #foToStructureTreeEventAdapter} or {@link #eventSwallower}. */ + /** The top of the {@link converters} stack. */ private FOEventHandler converter; - private final FOEventHandler foToStructureTreeEventAdapter; - - /** The descendants of some elements like fo:leader must be ignored. */ - private final FOEventHandler eventSwallower; - - private final StructureTreeEventHandler structureTreeEventHandler; - - private final class FOToStructureTreeEventAdapter extends FOEventHandler { - - public FOToStructureTreeEventAdapter(FOUserAgent foUserAgent) { - super(foUserAgent); - } - - @Override - public void startDocument() throws SAXException { - } - - @Override - public void endDocument() throws SAXException { - } - - @Override - public void startPageSequence(PageSequence pageSeq) { - Locale locale = null; - if (pageSeq.getLanguage() != null) { - if (pageSeq.getCountry() != null) { - locale = new Locale(pageSeq.getLanguage(), pageSeq.getCountry()); - } else { - locale = new Locale(pageSeq.getLanguage()); - } - } - structureTreeEventHandler.startPageSequence(locale); - } - - @Override - public void endPageSequence(PageSequence pageSeq) { - structureTreeEventHandler.endPageSequence(); - } - - @Override - public void startPageNumber(PageNumber pagenum) { - startElementWithID(pagenum); - } - - @Override - public void endPageNumber(PageNumber pagenum) { - endElement(pagenum); - } - - @Override - public void startPageNumberCitation(PageNumberCitation pageCite) { - startElementWithID(pageCite); - } - - @Override - public void endPageNumberCitation(PageNumberCitation pageCite) { - endElement(pageCite); - } - - @Override - public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { - startElementWithID(pageLast); - } - - @Override - public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { - endElement(pageLast); - } - - @Override - public void startFlow(Flow fl) { - startElement(fl); - } - - @Override - public void endFlow(Flow fl) { - endElement(fl); - } - - @Override - public void startBlock(Block bl) { - startElementWithID(bl); - } - - @Override - public void endBlock(Block bl) { - endElement(bl); - } + private final Stack<FOEventHandler> converters = new Stack<FOEventHandler>(); - @Override - public void startBlockContainer(BlockContainer blc) { - startElement(blc); - } + private final Stack<FOEventRecorder> tableFooterRecorders = new Stack<FOEventRecorder>(); - @Override - public void endBlockContainer(BlockContainer blc) { - endElement(blc); - } + private final FOEventHandler structureTreeEventTrigger; - @Override - public void startInline(Inline inl) { - startElementWithID(inl); - } - - @Override - public void endInline(Inline inl) { - endElement(inl); - } - - @Override - public void startTable(Table tbl) { - startElementWithID(tbl); - } - - @Override - public void endTable(Table tbl) { - endElement(tbl); - } - - @Override - public void startHeader(TableHeader header) { - startElementWithID(header); - } - - @Override - public void endHeader(TableHeader header) { - endElement(header); - } - - @Override - public void startFooter(TableFooter footer) { - startElementWithID(footer); - } - - @Override - public void endFooter(TableFooter footer) { - endElement(footer); - } - - @Override - public void startBody(TableBody body) { - startElementWithID(body); - } - - @Override - public void endBody(TableBody body) { - endElement(body); - } - - @Override - public void startRow(TableRow tr) { - startElementWithID(tr); - } - - @Override - public void endRow(TableRow tr) { - endElement(tr); - } - - @Override - public void startCell(TableCell tc) { - AttributesImpl attributes = new AttributesImpl(); - int colSpan = tc.getNumberColumnsSpanned(); - if (colSpan > 1) { - addNoNamespaceAttribute(attributes, "number-columns-spanned", - Integer.toString(colSpan)); - } - startElementWithID(tc, attributes); - } - - @Override - public void endCell(TableCell tc) { - endElement(tc); - } - - @Override - public void startList(ListBlock lb) { - startElement(lb); - } - - @Override - public void endList(ListBlock lb) { - endElement(lb); - } - - @Override - public void startListItem(ListItem li) { - startElement(li); - } - - @Override - public void endListItem(ListItem li) { - endElement(li); - } - - @Override - public void startListLabel(ListItemLabel listItemLabel) { - startElement(listItemLabel); - } - - @Override - public void endListLabel(ListItemLabel listItemLabel) { - endElement(listItemLabel); - } - - @Override - public void startListBody(ListItemBody listItemBody) { - startElement(listItemBody); - } - - @Override - public void endListBody(ListItemBody listItemBody) { - endElement(listItemBody); - } - - @Override - public void startStatic(StaticContent staticContent) { - startElement(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - endElement(statisContent); - } - - @Override - public void startLink(BasicLink basicLink) { - startElementWithID(basicLink); - } - - @Override - public void endLink(BasicLink basicLink) { - endElement(basicLink); - } - - @Override - public void image(ExternalGraphic eg) { - startElementWithIDAndAltText(eg); - endElement(eg); - } - - @Override - public void startInstreamForeignObject(InstreamForeignObject ifo) { - startElementWithIDAndAltText(ifo); - } - - @Override - public void endInstreamForeignObject(InstreamForeignObject ifo) { - endElement(ifo); - } - - @Override - public void startFootnote(Footnote footnote) { - startElement(footnote); - } - - @Override - public void endFootnote(Footnote footnote) { - endElement(footnote); - } - - @Override - public void startFootnoteBody(FootnoteBody body) { - startElement(body); - } - - @Override - public void endFootnoteBody(FootnoteBody body) { - endElement(body); - } - - @Override - public void startWrapper(Wrapper wrapper) { - startElement(wrapper); - } - - @Override - public void endWrapper(Wrapper wrapper) { - endElement(wrapper); - } - - @Override - public void character(Character c) { - startElementWithID(c); - endElement(c); - } - - - private void startElement(FONode node) { - startElement(node, new AttributesImpl()); - } - - private void startElementWithID(FONode node) { - startElementWithID(node, new AttributesImpl()); - } - - private void startElementWithIDAndAltText(AbstractGraphics node) { - AttributesImpl attributes = new AttributesImpl(); - addAttribute(attributes, ExtensionElementMapping.URI, "alt-text", - ExtensionElementMapping.STANDARD_PREFIX, node.getAltText()); - startElementWithID(node, attributes); - } - - private void startElementWithID(FONode node, AttributesImpl attributes) { - String id = Integer.toHexString(idCounter++); - node.setPtr(id); - addAttribute(attributes, - InternalElementMapping.URI, "ptr", InternalElementMapping.STANDARD_PREFIX, id); - startElement(node, attributes); - } - - private void startElement(FONode node, AttributesImpl attributes) { - String localName = node.getLocalName(); - if (node instanceof CommonAccessibilityHolder) { - addRole((CommonAccessibilityHolder) node, attributes); - } - structureTreeEventHandler.startNode(localName, attributes); - } - - private void addNoNamespaceAttribute(AttributesImpl attributes, String name, String value) { - attributes.addAttribute("", name, name, XMLUtil.CDATA, value); - } - - private void addAttribute(AttributesImpl attributes, - String namespace, String localName, String prefix, String value) { - assert namespace.length() > 0 && prefix.length() > 0; - String qualifiedName = prefix + ":" + localName; - attributes.addAttribute(namespace, localName, qualifiedName, XMLUtil.CDATA, value); - } - - private void addRole(CommonAccessibilityHolder node, AttributesImpl attributes) { - String role = node.getCommonAccessibility().getRole(); - if (role != null) { - addNoNamespaceAttribute(attributes, "role", role); - } - } - - private void endElement(FONode node) { - String localName = node.getLocalName(); - structureTreeEventHandler.endNode(localName); - } - - } + /** The descendants of some elements like fo:leader must be ignored. */ + private final FOEventHandler eventSwallower = new FOEventHandler() { + }; /** * Creates a new instance. @@ -428,10 +86,8 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { public FO2StructureTreeConverter(StructureTreeEventHandler structureTreeEventHandler, FOEventHandler delegate) { super(delegate); - this.structureTreeEventHandler = structureTreeEventHandler; - this.foToStructureTreeEventAdapter = new FOToStructureTreeEventAdapter(foUserAgent); - this.converter = foToStructureTreeEventAdapter; - this.eventSwallower = new FOEventHandler(foUserAgent) { }; + this.structureTreeEventTrigger = new StructureTreeEventTrigger(structureTreeEventHandler); + this.converter = structureTreeEventTrigger; } @Override @@ -557,11 +213,16 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void startTable(Table tbl) { converter.startTable(tbl); + tableFooterRecorders.push(null); super.startTable(tbl); } @Override public void endTable(Table tbl) { + FOEventRecorder tableFooterRecorder = tableFooterRecorders.pop(); + if (tableFooterRecorder != null) { + tableFooterRecorder.replay(converter); + } converter.endTable(tbl); super.endTable(tbl); } @@ -592,6 +253,8 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void startFooter(TableFooter footer) { + converters.push(converter); + converter = new FOEventRecorder(); converter.startFooter(footer); super.startFooter(footer); } @@ -599,6 +262,10 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void endFooter(TableFooter footer) { converter.endFooter(footer); + /* Replace the dummy table footer with the real one. */ + tableFooterRecorders.pop(); + tableFooterRecorders.push((FOEventRecorder) converter); + converter = converters.pop(); super.endFooter(footer); } @@ -772,6 +439,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void startLeader(Leader l) { + converters.push(converter); converter = eventSwallower; converter.startLeader(l); super.startLeader(l); @@ -780,7 +448,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void endLeader(Leader l) { converter.endLeader(l); - converter = foToStructureTreeEventAdapter; + converter = converters.pop(); super.endLeader(l); } @@ -803,9 +471,9 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { } @Override - public void characters(char[] data, int start, int length) { - converter.characters(data, start, length); - super.characters(data, start, length); + public void characters(FOText foText) { + converter.characters(foText); + super.characters(foText); } @Override diff --git a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java new file mode 100644 index 000000000..b2b18046d --- /dev/null +++ b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java @@ -0,0 +1,508 @@ +/* + * 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.fo; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.FOText; +import org.apache.fop.fo.flow.BasicLink; +import org.apache.fop.fo.flow.Block; +import org.apache.fop.fo.flow.BlockContainer; +import org.apache.fop.fo.flow.Character; +import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.Footnote; +import org.apache.fop.fo.flow.FootnoteBody; +import org.apache.fop.fo.flow.Inline; +import org.apache.fop.fo.flow.InstreamForeignObject; +import org.apache.fop.fo.flow.Leader; +import org.apache.fop.fo.flow.ListBlock; +import org.apache.fop.fo.flow.ListItem; +import org.apache.fop.fo.flow.ListItemBody; +import org.apache.fop.fo.flow.ListItemLabel; +import org.apache.fop.fo.flow.PageNumber; +import org.apache.fop.fo.flow.PageNumberCitation; +import org.apache.fop.fo.flow.PageNumberCitationLast; +import org.apache.fop.fo.flow.Wrapper; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; +import org.apache.fop.fo.flow.table.TableCell; +import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableFooter; +import org.apache.fop.fo.flow.table.TableHeader; +import org.apache.fop.fo.flow.table.TableRow; + +final class FOEventRecorder extends FOEventHandler { + + private interface Event { + void replay(FOEventHandler target); + } + + private final List<Event> events = new ArrayList<Event>(); + + public void replay(FOEventHandler target) { + for (Event event : events) { + event.replay(target); + } + } + + @Override + public void startPageNumber(final PageNumber pagenum) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumber(pagenum); + } + }); + } + + @Override + public void endPageNumber(final PageNumber pagenum) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumber(pagenum); + } + }); + } + + @Override + public void startPageNumberCitation(final PageNumberCitation pageCite) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumberCitation(pageCite); + } + }); + } + + @Override + public void endPageNumberCitation(final PageNumberCitation pageCite) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumberCitation(pageCite); + } + }); + } + + @Override + public void startPageNumberCitationLast(final PageNumberCitationLast pageLast) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumberCitationLast(pageLast); + } + }); + } + + @Override + public void endPageNumberCitationLast(final PageNumberCitationLast pageLast) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumberCitationLast(pageLast); + } + }); + } + + @Override + public void startBlock(final Block bl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBlock(bl); + } + }); + } + + @Override + public void endBlock(final Block bl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBlock(bl); + } + }); + } + + @Override + public void startBlockContainer(final BlockContainer blc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBlockContainer(blc); + } + }); + } + + @Override + public void endBlockContainer(final BlockContainer blc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBlockContainer(blc); + } + }); + } + + @Override + public void startInline(final Inline inl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startInline(inl); + } + }); + } + + @Override + public void endInline(final Inline inl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endInline(inl); + } + }); + } + + @Override + public void startTable(final Table tbl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startTable(tbl); + } + }); + } + + @Override + public void endTable(final Table tbl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endTable(tbl); + } + }); + } + + @Override + public void startColumn(final TableColumn tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startColumn(tc); + } + }); + } + + @Override + public void endColumn(final TableColumn tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endColumn(tc); + } + }); + } + + @Override + public void startHeader(final TableHeader header) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startHeader(header); + } + }); + } + + @Override + public void endHeader(final TableHeader header) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endHeader(header); + } + }); + } + + @Override + public void startFooter(final TableFooter footer) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFooter(footer); + } + }); + } + + @Override + public void endFooter(final TableFooter footer) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFooter(footer); + } + }); + } + + @Override + public void startBody(final TableBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBody(body); + } + }); + } + + @Override + public void endBody(final TableBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBody(body); + } + }); + } + + @Override + public void startRow(final TableRow tr) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startRow(tr); + } + }); + } + + @Override + public void endRow(final TableRow tr) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endRow(tr); + } + }); + } + + @Override + public void startCell(final TableCell tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startCell(tc); + } + }); + } + + @Override + public void endCell(final TableCell tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endCell(tc); + } + }); + } + + @Override + public void startList(final ListBlock lb) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startList(lb); + } + }); + } + + @Override + public void endList(final ListBlock lb) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endList(lb); + } + }); + } + + @Override + public void startListItem(final ListItem li) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListItem(li); + } + }); + } + + @Override + public void endListItem(final ListItem li) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListItem(li); + } + }); + } + + @Override + public void startListLabel(final ListItemLabel listItemLabel) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListLabel(listItemLabel); + } + }); + } + + @Override + public void endListLabel(final ListItemLabel listItemLabel) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListLabel(listItemLabel); + } + }); + } + + @Override + public void startListBody(final ListItemBody listItemBody) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListBody(listItemBody); + } + }); + } + + @Override + public void endListBody(final ListItemBody listItemBody) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListBody(listItemBody); + } + }); + } + + @Override + public void startLink(final BasicLink basicLink) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startLink(basicLink); + } + }); + } + + @Override + public void endLink(final BasicLink basicLink) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endLink(basicLink); + } + }); + } + + @Override + public void image(final ExternalGraphic eg) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.image(eg); + } + }); + } + + @Override + public void startInstreamForeignObject(final InstreamForeignObject ifo) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startInstreamForeignObject(ifo); + } + }); + } + + @Override + public void endInstreamForeignObject(final InstreamForeignObject ifo) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endInstreamForeignObject(ifo); + } + }); + } + + @Override + public void startFootnote(final Footnote footnote) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFootnote(footnote); + } + }); + } + + @Override + public void endFootnote(final Footnote footnote) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFootnote(footnote); + } + }); + } + + @Override + public void startFootnoteBody(final FootnoteBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFootnoteBody(body); + } + }); + } + + @Override + public void endFootnoteBody(final FootnoteBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFootnoteBody(body); + } + }); + } + + @Override + public void startLeader(final Leader l) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startLeader(l); + } + }); + } + + @Override + public void endLeader(final Leader l) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endLeader(l); + } + }); + } + + @Override + public void startWrapper(final Wrapper wrapper) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startWrapper(wrapper); + } + }); + } + + @Override + public void endWrapper(final Wrapper wrapper) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endWrapper(wrapper); + } + }); + } + + @Override + public void character(final Character c) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.character(c); + } + }); + } + + @Override + public void characters(final FOText foText) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.characters(foText); + } + }); + } + +} diff --git a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java new file mode 100644 index 000000000..b5cd0a5a4 --- /dev/null +++ b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java @@ -0,0 +1,408 @@ +/* + * 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.fo; + +import java.util.Locale; + +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FOText; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.flow.AbstractGraphics; +import org.apache.fop.fo.flow.BasicLink; +import org.apache.fop.fo.flow.Block; +import org.apache.fop.fo.flow.BlockContainer; +import org.apache.fop.fo.flow.Character; +import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.Footnote; +import org.apache.fop.fo.flow.FootnoteBody; +import org.apache.fop.fo.flow.Inline; +import org.apache.fop.fo.flow.InstreamForeignObject; +import org.apache.fop.fo.flow.ListBlock; +import org.apache.fop.fo.flow.ListItem; +import org.apache.fop.fo.flow.ListItemBody; +import org.apache.fop.fo.flow.ListItemLabel; +import org.apache.fop.fo.flow.PageNumber; +import org.apache.fop.fo.flow.PageNumberCitation; +import org.apache.fop.fo.flow.PageNumberCitationLast; +import org.apache.fop.fo.flow.Wrapper; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; +import org.apache.fop.fo.flow.table.TableCell; +import org.apache.fop.fo.flow.table.TableFooter; +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.StaticContent; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; +import org.apache.fop.util.XMLUtil; + +/** + * A bridge between {@link FOEventHandler} and {@link StructureTreeEventHandler}. + */ +class StructureTreeEventTrigger extends FOEventHandler { + + private StructureTreeEventHandler structureTreeEventHandler; + + public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { + this.structureTreeEventHandler = structureTreeEventHandler; + } + + @Override + public void startDocument() throws SAXException { + } + + @Override + public void endDocument() throws SAXException { + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + Locale locale = null; + if (pageSeq.getLanguage() != null) { + if (pageSeq.getCountry() != null) { + locale = new Locale(pageSeq.getLanguage(), pageSeq.getCountry()); + } else { + locale = new Locale(pageSeq.getLanguage()); + } + } + structureTreeEventHandler.startPageSequence(locale); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + structureTreeEventHandler.endPageSequence(); + } + + @Override + public void startPageNumber(PageNumber pagenum) { + startElementWithID(pagenum); + } + + @Override + public void endPageNumber(PageNumber pagenum) { + endElement(pagenum); + } + + @Override + public void startPageNumberCitation(PageNumberCitation pageCite) { + startElementWithID(pageCite); + } + + @Override + public void endPageNumberCitation(PageNumberCitation pageCite) { + endElement(pageCite); + } + + @Override + public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { + startElementWithID(pageLast); + } + + @Override + public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { + endElement(pageLast); + } + + @Override + public void startFlow(Flow fl) { + startElement(fl); + } + + @Override + public void endFlow(Flow fl) { + endElement(fl); + } + + @Override + public void startBlock(Block bl) { + startElement(bl); + } + + @Override + public void endBlock(Block bl) { + endElement(bl); + } + + @Override + public void startBlockContainer(BlockContainer blc) { + startElement(blc); + } + + @Override + public void endBlockContainer(BlockContainer blc) { + endElement(blc); + } + + @Override + public void startInline(Inline inl) { + startElement(inl); + } + + @Override + public void endInline(Inline inl) { + endElement(inl); + } + + @Override + public void startTable(Table tbl) { + startElement(tbl); + } + + @Override + public void endTable(Table tbl) { + endElement(tbl); + } + + @Override + public void startHeader(TableHeader header) { + startElement(header); + } + + @Override + public void endHeader(TableHeader header) { + endElement(header); + } + + @Override + public void startFooter(TableFooter footer) { + startElement(footer); + } + + @Override + public void endFooter(TableFooter footer) { + endElement(footer); + } + + @Override + public void startBody(TableBody body) { + startElement(body); + } + + @Override + public void endBody(TableBody body) { + endElement(body); + } + + @Override + public void startRow(TableRow tr) { + startElement(tr); + } + + @Override + public void endRow(TableRow tr) { + endElement(tr); + } + + @Override + public void startCell(TableCell tc) { + AttributesImpl attributes = new AttributesImpl(); + int colSpan = tc.getNumberColumnsSpanned(); + if (colSpan > 1) { + addNoNamespaceAttribute(attributes, "number-columns-spanned", + Integer.toString(colSpan)); + } + startElement(tc, attributes); + } + + @Override + public void endCell(TableCell tc) { + endElement(tc); + } + + @Override + public void startList(ListBlock lb) { + startElement(lb); + } + + @Override + public void endList(ListBlock lb) { + endElement(lb); + } + + @Override + public void startListItem(ListItem li) { + startElement(li); + } + + @Override + public void endListItem(ListItem li) { + endElement(li); + } + + @Override + public void startListLabel(ListItemLabel listItemLabel) { + startElement(listItemLabel); + } + + @Override + public void endListLabel(ListItemLabel listItemLabel) { + endElement(listItemLabel); + } + + @Override + public void startListBody(ListItemBody listItemBody) { + startElement(listItemBody); + } + + @Override + public void endListBody(ListItemBody listItemBody) { + endElement(listItemBody); + } + + @Override + public void startStatic(StaticContent staticContent) { + startElement(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + endElement(statisContent); + } + + @Override + public void startLink(BasicLink basicLink) { + startElementWithID(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + endElement(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + startElementWithIDAndAltText(eg); + endElement(eg); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + startElementWithIDAndAltText(ifo); + } + + @Override + public void endInstreamForeignObject(InstreamForeignObject ifo) { + endElement(ifo); + } + + @Override + public void startFootnote(Footnote footnote) { + startElement(footnote); + } + + @Override + public void endFootnote(Footnote footnote) { + endElement(footnote); + } + + @Override + public void startFootnoteBody(FootnoteBody body) { + startElement(body); + } + + @Override + public void endFootnoteBody(FootnoteBody body) { + endElement(body); + } + + @Override + public void startWrapper(Wrapper wrapper) { + startElement(wrapper); + } + + @Override + public void endWrapper(Wrapper wrapper) { + endElement(wrapper); + } + + @Override + public void character(Character c) { + startElementWithID(c); + endElement(c); + } + + @Override + public void characters(FOText foText) { + startElementWithID(foText); + endElement(foText); + } + + + private void startElement(FONode node) { + startElement(node, new AttributesImpl()); + } + + private void startElementWithID(FONode node) { + AttributesImpl attributes = new AttributesImpl(); + String localName = node.getLocalName(); + if (node instanceof CommonAccessibilityHolder) { + addRole((CommonAccessibilityHolder) node, attributes); + } + node.setStructureTreeElement(structureTreeEventHandler.startReferencedNode(localName, attributes)); + } + + private void startElementWithIDAndAltText(AbstractGraphics node) { + AttributesImpl attributes = new AttributesImpl(); + String localName = node.getLocalName(); + addRole(node, attributes); + addAttribute(attributes, ExtensionElementMapping.URI, "alt-text", + ExtensionElementMapping.STANDARD_PREFIX, node.getAltText()); + node.setStructureTreeElement(structureTreeEventHandler.startImageNode(localName, attributes)); + } + + private StructureTreeElement startElement(FONode node, AttributesImpl attributes) { + String localName = node.getLocalName(); + if (node instanceof CommonAccessibilityHolder) { + addRole((CommonAccessibilityHolder) node, attributes); + } + return structureTreeEventHandler.startNode(localName, attributes); + } + + private void addNoNamespaceAttribute(AttributesImpl attributes, String name, String value) { + attributes.addAttribute("", name, name, XMLUtil.CDATA, value); + } + + private void addAttribute(AttributesImpl attributes, + String namespace, String localName, String prefix, String value) { + assert namespace.length() > 0 && prefix.length() > 0; + String qualifiedName = prefix + ":" + localName; + attributes.addAttribute(namespace, localName, qualifiedName, XMLUtil.CDATA, value); + } + + private void addRole(CommonAccessibilityHolder node, AttributesImpl attributes) { + String role = node.getCommonAccessibility().getRole(); + if (role != null) { + addNoNamespaceAttribute(attributes, "role", role); + } + } + + private void endElement(FONode node) { + String localName = node.getLocalName(); + structureTreeEventHandler.endNode(localName); + } + +} diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index 545c9015d..29a363495 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -728,7 +728,6 @@ public class AreaTreeParser { setTraits(attributes, ip, SUBSET_BOX); setTraits(attributes, ip, SUBSET_COLOR); setTraits(attributes, ip, SUBSET_LINK); - setPtr(ip, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(ip); areaStack.push(ip); @@ -777,7 +776,6 @@ public class AreaTreeParser { "tlsadjust", 0)); text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, "twsadjust", 0)); - setPtr(text, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(text); areaStack.push(text); @@ -870,7 +868,6 @@ public class AreaTreeParser { viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos")); viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false)); viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); - setPtr(viewport, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(viewport); areaStack.push(viewport); @@ -889,7 +886,6 @@ public class AreaTreeParser { transferForeignObjects(attributes, image); setAreaAttributes(attributes, image); setTraits(attributes, image, SUBSET_COMMON); - setPtr(image, attributes); getCurrentViewport().setContent(image); } } @@ -1174,13 +1170,6 @@ public class AreaTreeParser { } } - private void setPtr(Area area, Attributes attributes) { - String ptr = attributes.getValue("ptr"); - if (ptr != null) { - area.addTrait(Trait.PTR, ptr); - } - } - /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index 042b65337..d9194559d 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -153,8 +153,8 @@ public final class Trait implements Serializable { /** Trait for color of linethrough decorations when rendering inline parent. */ public static final Integer LINETHROUGH_COLOR = 36; - /** The ptr trait. Used for accessibility */ - public static final Integer PTR = 37; + /** For navigation in the document structure. */ + public static final Integer STRUCTURE_TREE_ELEMENT = 37; /** Maximum value used by trait keys */ public static final int MAX_TRAIT_KEY = 37; @@ -186,7 +186,7 @@ public final class Trait implements Serializable { static { // Create a hashmap mapping trait code to name for external representation //put(ID_LINK, new TraitInfo("id-link", String.class)); - put(PTR, new TraitInfo("ptr", String.class)); + put(STRUCTURE_TREE_ELEMENT, new TraitInfo("structure-tree-element", String.class)); put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); put(FONT, new TraitInfo("font", FontTriplet.class)); diff --git a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java index ed85bd1c9..e64106a80 100644 --- a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java +++ b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java @@ -387,8 +387,8 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { } @Override - public void characters(char[] data, int start, int length) { - delegate.characters(data, start, length); + public void characters(FOText foText) { + delegate.characters(foText); } @Override diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index 1f3514dea..bed1f3677 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -88,6 +88,10 @@ public abstract class FOEventHandler { this.fontInfo.setEventListener(new FontEventAdapter(foUserAgent.getEventBroadcaster())); } + /** Constructor for sub-classes that do not need an {@link FOUserAgent} instance. */ + protected FOEventHandler() { + } + /** * Returns the User Agent object associated with this FOEventHandler. * @return the User Agent object @@ -532,11 +536,9 @@ public abstract class FOEventHandler { /** * Process character data. - * @param data Array of characters to process. - * @param start Offset for characters to process. - * @param length Portion of array to process. + * @param foText text to process */ - public void characters(char[] data, int start, int length) { + public void characters(FOText foText) { } /** diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java index 4946f6d5c..622ff86d4 100644 --- a/src/java/org/apache/fop/fo/FONode.java +++ b/src/java/org/apache/fop/fo/FONode.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.extensions.ExtensionAttachment; @@ -912,7 +913,7 @@ public abstract class FONode implements Cloneable { } - public void setPtr(String ptr) { + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { throw new UnsupportedOperationException(); } diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 5db11f731..3858cdea9 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -25,6 +25,7 @@ import java.util.NoSuchElementException; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.flow.Block; @@ -79,6 +80,8 @@ public class FOText extends FONode implements CharSequence { /** Holds the text decoration values. May be null */ private CommonTextDecoration textDecoration; + private StructureTreeElement structureTreeElement; + private static final int IS_WORD_CHAR_FALSE = 0; private static final int IS_WORD_CHAR_TRUE = 1; private static final int IS_WORD_CHAR_MAYBE = 2; @@ -115,25 +118,14 @@ public class FOText extends FONode implements CharSequence { /** * Return the array of characters for this instance. * - * @return a char array containing the text + * @return a char sequence containing the text */ - public char[] getCharArray() { - + public CharSequence getCharSequence() { if (this.charBuffer == null) { return null; } - - if (this.charBuffer.hasArray()) { - return this.charBuffer.array(); - } - - // only if the buffer implementation has - // no accessible backing array, return a new one - char[] ca = new char[this.charBuffer.limit()]; this.charBuffer.rewind(); - this.charBuffer.get(ca); - return ca; - + return this.charBuffer.asReadOnlyBuffer().subSequence(0, this.charBuffer.limit()); } /** {@inheritDoc} */ @@ -176,8 +168,7 @@ public class FOText extends FONode implements CharSequence { /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); - getFOEventHandler().characters( - this.getCharArray(), 0, this.charBuffer.limit()); + getFOEventHandler().characters(this); } /** {@inheritDoc} */ @@ -670,4 +661,14 @@ public class FOText extends FONode implements CharSequence { this.charBuffer.rewind(); } } + + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; + } + } diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index b053692d6..ece4e5448 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -33,7 +33,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; -import org.apache.fop.accessibility.FO2StructureTreeConverter; +import org.apache.fop.accessibility.fo.FO2StructureTreeConverter; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FormattingResults; diff --git a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java index e939841e1..687952d25 100644 --- a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java @@ -37,11 +37,18 @@ public class InternalElementMapping extends ElementMapping { /** The standard XML prefix for elements and attributes in this namespace. */ public static final String STANDARD_PREFIX = "foi"; + /** The "struct-id" attribute, to identify a structure tree element. */ + public static final String STRUCT_ID = "struct-id"; + + /** The "struct-ref" attribute, to refer to a structure tree element. */ + public static final String STRUCT_REF = "struct-ref"; + private static final Set<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>(); static { //These are FOP's extension properties for accessibility - PROPERTY_ATTRIBUTES.add("ptr"); + PROPERTY_ATTRIBUTES.add(STRUCT_ID); + PROPERTY_ATTRIBUTES.add(STRUCT_REF); } /** diff --git a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java index 67a33d088..2b8fa8a0b 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java +++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java @@ -19,6 +19,7 @@ package org.apache.fop.fo.flow; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; @@ -31,7 +32,7 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_instream-foreign-object"> @@ -40,7 +41,7 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * <code>fo:external-graphic</code></a> flow formatting objects. */ public abstract class AbstractGraphics extends FObj - implements GraphicsProperties, StructurePointerPropertySet, CommonAccessibilityHolder { + implements GraphicsProperties, StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:instream-foreign-object // and external-graphics. @@ -66,7 +67,7 @@ public abstract class AbstractGraphics extends FObj private int textAlign; private Length width; private String altText; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // Unused but valid items, commented out for performance: // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; @@ -226,13 +227,13 @@ public abstract class AbstractGraphics extends FObj } @Override - public void setPtr(String ptr) { - this.ptr = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } public String getAltText() { diff --git a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java index bbb632b29..108896d91 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java +++ b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java @@ -24,6 +24,7 @@ import java.awt.Color; import org.xml.sax.Attributes; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.Constants; @@ -37,7 +38,7 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation"> @@ -46,7 +47,7 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * <code>fo:page-number-citation-last</code></a> objects. */ public abstract class AbstractPageNumberCitation extends FObj - implements StructurePointerPropertySet, CommonAccessibilityHolder { + implements StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:page-number-citation(-last). private CommonAccessibility commonAccessibility; @@ -56,7 +57,7 @@ public abstract class AbstractPageNumberCitation extends FObj private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; private String refId; @@ -151,13 +152,13 @@ public abstract class AbstractPageNumberCitation extends FObj } @Override - public void setPtr(String ptr) { - this.ptr = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** @return the "alignment-adjust" property */ diff --git a/src/java/org/apache/fop/fo/flow/BasicLink.java b/src/java/org/apache/fop/fo/flow/BasicLink.java index 7bff2d521..0d6d5d9b4 100644 --- a/src/java/org/apache/fop/fo/flow/BasicLink.java +++ b/src/java/org/apache/fop/fo/flow/BasicLink.java @@ -21,12 +21,13 @@ package org.apache.fop.fo.flow; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_basic-link"> @@ -36,14 +37,14 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * and whether that link is external (uses a URI) or internal (an id * reference). */ -public class BasicLink extends InlineLevel implements StructurePointerPropertySet { +public class BasicLink extends InlineLevel implements StructureTreeElementHolder { // The value of properties relevant for fo:basic-link. private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty destinationPlacementOffset; private String externalDestination; // private ToBeImplementedProperty indicateDestination; @@ -143,13 +144,13 @@ public class BasicLink extends InlineLevel implements StructurePointerPropertySe } @Override - public void setPtr(String ptr) { - this.ptr = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** diff --git a/src/java/org/apache/fop/fo/flow/Block.java b/src/java/org/apache/fop/fo/flow/Block.java index b1705288a..4e5b6f15f 100644 --- a/src/java/org/apache/fop/fo/flow/Block.java +++ b/src/java/org/apache/fop/fo/flow/Block.java @@ -42,13 +42,12 @@ import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonRelativePosition; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_block"> * <code>fo:block object</code></a>. */ -public class Block extends FObjMixed implements BreakPropertySet, StructurePointerPropertySet, +public class Block extends FObjMixed implements BreakPropertySet, CommonAccessibilityHolder { // used for FO validation @@ -77,7 +76,6 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint private int lineHeightShiftAdjustment; private int lineStackingStrategy; private Numeric orphans; - private String ptr; //used for accessibility private int whiteSpaceTreatment; private int span; private int textAlign; @@ -183,16 +181,6 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint return breakAfter; } - @Override - public void setPtr(String ptr) { - this.ptr = ptr; - } - - /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - /** @return the "break-before" property. */ public int getBreakBefore() { return breakBefore; diff --git a/src/java/org/apache/fop/fo/flow/Character.java b/src/java/org/apache/fop/fo/flow/Character.java index a33bea53d..c4de9fb72 100644 --- a/src/java/org/apache/fop/fo/flow/Character.java +++ b/src/java/org/apache/fop/fo/flow/Character.java @@ -24,6 +24,7 @@ import java.util.NoSuchElementException; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.CharIterator; @@ -38,13 +39,13 @@ import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_character"> * <code>fo:character</code></a> object. */ -public class Character extends FObj implements StructurePointerPropertySet { +public class Character extends FObj implements StructureTreeElementHolder { // The value of properties relevant for fo:character. private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonFont commonFont; @@ -63,7 +64,7 @@ public class Character extends FObj implements StructurePointerPropertySet { private CommonTextDecoration textDecoration; // private ToBeImplementedProperty textShadow; private Property wordSpacing; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // Unused but valid items, commented out for performance: // private CommonAural commonAural; // private CommonMarginInline commonMarginInline; @@ -210,13 +211,13 @@ public class Character extends FObj implements StructurePointerPropertySet { } @Override - public void setPtr(String ptr) { - this.ptr = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/Inline.java b/src/java/org/apache/fop/fo/flow/Inline.java index e458184f4..debf6bbf6 100644 --- a/src/java/org/apache/fop/fo/flow/Inline.java +++ b/src/java/org/apache/fop/fo/flow/Inline.java @@ -26,19 +26,17 @@ import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; -import org.apache.fop.fo.properties.StructurePointerPropertySet; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_inline"> * <code>fo:inline</code></a> formatting object. */ -public class Inline extends InlineLevel implements StructurePointerPropertySet { +public class Inline extends InlineLevel { // The value of properties relevant for fo:inline. // See also superclass InlineLevel private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; - private String ptr; // used for accessibility private int dominantBaseline; // Unused but valid items, commented out for performance: // private CommonRelativePosition commonRelativePosition; @@ -147,16 +145,6 @@ public class Inline extends InlineLevel implements StructurePointerPropertySet { return dominantBaseline; } - @Override - public void setPtr(String ptr) { - this.ptr = ptr; - } - - /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - /** {@inheritDoc} */ public String getLocalName() { return "inline"; diff --git a/src/java/org/apache/fop/fo/flow/PageNumber.java b/src/java/org/apache/fop/fo/flow/PageNumber.java index c0373aa7f..0e5ce5071 100644 --- a/src/java/org/apache/fop/fo/flow/PageNumber.java +++ b/src/java/org/apache/fop/fo/flow/PageNumber.java @@ -23,6 +23,7 @@ import java.awt.Color; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.Constants; @@ -36,14 +37,14 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-number"> * <code>fo:page-number</code></a> object. */ public class PageNumber extends FObj - implements StructurePointerPropertySet, CommonAccessibilityHolder { + implements StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:page-number. private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; @@ -52,7 +53,7 @@ public class PageNumber extends FObj private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; /** Holds the text decoration values. May be null */ @@ -176,13 +177,13 @@ public class PageNumber extends FObj } @Override - public void setPtr(String ptr) { - this.ptr = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java index 0da7b9458..2506c26b4 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java @@ -36,19 +36,17 @@ import org.apache.fop.fo.properties.EnumProperty; import org.apache.fop.fo.properties.NumberProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.PropertyMaker; -import org.apache.fop.fo.properties.StructurePointerPropertySet; import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * Common base class for table-related FOs */ -public abstract class TableFObj extends FObj implements StructurePointerPropertySet { +public abstract class TableFObj extends FObj { private Numeric borderAfterPrecedence; private Numeric borderBeforePrecedence; private Numeric borderEndPrecedence; private Numeric borderStartPrecedence; - private String ptr; ConditionalBorder borderBefore; // CSOK: VisibilityModifier ConditionalBorder borderAfter; // CSOK: VisibilityModifier @@ -240,16 +238,6 @@ public abstract class TableFObj extends FObj implements StructurePointerProperty } } - @Override - public void setPtr(String ptr) { - this.ptr = ptr; - } - - /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - /** * Prepares the borders of this element if the collapsing-border model is in use. * Conflict resolution with parent elements is done where applicable. diff --git a/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java b/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java new file mode 100644 index 000000000..6fbb608de --- /dev/null +++ b/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java @@ -0,0 +1,38 @@ +/* + * 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.fo.properties; + +import org.apache.fop.accessibility.StructureTreeElement; + +/** + * Implementations of this interface can return the element in the document's + * structure tree that they resulted into. Used for tagged PDF and other formats + * that support a structure tree in addition to paged content. + */ +public interface StructureTreeElementHolder { + + /** + * Returns the element in the document's structure tree that corresponds to this instance. + * + * @return a structure tree element + */ + StructureTreeElement getStructureTreeElement(); + +} diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index b748c2152..ed2ea4ee9 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -329,7 +329,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); - TraitSetter.addPtr(curBlockArea, getBlockFO().getPtr()); // used for accessibility TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); flush(); diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index c0e451577..da548c23c 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.area.Area; import org.apache.fop.area.Trait; import org.apache.fop.datatypes.LengthBase; @@ -29,9 +30,9 @@ import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonTextDecoration; -import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.fonts.Font; import org.apache.fop.traits.BorderProps; import org.apache.fop.traits.MinOptMax; @@ -591,13 +592,14 @@ public final class TraitSetter { } /** - * Adds the ptr trait to the area. + * Sets the structure tree element associated to the given area. + * * @param area the area to set the traits on - * @param ptr string + * @param structureTreeElement the element the area is associated to in the document structure */ - public static void addPtr(Area area, String ptr) { - if (ptr != null && ptr.length() > 0) { - area.addTrait(Trait.PTR, ptr); + public static void addStructureTreeElement(Area area, StructureTreeElement structureTreeElement) { + if (structureTreeElement != null) { + area.addTrait(Trait.STRUCTURE_TREE_ELEMENT, structureTreeElement); } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java index 218497a53..8c797a48c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java @@ -85,7 +85,7 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage transferForeignAttributes(viewportArea); InlineViewport vp = new InlineViewport(viewportArea); - TraitSetter.addPtr(vp, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(vp, fobj.getStructureTreeElement()); TraitSetter.setProducerID(vp, fobj.getId()); vp.setIPD(imageLayout.getViewportSize().width); vp.setBPD(imageLayout.getViewportSize().height); diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java index e090fbae6..8c769924a 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java @@ -136,7 +136,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa text.setBaselineOffset(font.getAscender()); TraitSetter.addFontTraits(text, font); text.addTrait(Trait.COLOR, fobj.getColor()); - TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, fobj.getStructureTreeElement()); TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java index 40c9a324e..1390c04d8 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java @@ -59,7 +59,7 @@ public class BasicLinkLayoutManager extends InlineLayoutManager { private void setupBasicLinkArea(InlineArea area) { BasicLink fobj = (BasicLink) this.fobj; // internal destinations take precedence: - TraitSetter.addPtr(area, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(area, fobj.getStructureTreeElement()); if (fobj.hasInternalDestination()) { String idref = fobj.getInternalDestination(); PageSequenceLayoutManager pslm = getPSLM(); diff --git a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java index 2178b2e77..4877ff9bd 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java @@ -87,7 +87,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { } TraitSetter.setProducerID(text, node.getId()); TraitSetter.addTextDecoration(text, node.getTextDecoration()); - TraitSetter.addPtr(text, node.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, node.getStructureTreeElement()); return text; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java index d8cfe6cda..4b7289b37 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java @@ -19,13 +19,13 @@ package org.apache.fop.layoutmgr.inline; +import org.apache.fop.area.Trait; +import org.apache.fop.area.inline.InlineArea; +import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.Trait; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; @@ -85,7 +85,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { text.setBaselineOffset(font.getAscender()); TraitSetter.addFontTraits(text, font); text.addTrait(Trait.COLOR, fobj.getColor()); - TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, fobj.getStructureTreeElement()); TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); return text; diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index 81240dec0..8767fe296 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -31,8 +31,6 @@ import org.apache.fop.area.Trait; import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FOText; -import org.apache.fop.fo.FObj; -import org.apache.fop.fo.properties.StructurePointerPropertySet; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontSelector; import org.apache.fop.layoutmgr.InlineKnuthSequence; @@ -438,7 +436,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { setText(); TraitSetter.addFontTraits(textArea, font); textArea.addTrait(Trait.COLOR, foText.getColor()); - TraitSetter.addPtr(textArea, getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(textArea, foText.getStructureTreeElement()); TraitSetter.addTextDecoration(textArea, foText.getTextDecoration()); TraitSetter.addFontTraits(textArea, font); return textArea; @@ -577,20 +575,6 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } } - /** - * used for accessibility - * @return ptr of fobj - */ - private String getPtr() { - FObj fobj = parentLayoutManager.getFObj(); - if (fobj instanceof StructurePointerPropertySet) { - return (((StructurePointerPropertySet) fobj).getPtr()); - } else { - //No structure pointer applicable - return null; - } - } - private AreaInfo getAreaInfo(int index) { return (AreaInfo) areaInfos.get(index); } diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index 6dfb0dc95..44622638d 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -19,18 +19,29 @@ package org.apache.fop.pdf; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.util.LanguageTags; /** * Class representing a PDF Structure Element. */ -public class PDFStructElem extends PDFDictionary { +public class PDFStructElem extends PDFDictionary implements StructureTreeElement { private PDFStructElem parentElement; /** + * Elements to be added to the kids array. + */ + protected List<PDFObject> kids; + + /** * Creates a new structure element. * * @param parent parent of this element @@ -57,21 +68,12 @@ public class PDFStructElem extends PDFDictionary { /** {@inheritDoc} */ public void setParent(PDFObject parent) { - if (parent != null) { + if (parent != null && parent.hasObjectNumber()) { put("P", new PDFReference(parent)); } } /** - * Returns the kids of this structure element. - * - * @return the value of the K entry - */ - private PDFArray getKids() { - return (PDFArray) get("K"); - } - - /** * Add a kid to this structure element. This element will then add itself to * its parent structure element if it has not already, and so will the * parent, and so on. @@ -79,24 +81,10 @@ public class PDFStructElem extends PDFDictionary { * @param kid element to be added */ public void addKid(PDFObject kid) { - PDFArray kids = getKids(); if (kids == null) { - kids = new PDFArray(); - put("K", kids); + kids = new ArrayList<PDFObject>(); } kids.add(kid); - joinHierarchy(); - } - - private boolean containsKid(PDFObject kid) { - PDFArray kids = getKids(); - return kids != null && kids.contains(kid); - } - - private void joinHierarchy() { - if (parentElement != null && !parentElement.containsKid(this)) { - parentElement.addKid(this); - } } /** @@ -109,7 +97,6 @@ public class PDFStructElem extends PDFDictionary { */ public void setMCIDKid(int mcid) { put("K", mcid); - joinHierarchy(); } /** @@ -127,7 +114,7 @@ public class PDFStructElem extends PDFDictionary { * @return the value of the S entry */ public PDFName getStructureType() { - return (PDFName)get("S"); + return (PDFName) get("S"); } /** @@ -154,6 +141,63 @@ public class PDFStructElem extends PDFDictionary { * @return the value of the Lang entry (<code>null</code> if no language was specified) */ public String getLanguage() { - return (String)get("Lang"); + return (String) get("Lang"); + } + + @Override + protected void writeDictionary(OutputStream out, Writer writer) throws IOException { + attachKids(); + super.writeDictionary(out, writer); + } + + /** + * Attaches all valid kids to the kids array. + * + * @return true iff 1+ kids were added to the kids array + */ + protected boolean attachKids() { + List<PDFObject> validKids = new ArrayList<PDFObject>(); + if (kids != null) { + for (PDFObject kid : kids) { + if (kid instanceof Placeholder) { + if (((Placeholder) kid).attachKids()) { + validKids.add(kid); + } + } else { + validKids.add(kid); + } + } + } + boolean kidsAttached = !validKids.isEmpty(); + if (kidsAttached) { + PDFArray array = new PDFArray(); + for (PDFObject ob : validKids) { + array.add(ob); + } + put("K", array); + } + return kidsAttached; } + + public static class Placeholder extends PDFStructElem { + + @Override + public void outputInline(OutputStream out, Writer writer) throws IOException { + if (kids != null) { + assert kids.size() > 0; + for (int i = 0; i < kids.size(); i++) { + if (i > 0) { + writer.write(' '); + } + Object obj = kids.get(i); + formatObject(obj, out, writer); + } + } + } + + public Placeholder(PDFObject parent, String name) { + super(parent, new PDFName(name)); + } + } + } diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java index b8be97253..c59a02ba8 100644 --- a/src/java/org/apache/fop/render/intermediate/IFContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFContext.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOUserAgent; /** @@ -46,7 +47,7 @@ public class IFContext { private Locale language; - private String structurePointer; + private StructureTreeElement structureTreeElement; private String id = ""; @@ -132,29 +133,31 @@ public class IFContext { } /** - * Sets the structure pointer for the following painted marks. This method is used when - * accessibility features are enabled. - * @param ptr the structure pointer + * Sets the structure tree element to which the subsequently painted marks + * will correspond. This method is used when accessibility features are + * enabled. + * + * @param structureTreeElement the structure tree element */ - public void setStructurePointer(String ptr) { - this.structurePointer = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** - * Resets the current structure pointer. - * @see #setStructurePointer(String) + * Resets the current structure tree element. + * @see #setStructureTreeElement(String) */ - public void resetStructurePointer() { - setStructurePointer(null); + public void resetStructureTreeElement() { + setStructureTreeElement(null); } /** - * Returns the current structure pointer. - * @return the structure pointer (or null if no pointer is active) - * @see #setStructurePointer(String) + * Returns the current structure tree element. + * @return the structure tree element (or null if no element is active) + * @see #setStructureTreeElement(String) */ - public String getStructurePointer() { - return this.structurePointer; + public StructureTreeElement getStructureTreeElement() { + return this.structureTreeElement; } /** diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 8f0bb88ec..1a384c6e9 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -49,11 +49,13 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; import org.apache.fop.accessibility.AccessibilityEventProducer; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants; import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler; import org.apache.fop.traits.BorderProps; @@ -158,6 +160,9 @@ public class IFParser implements IFConstants { private Attributes pageSequenceAttributes; + private Map<String, StructureTreeElement> structureTreeElements = + new HashMap<String, StructureTreeElement>(); + private final class StructureTreeHandler extends DefaultHandler { private final StructureTreeEventHandler structureTreeEventHandler; @@ -177,7 +182,23 @@ public class IFParser implements IFConstants { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (!"structure-tree".equals(localName)) { - structureTreeEventHandler.startNode(localName, attributes); + if (localName.equals("marked-content")) { + localName = "#PCDATA"; + } + String structID = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID); + if (structID == null) { + structureTreeEventHandler.startNode(localName, attributes); + } else if (localName.equals("external-graphic") + || localName.equals("instream-foreign-object")) { + StructureTreeElement structureTreeElement = + structureTreeEventHandler.startImageNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } else { + StructureTreeElement structureTreeElement = + structureTreeEventHandler.startReferencedNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } } } @@ -225,14 +246,6 @@ public class IFParser implements IFConstants { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); - } - - private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); - } - /** {@inheritDoc} */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { @@ -276,7 +289,7 @@ public class IFParser implements IFConstants { } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { if (this.navParser == null) { this.navParser = new DocumentNavigationHandler( - this.documentHandler.getDocumentNavigationHandler()); + this.documentHandler.getDocumentNavigationHandler(), structureTreeElements); } delegate = this.navParser; delegateDepth++; @@ -604,9 +617,9 @@ public class IFParser implements IFConstants { s = lastAttributes.getValue("word-spacing"); int wordSpacing = (s != null ? Integer.parseInt(s) : 0); int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx"); - setStructurePointer(lastAttributes); + establishStructureTreeElement(lastAttributes); painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString()); - resetStructurePointer(); + resetStructureTreeElement(); } public boolean ignoreCharacters() { @@ -701,7 +714,7 @@ public class IFParser implements IFConstants { int height = Integer.parseInt(lastAttributes.getValue("height")); Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); - setStructurePointer(lastAttributes); + establishStructureTreeElement(lastAttributes); if (foreignObject != null) { painter.drawImage(foreignObject, new Rectangle(x, y, width, height)); @@ -715,7 +728,7 @@ public class IFParser implements IFConstants { painter.drawImage(uri, new Rectangle(x, y, width, height)); } resetForeignAttributes(); - resetStructurePointer(); + resetStructureTreeElement(); inForeignObject = false; } @@ -769,13 +782,20 @@ public class IFParser implements IFConstants { return foreignAttributes; } - private void setStructurePointer(Attributes attributes) { - String ptr = attributes.getValue("ptr"); - if (ptr != null && ptr.length() > 0) { - establishStructurePointer(ptr); + private void establishStructureTreeElement(Attributes attributes) { + String structRef = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF); + if (structRef != null && structRef.length() > 0) { + assert structureTreeElements.containsKey(structRef); + StructureTreeElement structureTreeElement = structureTreeElements.get(structRef); + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } } + private void resetStructureTreeElement() { + documentHandler.getContext().resetStructureTreeElement(); + } + /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index d217646f6..a2d8a0bea 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -50,6 +50,7 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; import org.apache.fop.Version; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; @@ -629,12 +630,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); + private void establishStructureTreeElement(StructureTreeElement structureTreeElement) { + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); + documentHandler.getContext().resetStructureTreeElement(); } /** {@inheritDoc} */ @@ -851,8 +852,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { /** {@inheritDoc} */ public void renderInlineViewport(InlineViewport viewport) { - String ptr = (String) viewport.getTrait(Trait.PTR); - establishStructurePointer(ptr); + StructureTreeElement structElem = + (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); pushdID(viewport); Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); viewportDimensionStack.push(dim); @@ -912,7 +914,6 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // stuff we only need if a link must be created: Rectangle ipRect = null; AbstractAction action = null; - String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility // make sure the rect is determined *before* calling super! int ipp = currentIPPosition; int bpp = currentBPPosition + ip.getOffset(); @@ -956,7 +957,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // warn if link trait found but not allowed, else create link if (linkTraitFound) { - action.setStructurePointer(ptr); // used for accessibility + StructureTreeElement structElem = + (StructureTreeElement) ip.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + action.setStructureTreeElement(structElem); Link link = new Link(action, ipRect); this.deferredLinks.add(link); } @@ -1009,8 +1012,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer { String fontName = getInternalFontNameForArea(text); int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); - String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility - establishStructurePointer(ptr); + StructureTreeElement structElem = (StructureTreeElement) text.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); // This assumes that *all* CIDFonts use a /ToUnicode mapping Typeface tf = getTypeface(fontName); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index a4431b972..c6bf9af9e 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -38,9 +38,11 @@ import org.apache.xmlgraphics.util.QName; import org.apache.xmlgraphics.util.XMLizable; import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.PrintRendererConfigurator; import org.apache.fop.render.RenderingContext; +import org.apache.fop.render.intermediate.IFStructureTreeBuilder.IFStructureTreeElement; import org.apache.fop.render.intermediate.extensions.AbstractAction; import org.apache.fop.render.intermediate.extensions.Bookmark; import org.apache.fop.render.intermediate.extensions.BookmarkTree; @@ -163,6 +165,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE); handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX, DocumentNavigationExtensionConstants.NAMESPACE); + handler.startPrefixMapping(InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI); handler.startElement(EL_DOCUMENT); } catch (SAXException e) { throw new IFException("SAX error in startDocument()", e); @@ -439,7 +442,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.element(EL_IMAGE, atts); } catch (SAXException e) { throw new IFException("SAX error in startGroup()", e); @@ -467,7 +470,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_IMAGE, atts); new DOM2SAX(handler).writeDocument(doc, true); handler.endElement(EL_IMAGE); @@ -582,7 +585,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler if (dx != null) { addAttribute(atts, "dx", IFUtil.toString(dx)); } - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_TEXT, atts); char[] chars = text.toCharArray(); handler.characters(chars, 0, chars.length); @@ -682,13 +685,22 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler XMLUtil.addAttribute(atts, localName, value); } - private void addStructurePointerAttribute(AttributesImpl atts) { - String ptr = getContext().getStructurePointer(); - if (ptr != null) { - addAttribute(atts, "ptr", ptr); + private void addStructureReference(AttributesImpl atts) { + IFStructureTreeElement structureTreeElement = + (IFStructureTreeElement) getContext().getStructureTreeElement(); + if (structureTreeElement != null) { + addStructRefAttribute(atts, structureTreeElement.id); } } + private void addStructRefAttribute(AttributesImpl atts, String id) { + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_REF, + XMLConstants.CDATA, + id); + } + private void addID() throws SAXException { String id = getContext().getID(); if (!currentID.equals(id)) { @@ -773,7 +785,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler atts.addAttribute(null, "rect", "rect", XMLConstants.CDATA, IFUtil.toString(link.getTargetRect())); if (getUserAgent().isAccessibilityEnabled()) { - addAttribute(atts, "ptr", link.getAction().getStructurePointer()); + addStructRefAttribute(atts, + ((IFStructureTreeElement) link.getAction().getStructureTreeElement()).id); } try { handler.startElement(DocumentNavigationExtensionConstants.LINK, atts); diff --git a/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java index 66457681f..b78ae35bc 100644 --- a/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java @@ -26,10 +26,14 @@ 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.xml.sax.helpers.DefaultHandler; import org.apache.fop.accessibility.StructureTree2SAXEventAdapter; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.util.XMLUtil; /** * Saves structure tree events as SAX events in order to replay them when it's @@ -37,42 +41,17 @@ import org.apache.fop.accessibility.StructureTreeEventHandler; */ 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); - } + static final class IFStructureTreeElement implements StructureTreeElement { - /** {@inheritDoc} */ - public void endPageSequence() { - delegate.endPageSequence(); - } + final String id; - /** {@inheritDoc} */ - public void startNode(String name, Attributes attributes) { - delegate.startNode(name, attributes); - } + IFStructureTreeElement() { + this.id = null; + } - /** {@inheritDoc} */ - public void endNode(String name) { - delegate.endNode(name); + IFStructureTreeElement(String id) { + this.id = id; + } } /** A SAX handler that records events to replay them later. */ @@ -159,22 +138,22 @@ final class IFStructureTreeBuilder implements StructureTreeEventHandler { 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. @@ -187,4 +166,69 @@ final class IFStructureTreeBuilder implements StructureTreeEventHandler { } } } + + private StructureTreeEventHandler delegate; + + private final List<SAXEventRecorder> pageSequenceEventRecorders = new ArrayList<SAXEventRecorder>(); + + private int idCounter; + + /** + * 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); + } + + public void startPageSequence(Locale locale) { + SAXEventRecorder eventRecorder = new SAXEventRecorder(); + pageSequenceEventRecorders.add(eventRecorder); + delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder); + delegate.startPageSequence(locale); + } + + public void endPageSequence() { + delegate.endPageSequence(); + } + + public StructureTreeElement startNode(String name, Attributes attributes) { + delegate.startNode(name, attributes); + return new IFStructureTreeElement(); + } + + public void endNode(String name) { + delegate.endNode(name); + } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startImageNode(name, atts); + return new IFStructureTreeElement(id); + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startReferencedNode(name, atts); + return new IFStructureTreeElement(id); + } + + private String getNextID() { + return Integer.toHexString(idCounter++); + } + + private AttributesImpl addIDAttribute(Attributes attributes, String id) { + AttributesImpl atts = new AttributesImpl(attributes); + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_ID, + XMLUtil.CDATA, + id); + return atts; + } } diff --git a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java index 340b2e068..a2595d320 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java @@ -21,13 +21,15 @@ package org.apache.fop.render.intermediate.extensions; import org.apache.xmlgraphics.util.XMLizable; +import org.apache.fop.accessibility.StructureTreeElement; + /** * Abstract base class for document actions, like "go-to" actions with absolute page coordinates. */ public abstract class AbstractAction implements XMLizable { private String id; - private String structurePointer; + private StructureTreeElement structureTreeElement; /** * Sets an ID to make the action referencable. @@ -47,18 +49,18 @@ public abstract class AbstractAction implements XMLizable { /** * Sets the structure element corresponding to this action. - * @param structurePointer a reference to the structure element + * @param structureTreeElement a reference to the structure element */ - public void setStructurePointer(String structurePointer) { - this.structurePointer = structurePointer; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** * Returns the structure element corresponding to this action. * @return the reference to the structure element */ - public String getStructurePointer() { - return structurePointer; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java index 1e613d7eb..693497b73 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java @@ -21,6 +21,7 @@ package org.apache.fop.render.intermediate.extensions; import java.awt.Point; import java.awt.Rectangle; +import java.util.Map; import java.util.Stack; import org.xml.sax.Attributes; @@ -30,6 +31,8 @@ import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.util.XMLUtil; @@ -48,14 +51,20 @@ public class DocumentNavigationHandler extends DefaultHandler private IFDocumentNavigationHandler navHandler; - private String structurePointer; + private StructureTreeElement structureTreeElement; + + private Map<String, StructureTreeElement> structureTreeElements; /** * Main constructor. * @param navHandler the navigation handler that will receive the events + * @param structureTreeElements the elements representing the structure of the document */ - public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler) { + public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler, + Map<String, StructureTreeElement> structureTreeElements) { this.navHandler = navHandler; + assert structureTreeElements != null; + this.structureTreeElements = structureTreeElements; } /** {@inheritDoc} */ @@ -98,7 +107,8 @@ public class DocumentNavigationHandler extends DefaultHandler throw new SAXException(localName + " must be the root element!"); } Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect"); - structurePointer = attributes.getValue("ptr"); + structureTreeElement = structureTreeElements.get( + attributes.getValue(InternalElementMapping.URI, InternalElementMapping.STRUCT_REF)); Link link = new Link(null, targetRect); objectStack.push(link); } else if (GOTO_XY.getLocalName().equals(localName)) { @@ -121,8 +131,8 @@ public class DocumentNavigationHandler extends DefaultHandler } action = new GoToXYAction(id, pageIndex, location); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else if (GOTO_URI.getLocalName().equals(localName)) { @@ -134,8 +144,8 @@ public class DocumentNavigationHandler extends DefaultHandler if (id != null) { action.setID(id); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else { diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java index c8fa481d4..2236778b5 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java @@ -31,6 +31,7 @@ import org.apache.fop.pdf.PDFFactory; import org.apache.fop.pdf.PDFGoTo; import org.apache.fop.pdf.PDFLink; import org.apache.fop.pdf.PDFOutline; +import org.apache.fop.pdf.PDFStructElem; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.extensions.AbstractAction; @@ -111,10 +112,9 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler PDFLink pdfLink = getPDFDoc().getFactory().makeLink( targetRect2D, pdfAction); if (pdfLink != null) { - String ptr = link.getAction().getStructurePointer(); - if (documentHandler.getUserAgent().isAccessibilityEnabled() - && ptr != null && ptr.length() > 0) { - documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, ptr); + PDFStructElem structure = (PDFStructElem) link.getAction().getStructureTreeElement(); + if (documentHandler.getUserAgent().isAccessibilityEnabled() && structure != null) { + documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, structure); } documentHandler.currentPage.addAnnotation(pdfLink); } diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java index d49ef4a3e..88a6e9c22 100644 --- a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java @@ -19,9 +19,7 @@ package org.apache.fop.render.pdf; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; import org.apache.fop.pdf.PDFArray; import org.apache.fop.pdf.PDFDictionary; @@ -47,11 +45,6 @@ class PDFLogicalStructureHandler { private final PDFDocument pdfDoc; - /** - * Map of references to the corresponding structure elements. - */ - private final Map<String, PDFStructElem> structTreeMap = new HashMap<String, PDFStructElem>(); - private final PDFParentTree parentTree = new PDFParentTree(); private int parentTreeKey; @@ -151,100 +144,79 @@ class PDFLogicalStructureHandler { parentTree.getNums().put(currentPage.getStructParents(), pageParentTreeArray); } - private MarkedContentInfo addToParentTree(String structurePointer) { - PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer); - if (parent == null) { - return ARTIFACT; - } else { - pageParentTreeArray.add(parent); - String type = parent.getStructureType().toString(); - int mcid = pageParentTreeArray.length() - 1; - return new MarkedContentInfo(type, mcid, parent); - } + private MarkedContentInfo addToParentTree(PDFStructElem structureTreeElement) { + PDFStructElem parent = (structureTreeElement instanceof PDFStructElem.Placeholder) + ? structureTreeElement.getParentStructElem() + : structureTreeElement; + pageParentTreeArray.add(parent); + String type = parent.getStructureType().toString(); + int mcid = pageParentTreeArray.length() - 1; + return new MarkedContentInfo(type, mcid, structureTreeElement); } /** * Adds a content item corresponding to text into the structure tree, if * there is a structure element associated to it. * - * @param structurePointer reference to the parent structure element of the - * piece of text + * @param structElem the parent structure element of the piece of text * @return the necessary information for bracketing the content as a * marked-content sequence. If there is no element in the structure tree * associated to that content, returns an instance whose * {@link MarkedContentInfo#tag} value is <code>null</code>. The content * must then be treated as an artifact. */ - MarkedContentInfo addTextContentItem(String structurePointer) { - MarkedContentInfo mci = addToParentTree(structurePointer); - if (mci != ARTIFACT) { + MarkedContentInfo addTextContentItem(PDFStructElem structElem) { + if (structElem == null) { + return ARTIFACT; + } else { + MarkedContentInfo mci = addToParentTree(structElem); PDFDictionary contentItem = new PDFDictionary(); contentItem.put("Type", MCR); contentItem.put("Pg", this.currentPage); contentItem.put("MCID", mci.mcid); mci.parent.addKid(contentItem); + return mci; } - return mci; } /** * Adds a content item corresponding to an image into the structure tree, if * there is a structure element associated to it. * - * @param structurePointer reference to the parent structure element of the - * image + * @param structElem the parent structure element of the image * @return the necessary information for bracketing the content as a * marked-content sequence. If there is no element in the structure tree * associated to that image, returns an instance whose - * {@link MarkedContentInfo#tag} value is <code>null</code>. The image - * must then be treated as an artifact. + * {@link MarkedContentInfo#tag} value is <code>null</code>. The image must + * then be treated as an artifact. */ - MarkedContentInfo addImageContentItem(String structurePointer) { - MarkedContentInfo mci = addToParentTree(structurePointer); - if (mci != ARTIFACT) { + MarkedContentInfo addImageContentItem(PDFStructElem structElem) { + if (structElem == null) { + return ARTIFACT; + } else { + MarkedContentInfo mci = addToParentTree(structElem); mci.parent.setMCIDKid(mci.mcid); mci.parent.setPage(this.currentPage); + return mci; } - return mci; } - // While the PDF spec allows images to be referred as PDF objects, this - // makes the Acrobat Pro checker complain that the image is not accessible. - // Its alt-text is still read aloud though. Using marked-content sequences - // like for text works. -// MarkedContentInfo addImageObject(String parentReference) { -// MarkedContentInfo mci = addToParentTree(parentReference); -// if (mci != ARTIFACT) { -// PDFDictionary contentItem = new PDFDictionary(); -// contentItem.put("Type", OBJR); -// contentItem.put("Pg", this.currentPage); -// contentItem.put("Obj", null); -// mci.parent.addKid(contentItem); -// } -// return mci; -// } - /** * Adds a content item corresponding to the given link into the structure * tree. * * @param link a link - * @param structurePointer reference to the corresponding parent structure element + * @param structureTreeElement its parent structure element */ - void addLinkContentItem(PDFLink link, String structurePointer) { + void addLinkContentItem(PDFLink link, PDFStructElem structureTreeElement) { int structParent = getNextParentTreeKey(); link.setStructParent(structParent); PDFDictionary contentItem = new PDFDictionary(); contentItem.put("Type", OBJR); contentItem.put("Pg", this.currentPage); contentItem.put("Obj", link); - PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer); - parentTree.getNums().put(structParent, parent); - parent.addKid(contentItem); - } - - void addStructurePointer(String ptr, PDFStructElem structElem) { - structTreeMap.put(ptr, structElem); + parentTree.getNums().put(structParent, structureTreeElement); + structureTreeElement.addKid(contentItem); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 11af216a2..f2fbfd014 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -37,6 +37,7 @@ import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.pdf.PDFStructElem; import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.RenderingContext; @@ -133,24 +134,24 @@ public class PDFPainter extends AbstractIFPainter { PDFXObject xobject = getPDFDoc().getXObject(uri); if (xobject != null) { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); placeImageAccess(rect, xobject); } else { placeImage(rect, xobject); } } else { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); } drawImageUsingURI(uri, rect); flushPDFDoc(); } } - private void prepareImageMCID(String ptr) { - imageMCI = logicalStructureHandler.addImageContentItem(ptr); + private void prepareImageMCID(PDFStructElem structElem) { + imageMCI = logicalStructureHandler.addImageContentItem(structElem); } /** {@inheritDoc} */ @@ -194,8 +195,8 @@ public class PDFPainter extends AbstractIFPainter { /** {@inheritDoc} */ public void drawImage(Document doc, Rectangle rect) throws IFException { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); } drawImageUsingDocument(doc, rect); flushPDFDoc(); @@ -294,8 +295,8 @@ public class PDFPainter extends AbstractIFPainter { String text) throws IFException { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(structElem); if (generator.getTextUtil().isInTextObject()) { generator.separateTextElements(mci.tag, mci.mcid); } diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 8ec10b209..3b5b00c33 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -24,10 +24,10 @@ import java.util.Locale; import org.xml.sax.Attributes; +import org.apache.fop.accessibility.StructureTreeElement; 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; @@ -61,33 +61,64 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { public void endPageSequence() { } - public void startNode(String name, Attributes attributes) { + public StructureTreeElement 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); + PDFStructElem created; + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); + parent.addKid(created); + ancestors.addFirst(created); + return created; + } + + public void endNode(String name) { + removeFirstAncestor(); + } + + private void removeFirstAncestor() { + ancestors.removeFirst(); + } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + PDFStructElem parent = ancestors.getFirst(); + String role = attributes.getValue("role"); + PDFStructElem created; + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); + parent.addKid(created); + 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); + return created; + } + + public void endImageNode(String name) { + removeFirstAncestor(); + } - 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"); - } + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + PDFStructElem parent = ancestors.getFirst(); + String role = attributes.getValue("role"); + PDFStructElem created; + if ("#PCDATA".equals(name)) { + created = new PDFStructElem.Placeholder(parent, name); + } else { + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, + eventBroadcaster), parent); } + parent.addKid(created); ancestors.addFirst(created); + return created; } - public void endNode(String name) { - ancestors.removeFirst(); + public void endReferencedNode(String name) { + removeFirstAncestor(); } } diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 8878b5d24..a3e3db461 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -117,13 +117,13 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfFootnote; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfHyperLink; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfList; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfListItem; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfListItem.RtfListItemLabel; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfPage; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfSection; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTable; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTableCell; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTableRow; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTextrun; -import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfListItem.RtfListItemLabel; import org.apache.fop.render.rtf.rtflib.tools.BuilderContext; import org.apache.fop.render.rtf.rtflib.tools.PercentContext; import org.apache.fop.render.rtf.rtflib.tools.TableContext; @@ -1362,11 +1362,9 @@ public class RTFHandler extends FOEventHandler { /** * @param text FOText object - * @param data Array of characters to process. - * @param start Offset for characters to process. - * @param length Portion of array to process. + * @param characters CharSequence of the characters to process. */ - public void text(FOText text, char[] data, int start, int length) { + public void text(FOText text, CharSequence characters) { if (bDefer) { return; } @@ -1381,7 +1379,7 @@ public class RTFHandler extends FOEventHandler { = TextAttributesConverter.convertCharacterAttributes(text); textrun.pushInlineAttributes(rtfAttr); - textrun.addString(new String(data, start, length - start)); + textrun.addString(characters.toString()); textrun.popInlineAttributes(); } catch (IOException ioe) { handleIOTrouble(ioe); @@ -1558,7 +1556,7 @@ public class RTFHandler extends FOEventHandler { } else if (foNode instanceof FOText) { if (bStart) { FOText text = (FOText) foNode; - text(text, text.getCharArray(), 0, text.length()); + text(text, text.getCharSequence()); } } else if (foNode instanceof Character) { if (bStart) { |