git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@830293 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
<include name="**/*.java"/> | <include name="**/*.java"/> | ||||
<exclude name="org/apache/fop/render/*/**/*.java"/> | <exclude name="org/apache/fop/render/*/**/*.java"/> | ||||
<exclude name="org/apache/fop/afp/**/*.java"/> | <exclude name="org/apache/fop/afp/**/*.java"/> | ||||
<exclude name="org/apache/fop/accessibility/**/*.java"/> | |||||
</fileset> | </fileset> | ||||
</eventResourceGenerator> | </eventResourceGenerator> | ||||
<fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/> | <fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/> | ||||
<eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/accessibility/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/accessibility/AccessibilityEventProducer.xml"> | |||||
<fileset dir="${src.java.dir}"> | |||||
<include name="org/apache/fop/accessibility/**/*.java"/> | |||||
</fileset> | |||||
</eventResourceGenerator> | |||||
<fixcrlf file="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml" tab="remove" tablength="2"/> | |||||
<eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/afp/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml"> | <eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/afp/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml"> | ||||
<fileset dir="${src.java.dir}"> | <fileset dir="${src.java.dir}"> | ||||
<include name="org/apache/fop/afp/**/*.java"/> | <include name="org/apache/fop/afp/**/*.java"/> |
<extensions label="Extensions" href="extensions.html"/> | <extensions label="Extensions" href="extensions.html"/> | ||||
<events label="Events" href="events.html"/> | <events label="Events" href="events.html"/> | ||||
<metadata label="Metadata" href="metadata.html"/> | <metadata label="Metadata" href="metadata.html"/> | ||||
<accessibility label="Accessibility" href="accessibility.html"/> | |||||
</features> | </features> | ||||
</trunk> | </trunk> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "document-v20.dtd"> | |||||
<document> | |||||
<header> | |||||
<title>Accessibility</title> | |||||
</header> | |||||
<body> | |||||
<section id="overview"> | |||||
<title>Overview</title> | |||||
<p> | |||||
This page describes the | |||||
<a href="http://en.wikipedia.org/wiki/Accessibility">accessibility</a> | |||||
features of Apache FOP. | |||||
<a href="http://www.section508.gov/">Section 508</a> defines accessibility in the context | |||||
of electronic documents for the USA but other countries have similar requirements. | |||||
</p> | |||||
<p> | |||||
Accessibility features are available only for the PDF output format and there are some | |||||
implementation limitations. Also, certain actions must be undertaken by the content creator | |||||
to ensure that FOP can create a truly accessible document. | |||||
</p> | |||||
</section> | |||||
<section> | |||||
<title>Enabling accessibility</title> | |||||
<p>There are 3 ways to enable accessibility:</p> | |||||
<ol> | |||||
<li> | |||||
<strong>Command line:</strong> The command line option -a turns on accessibility: | |||||
<code>fop -a -fo mydocument.fo -pdf mydocument.pdf</code> | |||||
</li> | |||||
<li> | |||||
<strong>Embedding:</strong> <code>userAgent.setAccessibility(true);</code> | |||||
</li> | |||||
<li> | |||||
<strong>Optional setting in fop.xconf file:</strong> | |||||
<pre> | |||||
<fop version="1.0"> | |||||
<accessibility>true</accessibility> | |||||
... | |||||
</fop> | |||||
</pre> | |||||
</li> | |||||
</ol> | |||||
<p> | |||||
When accessibility is enabled, additional information relating to the logical structure of | |||||
the document is added to the PDF. That information allows the PDF viewer (or a | |||||
text-to-speech application) to retrieve the natural reading order of the document. | |||||
</p> | |||||
<note>The processing of the logical structure is memory-hungry. You may need to adjust the | |||||
Java heap size in order to process larger files.</note> | |||||
</section> | |||||
<section id="source"> | |||||
<title>Changes to your XSL-FO input files</title> | |||||
<p> | |||||
Apache FOP cannot automatically generate accessible PDFs. Some of the work can only be | |||||
performed by the content provider. Following are some changes that may be necessary to | |||||
your XSL-FO content in order to generate really accessible documents: | |||||
</p> | |||||
<ul> | |||||
<li>Table cells must have a table row as their parent.</li> | |||||
<li> | |||||
Images must have an alternate text: use the <code>fox:alt-text</code> extension attribute | |||||
(in the <a href="extensions.html#fox-namespace">fox namespace</a>) on | |||||
<code>fo:external-graphic</code> and <code>fo:instream-foreign-object</code> to specify a | |||||
short text describing the image. | |||||
</li> | |||||
<li> | |||||
Ensure that the order of <code>fo:block-container</code> elements in a page corresponds to | |||||
the reading order. | |||||
</li> | |||||
<li> | |||||
Specify the natural language of the document using the language and country properties | |||||
(or via the <code>xml:lang</code> shorthand property). | |||||
</li> | |||||
</ul> | |||||
</section> | |||||
<section id="testing"> | |||||
<title>Testing</title> | |||||
<p> | |||||
Accessible PDFs can be tested, for example, using Adobe Acrobat Professional. Its | |||||
Accessibility Check feature creates a report indicating any deficiencies with a PDF | |||||
document. Alternatively, you can just let a screen reader read the document aloud. | |||||
</p> | |||||
</section> | |||||
<section id="limitations"> | |||||
<title>Limitations</title> | |||||
<p> | |||||
Accessibility support in Apache FOP is relatively new, so there are certain | |||||
limitations. Please help us identify and close any gaps. | |||||
</p> | |||||
<ul> | |||||
<li> | |||||
The natural language can currently only be specified at the page-sequence level. The | |||||
document language is derived from the language of the first page-sequence. It is | |||||
currently not possible to override the language inside the content below the | |||||
page-sequence level. | |||||
</li> | |||||
<li> | |||||
It's currently not possible to specify the expanded form of an abbreviation or acronym. | |||||
</li> | |||||
<li> | |||||
SVG graphics (or images in general) are treated as a single figure. Text contained in | |||||
SVGs is not accessible. It's only possible to work with <code>fox:alt-text</code>. | |||||
</li> | |||||
<li> | |||||
XSL-FO's role property is currently not supported. It could theoretically be used to | |||||
differentiate between headings and normal text. At the moment, the two are simply | |||||
identified as paragraphs. | |||||
</li> | |||||
<li> | |||||
The side regions (region-before, region-after etc.) are currently not specially | |||||
identified. Screen readers may read their content at page changes. | |||||
</li> | |||||
</ul> | |||||
</section> | |||||
<section id="links"> | |||||
<title>Related Links</title> | |||||
<p> | |||||
Many resources providing guidance about creating accessible documents can be found on the | |||||
web. Here are a few links, along with additional resources around the topic: | |||||
</p> | |||||
<ul> | |||||
<li><a href="http://www.section508.gov/">US Government - Website on Section 508</a></li> | |||||
<li><a href="http://en.wikipedia.org/wiki/Accessibility">Wikipedia on Accessibility in general</a></li> | |||||
<li><a href="http://en.wikipedia.org/wiki/Portable_Document_Format#Accessibility">Wikipedia on Accessibility in PDF</a></li> | |||||
<li> | |||||
<a href="http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf">PDF | |||||
Reference 1.4</a> (look up chapters 9.7 "Tagged PDF" and 9.8 "Accessibility Support") | |||||
</li> | |||||
<li><a href="pdfa.html">PDF/A support in Apache FOP</a></li> | |||||
<li><a href="http://wiki.apache.org/xmlgraphics-fop/PDF_Accessibility">Developer-oriented details on the accessibility features (on the Wiki)</a></li> | |||||
</ul> | |||||
</section> | |||||
</body> | |||||
</document> |
lack of a full license to get a detailed error protocol. | lack of a full license to get a detailed error protocol. | ||||
</p> | </p> | ||||
<p> | <p> | ||||
<strong>PDF/A-1a</strong> is not implemented, yet. This is mostly because of the requirement | |||||
for tagged PDF which is not available in FOP, yet. | |||||
<strong>PDF/A-1a</strong> is based on PDF-A-1b and adds accessibility features | |||||
(such as Tagged PDF). This format is available within the limitation described on | |||||
the <a href="accessibility.html">Accessibility page</a>. | |||||
</p> | </p> | ||||
</section> | </section> | ||||
<section id="command-line"> | <section id="command-line"> | ||||
as a parameter. If there is a violation of one of the validation rules for | as a parameter. If there is a violation of one of the validation rules for | ||||
PDF/A, an error message is presented and the processing stops. | PDF/A, an error message is presented and the processing stops. | ||||
</p> | </p> | ||||
<p> | |||||
PDF/A-1a is enabled by specifying "-pdfprofile PDF/A-1a". | |||||
</p> | |||||
</section> | </section> | ||||
<section id="embedded"> | <section id="embedded"> | ||||
<title>Usage (embedded)</title> | <title>Usage (embedded)</title> | ||||
If one of the validation rules of PDF/A is violated, an PDFConformanceException | If one of the validation rules of PDF/A is violated, an PDFConformanceException | ||||
(descendant of RuntimeException) is thrown. | (descendant of RuntimeException) is thrown. | ||||
</p> | </p> | ||||
<p> | |||||
For PDF/A-1a, just use the string "PDF/A-1a" instead of "PDF/A-1b". | |||||
</p> | |||||
</section> | </section> | ||||
<section id="rules"> | <section id="rules"> | ||||
<title>PDF/A in Action</title> | <title>PDF/A in Action</title> | ||||
embedded in clear text so non-PDF-aware applications can extract the XMP metadata. | embedded in clear text so non-PDF-aware applications can extract the XMP metadata. | ||||
</li> | </li> | ||||
</ul> | </ul> | ||||
<note> | |||||
There are additional requirements if you want to enabled PDF/A-1a (Tagged PDF). This is | |||||
particularly the specification of the natural language and alternative descriptions for | |||||
images. Please refer to the <a href="accessibility.html">Accessibility page</a> for details. | |||||
</note> | |||||
</section> | </section> | ||||
<section id="profile-compatibility"> | <section id="profile-compatibility"> | ||||
<title>PDF profile compatibility</title> | <title>PDF profile compatibility</title> | ||||
<p> | <p> | ||||
The PDF profiles "PDF/X-3:2003" and "PDF/A-1b" are compatible and can both be | |||||
activated at the same time. | |||||
The PDF profiles "PDF/X-3:2003" and "PDF/A-1b" (or "PDF/A-1a") are compatible and can | |||||
both be activated at the same time. | |||||
</p> | </p> | ||||
</section> | </section> | ||||
<section id="interoperability"> | <section id="interoperability"> |
org.apache.fop.fo.extensions.svg.SVGElementMapping | org.apache.fop.fo.extensions.svg.SVGElementMapping | ||||
org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping | org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping | ||||
org.apache.fop.fo.extensions.ExtensionElementMapping | org.apache.fop.fo.extensions.ExtensionElementMapping | ||||
org.apache.fop.fo.extensions.InternalElementMapping | |||||
org.apache.fop.fo.extensions.OldExtensionElementMapping | org.apache.fop.fo.extensions.OldExtensionElementMapping | ||||
org.apache.fop.fo.extensions.xmp.XMPElementMapping | org.apache.fop.fo.extensions.xmp.XMPElementMapping | ||||
org.apache.fop.fo.extensions.xmp.RDFElementMapping | org.apache.fop.fo.extensions.xmp.RDFElementMapping | ||||
org.apache.fop.render.ps.extensions.PSExtensionElementMapping | org.apache.fop.render.ps.extensions.PSExtensionElementMapping | ||||
org.apache.fop.render.afp.extensions.AFPElementMapping | org.apache.fop.render.afp.extensions.AFPElementMapping | ||||
org.apache.fop.render.pcl.extensions.PCLElementMapping | |||||
org.apache.fop.render.pcl.extensions.PCLElementMapping |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.accessibility; | |||||
import javax.xml.transform.Source; | |||||
import javax.xml.transform.Templates; | |||||
import javax.xml.transform.Transformer; | |||||
import javax.xml.transform.TransformerConfigurationException; | |||||
import javax.xml.transform.sax.SAXTransformerFactory; | |||||
import javax.xml.transform.sax.TransformerHandler; | |||||
import javax.xml.transform.stream.StreamSource; | |||||
import org.xml.sax.helpers.DefaultHandler; | |||||
import org.apache.fop.apps.FOPException; | |||||
import org.apache.fop.apps.FOUserAgent; | |||||
/** | |||||
* Helper class for FOP's accessibility features. | |||||
*/ | |||||
public final class Accessibility { | |||||
/** Constant string for the rendering options key to enable accessibility features. */ | |||||
public static final String ACCESSIBILITY = "accessibility"; | |||||
// TODO what if the default factory is not a SAXTransformerFactory? | |||||
private static SAXTransformerFactory tfactory | |||||
= (SAXTransformerFactory)SAXTransformerFactory.newInstance(); | |||||
private static Templates addPtrTemplates; | |||||
private static Templates reduceFOTreeTemplates; | |||||
private Accessibility() { } | |||||
/** | |||||
* Decorates the given handler so the structure tree used for accessibility | |||||
* features can be branched off the main content stream. | |||||
* @param handler the handler to decorate | |||||
* @param userAgent the user agent | |||||
* @return the decorated handler | |||||
* @throws FOPException if an error occurs setting up the decoration | |||||
*/ | |||||
public static DefaultHandler decorateDefaultHandler(DefaultHandler handler, | |||||
FOUserAgent userAgent) throws FOPException { | |||||
try { | |||||
setupTemplates(); | |||||
TransformerHandler addPtr = tfactory.newTransformerHandler(addPtrTemplates); | |||||
Transformer reduceFOTree = reduceFOTreeTemplates.newTransformer(); | |||||
return new AccessibilityPreprocessor(addPtr, reduceFOTree, userAgent, handler); | |||||
} catch (TransformerConfigurationException e) { | |||||
throw new FOPException(e); | |||||
} | |||||
} | |||||
private static synchronized void setupTemplates() throws TransformerConfigurationException { | |||||
if (addPtrTemplates == null) { | |||||
addPtrTemplates = loadTemplates("addPtr.xsl"); | |||||
} | |||||
if (reduceFOTreeTemplates == null) { | |||||
reduceFOTreeTemplates = loadTemplates("reduceFOTree.xsl"); | |||||
} | |||||
} | |||||
private static Templates loadTemplates(String source) throws TransformerConfigurationException { | |||||
Source src = new StreamSource(Accessibility.class.getResource(source).toExternalForm()); | |||||
return tfactory.newTemplates(src); | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.accessibility; | |||||
import org.apache.fop.events.EventBroadcaster; | |||||
import org.apache.fop.events.EventProducer; | |||||
/** | |||||
* Event producer for accessibility-related events. | |||||
*/ | |||||
public interface AccessibilityEventProducer extends EventProducer { | |||||
/** Provider class for the event producer. */ | |||||
public final class Provider { | |||||
private Provider() { } | |||||
/** | |||||
* Returns an event producer. | |||||
* | |||||
* @param broadcaster the event broadcaster to use | |||||
* @return the event producer | |||||
*/ | |||||
public static AccessibilityEventProducer get(EventBroadcaster broadcaster) { | |||||
return (AccessibilityEventProducer) broadcaster.getEventProducerFor( | |||||
AccessibilityEventProducer.class); | |||||
} | |||||
} | |||||
/** | |||||
* The structure tree is missing in the XML file. | |||||
* | |||||
* @param source the event source | |||||
* @event.severity FATAL | |||||
*/ | |||||
void noStructureTreeInXML(Object source); | |||||
} |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<catalogue xml:lang="en"> | |||||
<message key="org.apache.fop.accessibility.AccessibilityEventProducer.noStructureTreeInXML">Accessibility is enabled but structure tree is missing in XML file. Please disable accessibility, or re-generate XML file in accessibility mode.</message> | |||||
</catalogue> |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.accessibility; | |||||
import java.io.ByteArrayInputStream; | |||||
import java.io.InputStream; | |||||
import javax.xml.parsers.SAXParser; | |||||
import javax.xml.parsers.SAXParserFactory; | |||||
import javax.xml.transform.Source; | |||||
import javax.xml.transform.Transformer; | |||||
import javax.xml.transform.dom.DOMResult; | |||||
import javax.xml.transform.sax.TransformerHandler; | |||||
import javax.xml.transform.stream.StreamResult; | |||||
import javax.xml.transform.stream.StreamSource; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.SAXException; | |||||
import org.xml.sax.helpers.DefaultHandler; | |||||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||||
import org.apache.fop.apps.FOUserAgent; | |||||
import org.apache.fop.util.TransformerDefaultHandler; | |||||
/** | |||||
* This class prepares an XSL-FO document for accessibility. It adds a unique | |||||
* identifier to every applicable FO, then creates the structure tree, before | |||||
* handing the document over to the regular handler. | |||||
*/ | |||||
class AccessibilityPreprocessor extends TransformerDefaultHandler { | |||||
private final ByteArrayOutputStream enrichedFOBuffer = new ByteArrayOutputStream(); | |||||
private final Transformer reduceFOTree; | |||||
private final FOUserAgent userAgent; | |||||
private final DefaultHandler fopHandler; | |||||
public AccessibilityPreprocessor(TransformerHandler addPtr, Transformer reduceFOTree, | |||||
FOUserAgent userAgent, DefaultHandler fopHandler) { | |||||
super(addPtr); | |||||
this.reduceFOTree = reduceFOTree; | |||||
this.userAgent = userAgent; | |||||
this.fopHandler = fopHandler; | |||||
getTransformerHandler().setResult(new StreamResult(enrichedFOBuffer)); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endDocument() throws SAXException { | |||||
super.endDocument(); | |||||
// do the second transform to struct | |||||
try { | |||||
//TODO this must be optimized, no buffering (ex. SAX-based tee-proxy) | |||||
byte[] enrichedFO = enrichedFOBuffer.toByteArray(); | |||||
Source src = new StreamSource(new ByteArrayInputStream(enrichedFO)); | |||||
DOMResult res = new DOMResult(); | |||||
reduceFOTree.transform(src, res); | |||||
StructureTree structureTree = new StructureTree(); | |||||
NodeList pageSequences = res.getNode().getFirstChild().getChildNodes(); | |||||
for (int i = 0; i < pageSequences.getLength(); i++) { | |||||
structureTree.addPageSequenceStructure(pageSequences.item(i).getChildNodes()); | |||||
} | |||||
userAgent.setStructureTree(structureTree); | |||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); | |||||
saxParserFactory.setNamespaceAware(true); | |||||
saxParserFactory.setValidating(false); | |||||
SAXParser saxParser = saxParserFactory.newSAXParser(); | |||||
InputStream in = new ByteArrayInputStream(enrichedFO); | |||||
saxParser.parse(in, fopHandler); | |||||
} catch (Exception e) { | |||||
throw new SAXException(e); | |||||
} | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.accessibility; | |||||
import java.io.StringWriter; | |||||
import java.io.Writer; | |||||
import java.util.ArrayList; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import javax.xml.transform.Transformer; | |||||
import javax.xml.transform.TransformerFactory; | |||||
import javax.xml.transform.dom.DOMSource; | |||||
import javax.xml.transform.stream.StreamResult; | |||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.NodeList; | |||||
/** | |||||
* A reduced version of the document's FO tree, containing only its logical | |||||
* structure. Used by accessible output formats. | |||||
*/ | |||||
public final class StructureTree { | |||||
private final List pageSequenceStructures = new ArrayList(); | |||||
/** | |||||
* Package-private default constructor. | |||||
*/ | |||||
StructureTree() { } | |||||
private static boolean flowOrStaticContentNodes(NodeList nodes) { | |||||
for (int i = 0; i < nodes.getLength(); i++) { | |||||
Node node = nodes.item(i); | |||||
if (node.getNodeType() != Node.ELEMENT_NODE) { | |||||
return false; | |||||
} | |||||
String name = node.getLocalName(); | |||||
if (!(name.equals("flow") || name.equals("static-content"))) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
void addPageSequenceStructure(NodeList structureTree) { | |||||
assert flowOrStaticContentNodes(structureTree); | |||||
pageSequenceStructures.add(structureTree); | |||||
} | |||||
/** | |||||
* Returns the list of nodes that are the children of the given page sequence. | |||||
* | |||||
* @param index index of the page sequence, 0-based | |||||
* @return its children nodes | |||||
*/ | |||||
public NodeList getPageSequence(int index) { | |||||
return (NodeList) pageSequenceStructures.get(index); | |||||
} | |||||
/** | |||||
* Returns an XML-like representation of the structure trees. | |||||
* <p> | |||||
* <strong>Note:</strong> use only for debugging purpose, as this method | |||||
* performs non-trivial operations. | |||||
* </p> | |||||
* @return a string representation of this object | |||||
*/ | |||||
public String toString() { | |||||
try { | |||||
Transformer t = TransformerFactory.newInstance().newTransformer(); | |||||
Writer str = new StringWriter(); | |||||
for (Iterator iter = pageSequenceStructures.iterator(); iter.hasNext();) { | |||||
NodeList nodes = (NodeList) iter.next(); | |||||
for (int i = 0, c = nodes.getLength(); i < c; i++) { | |||||
t.transform(new DOMSource(nodes.item(i)), new StreamResult(str)); | |||||
} | |||||
} | |||||
return str.toString(); | |||||
} catch (Exception e) { | |||||
return e.toString(); | |||||
} | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.accessibility; | |||||
import javax.xml.transform.TransformerConfigurationException; | |||||
import javax.xml.transform.dom.DOMResult; | |||||
import javax.xml.transform.sax.SAXTransformerFactory; | |||||
import javax.xml.transform.sax.TransformerHandler; | |||||
import org.xml.sax.ContentHandler; | |||||
import org.xml.sax.SAXException; | |||||
import org.apache.fop.util.DelegatingContentHandler; | |||||
/** | |||||
* Helper class that re-builds a structure tree from what is stored in an | |||||
* intermediate XML file (IF XML or Area Tree XML). | |||||
*/ | |||||
public final class StructureTreeBuilder { | |||||
private final SAXTransformerFactory factory; | |||||
private final StructureTree structureTree = new StructureTree(); | |||||
/** | |||||
* Creates a new instance. | |||||
* | |||||
* @param factory a factory internally used to build the structures of page | |||||
* sequences | |||||
*/ | |||||
public StructureTreeBuilder(SAXTransformerFactory factory) { | |||||
this.factory = factory; | |||||
} | |||||
/** | |||||
* Returns the structure tree that will result from the parsing. | |||||
* | |||||
* @return the structure tree built by this object | |||||
*/ | |||||
public StructureTree getStructureTree() { | |||||
return structureTree; | |||||
} | |||||
/** | |||||
* Returns a ContenHandler for parsing the structure of a new page sequence. | |||||
* It is assumed that page sequences are being parsed in the document order. | |||||
* | |||||
* @return a handler for parsing the <structure-tree> or | |||||
* <structureTree> element and its descendants | |||||
* @throws SAXException if there is an error when creating the handler | |||||
*/ | |||||
public ContentHandler getHandlerForNextPageSequence() throws SAXException { | |||||
TransformerHandler structureTreeBuilder; | |||||
try { | |||||
structureTreeBuilder = factory.newTransformerHandler(); | |||||
} catch (TransformerConfigurationException e) { | |||||
throw new SAXException(e); | |||||
} | |||||
final DOMResult domResult = new DOMResult(); | |||||
structureTreeBuilder.setResult(domResult); | |||||
return new DelegatingContentHandler(structureTreeBuilder) { | |||||
public void characters(char[] ch, int start, int length) throws SAXException { | |||||
/* | |||||
* There's no text node in the structure tree. This is just | |||||
* whitespace => ignore | |||||
*/ | |||||
} | |||||
public void endDocument() throws SAXException { | |||||
super.endDocument(); | |||||
structureTree.addPageSequenceStructure(domResult.getNode().getFirstChild() | |||||
.getChildNodes()); | |||||
} | |||||
}; | |||||
} | |||||
} |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<xsl:stylesheet version="1.0" | |||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | |||||
xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> | |||||
<xsl:template name="addPtr"> | |||||
<xsl:copy> | |||||
<xsl:apply-templates select="@*"/> | |||||
<xsl:attribute name="foi:ptr"> | |||||
<xsl:value-of select="generate-id()"/> | |||||
</xsl:attribute> | |||||
<xsl:apply-templates/> | |||||
</xsl:copy> | |||||
</xsl:template> | |||||
<!-- Block-level Formatting Objects --> | |||||
<xsl:template match="fo:block|fo:block-container"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Inline-level Formatting Objects --> | |||||
<xsl:template match="fo:character|fo:inline|fo:inline-container"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:external-graphic|fo:instream-foreign-object"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Formatting Objects for Tables --> | |||||
<xsl:template match="fo:table-and-caption|fo:table-caption|fo:table"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Formatting Objects for Lists --> | |||||
<xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Dynamic Effects: Link and Multi Formatting Objects --> | |||||
<xsl:template match="fo:basic-link"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Out-of-Line Formatting Objects --> | |||||
<xsl:template match="fo:float|fo:footnote|fo:footnote-body"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<!-- Other Formatting Objects --> | |||||
<xsl:template match="fo:wrapper|fo:marker"> | |||||
<xsl:call-template name="addPtr"/> | |||||
</xsl:template> | |||||
<xsl:template match="@*|node()"> | |||||
<xsl:copy> | |||||
<xsl:apply-templates select="@*|node()"/> | |||||
</xsl:copy> | |||||
</xsl:template> | |||||
</xsl:stylesheet> |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | |||||
xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" | |||||
xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> | |||||
<xsl:output method="xml" indent="no"/> | |||||
<xsl:template name="copy"> | |||||
<xsl:copy> | |||||
<xsl:apply-templates select="@*|node()"/> | |||||
</xsl:copy> | |||||
</xsl:template> | |||||
<!-- Declarations and Pagination and Layout Formatting Objects --> | |||||
<xsl:template match="fo:root|fo:page-sequence|fo:static-content|fo:flow"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Block-level Formatting Objects --> | |||||
<xsl:template match="fo:block|fo:block-container"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Inline-level Formatting Objects --> | |||||
<xsl:template match="fo:character|fo:inline|fo:inline-container"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:external-graphic|fo:instream-foreign-object"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Formatting Objects for Tables --> | |||||
<xsl:template match="fo:table-and-caption|fo:table-caption|fo:table"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Formatting Objects for Lists --> | |||||
<xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Dynamic Effects: Link and Multi Formatting Objects --> | |||||
<xsl:template match="fo:basic-link"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Out-of-Line Formatting Objects --> | |||||
<xsl:template match="fo:float|fo:footnote|fo:footnote-body"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Other Formatting Objects --> | |||||
<xsl:template match="fo:wrapper|fo:marker"> | |||||
<xsl:call-template name="copy"/> | |||||
</xsl:template> | |||||
<!-- Discard descendants of fo:leader --> | |||||
<xsl:template match="fo:leader"/> | |||||
<!-- Keep foi:ptr and fox:alt-text attributes, discard everything else --> | |||||
<xsl:template match="@foi:ptr|@fox:alt-text"> | |||||
<xsl:copy-of select="."/> | |||||
</xsl:template> | |||||
<xsl:template match="@*"/> | |||||
<!-- Discard text --> | |||||
<xsl:template match="text()"/> | |||||
</xsl:stylesheet> |
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext; | import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext; | ||||
import org.apache.fop.Version; | import org.apache.fop.Version; | ||||
import org.apache.fop.accessibility.Accessibility; | |||||
import org.apache.fop.accessibility.StructureTree; | |||||
import org.apache.fop.events.DefaultEventBroadcaster; | import org.apache.fop.events.DefaultEventBroadcaster; | ||||
import org.apache.fop.events.Event; | import org.apache.fop.events.Event; | ||||
import org.apache.fop.events.EventBroadcaster; | import org.apache.fop.events.EventBroadcaster; | ||||
private boolean conserveMemoryPolicy = false; | private boolean conserveMemoryPolicy = false; | ||||
private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); | private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); | ||||
private StructureTree structureTree; | |||||
/** Producer: Metadata element for the system/software that produces | /** Producer: Metadata element for the system/software that produces | ||||
* the document. (Some renderers can store this in the document.) | * the document. (Some renderers can store this in the document.) | ||||
*/ | */ | ||||
this.factory = factory; | this.factory = factory; | ||||
setBaseURL(factory.getBaseURL()); | setBaseURL(factory.getBaseURL()); | ||||
setTargetResolution(factory.getTargetResolution()); | setTargetResolution(factory.getTargetResolution()); | ||||
setAccessibility(factory.isAccessibilityEnabled()); | |||||
} | } | ||||
/** @return the associated FopFactory instance */ | /** @return the associated FopFactory instance */ | ||||
return rendererOverride; | return rendererOverride; | ||||
} | } | ||||
/** | /** | ||||
* Sets an explicit FOEventHandler instance which overrides the one | * Sets an explicit FOEventHandler instance which overrides the one | ||||
* defined by the render type setting. | * defined by the render type setting. | ||||
this.conserveMemoryPolicy = conserveMemoryPolicy; | this.conserveMemoryPolicy = conserveMemoryPolicy; | ||||
} | } | ||||
/** | |||||
* Activates accessibility (for output formats that support it). | |||||
* | |||||
* @param accessibility <code>true</code> to enable accessibility support | |||||
*/ | |||||
public void setAccessibility(boolean accessibility) { | |||||
if (accessibility) { | |||||
getRendererOptions().put(Accessibility.ACCESSIBILITY, Boolean.TRUE); | |||||
} | |||||
} | |||||
/** | |||||
* Check if accessibility is enabled. | |||||
* @return true if accessibility is enabled | |||||
*/ | |||||
public boolean isAccessibilityEnabled() { | |||||
Boolean enabled = (Boolean)this.getRendererOptions().get(Accessibility.ACCESSIBILITY); | |||||
if (enabled != null) { | |||||
return enabled.booleanValue(); | |||||
} else { | |||||
return false; | |||||
} | |||||
} | |||||
/** | |||||
* Sets the document's structure tree, for use by accessible output formats. | |||||
* | |||||
* @param structureTree a simplified version of the FO tree, retaining only | |||||
* its logical structure | |||||
*/ | |||||
public void setStructureTree(StructureTree structureTree) { | |||||
this.structureTree = structureTree; | |||||
} | |||||
/** | |||||
* Returns the document's structure tree, for use by accessible output | |||||
* formats. | |||||
* | |||||
* @return a simplified version of the FO tree, retaining only its logical | |||||
* structure | |||||
*/ | |||||
public StructureTree getStructureTree() { | |||||
return this.structureTree; | |||||
} | |||||
} | } | ||||
import org.xml.sax.helpers.DefaultHandler; | import org.xml.sax.helpers.DefaultHandler; | ||||
import org.apache.fop.accessibility.Accessibility; | |||||
import org.apache.fop.fo.FOTreeBuilder; | import org.apache.fop.fo.FOTreeBuilder; | ||||
/** | /** | ||||
if (foTreeBuilder == null) { | if (foTreeBuilder == null) { | ||||
createDefaultHandler(); | createDefaultHandler(); | ||||
} | } | ||||
return this.foTreeBuilder; | |||||
if (this.foUserAgent.isAccessibilityEnabled()) { | |||||
return Accessibility.decorateDefaultHandler(this.foTreeBuilder, foUserAgent); | |||||
} else { | |||||
return this.foTreeBuilder; | |||||
} | |||||
} | } | ||||
/** | /** |
*/ | */ | ||||
private String base = null; | private String base = null; | ||||
/** | |||||
* Controls if accessibility is turned on or off | |||||
*/ | |||||
private boolean accessibility = false; | |||||
/** The base URL for all hyphen URL resolutions. */ | /** The base URL for all hyphen URL resolutions. */ | ||||
private String hyphenBase = null; | private String hyphenBase = null; | ||||
return userAgent; | return userAgent; | ||||
} | } | ||||
/** | |||||
* Sets accessibility support. | |||||
* | |||||
* @param value <code>true</code> to enable accessibility, <code>false</code> otherwise | |||||
*/ | |||||
void setAccessibility(boolean value) { | |||||
this.accessibility = value; | |||||
} | |||||
boolean isAccessibilityEnabled() { | |||||
return accessibility; | |||||
} | |||||
/** | /** | ||||
* Returns a new {@link Fop} instance. FOP will be configured with a default user agent | * Returns a new {@link Fop} instance. FOP will be configured with a default user agent | ||||
* instance. | * instance. |
log.debug("Initializing FopFactory Configuration"); | log.debug("Initializing FopFactory Configuration"); | ||||
} | } | ||||
if (cfg.getChild("accessibility", false) != null) { | |||||
try { | |||||
this.factory.setAccessibility( | |||||
cfg.getChild("accessibility").getValueAsBoolean()); | |||||
} catch (ConfigurationException e) { | |||||
throw new FOPException(e); | |||||
} | |||||
} | |||||
// strict configuration | // strict configuration | ||||
if (cfg.getChild("strict-configuration", false) != null) { | if (cfg.getChild("strict-configuration", false) != null) { | ||||
try { | try { |
import javax.xml.transform.sax.SAXTransformerFactory; | import javax.xml.transform.sax.SAXTransformerFactory; | ||||
import javax.xml.transform.sax.TransformerHandler; | import javax.xml.transform.sax.TransformerHandler; | ||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.w3c.dom.DOMImplementation; | import org.w3c.dom.DOMImplementation; | ||||
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.xml.sax.Attributes; | import org.xml.sax.Attributes; | ||||
import org.xml.sax.helpers.AttributesImpl; | import org.xml.sax.helpers.AttributesImpl; | ||||
import org.xml.sax.helpers.DefaultHandler; | import org.xml.sax.helpers.DefaultHandler; | ||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.apache.xmlgraphics.image.loader.ImageException; | import org.apache.xmlgraphics.image.loader.ImageException; | ||||
import org.apache.xmlgraphics.image.loader.ImageInfo; | import org.apache.xmlgraphics.image.loader.ImageInfo; | ||||
import org.apache.xmlgraphics.image.loader.ImageManager; | import org.apache.xmlgraphics.image.loader.ImageManager; | ||||
import org.apache.xmlgraphics.image.loader.ImageSessionContext; | import org.apache.xmlgraphics.image.loader.ImageSessionContext; | ||||
import org.apache.xmlgraphics.util.QName; | import org.apache.xmlgraphics.util.QName; | ||||
import org.apache.fop.accessibility.AccessibilityEventProducer; | |||||
import org.apache.fop.accessibility.StructureTreeBuilder; | |||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.area.Trait.Background; | import org.apache.fop.area.Trait.Background; | ||||
import org.apache.fop.area.Trait.InternalLink; | import org.apache.fop.area.Trait.InternalLink; | ||||
import org.apache.fop.util.ContentHandlerFactoryRegistry; | import org.apache.fop.util.ContentHandlerFactoryRegistry; | ||||
import org.apache.fop.util.ConversionUtils; | import org.apache.fop.util.ConversionUtils; | ||||
import org.apache.fop.util.DefaultErrorListener; | import org.apache.fop.util.DefaultErrorListener; | ||||
import org.apache.fop.util.DelegatingContentHandler; | |||||
import org.apache.fop.util.XMLConstants; | |||||
import org.apache.fop.util.XMLUtil; | import org.apache.fop.util.XMLUtil; | ||||
/** | /** | ||||
private Locator locator; | private Locator locator; | ||||
private StructureTreeBuilder structureTreeBuilder; | |||||
private ContentHandler structureTreeBuilderWrapper; | |||||
private Attributes pageSequenceAttributes; | |||||
private final class StructureTreeBuilderWrapper extends DelegatingContentHandler { | |||||
private StructureTreeBuilderWrapper() | |||||
throws SAXException { | |||||
super(structureTreeBuilder.getHandlerForNextPageSequence()); | |||||
} | |||||
public void endDocument() throws SAXException { | |||||
super.endDocument(); | |||||
startAreaTreeElement("pageSequence", pageSequenceAttributes); | |||||
pageSequenceAttributes = null; | |||||
} | |||||
} | |||||
public Handler(AreaTreeModel treeModel, FOUserAgent userAgent, | public Handler(AreaTreeModel treeModel, FOUserAgent userAgent, | ||||
ElementMappingRegistry elementMappingRegistry) { | ElementMappingRegistry elementMappingRegistry) { | ||||
this.treeModel = treeModel; | this.treeModel = treeModel; | ||||
makers.put("bookmarkTree", new BookmarkTreeMaker()); | makers.put("bookmarkTree", new BookmarkTreeMaker()); | ||||
makers.put("bookmark", new BookmarkMaker()); | makers.put("bookmark", new BookmarkMaker()); | ||||
makers.put("destination", new DestinationMaker()); | makers.put("destination", new DestinationMaker()); | ||||
if (userAgent.isAccessibilityEnabled()) { | |||||
structureTreeBuilder = new StructureTreeBuilder(tFactory); | |||||
userAgent.setStructureTree(structureTreeBuilder.getStructureTree()); | |||||
} | |||||
} | } | ||||
private Area findAreaType(Class clazz) { | private Area findAreaType(Class clazz) { | ||||
delegate.startDocument(); | delegate.startDocument(); | ||||
delegate.startElement(uri, localName, qName, attributes); | delegate.startElement(uri, localName, qName, attributes); | ||||
} else { | } else { | ||||
lastAttributes = new AttributesImpl(attributes); | |||||
boolean handled = true; | boolean handled = true; | ||||
if ("".equals(uri)) { | if ("".equals(uri)) { | ||||
Maker maker = (Maker)makers.get(localName); | |||||
content.clear(); | |||||
ignoreCharacters = true; | |||||
if (maker != null) { | |||||
ignoreCharacters = maker.ignoreCharacters(); | |||||
maker.startElement(attributes); | |||||
} else if ("extension-attachments".equals(localName)) { | |||||
//TODO implement me | |||||
if (localName.equals("pageSequence") && userAgent.isAccessibilityEnabled()) { | |||||
structureTreeBuilderWrapper = new StructureTreeBuilderWrapper(); | |||||
pageSequenceAttributes = new AttributesImpl(attributes); | |||||
} else if (localName.equals("structureTree")) { | |||||
if (userAgent.isAccessibilityEnabled()) { | |||||
delegate = structureTreeBuilderWrapper; | |||||
} else { | |||||
/* Delegate to a handler that does nothing */ | |||||
delegate = new DefaultHandler(); | |||||
} | |||||
delegateStack.push(qName); | |||||
delegate.startDocument(); | |||||
delegate.startElement(uri, localName, qName, attributes); | |||||
} else { | } else { | ||||
handled = false; | |||||
if (pageSequenceAttributes != null) { | |||||
/* | |||||
* This means that no structure-element tag was | |||||
* found in the XML, otherwise a | |||||
* StructureTreeBuilderWrapper object would have | |||||
* been created, which would have reset the | |||||
* pageSequenceAttributes field. | |||||
*/ | |||||
AccessibilityEventProducer.Provider | |||||
.get(userAgent.getEventBroadcaster()) | |||||
.noStructureTreeInXML(this); | |||||
} | |||||
handled = startAreaTreeElement(localName, attributes); | |||||
} | } | ||||
} else { | } else { | ||||
ContentHandlerFactoryRegistry registry | ContentHandlerFactoryRegistry registry | ||||
} | } | ||||
} | } | ||||
private boolean startAreaTreeElement(String localName, Attributes attributes) | |||||
throws SAXException { | |||||
lastAttributes = new AttributesImpl(attributes); | |||||
Maker maker = (Maker)makers.get(localName); | |||||
content.clear(); | |||||
ignoreCharacters = true; | |||||
if (maker != null) { | |||||
ignoreCharacters = maker.ignoreCharacters(); | |||||
maker.startElement(attributes); | |||||
} else if ("extension-attachments".equals(localName)) { | |||||
//TODO implement me | |||||
} else { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void endElement(String uri, String localName, String qName) throws SAXException { | public void endElement(String uri, String localName, String qName) throws SAXException { | ||||
if (delegate != null) { | if (delegate != null) { | ||||
setTraits(attributes, ip, SUBSET_BOX); | setTraits(attributes, ip, SUBSET_BOX); | ||||
setTraits(attributes, ip, SUBSET_COLOR); | setTraits(attributes, ip, SUBSET_COLOR); | ||||
setTraits(attributes, ip, SUBSET_LINK); | setTraits(attributes, ip, SUBSET_LINK); | ||||
setPtr(ip, attributes); | |||||
Area parent = (Area)areaStack.peek(); | Area parent = (Area)areaStack.peek(); | ||||
parent.addChildArea(ip); | parent.addChildArea(ip); | ||||
areaStack.push(ip); | areaStack.push(ip); | ||||
"tlsadjust", 0)); | "tlsadjust", 0)); | ||||
text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, | text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, | ||||
"twsadjust", 0)); | "twsadjust", 0)); | ||||
setPtr(text, attributes); | |||||
Area parent = (Area)areaStack.peek(); | Area parent = (Area)areaStack.peek(); | ||||
parent.addChildArea(text); | parent.addChildArea(text); | ||||
areaStack.push(text); | areaStack.push(text); | ||||
viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos")); | viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos")); | ||||
viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false)); | viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false)); | ||||
viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); | viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); | ||||
setPtr(viewport, attributes); | |||||
Area parent = (Area)areaStack.peek(); | Area parent = (Area)areaStack.peek(); | ||||
parent.addChildArea(viewport); | parent.addChildArea(viewport); | ||||
areaStack.push(viewport); | areaStack.push(viewport); | ||||
transferForeignObjects(attributes, image); | transferForeignObjects(attributes, image); | ||||
setAreaAttributes(attributes, image); | setAreaAttributes(attributes, image); | ||||
setTraits(attributes, image, SUBSET_COMMON); | setTraits(attributes, image, SUBSET_COMMON); | ||||
setPtr(image, attributes); | |||||
getCurrentViewport().setContent(image); | getCurrentViewport().setContent(image); | ||||
} | } | ||||
} | } | ||||
ExtensionAttachment attachment = (ExtensionAttachment)obj; | ExtensionAttachment attachment = (ExtensionAttachment)obj; | ||||
ato.addExtensionAttachment(attachment); | ato.addExtensionAttachment(attachment); | ||||
} else { | } else { | ||||
log.warn("Don't know how to handle externally generated object: " + obj); | |||||
} | |||||
log.warn("Don't know how to handle externally generated object: " + obj); | |||||
} | } | ||||
} | } | ||||
} | |||||
private void setAreaAttributes(Attributes attributes, Area area) { | private void setAreaAttributes(Attributes attributes, Area area) { | ||||
area.setIPD(Integer.parseInt(attributes.getValue("ipd"))); | area.setIPD(Integer.parseInt(attributes.getValue("ipd"))); | ||||
for (int i = 0, c = atts.getLength(); i < c; i++) { | for (int i = 0, c = atts.getLength(); i < c; i++) { | ||||
String ns = atts.getURI(i); | String ns = atts.getURI(i); | ||||
if (ns.length() > 0) { | if (ns.length() > 0) { | ||||
if ("http://www.w3.org/2000/xmlns/".equals(ns)) { | |||||
if (XMLConstants.XMLNS_NAMESPACE_URI.equals(ns)) { | |||||
continue; | continue; | ||||
} | } | ||||
QName qname = new QName(ns, atts.getQName(i)); | QName qname = new QName(ns, atts.getQName(i)); | ||||
} | } | ||||
} | } | ||||
private void setPtr(Area area, Attributes attributes) { | |||||
String ptr = attributes.getValue("ptr"); | |||||
if (ptr != null) { | |||||
area.addTrait(Trait.PTR, ptr); | |||||
} | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void characters(char[] ch, int start, int length) throws SAXException { | public void characters(char[] ch, int start, int length) throws SAXException { | ||||
if (delegate != null) { | if (delegate != null) { |
public static final Integer OVERLINE_COLOR = new Integer(35); | public static final Integer OVERLINE_COLOR = new Integer(35); | ||||
/** Trait for color of linethrough decorations when rendering inline parent. */ | /** Trait for color of linethrough decorations when rendering inline parent. */ | ||||
public static final Integer LINETHROUGH_COLOR = new Integer(36); | public static final Integer LINETHROUGH_COLOR = new Integer(36); | ||||
/** The ptr trait. Used for accessibility */ | |||||
public static final Integer PTR = new Integer(37); | |||||
/** Maximum value used by trait keys */ | /** Maximum value used by trait keys */ | ||||
public static final int MAX_TRAIT_KEY = 36; | |||||
public static final int MAX_TRAIT_KEY = 37; | |||||
private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1]; | private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1]; | ||||
static { | static { | ||||
// Create a hashmap mapping trait code to name for external representation | // Create a hashmap mapping trait code to name for external representation | ||||
//put(ID_LINK, new TraitInfo("id-link", String.class)); | //put(ID_LINK, new TraitInfo("id-link", String.class)); | ||||
put(PTR, new TraitInfo("ptr", String.class)); | |||||
put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); | put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); | ||||
put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); | put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); | ||||
put(FONT, new TraitInfo("font", FontTriplet.class)); | put(FONT, new TraitInfo("font", FontTriplet.class)); |
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.Version; | import org.apache.fop.Version; | ||||
import org.apache.fop.accessibility.Accessibility; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.apps.FopFactory; | import org.apache.fop.apps.FopFactory; | ||||
i = i + parseAreaTreeOption(args, i); | i = i + parseAreaTreeOption(args, i); | ||||
} else if (args[i].equals("-if")) { | } else if (args[i].equals("-if")) { | ||||
i = i + parseIntermediateFormatOption(args, i); | i = i + parseIntermediateFormatOption(args, i); | ||||
} else if (args[i].equals("-a")) { | |||||
this.renderingOptions.put(Accessibility.ACCESSIBILITY, Boolean.TRUE); | |||||
} else if (args[i].equals("-v")) { | } else if (args[i].equals("-v")) { | ||||
/* Currently just print the version */ | /* Currently just print the version */ | ||||
printVersion(); | printVersion(); | ||||
+ " -nocopy PDF file will be encrypted without copy content permission\n" | + " -nocopy PDF file will be encrypted without copy content permission\n" | ||||
+ " -noedit PDF file will be encrypted without edit content permission\n" | + " -noedit PDF file will be encrypted without edit content permission\n" | ||||
+ " -noannotations PDF file will be encrypted without edit annotation permission\n" | + " -noannotations PDF file will be encrypted without edit annotation permission\n" | ||||
+ " -a enables accessibility features (Tagged PDF etc., default off)\n" | |||||
+ " -pdfprofile prof PDF file will be generated with the specified profile\n" | + " -pdfprofile prof PDF file will be generated with the specified profile\n" | ||||
+ " (Examples for prof: PDF/A-1b or PDF/X-3:2003)\n\n" | + " (Examples for prof: PDF/A-1b or PDF/X-3:2003)\n\n" | ||||
+ " -conserve Enable memory-conservation policy (trades memory-consumption for disk I/O)\n" | + " -conserve Enable memory-conservation policy (trades memory-consumption for disk I/O)\n" |
<message key="org.apache.fop.fonts.FontEventAdapter.fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message> | <message key="org.apache.fop.fonts.FontEventAdapter.fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message> | ||||
<message key="org.apache.fop.fonts.FontEventAdapter.fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message> | <message key="org.apache.fop.fonts.FontEventAdapter.fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message> | ||||
<message key="org.apache.fop.fonts.FontEventAdapter.glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message> | <message key="org.apache.fop.fonts.FontEventAdapter.glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message> | ||||
<message key="org.apache.fop.fo.FOValidationEventProducer.altTextMissing">Alternate text is missing on {foElement}.{{locator}}</message> | |||||
</catalogue> | </catalogue> |
* multi-column layouts. | * multi-column layouts. | ||||
*/ | */ | ||||
int PR_X_DISABLE_COLUMN_BALANCING = 273; | int PR_X_DISABLE_COLUMN_BALANCING = 273; | ||||
/** Property constant - FOP proprietary: FOP internal use for accessibility */ | |||||
int PR_X_PTR = 274; | |||||
/** | |||||
* Property constant - FOP proprietary: alternative text for e-g and i-f-o. | |||||
* Used for accessibility. | |||||
*/ | |||||
int PR_X_ALT_TEXT = 275; | |||||
/** Number of property constants defined */ | /** Number of property constants defined */ | ||||
int PROPERTY_COUNT = 273; | |||||
int PROPERTY_COUNT = 275; | |||||
// compound property constants | // compound property constants | ||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.fo.extensions.ExtensionAttachment; | import org.apache.fop.fo.extensions.ExtensionAttachment; | ||||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | import org.apache.fop.fo.extensions.ExtensionElementMapping; | ||||
import org.apache.fop.fo.extensions.InternalElementMapping; | |||||
import org.apache.fop.fo.extensions.svg.SVGElementMapping; | import org.apache.fop.fo.extensions.svg.SVGElementMapping; | ||||
import org.apache.fop.fo.pagination.Root; | import org.apache.fop.fo.pagination.Root; | ||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
return "fo:" + localName; | return "fo:" + localName; | ||||
} else if (namespaceURI.equals(ExtensionElementMapping.URI)) { | } else if (namespaceURI.equals(ExtensionElementMapping.URI)) { | ||||
return "fox:" + localName; | return "fox:" + localName; | ||||
} else if (namespaceURI.equals(InternalElementMapping.URI)) { | |||||
return "foi:" + localName; // used FOP internally for accessibility | |||||
} else if (namespaceURI.equals(SVGElementMapping.URI)) { | } else if (namespaceURI.equals(SVGElementMapping.URI)) { | ||||
return "svg:" + localName; | return "svg:" + localName; | ||||
} else { | } else { |
m.setDefault(""); | m.setDefault(""); | ||||
addPropertyMaker("id", m); | addPropertyMaker("id", m); | ||||
// foi:ptr, used for accessibility | |||||
m = new StringProperty.Maker(PR_X_PTR); | |||||
m.setInherited(false); | |||||
m.setDefault(""); | |||||
addPropertyMaker("foi:ptr", m); | |||||
// fox:alt-text, used for accessibility | |||||
m = new StringProperty.Maker(PR_X_ALT_TEXT); | |||||
m.setInherited(false); | |||||
m.setDefault(""); | |||||
addPropertyMaker("fox:alt-text", m); | |||||
// provisional-label-separation | // provisional-label-separation | ||||
m = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION); | m = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION); | ||||
m.setInherited(true); | m.setInherited(true); |
/** | /** | ||||
* Provider class for the event producer. | * Provider class for the event producer. | ||||
*/ | */ | ||||
class Provider { | |||||
final class Provider { | |||||
private Provider() { } | |||||
/** | /** | ||||
* Returns an event producer. | * Returns an event producer. | ||||
void unknownFormattingObject(Object source, String elementName, | void unknownFormattingObject(Object source, String elementName, | ||||
QName offendingNode, Locator loc); | QName offendingNode, Locator loc); | ||||
/** | |||||
* Alternate text is missing for a graphic element. | |||||
* | |||||
* @param source the event source | |||||
* @param foElement name of the element (external-graphic or instream-foreign-object) | |||||
* @param loc the location of the error or null | |||||
* @event.severity WARN | |||||
*/ | |||||
void altTextMissing(Object source, String foElement, Locator loc); | |||||
} | } |
propertyAttributes.add("orphan-content-limit"); | propertyAttributes.add("orphan-content-limit"); | ||||
propertyAttributes.add("internal-destination"); | propertyAttributes.add("internal-destination"); | ||||
propertyAttributes.add("disable-column-balancing"); | propertyAttributes.add("disable-column-balancing"); | ||||
//These are FOP's extension properties for accessibility | |||||
propertyAttributes.add("alt-text"); | |||||
} | } | ||||
/** | /** |
import org.apache.fop.fo.properties.LengthRangeProperty; | import org.apache.fop.fo.properties.LengthRangeProperty; | ||||
/** | /** | ||||
* Class for the fox:external-document extenstion element. | |||||
* Class for the fox:external-document extension element. | |||||
*/ | */ | ||||
public class ExternalDocument extends AbstractPageSequence implements GraphicsProperties { | public class ExternalDocument extends AbstractPageSequence implements GraphicsProperties { | ||||
/* | |||||
* 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.extensions; | |||||
import java.util.HashMap; | |||||
import java.util.Set; | |||||
import org.apache.xmlgraphics.util.QName; | |||||
import org.apache.fop.fo.ElementMapping; | |||||
/** | |||||
* Element mapping for FOP's internal extension to XSL-FO. | |||||
*/ | |||||
public class InternalElementMapping extends ElementMapping { | |||||
/** The FOP extension namespace URI */ | |||||
public static final String URI = "http://xmlgraphics.apache.org/fop/internal"; | |||||
private static final Set PROPERTY_ATTRIBUTES = new java.util.HashSet(); | |||||
static { | |||||
//These are FOP's extension properties for accessibility | |||||
PROPERTY_ATTRIBUTES.add("ptr"); | |||||
} | |||||
/** | |||||
* Constructor. | |||||
*/ | |||||
public InternalElementMapping() { | |||||
namespaceURI = URI; | |||||
} | |||||
/** | |||||
* Initialize the data structures. | |||||
*/ | |||||
protected void initialize() { | |||||
if (foObjs == null) { | |||||
foObjs = new HashMap(); | |||||
} | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public String getStandardPrefix() { | |||||
return "foi"; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public boolean isAttributeProperty(QName attributeName) { | |||||
if (!URI.equals(attributeName.getNamespaceURI())) { | |||||
throw new IllegalArgumentException("The namespace URIs don't match"); | |||||
} | |||||
return PROPERTY_ATTRIBUTES.contains(attributeName.getLocalName()); | |||||
} | |||||
} |
import org.apache.fop.fo.properties.KeepProperty; | import org.apache.fop.fo.properties.KeepProperty; | ||||
import org.apache.fop.fo.properties.LengthRangeProperty; | import org.apache.fop.fo.properties.LengthRangeProperty; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | import org.apache.fop.fo.properties.SpaceProperty; | ||||
import org.apache.fop.fo.properties.StructurePointerPropertySet; | |||||
/** | /** | ||||
* Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_instream-foreign-object"> | * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_instream-foreign-object"> | ||||
* and <a href="http://www.w3.org/TR/xsl/#fo_external-graphic"> | * and <a href="http://www.w3.org/TR/xsl/#fo_external-graphic"> | ||||
* <code>fo:external-graphic</code></a> flow formatting objects. | * <code>fo:external-graphic</code></a> flow formatting objects. | ||||
*/ | */ | ||||
public abstract class AbstractGraphics extends FObj implements GraphicsProperties { | |||||
public abstract class AbstractGraphics extends FObj | |||||
implements GraphicsProperties, StructurePointerPropertySet { | |||||
// The value of properties relevant for fo:instream-foreign-object | // The value of properties relevant for fo:instream-foreign-object | ||||
// and external-graphics. | // and external-graphics. | ||||
private int scaling; | private int scaling; | ||||
private int textAlign; | private int textAlign; | ||||
private Length width; | private Length width; | ||||
private String ptr; // used for accessibility | |||||
// Unused but valid items, commented out for performance: | // Unused but valid items, commented out for performance: | ||||
// private CommonAccessibility commonAccessibility; | // private CommonAccessibility commonAccessibility; | ||||
// private CommonAural commonAural; | // private CommonAural commonAural; | ||||
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | ||||
height = pList.get(PR_HEIGHT).getLength(); | height = pList.get(PR_HEIGHT).getLength(); | ||||
id = pList.get(PR_ID).getString(); | id = pList.get(PR_ID).getString(); | ||||
ptr = pList.get(PR_X_PTR).getString(); // used for accessibility | |||||
inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); | inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); | ||||
keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); | keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); | ||||
keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); | keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); | ||||
scaling = pList.get(PR_SCALING).getEnum(); | scaling = pList.get(PR_SCALING).getEnum(); | ||||
textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); | textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); | ||||
width = pList.get(PR_WIDTH).getLength(); | width = pList.get(PR_WIDTH).getLength(); | ||||
if (getUserAgent().isAccessibilityEnabled()) { | |||||
String altText = pList.get(PR_X_ALT_TEXT).getString(); | |||||
if (altText.equals("")) { | |||||
getFOValidationEventProducer().altTextMissing(this, getLocalName(), getLocator()); | |||||
} | |||||
} | |||||
} | } | ||||
/** | /** | ||||
return keepWithPrevious; | return keepWithPrevious; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** @return the graphic's intrinsic width in millipoints */ | /** @return the graphic's intrinsic width in millipoints */ | ||||
public abstract int getIntrinsicWidth(); | public abstract int getIntrinsicWidth(); | ||||
import java.awt.Color; | import java.awt.Color; | ||||
import org.xml.sax.Locator; | |||||
import org.xml.sax.Attributes; | import org.xml.sax.Attributes; | ||||
import org.xml.sax.Locator; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.datatypes.Length; | import org.apache.fop.datatypes.Length; | ||||
import org.apache.fop.fo.properties.CommonFont; | import org.apache.fop.fo.properties.CommonFont; | ||||
import org.apache.fop.fo.properties.CommonTextDecoration; | import org.apache.fop.fo.properties.CommonTextDecoration; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | import org.apache.fop.fo.properties.SpaceProperty; | ||||
import org.apache.fop.fo.properties.StructurePointerPropertySet; | |||||
/** | /** | ||||
* Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation"> | * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation"> | ||||
* <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation-last"> | * <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation-last"> | ||||
* <code>fo:page-number-citation-last</code></a> objects. | * <code>fo:page-number-citation-last</code></a> objects. | ||||
*/ | */ | ||||
public abstract class AbstractPageNumberCitation extends FObj { | |||||
public abstract class AbstractPageNumberCitation extends FObj | |||||
implements StructurePointerPropertySet { | |||||
// The value of properties relevant for fo:page-number-citation(-last). | // The value of properties relevant for fo:page-number-citation(-last). | ||||
private CommonBorderPaddingBackground commonBorderPaddingBackground; | private CommonBorderPaddingBackground commonBorderPaddingBackground; | ||||
private int alignmentBaseline; | private int alignmentBaseline; | ||||
private Length baselineShift; | private Length baselineShift; | ||||
private int dominantBaseline; | private int dominantBaseline; | ||||
private String ptr; // used for accessibility | |||||
// private ToBeImplementedProperty letterSpacing; | // private ToBeImplementedProperty letterSpacing; | ||||
private SpaceProperty lineHeight; | private SpaceProperty lineHeight; | ||||
private String refId; | private String refId; | ||||
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | ||||
// letterSpacing = pList.get(PR_LETTER_SPACING); | // letterSpacing = pList.get(PR_LETTER_SPACING); | ||||
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | ||||
ptr = pList.get(PR_X_PTR).getString(); // used for accessibility | |||||
refId = pList.get(PR_REF_ID).getString(); | refId = pList.get(PR_REF_ID).getString(); | ||||
textDecoration = pList.getTextDecorationProps(); | textDecoration = pList.getTextDecorationProps(); | ||||
// textShadow = pList.get(PR_TEXT_SHADOW); | // textShadow = pList.get(PR_TEXT_SHADOW); | ||||
return textDecoration; | return textDecoration; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** @return the "alignment-adjust" property */ | /** @return the "alignment-adjust" property */ | ||||
public Length getAlignmentAdjust() { | public Length getAlignmentAdjust() { | ||||
return alignmentAdjust; | return alignmentAdjust; |
import java.awt.Color; | import java.awt.Color; | ||||
import org.xml.sax.Locator; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.datatypes.Length; | import org.apache.fop.datatypes.Length; | ||||
import org.apache.fop.datatypes.Numeric; | import org.apache.fop.datatypes.Numeric; | ||||
import org.apache.fop.fo.properties.CommonRelativePosition; | import org.apache.fop.fo.properties.CommonRelativePosition; | ||||
import org.apache.fop.fo.properties.KeepProperty; | import org.apache.fop.fo.properties.KeepProperty; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | import org.apache.fop.fo.properties.SpaceProperty; | ||||
import org.xml.sax.Locator; | |||||
import org.apache.fop.fo.properties.StructurePointerPropertySet; | |||||
/** | /** | ||||
* Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_block"> | * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_block"> | ||||
* <code>fo:block object</code></a>. | * <code>fo:block object</code></a>. | ||||
*/ | */ | ||||
public class Block extends FObjMixed implements BreakPropertySet { | |||||
public class Block extends FObjMixed implements BreakPropertySet, StructurePointerPropertySet { | |||||
// used for FO validation | // used for FO validation | ||||
private boolean blockOrInlineItemFound = false; | private boolean blockOrInlineItemFound = false; | ||||
private int lineHeightShiftAdjustment; | private int lineHeightShiftAdjustment; | ||||
private int lineStackingStrategy; | private int lineStackingStrategy; | ||||
private Numeric orphans; | private Numeric orphans; | ||||
private String ptr; //used for accessibility | |||||
private int whiteSpaceTreatment; | private int whiteSpaceTreatment; | ||||
private int span; | private int span; | ||||
private int textAlign; | private int textAlign; | ||||
lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum(); | lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum(); | ||||
lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum(); | lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum(); | ||||
orphans = pList.get(PR_ORPHANS).getNumeric(); | orphans = pList.get(PR_ORPHANS).getNumeric(); | ||||
ptr = pList.get(PR_X_PTR).getString(); //used for accessibility | |||||
whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum(); | whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum(); | ||||
span = pList.get(PR_SPAN).getEnum(); | span = pList.get(PR_SPAN).getEnum(); | ||||
textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); | textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); | ||||
return breakAfter; | return breakAfter; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** @return the "break-before" property. */ | /** @return the "break-before" property. */ | ||||
public int getBreakBefore() { | public int getBreakBefore() { | ||||
return breakBefore; | return breakBefore; |
import org.apache.fop.fo.properties.KeepProperty; | import org.apache.fop.fo.properties.KeepProperty; | ||||
import org.apache.fop.fo.properties.Property; | import org.apache.fop.fo.properties.Property; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | 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_character"> | * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_character"> | ||||
* <code>fo:character</code></a> object. | * <code>fo:character</code></a> object. | ||||
*/ | */ | ||||
public class Character extends FObj { | |||||
public class Character extends FObj implements StructurePointerPropertySet { | |||||
// The value of properties relevant for fo:character. | // The value of properties relevant for fo:character. | ||||
private CommonBorderPaddingBackground commonBorderPaddingBackground; | private CommonBorderPaddingBackground commonBorderPaddingBackground; | ||||
private CommonFont commonFont; | private CommonFont commonFont; | ||||
private CommonTextDecoration textDecoration; | private CommonTextDecoration textDecoration; | ||||
// private ToBeImplementedProperty textShadow; | // private ToBeImplementedProperty textShadow; | ||||
private Property wordSpacing; | private Property wordSpacing; | ||||
private String ptr; // used for accessibility | |||||
// Unused but valid items, commented out for performance: | // Unused but valid items, commented out for performance: | ||||
// private CommonAural commonAural; | // private CommonAural commonAural; | ||||
// private CommonMarginInline commonMarginInline; | // private CommonMarginInline commonMarginInline; | ||||
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | ||||
textDecoration = pList.getTextDecorationProps(); | textDecoration = pList.getTextDecorationProps(); | ||||
wordSpacing = pList.get(PR_WORD_SPACING); | wordSpacing = pList.get(PR_WORD_SPACING); | ||||
ptr = pList.get(PR_X_PTR).getString(); // used for accessibility | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
return keepWithPrevious; | return keepWithPrevious; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String getLocalName() { | public String getLocalName() { | ||||
return "character"; | return "character"; |
import org.apache.fop.fo.FONode; | import org.apache.fop.fo.FONode; | ||||
import org.apache.fop.fo.PropertyList; | import org.apache.fop.fo.PropertyList; | ||||
import org.apache.fop.fo.ValidationException; | 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"> | * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_inline"> | ||||
* <code>fo:inline</code></a> formatting object. | * <code>fo:inline</code></a> formatting object. | ||||
*/ | */ | ||||
public class Inline extends InlineLevel { | |||||
public class Inline extends InlineLevel implements StructurePointerPropertySet { | |||||
// The value of properties relevant for fo:inline. | // The value of properties relevant for fo:inline. | ||||
// See also superclass InlineLevel | // See also superclass InlineLevel | ||||
private Length alignmentAdjust; | private Length alignmentAdjust; | ||||
private int alignmentBaseline; | private int alignmentBaseline; | ||||
private Length baselineShift; | private Length baselineShift; | ||||
private String ptr; // used for accessibility | |||||
private int dominantBaseline; | private int dominantBaseline; | ||||
// Unused but valid items, commented out for performance: | // Unused but valid items, commented out for performance: | ||||
// private CommonRelativePosition commonRelativePosition; | // private CommonRelativePosition commonRelativePosition; | ||||
alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); | alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); | ||||
baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); | baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); | ||||
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); | ||||
ptr = pList.get(PR_X_PTR).getString(); // used for accessibility | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
return dominantBaseline; | return dominantBaseline; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String getLocalName() { | public String getLocalName() { | ||||
return "inline"; | return "inline"; |
import org.apache.fop.fo.properties.CommonFont; | import org.apache.fop.fo.properties.CommonFont; | ||||
import org.apache.fop.fo.properties.CommonTextDecoration; | import org.apache.fop.fo.properties.CommonTextDecoration; | ||||
import org.apache.fop.fo.properties.SpaceProperty; | 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_page-number"> | * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-number"> | ||||
* <code>fo:page-number</code></a> object. | * <code>fo:page-number</code></a> object. | ||||
*/ | */ | ||||
public class PageNumber extends FObj { | |||||
public class PageNumber extends FObj implements StructurePointerPropertySet { | |||||
// The value of properties relevant for fo:page-number. | // The value of properties relevant for fo:page-number. | ||||
private CommonBorderPaddingBackground commonBorderPaddingBackground; | private CommonBorderPaddingBackground commonBorderPaddingBackground; | ||||
private CommonFont commonFont; | private CommonFont commonFont; | ||||
private int alignmentBaseline; | private int alignmentBaseline; | ||||
private Length baselineShift; | private Length baselineShift; | ||||
private int dominantBaseline; | private int dominantBaseline; | ||||
private String ptr; // used for accessibility | |||||
// private ToBeImplementedProperty letterSpacing; | // private ToBeImplementedProperty letterSpacing; | ||||
private SpaceProperty lineHeight; | private SpaceProperty lineHeight; | ||||
/** Holds the text decoration values. May be null */ | /** Holds the text decoration values. May be null */ | ||||
// letterSpacing = pList.get(PR_LETTER_SPACING); | // letterSpacing = pList.get(PR_LETTER_SPACING); | ||||
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); | ||||
textDecoration = pList.getTextDecorationProps(); | textDecoration = pList.getTextDecorationProps(); | ||||
ptr = pList.get(PR_X_PTR).getString(); // used for accessibility | |||||
// textShadow = pList.get(PR_TEXT_SHADOW); | // textShadow = pList.get(PR_TEXT_SHADOW); | ||||
// implicit properties | // implicit properties | ||||
return lineHeight; | return lineHeight; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String getLocalName() { | public String getLocalName() { | ||||
return "page-number"; | return "page-number"; |
package org.apache.fop.fo.flow.table; | package org.apache.fop.fo.flow.table; | ||||
import org.xml.sax.Attributes; | |||||
import org.xml.sax.Locator; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.datatypes.Numeric; | import org.apache.fop.datatypes.Numeric; | ||||
import org.apache.fop.datatypes.ValidationPercentBaseContext; | import org.apache.fop.datatypes.ValidationPercentBaseContext; | ||||
import org.apache.fop.fo.properties.NumberProperty; | import org.apache.fop.fo.properties.NumberProperty; | ||||
import org.apache.fop.fo.properties.Property; | import org.apache.fop.fo.properties.Property; | ||||
import org.apache.fop.fo.properties.PropertyMaker; | import org.apache.fop.fo.properties.PropertyMaker; | ||||
import org.apache.fop.fo.properties.StructurePointerPropertySet; | |||||
import org.apache.fop.layoutmgr.table.CollapsingBorderModel; | import org.apache.fop.layoutmgr.table.CollapsingBorderModel; | ||||
import org.xml.sax.Locator; | |||||
import org.xml.sax.Attributes; | |||||
/** | /** | ||||
* Common base class for table-related FOs | * Common base class for table-related FOs | ||||
*/ | */ | ||||
public abstract class TableFObj extends FObj { | |||||
public abstract class TableFObj extends FObj implements StructurePointerPropertySet { | |||||
private Numeric borderAfterPrecedence; | private Numeric borderAfterPrecedence; | ||||
private Numeric borderBeforePrecedence; | private Numeric borderBeforePrecedence; | ||||
private Numeric borderEndPrecedence; | private Numeric borderEndPrecedence; | ||||
private Numeric borderStartPrecedence; | private Numeric borderStartPrecedence; | ||||
private String ptr; | |||||
ConditionalBorder borderBefore; | ConditionalBorder borderBefore; | ||||
ConditionalBorder borderAfter; | ConditionalBorder borderAfter; | ||||
borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric(); | borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric(); | ||||
borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric(); | borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric(); | ||||
borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric(); | borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric(); | ||||
ptr = pList.get(PR_X_PTR).getString(); | |||||
if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java | if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java | ||||
&& getNameId() != FO_TABLE_CELL | && getNameId() != FO_TABLE_CELL | ||||
&& getCommonBorderPaddingBackground().hasPadding( | && getCommonBorderPaddingBackground().hasPadding( | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getPtr() { | |||||
return ptr; | |||||
} | |||||
/** | /** | ||||
* Prepares the borders of this element if the collapsing-border model is in use. | * Prepares the borders of this element if the collapsing-border model is in use. | ||||
* Conflict resolution with parent elements is done where applicable. | * Conflict resolution with parent elements is done where applicable. |
/* | |||||
* 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; | |||||
/** | |||||
* 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(); | |||||
} |
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | ||||
TraitSetter.addPtr(curBlockArea, getBlockFO().getPtr()); // used for accessibility | |||||
TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), | TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), | ||||
effSpaceBefore, effSpaceAfter); | effSpaceBefore, effSpaceAfter); | ||||
flush(); | flush(); |
} | } | ||||
} | } | ||||
/** | |||||
* Adds the ptr trait to the area. | |||||
* @param area the area to set the traits on | |||||
* @param ptr string | |||||
*/ | |||||
public static void addPtr(Area area, String ptr) { | |||||
if (ptr != null && ptr.length() > 0) { | |||||
area.addTrait(Trait.PTR, ptr); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Sets the producer's ID as a trait on the area. This can be used to track back the | * Sets the producer's ID as a trait on the area. This can be used to track back the | ||||
* generating FO node. | * generating FO node. |
transferForeignAttributes(viewportArea); | transferForeignAttributes(viewportArea); | ||||
Viewport vp = new Viewport(viewportArea); | Viewport vp = new Viewport(viewportArea); | ||||
TraitSetter.addPtr(vp, fobj.getPtr()); // used for accessibility | |||||
TraitSetter.setProducerID(vp, fobj.getId()); | TraitSetter.setProducerID(vp, fobj.getId()); | ||||
vp.setIPD(imageLayout.getViewportSize().width); | vp.setIPD(imageLayout.getViewportSize().width); | ||||
vp.setBPD(imageLayout.getViewportSize().height); | vp.setBPD(imageLayout.getViewportSize().height); |
text.setBaselineOffset(font.getAscender()); | text.setBaselineOffset(font.getAscender()); | ||||
TraitSetter.addFontTraits(text, font); | TraitSetter.addFontTraits(text, font); | ||||
text.addTrait(Trait.COLOR, fobj.getColor()); | text.addTrait(Trait.COLOR, fobj.getColor()); | ||||
TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility | |||||
TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); | TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); | ||||
} | } | ||||
package org.apache.fop.layoutmgr.inline; | package org.apache.fop.layoutmgr.inline; | ||||
import org.apache.fop.area.LinkResolver; | |||||
import org.apache.fop.area.Trait; | |||||
import org.apache.fop.area.inline.InlineArea; | |||||
import org.apache.fop.datatypes.URISpecification; | import org.apache.fop.datatypes.URISpecification; | ||||
import org.apache.fop.fo.flow.BasicLink; | |||||
import org.apache.fop.fo.Constants; | import org.apache.fop.fo.Constants; | ||||
import org.apache.fop.fo.flow.BasicLink; | |||||
import org.apache.fop.layoutmgr.PageSequenceLayoutManager; | import org.apache.fop.layoutmgr.PageSequenceLayoutManager; | ||||
import org.apache.fop.area.inline.InlineArea; | |||||
import org.apache.fop.area.Trait; | |||||
import org.apache.fop.area.LinkResolver; | |||||
import org.apache.fop.layoutmgr.TraitSetter; | |||||
/** | /** | ||||
* LayoutManager for the fo:basic-link formatting object | * LayoutManager for the fo:basic-link formatting object | ||||
private void setupBasicLinkArea(InlineArea area) { | private void setupBasicLinkArea(InlineArea area) { | ||||
BasicLink fobj = (BasicLink) this.fobj; | BasicLink fobj = (BasicLink) this.fobj; | ||||
// internal destinations take precedence: | // internal destinations take precedence: | ||||
TraitSetter.addPtr(area, fobj.getPtr()); // used for accessibility | |||||
if (fobj.hasInternalDestination()) { | if (fobj.hasInternalDestination()) { | ||||
String idref = fobj.getInternalDestination(); | String idref = fobj.getInternalDestination(); | ||||
PageSequenceLayoutManager pslm = getPSLM(); | PageSequenceLayoutManager pslm = getPSLM(); |
} | } | ||||
TraitSetter.setProducerID(text, node.getId()); | TraitSetter.setProducerID(text, node.getId()); | ||||
TraitSetter.addTextDecoration(text, node.getTextDecoration()); | TraitSetter.addTextDecoration(text, node.getTextDecoration()); | ||||
TraitSetter.addPtr(text, node.getPtr()); // used for accessibility | |||||
return text; | return text; | ||||
} | } | ||||
text.setBaselineOffset(font.getAscender()); | text.setBaselineOffset(font.getAscender()); | ||||
TraitSetter.addFontTraits(text, font); | TraitSetter.addFontTraits(text, font); | ||||
text.addTrait(Trait.COLOR, fobj.getColor()); | text.addTrait(Trait.COLOR, fobj.getColor()); | ||||
TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility | |||||
TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); | TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); | ||||
return text; | return text; |
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.area.Trait; | import org.apache.fop.area.Trait; | ||||
import org.apache.fop.area.inline.TextArea; | import org.apache.fop.area.inline.TextArea; | ||||
import org.apache.fop.fo.Constants; | import org.apache.fop.fo.Constants; | ||||
import org.apache.fop.fo.FOText; | 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.Font; | ||||
import org.apache.fop.fonts.FontSelector; | import org.apache.fop.fonts.FontSelector; | ||||
import org.apache.fop.layoutmgr.InlineKnuthSequence; | import org.apache.fop.layoutmgr.InlineKnuthSequence; | ||||
} | } | ||||
TraitSetter.addFontTraits(textArea, font); | TraitSetter.addFontTraits(textArea, font); | ||||
textArea.addTrait(Trait.COLOR, this.foText.getColor()); | textArea.addTrait(Trait.COLOR, this.foText.getColor()); | ||||
TraitSetter.addPtr(textArea, getPtr()); // used for accessibility | |||||
TraitSetter.addTextDecoration(textArea, this.foText.getTextDecoration()); | TraitSetter.addTextDecoration(textArea, this.foText.getTextDecoration()); | ||||
return textArea; | return textArea; | ||||
} | } | ||||
/** | |||||
* used for accessibility | |||||
* @return ptr of fobj | |||||
*/ | |||||
private String getPtr() { | |||||
FObj fobj = this.parentLM.getFObj(); | |||||
if (fobj instanceof StructurePointerPropertySet) { | |||||
return (((StructurePointerPropertySet) fobj).getPtr()); | |||||
} else { | |||||
//No structure pointer applicable | |||||
return null; | |||||
} | |||||
} | |||||
private void addToLetterAdjust(final int index, final int width) { | private void addToLetterAdjust(final int index, final int width) { | ||||
if (this.letterAdjustArray[index] == null) { | if (this.letterAdjustArray[index] == null) { | ||||
this.letterAdjustArray[index] = new MinOptMax(width); | this.letterAdjustArray[index] = new MinOptMax(width); |
return this.name; | return this.name; | ||||
} | } | ||||
/** @return true if this mode obey the restrictions established by PDF/A-1b. */ | |||||
/** | |||||
* Indicates whether this mode obeys the restrictions established by PDF/A-1a. | |||||
* @return true if this mode obeys the restrictions established by PDF/A-1a. | |||||
*/ | |||||
public boolean isPDFA1LevelA() { | |||||
return (this != DISABLED); | |||||
} | |||||
/** | |||||
* Indicates whether this mode obeys the restrictions established by PDF/A-1b. | |||||
* @return true if this mode obeys the restrictions established by PDF/A-1b. | |||||
*/ | |||||
public boolean isPDFA1LevelB() { | public boolean isPDFA1LevelB() { | ||||
return (this != DISABLED); | return (this != DISABLED); | ||||
//PDF/A-1a is a superset of PDF/A-1b! | //PDF/A-1a is a superset of PDF/A-1b! |
} | } | ||||
} | } | ||||
/** | |||||
* Indicates whether the given object exists in the array. | |||||
* @param obj the object to look for | |||||
* @return true if obj is contained | |||||
*/ | |||||
public boolean contains(Object obj) { | |||||
return this.values.contains(obj); | |||||
} | |||||
/** | /** | ||||
* Returns the length of the array | * Returns the length of the array | ||||
* @return the length of the array | * @return the length of the array |
return this.root; | return this.root; | ||||
} | } | ||||
/** | |||||
* Makes sure a Lang entry has been set on the document catalog, setting it | |||||
* to a default value if necessary. When accessibility is enabled the | |||||
* language must be specified for any text element in the document. | |||||
*/ | |||||
public void enforceLanguageOnRoot() { | |||||
if (root.getLanguage() == null) { | |||||
String fallbackLanguage; | |||||
if (getProfile().getPDFAMode().isPDFA1LevelA()) { | |||||
//According to Annex B of ISO-19005-1:2005(E), section B.2 | |||||
fallbackLanguage = "x-unknown"; | |||||
} else { | |||||
//No language has been set on the first page-sequence, so fall back to "en". | |||||
fallbackLanguage = "en"; | |||||
} | |||||
root.setLanguage(fallbackLanguage); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Get the {@link PDFInfo} object for this document. | * Get the {@link PDFInfo} object for this document. | ||||
* | * |
public PDFPage makePage(PDFResources resources, int pageIndex, | public PDFPage makePage(PDFResources resources, int pageIndex, | ||||
Rectangle2D mediaBox, Rectangle2D cropBox, | Rectangle2D mediaBox, Rectangle2D cropBox, | ||||
Rectangle2D bleedBox, Rectangle2D trimBox) { | Rectangle2D bleedBox, Rectangle2D trimBox) { | ||||
/* | |||||
* create a PDFPage with the next object number, the given | |||||
* resources, contents and dimensions | |||||
*/ | |||||
PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox); | PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox); | ||||
getDocument().assignObjectNumber(page); | getDocument().assignObjectNumber(page); | ||||
return pageLabels; | return pageLabels; | ||||
} | } | ||||
/** | |||||
* Creates and returns a StructTreeRoot object. Used for accessibility. | |||||
* @param parentTree the value of the ParenTree entry | |||||
* @return structure Tree Root element | |||||
*/ | |||||
public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { | |||||
PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); | |||||
getDocument().assignObjectNumber(structTreeRoot); | |||||
getDocument().addTrailerObject(structTreeRoot); | |||||
getDocument().getRoot().setStructTreeRoot(structTreeRoot); | |||||
return structTreeRoot; | |||||
} | |||||
/** | |||||
* Creates and returns a StructElem object. | |||||
* | |||||
* @param structureType the structure type of the new element (value for the | |||||
* S entry) | |||||
* @param parent the parent of the new structure element in the structure | |||||
* hierarchy | |||||
* @return the newly created element | |||||
*/ | |||||
public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { | |||||
PDFStructElem structElem = new PDFStructElem(parent, structureType); | |||||
getDocument().assignObjectNumber(structElem); | |||||
getDocument().addTrailerObject(structElem); | |||||
return structElem; | |||||
} | |||||
/** | /** | ||||
* Make a the head object of the name dictionary (the /Dests object). | * Make a the head object of the name dictionary (the /Dests object). | ||||
* | * |
private float bry; | private float bry; | ||||
private String color; | private String color; | ||||
private PDFAction action; | private PDFAction action; | ||||
private Integer structParent; | |||||
/** | /** | ||||
* create objects associated with a link annotation (GoToR) | * create objects associated with a link annotation (GoToR) | ||||
this.action = action; | this.action = action; | ||||
} | } | ||||
/** | |||||
* Sets the value of the StructParent entry for this link. | |||||
* | |||||
* @param structParent key in the structure parent tree | |||||
*/ | |||||
public void setStructParent(int structParent) { | |||||
this.structParent = new Integer(structParent); | |||||
} | |||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
+ (brx) + " " + (bry) + " ]\n" + "/C [ " | + (brx) + " " + (bry) + " ]\n" + "/C [ " | ||||
+ this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " | + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " | ||||
+ this.action.getAction() + "\n" + "/H /I\n" | + this.action.getAction() + "\n" + "/H /I\n" | ||||
+ (this.structParent != null | |||||
? "/StructParent " + this.structParent.toString() + "\n" : "") | |||||
+ fFlag + "\n>>\nendobj\n"; | + fFlag + "\n>>\nendobj\n"; | ||||
return s; | return s; | ||||
} | } |
return this.map.size(); | return this.map.size(); | ||||
} | } | ||||
/** | |||||
* Sets an entry. | |||||
* @param key the key of the value to set | |||||
* @param obj the new value | |||||
*/ | |||||
public void put(Integer key, Object obj) { | |||||
this.map.put(key, obj); | |||||
} | |||||
/** | /** | ||||
* Sets an entry. | * Sets an entry. | ||||
* @param key the key of the value to set | * @param key the key of the value to set | ||||
* @param obj the new value | * @param obj the new value | ||||
*/ | */ | ||||
public void put(int key, Object obj) { | public void put(int key, Object obj) { | ||||
this.map.put(new Integer(key), obj); | |||||
put(new Integer(key), obj); | |||||
} | |||||
/** | |||||
* Gets an entry. | |||||
* @param key the key of requested value | |||||
* @return the requested value | |||||
*/ | |||||
public Object get(Integer key) { | |||||
return this.map.get(key); | |||||
} | } | ||||
/** | /** | ||||
* @return the requested value | * @return the requested value | ||||
*/ | */ | ||||
public Object get(int key) { | public Object get(int key) { | ||||
return this.map.get(new Integer(key)); | |||||
return get(new Integer(key)); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ |
return this.pageIndex; | return this.pageIndex; | ||||
} | } | ||||
/** | |||||
* Sets the "StructParents" value. | |||||
* @param structParents the integer key of this object's entry in the structural parent tree. | |||||
*/ | |||||
public void setStructParents(int structParents) { | |||||
put("StructParents", structParents); | |||||
//This is a PDF 1.5 feature. It is set as a work-around for a bug in Adobe Acrobat | |||||
//which reports this missing even if the PDF file is PDF 1.4. | |||||
setTabs(new PDFName("S")); | |||||
} | |||||
/** | |||||
* Returns the value of the StructParents entry. | |||||
* | |||||
* @return the StructParents value, <code>null</code> if the entry has not been set | |||||
*/ | |||||
public Integer getStructParents() { | |||||
return (Integer) get("StructParents"); | |||||
} | |||||
/** | |||||
* Specifies the tab order for annotations on a page. | |||||
* @param value one of the allowed values (see PDF 1.5) | |||||
* @since PDF 1.5 | |||||
*/ | |||||
public void setTabs(PDFName value) { | |||||
put("Tabs", value); | |||||
} | |||||
} | } |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.pdf; | |||||
/** | |||||
* Class representing a PDF /ParentTree. | |||||
*/ | |||||
public class PDFParentTree extends PDFNumberTreeNode { | |||||
/** | |||||
* Returns the number tree corresponding to this parent tree. | |||||
* | |||||
* @return the number tree | |||||
*/ | |||||
public PDFNumsArray getNums() { | |||||
PDFNumsArray nums = super.getNums(); | |||||
if (nums == null) { | |||||
nums = new PDFNumsArray(this); | |||||
setNums(nums); | |||||
} | |||||
return nums; | |||||
} | |||||
} | |||||
*/ | */ | ||||
protected void validateProfileCombination() { | protected void validateProfileCombination() { | ||||
if (pdfAMode != PDFAMode.DISABLED) { | if (pdfAMode != PDFAMode.DISABLED) { | ||||
if (pdfAMode == PDFAMode.PDFA_1A) { | |||||
throw new UnsupportedOperationException("PDF/A-1a is not implemented, yet"); | |||||
} | |||||
if (pdfAMode == PDFAMode.PDFA_1B) { | if (pdfAMode == PDFAMode.PDFA_1B) { | ||||
if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003) { | if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003) { | ||||
throw new PDFConformanceException( | throw new PDFConformanceException( | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Checks a few things required for tagged PDF. | |||||
*/ | |||||
public void verifyTaggedPDF() { | |||||
if (getPDFAMode().isPDFA1LevelA()) { | |||||
final String err = "{0} requires the {1} dictionary entry to be set"; | |||||
PDFDictionary markInfo = getDocument().getRoot().getMarkInfo(); | |||||
if (markInfo == null) { | |||||
throw new PDFConformanceException(format( | |||||
"{0} requires the MarkInfo dictionary to be present", getPDFAMode())); | |||||
} | |||||
if (!Boolean.TRUE.equals(markInfo.get("Marked"))) { | |||||
throw new PDFConformanceException(format(err, | |||||
new Object[] {getPDFAMode(), "Marked"})); | |||||
} | |||||
if (getDocument().getRoot().getStructTreeRoot() == null) { | |||||
throw new PDFConformanceException(format(err, | |||||
new Object[] {getPDFAMode(), "StructTreeRoot"})); | |||||
} | |||||
if (getDocument().getRoot().getLanguage() == null) { | |||||
throw new PDFConformanceException(format(err, | |||||
new Object[] {getPDFAMode(), "Lang"})); | |||||
} | |||||
} | |||||
} | |||||
/** @return true if the ID entry must be present in the trailer. */ | /** @return true if the ID entry must be present in the trailer. */ | ||||
public boolean isIDEntryRequired() { | public boolean isIDEntryRequired() { | ||||
return isPDFAActive() || isPDFXActive(); | return isPDFAActive() || isPDFXActive(); |
package org.apache.fop.pdf; | package org.apache.fop.pdf; | ||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
/** | /** | ||||
* Class representing a Root (/Catalog) object. | * Class representing a Root (/Catalog) object. | ||||
*/ | */ | ||||
* object must be created before the PDF document is | * object must be created before the PDF document is | ||||
* generated, but it is not assigned an object ID until | * generated, but it is not assigned an object ID until | ||||
* it is about to be written (immediately before the xref | * it is about to be written (immediately before the xref | ||||
* table as part of the trsailer). (mark-fop@inomial.com) | |||||
* table as part of the trailer). (mark-fop@inomial.com) | |||||
* | * | ||||
* @param objnum the object's number | * @param objnum the object's number | ||||
* @param pages the PDFPages object | * @param pages the PDFPages object | ||||
setRootPages(pages); | setRootPages(pages); | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
protected int output(OutputStream stream) throws IOException { | |||||
getDocument().getProfile().verifyTaggedPDF(); | |||||
return super.output(stream); | |||||
} | |||||
/** | /** | ||||
* Set the page mode for the PDF document. | * Set the page mode for the PDF document. | ||||
* | * | ||||
put("Lang", lang); | put("Lang", lang); | ||||
} | } | ||||
/** | |||||
* Sets the StructTreeRoot object. Used for accessibility. | |||||
* @param structTreeRoot of this document | |||||
*/ | |||||
public void setStructTreeRoot(PDFStructTreeRoot structTreeRoot) { | |||||
if (structTreeRoot == null) { | |||||
throw new NullPointerException("structTreeRoot must not be null"); | |||||
} | |||||
put("StructTreeRoot", structTreeRoot); | |||||
} | |||||
/** | |||||
* Returns the StructTreeRoot object. | |||||
* @return the structure tree root (or null if accessibility is not enabled) | |||||
*/ | |||||
public PDFStructTreeRoot getStructTreeRoot() { | |||||
return (PDFStructTreeRoot)get("StructTreeRoot"); | |||||
} | |||||
/** | |||||
* Marks this document as conforming to the Tagged PDF conventions. | |||||
*/ | |||||
public void makeTagged() { | |||||
PDFDictionary dict = new PDFDictionary(); | |||||
dict.put("Marked", Boolean.TRUE); | |||||
put("MarkInfo", dict); //new PDFMarkInfo() | |||||
} | |||||
/** | |||||
* Returns the MarkInfo dictionary. | |||||
* @return the MarkInfo dictionary (or null if it's not present) | |||||
*/ | |||||
public PDFDictionary getMarkInfo() { | |||||
return (PDFDictionary)get("MarkInfo"); | |||||
} | |||||
} | } |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.pdf; | |||||
import java.util.Locale; | |||||
import org.apache.fop.util.XMLUtil; | |||||
/** | |||||
* Class representing a PDF Structure Element. | |||||
*/ | |||||
public class PDFStructElem extends PDFDictionary { | |||||
private PDFStructElem parentElement; | |||||
/** | |||||
* Creates a new structure element. | |||||
* | |||||
* @param parent parent of this element | |||||
* @param structureType the structure type of this element | |||||
*/ | |||||
PDFStructElem(PDFObject parent, PDFName structureType) { | |||||
if (parent instanceof PDFStructElem) { | |||||
parentElement = (PDFStructElem) parent; | |||||
} | |||||
put("Type", new PDFName("StructElem")); | |||||
put("S", structureType); | |||||
setParent(parent); | |||||
} | |||||
/** | |||||
* Returns the parent of this structure element. | |||||
* | |||||
* @return the parent, <code>null</code> if the parent is not a structure | |||||
* element (i.e., is the structure tree root) | |||||
*/ | |||||
public PDFStructElem getParentStructElem() { | |||||
return parentElement; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void setParent(PDFObject parent) { | |||||
if (parent != null) { | |||||
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. | |||||
* | |||||
* @param kid element to be added | |||||
*/ | |||||
public void addKid(PDFObject kid) { | |||||
PDFArray kids = getKids(); | |||||
if (kids == null) { | |||||
kids = new PDFArray(); | |||||
put("K", kids); | |||||
} | |||||
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); | |||||
} | |||||
} | |||||
/** | |||||
* Sets the given mcid as the kid of 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. | |||||
* | |||||
* @param mcid mcid of the marked-content sequence corresponding to this | |||||
* structure element's kid | |||||
*/ | |||||
public void setMCIDKid(int mcid) { | |||||
put("K", mcid); | |||||
joinHierarchy(); | |||||
} | |||||
/** | |||||
* Sets the page reference of this structure element. | |||||
* | |||||
* @param page value for the Pg entry | |||||
*/ | |||||
public void setPage(PDFPage page) { | |||||
put("Pg", page); | |||||
} | |||||
/** | |||||
* Returns the structure type of this structure element. | |||||
* | |||||
* @return the value of the S entry | |||||
*/ | |||||
public PDFName getStructureType() { | |||||
return (PDFName)get("S"); | |||||
} | |||||
/** | |||||
* Sets the language of this structure element. | |||||
* @param language the language (as defined in the section about | |||||
* "Natural Language Specification") | |||||
*/ | |||||
private void setLanguage(String language) { | |||||
put("Lang", language); | |||||
} | |||||
/** | |||||
* Sets the language of this structure element. | |||||
* | |||||
* @param language a value for the Lang entry | |||||
*/ | |||||
public void setLanguage(Locale language) { | |||||
setLanguage(XMLUtil.toRFC3066(language)); | |||||
} | |||||
/** | |||||
* Returns the language of this structure element. | |||||
* | |||||
* @return the value of the Lang entry (<code>null</code> if no language was specified) | |||||
*/ | |||||
public String getLanguage() { | |||||
return (String)get("Lang"); | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.pdf; | |||||
/** | |||||
* Class representing a PDF /StructTreeRoot dictionary. | |||||
*/ | |||||
public class PDFStructTreeRoot extends PDFDictionary { | |||||
/** | |||||
* Creates a new /StructTreeRoot dictionary. | |||||
* | |||||
* @param parentTree the value of the ParenTree entry | |||||
*/ | |||||
PDFStructTreeRoot(PDFParentTree parentTree) { | |||||
put("Type", new PDFName("StructTreeRoot")); | |||||
put("K", new PDFArray()); | |||||
put("ParentTree", parentTree); | |||||
} | |||||
/** | |||||
* Returns the children element of this StructTreeRoot. | |||||
* | |||||
* @return the value of the K entry | |||||
*/ | |||||
public PDFArray getKids() { | |||||
return (PDFArray)get("K"); | |||||
} | |||||
/** | |||||
* Adds the given object to the array of kids. | |||||
* | |||||
* @param kid an object to be added to the K entry | |||||
*/ | |||||
public void addKid(PDFObject kid) { | |||||
getKids().add(kid); | |||||
} | |||||
} |
package org.apache.fop.render; | package org.apache.fop.render; | ||||
import org.apache.avalon.framework.configuration.Configuration; | import org.apache.avalon.framework.configuration.Configuration; | ||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
/** | /** | ||||
public abstract class AbstractRendererConfigurator extends AbstractConfigurator { | public abstract class AbstractRendererConfigurator extends AbstractConfigurator { | ||||
private static final String TYPE = "renderer"; | private static final String TYPE = "renderer"; | ||||
/** | /** | ||||
* Default constructor | * Default constructor | ||||
* @param userAgent user agent | * @param userAgent user agent | ||||
protected Configuration getRendererConfig(String mimeType) { | protected Configuration getRendererConfig(String mimeType) { | ||||
return super.getConfig(mimeType); | return super.getConfig(mimeType); | ||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ |
<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en"/> |
String EL_BORDER_RECT = "border-rect"; | String EL_BORDER_RECT = "border-rect"; | ||||
String EL_FONT = "font"; | String EL_FONT = "font"; | ||||
String EL_TEXT = "text"; | String EL_TEXT = "text"; | ||||
/** Parent element of the logical structure tree. */ | |||||
String EL_STRUCTURE_TREE = "structure-tree"; | |||||
} | } |
package org.apache.fop.render.intermediate; | package org.apache.fop.render.intermediate; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Locale; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.xmlgraphics.util.QName; | import org.apache.xmlgraphics.util.QName; | ||||
/** foreign attributes: Map<QName, Object> */ | /** foreign attributes: Map<QName, Object> */ | ||||
private Map foreignAttributes = Collections.EMPTY_MAP; | private Map foreignAttributes = Collections.EMPTY_MAP; | ||||
private Locale language; | |||||
private String structurePointer; | |||||
/** | /** | ||||
* Main constructor. | * Main constructor. | ||||
* @param ua the user agent | * @param ua the user agent | ||||
setForeignAttributes(null); | setForeignAttributes(null); | ||||
} | } | ||||
/** | |||||
* Sets the currently applicable language. | |||||
* @param lang the language | |||||
*/ | |||||
public void setLanguage(Locale lang) { | |||||
this.language = lang; | |||||
} | |||||
/** | |||||
* Returns the currently applicable language. | |||||
* @return the language (or null if the language is undefined) | |||||
*/ | |||||
public Locale getLanguage() { | |||||
return this.language; | |||||
} | |||||
/** | |||||
* Sets the structure pointer for the following painted marks. This method is used when | |||||
* accessibility features are enabled. | |||||
* @param ptr the structure pointer | |||||
*/ | |||||
public void setStructurePointer(String ptr) { | |||||
this.structurePointer = ptr; | |||||
} | |||||
/** | |||||
* Resets the current structure pointer. | |||||
* @see #setStructurePointer(String) | |||||
*/ | |||||
public void resetStructurePointer() { | |||||
setStructurePointer(null); | |||||
} | |||||
/** | |||||
* Returns the current structure pointer. | |||||
* @return the structure pointer (or null if no pointer is active) | |||||
* @see #setStructurePointer(String) | |||||
*/ | |||||
public String getStructurePointer() { | |||||
return this.structurePointer; | |||||
} | |||||
} | } |
import java.awt.Rectangle; | import java.awt.Rectangle; | ||||
import java.awt.geom.AffineTransform; | import java.awt.geom.AffineTransform; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | |||||
import javax.xml.transform.Source; | import javax.xml.transform.Source; | ||||
import javax.xml.transform.Transformer; | import javax.xml.transform.Transformer; | ||||
import org.w3c.dom.DOMImplementation; | import org.w3c.dom.DOMImplementation; | ||||
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.xml.sax.Attributes; | import org.xml.sax.Attributes; | ||||
import org.xml.sax.ContentHandler; | import org.xml.sax.ContentHandler; | ||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.apache.xmlgraphics.util.QName; | import org.apache.xmlgraphics.util.QName; | ||||
import org.apache.fop.accessibility.AccessibilityEventProducer; | |||||
import org.apache.fop.accessibility.StructureTreeBuilder; | |||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.fo.ElementMapping; | import org.apache.fop.fo.ElementMapping; | ||||
import org.apache.fop.fo.ElementMappingRegistry; | import org.apache.fop.fo.ElementMappingRegistry; | ||||
import org.apache.fop.util.ContentHandlerFactoryRegistry; | import org.apache.fop.util.ContentHandlerFactoryRegistry; | ||||
import org.apache.fop.util.DOMBuilderContentHandlerFactory; | import org.apache.fop.util.DOMBuilderContentHandlerFactory; | ||||
import org.apache.fop.util.DefaultErrorListener; | import org.apache.fop.util.DefaultErrorListener; | ||||
import org.apache.fop.util.DelegatingContentHandler; | |||||
import org.apache.fop.util.XMLUtil; | import org.apache.fop.util.XMLUtil; | ||||
/** | /** | ||||
private static SAXTransformerFactory tFactory | private static SAXTransformerFactory tFactory | ||||
= (SAXTransformerFactory)SAXTransformerFactory.newInstance(); | = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); | ||||
private static Set handledNamespaces = new java.util.HashSet(); | |||||
static { | |||||
handledNamespaces.add(XMLNS_NAMESPACE_URI); | |||||
handledNamespaces.add(XML_NAMESPACE); | |||||
handledNamespaces.add(NAMESPACE); | |||||
handledNamespaces.add(XLINK_NAMESPACE); | |||||
} | |||||
/** | /** | ||||
* Parses an intermediate file and paints it. | * Parses an intermediate file and paints it. | ||||
* @param src the Source instance pointing to the intermediate file | * @param src the Source instance pointing to the intermediate file | ||||
private ContentHandler navParser; | private ContentHandler navParser; | ||||
private StructureTreeBuilder structureTreeBuilder; | |||||
private ContentHandler structureTreeBuilderWrapper; | |||||
private Attributes pageSequenceAttributes; | |||||
private final class StructureTreeBuilderWrapper extends DelegatingContentHandler { | |||||
private StructureTreeBuilderWrapper() | |||||
throws SAXException { | |||||
super(structureTreeBuilder.getHandlerForNextPageSequence()); | |||||
} | |||||
public void endDocument() throws SAXException { | |||||
super.endDocument(); | |||||
startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes); | |||||
pageSequenceAttributes = null; | |||||
} | |||||
} | |||||
public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent, | public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent, | ||||
ElementMappingRegistry elementMappingRegistry) { | ElementMappingRegistry elementMappingRegistry) { | ||||
this.documentHandler = documentHandler; | this.documentHandler = documentHandler; | ||||
elementHandlers.put(EL_LINE, new LineHandler()); | elementHandlers.put(EL_LINE, new LineHandler()); | ||||
elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler()); | elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler()); | ||||
elementHandlers.put(EL_IMAGE, new ImageHandler()); | elementHandlers.put(EL_IMAGE, new ImageHandler()); | ||||
if (userAgent.isAccessibilityEnabled()) { | |||||
structureTreeBuilder = new StructureTreeBuilder(tFactory); | |||||
userAgent.setStructureTree(structureTreeBuilder.getStructureTree()); | |||||
} | |||||
} | } | ||||
private void establishForeignAttributes(Map foreignAttributes) { | private void establishForeignAttributes(Map foreignAttributes) { | ||||
documentHandler.getContext().resetForeignAttributes(); | documentHandler.getContext().resetForeignAttributes(); | ||||
} | } | ||||
private void establishStructurePointer(String ptr) { | |||||
documentHandler.getContext().setStructurePointer(ptr); | |||||
} | |||||
private void resetStructurePointer() { | |||||
documentHandler.getContext().resetStructurePointer(); | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void startElement(String uri, String localName, String qName, Attributes attributes) | public void startElement(String uri, String localName, String qName, Attributes attributes) | ||||
throws SAXException { | throws SAXException { | ||||
if (delegate != null) { | if (delegate != null) { | ||||
//delegateStack.push(qName); | |||||
delegateDepth++; | delegateDepth++; | ||||
delegate.startElement(uri, localName, qName, attributes); | delegate.startElement(uri, localName, qName, attributes); | ||||
} else { | } else { | ||||
boolean handled = true; | boolean handled = true; | ||||
if (NAMESPACE.equals(uri)) { | if (NAMESPACE.equals(uri)) { | ||||
lastAttributes = new AttributesImpl(attributes); | |||||
ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName); | |||||
content.setLength(0); | |||||
ignoreCharacters = true; | |||||
if (elementHandler != null) { | |||||
ignoreCharacters = elementHandler.ignoreCharacters(); | |||||
try { | |||||
elementHandler.startElement(attributes); | |||||
} catch (IFException ife) { | |||||
handleIFException(ife); | |||||
if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) { | |||||
pageSequenceAttributes = new AttributesImpl(attributes); | |||||
structureTreeBuilderWrapper = new StructureTreeBuilderWrapper(); | |||||
} else if (localName.equals(EL_STRUCTURE_TREE)) { | |||||
if (userAgent.isAccessibilityEnabled()) { | |||||
delegate = structureTreeBuilderWrapper; | |||||
} else { | |||||
/* Delegate to a handler that does nothing */ | |||||
delegate = new DefaultHandler(); | |||||
} | } | ||||
} else if ("extension-attachments".equals(localName)) { | |||||
//TODO implement me | |||||
delegateDepth++; | |||||
delegate.startDocument(); | |||||
delegate.startElement(uri, localName, qName, attributes); | |||||
} else { | } else { | ||||
handled = false; | |||||
if (pageSequenceAttributes != null) { | |||||
/* | |||||
* This means that no structure-element tag was | |||||
* found in the XML, otherwise a | |||||
* StructureTreeBuilderWrapper object would have | |||||
* been created, which would have reset the | |||||
* pageSequenceAttributes field. | |||||
*/ | |||||
AccessibilityEventProducer.Provider | |||||
.get(userAgent.getEventBroadcaster()) | |||||
.noStructureTreeInXML(this); | |||||
} | |||||
handled = startIFElement(localName, attributes); | |||||
} | } | ||||
} else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { | } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { | ||||
if (this.navParser == null) { | if (this.navParser == null) { | ||||
} | } | ||||
} | } | ||||
private boolean startIFElement(String localName, Attributes attributes) | |||||
throws SAXException { | |||||
lastAttributes = new AttributesImpl(attributes); | |||||
ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName); | |||||
content.setLength(0); | |||||
ignoreCharacters = true; | |||||
if (elementHandler != null) { | |||||
ignoreCharacters = elementHandler.ignoreCharacters(); | |||||
try { | |||||
elementHandler.startElement(attributes); | |||||
} catch (IFException ife) { | |||||
handleIFException(ife); | |||||
} | |||||
return true; | |||||
} else { | |||||
return false; | |||||
} | |||||
} | |||||
private void handleIFException(IFException ife) throws SAXException { | private void handleIFException(IFException ife) throws SAXException { | ||||
if (ife.getCause() instanceof SAXException) { | if (ife.getCause() instanceof SAXException) { | ||||
//unwrap | //unwrap | ||||
public void startElement(Attributes attributes) throws IFException { | public void startElement(Attributes attributes) throws IFException { | ||||
String id = attributes.getValue("id"); | String id = attributes.getValue("id"); | ||||
String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); | |||||
if (xmllang != null) { | |||||
documentHandler.getContext().setLanguage( | |||||
XMLUtil.convertRFC3066ToLocale(xmllang)); | |||||
} | |||||
Map foreignAttributes = getForeignAttributes(lastAttributes); | Map foreignAttributes = getForeignAttributes(lastAttributes); | ||||
establishForeignAttributes(foreignAttributes); | establishForeignAttributes(foreignAttributes); | ||||
documentHandler.startPageSequence(id); | documentHandler.startPageSequence(id); | ||||
public void endElement() throws IFException { | public void endElement() throws IFException { | ||||
documentHandler.endPageSequence(); | documentHandler.endPageSequence(); | ||||
documentHandler.getContext().setLanguage(null); | |||||
} | } | ||||
} | } | ||||
s = lastAttributes.getValue("word-spacing"); | s = lastAttributes.getValue("word-spacing"); | ||||
int wordSpacing = (s != null ? Integer.parseInt(s) : 0); | int wordSpacing = (s != null ? Integer.parseInt(s) : 0); | ||||
int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx"); | int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx"); | ||||
setStructurePointer(lastAttributes); | |||||
painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString()); | painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString()); | ||||
resetStructurePointer(); | |||||
} | } | ||||
public boolean ignoreCharacters() { | public boolean ignoreCharacters() { | ||||
int height = Integer.parseInt(lastAttributes.getValue("height")); | int height = Integer.parseInt(lastAttributes.getValue("height")); | ||||
Map foreignAttributes = getForeignAttributes(lastAttributes); | Map foreignAttributes = getForeignAttributes(lastAttributes); | ||||
establishForeignAttributes(foreignAttributes); | establishForeignAttributes(foreignAttributes); | ||||
setStructurePointer(lastAttributes); | |||||
if (foreignObject != null) { | if (foreignObject != null) { | ||||
painter.drawImage(foreignObject, | painter.drawImage(foreignObject, | ||||
new Rectangle(x, y, width, height)); | new Rectangle(x, y, width, height)); | ||||
painter.drawImage(uri, new Rectangle(x, y, width, height)); | painter.drawImage(uri, new Rectangle(x, y, width, height)); | ||||
} | } | ||||
resetForeignAttributes(); | resetForeignAttributes(); | ||||
resetStructurePointer(); | |||||
inForeignObject = false; | inForeignObject = false; | ||||
} | } | ||||
for (int i = 0, c = atts.getLength(); i < c; i++) { | for (int i = 0, c = atts.getLength(); i < c; i++) { | ||||
String ns = atts.getURI(i); | String ns = atts.getURI(i); | ||||
if (ns.length() > 0) { | if (ns.length() > 0) { | ||||
if ("http://www.w3.org/2000/xmlns/".equals(ns)) { | |||||
continue; | |||||
} else if (NAMESPACE.equals(ns)) { | |||||
continue; | |||||
} else if (XLINK_NAMESPACE.equals(ns)) { | |||||
if (handledNamespaces.contains(ns)) { | |||||
continue; | continue; | ||||
} | } | ||||
if (foreignAttributes == null) { | if (foreignAttributes == null) { | ||||
return foreignAttributes; | return foreignAttributes; | ||||
} | } | ||||
private void setStructurePointer(Attributes attributes) { | |||||
String ptr = attributes.getValue("ptr"); | |||||
if (ptr != null && ptr.length() > 0) { | |||||
establishStructurePointer(ptr); | |||||
} | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void characters(char[] ch, int start, int length) throws SAXException { | public void characters(char[] ch, int start, int length) throws SAXException { | ||||
if (delegate != null) { | if (delegate != null) { |
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Stack; | import java.util.Stack; | ||||
try { | try { | ||||
if (this.inPageSequence) { | if (this.inPageSequence) { | ||||
documentHandler.endPageSequence(); | documentHandler.endPageSequence(); | ||||
documentHandler.getContext().setLanguage(null); | |||||
} else { | } else { | ||||
if (this.documentMetadata == null) { | if (this.documentMetadata == null) { | ||||
this.documentMetadata = createDefaultDocumentMetadata(); | this.documentMetadata = createDefaultDocumentMetadata(); | ||||
this.inPageSequence = true; | this.inPageSequence = true; | ||||
} | } | ||||
establishForeignAttributes(pageSequence.getForeignAttributes()); | establishForeignAttributes(pageSequence.getForeignAttributes()); | ||||
documentHandler.getContext().setLanguage(toLocale(pageSequence)); | |||||
documentHandler.startPageSequence(null); | documentHandler.startPageSequence(null); | ||||
resetForeignAttributes(); | resetForeignAttributes(); | ||||
processExtensionAttachments(pageSequence); | processExtensionAttachments(pageSequence); | ||||
} | } | ||||
} | } | ||||
private Locale toLocale(PageSequence pageSequence) { | |||||
if (pageSequence.getLanguage() != null) { | |||||
if (pageSequence.getCountry() != null) { | |||||
return new Locale(pageSequence.getLanguage(), pageSequence.getCountry()); | |||||
} else { | |||||
return new Locale(pageSequence.getLanguage()); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private Metadata createDefaultDocumentMetadata() { | private Metadata createDefaultDocumentMetadata() { | ||||
Metadata xmp = new Metadata(); | Metadata xmp = new Metadata(); | ||||
DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp); | DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp); | ||||
documentHandler.getContext().resetForeignAttributes(); | documentHandler.getContext().resetForeignAttributes(); | ||||
} | } | ||||
private void establishStructurePointer(String ptr) { | |||||
documentHandler.getContext().setStructurePointer(ptr); | |||||
} | |||||
private void resetStructurePointer() { | |||||
documentHandler.getContext().resetStructurePointer(); | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected void saveGraphicsState() { | protected void saveGraphicsState() { | ||||
graphicContextStack.push(graphicContext); | graphicContextStack.push(graphicContext); | ||||
currentIPPosition = saveIP; | currentIPPosition = saveIP; | ||||
currentBPPosition = saveBP; | currentBPPosition = saveBP; | ||||
currentBPPosition += (int)(bv.getAllocBPD()); | |||||
currentBPPosition += bv.getAllocBPD(); | |||||
} | } | ||||
viewportDimensionStack.pop(); | viewportDimensionStack.pop(); | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void renderViewport(Viewport viewport) { | public void renderViewport(Viewport viewport) { | ||||
String ptr = (String) viewport.getTrait(Trait.PTR); | |||||
establishStructurePointer(ptr); | |||||
Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); | Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); | ||||
viewportDimensionStack.push(dim); | viewportDimensionStack.push(dim); | ||||
super.renderViewport(viewport); | super.renderViewport(viewport); | ||||
viewportDimensionStack.pop(); | viewportDimensionStack.pop(); | ||||
resetStructurePointer(); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
// stuff we only need if a link must be created: | // stuff we only need if a link must be created: | ||||
Rectangle ipRect = null; | Rectangle ipRect = null; | ||||
AbstractAction action = null; | AbstractAction action = null; | ||||
String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility | |||||
// make sure the rect is determined *before* calling super! | // make sure the rect is determined *before* calling super! | ||||
int ipp = currentIPPosition; | int ipp = currentIPPosition; | ||||
int bpp = currentBPPosition + ip.getOffset(); | int bpp = currentBPPosition + ip.getOffset(); | ||||
// warn if link trait found but not allowed, else create link | // warn if link trait found but not allowed, else create link | ||||
if (linkTraitFound) { | if (linkTraitFound) { | ||||
action.setStructurePointer(ptr); // used for accessibility | |||||
Link link = new Link(action, ipRect); | Link link = new Link(action, ipRect); | ||||
this.deferredLinks.add(link); | this.deferredLinks.add(link); | ||||
} | } | ||||
String fontName = getInternalFontNameForArea(text); | String fontName = getInternalFontNameForArea(text); | ||||
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); | int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); | ||||
String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility | |||||
establishStructurePointer(ptr); | |||||
// This assumes that *all* CIDFonts use a /ToUnicode mapping | // This assumes that *all* CIDFonts use a /ToUnicode mapping | ||||
Typeface tf = getTypeface(fontName); | Typeface tf = getTypeface(fontName); | ||||
textUtil.flush(); | textUtil.flush(); | ||||
renderTextDecoration(tf, size, text, bl, rx); | renderTextDecoration(tf, size, text, bl, rx); | ||||
resetStructurePointer(); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
private static final int INITIAL_BUFFER_SIZE = 16; | private static final int INITIAL_BUFFER_SIZE = 16; | ||||
private int[] dx = new int[INITIAL_BUFFER_SIZE]; | private int[] dx = new int[INITIAL_BUFFER_SIZE]; | ||||
private int lastDXPos = 0; | private int lastDXPos = 0; | ||||
private StringBuffer text = new StringBuffer(); | |||||
private final StringBuffer text = new StringBuffer(); | |||||
private int startx, starty; | private int startx, starty; | ||||
private int tls, tws; | private int tls, tws; | ||||
private boolean combined = false; | |||||
private final boolean combined = false; | |||||
void addChar(char ch) { | void addChar(char ch) { | ||||
text.append(ch); | text.append(ch); |
import java.awt.geom.AffineTransform; | import java.awt.geom.AffineTransform; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.xml.sax.helpers.AttributesImpl; | import org.xml.sax.helpers.AttributesImpl; | ||||
import org.apache.xmlgraphics.util.QName; | import org.apache.xmlgraphics.util.QName; | ||||
import org.apache.xmlgraphics.util.XMLizable; | import org.apache.xmlgraphics.util.XMLizable; | ||||
import org.apache.fop.accessibility.StructureTree; | |||||
import org.apache.fop.fonts.FontInfo; | import org.apache.fop.fonts.FontInfo; | ||||
import org.apache.fop.render.PrintRendererConfigurator; | import org.apache.fop.render.PrintRendererConfigurator; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
implements IFConstants, IFPainter, IFDocumentNavigationHandler { | implements IFConstants, IFPainter, IFDocumentNavigationHandler { | ||||
private IFDocumentHandler mimicHandler; | private IFDocumentHandler mimicHandler; | ||||
private int pageSequenceIndex; // used for accessibility | |||||
/** Holds the intermediate format state */ | /** Holds the intermediate format state */ | ||||
private IFState state; | private IFState state; | ||||
if (id != null) { | if (id != null) { | ||||
atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id); | atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id); | ||||
} | } | ||||
Locale lang = getContext().getLanguage(); | |||||
if (lang != null) { | |||||
atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, | |||||
XMLUtil.toRFC3066(lang)); | |||||
} | |||||
addForeignAttributes(atts); | addForeignAttributes(atts); | ||||
handler.startElement(EL_PAGE_SEQUENCE, atts); | handler.startElement(EL_PAGE_SEQUENCE, atts); | ||||
if (this.getUserAgent().isAccessibilityEnabled()) { | |||||
StructureTree structureTree = getUserAgent().getStructureTree(); | |||||
handler.startElement(EL_STRUCTURE_TREE); // add structure tree | |||||
NodeList nodes = structureTree.getPageSequence(pageSequenceIndex++); | |||||
for (int i = 0, n = nodes.getLength(); i < n; i++) { | |||||
Node node = nodes.item(i); | |||||
new DOM2SAX(handler).writeFragment(node); | |||||
} | |||||
handler.endElement(EL_STRUCTURE_TREE); | |||||
} | |||||
} catch (SAXException e) { | } catch (SAXException e) { | ||||
throw new IFException("SAX error in startPageSequence()", e); | throw new IFException("SAX error in startPageSequence()", e); | ||||
} | } | ||||
addAttribute(atts, "width", Integer.toString(rect.width)); | addAttribute(atts, "width", Integer.toString(rect.width)); | ||||
addAttribute(atts, "height", Integer.toString(rect.height)); | addAttribute(atts, "height", Integer.toString(rect.height)); | ||||
addForeignAttributes(atts); | addForeignAttributes(atts); | ||||
addStructurePointerAttribute(atts); | |||||
handler.element(EL_IMAGE, atts); | handler.element(EL_IMAGE, atts); | ||||
} catch (SAXException e) { | } catch (SAXException e) { | ||||
throw new IFException("SAX error in startGroup()", e); | throw new IFException("SAX error in startGroup()", e); | ||||
} | } | ||||
} | } | ||||
private void addForeignAttributes(AttributesImpl atts) { | |||||
private void addForeignAttributes(AttributesImpl atts) throws SAXException { | |||||
Map foreignAttributes = getContext().getForeignAttributes(); | Map foreignAttributes = getContext().getForeignAttributes(); | ||||
if (!foreignAttributes.isEmpty()) { | if (!foreignAttributes.isEmpty()) { | ||||
Iterator iter = foreignAttributes.entrySet().iterator(); | Iterator iter = foreignAttributes.entrySet().iterator(); | ||||
addAttribute(atts, "width", Integer.toString(rect.width)); | addAttribute(atts, "width", Integer.toString(rect.width)); | ||||
addAttribute(atts, "height", Integer.toString(rect.height)); | addAttribute(atts, "height", Integer.toString(rect.height)); | ||||
addForeignAttributes(atts); | addForeignAttributes(atts); | ||||
addStructurePointerAttribute(atts); | |||||
handler.startElement(EL_IMAGE, atts); | handler.startElement(EL_IMAGE, atts); | ||||
new DOM2SAX(handler).writeDocument(doc, true); | new DOM2SAX(handler).writeDocument(doc, true); | ||||
handler.endElement(EL_IMAGE); | handler.endElement(EL_IMAGE); | ||||
if (dx != null) { | if (dx != null) { | ||||
addAttribute(atts, "dx", IFUtil.toString(dx)); | addAttribute(atts, "dx", IFUtil.toString(dx)); | ||||
} | } | ||||
addStructurePointerAttribute(atts); | |||||
handler.startElement(EL_TEXT, atts); | handler.startElement(EL_TEXT, atts); | ||||
char[] chars = text.toCharArray(); | char[] chars = text.toCharArray(); | ||||
handler.characters(chars, 0, chars.length); | handler.characters(chars, 0, chars.length); | ||||
} | } | ||||
private void addAttribute(AttributesImpl atts, | private void addAttribute(AttributesImpl atts, | ||||
org.apache.xmlgraphics.util.QName attribute, String value) { | |||||
org.apache.xmlgraphics.util.QName attribute, String value) throws SAXException { | |||||
handler.startPrefixMapping(attribute.getPrefix(), attribute.getNamespaceURI()); | |||||
XMLUtil.addAttribute(atts, attribute, value); | XMLUtil.addAttribute(atts, attribute, value); | ||||
} | } | ||||
XMLUtil.addAttribute(atts, localName, value); | XMLUtil.addAttribute(atts, localName, value); | ||||
} | } | ||||
private void addStructurePointerAttribute(AttributesImpl atts) { | |||||
String ptr = getContext().getStructurePointer(); | |||||
if (ptr != null) { | |||||
addAttribute(atts, "ptr", ptr); | |||||
} | |||||
} | |||||
// ---=== IFDocumentNavigationHandler ===--- | // ---=== IFDocumentNavigationHandler ===--- | ||||
private Map incompleteActions = new java.util.HashMap(); | private Map incompleteActions = new java.util.HashMap(); | ||||
AttributesImpl atts = new AttributesImpl(); | AttributesImpl atts = new AttributesImpl(); | ||||
atts.addAttribute(null, "rect", "rect", | atts.addAttribute(null, "rect", "rect", | ||||
XMLConstants.CDATA, IFUtil.toString(link.getTargetRect())); | XMLConstants.CDATA, IFUtil.toString(link.getTargetRect())); | ||||
if (getUserAgent().isAccessibilityEnabled()) { | |||||
addAttribute(atts, "ptr", link.getAction().getStructurePointer()); | |||||
} | |||||
try { | try { | ||||
handler.startElement(DocumentNavigationExtensionConstants.LINK, atts); | handler.startElement(DocumentNavigationExtensionConstants.LINK, atts); | ||||
serializeXMLizable(link.getAction()); | serializeXMLizable(link.getAction()); |
public abstract class AbstractAction implements XMLizable { | public abstract class AbstractAction implements XMLizable { | ||||
private String id; | private String id; | ||||
private String structurePointer; | |||||
/** | /** | ||||
* Sets an ID to make the action referencable. | * Sets an ID to make the action referencable. | ||||
return this.id; | return this.id; | ||||
} | } | ||||
/** | |||||
* Sets the structure element corresponding to this action. | |||||
* @param structurePointer a reference to the structure element | |||||
*/ | |||||
public void setStructurePointer(String structurePointer) { | |||||
this.structurePointer = structurePointer; | |||||
} | |||||
/** | |||||
* Returns the structure element corresponding to this action. | |||||
* @return the reference to the structure element | |||||
*/ | |||||
public String getStructurePointer() { | |||||
return structurePointer; | |||||
} | |||||
/** | /** | ||||
* Indicates whether the action has an ID and is therefore referencable. | * Indicates whether the action has an ID and is therefore referencable. | ||||
* @return true if the action has an ID | * @return true if the action has an ID |
private IFDocumentNavigationHandler navHandler; | private IFDocumentNavigationHandler navHandler; | ||||
private String structurePointer; | |||||
/** | /** | ||||
* Main constructor. | * Main constructor. | ||||
* @param navHandler the navigation handler that will receive the events | * @param navHandler the navigation handler that will receive the events | ||||
throw new SAXException(localName + " must be the root element!"); | throw new SAXException(localName + " must be the root element!"); | ||||
} | } | ||||
Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect"); | Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect"); | ||||
structurePointer = attributes.getValue("ptr"); | |||||
Link link = new Link(null, targetRect); | Link link = new Link(null, targetRect); | ||||
objectStack.push(link); | objectStack.push(link); | ||||
} else if (GOTO_XY.getLocalName().equals(localName)) { | } else if (GOTO_XY.getLocalName().equals(localName)) { | ||||
} | } | ||||
action = new GoToXYAction(id, pageIndex, location); | action = new GoToXYAction(id, pageIndex, location); | ||||
} | } | ||||
if (structurePointer != null) { | |||||
action.setStructurePointer(structurePointer); | |||||
} | |||||
objectStack.push(action); | objectStack.push(action); | ||||
} else if (GOTO_URI.getLocalName().equals(localName)) { | } else if (GOTO_URI.getLocalName().equals(localName)) { | ||||
String id = attributes.getValue("id"); | String id = attributes.getValue("id"); | ||||
if (id != null) { | if (id != null) { | ||||
action.setID(id); | action.setID(id); | ||||
} | } | ||||
if (structurePointer != null) { | |||||
action.setStructurePointer(structurePointer); | |||||
} | |||||
objectStack.push(action); | objectStack.push(action); | ||||
} else { | } else { | ||||
throw new SAXException( | throw new SAXException( |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.render.pdf; | |||||
import java.util.Map; | |||||
import org.apache.fop.pdf.PDFName; | |||||
import org.apache.fop.pdf.PDFObject; | |||||
import org.apache.fop.pdf.PDFStructElem; | |||||
/** | |||||
* This class provides the standard mappings from Formatting Objects to PDF structure types. | |||||
*/ | |||||
final class FOToPDFRoleMap { | |||||
private static final Map STANDARD_MAPPINGS = new java.util.HashMap(); | |||||
private static final PDFName TFOOT = new PDFName("TFoot"); | |||||
private static final PDFName THEAD = new PDFName("THead"); | |||||
private static final PDFName NON_STRUCT = new PDFName("NonStruct"); | |||||
static { | |||||
addMapping("block", "P"); | |||||
PDFName st = new PDFName("Div"); | |||||
addMapping("block-container", st); | |||||
addMapping("inline-container", st); | |||||
addMapping("table-and-caption", st); | |||||
addMapping("float", st); | |||||
st = new PDFName("Span"); | |||||
addMapping("inline", st); | |||||
addMapping("wrapper", st); | |||||
addMapping("character", st); | |||||
addMapping("root", "Document"); | |||||
addMapping("page-sequence", "Part"); | |||||
addMapping("flow", "Sect"); | |||||
addMapping("static-content", "Sect"); | |||||
st = new PDFName("Quote"); | |||||
addMapping("page-number", st); | |||||
addMapping("page-number-citation", st); | |||||
addMapping("page-number-citation-last", st); | |||||
st = new PDFName("Figure"); | |||||
addMapping("external-graphic", st); | |||||
addMapping("instream-foreign-object", st); | |||||
addMapping("table-caption", "Caption"); | |||||
addMapping("table", "Table"); | |||||
addMapping("table-body", "TBody"); | |||||
addMapping("table-header", THEAD); | |||||
addMapping("table-footer", TFOOT); | |||||
addMapping("table-row", "TR"); | |||||
addMapping("table-cell", new TableCellMapper()); | |||||
addMapping("list-block", "L"); | |||||
addMapping("list-item", "LI"); | |||||
addMapping("list-item-label", "Lbl"); | |||||
addMapping("list-item-body", "LBody"); | |||||
addMapping("basic-link", "Link"); | |||||
addMapping("footnote", "Note"); | |||||
addMapping("footnote-body", "Sect"); | |||||
addMapping("marker", "Private"); | |||||
} | |||||
private static void addMapping(String fo, String pdfName) { | |||||
addMapping(fo, new PDFName(pdfName)); | |||||
} | |||||
private static void addMapping(String fo, PDFName pdfName) { | |||||
addMapping(fo, new SimpleMapper(pdfName)); | |||||
} | |||||
private static void addMapping(String fo, Mapper mapper) { | |||||
STANDARD_MAPPINGS.put(fo, mapper); | |||||
} | |||||
/** | |||||
* Maps a Formatting Object to a PDFName representing the associated structure type. | |||||
* @param fo the formatting object's local name | |||||
* @param parent the parent of the structure element to be mapped | |||||
* @return the structure type or null if no match could be found | |||||
*/ | |||||
public static PDFName mapFormattingObject(String fo, PDFObject parent) { | |||||
Mapper mapper = (Mapper)STANDARD_MAPPINGS.get(fo); | |||||
if (mapper != null) { | |||||
return mapper.getStructureType(parent); | |||||
} else { | |||||
return NON_STRUCT; | |||||
} | |||||
} | |||||
private interface Mapper { | |||||
PDFName getStructureType(PDFObject parent); | |||||
} | |||||
private static class SimpleMapper implements Mapper { | |||||
private PDFName structureType; | |||||
public SimpleMapper(PDFName structureType) { | |||||
this.structureType = structureType; | |||||
} | |||||
public PDFName getStructureType(PDFObject parent) { | |||||
return structureType; | |||||
} | |||||
} | |||||
private static class TableCellMapper implements Mapper { | |||||
private static final PDFName TD = new PDFName("TD"); | |||||
private static final PDFName TH = new PDFName("TH"); | |||||
public PDFName getStructureType(PDFObject parent) { | |||||
PDFStructElem grandParent = (PDFStructElem) | |||||
((PDFStructElem)parent).getParentStructElem(); | |||||
//TODO What to do with cells from table-footer? Currently they are mapped on TD. | |||||
if (THEAD.equals(grandParent.getStructureType())) { | |||||
return TH; | |||||
} else { | |||||
return TD; | |||||
} | |||||
} | |||||
} | |||||
private FOToPDFRoleMap() { } | |||||
} |
/** Text generation utility holding the current font status */ | /** Text generation utility holding the current font status */ | ||||
protected PDFTextUtil textutil; | protected PDFTextUtil textutil; | ||||
private boolean inMarkedContentSequence; | |||||
private boolean inArtifactMode; | |||||
/** | /** | ||||
* Main constructor. Creates a new PDF stream and additional helper classes for text painting | * Main constructor. Creates a new PDF stream and additional helper classes for text painting | ||||
currentStream.add("q\n"); | currentStream.add("q\n"); | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
protected void saveGraphicsState(String structElemType, int sequenceNum) { | |||||
endTextObject(); | |||||
currentState.save(); | |||||
beginMarkedContentSequence(structElemType, sequenceNum); | |||||
currentStream.add("q\n"); | |||||
} | |||||
/** | |||||
* Begins a new marked content sequence (BDC or BMC). If the parameter structElemType is null, | |||||
* the sequenceNum is ignored and instead of a BDC with the MCID as parameter, an "Artifact" | |||||
* and a BMC command is generated. | |||||
* @param structElemType Structure Element Type | |||||
* @param mcid Sequence number | |||||
*/ | |||||
protected void beginMarkedContentSequence(String structElemType, int mcid) { | |||||
assert !this.inMarkedContentSequence; | |||||
assert !this.inArtifactMode; | |||||
if (structElemType != null) { | |||||
currentStream.add(structElemType + " <</MCID " + String.valueOf(mcid) + ">>\n" | |||||
+ "BDC\n"); | |||||
} else { | |||||
currentStream.add("/Artifact\nBMC\n"); | |||||
this.inArtifactMode = true; | |||||
} | |||||
this.inMarkedContentSequence = true; | |||||
} | |||||
void endMarkedContentSequence() { | |||||
currentStream.add("EMC\n"); | |||||
this.inMarkedContentSequence = false; | |||||
this.inArtifactMode = false; | |||||
} | |||||
/** | /** | ||||
* Restored the graphics state valid before the previous {@link #saveGraphicsState()}. | * Restored the graphics state valid before the previous {@link #saveGraphicsState()}. | ||||
* @param popState true if the state should also be popped, false if only the PDF command | * @param popState true if the state should also be popped, false if only the PDF command | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
/** | |||||
* Same as {@link #restoreGraphicsState(boolean)}, with <code>true</code> as | |||||
* a parameter. | |||||
*/ | |||||
protected void restoreGraphicsState() { | protected void restoreGraphicsState() { | ||||
restoreGraphicsState(true); | restoreGraphicsState(true); | ||||
} | } | ||||
/** | |||||
* Same as {@link #restoreGraphicsState()}, additionally ending the current | |||||
* marked content sequence if any. | |||||
*/ | |||||
protected void restoreGraphicsStateAccess() { | |||||
endTextObject(); | |||||
currentStream.add("Q\n"); | |||||
if (this.inMarkedContentSequence) { | |||||
endMarkedContentSequence(); | |||||
} | |||||
currentState.restore(); | |||||
} | |||||
/** | |||||
* Separates 2 text elements, ending the current marked content sequence and | |||||
* starting a new one. | |||||
* | |||||
* @param structElemType structure element type | |||||
* @param mcid sequence number | |||||
* @see #beginMarkedContentSequence(String, int) | |||||
*/ | |||||
protected void separateTextElements(String structElemType, int mcid) { | |||||
textutil.endTextObject(); | |||||
endMarkedContentSequence(); | |||||
beginMarkedContentSequence(structElemType, mcid); | |||||
textutil.beginTextObject(); | |||||
} | |||||
/** Indicates the beginning of a text object. */ | /** Indicates the beginning of a text object. */ | ||||
protected void beginTextObject() { | protected void beginTextObject() { | ||||
if (!textutil.isInTextObject()) { | if (!textutil.isInTextObject()) { | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Indicates the beginning of a marked-content text object. | |||||
* | |||||
* @param structElemType structure element type | |||||
* @param mcid sequence number | |||||
* @see #beginTextObject() | |||||
* @see #beginMarkedContentSequence(String, int) | |||||
*/ | |||||
protected void beginTextObject(String structElemType, int mcid) { | |||||
if (!textutil.isInTextObject()) { | |||||
beginMarkedContentSequence(structElemType, mcid); | |||||
textutil.beginTextObject(); | |||||
} | |||||
} | |||||
/** Indicates the end of a text object. */ | /** Indicates the end of a text object. */ | ||||
protected void endTextObject() { | protected void endTextObject() { | ||||
if (textutil.isInTextObject()) { | if (textutil.isInTextObject()) { | ||||
if (this.inMarkedContentSequence) { | |||||
endMarkedContentSequence(); | |||||
} | |||||
textutil.endTextObject(); | textutil.endTextObject(); | ||||
} | } | ||||
} | } | ||||
restoreGraphicsState(); | restoreGraphicsState(); | ||||
} | } | ||||
/** | |||||
* Places a previously registered image at a certain place on the page, | |||||
* bracketing it as a marked-content sequence. | |||||
* | |||||
* @param x X coordinate | |||||
* @param y Y coordinate | |||||
* @param w width for image | |||||
* @param h height for image | |||||
* @param xobj the image XObject | |||||
* @param structElemType structure element type | |||||
* @param mcid sequence number | |||||
* @see #beginMarkedContentSequence(String, int) | |||||
*/ | |||||
public void placeImage(float x, float y, float w, float h, PDFXObject xobj, | |||||
String structElemType, int mcid) { | |||||
saveGraphicsState(structElemType, mcid); | |||||
add(format(w) + " 0 0 " | |||||
+ format(-h) + " " | |||||
+ format(x) + " " | |||||
+ format(y + h) | |||||
+ " cm\n" + xobj.getName() + " Do\n"); | |||||
restoreGraphicsStateAccess(); | |||||
} | |||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.w3c.dom.NodeList; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; | import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; | ||||
import org.apache.fop.render.intermediate.IFException; | import org.apache.fop.render.intermediate.IFException; | ||||
import org.apache.fop.render.intermediate.IFPainter; | import org.apache.fop.render.intermediate.IFPainter; | ||||
import org.apache.fop.util.XMLUtil; | |||||
/** | /** | ||||
* {@link IFDocumentHandler} implementation that produces PDF. | * {@link IFDocumentHandler} implementation that produces PDF. | ||||
/** logging instance */ | /** logging instance */ | ||||
private static Log log = LogFactory.getLog(PDFDocumentHandler.class); | private static Log log = LogFactory.getLog(PDFDocumentHandler.class); | ||||
private int pageSequenceIndex; | |||||
private boolean accessEnabled; | |||||
private PDFLogicalStructureHandler logicalStructureHandler; | |||||
/** the PDF Document being created */ | /** the PDF Document being created */ | ||||
protected PDFDocument pdfDoc; | protected PDFDocument pdfDoc; | ||||
/** Used for bookmarks/outlines. */ | /** Used for bookmarks/outlines. */ | ||||
protected Map pageReferences = new java.util.HashMap(); | protected Map pageReferences = new java.util.HashMap(); | ||||
private PDFDocumentNavigationHandler documentNavigationHandler | |||||
private final PDFDocumentNavigationHandler documentNavigationHandler | |||||
= new PDFDocumentNavigationHandler(this); | = new PDFDocumentNavigationHandler(this); | ||||
/** | /** | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public boolean supportsPagesOutOfOrder() { | public boolean supportsPagesOutOfOrder() { | ||||
return true; | |||||
return !accessEnabled; | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
return this.pdfUtil; | return this.pdfUtil; | ||||
} | } | ||||
PDFLogicalStructureHandler getLogicalStructureHandler() { | |||||
return logicalStructureHandler; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void startDocument() throws IFException { | public void startDocument() throws IFException { | ||||
super.startDocument(); | super.startDocument(); | ||||
try { | try { | ||||
this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream); | this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream); | ||||
this.accessEnabled = getUserAgent().isAccessibilityEnabled(); | |||||
if (accessEnabled) { | |||||
pdfDoc.getRoot().makeTagged(); | |||||
logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc); | |||||
} | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new IFException("I/O error in startDocument()", e); | throw new IFException("I/O error in startDocument()", e); | ||||
} | } | ||||
try { | try { | ||||
pdfDoc.getResources().addFonts(pdfDoc, fontInfo); | pdfDoc.getResources().addFonts(pdfDoc, fontInfo); | ||||
pdfDoc.outputTrailer(this.outputStream); | pdfDoc.outputTrailer(this.outputStream); | ||||
this.pdfDoc = null; | this.pdfDoc = null; | ||||
pdfResources = null; | pdfResources = null; | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void startPageSequence(String id) throws IFException { | public void startPageSequence(String id) throws IFException { | ||||
//TODO page sequence title, country and language | |||||
//TODO page sequence title | |||||
if (this.pdfDoc.getRoot().getLanguage() == null | |||||
&& getContext().getLanguage() != null) { | |||||
//No document-level language set, so we use the first page-sequence's language | |||||
this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage())); | |||||
} | |||||
if (accessEnabled) { | |||||
NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++); | |||||
logicalStructureHandler.processStructureTree(nodes, getContext().getLanguage()); | |||||
} | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
toPointAndScale(cropBox, scaleX, scaleY), | toPointAndScale(cropBox, scaleX, scaleY), | ||||
toPointAndScale(bleedBox, scaleX, scaleY), | toPointAndScale(bleedBox, scaleX, scaleY), | ||||
toPointAndScale(trimBox, scaleX, scaleY)); | toPointAndScale(trimBox, scaleX, scaleY)); | ||||
if (accessEnabled) { | |||||
logicalStructureHandler.startPage(currentPage); | |||||
} | |||||
pdfUtil.generatePageLabel(index, name); | pdfUtil.generatePageLabel(index, name); | ||||
currentPageRef = new PageReference(currentPage, size); | currentPageRef = new PageReference(currentPage, size); | ||||
this.pageReferences.put(new Integer(index), currentPageRef); | this.pageReferences.put(new Integer(index), currentPageRef); | ||||
this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, this.currentPage); | |||||
this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, | |||||
this.currentPage); | |||||
// Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's | // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's | ||||
AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0, | AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0, | ||||
(scaleY * size.height) / 1000f); | (scaleY * size.height) / 1000f); | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public IFPainter startPageContent() throws IFException { | public IFPainter startPageContent() throws IFException { | ||||
return new PDFPainter(this); | |||||
return new PDFPainter(this, logicalStructureHandler); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void endPage() throws IFException { | public void endPage() throws IFException { | ||||
if (accessEnabled) { | |||||
logicalStructureHandler.endPage(); | |||||
} | |||||
try { | try { | ||||
this.documentNavigationHandler.commit(); | this.documentNavigationHandler.commit(); | ||||
this.pdfDoc.registerObject(generator.getStream()); | this.pdfDoc.registerObject(generator.getStream()); | ||||
static final class PageReference { | static final class PageReference { | ||||
private PDFReference pageRef; | |||||
private Dimension pageDimension; | |||||
private final PDFReference pageRef; | |||||
private final Dimension pageDimension; | |||||
private PageReference(PDFPage page, Dimension dim) { | private PageReference(PDFPage page, Dimension dim) { | ||||
this.pageRef = page.makeReference(); | this.pageRef = page.makeReference(); |
*/ | */ | ||||
public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler { | public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler { | ||||
private PDFDocumentHandler documentHandler; | |||||
private final PDFDocumentHandler documentHandler; | |||||
private Map incompleteActions = new java.util.HashMap(); | |||||
private Map completeActions = new java.util.HashMap(); | |||||
private final Map incompleteActions = new java.util.HashMap(); | |||||
private final Map completeActions = new java.util.HashMap(); | |||||
/** | /** | ||||
* Default constructor. | * Default constructor. | ||||
PDFLink pdfLink = getPDFDoc().getFactory().makeLink( | PDFLink pdfLink = getPDFDoc().getFactory().makeLink( | ||||
targetRect2D, pdfAction); | targetRect2D, pdfAction); | ||||
if (pdfLink != null) { | if (pdfLink != null) { | ||||
String ptr = link.getAction().getStructurePointer(); | |||||
if (documentHandler.getUserAgent().isAccessibilityEnabled() | |||||
&& ptr != null && ptr.length() > 0) { | |||||
documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, ptr); | |||||
} | |||||
documentHandler.currentPage.addAnnotation(pdfLink); | documentHandler.currentPage.addAnnotation(pdfLink); | ||||
} | } | ||||
} | } |
import org.apache.fop.render.AbstractImageHandlerGraphics2D; | import org.apache.fop.render.AbstractImageHandlerGraphics2D; | ||||
import org.apache.fop.render.RendererContext; | import org.apache.fop.render.RendererContext; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
import org.apache.fop.svg.PDFGraphics2D; | import org.apache.fop.svg.PDFGraphics2D; | ||||
/** | /** | ||||
renderer.currentPage, | renderer.currentPage, | ||||
renderer.getFontInfo()); | renderer.getFontInfo()); | ||||
Rectangle effPos = new Rectangle(origin.x + pos.x, origin.y + pos.y, pos.width, pos.height); | Rectangle effPos = new Rectangle(origin.x + pos.x, origin.y + pos.y, pos.width, pos.height); | ||||
if (context.getUserAgent().isAccessibilityEnabled()) { | |||||
pdfContext.setMarkedContentInfo(renderer.addCurrentImageToStructureTree()); | |||||
} | |||||
handleImage(pdfContext, image, effPos); | handleImage(pdfContext, image, effPos); | ||||
return null; | return null; | ||||
} | } | ||||
float sy = fheight / (float)imh; | float sy = fheight / (float)imh; | ||||
generator.comment("G2D start"); | generator.comment("G2D start"); | ||||
generator.saveGraphicsState(); | |||||
boolean accessibilityEnabled = context.getUserAgent().isAccessibilityEnabled(); | |||||
if (accessibilityEnabled) { | |||||
MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); | |||||
generator.saveGraphicsState(mci.tag, mci.mcid); | |||||
} else { | |||||
generator.saveGraphicsState(); | |||||
} | |||||
generator.updateColor(Color.black, false, null); | generator.updateColor(Color.black, false, null); | ||||
generator.updateColor(Color.black, true, null); | generator.updateColor(Color.black, true, null); | ||||
imageG2D.getGraphics2DImagePainter().paint(graphics, area); | imageG2D.getGraphics2DImagePainter().paint(graphics, area); | ||||
generator.add(graphics.getString()); | generator.add(graphics.getString()); | ||||
generator.restoreGraphicsState(); | |||||
if (accessibilityEnabled) { | |||||
generator.restoreGraphicsStateAccess(); | |||||
} else { | |||||
generator.restoreGraphicsState(); | |||||
} | |||||
generator.comment("G2D end"); | generator.comment("G2D end"); | ||||
} | } | ||||
import org.apache.fop.render.ImageHandler; | import org.apache.fop.render.ImageHandler; | ||||
import org.apache.fop.render.RendererContext; | import org.apache.fop.render.RendererContext; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
/** | /** | ||||
* Image handler implementation which handles raw JPEG images for PDF output. | * Image handler implementation which handles raw JPEG images for PDF output. | ||||
float y = (float)pos.getY() / 1000f; | float y = (float)pos.getY() / 1000f; | ||||
float w = (float)pos.getWidth() / 1000f; | float w = (float)pos.getWidth() / 1000f; | ||||
float h = (float)pos.getHeight() / 1000f; | float h = (float)pos.getHeight() / 1000f; | ||||
generator.placeImage(x, y, w, h, xobj); | |||||
if (context.getUserAgent().isAccessibilityEnabled()) { | |||||
MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); | |||||
generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid); | |||||
} else { | |||||
generator.placeImage(x, y, w, h, xobj); | |||||
} | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ |
import org.apache.fop.render.ImageHandler; | import org.apache.fop.render.ImageHandler; | ||||
import org.apache.fop.render.RendererContext; | import org.apache.fop.render.RendererContext; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
/** | /** | ||||
* Image handler implementation which handles RenderedImage instances for PDF output. | * Image handler implementation which handles RenderedImage instances for PDF output. | ||||
float y = (float)pos.getY() / 1000f; | float y = (float)pos.getY() / 1000f; | ||||
float w = (float)pos.getWidth() / 1000f; | float w = (float)pos.getWidth() / 1000f; | ||||
float h = (float)pos.getHeight() / 1000f; | float h = (float)pos.getHeight() / 1000f; | ||||
generator.placeImage(x, y, w, h, xobj); | |||||
if (context.getUserAgent().isAccessibilityEnabled()) { | |||||
MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); | |||||
generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid); | |||||
} else { | |||||
generator.placeImage(x, y, w, h, xobj); | |||||
} | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ |
import org.apache.fop.image.loader.batik.BatikImageFlavors; | import org.apache.fop.image.loader.batik.BatikImageFlavors; | ||||
import org.apache.fop.render.ImageHandler; | import org.apache.fop.render.ImageHandler; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
import org.apache.fop.svg.PDFAElementBridge; | import org.apache.fop.svg.PDFAElementBridge; | ||||
import org.apache.fop.svg.PDFBridgeContext; | import org.apache.fop.svg.PDFBridgeContext; | ||||
import org.apache.fop.svg.PDFGraphics2D; | import org.apache.fop.svg.PDFGraphics2D; | ||||
float w = (float)ctx.getDocumentSize().getWidth() * 1000f; | float w = (float)ctx.getDocumentSize().getWidth() * 1000f; | ||||
float h = (float)ctx.getDocumentSize().getHeight() * 1000f; | float h = (float)ctx.getDocumentSize().getHeight() * 1000f; | ||||
float sx = pos.width / (float)w; | |||||
float sy = pos.height / (float)h; | |||||
float sx = pos.width / w; | |||||
float sy = pos.height / h; | |||||
//Scaling and translation for the bounding box of the image | //Scaling and translation for the bounding box of the image | ||||
AffineTransform scaling = new AffineTransform( | AffineTransform scaling = new AffineTransform( | ||||
*/ | */ | ||||
generator.comment("SVG setup"); | generator.comment("SVG setup"); | ||||
generator.saveGraphicsState(); | generator.saveGraphicsState(); | ||||
if (context.getUserAgent().isAccessibilityEnabled()) { | |||||
MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); | |||||
generator.beginMarkedContentSequence(mci.tag, mci.mcid); | |||||
} | |||||
generator.setColor(Color.black, false); | generator.setColor(Color.black, false); | ||||
generator.setColor(Color.black, true); | generator.setColor(Color.black, true); | ||||
eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); | eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); | ||||
} | } | ||||
generator.getState().restore(); | generator.getState().restore(); | ||||
generator.restoreGraphicsState(); | |||||
if (context.getUserAgent().isAccessibilityEnabled()) { | |||||
generator.restoreGraphicsStateAccess(); | |||||
} else { | |||||
generator.restoreGraphicsState(); | |||||
} | |||||
generator.comment("SVG end"); | generator.comment("SVG end"); | ||||
} | } | ||||
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.render.pdf; | |||||
import java.util.HashMap; | |||||
import java.util.Locale; | |||||
import java.util.Map; | |||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.NodeList; | |||||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | |||||
import org.apache.fop.fo.extensions.InternalElementMapping; | |||||
import org.apache.fop.pdf.PDFArray; | |||||
import org.apache.fop.pdf.PDFDictionary; | |||||
import org.apache.fop.pdf.PDFDocument; | |||||
import org.apache.fop.pdf.PDFLink; | |||||
import org.apache.fop.pdf.PDFName; | |||||
import org.apache.fop.pdf.PDFPage; | |||||
import org.apache.fop.pdf.PDFParentTree; | |||||
import org.apache.fop.pdf.PDFStructElem; | |||||
import org.apache.fop.pdf.PDFStructTreeRoot; | |||||
/** | |||||
* Handles the creation of the logical structure in the PDF document. | |||||
*/ | |||||
class PDFLogicalStructureHandler { | |||||
private static final PDFName MCR = new PDFName("MCR"); | |||||
private static final PDFName OBJR = new PDFName("OBJR"); | |||||
private static final MarkedContentInfo ARTIFACT = new MarkedContentInfo(null, -1, null); | |||||
private final PDFDocument pdfDoc; | |||||
/** | |||||
* Map of references to the corresponding structure elements. | |||||
*/ | |||||
private final Map structTreeMap = new HashMap(); | |||||
private final PDFParentTree parentTree = new PDFParentTree(); | |||||
private int parentTreeKey; | |||||
private PDFPage currentPage; | |||||
/** | |||||
* The array of references, from marked-content sequences in the current | |||||
* page, to their parent structure elements. This will be a value in the | |||||
* structure parent tree, whose corresponding key will be the page's | |||||
* StructParents entry. | |||||
*/ | |||||
private PDFArray pageParentTreeArray; | |||||
private PDFStructElem rootStructureElement; | |||||
/** | |||||
* Class providing the necessary information for bracketing content | |||||
* associated to a structure element as a marked-content sequence. | |||||
*/ | |||||
static final class MarkedContentInfo { | |||||
/** | |||||
* A value that can be used for the tag operand of a marked-content | |||||
* operator. This is the structure type of the corresponding structure | |||||
* element. | |||||
*/ | |||||
final String tag; | |||||
/** | |||||
* The value for the MCID entry of the marked-content sequence's property list. | |||||
*/ | |||||
final int mcid; | |||||
private final PDFStructElem parent; | |||||
private MarkedContentInfo(String tag, int mcid, PDFStructElem parent) { | |||||
this.tag = tag; | |||||
this.mcid = mcid; | |||||
this.parent = parent; | |||||
} | |||||
} | |||||
/** | |||||
* Creates a new instance for handling the logical structure of the given document. | |||||
* | |||||
* @param pdfDoc a document | |||||
*/ | |||||
PDFLogicalStructureHandler(PDFDocument pdfDoc) { | |||||
this.pdfDoc = pdfDoc; | |||||
PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree); | |||||
rootStructureElement = pdfDoc.getFactory().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot); | |||||
structTreeRoot.addKid(rootStructureElement); | |||||
} | |||||
/** | |||||
* Converts the given structure tree into PDF. | |||||
* | |||||
* @param structureTree the structure tree of the current page sequence | |||||
* @param language language set on the page sequence | |||||
*/ | |||||
void processStructureTree(NodeList structureTree, Locale language) { | |||||
pdfDoc.enforceLanguageOnRoot(); | |||||
PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), | |||||
rootStructureElement); | |||||
rootStructureElement.addKid(structElemPart); | |||||
if (language != null) { | |||||
structElemPart.setLanguage(language); | |||||
} | |||||
for (int i = 0, n = structureTree.getLength(); i < n; i++) { | |||||
Node node = structureTree.item(i); | |||||
assert node.getLocalName().equals("flow") | |||||
|| node.getLocalName().equals("static-content"); | |||||
PDFStructElem structElemSect = pdfDoc.getFactory().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject(node.getLocalName(), structElemPart), | |||||
structElemPart); | |||||
structElemPart.addKid(structElemSect); | |||||
NodeList childNodes = node.getChildNodes(); | |||||
for (int j = 0, m = childNodes.getLength(); j < m; j++) { | |||||
processNode(childNodes.item(j), structElemSect, true); | |||||
} | |||||
} | |||||
} | |||||
private void processNode(Node node, PDFStructElem parent, boolean addKid) { | |||||
Node attr = node.getAttributes().getNamedItemNS(InternalElementMapping.URI, "ptr"); | |||||
assert attr != null; | |||||
String ptr = attr.getNodeValue(); | |||||
String nodeName = node.getLocalName(); | |||||
PDFStructElem structElem = pdfDoc.getFactory().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject(nodeName, parent), parent); | |||||
// TODO necessary? If a page-sequence is empty (e.g., contains a single | |||||
// empty fo:block), should the block still be added to the structure | |||||
// tree? This is not being done for descendant empty elements... | |||||
if (addKid) { | |||||
parent.addKid(structElem); | |||||
} | |||||
if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) { | |||||
Node altTextNode = node.getAttributes().getNamedItemNS( | |||||
ExtensionElementMapping.URI, "alt-text"); | |||||
if (altTextNode != null) { | |||||
structElem.put("Alt", altTextNode.getNodeValue()); | |||||
} else { | |||||
structElem.put("Alt", "No alternate text specified"); | |||||
} | |||||
} | |||||
structTreeMap.put(ptr, structElem); | |||||
NodeList nodes = node.getChildNodes(); | |||||
for (int i = 0, n = nodes.getLength(); i < n; i++) { | |||||
processNode(nodes.item(i), structElem, false); | |||||
} | |||||
} | |||||
private int getNextParentTreeKey() { | |||||
return parentTreeKey++; | |||||
} | |||||
/** | |||||
* Receive notification of the beginning of a new page. | |||||
* | |||||
* @param page the page that will be rendered in PDF | |||||
*/ | |||||
void startPage(PDFPage page) { | |||||
currentPage = page; | |||||
currentPage.setStructParents(getNextParentTreeKey()); | |||||
pageParentTreeArray = new PDFArray(); | |||||
} | |||||
/** | |||||
* Receive notification of the end of the current page. | |||||
*/ | |||||
void endPage() { | |||||
// TODO | |||||
// Values in a number tree must be indirect references to the PDF | |||||
// objects associated to the keys. To enforce that the array is | |||||
// registered to the PDF document. Unfortunately that can't be done | |||||
// earlier since a call to PDFContentGenerator.flushPDFDoc can be made | |||||
// before the array is complete, which would result in only part of it | |||||
// being output to the PDF. | |||||
// This should really be handled by PDFNumsArray | |||||
pdfDoc.registerObject(pageParentTreeArray); | |||||
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); | |||||
} | |||||
} | |||||
/** | |||||
* 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 | |||||
* @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) { | |||||
PDFDictionary contentItem = new PDFDictionary(); | |||||
contentItem.put("Type", MCR); | |||||
contentItem.put("Pg", this.currentPage); | |||||
contentItem.put("MCID", mci.mcid); | |||||
mci.parent.addKid(contentItem); | |||||
} | |||||
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 | |||||
* @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. | |||||
*/ | |||||
MarkedContentInfo addImageContentItem(String structurePointer) { | |||||
MarkedContentInfo mci = addToParentTree(structurePointer); | |||||
if (mci != ARTIFACT) { | |||||
mci.parent.setMCIDKid(mci.mcid); | |||||
mci.parent.setPage(this.currentPage); | |||||
} | |||||
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 | |||||
*/ | |||||
void addLinkContentItem(PDFLink link, String structurePointer) { | |||||
int structParent = getNextParentTreeKey(); | |||||
link.setStructParent(structParent); | |||||
parentTree.getNums().put(structParent, link); | |||||
PDFDictionary contentItem = new PDFDictionary(); | |||||
contentItem.put("Type", OBJR); | |||||
contentItem.put("Pg", this.currentPage); | |||||
contentItem.put("Obj", link); | |||||
PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer); | |||||
parent.addKid(contentItem); | |||||
} | |||||
} |
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.apache.fop.fonts.Font; | import org.apache.fop.fonts.Font; | ||||
import org.apache.fop.fonts.FontInfo; | import org.apache.fop.fonts.FontInfo; | ||||
import org.apache.fop.fonts.FontTriplet; | import org.apache.fop.fonts.FontTriplet; | ||||
import org.apache.fop.render.intermediate.IFContext; | import org.apache.fop.render.intermediate.IFContext; | ||||
import org.apache.fop.render.intermediate.IFException; | import org.apache.fop.render.intermediate.IFException; | ||||
import org.apache.fop.render.intermediate.IFState; | import org.apache.fop.render.intermediate.IFState; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
import org.apache.fop.traits.BorderProps; | import org.apache.fop.traits.BorderProps; | ||||
import org.apache.fop.traits.RuleStyle; | import org.apache.fop.traits.RuleStyle; | ||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
*/ | */ | ||||
public class PDFPainter extends AbstractIFPainter { | public class PDFPainter extends AbstractIFPainter { | ||||
/** logging instance */ | |||||
private static Log log = LogFactory.getLog(PDFPainter.class); | |||||
private PDFDocumentHandler documentHandler; | |||||
private final PDFDocumentHandler documentHandler; | |||||
/** The current content generator */ | /** The current content generator */ | ||||
protected PDFContentGenerator generator; | protected PDFContentGenerator generator; | ||||
private PDFBorderPainter borderPainter; | |||||
private final PDFBorderPainter borderPainter; | |||||
private boolean accessEnabled; | |||||
private MarkedContentInfo imageMCI; | |||||
private PDFLogicalStructureHandler logicalStructureHandler; | |||||
/** | /** | ||||
* Default constructor. | * Default constructor. | ||||
* @param documentHandler the parent document handler | * @param documentHandler the parent document handler | ||||
* @param logicalStructureHandler the logical structure handler | |||||
*/ | */ | ||||
public PDFPainter(PDFDocumentHandler documentHandler) { | |||||
public PDFPainter(PDFDocumentHandler documentHandler, | |||||
PDFLogicalStructureHandler logicalStructureHandler) { | |||||
super(); | super(); | ||||
this.documentHandler = documentHandler; | this.documentHandler = documentHandler; | ||||
this.logicalStructureHandler = logicalStructureHandler; | |||||
this.generator = documentHandler.generator; | this.generator = documentHandler.generator; | ||||
this.borderPainter = new PDFBorderPainter(this.generator); | this.borderPainter = new PDFBorderPainter(this.generator); | ||||
this.state = IFState.create(); | this.state = IFState.create(); | ||||
accessEnabled = this.getUserAgent().isAccessibilityEnabled(); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void drawImage(String uri, Rectangle rect) throws IFException { | |||||
public void drawImage(String uri, Rectangle rect) | |||||
throws IFException { | |||||
PDFXObject xobject = getPDFDoc().getXObject(uri); | PDFXObject xobject = getPDFDoc().getXObject(uri); | ||||
if (xobject != null) { | if (xobject != null) { | ||||
placeImage(rect, xobject); | |||||
return; | |||||
if (accessEnabled) { | |||||
String ptr = getContext().getStructurePointer(); | |||||
prepareImageMCID(ptr); | |||||
placeImageAccess(rect, xobject); | |||||
} else { | |||||
placeImage(rect, xobject); | |||||
} | |||||
} else { | |||||
if (accessEnabled) { | |||||
String ptr = getContext().getStructurePointer(); | |||||
prepareImageMCID(ptr); | |||||
} | |||||
drawImageUsingURI(uri, rect); | |||||
flushPDFDoc(); | |||||
} | } | ||||
} | |||||
drawImageUsingURI(uri, rect); | |||||
flushPDFDoc(); | |||||
private void prepareImageMCID(String ptr) { | |||||
imageMCI = logicalStructureHandler.addImageContentItem(ptr); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected RenderingContext createRenderingContext() { | protected RenderingContext createRenderingContext() { | ||||
PDFRenderingContext pdfContext = new PDFRenderingContext( | PDFRenderingContext pdfContext = new PDFRenderingContext( | ||||
getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo()); | getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo()); | ||||
pdfContext.setMarkedContentInfo(imageMCI); | |||||
return pdfContext; | return pdfContext; | ||||
} | } | ||||
+ " cm " + xobj.getName() + " Do\n"); | + " cm " + xobj.getName() + " Do\n"); | ||||
generator.restoreGraphicsState(); | generator.restoreGraphicsState(); | ||||
} | } | ||||
/** | |||||
* Places a previously registered image at a certain place on the page - Accessibility version | |||||
* @param x X coordinate | |||||
* @param y Y coordinate | |||||
* @param w width for image | |||||
* @param h height for image | |||||
* @param xobj the image XObject | |||||
*/ | |||||
private void placeImageAccess(Rectangle rect, PDFXObject xobj) { | |||||
generator.saveGraphicsState(imageMCI.tag, imageMCI.mcid); | |||||
generator.add(format(rect.width) + " 0 0 " | |||||
+ format(-rect.height) + " " | |||||
+ format(rect.x) + " " | |||||
+ format(rect.y + rect.height ) | |||||
+ " cm " + xobj.getName() + " Do\n"); | |||||
generator.restoreGraphicsStateAccess(); | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void drawImage(Document doc, Rectangle rect) throws IFException { | public void drawImage(Document doc, Rectangle rect) throws IFException { | ||||
if (accessEnabled) { | |||||
String ptr = getContext().getStructurePointer(); | |||||
prepareImageMCID(ptr); | |||||
} | |||||
drawImageUsingDocument(doc, rect); | drawImageUsingDocument(doc, rect); | ||||
flushPDFDoc(); | flushPDFDoc(); | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text) | |||||
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, | |||||
String text) | |||||
throws IFException { | throws IFException { | ||||
generator.updateColor(state.getTextColor(), true, null); | |||||
generator.beginTextObject(); | |||||
if (accessEnabled) { | |||||
String ptr = getContext().getStructurePointer(); | |||||
MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr); | |||||
if (generator.getTextUtil().isInTextObject()) { | |||||
generator.separateTextElements(mci.tag, mci.mcid); | |||||
} | |||||
generator.updateColor(state.getTextColor(), true, null); | |||||
generator.beginTextObject(mci.tag, mci.mcid); | |||||
} else { | |||||
generator.updateColor(state.getTextColor(), true, null); | |||||
generator.beginTextObject(); | |||||
} | |||||
FontTriplet triplet = new FontTriplet( | FontTriplet triplet = new FontTriplet( | ||||
state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); | state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); | ||||
//TODO Ignored: state.getFontVariant() | //TODO Ignored: state.getFontVariant() | ||||
PDFTextUtil textutil = generator.getTextUtil(); | PDFTextUtil textutil = generator.getTextUtil(); | ||||
textutil.updateTf(fontKey, fontSize, tf.isMultiByte()); | textutil.updateTf(fontKey, fontSize, tf.isMultiByte()); | ||||
generator.updateCharacterSpacing((float)letterSpacing / 1000f); | |||||
generator.updateCharacterSpacing(letterSpacing / 1000f); | |||||
textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); | textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); | ||||
int l = text.length(); | int l = text.length(); |
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.NodeList; | |||||
import org.apache.xmlgraphics.image.loader.ImageException; | import org.apache.xmlgraphics.image.loader.ImageException; | ||||
import org.apache.xmlgraphics.image.loader.ImageFlavor; | import org.apache.xmlgraphics.image.loader.ImageFlavor; | ||||
import org.apache.xmlgraphics.image.loader.ImageInfo; | import org.apache.xmlgraphics.image.loader.ImageInfo; | ||||
import org.apache.fop.area.inline.Leader; | import org.apache.fop.area.inline.Leader; | ||||
import org.apache.fop.area.inline.SpaceArea; | import org.apache.fop.area.inline.SpaceArea; | ||||
import org.apache.fop.area.inline.TextArea; | import org.apache.fop.area.inline.TextArea; | ||||
import org.apache.fop.area.inline.Viewport; | |||||
import org.apache.fop.area.inline.WordArea; | import org.apache.fop.area.inline.WordArea; | ||||
import org.apache.fop.datatypes.URISpecification; | import org.apache.fop.datatypes.URISpecification; | ||||
import org.apache.fop.events.ResourceEventProducer; | import org.apache.fop.events.ResourceEventProducer; | ||||
import org.apache.fop.render.AbstractPathOrientedRenderer; | import org.apache.fop.render.AbstractPathOrientedRenderer; | ||||
import org.apache.fop.render.Graphics2DAdapter; | import org.apache.fop.render.Graphics2DAdapter; | ||||
import org.apache.fop.render.RendererContext; | import org.apache.fop.render.RendererContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
import org.apache.fop.traits.RuleStyle; | import org.apache.fop.traits.RuleStyle; | ||||
import org.apache.fop.util.AbstractPaintingState; | import org.apache.fop.util.AbstractPaintingState; | ||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
import org.apache.fop.util.XMLUtil; | |||||
import org.apache.fop.util.AbstractPaintingState.AbstractData; | import org.apache.fop.util.AbstractPaintingState.AbstractData; | ||||
/** | /** | ||||
* this is used for prepared pages that cannot be immediately | * this is used for prepared pages that cannot be immediately | ||||
* rendered | * rendered | ||||
*/ | */ | ||||
protected Map pages = null; | |||||
private Map pages; | |||||
/** | /** | ||||
* Maps unique PageViewport key to PDF page reference | * Maps unique PageViewport key to PDF page reference | ||||
/** Image handler registry */ | /** Image handler registry */ | ||||
private final PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry(); | private final PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry(); | ||||
private boolean accessEnabled; | |||||
private PDFLogicalStructureHandler logicalStructureHandler; | |||||
private int pageSequenceIndex; | |||||
/** Reference in the structure tree to the image being rendered. */ | |||||
private String imageReference; | |||||
/** | /** | ||||
* create the PDF renderer | * create the PDF renderer | ||||
public void setUserAgent(FOUserAgent agent) { | public void setUserAgent(FOUserAgent agent) { | ||||
super.setUserAgent(agent); | super.setUserAgent(agent); | ||||
this.pdfUtil = new PDFRenderingUtil(getUserAgent()); | this.pdfUtil = new PDFRenderingUtil(getUserAgent()); | ||||
accessEnabled = agent.isAccessibilityEnabled(); | |||||
} | } | ||||
PDFRenderingUtil getPDFUtil() { | PDFRenderingUtil getPDFUtil() { | ||||
} | } | ||||
ostream = stream; | ostream = stream; | ||||
this.pdfDoc = pdfUtil.setupPDFDocument(stream); | this.pdfDoc = pdfUtil.setupPDFDocument(stream); | ||||
if (accessEnabled) { | |||||
pdfDoc.getRoot().makeTagged(); | |||||
logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
public boolean supportsOutOfOrder() { | public boolean supportsOutOfOrder() { | ||||
//return false; | |||||
return true; | |||||
return !accessEnabled; | |||||
} | } | ||||
/** | /** | ||||
info.setTitle(str); | info.setTitle(str); | ||||
} | } | ||||
} | } | ||||
Locale language = null; | |||||
if (pageSequence.getLanguage() != null) { | if (pageSequence.getLanguage() != null) { | ||||
String lang = pageSequence.getLanguage(); | String lang = pageSequence.getLanguage(); | ||||
String country = pageSequence.getCountry(); | String country = pageSequence.getCountry(); | ||||
String langCode = lang + (country != null ? "-" + country : ""); | |||||
if (lang != null) { | |||||
language = (country == null) ? new Locale(lang) : new Locale(lang, country); | |||||
} | |||||
if (pdfDoc.getRoot().getLanguage() == null) { | if (pdfDoc.getRoot().getLanguage() == null) { | ||||
//Only set if not set already (first non-null is used) | //Only set if not set already (first non-null is used) | ||||
//Note: No checking is performed whether the values are valid! | //Note: No checking is performed whether the values are valid! | ||||
pdfDoc.getRoot().setLanguage(langCode); | |||||
pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(language)); | |||||
} | } | ||||
} | } | ||||
pdfUtil.generateDefaultXMPMetadata(); | pdfUtil.generateDefaultXMPMetadata(); | ||||
if (accessEnabled) { | |||||
NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++); | |||||
logicalStructureHandler.processStructureTree(nodes, language); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
} | } | ||||
currentPageRef = currentPage.referencePDF(); | currentPageRef = currentPage.referencePDF(); | ||||
if (accessEnabled) { | |||||
logicalStructureHandler.startPage(currentPage); | |||||
} | |||||
Rectangle bounds = page.getViewArea(); | Rectangle bounds = page.getViewArea(); | ||||
pageHeight = bounds.height; | pageHeight = bounds.height; | ||||
super.renderPage(page); | super.renderPage(page); | ||||
if (accessEnabled) { | |||||
logicalStructureHandler.endPage(); | |||||
} | |||||
this.pdfDoc.registerObject(generator.getStream()); | this.pdfDoc.registerObject(generator.getStream()); | ||||
currentPage.setContents(generator.getStream()); | currentPage.setContents(generator.getStream()); | ||||
PDFAnnotList annots = currentPage.getAnnotations(); | PDFAnnotList annots = currentPage.getAnnotations(); | ||||
+ pdfDoc.getProfile()); | + pdfDoc.getProfile()); | ||||
} else if (action != null) { | } else if (action != null) { | ||||
PDFLink pdfLink = factory.makeLink(ipRect, action); | PDFLink pdfLink = factory.makeLink(ipRect, action); | ||||
if (accessEnabled) { | |||||
String ptr = (String) ip.getTrait(Trait.PTR); | |||||
logicalStructureHandler.addLinkContentItem(pdfLink, ptr); | |||||
} | |||||
currentPage.addAnnotation(pdfLink); | currentPage.addAnnotation(pdfLink); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void renderViewport(Viewport viewport) { | |||||
imageReference = (String) viewport.getTrait(Trait.PTR); | |||||
super.renderViewport(viewport); | |||||
imageReference = null; | |||||
} | |||||
private Typeface getTypeface(String fontName) { | private Typeface getTypeface(String fontName) { | ||||
Typeface tf = (Typeface) fontInfo.getFonts().get(fontName); | Typeface tf = (Typeface) fontInfo.getFonts().get(fontName); | ||||
if (tf instanceof LazyFont) { | if (tf instanceof LazyFont) { | ||||
Color ct = (Color) text.getTrait(Trait.COLOR); | Color ct = (Color) text.getTrait(Trait.COLOR); | ||||
updateColor(ct, true); | updateColor(ct, true); | ||||
beginTextObject(); | |||||
if (accessEnabled) { | |||||
String ptr = (String) text.getTrait(Trait.PTR); | |||||
MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr); | |||||
if (generator.getTextUtil().isInTextObject()) { | |||||
generator.separateTextElements(mci.tag, mci.mcid); | |||||
} | |||||
generator.beginTextObject(mci.tag, mci.mcid); | |||||
} else { | |||||
beginTextObject(); | |||||
} | |||||
String fontName = getInternalFontNameForArea(text); | String fontName = getInternalFontNameForArea(text); | ||||
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); | int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); | ||||
* @param xobj the image XObject | * @param xobj the image XObject | ||||
*/ | */ | ||||
public void placeImage(float x, float y, float w, float h, PDFXObject xobj) { | public void placeImage(float x, float y, float w, float h, PDFXObject xobj) { | ||||
saveGraphicsState(); | |||||
if (accessEnabled) { | |||||
MarkedContentInfo mci = logicalStructureHandler.addImageContentItem(imageReference); | |||||
generator.saveGraphicsState(mci.tag, mci.mcid); | |||||
} else { | |||||
saveGraphicsState(); | |||||
} | |||||
generator.add(format(w) + " 0 0 " | generator.add(format(w) + " 0 0 " | ||||
+ format(-h) + " " | + format(-h) + " " | ||||
+ format(currentIPPosition / 1000f + x) + " " | + format(currentIPPosition / 1000f + x) + " " | ||||
+ format(currentBPPosition / 1000f + h + y) | + format(currentBPPosition / 1000f + h + y) | ||||
+ " cm\n" + xobj.getName() + " Do\n"); | + " cm\n" + xobj.getName() + " Do\n"); | ||||
restoreGraphicsState(); | |||||
if (accessEnabled) { | |||||
generator.restoreGraphicsStateAccess(); | |||||
} else { | |||||
restoreGraphicsState(); | |||||
} | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
return context; | return context; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public void renderDocument(Document doc, String ns, Rectangle2D pos, Map foreignAttributes) { | |||||
if (accessEnabled) { | |||||
MarkedContentInfo mci = logicalStructureHandler.addImageContentItem(imageReference); | |||||
generator.beginMarkedContentSequence(mci.tag, mci.mcid); | |||||
} | |||||
super.renderDocument(doc, ns, pos, foreignAttributes); | |||||
if (accessEnabled) { | |||||
generator.endMarkedContentSequence(); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Render leader area. | * Render leader area. | ||||
* This renders a leader area which is an area with a rule. | * This renders a leader area which is an area with a rule. | ||||
public void setEncryptionParams(PDFEncryptionParams encryptionParams) { | public void setEncryptionParams(PDFEncryptionParams encryptionParams) { | ||||
this.pdfUtil.setEncryptionParams(encryptionParams); | this.pdfUtil.setEncryptionParams(encryptionParams); | ||||
} | } | ||||
MarkedContentInfo addCurrentImageToStructureTree() { | |||||
return logicalStructureHandler.addImageContentItem(imageReference); | |||||
} | |||||
} | } | ||||
import org.apache.fop.fonts.FontInfo; | import org.apache.fop.fonts.FontInfo; | ||||
import org.apache.fop.pdf.PDFPage; | import org.apache.fop.pdf.PDFPage; | ||||
import org.apache.fop.render.AbstractRenderingContext; | import org.apache.fop.render.AbstractRenderingContext; | ||||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||||
/** | /** | ||||
* Rendering context for PDF production. | * Rendering context for PDF production. | ||||
private PDFContentGenerator generator; | private PDFContentGenerator generator; | ||||
private FontInfo fontInfo; | private FontInfo fontInfo; | ||||
private PDFPage page; | private PDFPage page; | ||||
private MarkedContentInfo mci; | |||||
/** | /** | ||||
* Main constructor. | * Main constructor. | ||||
return this.fontInfo; | return this.fontInfo; | ||||
} | } | ||||
void setMarkedContentInfo(MarkedContentInfo mci) { | |||||
this.mci = mci; | |||||
} | |||||
MarkedContentInfo getMarkedContentInfo() { | |||||
return mci; | |||||
} | |||||
} | } |
import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; | import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; | ||||
import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; | import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; | ||||
import org.apache.fop.accessibility.Accessibility; | |||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.fo.extensions.xmp.XMPMetadata; | import org.apache.fop.fo.extensions.xmp.XMPMetadata; | ||||
import org.apache.fop.pdf.PDFAMode; | import org.apache.fop.pdf.PDFAMode; | ||||
private void initialize() { | private void initialize() { | ||||
PDFEncryptionParams params | PDFEncryptionParams params | ||||
= (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS); | |||||
= (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS); | |||||
if (params != null) { | if (params != null) { | ||||
this.encryptionParams = params; //overwrite if available | this.encryptionParams = params; //overwrite if available | ||||
} | } | ||||
if (s != null) { | if (s != null) { | ||||
this.pdfAMode = PDFAMode.valueOf(s); | this.pdfAMode = PDFAMode.valueOf(s); | ||||
} | } | ||||
if (this.pdfAMode.isPDFA1LevelA()) { | |||||
//Enable accessibility if PDF/A-1a is enabled because it requires tagged PDF. | |||||
userAgent.getRendererOptions().put(Accessibility.ACCESSIBILITY, Boolean.TRUE); | |||||
} | |||||
s = (String)userAgent.getRendererOptions().get(PDF_X_MODE); | s = (String)userAgent.getRendererOptions().get(PDF_X_MODE); | ||||
if (s != null) { | if (s != null) { | ||||
this.pdfXMode = PDFXMode.valueOf(s); | this.pdfXMode = PDFXMode.valueOf(s); |
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.xmlgraphics.util.UnitConv; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.CTM; | import org.apache.fop.area.CTM; | ||||
import org.apache.fop.render.AbstractPathOrientedRenderer; | import org.apache.fop.render.AbstractPathOrientedRenderer; | ||||
import org.apache.fop.render.txt.border.AbstractBorderElement; | import org.apache.fop.render.txt.border.AbstractBorderElement; | ||||
import org.apache.fop.render.txt.border.BorderManager; | import org.apache.fop.render.txt.border.BorderManager; | ||||
import org.apache.xmlgraphics.util.UnitConv; | |||||
/** | /** | ||||
* Renderer that renders areas to plain text. | * Renderer that renders areas to plain text. |
import javax.xml.transform.stream.StreamResult; | import javax.xml.transform.stream.StreamResult; | ||||
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.w3c.dom.Node; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.apache.xmlgraphics.util.QName; | import org.apache.xmlgraphics.util.QName; | ||||
import org.apache.fop.render.RendererContext; | import org.apache.fop.render.RendererContext; | ||||
import org.apache.fop.render.XMLHandler; | import org.apache.fop.render.XMLHandler; | ||||
import org.apache.fop.util.ColorUtil; | import org.apache.fop.util.ColorUtil; | ||||
import org.apache.fop.util.DOM2SAX; | |||||
/** | /** | ||||
* Renderer that renders areas to XML for debugging purposes. | * Renderer that renders areas to XML for debugging purposes. | ||||
/** If not null, the XMLRenderer will mimic another renderer by using its font setup. */ | /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */ | ||||
protected Renderer mimic; | protected Renderer mimic; | ||||
private int pageSequenceIndex; | |||||
/** | /** | ||||
* Creates a new XML renderer. | * Creates a new XML renderer. | ||||
*/ | */ | ||||
} | } | ||||
transferForeignObjects(pageSequence); | transferForeignObjects(pageSequence); | ||||
startElement("pageSequence", atts); | startElement("pageSequence", atts); | ||||
if (this.getUserAgent().isAccessibilityEnabled()) { | |||||
String structureTreeElement = "structureTree"; | |||||
startElement(structureTreeElement); | |||||
NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++); | |||||
for (int i = 0, n = nodes.getLength(); i < n; i++) { | |||||
Node node = nodes.item(i); | |||||
try { | |||||
new DOM2SAX(handler).writeFragment(node); | |||||
} catch (SAXException e) { | |||||
handleSAXException(e); | |||||
} | |||||
} | |||||
endElement(structureTreeElement); | |||||
} | |||||
handleExtensionAttachments(pageSequence.getExtensionAttachments()); | handleExtensionAttachments(pageSequence.getExtensionAttachments()); | ||||
LineArea seqTitle = pageSequence.getTitle(); | LineArea seqTitle = pageSequence.getTitle(); | ||||
if (seqTitle != null) { | if (seqTitle != null) { |
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.w3c.dom.NamedNodeMap; | import org.w3c.dom.NamedNodeMap; | ||||
import org.w3c.dom.Node; | import org.w3c.dom.Node; | ||||
import org.xml.sax.ContentHandler; | import org.xml.sax.ContentHandler; | ||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.xml.sax.ext.LexicalHandler; | import org.xml.sax.ext.LexicalHandler; | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Writes the given fragment using the given ContentHandler. | |||||
* @param node DOM node | |||||
* @throws SAXException In case of a problem while writing XML | |||||
*/ | |||||
public void writeFragment(Node node) throws SAXException { | |||||
writeNode(node); | |||||
} | |||||
/** | /** | ||||
* Begin the scope of namespace prefix. Forward the event to the SAX handler | * Begin the scope of namespace prefix. Forward the event to the SAX handler | ||||
* only if the prefix is unknown or it is mapped to a different URI. | * only if the prefix is unknown or it is mapped to a different URI. |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.util; | |||||
import javax.xml.transform.sax.TransformerHandler; | |||||
import org.xml.sax.Attributes; | |||||
import org.xml.sax.Locator; | |||||
import org.xml.sax.SAXException; | |||||
import org.xml.sax.ext.DefaultHandler2; | |||||
import org.xml.sax.helpers.AttributesImpl; | |||||
/** | |||||
* A DefaultHandler implementation that delegates all the method calls to a | |||||
* {@link TransformerHandler} instance. | |||||
*/ | |||||
public class TransformerDefaultHandler extends DefaultHandler2 { | |||||
private TransformerHandler transformerHandler; | |||||
/** | |||||
* Creates a new instance delegating to the given TransformerHandler object. | |||||
* | |||||
* @param transformerHandler the object to which all the method calls will | |||||
* be delegated | |||||
*/ | |||||
public TransformerDefaultHandler(TransformerHandler transformerHandler) { | |||||
this.transformerHandler = transformerHandler; | |||||
} | |||||
/** | |||||
* Returns the delegate TransformerHandler instance. | |||||
* | |||||
* @return the object to which all method calls are delegated | |||||
*/ | |||||
public TransformerHandler getTransformerHandler() { | |||||
return transformerHandler; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void setDocumentLocator(Locator locator) { | |||||
transformerHandler.setDocumentLocator(locator); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startDocument() throws SAXException { | |||||
transformerHandler.startDocument(); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endDocument() throws SAXException { | |||||
transformerHandler.endDocument(); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startPrefixMapping(String prefix, String uri) throws SAXException { | |||||
transformerHandler.startPrefixMapping(prefix, uri); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endPrefixMapping(String string) throws SAXException { | |||||
transformerHandler.endPrefixMapping(string); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startElement(String uri, String localName, String qName, Attributes attrs) | |||||
throws SAXException { | |||||
AttributesImpl ai = new AttributesImpl(attrs); | |||||
transformerHandler.startElement(uri, localName, qName, ai); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endElement(String uri, String localName, String qName) throws SAXException { | |||||
transformerHandler.endElement(uri, localName, qName); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void characters(char[] ch, int start, int length) throws SAXException { | |||||
transformerHandler.characters(ch, start, length); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { | |||||
transformerHandler.ignorableWhitespace(ch, start, length); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void processingInstruction(String target, String data) throws SAXException { | |||||
transformerHandler.processingInstruction(target, data); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void skippedEntity(String name) throws SAXException { | |||||
transformerHandler.skippedEntity(name); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void notationDecl(String name, String publicId, String systemId) throws SAXException { | |||||
transformerHandler.notationDecl(name, publicId, systemId); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void unparsedEntityDecl(String name, String publicId, String systemId, | |||||
String notationName) throws SAXException { | |||||
transformerHandler.unparsedEntityDecl(name, publicId, systemId, notationName); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startDTD(String name, String pid, String lid) throws SAXException { | |||||
transformerHandler.startDTD(name, pid, lid); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endDTD() throws SAXException { | |||||
transformerHandler.endDTD(); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startEntity(String name) throws SAXException { | |||||
transformerHandler.startEntity(name); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endEntity(String name) throws SAXException { | |||||
transformerHandler.endEntity(name); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void startCDATA() throws SAXException { | |||||
transformerHandler.startCDATA(); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void endCDATA() throws SAXException { | |||||
transformerHandler.endCDATA(); | |||||
} | |||||
/** {@inheritDoc} */ | |||||
public void comment(char[] charArray, int start, int length) throws SAXException { | |||||
transformerHandler.comment(charArray, start, length); | |||||
} | |||||
} |
import java.awt.Rectangle; | import java.awt.Rectangle; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.util.Locale; | |||||
import org.xml.sax.Attributes; | import org.xml.sax.Attributes; | ||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
atts.addAttribute("", localName, localName, XMLUtil.CDATA, value); | atts.addAttribute("", localName, localName, XMLUtil.CDATA, value); | ||||
} | } | ||||
/** | |||||
* Converts a {@link Locale} instance to an RFC 3066 compliant language identifier. | |||||
* @param language the language | |||||
* @return the formatted language identifier | |||||
*/ | |||||
public static String toRFC3066(Locale language) { | |||||
if (language == null || language.getLanguage().length() == 0) { | |||||
return null; | |||||
} | |||||
StringBuffer sb = new StringBuffer(); | |||||
sb.append(language.getLanguage()); | |||||
if (language.getCountry().length() > 0) { | |||||
sb.append('-'); | |||||
sb.append(language.getCountry()); | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* Converts an RFC 3066 compliant language identifier to a {@link Locale} instance. | |||||
* @param lang the language string | |||||
* @return the converted locale instance | |||||
*/ | |||||
public static Locale convertRFC3066ToLocale(String lang) { | |||||
if (lang == null || lang.length() == 0) { | |||||
return null; | |||||
} | |||||
String[] parts = lang.split("-"); | |||||
if (parts.length == 1) { | |||||
return new Locale(parts[0]); | |||||
} else { | |||||
return new Locale(parts[0], parts[1]); | |||||
} | |||||
} | |||||
} | } |
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text) | |||||
throws IFException { | |||||
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, | |||||
String text) throws IFException { | |||||
try { | try { | ||||
establish(MODE_TEXT); | establish(MODE_TEXT); | ||||
AttributesImpl atts = new AttributesImpl(); | AttributesImpl atts = new AttributesImpl(); |
documents. Example: the fix of marks layering will be such a case when it's done. | documents. Example: the fix of marks layering will be such a case when it's done. | ||||
--> | --> | ||||
<release version="FOP Trunk" date="TBD"> | <release version="FOP Trunk" date="TBD"> | ||||
<action context="Renderers" dev="JM,VH" type="add" fixes-bug="46705" due-to="Jost Klopfstein"> | |||||
Added basic accessibility and Tagged PDF support. | |||||
</action> | |||||
<action context="Code" dev="JM" type="add"> | <action context="Code" dev="JM" type="add"> | ||||
Added support for encoding CMYK bitmap images (IOCA FS45) and TIFF images as embedded objects. | Added support for encoding CMYK bitmap images (IOCA FS45) and TIFF images as embedded objects. | ||||
</action> | </action> |
his directory contains sample FO files for testing the accessibility features of | |||||
FOP. | |||||
To every FO file in this directory correspond two PDF files in the pdf/ | |||||
sub-directory: one generated by the painter, one by the renderer. For example, | |||||
the text_1.fo file has been rendered into pdf/text_1_painter_orig.pdf and | |||||
pdf/text_1_renderer_orig.pdf. The configuration file config-painter.xconf (resp. | |||||
config-renderer.xconf) was used. | |||||
The PDF files have been checked with Adobe Acrobat Professional 9, using both | |||||
the full accessibility checker and the read-aloud feature. The checker reports | |||||
no error /and/ the entire document can be read aloud. | |||||
!! DO NOT MODIFY THOSE FILES, NEITHER THE FO NOR THE PDF !! | |||||
... Or at least, know what you are doing | |||||
If the FO files are modified, the resulting PDFs must be checked again, both | |||||
with the checker and the read-aloud feature. (Sometimes the checker reports no | |||||
problem yet part or all of the document cannot be read aloud.) | |||||
The purpose of this infrastructure is to be able to quickly re-test the | |||||
accessibility processing chain when any change has been made to it. The | |||||
configuration files disable the compression of the PDF streams, so it is | |||||
possible to compare a re-generated PDF with the original one by using a simple | |||||
diff tool. The files will not be identical because of the different creation | |||||
dates (and the ID key in the trailer), but apart from that there should be no | |||||
difference. | |||||
The rationale is that using a diff tool is much quicker and less tedious than | |||||
running Acrobat's accessibility checker and read-aloud feature every time. | |||||
To re-generate the PDF files using the painter: | |||||
../../fop -c config-painter.xconf text_1.fo pdf/text_1_painter.pdf | |||||
diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf | |||||
Or, going through the intermediate format: | |||||
../../fop -c config-painter.xconf text_1.fo -if application/pdf text_1_if.xml | |||||
../../fop -c config-painter.xconf -ifin text_1_if.xml pdf/text_1_painter.pdf | |||||
diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf | |||||
To re-generate the PDF files using the legacy renderer: | |||||
../../fop -c config-renderer.xconf text_1.fo pdf/text_1_renderer.pdf | |||||
diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf | |||||
Or, going through the intermediate format: | |||||
../../fop -c config-renderer.xconf text_1.fo -at application/pdf text_1_at.xml | |||||
../../fop -c config-renderer.xconf -atin text_1_at.xml pdf/text_1_renderer.pdf | |||||
diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf | |||||
$Id$ |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/bgimg72dpi.jpg"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/bgimg72dpi.jpg" | |||||
background-repeat="no-repeat" background-position-horizontal="50%" | |||||
background-position-vertical="50%"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/bgimg72dpi.png"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/fop-logo-color-24bit.png" | |||||
background-repeat="no-repeat" background-position-horizontal="50%" | |||||
background-position-vertical="50%"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/rgb-circles.svg"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<!-- $Id$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body background-image="../resources/images/rgb-circles.svg" | |||||
background-repeat="no-repeat" background-position-horizontal="50%" | |||||
background-position-vertical="50%"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body column-count="2" margin-top="15pt"/> | |||||
<fo:region-before extent="12pt"/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:static-content flow-name="xsl-region-before"> | |||||
<fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page | |||||
<fo:page-number/></fo:block> | |||||
</fo:static-content> | |||||
<fo:static-content flow-name="xsl-footnote-separator"> | |||||
<fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> | |||||
</fo:static-content> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>(There’s another page sequence <fo:wrapper color="blue"><fo:basic-link | |||||
internal-destination="second">below</fo:basic-link></fo:wrapper>.)</fo:block> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter<fo:footnote><fo:inline | |||||
baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the | |||||
<fo:wrapper color="blue"><fo:basic-link | |||||
external-destination="http://xmlgraphics.apache.org/fop/">FOP | |||||
website</fo:basic-link></fo:wrapper> for more | |||||
information</fo:block></fo:footnote-body></fo:footnote>. FOP has a nice logo: | |||||
<fo:external-graphic src="../resources/images/fop-logo-color-24bit.png" | |||||
inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | |||||
fox:alt-text="FOP Logo"/></fo:block> | |||||
<fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> | |||||
<fo:table-header> | |||||
<fo:table-row> | |||||
<fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Header 1.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Header 1.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
</fo:table-header> | |||||
<fo:table-body> | |||||
<fo:table-row> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 1.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 1.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
<fo:table-row> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 2.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 2.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
</fo:table-body> | |||||
</fo:table> | |||||
<fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||||
formatting objects (XSL-FO) and an output independent formatter. It is a Java application | |||||
that reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
<fo:block span="all" border-top="1pt solid black" border-bottom="1pt solid black" | |||||
padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the | |||||
document. This is intended to test the abilities of the text-to-speech program.</fo:block> | |||||
<fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered | |||||
list:</fo:block> | |||||
<fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" | |||||
keep-with-previous="auto"> | |||||
<fo:list-item keep-with-previous="always"> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block>1.</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block> | |||||
<fo:block>Line 1 of item 1</fo:block> | |||||
<fo:block>Line 2 of item 1</fo:block> | |||||
<fo:block>Line 3 of item 1</fo:block> | |||||
</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
<fo:list-item keep-with-previous="always"> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block>2.</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block> | |||||
<fo:block>Line 1 of item 2</fo:block> | |||||
<fo:block>Line 2 of item 2</fo:block> | |||||
<fo:block>Line 3 of item 2</fo:block> | |||||
</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
</fo:list-block> | |||||
<fo:block>And now we are going to see how a second page sequence is handled.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:static-content flow-name="xsl-region-before"> | |||||
<fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page | |||||
<fo:page-number/></fo:block> | |||||
</fo:static-content> | |||||
<fo:static-content flow-name="xsl-footnote-separator"> | |||||
<fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> | |||||
</fo:static-content> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block id="second">Apache FOP (Formatting Objects Processor) is a print formatter driven by | |||||
XSL formatting objects (XSL-FO) and an output independent formatter<fo:footnote><fo:inline | |||||
baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the | |||||
<fo:wrapper color="blue"><fo:basic-link | |||||
external-destination="http://xmlgraphics.apache.org/fop/">FOP | |||||
website</fo:basic-link></fo:wrapper> for more | |||||
information</fo:block></fo:footnote-body></fo:footnote>. It is a Java application that | |||||
reads a formatting object (FO) tree and renders the resulting pages to a specified | |||||
output.</fo:block> | |||||
<fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> | |||||
<fo:table-header> | |||||
<fo:table-row> | |||||
<fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Header 1.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Header 1.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
</fo:table-header> | |||||
<fo:table-body> | |||||
<fo:table-row> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 1.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 1.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
<fo:table-row> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 2.1</fo:block> | |||||
</fo:table-cell> | |||||
<fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> | |||||
<fo:block>Cell 2.2</fo:block> | |||||
</fo:table-cell> | |||||
</fo:table-row> | |||||
</fo:table-body> | |||||
</fo:table> | |||||
<fo:block language="fr" country="FR">Apache FOP (Formatting Objects Processor) est une | |||||
application de mise en page de documents respectant le standard XSL-FO. À partir d’un | |||||
document au format XSL-FO, cette application écrite en Java effectue une mise en page et | |||||
renvoie un document prêt pour impression.</fo:block> | |||||
<fo:block span="all" border-top="1pt solid black" border-bottom="1pt solid black" | |||||
padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the | |||||
document. This is intended to test the abilities of the text-to-speech program.</fo:block> | |||||
<fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered | |||||
list:</fo:block> | |||||
<fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" | |||||
keep-with-previous="auto"> | |||||
<fo:list-item keep-with-previous="always"> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block>1.</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block> | |||||
<fo:block>Line 1 of item 1</fo:block> | |||||
<fo:block>Line 2 of item 1</fo:block> | |||||
<fo:block>Line 3 of item 1</fo:block> | |||||
</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
<fo:list-item keep-with-previous="always"> | |||||
<fo:list-item-label end-indent="label-end()"> | |||||
<fo:block>2.</fo:block> | |||||
</fo:list-item-label> | |||||
<fo:list-item-body start-indent="body-start()"> | |||||
<fo:block> | |||||
<fo:block>Line 1 of item 2</fo:block> | |||||
<fo:block>Line 2 of item 2</fo:block> | |||||
<fo:block>Line 3 of item 2</fo:block> | |||||
</fo:block> | |||||
</fo:list-item-body> | |||||
</fo:list-item> | |||||
</fo:list-block> | |||||
<fo:block>The end of the document has now been reached.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<fop version="1.0"> | |||||
<accessibility>true</accessibility> | |||||
<source-resolution>144</source-resolution> | |||||
<use-cache>false</use-cache> | |||||
<font-base>../resources/fonts/</font-base> | |||||
<renderers> | |||||
<renderer mime="application/pdf"> | |||||
<filterList> | |||||
<value>null</value> | |||||
</filterList> | |||||
<filterList type="image"> | |||||
<value>flate</value> | |||||
<value>ascii-85</value> | |||||
</filterList> | |||||
<fonts> | |||||
<font embed-url="DejaVuLGCSerif.ttf"> | |||||
<font-triplet name="DejaVu" style="normal" weight="normal"/> | |||||
</font> | |||||
</fonts> | |||||
</renderer> | |||||
</renderers> | |||||
</fop> |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<fop version="1.0"> | |||||
<prefer-renderer>true</prefer-renderer> | |||||
<accessibility>true</accessibility> | |||||
<source-resolution>144</source-resolution> | |||||
<use-cache>false</use-cache> | |||||
<font-base>../resources/fonts/</font-base> | |||||
<renderers> | |||||
<renderer mime="application/pdf"> | |||||
<filterList> | |||||
<value>null</value> | |||||
</filterList> | |||||
<filterList type="image"> | |||||
<value>flate</value> | |||||
<value>ascii-85</value> | |||||
</filterList> | |||||
<fonts> | |||||
<font embed-url="DejaVuLGCSerif.ttf"> | |||||
<font-triplet name="DejaVu" style="normal" weight="normal"/> | |||||
</font> | |||||
</fonts> | |||||
</renderer> | |||||
</renderers> | |||||
</fop> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>This document contains an image in the JPEG format: <fo:external-graphic | |||||
src="../resources/images/cmyk.jpg" | |||||
inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | |||||
fox:alt-text="CMYK colours"/>. Here is the end of the text.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>This document contains an image in the PNG format: <fo:external-graphic | |||||
src="../resources/images/fop-logo-color-24bit.png" | |||||
inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | |||||
fox:alt-text="FOP Logo"/>. Here is the end of the text.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="220pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>This document contains an image in the SVG format: <fo:external-graphic | |||||
src="../resources/images/circles.svg" | |||||
inline-progression-dimension.maximum="75pt" content-width="scale-to-fit" | |||||
fox:alt-text="Nice circles"/>. And here is the same image as an instream-foreign-object: | |||||
<fo:instream-foreign-object inline-progression-dimension.maximum="75pt" | |||||
content-width="scale-down-to-fit" fox:alt-text="The same nice circles"> | |||||
<svg xmlns="http://www.w3.org/2000/svg" width="12cm" height="12cm"> | |||||
<g style="fill-opacity:0.7; stroke:black; stroke-width:0.1cm;"> | |||||
<circle cx="6cm" cy="2cm" r="100" style="fill:red;" transform="translate(0,50)" /> | |||||
<circle cx="6cm" cy="2cm" r="100" style="fill:blue;" transform="translate(70,150)" /> | |||||
<circle cx="6cm" cy="2cm" r="100" style="fill:green;" transform="translate(-70,150)"/> | |||||
</g> | |||||
</svg> | |||||
</fo:instream-foreign-object>.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |
<?xml version="1.0" standalone="no"?> | |||||
<!-- | |||||
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$ --> | |||||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||||
<fo:layout-master-set> | |||||
<fo:simple-page-master master-name="page" | |||||
page-height="320pt" page-width="320pt" margin="10pt"> | |||||
<fo:region-body/> | |||||
</fo:simple-page-master> | |||||
</fo:layout-master-set> | |||||
<fo:page-sequence master-reference="page" language="en" country="GB"> | |||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | |||||
<fo:block>This document contains an image in the WMF format: <fo:external-graphic | |||||
src="../resources/images/testChart.wmf" | |||||
inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | |||||
fox:alt-text="Metafile Companion Test Chart"/> Here is the end of the text.</fo:block> | |||||
</fo:flow> | |||||
</fo:page-sequence> | |||||
</fo:root> |