https://svn.eu.apache.org/repos/asf/xmlgraphics/fop/trunk ........ r687576 | maxberger | 2008-08-21 07:25:40 +0100 (Thu, 21 Aug 2008) | 1 line Included patched retroweaver which does not modify Boolean.valueOf ........ r687657 | jeremias | 2008-08-21 09:51:50 +0100 (Thu, 21 Aug 2008) | 1 line Added an FAQ entry about the element mismatch error message. ........ r687786 | jeremias | 2008-08-21 16:49:13 +0100 (Thu, 21 Aug 2008) | 1 line Added some performance statistics to MemoryEater. ........ r688078 | jeremias | 2008-08-22 14:02:37 +0100 (Fri, 22 Aug 2008) | 1 line Final statistics output after the test with average speed indication. ........ r688087 | jeremias | 2008-08-22 15:16:58 +0100 (Fri, 22 Aug 2008) | 2 lines Merge from branch https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-0_95: Fixed text stroking in SVG when the stroke-width is zero. ........ r688139 | maxberger | 2008-08-22 19:08:36 +0100 (Fri, 22 Aug 2008) | 1 line minor spelling mistakes ........ r688508 | maxberger | 2008-08-24 14:12:02 +0100 (Sun, 24 Aug 2008) | 2 lines Moved DataURIResolver from FOP to commons; use new URIResolver registry ........ r688633 | jeremias | 2008-08-25 07:42:44 +0100 (Mon, 25 Aug 2008) | 2 lines Fixed memory leak in property cache (not cleaning stale PropertyCache$CacheEntry instances). Special thanks to Andreas Delmelle for his help! ........ r688652 | maxberger | 2008-08-25 09:19:13 +0100 (Mon, 25 Aug 2008) | 1 line Re-added moved class as deprecated ........ r688653 | jeremias | 2008-08-25 09:23:31 +0100 (Mon, 25 Aug 2008) | 1 line Added a page on metadata (partly ported from the FOP Wiki). ........ r688660 | jeremias | 2008-08-25 09:41:29 +0100 (Mon, 25 Aug 2008) | 2 lines Fixed example. Thanks for noticing, Pascal Sancho! ........ r688664 | jeremias | 2008-08-25 09:59:27 +0100 (Mon, 25 Aug 2008) | 1 line Minor documentation improvements, most notably a comment about keep-together="always". ........ r688666 | maxberger | 2008-08-25 10:00:27 +0100 (Mon, 25 Aug 2008) | 1 line CommonURIResolver is no longer a singleton ........ r688674 | jeremias | 2008-08-25 10:15:22 +0100 (Mon, 25 Aug 2008) | 1 line Hmm, I missed the redirects for the 0.95 release. ........ r688698 | jeremias | 2008-08-25 12:24:15 +0100 (Mon, 25 Aug 2008) | 2 lines Merge from 0.95 branch: Fixed jar-sources target. ........ r688994 | acumiskey | 2008-08-26 09:34:45 +0100 (Tue, 26 Aug 2008) | 1 line Deprecate UnitConv now that it resides only in xmlgraphics commons. ........ r689000 | acumiskey | 2008-08-26 09:59:31 +0100 (Tue, 26 Aug 2008) | 1 line Forgot to add deprecated public statics. ........ git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@689005 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
<pathelement location="${java14.jce.lib}"/> | <pathelement location="${java14.jce.lib}"/> | ||||
<pathelement location="${ant.home}/lib/ant.jar"/> | <pathelement location="${ant.home}/lib/ant.jar"/> | ||||
<path refid="libs-build-classpath"/> | <path refid="libs-build-classpath"/> | ||||
<path refid="libs-build-tools-classpath"/> | |||||
</path> | </path> | ||||
<!-- If we decide to use retroweaver for the actual weaving, the mkdir and | <!-- If we decide to use retroweaver for the actual weaving, the mkdir and | ||||
destdir= will have to be removed. Also, the weaving task would additionally | destdir= will have to be removed. Also, the weaving task would additionally | ||||
<fileset dir="${src.java.dir}"> | <fileset dir="${src.java.dir}"> | ||||
<patternset refid="java-only"/> | <patternset refid="java-only"/> | ||||
</fileset> | </fileset> | ||||
<fileset dir="${src.java.version.dir}"> | |||||
<patternset refid="java-only"/> | |||||
</fileset> | |||||
<fileset dir="${basedir}"> | <fileset dir="${basedir}"> | ||||
<include name="LICENSE"/> | <include name="LICENSE"/> | ||||
<include name="NOTICE"/> | <include name="NOTICE"/> |
# redirect moved files | # redirect moved files | ||||
RedirectMatch Permanent ^/fop/anttask(.*) http://xmlgraphics.apache.org/fop/0.94/anttask$1 | |||||
RedirectMatch Permanent ^/fop/compiling(.*) http://xmlgraphics.apache.org/fop/0.94/compiling$1 | |||||
RedirectMatch Permanent ^/fop/configuration(.*) http://xmlgraphics.apache.org/fop/0.94/configuration$1 | |||||
RedirectMatch Permanent ^/fop/embedding(.*) http://xmlgraphics.apache.org/fop/0.94/embedding$1 | |||||
RedirectMatch Permanent ^/fop/extensions(.*) http://xmlgraphics.apache.org/fop/0.94/extensions$1 | |||||
RedirectMatch Permanent ^/fop/fonts(.*) http://xmlgraphics.apache.org/fop/0.94/fonts$1 | |||||
RedirectMatch Permanent ^/fop/graphics(.*) http://xmlgraphics.apache.org/fop/0.94/graphics$1 | |||||
RedirectMatch Permanent ^/fop/hyphenation(.*) http://xmlgraphics.apache.org/fop/0.94/hyphenation$1 | |||||
RedirectMatch Permanent ^/fop/intermediate(.*) http://xmlgraphics.apache.org/fop/0.94/intermediate$1 | |||||
RedirectMatch Permanent ^/fop/output(.*) http://xmlgraphics.apache.org/fop/0.94/output$1 | |||||
RedirectMatch Permanent ^/fop/pdfa(.*) http://xmlgraphics.apache.org/fop/0.94/pdfa$1 | |||||
RedirectMatch Permanent ^/fop/pdfencryption(.*) http://xmlgraphics.apache.org/fop/0.94/pdfencryption$1 | |||||
RedirectMatch Permanent ^/fop/pdfx(.*) http://xmlgraphics.apache.org/fop/0.94/pdfx$1 | |||||
RedirectMatch Permanent ^/fop/running(.*) http://xmlgraphics.apache.org/fop/0.94/running$1 | |||||
RedirectMatch Permanent ^/fop/servlets(.*) http://xmlgraphics.apache.org/fop/0.94/servlets$1 | |||||
RedirectMatch Permanent ^/fop/upgrading(.*) http://xmlgraphics.apache.org/fop/0.94/upgrading$1 | |||||
RedirectMatch Permanent ^/fop/anttask(.*) http://xmlgraphics.apache.org/fop/0.95/anttask$1 | |||||
RedirectMatch Permanent ^/fop/compiling(.*) http://xmlgraphics.apache.org/fop/0.95/compiling$1 | |||||
RedirectMatch Permanent ^/fop/configuration(.*) http://xmlgraphics.apache.org/fop/0.95/configuration$1 | |||||
RedirectMatch Permanent ^/fop/embedding(.*) http://xmlgraphics.apache.org/fop/0.95/embedding$1 | |||||
RedirectMatch Permanent ^/fop/extensions(.*) http://xmlgraphics.apache.org/fop/0.95/extensions$1 | |||||
RedirectMatch Permanent ^/fop/fonts(.*) http://xmlgraphics.apache.org/fop/0.95/fonts$1 | |||||
RedirectMatch Permanent ^/fop/graphics(.*) http://xmlgraphics.apache.org/fop/0.95/graphics$1 | |||||
RedirectMatch Permanent ^/fop/hyphenation(.*) http://xmlgraphics.apache.org/fop/0.95/hyphenation$1 | |||||
RedirectMatch Permanent ^/fop/intermediate(.*) http://xmlgraphics.apache.org/fop/0.95/intermediate$1 | |||||
RedirectMatch Permanent ^/fop/output(.*) http://xmlgraphics.apache.org/fop/0.95/output$1 | |||||
RedirectMatch Permanent ^/fop/pdfa(.*) http://xmlgraphics.apache.org/fop/0.95/pdfa$1 | |||||
RedirectMatch Permanent ^/fop/pdfencryption(.*) http://xmlgraphics.apache.org/fop/0.95/pdfencryption$1 | |||||
RedirectMatch Permanent ^/fop/pdfx(.*) http://xmlgraphics.apache.org/fop/0.95/pdfx$1 | |||||
RedirectMatch Permanent ^/fop/running(.*) http://xmlgraphics.apache.org/fop/0.95/running$1 | |||||
RedirectMatch Permanent ^/fop/servlets(.*) http://xmlgraphics.apache.org/fop/0.95/servlets$1 | |||||
RedirectMatch Permanent ^/fop/upgrading(.*) http://xmlgraphics.apache.org/fop/0.95/upgrading$1 | |||||
# redirect to versioned documentation | # redirect to versioned documentation | ||||
Redirect Temp /fop/stable http://xmlgraphics.apache.org/fop/0.94 | |||||
# Current stable release | |||||
Redirect Temp /fop/stable http://xmlgraphics.apache.org/fop/0.95 | |||||
# Current unstable release (or trunk if no beta is the latest release) | |||||
Redirect Temp /fop/unstable http://xmlgraphics.apache.org/fop/trunk | |||||
# Latest release | |||||
Redirect Temp /fop/current http://xmlgraphics.apache.org/fop/0.95 | Redirect Temp /fop/current http://xmlgraphics.apache.org/fop/0.95 | ||||
Redirect Temp /fop/unstable http://xmlgraphics.apache.org/fop/0.95 | |||||
Redirect Temp /fop/latest http://xmlgraphics.apache.org/fop/trunk | |||||
Redirect Temp /fop/maintenance http://xmlgraphics.apache.org/fop/0.93 | |||||
Redirect Temp /fop/previous http://xmlgraphics.apache.org/fop/0.93 | |||||
Redirect Temp /fop/0.90alpha1 http://xmlgraphics.apache.org/fop/0.93 | |||||
Redirect Temp /fop/0.91beta http://xmlgraphics.apache.org/fop/0.93 | |||||
Redirect Temp /fop/0.92beta http://xmlgraphics.apache.org/fop/0.93 | |||||
Redirect Temp /fop/latest http://xmlgraphics.apache.org/fop/0.95 | |||||
# Previous stable release | |||||
Redirect Temp /fop/previous http://xmlgraphics.apache.org/fop/0.94 | |||||
# Old releases | |||||
Redirect Temp /fop/maintenance http://xmlgraphics.apache.org/fop/0.94 | |||||
Redirect Temp /fop/0.90alpha1 http://xmlgraphics.apache.org/fop/0.94 | |||||
Redirect Temp /fop/0.91beta http://xmlgraphics.apache.org/fop/0.94 | |||||
Redirect Temp /fop/0.92beta http://xmlgraphics.apache.org/fop/0.94 | |||||
Redirect Temp /fop/0.93 http://xmlgraphics.apache.org/fop/0.94 |
<?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>Metadata</title> | |||||
</header> | |||||
<body> | |||||
<section id="overview"> | |||||
<title>Overview</title> | |||||
<p> | |||||
Document metadata is an important tool for categorizing and finding documents. | |||||
Various formats support different kinds of metadata representation and to | |||||
different levels. One of the more popular and flexible means of representing | |||||
document or object metadata is | |||||
<a href="http://www.adobe.com/products/xmp/">XMP (eXtensible Metadata Platform, specified by Adobe)</a>. | |||||
PDF 1.4 introduced the use of XMP. The XMP specification lists recommendation for | |||||
embedding XMP metdata in other document and image formats. Given its flexibility it makes | |||||
sense to make use this approach in the XSL-FO context. Unfortunately, unlike SVG which | |||||
also refers to XMP, XSL-FO doesn't recommend a preferred way of specifying document and | |||||
object metadata. Therefore, there's no portable way to represent metadata in XSL-FO | |||||
documents. Each implementation does it differently. | |||||
</p> | |||||
</section> | |||||
<section id="xmp-in-fo"> | |||||
<title>Embedding XMP in an XSL-FO document</title> | |||||
<p> | |||||
As noted above, there's no officially recommended way to embed metadata in XSL-FO. | |||||
Apache FOP supports embedding XMP in XSL-FO. Currently, only support for document-level | |||||
metadata is implemented. Object-level metadata will be implemented when there's | |||||
interest. | |||||
</p> | |||||
<p> | |||||
Document-level metadata can be specified in the <code>fo:declarations</code> element. | |||||
XMP specification recommends to use <code>x:xmpmeta</code>, <code>rdf:RDF</code>, and | |||||
<code>rdf:Description</code> elements as shown in example below. Both | |||||
<code>x:xmpmeta</code> and <code>rdf:RDF</code> elements are recognized as the top-level | |||||
element introducing an XMP fragment (as per the XMP specification). | |||||
</p> | |||||
<section id="xmp-example"> | |||||
<title>Example</title> | |||||
<source><![CDATA[[..] | |||||
</fo:layout-master-set> | |||||
<fo:declarations> | |||||
<x:xmpmeta xmlns:x="adobe:ns:meta/"> | |||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> | |||||
<rdf:Description rdf:about="" | |||||
xmlns:dc="http://purl.org/dc/elements/1.1/"> | |||||
<!-- Dublin Core properties go here --> | |||||
<dc:title>Document title</dc:title> | |||||
<dc:creator>Document author</dc:creator> | |||||
<dc:description>Document subject</dc:description> | |||||
</rdf:Description> | |||||
<rdf:Description rdf:about="" | |||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/"> | |||||
<!-- XMP properties go here --> | |||||
<xmp:CreatorTool>Tool used to make the PDF</xmp:CreatorTool> | |||||
</rdf:Description> | |||||
</rdf:RDF> | |||||
</x:xmpmeta> | |||||
</fo:declarations> | |||||
<fo:page-sequence ... | |||||
[..]]]></source> | |||||
<note> | |||||
<code>fo:declarations</code> <strong>must</strong> be declared after | |||||
<code>fo:layout-master-set</code> and before the first <code>page-sequence</code>. | |||||
</note> | |||||
</section> | |||||
</section> | |||||
<section id="xmp-impl-in-fop"> | |||||
<title>Implementation in Apache FOP</title> | |||||
<p> | |||||
Currently, XMP support is only available for PDF output. | |||||
</p> | |||||
<p> | |||||
Originally, you could set some metadata information through FOP's FOUserAgent by | |||||
using its set*() methods (like setTitle(String) or setAuthor(String). These values are | |||||
directly used to set value in the PDF Info object. Since PDF 1.4, adding metadata as an | |||||
XMP document to a PDF is possible. That means that there are now two mechanisms in PDF | |||||
that hold metadata. | |||||
</p> | |||||
<p> | |||||
Apache FOP now synchronizes the Info and the Metadata object in PDF, i.e. when you | |||||
set the title and the author through the FOUserAgent, the two values will end up in | |||||
the (old) Info object and in the new Metadata object as XMP content. If instead of | |||||
FOUserAgent, you embed XMP metadata in the XSL-FO document (as shown above), the | |||||
XMP metadata will be used as-is in the PDF Metadata object and some values from the | |||||
XMP metadata will be copied to the Info object to maintain backwards-compatibility | |||||
for PDF readers that don't support XMP metadata. | |||||
</p> | |||||
<p> | |||||
The mapping between the Info and the Metadata object used by Apache FOP comes from | |||||
the <a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=38920">PDF/A-1 specification</a>. | |||||
For convenience, here's the mapping table: | |||||
</p> | |||||
<table> | |||||
<tr> | |||||
<th colspan="2">Document information dictionary</th> | |||||
<th colspan="3">XMP</th> | |||||
</tr> | |||||
<tr> | |||||
<th>Entry</th> | |||||
<th>PDF type</th> | |||||
<th>Property</th> | |||||
<th>XMP type</th> | |||||
<th>Category</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Title</td> | |||||
<td>text string</td> | |||||
<td>dc:title</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Author</td> | |||||
<td>text string</td> | |||||
<td>dc:creator</td> | |||||
<td>seq Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Subject</td> | |||||
<td>text string</td> | |||||
<td>dc:description["x-default"]</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Keywords</td> | |||||
<td>text string</td> | |||||
<td>pdf:Keywords</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Creator</td> | |||||
<td>text string</td> | |||||
<td>xmp:CreatorTool</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Producer</td> | |||||
<td>text string</td> | |||||
<td>pdf:Producer</td> | |||||
<td>Text</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CreationDate</td> | |||||
<td>date</td> | |||||
<td>xmp:CreationDate</td> | |||||
<td>Date</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
<tr> | |||||
<td>ModDate</td> | |||||
<td>date</td> | |||||
<td>xmp:ModifyDate</td> | |||||
<td>Date</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
</table> | |||||
<note> | |||||
"Internal" in the Category column means that the user should not set this value. | |||||
It is set by the application. | |||||
</note> | |||||
<note> | |||||
The "Subject" used to be mapped to <code>dc:subject</code> in the initial publication of | |||||
PDF/A-1 (ISO 19005-1). In the | |||||
<a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=45613">Technical Corrigendum 1</a> | |||||
this was changed to map to <code>dc:description["x-default"]</code>. | |||||
</note> | |||||
<section id="namespaces"> | |||||
<title>Namespaces</title> | |||||
<p> | |||||
Metadata is made of property sets where each property set uses a different namespace URI. | |||||
</p> | |||||
<p> | |||||
The following is a listing of namespaces that Apache FOP recognizes and acts upon, | |||||
mostly to synchronize the XMP metadata with the PDF Info dictionary: | |||||
</p> | |||||
<table> | |||||
<tr> | |||||
<th>Set/Schema</th> | |||||
<th>Namespace Prefix</th> | |||||
<th>Namespace URI</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Dublin Core</td> | |||||
<td>dc</td> | |||||
<td>http://purl.org/dc/elements/1.1/</td> | |||||
</tr> | |||||
<tr> | |||||
<td>XMP Basic</td> | |||||
<td>xmp</td> | |||||
<td>http://ns.adobe.com/xap/1.0/</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Adobe PDF Schema</td> | |||||
<td>pdf</td> | |||||
<td>http://ns.adobe.com/pdf/1.3/</td> | |||||
</tr> | |||||
</table> | |||||
<p> | |||||
Please refer to the <a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">XMP Specification</a> | |||||
for information on other metadata namespaces. | |||||
</p> | |||||
<p> | |||||
Property sets (Namespaces) not listed here are simply passed through to the final | |||||
document (if supported). That is useful if you want to specify a custom metadata | |||||
schema. | |||||
</p> | |||||
</section> | |||||
</section> | |||||
<section id="links"> | |||||
<title>Links</title> | |||||
<ul> | |||||
<li><a href="http://www.adobe.com/products/xmp/">Adobe's Extensible Metadata Platform (XMP) website</a></li> | |||||
<li><a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">Adobe XMP Specification</a></li> | |||||
<li><a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">Adobe XMP Specification</a></li> | |||||
<li><a href="http://dublincore.org/">http://dublincore.org/</a></li> | |||||
</ul> | |||||
</section> | |||||
</body> | |||||
</document> |
<body> | <body> | ||||
<section id="overview"> | <section id="overview"> | ||||
<title>Overview</title> | <title>Overview</title> | ||||
<warning> | |||||
Support for PDF/A is available beginning with version 0.92. | |||||
</warning> | |||||
<p> | <p> | ||||
PDF/A is a standard which turns PDF into an "electronic document file | PDF/A is a standard which turns PDF into an "electronic document file | ||||
format for long-term preservation". PDF/A-1 is the first part of the | format for long-term preservation". PDF/A-1 is the first part of the |
</p> | </p> | ||||
</answer> | </answer> | ||||
</faq> | </faq> | ||||
<faq id="saxexception-mismatch"> | |||||
<question>I get a SAXException: Mismatch: page-sequence vs. root | |||||
(or similar).</question> | |||||
<answer> | |||||
<p> | |||||
The full exception usually looks similar to this: | |||||
</p> | |||||
<source>Mismatch: page-sequence (http://www.w3.org/1999/XSL/Format) vs. root | |||||
(http://www.w3.org/1999/XSL/Format)</source> | |||||
<p> | |||||
This exception is usually a follow-up error after another exception. Sometimes | |||||
the original exception gets swallowed by Xalan's default <code>ErrorListener</code> | |||||
(should be fixed in the latest Xalan release). | |||||
</p> | |||||
<p> | |||||
The work-around is to set an explicit <code>ErrorListener</code> on the | |||||
<code>Transformer</code>. The <code>ErrorListener</code> can be as simple as this: | |||||
</p> | |||||
<source><![CDATA[ | |||||
import javax.xml.transform.ErrorListener; | |||||
import javax.xml.transform.TransformerException; | |||||
public class DefaultErrorListener implements ErrorListener { | |||||
public void warning(TransformerException exc) { | |||||
System.err.println(exc.toString()); | |||||
} | |||||
public void error(TransformerException exc) | |||||
throws TransformerException { | |||||
throw exc; | |||||
} | |||||
public void fatalError(TransformerException exc) | |||||
throws TransformerException { | |||||
throw exc; | |||||
} | |||||
}]]></source> | |||||
</answer> | |||||
</faq> | |||||
</part> | </part> | ||||
<part id="part-output"> | <part id="part-output"> | ||||
<title>Problems with FOP output</title> | <title>Problems with FOP output</title> | ||||
text flows into adjacent cells/block, obscuring stuff there.</question> | text flows into adjacent cells/block, obscuring stuff there.</question> | ||||
<answer> | <answer> | ||||
<p> | <p> | ||||
Clipping as specified by the <code>overflow="hidden"</code> is not yet | |||||
implemented. If you have long words overflowing table cells, try to | |||||
Since the <code>overflow</code> property doesn't apply to table-cell, you | |||||
can wrap the cell content in a block-container and specify | |||||
<code>overflow="hidden"</code> there. Alternatively, | |||||
if you have long words overflowing table cells, try to | |||||
get them hyphenated. Artificial names like product identifications or | get them hyphenated. Artificial names like product identifications or | ||||
long numbers usually aren't hyphenated. You can try special processing | long numbers usually aren't hyphenated. You can try special processing | ||||
at XSLT level, like | at XSLT level, like | ||||
<link href="http://www.mulberrytech.com/xsl/xsl-list/">XSL list | <link href="http://www.mulberrytech.com/xsl/xsl-list/">XSL list | ||||
archive</link> for how to perform these tasks. | archive</link> for how to perform these tasks. | ||||
</p> | </p> | ||||
<p> | |||||
If your text is not hyphenated at all and overflows the cell, please check | |||||
if you've specified <code>keep-together="always"</code> on the table-cell | |||||
or one of its parent elements. <code>keep-together="always"</code> implicitely | |||||
also sets <code>keep-together.within-line="always"</code> which forbids FOP | |||||
to break the text into multiple lines. This is important as FOP supports inline-level | |||||
keeps since version 0.94. It's a good idea not to use the shorthand | |||||
<code>keep-together="always"</code> at all! | |||||
</p> | |||||
</answer> | </answer> | ||||
</faq> | </faq> | ||||
<faq id="row-height-constraint"> | <faq id="row-height-constraint"> |
<fonts label="Fonts" href="fonts.html"/> | <fonts label="Fonts" href="fonts.html"/> | ||||
<hyphenation label="Hyphenation" href="hyphenation.html"/> | <hyphenation label="Hyphenation" href="hyphenation.html"/> | ||||
<extensions label="Extensions" href="extensions.html"/> | <extensions label="Extensions" href="extensions.html"/> | ||||
<metadata label="Metadata" href="metadata.html"/> | |||||
</features> | </features> | ||||
</trunk> | </trunk> | ||||
<hyphenation label="Hyphenation" href="hyphenation.html"/> | <hyphenation label="Hyphenation" href="hyphenation.html"/> | ||||
<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"/> | |||||
</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>Metadata</title> | |||||
</header> | |||||
<body> | |||||
<section id="overview"> | |||||
<title>Overview</title> | |||||
<p> | |||||
Document metadata is an important tool for categorizing and finding documents. | |||||
Various formats support different kinds of metadata representation and to | |||||
different levels. One of the more popular and flexible means of representing | |||||
document or object metadata is | |||||
<a href="http://www.adobe.com/products/xmp/">XMP (eXtensible Metadata Platform, specified by Adobe)</a>. | |||||
PDF 1.4 introduced the use of XMP. The XMP specification lists recommendation for | |||||
embedding XMP metdata in other document and image formats. Given its flexibility it makes | |||||
sense to make use this approach in the XSL-FO context. Unfortunately, unlike SVG which | |||||
also refers to XMP, XSL-FO doesn't recommend a preferred way of specifying document and | |||||
object metadata. Therefore, there's no portable way to represent metadata in XSL-FO | |||||
documents. Each implementation does it differently. | |||||
</p> | |||||
</section> | |||||
<section id="xmp-in-fo"> | |||||
<title>Embedding XMP in an XSL-FO document</title> | |||||
<p> | |||||
As noted above, there's no officially recommended way to embed metadata in XSL-FO. | |||||
Apache FOP supports embedding XMP in XSL-FO. Currently, only support for document-level | |||||
metadata is implemented. Object-level metadata will be implemented when there's | |||||
interest. | |||||
</p> | |||||
<p> | |||||
Document-level metadata can be specified in the <code>fo:declarations</code> element. | |||||
XMP specification recommends to use <code>x:xmpmeta</code>, <code>rdf:RDF</code>, and | |||||
<code>rdf:Description</code> elements as shown in example below. Both | |||||
<code>x:xmpmeta</code> and <code>rdf:RDF</code> elements are recognized as the top-level | |||||
element introducing an XMP fragment (as per the XMP specification). | |||||
</p> | |||||
<section id="xmp-example"> | |||||
<title>Example</title> | |||||
<source><![CDATA[[..] | |||||
</fo:layout-master-set> | |||||
<fo:declarations> | |||||
<x:xmpmeta xmlns:x="adobe:ns:meta/"> | |||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> | |||||
<rdf:Description rdf:about="" | |||||
xmlns:dc="http://purl.org/dc/elements/1.1/"> | |||||
<!-- Dublin Core properties go here --> | |||||
<dc:title>Document title</dc:title> | |||||
<dc:creator>Document author</dc:creator> | |||||
<dc:description>Document subject</dc:description> | |||||
</rdf:Description> | |||||
<rdf:Description rdf:about="" | |||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/"> | |||||
<!-- XMP properties go here --> | |||||
<xmp:CreatorTool>Tool used to make the PDF</xmp:CreatorTool> | |||||
</rdf:Description> | |||||
</rdf:RDF> | |||||
</x:xmpmeta> | |||||
</fo:declarations> | |||||
<fo:page-sequence ... | |||||
[..]]]></source> | |||||
<note> | |||||
<code>fo:declarations</code> <strong>must</strong> be declared after | |||||
<code>fo:layout-master-set</code> and before the first <code>page-sequence</code>. | |||||
</note> | |||||
</section> | |||||
</section> | |||||
<section id="xmp-impl-in-fop"> | |||||
<title>Implementation in Apache FOP</title> | |||||
<p> | |||||
Currently, XMP support is only available for PDF output. | |||||
</p> | |||||
<p> | |||||
Originally, you could set some metadata information through FOP's FOUserAgent by | |||||
using its set*() methods (like setTitle(String) or setAuthor(String). These values are | |||||
directly used to set value in the PDF Info object. Since PDF 1.4, adding metadata as an | |||||
XMP document to a PDF is possible. That means that there are now two mechanisms in PDF | |||||
that hold metadata. | |||||
</p> | |||||
<p> | |||||
Apache FOP now synchronizes the Info and the Metadata object in PDF, i.e. when you | |||||
set the title and the author through the FOUserAgent, the two values will end up in | |||||
the (old) Info object and in the new Metadata object as XMP content. If instead of | |||||
FOUserAgent, you embed XMP metadata in the XSL-FO document (as shown above), the | |||||
XMP metadata will be used as-is in the PDF Metadata object and some values from the | |||||
XMP metadata will be copied to the Info object to maintain backwards-compatibility | |||||
for PDF readers that don't support XMP metadata. | |||||
</p> | |||||
<p> | |||||
The mapping between the Info and the Metadata object used by Apache FOP comes from | |||||
the <a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=38920">PDF/A-1 specification</a>. | |||||
For convenience, here's the mapping table: | |||||
</p> | |||||
<table> | |||||
<tr> | |||||
<th colspan="2">Document information dictionary</th> | |||||
<th colspan="3">XMP</th> | |||||
</tr> | |||||
<tr> | |||||
<th>Entry</th> | |||||
<th>PDF type</th> | |||||
<th>Property</th> | |||||
<th>XMP type</th> | |||||
<th>Category</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Title</td> | |||||
<td>text string</td> | |||||
<td>dc:title</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Author</td> | |||||
<td>text string</td> | |||||
<td>dc:creator</td> | |||||
<td>seq Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Subject</td> | |||||
<td>text string</td> | |||||
<td>dc:description["x-default"]</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Keywords</td> | |||||
<td>text string</td> | |||||
<td>pdf:Keywords</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Creator</td> | |||||
<td>text string</td> | |||||
<td>xmp:CreatorTool</td> | |||||
<td>Text</td> | |||||
<td>External</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Producer</td> | |||||
<td>text string</td> | |||||
<td>pdf:Producer</td> | |||||
<td>Text</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CreationDate</td> | |||||
<td>date</td> | |||||
<td>xmp:CreationDate</td> | |||||
<td>Date</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
<tr> | |||||
<td>ModDate</td> | |||||
<td>date</td> | |||||
<td>xmp:ModifyDate</td> | |||||
<td>Date</td> | |||||
<td>Internal</td> | |||||
</tr> | |||||
</table> | |||||
<note> | |||||
"Internal" in the Category column means that the user should not set this value. | |||||
It is set by the application. | |||||
</note> | |||||
<note> | |||||
The "Subject" used to be mapped to <code>dc:subject</code> in the initial publication of | |||||
PDF/A-1 (ISO 19005-1). In the | |||||
<a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=45613">Technical Corrigendum 1</a> | |||||
this was changed to map to <code>dc:description["x-default"]</code>. | |||||
</note> | |||||
<section id="namespaces"> | |||||
<title>Namespaces</title> | |||||
<p> | |||||
Metadata is made of property sets where each property set uses a different namespace URI. | |||||
</p> | |||||
<p> | |||||
The following is a listing of namespaces that Apache FOP recognizes and acts upon, | |||||
mostly to synchronize the XMP metadata with the PDF Info dictionary: | |||||
</p> | |||||
<table> | |||||
<tr> | |||||
<th>Set/Schema</th> | |||||
<th>Namespace Prefix</th> | |||||
<th>Namespace URI</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Dublin Core</td> | |||||
<td>dc</td> | |||||
<td>http://purl.org/dc/elements/1.1/</td> | |||||
</tr> | |||||
<tr> | |||||
<td>XMP Basic</td> | |||||
<td>xmp</td> | |||||
<td>http://ns.adobe.com/xap/1.0/</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Adobe PDF Schema</td> | |||||
<td>pdf</td> | |||||
<td>http://ns.adobe.com/pdf/1.3/</td> | |||||
</tr> | |||||
</table> | |||||
<p> | |||||
Please refer to the <a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">XMP Specification</a> | |||||
for information on other metadata namespaces. | |||||
</p> | |||||
<p> | |||||
Property sets (Namespaces) not listed here are simply passed through to the final | |||||
document (if supported). That is useful if you want to specify a custom metadata | |||||
schema. | |||||
</p> | |||||
</section> | |||||
</section> | |||||
<section id="links"> | |||||
<title>Links</title> | |||||
<ul> | |||||
<li><a href="http://www.adobe.com/products/xmp/">Adobe's Extensible Metadata Platform (XMP) website</a></li> | |||||
<li><a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">Adobe XMP Specification</a></li> | |||||
<li><a href="http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf">Adobe XMP Specification</a></li> | |||||
<li><a href="http://dublincore.org/">http://dublincore.org/</a></li> | |||||
</ul> | |||||
</section> | |||||
</body> | |||||
</document> |
<body> | <body> | ||||
<section id="overview"> | <section id="overview"> | ||||
<title>Overview</title> | <title>Overview</title> | ||||
<warning> | |||||
Support for PDF/A is available beginning with version 0.92. | |||||
</warning> | |||||
<p> | <p> | ||||
PDF/A is a standard which turns PDF into an "electronic document file | PDF/A is a standard which turns PDF into an "electronic document file | ||||
format for long-term preservation". PDF/A-1 is the first part of the | format for long-term preservation". PDF/A-1 is the first part of the |
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.xmlgraphics.util.io.Base64EncodeStream; | import org.apache.xmlgraphics.util.io.Base64EncodeStream; | ||||
import org.apache.fop.util.DataURIResolver; | |||||
import org.apache.xmlgraphics.util.uri.CommonURIResolver; | |||||
/** | /** | ||||
* Provides FOP specific URI resolution. This is the default URIResolver | * Provides FOP specific URI resolution. This is the default URIResolver | ||||
* {@link FOUserAgent} will use unless overidden. | |||||
* {@link FOUserAgent} will use unless overridden. | |||||
* | * | ||||
* @see javax.xml.transform.URIResolver | * @see javax.xml.transform.URIResolver | ||||
*/ | */ | ||||
// log | // log | ||||
private Log log = LogFactory.getLog("FOP"); | private Log log = LogFactory.getLog("FOP"); | ||||
/** URIResolver for RFC 2397 data URLs */ | |||||
private URIResolver dataURIResolver = new DataURIResolver(); | |||||
/** Common URIResolver */ | |||||
private CommonURIResolver commonURIResolver = new CommonURIResolver(); | |||||
/** A user settable URI Resolver */ | /** A user settable URI Resolver */ | ||||
private URIResolver uriResolver = null; | private URIResolver uriResolver = null; | ||||
// data URLs can be quite long so evaluate early and don't try to build a File | // data URLs can be quite long so evaluate early and don't try to build a File | ||||
// (can lead to problems) | // (can lead to problems) | ||||
source = dataURIResolver.resolve(href, base); | |||||
source = commonURIResolver.resolve(href, base); | |||||
// Custom uri resolution | // Custom uri resolution | ||||
if (source == null && uriResolver != null) { | if (source == null && uriResolver != null) { |
*/ | */ | ||||
public final class PropertyCache { | public final class PropertyCache { | ||||
private static final int SEGMENT_COUNT = 32; //0x20 | |||||
private static final int INITIAL_BUCKET_COUNT = SEGMENT_COUNT; | |||||
/** bitmask to apply to the hash to get to the | /** bitmask to apply to the hash to get to the | ||||
* corresponding cache segment */ | * corresponding cache segment */ | ||||
private static final int SEGMENT_MASK = 0x1F; | |||||
private static final int SEGMENT_MASK = SEGMENT_COUNT - 1; //0x1F | |||||
/** | /** | ||||
* Indicates whether the cache should be used at all | * Indicates whether the cache should be used at all | ||||
* Can be controlled by the system property: | * Can be controlled by the system property: | ||||
private final boolean useCache; | private final boolean useCache; | ||||
/** the segments array (length = 32) */ | /** the segments array (length = 32) */ | ||||
private CacheSegment[] segments = new CacheSegment[SEGMENT_MASK + 1]; | |||||
private CacheSegment[] segments = new CacheSegment[SEGMENT_COUNT]; | |||||
/** the table of hash-buckets */ | /** the table of hash-buckets */ | ||||
private CacheEntry[] table = new CacheEntry[8]; | |||||
private CacheEntry[] table = new CacheEntry[INITIAL_BUCKET_COUNT]; | |||||
private Class runtimeType; | private Class runtimeType; | ||||
final boolean[] votesForRehash = new boolean[SEGMENT_MASK + 1]; | |||||
private boolean[] votesForRehash = new boolean[SEGMENT_COUNT]; | |||||
/* same hash function as used by java.util.HashMap */ | /* same hash function as used by java.util.HashMap */ | ||||
private static int hash(Object x) { | private static int hash(Object x) { | ||||
} | } | ||||
/* Class modeling a cached entry */ | /* Class modeling a cached entry */ | ||||
private final class CacheEntry extends WeakReference { | |||||
volatile CacheEntry nextEntry; | |||||
final int hash; | |||||
private static class CacheEntry extends WeakReference { | |||||
private volatile CacheEntry nextEntry; | |||||
private final int hash; | |||||
/* main constructor */ | /* main constructor */ | ||||
public CacheEntry(Object p, CacheEntry nextEntry, ReferenceQueue refQueue) { | public CacheEntry(Object p, CacheEntry nextEntry, ReferenceQueue refQueue) { | ||||
super(p, refQueue); | super(p, refQueue); | ||||
this.nextEntry = nextEntry; | this.nextEntry = nextEntry; | ||||
this.hash = p.hashCode(); | |||||
this.hash = hash(p); | |||||
} | |||||
/* main constructor */ | |||||
public CacheEntry(Object p, CacheEntry nextEntry) { | |||||
super(p); | |||||
this.nextEntry = nextEntry; | |||||
this.hash = hash(p); | |||||
} | } | ||||
} | } | ||||
/* Wrapper objects to synchronize on */ | /* Wrapper objects to synchronize on */ | ||||
private final class CacheSegment { | |||||
private static class CacheSegment { | |||||
private int count = 0; | private int count = 0; | ||||
private volatile ReferenceQueue staleEntries = new ReferenceQueue(); | |||||
} | } | ||||
private void cleanSegment(int segmentIndex) { | private void cleanSegment(int segmentIndex) { | ||||
CacheEntry entry; | |||||
CacheSegment segment = segments[segmentIndex]; | CacheSegment segment = segments[segmentIndex]; | ||||
int bucketIndex; | |||||
int oldCount = segment.count; | int oldCount = segment.count; | ||||
while ((entry = (CacheEntry) segment.staleEntries.poll()) != null) { | |||||
bucketIndex = hash(entry.hash) & (table.length - 1); | |||||
/* remove obsolete entry */ | |||||
/* 1. move to the corresponding entry */ | |||||
/* clean all buckets in this segment */ | |||||
for (int bucketIndex = segmentIndex; | |||||
bucketIndex < table.length; | |||||
bucketIndex += SEGMENT_COUNT) { | |||||
CacheEntry prev = null; | CacheEntry prev = null; | ||||
CacheEntry e = table[bucketIndex]; | |||||
while (e != null | |||||
&& e.nextEntry != null | |||||
&& e.hash != entry.hash) { | |||||
prev = e; | |||||
e = e.nextEntry; | |||||
CacheEntry entry = table[bucketIndex]; | |||||
if (entry == null) { | |||||
continue; | |||||
} | } | ||||
if (e != null) { | |||||
/* 2. remove reference from the chain */ | |||||
if (prev == null) { | |||||
table[bucketIndex] = e.nextEntry; | |||||
do { | |||||
if (entry.get() == null) { | |||||
if (prev == null) { | |||||
table[bucketIndex] = entry.nextEntry; | |||||
} else { | |||||
prev.nextEntry = entry.nextEntry; | |||||
} | |||||
segment.count--; | |||||
assert segment.count >= 0; | |||||
} else { | } else { | ||||
prev.nextEntry = e.nextEntry; | |||||
prev = entry; | |||||
} | } | ||||
segment.count--; | |||||
} | |||||
entry = entry.nextEntry; | |||||
} while (entry != null); | |||||
} | } | ||||
synchronized (votesForRehash) { | synchronized (votesForRehash) { | ||||
if (oldCount > segment.count) { | if (oldCount > segment.count) { | ||||
votesForRehash[segmentIndex] = false; | votesForRehash[segmentIndex] = false; | ||||
/* first time for this segment */ | /* first time for this segment */ | ||||
votesForRehash[segmentIndex] = true; | votesForRehash[segmentIndex] = true; | ||||
int voteCount = 0; | int voteCount = 0; | ||||
for (int i = SEGMENT_MASK + 1; --i >= 0; ) { | |||||
for (int i = SEGMENT_MASK + 1; --i >= 0;) { | |||||
if (votesForRehash[i]) { | if (votesForRehash[i]) { | ||||
voteCount++; | voteCount++; | ||||
} | } | ||||
private void put(Object o) { | private void put(Object o) { | ||||
int hash = hash(o); | int hash = hash(o); | ||||
CacheSegment segment = segments[hash & SEGMENT_MASK]; | |||||
int segmentIndex = hash & SEGMENT_MASK; | |||||
CacheSegment segment = segments[segmentIndex]; | |||||
synchronized (segment) { | synchronized (segment) { | ||||
int index = hash & (table.length - 1); | int index = hash & (table.length - 1); | ||||
CacheEntry entry = table[index]; | CacheEntry entry = table[index]; | ||||
if (entry == null) { | if (entry == null) { | ||||
entry = new CacheEntry(o, null, segment.staleEntries); | |||||
entry = new CacheEntry(o, null); | |||||
table[index] = entry; | table[index] = entry; | ||||
segment.count++; | segment.count++; | ||||
} else { | } else { | ||||
if (eq(p, o)) { | if (eq(p, o)) { | ||||
return; | return; | ||||
} else { | } else { | ||||
CacheEntry newEntry = new CacheEntry(o, entry, segment.staleEntries); | |||||
CacheEntry newEntry = new CacheEntry(o, entry); | |||||
table[index] = newEntry; | table[index] = newEntry; | ||||
segment.count++; | segment.count++; | ||||
} | } | ||||
} | } | ||||
if (segment.count > (2 * table.length)) { | if (segment.count > (2 * table.length)) { | ||||
cleanSegment(hash & SEGMENT_MASK); | |||||
cleanSegment(segmentIndex); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/* try non-synched first */ | /* try non-synched first */ | ||||
for (CacheEntry e = entry; e != null; e = e.nextEntry) { | for (CacheEntry e = entry; e != null; e = e.nextEntry) { | ||||
if (e.hash == o.hashCode() | |||||
if (e.hash == hash | |||||
&& (q = e.get()) != null | && (q = e.get()) != null | ||||
&& eq(q, o)) { | && eq(q, o)) { | ||||
return q; | return q; | ||||
synchronized (segment) { | synchronized (segment) { | ||||
entry = table[index]; | entry = table[index]; | ||||
for (CacheEntry e = entry; e != null; e = e.nextEntry) { | for (CacheEntry e = entry; e != null; e = e.nextEntry) { | ||||
if (e.hash == o.hashCode() | |||||
if (e.hash == hash | |||||
&& (q = e.get()) != null | && (q = e.get()) != null | ||||
&& eq(q, o)) { | && eq(q, o)) { | ||||
return q; | return q; | ||||
/* double the amount of buckets */ | /* double the amount of buckets */ | ||||
int newLength = table.length << 1; | int newLength = table.length << 1; | ||||
if (newLength > 0) { //no overflow? | if (newLength > 0) { //no overflow? | ||||
/* reset segmentcounts */ | |||||
/* reset segment counts */ | |||||
for (int i = segments.length; --i >= 0;) { | for (int i = segments.length; --i >= 0;) { | ||||
segments[i].count = 0; | segments[i].count = 0; | ||||
} | } | ||||
if ((o = c.get()) != null) { | if ((o = c.get()) != null) { | ||||
hash = c.hash; | hash = c.hash; | ||||
idx = hash & newLength; | idx = hash & newLength; | ||||
newTable[idx] = new CacheEntry(o, newTable[idx], | |||||
segments[hash & SEGMENT_MASK].staleEntries); | |||||
newTable[idx] = new CacheEntry(o, newTable[idx]); | |||||
segments[hash & SEGMENT_MASK].count++; | segments[hash & SEGMENT_MASK].count++; | ||||
} | } | ||||
} | } | ||||
* @param prop the Property instance to check for | * @param prop the Property instance to check for | ||||
* @return the cached instance | * @return the cached instance | ||||
*/ | */ | ||||
public final Property fetch(Property prop) { | |||||
public Property fetch(Property prop) { | |||||
return (Property) fetch((Object) prop); | return (Property) fetch((Object) prop); | ||||
} | } | ||||
* @param chy the CommonHyphenation instance to check for | * @param chy the CommonHyphenation instance to check for | ||||
* @return the cached instance | * @return the cached instance | ||||
*/ | */ | ||||
public final CommonHyphenation fetch(CommonHyphenation chy) { | |||||
public CommonHyphenation fetch(CommonHyphenation chy) { | |||||
return (CommonHyphenation) fetch((Object) chy); | return (CommonHyphenation) fetch((Object) chy); | ||||
} | } | ||||
* @param cf the CommonFont instance to check for | * @param cf the CommonFont instance to check for | ||||
* @return the cached instance | * @return the cached instance | ||||
*/ | */ | ||||
public final CommonFont fetch(CommonFont cf) { | |||||
public CommonFont fetch(CommonFont cf) { | |||||
return (CommonFont) fetch((Object) cf); | return (CommonFont) fetch((Object) cf); | ||||
} | } | ||||
* @param cbpb the CommonBorderPaddingBackground instance to check for | * @param cbpb the CommonBorderPaddingBackground instance to check for | ||||
* @return the cached instance | * @return the cached instance | ||||
*/ | */ | ||||
public final CommonBorderPaddingBackground fetch(CommonBorderPaddingBackground cbpb) { | |||||
public CommonBorderPaddingBackground fetch(CommonBorderPaddingBackground cbpb) { | |||||
return (CommonBorderPaddingBackground) fetch((Object) cbpb); | return (CommonBorderPaddingBackground) fetch((Object) cbpb); | ||||
} | } | ||||
/** | /** | ||||
* Checks if the given {@link CommonBorderPaddingBackground.BorderInfo} is present in the cache - | |||||
* if so, returns a reference to the cached instance. | |||||
* Checks if the given {@link CommonBorderPaddingBackground.BorderInfo} is present | |||||
* in the cache - if so, returns a reference to the cached instance. | |||||
* Otherwise the given object is added to the cache and returned. | * Otherwise the given object is added to the cache and returned. | ||||
* | * | ||||
* @param bi the BorderInfo instance to check for | * @param bi the BorderInfo instance to check for | ||||
* @return the cached instance | * @return the cached instance | ||||
*/ | */ | ||||
public final CommonBorderPaddingBackground.BorderInfo fetch(CommonBorderPaddingBackground.BorderInfo bi) { | |||||
public CommonBorderPaddingBackground.BorderInfo fetch( | |||||
CommonBorderPaddingBackground.BorderInfo bi) { | |||||
return (CommonBorderPaddingBackground.BorderInfo) fetch((Object) bi); | return (CommonBorderPaddingBackground.BorderInfo) fetch((Object) bi); | ||||
} | } | ||||
/** | /** | ||||
* Get the string containing all the commands written into this | * Get the string containing all the commands written into this | ||||
* Grpahics. | |||||
* Graphics. | |||||
* @return the string containing the PDF markup | * @return the string containing the PDF markup | ||||
*/ | */ | ||||
public String getString() { | public String getString() { | ||||
/** | /** | ||||
* Get the string buffer from the currentStream, containing all | * Get the string buffer from the currentStream, containing all | ||||
* the commands written into this Grpahics so far. | |||||
* the commands written into this Graphics so far. | |||||
* @return the StringBuffer containing the PDF markup | * @return the StringBuffer containing the PDF markup | ||||
*/ | */ | ||||
public StringBuffer getBuffer() { | public StringBuffer getBuffer() { | ||||
if (paint instanceof RadialGradientPaint) { | if (paint instanceof RadialGradientPaint) { | ||||
RadialGradientPaint rgp = (RadialGradientPaint)paint; | RadialGradientPaint rgp = (RadialGradientPaint)paint; | ||||
// There is essentially no way to support repeate | |||||
// There is essentially no way to support repeats | |||||
// in PDF for radial gradients (the one option would | // in PDF for radial gradients (the one option would | ||||
// be to 'grow' the outer circle until it fully covered | // be to 'grow' the outer circle until it fully covered | ||||
// the bounds and then grow the stops accordingly, the | // the bounds and then grow the stops accordingly, the |
textUtil.beginTextObject(); | textUtil.beginTextObject(); | ||||
textUtil.setFonts(fonts); | textUtil.setFonts(fonts); | ||||
textUtil.setTextRenderingMode(tpi.fillPaint != null, tpi.strokePaint != null, false); | |||||
boolean stroke = (tpi.strokePaint != null) | |||||
&& (tpi.strokeStroke != null); | |||||
textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false); | |||||
AffineTransform localTransform = new AffineTransform(); | AffineTransform localTransform = new AffineTransform(); | ||||
Point2D prevPos = null; | Point2D prevPos = null; |
package org.apache.fop.util; | package org.apache.fop.util; | ||||
import java.io.ByteArrayInputStream; | |||||
import javax.xml.transform.Source; | import javax.xml.transform.Source; | ||||
import javax.xml.transform.TransformerException; | import javax.xml.transform.TransformerException; | ||||
import javax.xml.transform.URIResolver; | import javax.xml.transform.URIResolver; | ||||
import javax.xml.transform.stream.StreamSource; | |||||
// base64 support for "data" urls | |||||
import org.apache.xmlgraphics.util.io.Base64DecodeStream; | |||||
/** | /** | ||||
* Resolves data URLs (described in RFC 2397) returning its data as a StreamSource. | |||||
* | |||||
* @see javax.xml.transform.URIResolver | |||||
* @see <a href="http://www.ietf.org/rfc/rfc2397">RFC 2397</a> | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.uri.DataURIResolver | |||||
*/ | */ | ||||
public class DataURIResolver implements URIResolver { | public class DataURIResolver implements URIResolver { | ||||
/** | |||||
* {@inheritDoc} | |||||
*/ | |||||
public Source resolve(String href, String base) throws TransformerException { | |||||
if (href.startsWith("data:")) { | |||||
return parseDataURI(href); | |||||
} else { | |||||
return null; | |||||
} | |||||
} | |||||
private final URIResolver newResolver = new org.apache.xmlgraphics.util.uri.DataURIResolver(); | |||||
/** | /** | ||||
* Parses inline data URIs as generated by MS Word's XML export and FO | |||||
* stylesheet. | |||||
* | |||||
* @see <a href="http://www.ietf.org/rfc/rfc2397">RFC 2397</a> | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.uri.DataURIResolver#resolve(String, | |||||
* String) | |||||
*/ | */ | ||||
private Source parseDataURI(String href) { | |||||
int commaPos = href.indexOf(','); | |||||
// header is of the form data:[<mediatype>][;base64] | |||||
String header = href.substring(0, commaPos); | |||||
String data = href.substring(commaPos + 1); | |||||
if (header.endsWith(";base64")) { | |||||
byte[] bytes = data.getBytes(); | |||||
ByteArrayInputStream encodedStream = new ByteArrayInputStream(bytes); | |||||
Base64DecodeStream decodedStream = new Base64DecodeStream( | |||||
encodedStream); | |||||
return new StreamSource(decodedStream); | |||||
} else { | |||||
// Note that this is not quite the full story here. But since we are | |||||
// only interested | |||||
// in base64-encoded binary data, the next line will probably never | |||||
// be called. | |||||
//TODO Handle un-escaping of special URL chars like %20 | |||||
return new StreamSource(new java.io.StringReader(data)); | |||||
} | |||||
public Source resolve(String href, String base) throws TransformerException { | |||||
return newResolver.resolve(href, base); | |||||
} | } | ||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.StringWriter; | |||||
import java.io.Writer; | import java.io.Writer; | ||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.xmlgraphics.util.io.Base64EncodeStream; | |||||
/** | /** | ||||
* Utility classes for generating RFC 2397 data URLs. | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.uri.DataURLUtil | |||||
*/ | */ | ||||
public class DataURLUtil { | public class DataURLUtil { | ||||
/** | /** | ||||
* Creates a new data URL and returns it as a String. | |||||
* @param in the InputStream to read the data from | |||||
* @param mediatype the MIME type of the content, or null | |||||
* @return the newly created data URL | |||||
* @throws IOException if an I/O error occurs | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.uri.DataURLUtil#createDataURL(InputStream, | |||||
* String) | |||||
*/ | */ | ||||
public static String createDataURL(InputStream in, String mediatype) throws IOException { | |||||
StringWriter writer = new StringWriter(); | |||||
writeDataURL(in, mediatype, writer); | |||||
return writer.toString(); | |||||
public static String createDataURL(InputStream in, String mediatype) | |||||
throws IOException { | |||||
return org.apache.xmlgraphics.util.uri.DataURLUtil.createDataURL(in, | |||||
mediatype); | |||||
} | } | ||||
/** | /** | ||||
* Generates a data URL and writes it to a Writer. | |||||
* @param in the InputStream to read the data from | |||||
* @param mediatype the MIME type of the content, or null | |||||
* @param writer the Writer to write to | |||||
* @throws IOException if an I/O error occurs | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.uri.DataURLUtil#writeDataURL(InputStream, | |||||
* String, Writer) | |||||
*/ | */ | ||||
public static void writeDataURL(InputStream in, String mediatype, Writer writer) | |||||
throws IOException { | |||||
writer.write("data:"); | |||||
if (mediatype != null) { | |||||
writer.write(mediatype); | |||||
} | |||||
writer.write(";base64,"); | |||||
Base64EncodeStream out = new Base64EncodeStream( | |||||
new WriterOutputStream(writer, "US-ASCII")); | |||||
IOUtils.copy(in, out); | |||||
out.flush(); | |||||
public static void writeDataURL(InputStream in, String mediatype, | |||||
Writer writer) throws IOException { | |||||
org.apache.xmlgraphics.util.uri.DataURLUtil.writeDataURL(in, mediatype, | |||||
writer); | |||||
} | } | ||||
} | } |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id: $ */ | |||||
package org.apache.fop.util; | |||||
import java.awt.geom.AffineTransform; | |||||
/** | |||||
* Utility class for unit conversions. | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv instead. | |||||
*/ | |||||
public final class UnitConv { | |||||
/** | |||||
* conversion factory from millimeters to inches. | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2MM instead. | |||||
*/ | |||||
public static final float IN2MM = org.apache.xmlgraphics.util.UnitConv.IN2MM; | |||||
/** | |||||
* conversion factory from centimeters to inches. | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2CM instead. | |||||
*/ | |||||
public static final float IN2CM = org.apache.xmlgraphics.util.UnitConv.IN2CM; | |||||
/** | |||||
* conversion factory from inches to points. | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2PT instead. | |||||
*/ | |||||
public static final int IN2PT = org.apache.xmlgraphics.util.UnitConv.IN2PT; | |||||
/** | |||||
* Converts millimeters (mm) to points (pt) | |||||
* @param mm the value in mm | |||||
* @return the value in pt | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2pt(mm) instead. | |||||
*/ | |||||
public static double mm2pt(double mm) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mm2pt(mm); | |||||
} | |||||
/** | |||||
* Converts millimeters (mm) to millipoints (mpt) | |||||
* @param mm the value in mm | |||||
* @return the value in mpt | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2mpt(mm) instead. | |||||
*/ | |||||
public static double mm2mpt(double mm) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mm2mpt(mm); | |||||
} | |||||
/** | |||||
* Converts points (pt) to millimeters (mm) | |||||
* @param pt the value in pt | |||||
* @return the value in mm | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.pt2mm(pt) instead. | |||||
*/ | |||||
public static double pt2mm(double pt) { | |||||
return org.apache.xmlgraphics.util.UnitConv.pt2mm(pt); | |||||
} | |||||
/** | |||||
* Converts millimeters (mm) to inches (in) | |||||
* @param mm the value in mm | |||||
* @return the value in inches | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.pt2mm(pt) instead. | |||||
*/ | |||||
public static double mm2in(double mm) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mm2in(mm); | |||||
} | |||||
/** | |||||
* Converts inches (in) to millimeters (mm) | |||||
* @param in the value in inches | |||||
* @return the value in mm | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.in2mm(in) instead. | |||||
*/ | |||||
public static double in2mm(double in) { | |||||
return org.apache.xmlgraphics.util.UnitConv.in2mm(in); | |||||
} | |||||
/** | |||||
* Converts inches (in) to millipoints (mpt) | |||||
* @param in the value in inches | |||||
* @return the value in mpt | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.in2mpt(in) instead. | |||||
*/ | |||||
public static double in2mpt(double in) { | |||||
return org.apache.xmlgraphics.util.UnitConv.in2mpt(in); | |||||
} | |||||
/** | |||||
* Converts inches (in) to points (pt) | |||||
* @param in the value in inches | |||||
* @return the value in pt | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.in2pt(in) instead. | |||||
*/ | |||||
public static double in2pt(double in) { | |||||
return org.apache.xmlgraphics.util.UnitConv.in2pt(in); | |||||
} | |||||
/** | |||||
* Converts millipoints (mpt) to inches (in) | |||||
* @param mpt the value in mpt | |||||
* @return the value in inches | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mpt2in(mpt) instead. | |||||
*/ | |||||
public static double mpt2in(double mpt) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mpt2in(mpt); | |||||
} | |||||
/** | |||||
* Converts millimeters (mm) to pixels (px) | |||||
* @param mm the value in mm | |||||
* @param resolution the resolution in dpi (dots per inch) | |||||
* @return the value in pixels | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2px(mm, resolution) instead. | |||||
*/ | |||||
public static double mm2px(double mm, int resolution) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mm2px(mm, resolution); | |||||
} | |||||
/** | |||||
* Converts millipoints (mpt) to pixels (px) | |||||
* @param mpt the value in mpt | |||||
* @param resolution the resolution in dpi (dots per inch) | |||||
* @return the value in pixels | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mpt2px(mpt, resolution) instead. | |||||
*/ | |||||
public static double mpt2px(double mpt, int resolution) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mpt2px(mpt, resolution); | |||||
} | |||||
/** | |||||
* Converts a millipoint-based transformation matrix to points. | |||||
* @param at a millipoint-based transformation matrix | |||||
* @return a point-based transformation matrix | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.mptToPt(at) instead. | |||||
*/ | |||||
public static AffineTransform mptToPt(AffineTransform at) { | |||||
return org.apache.xmlgraphics.util.UnitConv.mptToPt(at); | |||||
} | |||||
/** | |||||
* Converts a point-based transformation matrix to millipoints. | |||||
* @param at a point-based transformation matrix | |||||
* @return a millipoint-based transformation matrix | |||||
* @deprecated use org.apache.xmlgraphics.util.UnitConv.ptToMpt(at) instead. | |||||
*/ | |||||
public static AffineTransform ptToMpt(AffineTransform at) { | |||||
return org.apache.xmlgraphics.util.UnitConv.ptToMpt(at); | |||||
} | |||||
} |
import java.io.Writer; | import java.io.Writer; | ||||
/** | /** | ||||
* An OutputStream wrapper for a Writer. | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream | |||||
*/ | */ | ||||
public class WriterOutputStream extends OutputStream { | public class WriterOutputStream extends OutputStream { | ||||
private Writer writer; | |||||
private String encoding; | |||||
private final org.apache.xmlgraphics.util.WriterOutputStream writerOutputStream; | |||||
/** | /** | ||||
* Creates a new WriterOutputStream. | |||||
* @param writer the Writer to write to | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#WriterOutputStream(Writer) | |||||
* String) | |||||
*/ | */ | ||||
public WriterOutputStream(Writer writer) { | public WriterOutputStream(Writer writer) { | ||||
this(writer, null); | |||||
writerOutputStream = new org.apache.xmlgraphics.util.WriterOutputStream( | |||||
writer); | |||||
} | } | ||||
/** | /** | ||||
* Creates a new WriterOutputStream. | |||||
* @param writer the Writer to write to | |||||
* @param encoding the encoding to use, or null if the default encoding should be used | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#WriterOutputStream(Writer, | |||||
* String) String) | |||||
*/ | */ | ||||
public WriterOutputStream(Writer writer, String encoding) { | public WriterOutputStream(Writer writer, String encoding) { | ||||
this.writer = writer; | |||||
this.encoding = encoding; | |||||
writerOutputStream = new org.apache.xmlgraphics.util.WriterOutputStream( | |||||
writer, encoding); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#close() | |||||
*/ | */ | ||||
public void close() throws IOException { | public void close() throws IOException { | ||||
writer.close(); | |||||
writerOutputStream.close(); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#flush() | |||||
*/ | */ | ||||
public void flush() throws IOException { | public void flush() throws IOException { | ||||
writer.flush(); | |||||
writerOutputStream.flush(); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#write(byte[], int, | |||||
* int) | |||||
*/ | */ | ||||
public void write(byte[] buf, int offset, int length) throws IOException { | public void write(byte[] buf, int offset, int length) throws IOException { | ||||
if (encoding != null) { | |||||
writer.write(new String(buf, offset, length, encoding)); | |||||
} else { | |||||
writer.write(new String(buf, offset, length)); | |||||
} | |||||
writerOutputStream.write(buf, offset, length); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#write(byte[]) | |||||
*/ | */ | ||||
public void write(byte[] buf) throws IOException { | public void write(byte[] buf) throws IOException { | ||||
write(buf, 0, buf.length); | |||||
writerOutputStream.write(buf); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | |||||
* @deprecated | |||||
* @see org.apache.xmlgraphics.util.WriterOutputStream#write(int) | |||||
*/ | */ | ||||
public void write(int b) throws IOException { | public void write(int b) throws IOException { | ||||
write(new byte[] {(byte)b}); | |||||
writerOutputStream.write(b); | |||||
} | } | ||||
} | } |
<changes> | <changes> | ||||
<release version="FOP Trunk" date="TBD"> | <release version="FOP Trunk" date="TBD"> | ||||
<action context="Code" dev="JM" type="fix" importance="high"> | |||||
Fixed memory leak in property cache (not cleaning stale PropertyCache$CacheEntry instances). | |||||
</action> | |||||
<action context="Renderers" dev="JM" type="fix"> | |||||
Fixed text stroking in SVG when the stroke-width is zero. | |||||
</action> | |||||
<action context="Layout" dev="JM" type="fix"> | <action context="Layout" dev="JM" type="fix"> | ||||
Fixed the source for a division by zero when the content of an fo:leader with | Fixed the source for a division by zero when the content of an fo:leader with | ||||
leader-pattern="use-content" collapses to zero width during layout. | leader-pattern="use-content" collapses to zero width during layout. | ||||
Fixed a ClassCastException when using an fo:wrapper as a child | Fixed a ClassCastException when using an fo:wrapper as a child | ||||
of an fo:block-container. | of an fo:block-container. | ||||
</action> | </action> | ||||
<action context="Fonts" dev="AC" type="add"> | |||||
<action context="Fonts" dev="AC" type="add" importance="high"> | |||||
Add support for font substitution. | Add support for font substitution. | ||||
</action> | </action> | ||||
<action context="Renderers" dev="JM" type="fix" fixes-bug="43650"> | <action context="Renderers" dev="JM" type="fix" fixes-bug="43650"> | ||||
Add partial support for the "show-destination" property on fo:basic-link | Add partial support for the "show-destination" property on fo:basic-link | ||||
(PDF output only; see limitations on the compliance page) | (PDF output only; see limitations on the compliance page) | ||||
</action> | </action> | ||||
<action context="Layout" dev="JM" type="add"> | |||||
<action context="Layout" dev="JM" type="add" importance="high"> | |||||
Added minimal support for integer keep values on the various keep properties on block-level | Added minimal support for integer keep values on the various keep properties on block-level | ||||
FOs. For now, all integer values are treated the same (i.e. without strength distinction). | FOs. For now, all integer values are treated the same (i.e. without strength distinction). | ||||
Using integers allows to avoid overflows that can happen when "always" is used extensively. | Using integers allows to avoid overflows that can happen when "always" is used extensively. | ||||
When a JPEG image is embedded, an optionally embedded color profile is filtered out | When a JPEG image is embedded, an optionally embedded color profile is filtered out | ||||
as it's already embedded separately in the PDF file. | as it's already embedded separately in the PDF file. | ||||
</action> | </action> | ||||
<action context="Fonts" dev="JM" type="add"> | |||||
<action context="Fonts" dev="JM" type="add" importance="high"> | |||||
Added support for addressing all glyphs available in a Type 1 font, not just the ones | Added support for addressing all glyphs available in a Type 1 font, not just the ones | ||||
in the font's primary encoding. | in the font's primary encoding. | ||||
</action> | </action> | ||||
<action context="Code" dev="JM" type="fix"> | <action context="Code" dev="JM" type="fix"> | ||||
AFP Renderer: Bugfix for 1 bit images where the width is not a multiple of 8. | AFP Renderer: Bugfix for 1 bit images where the width is not a multiple of 8. | ||||
</action> | </action> | ||||
<action context="Code" dev="MM" type="add"> | |||||
<action context="Code" dev="MM" type="add" importance="high"> | |||||
Support for keep-together.within-line="always". | Support for keep-together.within-line="always". | ||||
</action> | </action> | ||||
<action context="Code" dev="MM" type="fix"> | <action context="Code" dev="MM" type="fix"> |
import org.apache.fop.pdf.PDFObjectTestCase; | import org.apache.fop.pdf.PDFObjectTestCase; | ||||
import org.apache.fop.traits.BorderPropsTestCase; | import org.apache.fop.traits.BorderPropsTestCase; | ||||
import org.apache.fop.util.ColorUtilTestCase; | import org.apache.fop.util.ColorUtilTestCase; | ||||
import org.apache.fop.util.DataURIResolverTestCase; | |||||
import org.apache.fop.util.ElementListUtilsTestCase; | import org.apache.fop.util.ElementListUtilsTestCase; | ||||
import org.apache.fop.util.PDFNumberTestCase; | import org.apache.fop.util.PDFNumberTestCase; | ||||
import org.apache.fop.util.XMLResourceBundleTestCase; | import org.apache.fop.util.XMLResourceBundleTestCase; | ||||
suite.addTest(new TestSuite(ColorUtilTestCase.class)); | suite.addTest(new TestSuite(ColorUtilTestCase.class)); | ||||
suite.addTest(new TestSuite(BorderPropsTestCase.class)); | suite.addTest(new TestSuite(BorderPropsTestCase.class)); | ||||
suite.addTest(new TestSuite(ElementListUtilsTestCase.class)); | suite.addTest(new TestSuite(ElementListUtilsTestCase.class)); | ||||
suite.addTest(new TestSuite(DataURIResolverTestCase.class)); | |||||
suite.addTest(new TestSuite(BasicEventTestCase.class)); | suite.addTest(new TestSuite(BasicEventTestCase.class)); | ||||
suite.addTest(new TestSuite(XMLResourceBundleTestCase.class)); | suite.addTest(new TestSuite(XMLResourceBundleTestCase.class)); | ||||
//$JUnit-END$ | //$JUnit-END$ |
import javax.xml.transform.sax.SAXTransformerFactory; | import javax.xml.transform.sax.SAXTransformerFactory; | ||||
import javax.xml.transform.stream.StreamSource; | import javax.xml.transform.stream.StreamSource; | ||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.io.output.NullOutputStream; | import org.apache.commons.io.output.NullOutputStream; | ||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
private FopFactory fopFactory = FopFactory.newInstance(); | private FopFactory fopFactory = FopFactory.newInstance(); | ||||
private Templates replicatorTemplates; | private Templates replicatorTemplates; | ||||
private Stats stats; | |||||
public MemoryEater() throws TransformerConfigurationException, MalformedURLException { | public MemoryEater() throws TransformerConfigurationException, MalformedURLException { | ||||
File xsltFile = new File("test/xsl/fo-replicator.xsl"); | File xsltFile = new File("test/xsl/fo-replicator.xsl"); | ||||
Source xslt = new StreamSource(xsltFile); | Source xslt = new StreamSource(xsltFile); | ||||
replicatorTemplates = tFactory.newTemplates(xslt); | replicatorTemplates = tFactory.newTemplates(xslt); | ||||
} | } | ||||
private void eatMemory(File foFile, int replicatorRepeats) throws Exception { | |||||
private void eatMemory(File foFile, int runRepeats, int replicatorRepeats) throws Exception { | |||||
stats = new Stats(); | |||||
for (int i = 0; i < runRepeats; i++) { | |||||
eatMemory(i, foFile, replicatorRepeats); | |||||
stats.progress(i, runRepeats); | |||||
} | |||||
stats.dumpFinalStats(); | |||||
System.out.println(stats.getGoogleChartURL()); | |||||
} | |||||
private void eatMemory(int callIndex, File foFile, int replicatorRepeats) throws Exception { | |||||
Source src = new StreamSource(foFile); | Source src = new StreamSource(foFile); | ||||
Transformer transformer = replicatorTemplates.newTransformer(); | Transformer transformer = replicatorTemplates.newTransformer(); | ||||
transformer.setParameter("repeats", new Integer(replicatorRepeats)); | transformer.setParameter("repeats", new Integer(replicatorRepeats)); | ||||
OutputStream out = new NullOutputStream(); //write to /dev/nul | OutputStream out = new NullOutputStream(); //write to /dev/nul | ||||
FOUserAgent userAgent = fopFactory.newFOUserAgent(); | |||||
userAgent.setBaseURL(foFile.getParentFile().toURL().toExternalForm()); | |||||
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); | |||||
Result res = new SAXResult(fop.getDefaultHandler()); | |||||
transformer.transform(src, res); | |||||
try { | |||||
FOUserAgent userAgent = fopFactory.newFOUserAgent(); | |||||
userAgent.setBaseURL(foFile.getParentFile().toURL().toExternalForm()); | |||||
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); | |||||
Result res = new SAXResult(fop.getDefaultHandler()); | |||||
System.out.println("Generated " + fop.getResults().getPageCount() + " pages."); | |||||
transformer.transform(src, res); | |||||
stats.notifyPagesProduced(fop.getResults().getPageCount()); | |||||
if (callIndex == 0) { | |||||
System.out.println(foFile.getName() + " generates " | |||||
+ fop.getResults().getPageCount() + " pages."); | |||||
} | |||||
stats.checkStats(); | |||||
} finally { | |||||
IOUtils.closeQuietly(out); | |||||
} | |||||
} | } | ||||
private static void prompt() throws IOException { | private static void prompt() throws IOException { | ||||
long start = System.currentTimeMillis(); | long start = System.currentTimeMillis(); | ||||
MemoryEater app = new MemoryEater(); | MemoryEater app = new MemoryEater(); | ||||
for (int i = 0; i < runRepeats; i++) { | |||||
app.eatMemory(testFile, replicatorRepeats); | |||||
} | |||||
app.eatMemory(testFile, runRepeats, replicatorRepeats); | |||||
long duration = System.currentTimeMillis() - start; | long duration = System.currentTimeMillis() - start; | ||||
System.out.println("Success! Job took " + duration + " ms"); | System.out.println("Success! Job took " + duration + " ms"); |
/* | |||||
* 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.memory; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
class Stats { | |||||
private static final int INTERVAL = 2000; | |||||
private long startTime = System.currentTimeMillis(); | |||||
private long lastProgressDump = startTime; | |||||
private int pagesProduced; | |||||
private int totalPagesProduced; | |||||
private int step; | |||||
private int stepCount; | |||||
private List samples = new java.util.LinkedList(); | |||||
public void checkStats() { | |||||
long now = System.currentTimeMillis(); | |||||
if (now > lastProgressDump + INTERVAL) { | |||||
dumpStats(); | |||||
reset(); | |||||
} | |||||
} | |||||
public void notifyPagesProduced(int count) { | |||||
pagesProduced += count; | |||||
totalPagesProduced += count; | |||||
} | |||||
public void reset() { | |||||
pagesProduced = 0; | |||||
lastProgressDump = System.currentTimeMillis(); | |||||
} | |||||
public void dumpStats() { | |||||
long duration = System.currentTimeMillis() - lastProgressDump; | |||||
if (stepCount != 0) { | |||||
int progress = 100 * step / stepCount; | |||||
System.out.println("Progress: " + progress + "%, " + (stepCount - step) + " left"); | |||||
} | |||||
long ppm = 60000 * pagesProduced / duration; | |||||
System.out.println("Speed: " + ppm + "ppm"); | |||||
samples.add(new Sample((int)ppm)); | |||||
} | |||||
public void dumpFinalStats() { | |||||
long duration = System.currentTimeMillis() - startTime; | |||||
System.out.println("Final statistics"); | |||||
System.out.println("Pages produced: " +totalPagesProduced); | |||||
long ppm = 60000 * totalPagesProduced / duration; | |||||
System.out.println("Average speed: " + ppm + "ppm"); | |||||
} | |||||
public String getGoogleChartURL() { | |||||
StringBuffer sb = new StringBuffer("http://chart.apis.google.com/chart?"); | |||||
//http://chart.apis.google.com/chart?cht=ls&chd=t:60,40&chs=250x100&chl=Hello|World | |||||
sb.append("cht=ls"); | |||||
sb.append("&chd=t:"); | |||||
boolean first = true; | |||||
int maxY = 0; | |||||
Iterator iter = samples.iterator(); | |||||
while (iter.hasNext()) { | |||||
Sample sample = (Sample)iter.next(); | |||||
if (first) { | |||||
first = false; | |||||
} else { | |||||
sb.append(','); | |||||
} | |||||
sb.append(sample.ppm); | |||||
maxY = Math.max(maxY, sample.ppm); | |||||
} | |||||
int ceilY = ((maxY / 1000) + 1) * 1000; | |||||
sb.append("&chs=1000x300"); //image size | |||||
sb.append("&chds=0,").append(ceilY); //data scale | |||||
sb.append("&chg=0,20"); //scale steps | |||||
sb.append("&chxt=y"); | |||||
sb.append("&chxl=0:|0|" + ceilY); | |||||
return sb.toString(); | |||||
} | |||||
private static class Sample { | |||||
private int ppm; | |||||
public Sample(int ppm) { | |||||
this.ppm = ppm; | |||||
} | |||||
} | |||||
public void progress(int step, int stepCount) { | |||||
this.step = step; | |||||
this.stepCount = stepCount; | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.util; | |||||
import java.io.ByteArrayInputStream; | |||||
import javax.xml.transform.Source; | |||||
import javax.xml.transform.URIResolver; | |||||
import javax.xml.transform.stream.StreamSource; | |||||
import org.apache.commons.io.IOUtils; | |||||
import junit.framework.TestCase; | |||||
/** | |||||
* Test case for the RFC 2397 data URL/URI resolver. | |||||
*/ | |||||
public class DataURIResolverTestCase extends TestCase { | |||||
private static final byte[] TESTDATA = new byte[] {0, 1, 2, 3, 4, 5}; | |||||
/** | |||||
* Tests DataURLUtil. | |||||
* @throws Exception if an error occurs | |||||
*/ | |||||
public void testRFC2397Generator() throws Exception { | |||||
String url = DataURLUtil.createDataURL(new ByteArrayInputStream(TESTDATA), null); | |||||
assertEquals("Generated data URL is wrong", "data:;base64,AAECAwQF", url); | |||||
url = DataURLUtil.createDataURL(new ByteArrayInputStream(TESTDATA), "application/pdf"); | |||||
assertEquals("Generated data URL is wrong", "data:application/pdf;base64,AAECAwQF", url); | |||||
} | |||||
/** | |||||
* Test the URIResolver contract if the protocol doesn't match. Resolver must return null | |||||
* in this case. | |||||
* @throws Exception if an error occurs | |||||
*/ | |||||
public void testNonMatchingContract() throws Exception { | |||||
URIResolver resolver = new DataURIResolver(); | |||||
Source src; | |||||
src = resolver.resolve("http://xmlgraphics.apache.org/fop/index.html", null); | |||||
assertNull(src); | |||||
src = resolver.resolve("index.html", "http://xmlgraphics.apache.org/fop/"); | |||||
assertNull(src); | |||||
} | |||||
private static boolean byteCmp(byte[] src, int srcOffset, byte[] cmp) { | |||||
for (int i = 0, c = cmp.length; i < c; i++) { | |||||
if (src[srcOffset + i] != cmp[i]) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Test the DataURIResolver with correct values. | |||||
* @throws Exception if an error occurs | |||||
*/ | |||||
public void testDataURLHandling() throws Exception { | |||||
URIResolver resolver = new DataURIResolver(); | |||||
Source src; | |||||
src = resolver.resolve("data:;base64,AAECAwQF", null); | |||||
assertNotNull(src); | |||||
StreamSource streamSource = (StreamSource)src; | |||||
byte[] data = IOUtils.toByteArray(streamSource.getInputStream()); | |||||
assertTrue("Decoded data doesn't match the test data", byteCmp(TESTDATA, 0, data)); | |||||
src = resolver.resolve( | |||||
"data:application/octet-stream;interpreter=fop;base64,AAECAwQF", null); | |||||
assertNotNull(src); | |||||
streamSource = (StreamSource)src; | |||||
assertNotNull(streamSource.getInputStream()); | |||||
assertNull(streamSource.getReader()); | |||||
data = IOUtils.toByteArray(streamSource.getInputStream()); | |||||
assertTrue("Decoded data doesn't match the test data", byteCmp(TESTDATA, 0, data)); | |||||
src = resolver.resolve("data:,FOP", null); | |||||
assertNotNull(src); | |||||
streamSource = (StreamSource)src; | |||||
assertNull(streamSource.getInputStream()); | |||||
assertNotNull(streamSource.getReader()); | |||||
String text = IOUtils.toString(streamSource.getReader()); | |||||
assertEquals("FOP", text); | |||||
/* TODO Un-escaping of special URL chars like %20 hasn't been implemented, yet. | |||||
src = resolver.resolve("data:,A%20brief%20note", null); | |||||
assertNotNull(src); | |||||
streamSource = (StreamSource)src; | |||||
text = IOUtils.toString(streamSource.getReader()); | |||||
assertEquals("A brief note", text); | |||||
*/ | |||||
} | |||||
} |