diff options
106 files changed, 3386 insertions, 1099 deletions
@@ -358,12 +358,12 @@ list of possible build targets. </fileset> </eventResourceGenerator> <fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/> - <eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/render/afp/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/render/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}"> <include name="org/apache/fop/render/afp/**/*.java"/> </fileset> </eventResourceGenerator> - <fixcrlf file="${src.java.dir}/org/apache/fop/render/afp/AFPEventProducer.xml" tab="remove" tablength="2"/> + <fixcrlf file="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml" tab="remove" tablength="2"/> <eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/render/bitmap/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml"> <fileset dir="${src.java.dir}"> <include name="org/apache/fop/render/bitmap/**/*.java"/> @@ -608,7 +608,7 @@ list of possible build targets. <exclude name="org/apache/fop/render/pdf/PDFRenderer.class"/> <exclude name="org/apache/fop/render/pdf/PDFXMLHandler*"/> <include name="org/apache/fop/render/intermediate/IFDocumentHandlerConfigurator.class"/> - <include name="org/apache/fop/render/*RendererConfigurator**"/> + <include name="org/apache/fop/render/**Configurator**"/> <include name="org/apache/fop/util/AbstractPaintingState**"/> <include name="org/apache/fop/pdf/**"/> </patternset> diff --git a/examples/embedding/java/embedding/intermediate/ExampleConcat.java b/examples/embedding/java/embedding/intermediate/ExampleConcat.java index a5765f49b..6b168f968 100644 --- a/examples/embedding/java/embedding/intermediate/ExampleConcat.java +++ b/examples/embedding/java/embedding/intermediate/ExampleConcat.java @@ -92,7 +92,7 @@ public class ExampleConcat { //Create an instance of the target document handler so the IFSerializer //can use its font setup IFDocumentHandler targetHandler = userAgent.getRendererFactory().createDocumentHandler( - userAgent, MimeConstants.MIME_PDF + ";mode=painter"); + userAgent, MimeConstants.MIME_PDF); //Create the IFSerializer to write the intermediate format IFSerializer ifSerializer = new IFSerializer(); @@ -150,7 +150,7 @@ public class ExampleConcat { FOUserAgent userAgent = fopFactory.newFOUserAgent(); //Setup target handler - String mime = MimeConstants.MIME_PDF + ";mode=painter"; + String mime = MimeConstants.MIME_PDF; IFDocumentHandler targetHandler = fopFactory.getRendererFactory().createDocumentHandler( userAgent, mime); diff --git a/examples/embedding/java/embedding/intermediate/ExampleStamp.java b/examples/embedding/java/embedding/intermediate/ExampleStamp.java index 860a75401..c5d1a12c5 100644 --- a/examples/embedding/java/embedding/intermediate/ExampleStamp.java +++ b/examples/embedding/java/embedding/intermediate/ExampleStamp.java @@ -73,7 +73,7 @@ public class ExampleStamp { FOUserAgent userAgent = fopFactory.newFOUserAgent(); //Setup target handler - String mime = MimeConstants.MIME_PDF + ";mode=painter"; + String mime = MimeConstants.MIME_PDF; IFDocumentHandler targetHandler = fopFactory.getRendererFactory().createDocumentHandler( userAgent, mime); diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml index 08463b9b9..7fc0d97d9 100644 --- a/src/documentation/content/xdocs/trunk/configuration.xml +++ b/src/documentation/content/xdocs/trunk/configuration.xml @@ -152,6 +152,18 @@ <td>"height" 11 inches, "width" 8.26 inches</td> </tr> <tr> + <td>prefer-renderer</td> + <td>boolean (true, false)</td> + <td> + By default, FOP prefers the newer output implementations based on the + <code>IFDocumentHandler</code> interface. If no such implementation can be found for + a given MIME type, it looks for an implementation of the <code>Renderer</code> interface. + If necessary, you can invert the lookup order to prefer the Renderer variant over the + IFDocumentHandler variant by setting this value to true. + </td> + <td>false</td> + </tr> + <tr> <td>use-cache</td> <td>boolean (true, false)</td> <td>All fonts information that has been gathered as a result of "directory" diff --git a/src/documentation/content/xdocs/trunk/fonts.xml b/src/documentation/content/xdocs/trunk/fonts.xml index 429c86a51..9fa34552b 100644 --- a/src/documentation/content/xdocs/trunk/fonts.xml +++ b/src/documentation/content/xdocs/trunk/fonts.xml @@ -344,7 +344,7 @@ </warning> </section> <section id="truetype-collections-metrics"> - <title>TrueType Collections Font Metrics</title> + <title>TrueType Collections</title> <p>TrueType collections (.ttc files) contain more than one font. To create metrics files for these fonts, you must specify which font in the collection should be generated, by using the "-ttcname" option with the TTFReader.</p> <p>To get a list of the fonts in a collection, just start the TTFReader as if it were a normal TrueType file (without the -ttcname option). @@ -356,8 +356,11 @@ <p> Alternatively, the individual sub-fonts of a TrueType Collections can be selected using the "sub-font" attribute on the "font" element. That means that generating - an XML font metrics file for TrueType collections is not necessary anymore. + an XML font metrics file for TrueType collections is not necessary anymore. Example: </p> + <source><![CDATA[<font embed-url="gulim.ttc" sub-font="GulimChe"> + <font-triplet name="GulimChe" style="normal" weight="normal"/> +</font>]]></source> </section> <section id="register"> <title>Register Fonts with FOP</title> @@ -542,5 +545,13 @@ </ul> <p>Character-by-Character is NOT yet supported!</p> </section> + <section id="font-list"> + <title>Font List Command-Line Tool</title> + <p> + FOP contains a small command-line tool that lets you generate a list of all configured + fonts. Its class name is: <code>org.apache.fop.tools.fontlist.FontListMain</code>. + Run it with the "-?" parameter to get help for the various options. + </p> + </section> </body> </document> diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index 83e730de9..c2548b390 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -451,11 +451,35 @@ out = proc.getOutputStream();]]></source> </section> <section id="afp"> <title>AFP</title> - <warning>The AFP Renderer is a new addition (27-Apr-2006) to the sandbox and as such not yet fully tested or feature complete.</warning> <p> The FOP AFP Renderer deals with creating documents conforming to the IBM AFP document architecture also refered to as MO:DCA (Mixed Object Document Content Architecture). </p> + <p> + The mapping of XSL-FO elements to the major MO:DCA structures is as follows: + </p> + <table> + <tr> + <th>XSL-FO element</th> + <th>MO:DCA-P object</th> + </tr> + <tr> + <td>fo:root</td> + <td>Document</td> + </tr> + <tr> + <td>fo:page-sequence</td> + <td>Page Group</td> + </tr> + <tr> + <td>fo:simple-page-master</td> + <td>Page</td> + </tr> + </table> + <p> + FOP creates exactly one Document per Printfile with an optional Resource Group at the + beginning. FOP does not create document indices. + </p> <section id="afp-references"> <title>References</title> <ul> @@ -512,8 +536,8 @@ out = proc.getOutputStream();]]></source> <source><![CDATA[ <!-- This is an example of mapping actual IBM raster fonts / code pages to a FOP font --> <font> <!-- The afp-font element defines the IBM code page, the matching Java encoding and the - path to the font --> - <afp-font type="raster" codepage="T1V10500" encoding="Cp500" path="fonts/ibm"> + base URI for the font --> + <afp-font type="raster" codepage="T1V10500" encoding="Cp500" base-uri="fonts/ibm/"> <!-- For a raster font a separate element for each font size is required providing the font size and the corresponding IBM Character set name --> <afp-raster-font size="7" characterset="C0N20070"/> @@ -539,15 +563,24 @@ out = proc.getOutputStream();]]></source> However, the characterset definition is now required within the afp-font element.</p> <source><![CDATA[ <font> <afp-font type="outline" codepage="T1V10500" encoding="Cp500" characterset="CZH200 " - path="fonts/ibm" /> + base-uri="file:/fonts/ibm" /> <font-triplet name="sans-serif" style="normal" weight="normal"/> <font-triplet name="Helvetica" style="normal" weight="normal"/> <font-triplet name="any" style="normal" weight="normal"/> </font> ]]></source> + <p> + If "base-uri" is missing or a relative URI, the fonts are resolved relative to + the font base URI specified in the configuration (or on the FopFactory). + </p> + <note> + Previously, the location of the font files was given by the "path" attribute. This is still + supported for the time being, but you should move to using the more flexible "base-uri" + attribute so you can profit from the power of URI resolvers. + </note> <p>Experimentation has shown that the font metrics for the FOP built-in Base14 fonts are actually very similar to some of the IBM outline and raster fonts. In cases were the IBM font files are not - available the path attribute in the afp-font element can be replaced by a base14-font attribute + available the base-uri attribute in the afp-font element can be replaced by a base14-font attribute giving the name of the matching Base14 font. In this case the AFP Renderer will take the font metrics from the built-in font.</p> <source><![CDATA[ <!-- The following are examples of defining outline fonts based on FOP built-in @@ -687,7 +720,7 @@ out = proc.getOutputStream();]]></source> xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp"> ]]></source> <section id="afp-page-overlay"> - <title>Page Overlay Extension</title> + <title>Page Overlay (IPO) Extension</title> <p>The include-page-overlay extension element allows to define on a per simple-page-master basis a page overlay resource. Example:</p> <source><![CDATA[ <fo:layout-master-set> @@ -701,7 +734,7 @@ out = proc.getOutputStream();]]></source> must be known in the AFP processing environment.</p> </section> <section id="afp-page-segment"> - <title>Page Segment Extension</title> + <title>Page Segment (IPS) Extension</title> <p>The include-page-segment extension element allows to define resource substitution for fo:external-graphics elements. Example:</p> <source><![CDATA[ @@ -724,7 +757,7 @@ out = proc.getOutputStream();]]></source> </p> </section> <section id="afp-tag-logical-element"> - <title>Tag Logical Element Extension</title> + <title>Tag Logical Element (TLE) Extension</title> <p>The tag-logical-element extension element allows to injects TLEs into the AFP output stream. Example:</p> <source><![CDATA[ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" @@ -735,14 +768,21 @@ out = proc.getOutputStream();]]></source> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> + [..] + <fo:page-sequence master-reference="simple"> + <afp:tag-logical-element name="foo" value="bar"/> + <fo:flow flow-name="xsl-region-body"> + [..] ]]></source> - <p>The tag-logical-element extension element can only occur within a simple-page-master. - Multiple tag-logical-element extension elements within a simple-page-master are allowed. - The name and value attributes are mandatory. + <p> + The tag-logical-element extension element can appear within a simple-page-master + (page level) or it can appear as child of page-sequence (page group level). + Multiple tag-logical-element extension elements within a simple-page-master or + page-sequence are allowed. The name and value attributes are mandatory. </p> </section> <section id="afp-no-operation"> - <title>No Operation Extension</title> + <title>No Operation (NOP) Extension</title> <p>The no-operation extension provides the ability to carry up to 32K of comments or any other type of unarchitected data into the AFP output stream. Example:</p> <source><![CDATA[ @@ -759,6 +799,29 @@ out = proc.getOutputStream();]]></source> The name attribute is mandatory. </p> </section> + <section id="afp-invoke-medium-map"> + <title>Invoke Medium Map (IMM) Extension</title> + <p> + The invoke-medium-map extension allows to generate IMM fields (Invoke Medium Map) in the + generated AFP output. Example: + </p> + <source><![CDATA[ + <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" + xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp"> + [..] + <fo:page-sequence master-reference="normal"> + <afp:invoke-medium-map name="MYMAP"/> + <fo:flow flow-name="xsl-region-body"> + [..] +]]></source> + <p> + The invoke-medium-map element is allowed as child of fo:page-sequence (page group + level). It is NOT supported on document level (fo:root), yet. FOP also doesn't support + specifying medium maps inside XML (using BMM/EMM). It can only reference an existing + medium map by name. The medium map has to be constructed through different means and + available on the target platform. + </p> + </section> </section> <section id="afp-foreign-attributes"> <title>Foreign Attributes</title> diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd index 483935c16..93aacb62d 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd @@ -55,6 +55,9 @@ <xs:element name="page-sequence"> <xs:complexType> <xs:sequence> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> <xs:element ref="mf:page" minOccurs="1" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="id" type="xs:ID"/> diff --git a/src/documentation/skinconf.xml b/src/documentation/skinconf.xml index a6a5ac65f..1944ad856 100644 --- a/src/documentation/skinconf.xml +++ b/src/documentation/skinconf.xml @@ -91,7 +91,7 @@ which will be used to configure the chosen Forrest skin. <favicon-url></favicon-url> <!-- The following used to construct a copyright statement --> - <year>1999-2008</year> + <year>1999-2009</year> <vendor>The Apache Software Foundation.</vendor> <copyright-link>http://www.apache.org/licenses/</copyright-link> diff --git a/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory index b607a5e64..46760b291 100644 --- a/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory +++ b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory @@ -1,2 +1,2 @@ org.apache.fop.image.loader.batik.ImageLoaderFactorySVG
-org.apache.fop.image.loader.batik.ImageLoaderFactoryWMF
\ No newline at end of file +org.apache.fop.image.loader.batik.ImageLoaderFactoryWMF
diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index 78c06bedf..c44698a10 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -37,7 +37,6 @@ import org.apache.fop.afp.modca.Registry; import org.apache.fop.afp.modca.ResourceGroup; import org.apache.fop.afp.modca.ResourceObject; import org.apache.fop.afp.util.ResourceAccessor; -import org.apache.fop.afp.util.SimpleResourceAccessor; /** * Manages the creation and storage of document resources @@ -236,11 +235,11 @@ public class AFPResourceManager { /** * Creates an included resource object by loading the contained object from a file. * @param resourceName the name of the resource - * @param basePath the base path in which to look for the resource files + * @param accessor resource accessor to access the resource with * @param resourceObjectType the resource object type ({@link ResourceObject}.*) * @throws IOException if an I/O error occurs while loading the resource */ - public void createIncludedResource(String resourceName, String basePath, + public void createIncludedResource(String resourceName, ResourceAccessor accessor, byte resourceObjectType) throws IOException { AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE); URI uri; @@ -261,9 +260,6 @@ public class AFPResourceManager { if (log.isDebugEnabled()) { log.debug("Adding included resource: " + resourceName); } - //TODO This works with local filenames only. In the long term, this - //should work through FOP's URI resolver. - ResourceAccessor accessor = new SimpleResourceAccessor(basePath); IncludedResourceObject resourceContent = new IncludedResourceObject( resourceName, accessor, uri); diff --git a/src/java/org/apache/fop/afp/DataStream.java b/src/java/org/apache/fop/afp/DataStream.java index 492669311..b1ff96859 100644 --- a/src/java/org/apache/fop/afp/DataStream.java +++ b/src/java/org/apache/fop/afp/DataStream.java @@ -486,10 +486,10 @@ public class DataStream { * The tag value */ public void createTagLogicalElement(String name, String value) { - if (currentPageGroup != null) { - currentPageGroup.createTagLogicalElement(name, value); - } else { + if (currentPage != null) { currentPage.createTagLogicalElement(name, value, tleSequence++); + } else { + currentPageGroup.createTagLogicalElement(name, value); } } diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java b/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java index 670373fbf..66b3f5564 100644 --- a/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java +++ b/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java @@ -67,18 +67,14 @@ public class AFPFontCollection implements FontCollection { num++; } } - if (!fontInfo.hasFont("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)) { - eventProducer.warnMissingDefaultFont(this, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); - } - if (!fontInfo.hasFont("any", Font.STYLE_ITALIC, Font.WEIGHT_NORMAL)) { - eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_NORMAL); - } - if (!fontInfo.hasFont("any", Font.STYLE_NORMAL, Font.WEIGHT_BOLD)) { - eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_BOLD); - } - if (!fontInfo.hasFont("any", Font.STYLE_ITALIC, Font.WEIGHT_BOLD)) { - eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_BOLD); - } + checkDefaultFontAvailable(fontInfo, eventProducer, + Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); + checkDefaultFontAvailable(fontInfo, eventProducer, + Font.STYLE_ITALIC, Font.WEIGHT_NORMAL); + checkDefaultFontAvailable(fontInfo, eventProducer, + Font.STYLE_NORMAL, Font.WEIGHT_BOLD); + checkDefaultFontAvailable(fontInfo, eventProducer, + Font.STYLE_ITALIC, Font.WEIGHT_BOLD); } else { eventProducer.warnDefaultFontSetup(this); @@ -89,4 +85,11 @@ public class AFPFontCollection implements FontCollection { return num; } + private void checkDefaultFontAvailable(FontInfo fontInfo, AFPEventProducer eventProducer, + String style, int weight) { + if (!fontInfo.hasFont("any", style, weight)) { + eventProducer.warnMissingDefaultFont(this, style, weight); + } + } + } diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java b/src/java/org/apache/fop/afp/fonts/AFPFontReader.java index d1e8bd8db..4e6a03259 100644 --- a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java +++ b/src/java/org/apache/fop/afp/fonts/AFPFontReader.java @@ -19,21 +19,19 @@ package org.apache.fop.afp.fonts; -import java.io.File; import java.io.FileNotFoundException; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; import java.util.Map; -import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.AFPConstants; +import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.afp.util.StructuredFieldReader; /** @@ -58,7 +56,7 @@ public final class AFPFontReader { /** * Static logging instance */ - protected static final Log log = LogFactory.getLog("org.apache.xmlgraphics.afp.fonts"); + protected static final Log log = LogFactory.getLog(AFPFontReader.class); /** * Template used to convert lists to arrays. @@ -96,7 +94,7 @@ public final class AFPFontReader { /** * The collection of code pages */ - private final Map/*<String, Map<String, String>>*/ codePages + private final Map/*<String, Map<String, String>>*/ codePagesCache = new java.util.HashMap/*<String, Map<String, String>>*/(); /** @@ -108,65 +106,16 @@ public final class AFPFontReader { * * @throws IOException in the event that an I/O exception of some sort has occurred */ - private InputStream openInputStream(String path, String filename) throws IOException { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader == null) { - classLoader = AFPFontReader.class.getClassLoader(); - } - - URL url = classLoader.getResource(path); - - if (url == null) { - try { - File file = new File(path); - url = file.toURI().toURL(); - if (url == null) { - String msg = "file not found " + filename + " in classpath: " + path; - log.error(msg); - throw new FileNotFoundException(msg); - } - } catch (MalformedURLException ex) { - String msg = "file not found " + filename + " in classpath: " + path; - log.error(msg); - throw new FileNotFoundException(msg); - } - } - - File directory = FileUtils.toFile(url); - if (!directory.canRead()) { - String msg = "Failed to read directory " + url.getPath(); - log.error(msg); - throw new FileNotFoundException(msg); - } - - final String filterpattern = filename.trim(); - FilenameFilter filter = new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.startsWith(filterpattern); - } - }; - - File[] files = directory.listFiles(filter); - - if (files.length < 1) { - String msg = "file search for " + filename + " located " - + files.length + " files"; - log.error(msg); - throw new FileNotFoundException(msg); - } else if (files.length > 1) { - String msg = "file search for " + filename + " located " - + files.length + " files"; - log.warn(msg); - } - - InputStream inputStream = files[0].toURI().toURL().openStream(); - - if (inputStream == null) { - String msg = "AFPFontReader:: getInputStream():: file not found for " + filename; - log.error(msg); - throw new FileNotFoundException(msg); + private InputStream openInputStream(ResourceAccessor accessor, String filename) + throws IOException { + URI uri; + try { + uri = new URI(filename.trim()); + } catch (URISyntaxException e) { + throw new FileNotFoundException("Invalid filename: " + + filename + " (" + e.getMessage() + ")"); } - + InputStream inputStream = accessor.createInputStream(uri); return inputStream; } @@ -206,13 +155,14 @@ public final class AFPFontReader { * chracter global identifier. */ String codePageId = new String(characterSet.getCodePage()); - String path = characterSet.getPath(); + ResourceAccessor accessor = characterSet.getResourceAccessor(); - Map/*<String,String>*/ codePage = (Map/*<String,String>*/)codePages.get(codePageId); + Map/*<String,String>*/ codePage + = (Map/*<String,String>*/)codePagesCache.get(codePageId); if (codePage == null) { - codePage = loadCodePage(codePageId, characterSet.getEncoding(), path); - codePages.put(codePageId, codePage); + codePage = loadCodePage(codePageId, characterSet.getEncoding(), accessor); + codePagesCache.put(codePageId, codePage); } /** @@ -222,7 +172,7 @@ public final class AFPFontReader { */ final String characterSetName = characterSet.getName(); - inputStream = openInputStream(path, characterSetName); + inputStream = openInputStream(accessor, characterSetName); StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream); @@ -246,7 +196,8 @@ public final class AFPFontReader { } //process D3AC89 Font Position - processFontPosition(structuredFieldReader, characterSetOrientations, metricNormalizationFactor); + processFontPosition(structuredFieldReader, characterSetOrientations, + metricNormalizationFactor); //process D38C89 Font Index (per orientation) for (int i = 0; i < characterSetOrientations.length; i++) { @@ -274,17 +225,18 @@ public final class AFPFontReader { * the code page identifier * @param encoding * the encoding to use for the character decoding + * @param accessor the resource accessor * @returns a code page mapping */ private Map/*<String,String>*/ loadCodePage(String codePage, String encoding, - String path) throws IOException { + ResourceAccessor accessor) throws IOException { // Create the HashMap to store code page information Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/(); InputStream inputStream = null; try { - inputStream = openInputStream(path, codePage.trim()); + inputStream = openInputStream(accessor, codePage.trim()); StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream); byte[] data = structuredFieldReader.getNext(CHARACTER_TABLE_SF); diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSet.java b/src/java/org/apache/fop/afp/fonts/CharacterSet.java index 52bebca56..9573506b3 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSet.java @@ -19,20 +19,25 @@ package org.apache.fop.afp.fonts; +import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; +import java.nio.charset.UnsupportedCharsetException; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.AFPConstants; +import org.apache.fop.afp.util.ResourceAccessor; +import org.apache.fop.afp.util.SimpleResourceAccessor; import org.apache.fop.afp.util.StringUtils; /** @@ -79,7 +84,7 @@ public class CharacterSet { protected String name; /** The path to the installed fonts */ - protected String path; + private ResourceAccessor accessor; /** Indicator as to whether to metrics have been loaded */ private boolean isMetricsLoaded = false; @@ -98,8 +103,23 @@ public class CharacterSet { * @param encoding the encoding of the font * @param name the character set name * @param path the path to the installed afp fonts + * @deprecated Please use {@link #CharacterSet(String, String, String, URI)} instead. */ public CharacterSet(String codePage, String encoding, String name, String path) { + this(codePage, encoding, name, + new SimpleResourceAccessor(path != null ? new File(path) : null)); + } + + /** + * Constructor for the CharacterSetMetric object, the character set is used + * to load the font information from the actual AFP font. + * + * @param codePage the code page identifier + * @param encoding the encoding of the font + * @param name the character set name + * @param accessor the resource accessor to load resource with + */ + public CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) { if (name.length() > MAX_NAME_LEN) { String msg = "Character set name '" + name + "' must be a maximum of " + MAX_NAME_LEN + " characters"; @@ -114,9 +134,15 @@ public class CharacterSet { } this.codePage = codePage; this.encoding = encoding; - this.encoder = Charset.forName(encoding).newEncoder(); - this.encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); - this.path = path; + try { + this.encoder = Charset.forName(encoding).newEncoder(); + this.encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } catch (UnsupportedCharsetException uce) { + //No nio-capable encoder available + //This may happen with "Cp500" on Sun Java 1.4.2 + this.encoder = null; + } + this.accessor = accessor; this.characterSetOrientations = new java.util.HashMap(4); } @@ -195,12 +221,11 @@ public class CharacterSet { } /** - * Returns the path where the font resources are installed - * - * @return the path where the font resources are installed + * Returns the resource accessor to load the font resources with. + * @return the resource accessor to load the font resources with */ - public String getPath() { - return path; + public ResourceAccessor getResourceAccessor() { + return this.accessor; } /** @@ -321,7 +346,12 @@ public class CharacterSet { * @return true if the character is in the character set */ public boolean hasChar(char c) { - return encoder.canEncode(c); + if (encoder != null) { + return encoder.canEncode(c); + } else { + //Sun Java 1.4.2 compatibility + return true; + } } /** @@ -331,14 +361,26 @@ public class CharacterSet { * @throws CharacterCodingException if the encoding operation fails */ public byte[] encodeChars(CharSequence chars) throws CharacterCodingException { - ByteBuffer bb = encoder.encode(CharBuffer.wrap(chars)); - if (bb.hasArray()) { - return bb.array(); + if (encoder != null) { + ByteBuffer bb = encoder.encode(CharBuffer.wrap(chars)); + if (bb.hasArray()) { + return bb.array(); + } else { + bb.rewind(); + byte[] bytes = new byte[bb.remaining()]; + bb.get(bytes); + return bytes; + } } else { - bb.rewind(); - byte[] bytes = new byte[bb.remaining()]; - bb.get(bytes); - return bytes; + //Sun Java 1.4.2 compatibility + byte[] bytes; + try { + bytes = chars.toString().getBytes(this.encoding); + return bytes; + } catch (UnsupportedEncodingException uee) { + throw new UnsupportedOperationException( + "Unsupported encoding: " + uee.getMessage()); + } } } diff --git a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java index aec220b40..42950dc5b 100644 --- a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java @@ -19,6 +19,7 @@ package org.apache.fop.afp.fonts; +import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.fonts.Typeface; /** @@ -43,7 +44,7 @@ public class FopCharacterSet extends CharacterSet { String name, Typeface charSet) { - super(codePage, encoding, name, null); + super(codePage, encoding, name, (ResourceAccessor)null); this.charSet = charSet; } diff --git a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java index 55c8eab7d..97646542b 100644 --- a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java +++ b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java @@ -19,42 +19,68 @@ package org.apache.fop.afp.util; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.net.URL; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import org.apache.commons.io.IOUtils; + import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.fonts.FontManager; /** * Default implementation of the {@link ResourceAccessor} interface for use inside FOP. */ -public class DefaultFOPResourceAccessor implements ResourceAccessor { +public class DefaultFOPResourceAccessor extends SimpleResourceAccessor { private FOUserAgent userAgent; + private String categoryBaseURI; /** - * Main constructor. + * Constructor for resource to be accessed via the {@link FOUserAgent}. This contructor + * can take two base URIs: the category base URI is the one to use when differentiating between + * normal resources (ex. images) and font resources. So, if fonts need to be accessed, you can + * set the {@link FontManager}'s base URI instead of the one on the {@link FopFactory}. * @param userAgent the FO user agent + * @param categoryBaseURI the category base URI (may be null) + * @param baseURI the custom base URI to resolve relative URIs against (may be null) */ - public DefaultFOPResourceAccessor(FOUserAgent userAgent) { + public DefaultFOPResourceAccessor(FOUserAgent userAgent, String categoryBaseURI, URI baseURI) { + super(baseURI); this.userAgent = userAgent; + this.categoryBaseURI = categoryBaseURI; } /** {@inheritDoc} */ public InputStream createInputStream(URI uri) throws IOException { - Source src = userAgent.resolveURI(uri.toASCIIString()); + //Step 1: resolve against local base URI --> URI + URI resolved = resolveAgainstBase(uri); + + //Step 2: resolve against the user agent --> stream + Source src; + src = userAgent.resolveURI(resolved.toASCIIString(), this.categoryBaseURI); + if (src == null) { - return null; + throw new FileNotFoundException("Resource not found: " + uri.toASCIIString()); } else if (src instanceof StreamSource) { StreamSource ss = (StreamSource)src; InputStream in = ss.getInputStream(); - return in; - } else { - return null; + if (in != null) { + return in; + } + if (ss.getReader() != null) { + //Don't support reader, retry using system ID below + IOUtils.closeQuietly(ss.getReader()); + } } + URL url = new URL(src.getSystemId()); + return url.openStream(); } } diff --git a/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java b/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java index 51772c253..7a963928b 100644 --- a/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java +++ b/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java @@ -26,7 +26,8 @@ import java.net.URI; import java.net.URL; /** - * Simple implementation of the {@link ResourceAccessor} interface for access via files. + * Simple implementation of the {@link ResourceAccessor} interface for access relative to a + * base URI. */ public class SimpleResourceAccessor implements ResourceAccessor { @@ -34,23 +35,40 @@ public class SimpleResourceAccessor implements ResourceAccessor { /** * Creates a new simple resource accessor. - * @param basePath the base path to resolve relative URIs to + * @param baseURI the base URI to resolve relative URIs against (may be null) */ - public SimpleResourceAccessor(File basePath) { - this.baseURI = basePath.toURI(); + public SimpleResourceAccessor(URI baseURI) { + this.baseURI = baseURI; } /** * Creates a new simple resource accessor. - * @param basePath the base path to resolve relative URIs to + * @param baseDir the base directory to resolve relative filenames against (may be null) */ - public SimpleResourceAccessor(String basePath) { - this(new File(basePath)); + public SimpleResourceAccessor(File baseDir) { + this(baseDir != null ? baseDir.toURI() : null); + } + + /** + * Returns the base URI. + * @return the base URI (or null if no base URI was set) + */ + public URI getBaseURI() { + return this.baseURI; + } + + /** + * Resolve the given URI against the baseURI. + * @param uri the URI to resolve + * @return the resolved URI + */ + protected URI resolveAgainstBase(URI uri) { + return (getBaseURI() != null ? getBaseURI().resolve(uri) : uri); } /** {@inheritDoc} */ public InputStream createInputStream(URI uri) throws IOException { - URI resolved = this.baseURI.resolve(uri); + URI resolved = resolveAgainstBase(uri); URL url = resolved.toURL(); return url.openStream(); } diff --git a/src/java/org/apache/fop/apps/FOURIResolver.java b/src/java/org/apache/fop/apps/FOURIResolver.java index 58ae6e8e6..f96711d31 100644 --- a/src/java/org/apache/fop/apps/FOURIResolver.java +++ b/src/java/org/apache/fop/apps/FOURIResolver.java @@ -24,6 +24,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; @@ -32,8 +34,10 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; +import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.xmlgraphics.util.io.Base64EncodeStream; import org.apache.xmlgraphics.util.uri.CommonURIResolver; @@ -74,15 +78,31 @@ public class FOURIResolver implements javax.xml.transform.URIResolver { base += "/"; } File dir = new File(base); - try { - base = (dir.isDirectory() ? dir.toURI().toURL() : new URL(base)).toExternalForm(); - } catch (MalformedURLException mfue) { - if (throwExceptions) { - throw mfue; + if (dir.isDirectory()) { + return dir.toURI().toASCIIString(); + } else { + URI baseURI; + try { + baseURI = new URI(base); + String scheme = baseURI.getScheme(); + boolean directoryExists = true; + if ("file".equals(scheme)) { + dir = FileUtils.toFile(baseURI.toURL()); + directoryExists = dir.isDirectory(); + } + if (scheme == null || !directoryExists) { + String message = "base " + base + " is not a valid directory"; + if (throwExceptions) { + throw new MalformedURLException(message); + } + log.error(message); + } + return baseURI.toASCIIString(); + } catch (URISyntaxException e) { + //TODO not ideal: our base URLs are actually base URIs. + throw new MalformedURLException(e.getMessage()); } - log.error(mfue.getMessage()); } - return base; } /** diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index c0c14de62..64ab413dc 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -21,6 +21,7 @@ package org.apache.fop.apps; // Java import java.io.File; +import java.net.MalformedURLException; import java.util.Date; import java.util.Map; @@ -43,6 +44,7 @@ import org.apache.fop.events.EventListener; import org.apache.fop.events.FOPEventListenerProxy; import org.apache.fop.events.LoggingEventListener; import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fonts.FontManager; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererFactory; import org.apache.fop.render.XMLHandlerRegistry; @@ -84,9 +86,6 @@ public class FOUserAgent { */ private String base = null; - /** The base URL for all font URL resolutions. */ - private String fontBase = null; - /** A user settable URI Resolver */ private URIResolver uriResolver = null; @@ -97,6 +96,7 @@ public class FOUserAgent { private Renderer rendererOverride = null; private FOEventHandler foEventHandlerOverride = null; private boolean locatorEnabled = true; // true by default (for error messages). + private boolean conserveMemoryPolicy = false; private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); //TODO Verify that a byte array is the best solution here @@ -154,7 +154,6 @@ public class FOUserAgent { } this.factory = factory; setBaseURL(factory.getBaseURL()); - setFontBaseURL(factory.getFontManager().getFontBaseURL()); setTargetResolution(factory.getTargetResolution()); if (this.getRendererOptions().get("accessibility") == null) { this.rendererOptions.put("accessibility", Boolean.FALSE); @@ -351,11 +350,16 @@ public class FOUserAgent { } /** - * sets font base URL + * Sets font base URL. * @param fontBaseUrl font base URL + * @deprecated Use {@link FontManager#setFontBaseURL(String)} instead. */ public void setFontBaseURL(String fontBaseUrl) { - this.fontBase = fontBaseUrl; + try { + getFactory().getFontManager().setFontBaseURL(fontBaseUrl); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e.getMessage()); + } } /** @@ -490,8 +494,13 @@ public class FOUserAgent { // ---------------------------------------------- environment-level stuff // (convenience access to FopFactory methods) - /** @return the font base URL */ + /** + * Returns the font base URL. + * @return the font base URL + * @deprecated Use {@link FontManager#getFontBaseURL()} instead. This method is not used by FOP. + */ public String getFontBaseURL() { + String fontBase = getFactory().getFontManager().getFontBaseURL(); return fontBase != null ? fontBase : getBaseURL(); } @@ -623,6 +632,24 @@ public class FOUserAgent { } /** + * Check whether memory-conservation is enabled. + * + * @return true if FOP is to conserve as much as possible + */ + public boolean isConserveMemoryPolicyEnabled() { + return this.conserveMemoryPolicy; + } + + /** + * Control whether memory-conservation should be enabled + * + * @param conserveMemoryPolicy the cachingEnabled to set + */ + public void setConserveMemoryPolicy(boolean conserveMemoryPolicy) { + this.conserveMemoryPolicy = conserveMemoryPolicy; + } + + /** * Check if accessibility is enabled. * @return true if accessibility is enabled */ diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index cf22a1e56..1a5c3cfd4 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -278,9 +278,11 @@ public class FopFactory implements ImageContext { */ public Fop newFop(FOUserAgent userAgent) throws FOPException { if (userAgent.getRendererOverride() == null - && userAgent.getFOEventHandlerOverride() == null) { - throw new IllegalStateException("Either the overriding renderer or the overriding" - + " FOEventHandler must be set when this factory method is used!"); + && userAgent.getFOEventHandlerOverride() == null + && userAgent.getDocumentHandlerOverride() == null) { + throw new IllegalStateException("An overriding renderer," + + " FOEventHandler or IFDocumentHandler must be set on the user agent" + + " when this factory method is used!"); } return newFop(null, userAgent); } diff --git a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java index e6f1aff16..a8b964a5d 100644 --- a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java +++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java @@ -61,6 +61,8 @@ public class FopFactoryConfigurator { /** Defines the default target resolution (72dpi) for FOP */ public static final float DEFAULT_TARGET_RESOLUTION = 72.0f; //dpi + private static final String PREFER_RENDERER = "prefer-renderer"; + /** logger instance */ private final Log log = LogFactory.getLog(FopFactoryConfigurator.class); @@ -182,6 +184,16 @@ public class FopFactoryConfigurator { } } + // prefer Renderer over IFDocumentHandler + if (cfg.getChild(PREFER_RENDERER, false) != null) { + try { + factory.getRendererFactory().setRendererPreferred( + cfg.getChild(PREFER_RENDERER).getValueAsBoolean()); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + } + } + // configure font manager FontManager fontManager = factory.getFontManager(); FontManagerConfigurator fontManagerConfigurator = new FontManagerConfigurator(cfg); diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java index 128fc8ce9..298cf263b 100644 --- a/src/java/org/apache/fop/area/AreaTreeHandler.java +++ b/src/java/org/apache/fop/area/AreaTreeHandler.java @@ -124,7 +124,11 @@ public class AreaTreeHandler extends FOEventHandler { */ protected void setupModel(FOUserAgent userAgent, String outputFormat, OutputStream stream) throws FOPException { - this.model = new RenderPagesModel(userAgent, outputFormat, fontInfo, stream); + if (userAgent.isConserveMemoryPolicyEnabled()) { + this.model = new CachedRenderPagesModel(userAgent, outputFormat, fontInfo, stream); + } else { + this.model = new RenderPagesModel(userAgent, outputFormat, fontInfo, stream); + } } /** @@ -442,7 +446,6 @@ public class AreaTreeHandler extends FOEventHandler { /** * Default constructor - * @param areaTreeHandler area tree handler */ protected Statistics() { this.runtime = Runtime.getRuntime(); diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index 7269bdf10..bb7e47dd3 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -21,6 +21,8 @@ package org.apache.fop.area; import java.awt.Color; import java.awt.geom.Rectangle2D; +import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.CharBuffer; import java.util.List; import java.util.Map; @@ -41,6 +43,7 @@ import org.w3c.dom.Document; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; @@ -48,6 +51,7 @@ 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.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; @@ -68,6 +72,7 @@ import org.apache.fop.area.inline.SpaceArea; 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.events.ResourceEventProducer; import org.apache.fop.fo.Constants; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fo.expr.PropertyException; @@ -149,6 +154,7 @@ public class AreaTreeParser { private Stack delegateStack = new Stack(); private ContentHandler delegate; private DOMImplementation domImplementation; + private Locator locator; public Handler(AreaTreeModel treeModel, FOUserAgent userAgent, @@ -224,6 +230,15 @@ public class AreaTreeParser { } /** {@inheritDoc} */ + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + private Locator getLocator() { + return this.locator; + } + + /** {@inheritDoc} */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (delegate != null) { @@ -356,6 +371,7 @@ public class AreaTreeParser { pageSequence.setLanguage(lang); String country = attributes.getValue("country"); pageSequence.setCountry(country); + transferForeignObjects(attributes, pageSequence); areaStack.push(pageSequence); } } @@ -970,9 +986,16 @@ public class AreaTreeParser { this.currentPageViewport.addExtensionAttachment(attachment); } } else { + Object o = areaStack.peek(); + if (o instanceof AreaTreeObject && obj instanceof ExtensionAttachment) { + AreaTreeObject ato = (AreaTreeObject)o; + ExtensionAttachment attachment = (ExtensionAttachment)obj; + ato.addExtensionAttachment(attachment); + } else { log.warn("Don't know how to handle externally generated object: " + obj); } } + } private void setAreaAttributes(Attributes attributes, Area area) { area.setIPD(Integer.parseInt(attributes.getValue("ipd"))); @@ -1048,8 +1071,21 @@ public class AreaTreeParser { = userAgent.getImageSessionContext(); ImageInfo info = manager.getImageInfo(uri, sessionContext); bkg.setImageInfo(info); - } catch (Exception e) { - log.error("Background image not available: " + uri, e); + } catch (ImageException e) { + ResourceEventProducer eventProducer + = ResourceEventProducer.Provider.get( + this.userAgent.getEventBroadcaster()); + eventProducer.imageError(this, uri, e, getLocator()); + } catch (FileNotFoundException fnfe) { + ResourceEventProducer eventProducer + = ResourceEventProducer.Provider.get( + this.userAgent.getEventBroadcaster()); + eventProducer.imageNotFound(this, uri, fnfe, getLocator()); + } catch (IOException ioe) { + ResourceEventProducer eventProducer + = ResourceEventProducer.Provider.get( + this.userAgent.getEventBroadcaster()); + eventProducer.imageIOError(this, uri, ioe, getLocator()); } String repeat = attributes.getValue("bkg-repeat"); diff --git a/src/java/org/apache/fop/area/CachedRenderPagesModel.java b/src/java/org/apache/fop/area/CachedRenderPagesModel.java index f522b978b..73c50fa17 100644 --- a/src/java/org/apache/fop/area/CachedRenderPagesModel.java +++ b/src/java/org/apache/fop/area/CachedRenderPagesModel.java @@ -129,7 +129,7 @@ public class CachedRenderPagesModel extends RenderPagesModel { try { // save page to cache ObjectOutputStream tempstream; - String fname = "fop-page-" + page.toString() + ".ser"; + String fname = "fop-page-" + page.getPageIndex() + ".ser"; File tempFile = new File(baseDir, fname); tempFile.deleteOnExit(); tempstream = new ObjectOutputStream(new BufferedOutputStream( diff --git a/src/java/org/apache/fop/area/PageSequence.java b/src/java/org/apache/fop/area/PageSequence.java index 887fdb43d..8fd3cd571 100644 --- a/src/java/org/apache/fop/area/PageSequence.java +++ b/src/java/org/apache/fop/area/PageSequence.java @@ -24,7 +24,7 @@ import java.util.List; /** * Represents a page sequence in the area tree. */ -public class PageSequence { +public class PageSequence extends AreaTreeObject { private List pages = new java.util.ArrayList(); private LineArea title; diff --git a/src/java/org/apache/fop/area/inline/InlineArea.java b/src/java/org/apache/fop/area/inline/InlineArea.java index 6d5d9ca98..5106fd5bc 100644 --- a/src/java/org/apache/fop/area/inline/InlineArea.java +++ b/src/java/org/apache/fop/area/inline/InlineArea.java @@ -19,6 +19,8 @@ package org.apache.fop.area.inline; +import java.io.Serializable; + import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; import org.apache.fop.area.Trait; @@ -35,7 +37,7 @@ public class InlineArea extends Area { * that can be used in order to re-compute adjustments when a * page-number or a page-number-citation is resolved */ - protected class InlineAdjustingInfo { + protected class InlineAdjustingInfo implements Serializable { /** stretch of the inline area */ protected int availableStretch; /** shrink of the inline area */ diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 734cd9b98..cb6756476 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -110,6 +110,8 @@ public class CommandLineOptions { private Map renderingOptions = new java.util.HashMap(); /* target resolution (for the user agent) */ private int targetResolution = 0; + /* control memory-conservation policy */ + private boolean conserveMemoryPolicy = false; private FopFactory factory = FopFactory.newInstance(); private FOUserAgent foUserAgent; @@ -168,6 +170,7 @@ public class CommandLineOptions { } addXSLTParameter("fop-output-format", getOutputFormat()); addXSLTParameter("fop-version", Version.getVersion()); + foUserAgent.setConserveMemoryPolicy(conserveMemoryPolicy); } else { return false; } @@ -268,6 +271,8 @@ public class CommandLineOptions { setLogOption("debug", "debug"); } else if (args[i].equals("-r")) { factory.setStrictValidation(false); + } else if (args[i].equals("-conserve")) { + conserveMemoryPolicy = true; } else if (args[i].equals("-dpi")) { i = i + parseResolution(args, i); } else if (args[i].equals("-q") || args[i].equals("--quiet")) { @@ -1134,6 +1139,8 @@ public class CommandLineOptions { + " -a enables accessibility features (Tagged PDF etc., default off)\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" + + " -conserve Enable memory-conservation policy (trades memory-consumption for disk I/O)" + + " (Note: currently only influences whether the area tree is serialized.)" + " [INPUT] \n" + " infile xsl:fo input file (the same as the next) \n" + " (use '-' for infile to pipe input from stdin)\n" diff --git a/src/java/org/apache/fop/events/EventFormatter.xml b/src/java/org/apache/fop/events/EventFormatter.xml index 3b3b34e36..d26fbeb93 100644 --- a/src/java/org/apache/fop/events/EventFormatter.xml +++ b/src/java/org/apache/fop/events/EventFormatter.xml @@ -24,6 +24,8 @@ <message key="rule.childOfDeclarations">The element must be a child of fo:declarations.</message> <message key="rule.childOfSPMorDeclarations">The element must be a child of fo:declarations or fo:simple-page-master.</message> <message key="rule.childOfInstreamForeignObjectorExternalGraphic">The element must be a child of fo:instream-foreign-object or fo:external-graphic.</message> + <message key="rule.childOfPageSequence">The element must be a child of fo:page-sequence.</message> + <message key="rule.childOfPageSequenceOrSPM">The element must be a child of fo:page-sequence or fo:simple-page-master.</message> <message key="rule.wrapperInvalidChildForParent">An fo:wrapper is only permitted to have children that would be permitted for its parent.</message> <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message> <message key="org.apache.fop.fo.FOValidationEventProducer.nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message> diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 8bcaa1538..5f23502f3 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -784,27 +784,27 @@ public interface Constants { // compound property constants - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_BLOCK_PROGRESSION_DIRECTION = 1 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_CONDITIONALITY = 2 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_INLINE_PROGRESSION_DIRECTION = 3 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_LENGTH = 4 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_MAXIMUM = 5 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_MINIMUM = 6 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_OPTIMUM = 7 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_PRECEDENCE = 8 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_WITHIN_COLUMN = 9 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_WITHIN_LINE = 10 << COMPOUND_SHIFT; - /** Property constant for compund property */ + /** Property constant for compound property */ int CP_WITHIN_PAGE = 11 << COMPOUND_SHIFT; // Enumeration constants diff --git a/src/java/org/apache/fop/fo/flow/Leader.java b/src/java/org/apache/fop/fo/flow/Leader.java index d72ed1e63..c25fc8cab 100644 --- a/src/java/org/apache/fop/fo/flow/Leader.java +++ b/src/java/org/apache/fop/fo/flow/Leader.java @@ -73,6 +73,7 @@ public class Leader extends InlineLevel { leaderLength = pList.get(PR_LEADER_LENGTH).getLengthRange(); leaderPattern = pList.get(PR_LEADER_PATTERN).getEnum(); leaderPatternWidth = pList.get(PR_LEADER_PATTERN_WIDTH).getLength(); + ruleThickness = pList.get(PR_RULE_THICKNESS).getLength(); switch(leaderPattern) { case EN_SPACE: // use Space @@ -81,7 +82,6 @@ public class Leader extends InlineLevel { // the following properties only apply // for leader-pattern = "rule" ruleStyle = pList.get(PR_RULE_STYLE).getEnum(); - ruleThickness = pList.get(PR_RULE_THICKNESS).getLength(); break; case EN_DOTS: break; diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index d41ac86ab..26812166d 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -48,7 +48,7 @@ public class PageSequence extends AbstractPageSequence { // the set of flows includes StaticContent flows also /** Map of flows to their flow name (flow-name, Flow) */ - private Map flowMap; + private Map/*<String, Flow>*/ flowMap; /** * The currentSimplePageMaster is either the page master for the @@ -96,18 +96,18 @@ public class PageSequence extends AbstractPageSequence { /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); - flowMap = new java.util.HashMap(); + flowMap = new java.util.HashMap/*<String, Flow>*/(); - this.simplePageMaster = getRoot().getLayoutMasterSet().getSimplePageMaster(masterReference); - if (this.simplePageMaster == null) { + this.simplePageMaster + = getRoot().getLayoutMasterSet().getSimplePageMaster(masterReference); + if (simplePageMaster == null) { this.pageSequenceMaster - = getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference); - if (this.pageSequenceMaster == null) { + = getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference); + if (pageSequenceMaster == null) { getFOValidationEventProducer().masterNotFound(this, getName(), masterReference, getLocator()); } } - getFOEventHandler().startPageSequence(this); } @@ -170,7 +170,7 @@ public class PageSequence extends AbstractPageSequence { flowMap.put(((StaticContent)child).getFlowName(), child); break; default: - assert false; + super.addChildNode(child); } } diff --git a/src/java/org/apache/fop/fo/pagination/Region.java b/src/java/org/apache/fop/fo/pagination/Region.java index 87e9c4590..6f94418be 100644 --- a/src/java/org/apache/fop/fo/pagination/Region.java +++ b/src/java/org/apache/fop/fo/pagination/Region.java @@ -26,6 +26,7 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.Numeric; +import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; @@ -130,6 +131,24 @@ public abstract class Region extends FObj { || name.equals("xsl-footnote-separator")); } + /** + * Get the page-width context + * @param lengthBase the lengthBase to use for resolving percentages + * @return context for the width of the page-reference-area + */ + protected PercentBaseContext getPageWidthContext(int lengthBase) { + return layoutMaster.getPageWidthContext(lengthBase); + } + + /** + * Get the page-width context + * @param lengthBase the lengthBase to use for resolving percentages + * @return context for the width of the page-reference-area + */ + protected PercentBaseContext getPageHeightContext(int lengthBase) { + return layoutMaster.getPageHeightContext(lengthBase); + } + /** {@inheritDoc} */ public boolean generatesReferenceAreas() { return true; diff --git a/src/java/org/apache/fop/fo/pagination/RegionAfter.java b/src/java/org/apache/fop/fo/pagination/RegionAfter.java index 2189fa67b..841bff0d6 100644 --- a/src/java/org/apache/fop/fo/pagination/RegionAfter.java +++ b/src/java/org/apache/fop/fo/pagination/RegionAfter.java @@ -26,7 +26,7 @@ import java.awt.Rectangle; import org.apache.fop.fo.FONode; import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.LengthBase; -import org.apache.fop.datatypes.SimplePercentBaseContext; +import org.apache.fop.datatypes.PercentBaseContext; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_region-after"> @@ -48,25 +48,9 @@ public class RegionAfter extends RegionBA { /* Special rules apply to resolving extent as values are resolved relative * to the page size and reference orientation. */ - SimplePercentBaseContext pageWidthContext; - SimplePercentBaseContext pageHeightContext; - if (spm.getReferenceOrientation() % 180 == 0) { - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - } else { - // invert width and height since top left are rotated by 90 (cl or ccl) - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - } - SimplePercentBaseContext neighbourContext; + PercentBaseContext pageWidthContext = getPageWidthContext(LengthBase.CUSTOM_BASE); + PercentBaseContext pageHeightContext = getPageHeightContext(LengthBase.CUSTOM_BASE); + PercentBaseContext neighbourContext; Rectangle vpRect; if (spm.getWritingMode() == EN_LR_TB || spm.getWritingMode() == EN_RL_TB) { neighbourContext = pageWidthContext; diff --git a/src/java/org/apache/fop/fo/pagination/RegionBody.java b/src/java/org/apache/fop/fo/pagination/RegionBody.java index 43499678a..165bb4734 100644 --- a/src/java/org/apache/fop/fo/pagination/RegionBody.java +++ b/src/java/org/apache/fop/fo/pagination/RegionBody.java @@ -27,7 +27,7 @@ import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.LengthBase; import org.apache.fop.datatypes.Numeric; -import org.apache.fop.datatypes.SimplePercentBaseContext; +import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.properties.CommonMarginBlock; @@ -106,24 +106,8 @@ public class RegionBody extends Region { * Also the values are resolved relative to the page size * and reference orientation. */ - SimplePercentBaseContext pageWidthContext; - SimplePercentBaseContext pageHeightContext; - if (spm.getReferenceOrientation() % 180 == 0) { - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CONTAINING_BLOCK_WIDTH, - spm.getPageWidth().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CONTAINING_BLOCK_WIDTH, - spm.getPageHeight().getValue()); - } else { - // invert width and height since top left are rotated by 90 (cl or ccl) - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CONTAINING_BLOCK_WIDTH, - spm.getPageHeight().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CONTAINING_BLOCK_WIDTH, - spm.getPageWidth().getValue()); - } + PercentBaseContext pageWidthContext = getPageWidthContext(LengthBase.CONTAINING_BLOCK_WIDTH); + PercentBaseContext pageHeightContext = getPageHeightContext(LengthBase.CONTAINING_BLOCK_WIDTH); int start; int end; diff --git a/src/java/org/apache/fop/fo/pagination/RegionEnd.java b/src/java/org/apache/fop/fo/pagination/RegionEnd.java index 611b1edf6..8b348ed5d 100644 --- a/src/java/org/apache/fop/fo/pagination/RegionEnd.java +++ b/src/java/org/apache/fop/fo/pagination/RegionEnd.java @@ -26,7 +26,7 @@ import java.awt.Rectangle; import org.apache.fop.fo.FONode; import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.LengthBase; -import org.apache.fop.datatypes.SimplePercentBaseContext; +import org.apache.fop.datatypes.PercentBaseContext; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_region-end"> @@ -48,25 +48,9 @@ public class RegionEnd extends RegionSE { /* Special rules apply to resolving extent as values are resolved relative * to the page size and reference orientation. */ - SimplePercentBaseContext pageWidthContext; - SimplePercentBaseContext pageHeightContext; - if (spm.getReferenceOrientation() % 180 == 0) { - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - } else { - // invert width and height since top left are rotated by 90 (cl or ccl) - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - } - SimplePercentBaseContext neighbourContext; + PercentBaseContext pageWidthContext = getPageWidthContext(LengthBase.CUSTOM_BASE); + PercentBaseContext pageHeightContext = getPageHeightContext(LengthBase.CUSTOM_BASE); + PercentBaseContext neighbourContext; Rectangle vpRect; if (spm.getWritingMode() == EN_LR_TB || spm.getWritingMode() == EN_RL_TB) { neighbourContext = pageHeightContext; diff --git a/src/java/org/apache/fop/fo/pagination/RegionStart.java b/src/java/org/apache/fop/fo/pagination/RegionStart.java index 12b578634..afe9ddfe1 100644 --- a/src/java/org/apache/fop/fo/pagination/RegionStart.java +++ b/src/java/org/apache/fop/fo/pagination/RegionStart.java @@ -26,7 +26,7 @@ import java.awt.Rectangle; import org.apache.fop.fo.FONode; import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.LengthBase; -import org.apache.fop.datatypes.SimplePercentBaseContext; +import org.apache.fop.datatypes.PercentBaseContext; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_region-start"> @@ -48,25 +48,9 @@ public class RegionStart extends RegionSE { /* Special rules apply to resolving extent as values are resolved relative * to the page size and reference orientation. */ - SimplePercentBaseContext pageWidthContext; - SimplePercentBaseContext pageHeightContext; - if (spm.getReferenceOrientation() % 180 == 0) { - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - } else { - // invert width and height since top left are rotated by 90 (cl or ccl) - pageWidthContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageHeight().getValue()); - pageHeightContext = new SimplePercentBaseContext(null, - LengthBase.CUSTOM_BASE, - spm.getPageWidth().getValue()); - } - SimplePercentBaseContext neighbourContext; + PercentBaseContext pageWidthContext = getPageWidthContext(LengthBase.CUSTOM_BASE); + PercentBaseContext pageHeightContext = getPageHeightContext(LengthBase.CUSTOM_BASE); + PercentBaseContext neighbourContext; Rectangle vpRect; if (spm.getWritingMode() == EN_LR_TB || spm.getWritingMode() == EN_RL_TB) { neighbourContext = pageHeightContext; diff --git a/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java b/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java index b0ec96406..8c95e1b8a 100644 --- a/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java +++ b/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java @@ -29,6 +29,8 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.Numeric; +import org.apache.fop.datatypes.SimplePercentBaseContext; +import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; @@ -195,6 +197,44 @@ public class SimplePageMaster extends FObj { } /** + * Gets the context for the width of the page-reference-area, + * taking into account the reference-orientation. + * + * @param lengthBase the lengthBase to use to resolve percentages + * @return context for the width of the page-reference-area + */ + protected final PercentBaseContext getPageWidthContext(int lengthBase) { + return (this.referenceOrientation.getValue() % 180 == 0) + ? new SimplePercentBaseContext( + null, + lengthBase, + this.getPageWidth().getValue()) + : new SimplePercentBaseContext( + null, + lengthBase, + this.getPageHeight().getValue()); + } + + /** + * Gets the context for the height of the page-reference-area, + * taking into account the reference-orientation. + * + * @param lengthBase the lengthBase to use to resolve percentages + * @return the context for the height of the page-reference-area + */ + protected final PercentBaseContext getPageHeightContext(int lengthBase) { + return (this.referenceOrientation.getValue() % 180 == 0) + ? new SimplePercentBaseContext( + null, + lengthBase, + this.getPageHeight().getValue()) + : new SimplePercentBaseContext( + null, + lengthBase, + this.getPageWidth().getValue()); + } + + /** * Returns the region for a given region class. * @param regionId Constants ID of the FO representing the region * @return the region, null if it doesn't exist diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index eeafbb68b..8e3955cd8 100755 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -650,6 +650,78 @@ public class CommonBorderPaddingBackground { || borderInfo[START] != null || borderInfo[END] != null); } + /** + * Returns the "background-color" property. + * @return the "background-color" property. + */ + public Color getBackgroundColor() { + return backgroundColor; + } + + /** + * Returns the "background-attachment" property. + * @return the "background-attachment" property. + */ + public int getBackgroundAttachment() { + return backgroundAttachment; + } + + /** + * Returns the "background-image" property. + * @return the "background-image" property. + */ + public String getBackgroundImage() { + return backgroundImage; + } + + /** + * Returns the "background-repeat" property. + * @return the "background-repeat" property. + */ + public int getBackgroundRepeat() { + return backgroundRepeat; + } + + /** + * Returns the "background-position-horizontal" property. + * @return the "background-position-horizontal" property. + */ + public Length getBackgroundPositionHorizontal() { + return backgroundPositionHorizontal; + } + + /** + * Returns the "background-position-vertical" property. + * @return the "background-position-vertical" property. + */ + public Length getBackgroundPositionVertical() { + return backgroundPositionVertical; + } + + /** + * Returns the background image info + * @return the background image info + */ + public ImageInfo getBackgroundImageInfo() { + return backgroundImageInfo; + } + + /** + * Returns the border info + * @return the border info + */ + public BorderInfo[] getBorderInfo() { + return borderInfo; + } + + /** + * Returns the padding + * @return the padding + */ + public CondLengthProperty[] getPadding() { + return padding; + } + /** {@inheritDoc} */ public boolean equals(Object obj) { if (this == obj) { diff --git a/src/java/org/apache/fop/fo/properties/CommonFont.java b/src/java/org/apache/fop/fo/properties/CommonFont.java index 11275fdd3..55a2393fa 100755 --- a/src/java/org/apache/fop/fo/properties/CommonFont.java +++ b/src/java/org/apache/fop/fo/properties/CommonFont.java @@ -170,6 +170,16 @@ public final class CommonFont { return fontWeight.getEnum(); } + /** @return the "font-size" property. */ + public Length getFontSize() { + return fontSize; + } + + /** @return the "font-size-adjust" property. */ + public Numeric getFontSizeAdjust() { + return fontSizeAdjust; + } + /** * Create and return an array of <code>FontTriplets</code> based on * the properties stored in the instance variables. diff --git a/src/java/org/apache/fop/fonts/FontAdder.java b/src/java/org/apache/fop/fonts/FontAdder.java new file mode 100644 index 000000000..0d6a730cf --- /dev/null +++ b/src/java/org/apache/fop/fonts/FontAdder.java @@ -0,0 +1,72 @@ +/* + * 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.fonts; + +import java.net.URL; +import java.util.Iterator; +import java.util.List; + +import org.apache.fop.fonts.autodetect.FontInfoFinder; + +/** + * Adds a list of fonts to a given font info list + */ +public class FontAdder { + private FontEventListener listener; + private FontResolver resolver; + private FontManager manager; + + /** + * Main constructor + * @param manager a font manager + * @param resolver a font resolver + * @param listener a font event handler + */ + public FontAdder(FontManager manager, FontResolver resolver, FontEventListener listener) { + this.manager = manager; + this.resolver = resolver; + this.listener = listener; + } + + /** + * Iterates over font url list adding to font info list + * @param fontURLList font file list + * @param fontInfoList a configured font info list + */ + public void add(List/*<URL>*/ fontURLList, List/*<EmbedFontInfo>*/ fontInfoList) { + FontCache cache = manager.getFontCache(); + FontInfoFinder finder = new FontInfoFinder(); + finder.setEventListener(listener); + + for (Iterator iter = fontURLList.iterator(); iter.hasNext();) { + URL fontUrl = (URL)iter.next(); + EmbedFontInfo[] embedFontInfos = finder.find(fontUrl, resolver, cache); + if (embedFontInfos == null) { + continue; + } + for (int i = 0, c = embedFontInfos.length; i < c; i++) { + EmbedFontInfo fontInfo = embedFontInfos[i]; + if (fontInfo != null) { + fontInfoList.add(fontInfo); + } + } + } + } +} diff --git a/src/java/org/apache/fop/fonts/FontDetector.java b/src/java/org/apache/fop/fonts/FontDetector.java new file mode 100644 index 000000000..09671f1f8 --- /dev/null +++ b/src/java/org/apache/fop/fonts/FontDetector.java @@ -0,0 +1,101 @@ +/* + * 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.fonts; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOPException; +import org.apache.fop.fonts.autodetect.FontFileFinder; +import org.apache.fop.util.LogUtil; +import org.apache.xmlgraphics.util.ClasspathResource; + +/** + * Detector of operating system and classpath fonts + */ +public class FontDetector { + private static Log log = LogFactory.getLog(FontDetector.class); + + private static final String[] FONT_MIMETYPES = { + "application/x-font", "application/x-font-truetype" + }; + + private FontManager fontManager; + private FontAdder fontAdder; + private boolean strict; + + /** + * Main constructor + * @param manager the font manager + * @param adder the font adder + * @param strict true if an Exception should be thrown if an error is found. + */ + public FontDetector(FontManager manager, FontAdder adder, boolean strict) { + this.fontManager = manager; + this.fontAdder = adder; + this.strict = strict; + } + + /** + * Detect installed fonts on the system + * @param fontInfoList a list of fontinfo to populate + * @throws FOPException thrown if a problem occurred during detection + */ + public void detect(List/*<EmbedFontInfo>*/ fontInfoList) throws FOPException { + // search in font base if it is defined and + // is a directory but don't recurse + FontFileFinder fontFileFinder = new FontFileFinder(); + String fontBaseURL = fontManager.getFontBaseURL(); + if (fontBaseURL != null) { + try { + File fontBase = FileUtils.toFile(new URL(fontBaseURL)); + if (fontBase != null) { + List/*<URL>*/ fontURLList = fontFileFinder.find( + fontBase.getAbsolutePath()); + fontAdder.add(fontURLList, fontInfoList); + + //Can only use the font base URL if it's a file URL + } + } catch (IOException e) { + LogUtil.handleException(log, e, strict); + } + } + + // native o/s font directory finding + List/*<URL>*/ systemFontList; + try { + systemFontList = fontFileFinder.find(); + fontAdder.add(systemFontList, fontInfoList); + } catch (IOException e) { + LogUtil.handleException(log, e, strict); + } + + // classpath font finding + ClasspathResource resource = ClasspathResource.getInstance(); + for (int i = 0; i < FONT_MIMETYPES.length; i++) { + fontAdder.add(resource.listResourcesOfMimeType(FONT_MIMETYPES[i]), fontInfoList); + } + } +} diff --git a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java new file mode 100644 index 000000000..c97901163 --- /dev/null +++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java @@ -0,0 +1,313 @@ +/* + * 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.fonts; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOPException; +import org.apache.fop.fonts.autodetect.FontFileFinder; +import org.apache.fop.fonts.autodetect.FontInfoFinder; +import org.apache.fop.util.LogUtil; + +/** + * An abstract FontInfo configurator + */ +public class FontInfoConfigurator { + /** logger instance */ + protected static Log log = LogFactory.getLog(FontInfoConfigurator.class); + + private Configuration cfg; + private FontManager fontManager; + private FontResolver fontResolver; + private FontEventListener listener; + private boolean strict; + + /** + * Main constructor + * @param cfg the configuration object + * @param fontManager the font manager + * @param fontResolver the font resolver + * @param listener the font event listener + * @param strict true if an Exception should be thrown if an error is found. + */ + public FontInfoConfigurator(Configuration cfg, FontManager fontManager, + FontResolver fontResolver, FontEventListener listener, boolean strict) { + this.cfg = cfg; + this.fontManager = fontManager; + this.fontResolver = fontResolver; + this.listener = listener; + this.strict = strict; + } + + /** + * Initializes font info settings from the user configuration + * @param fontInfoList a font info list + * @throws FOPException if an exception occurs while processing the configuration + */ + public void configure(List/*<EmbedFontInfo>*/ fontInfoList) throws FOPException { + Configuration fonts = cfg.getChild("fonts", false); + if (fonts != null) { + long start = 0; + if (log.isDebugEnabled()) { + log.debug("Starting font configuration..."); + start = System.currentTimeMillis(); + } + + FontAdder fontAdder = new FontAdder(fontManager, fontResolver, listener); + + // native o/s search (autodetect) configuration + boolean autodetectFonts = (fonts.getChild("auto-detect", false) != null); + if (autodetectFonts) { + FontDetector fontDetector = new FontDetector(fontManager, fontAdder, strict); + fontDetector.detect(fontInfoList); + } + + // Add configured directories to FontInfo + addDirectories(fonts, fontAdder, fontInfoList); + + // Add configured fonts to FontInfo + FontCache fontCache = fontManager.getFontCache(); + addFonts(fonts, fontCache, fontInfoList); + + // Update referenced fonts (fonts which are not to be embedded) + fontManager.updateReferencedFonts(fontInfoList); + + // Update font cache if it has changed + if (fontCache != null && fontCache.hasChanged()) { + fontCache.save(); + } + + if (log.isDebugEnabled()) { + log.debug("Finished font configuration in " + + (System.currentTimeMillis() - start) + "ms"); + } + } + } + + private void addDirectories(Configuration fontsCfg, + FontAdder fontAdder, List/*<URL>*/ fontInfoList) throws FOPException { + // directory (multiple font) configuration + Configuration[] directories = fontsCfg.getChildren("directory"); + for (int i = 0; i < directories.length; i++) { + boolean recursive = directories[i].getAttributeAsBoolean("recursive", false); + String directory = null; + try { + directory = directories[i].getValue(); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + continue; + } + if (directory == null) { + LogUtil.handleException(log, + new FOPException("directory defined without value"), strict); + continue; + } + + // add fonts found in directory + FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1); + List/*<URL>*/ fontURLList; + try { + fontURLList = fontFileFinder.find(directory); + fontAdder.add(fontURLList, fontInfoList); + } catch (IOException e) { + LogUtil.handleException(log, e, strict); + } + } + } + + /** + * Populates the font info list from the fonts configuration + * @param fontsCfg a fonts configuration + * @param fontCache a font cache + * @param fontInfoList a font info list + * @throws FOPException if an exception occurs while processing the configuration + */ + protected void addFonts(Configuration fontsCfg, FontCache fontCache, + List/*<EmbedFontInfo>*/ fontInfoList) throws FOPException { + // font file (singular) configuration + Configuration[] font = fontsCfg.getChildren("font"); + for (int i = 0; i < font.length; i++) { + EmbedFontInfo embedFontInfo = getFontInfo( + font[i], fontCache); + if (embedFontInfo != null) { + fontInfoList.add(embedFontInfo); + } + } + } + + private static void closeSource(Source src) { + if (src instanceof StreamSource) { + StreamSource streamSource = (StreamSource)src; + IOUtils.closeQuietly(streamSource.getInputStream()); + IOUtils.closeQuietly(streamSource.getReader()); + } + } + + /** + * Returns a font info from a font node Configuration definition + * + * @param fontCfg Configuration object (font node) + * @param fontCache the font cache (or null if it is disabled) + * @return the embedded font info + * @throws FOPException if something's wrong with the config data + */ + protected EmbedFontInfo getFontInfo( + Configuration fontCfg, FontCache fontCache) + throws FOPException { + String metricsUrl = fontCfg.getAttribute("metrics-url", null); + String embedUrl = fontCfg.getAttribute("embed-url", null); + String subFont = fontCfg.getAttribute("sub-font", null); + + if (metricsUrl == null && embedUrl == null) { + LogUtil.handleError(log, + "Font configuration without metric-url or embed-url attribute", + strict); + return null; + } + if (strict) { + //This section just checks early whether the URIs can be resolved + //Stream are immediately closed again since they will never be used anyway + if (embedUrl != null) { + Source source = fontResolver.resolve(embedUrl); + closeSource(source); + if (source == null) { + LogUtil.handleError(log, + "Failed to resolve font with embed-url '" + embedUrl + "'", strict); + return null; + } + } + if (metricsUrl != null) { + Source source = fontResolver.resolve(metricsUrl); + closeSource(source); + if (source == null) { + LogUtil.handleError(log, + "Failed to resolve font with metric-url '" + metricsUrl + "'", strict); + return null; + } + } + } + + Configuration[] tripletCfg = fontCfg.getChildren("font-triplet"); + + // no font triplet info + if (tripletCfg.length == 0) { + LogUtil.handleError(log, "font without font-triplet", strict); + + File fontFile = FontCache.getFileFromUrls(new String[] {embedUrl, metricsUrl}); + URL fontUrl; + try { + fontUrl = fontFile.toURI().toURL(); + } catch (MalformedURLException e) { + // Should never happen + log.debug("Malformed Url: " + e.getMessage()); + return null; + } + if (fontFile != null) { + FontInfoFinder finder = new FontInfoFinder(); + finder.setEventListener(listener); + EmbedFontInfo[] infos = finder.find(fontUrl, fontResolver, fontCache); + return infos[0]; //When subFont is set, only one font is returned + } else { + return null; + } + } + + List/*<FontTriplet>*/ tripletList = new java.util.ArrayList/*<FontTriplet>*/(); + for (int j = 0; j < tripletCfg.length; j++) { + FontTriplet fontTriplet = getFontTriplet(tripletCfg[j]); + tripletList.add(fontTriplet); + } + + boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true); + EncodingMode encodingMode = EncodingMode.valueOf( + fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName())); + EmbedFontInfo embedFontInfo + = new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont); + embedFontInfo.setEncodingMode(encodingMode); + if (fontCache != null) { + if (!fontCache.containsFont(embedFontInfo)) { + fontCache.addFont(embedFontInfo); + } + } + + if (log.isDebugEnabled()) { + String embedFile = embedFontInfo.getEmbedFile(); + log.debug("Adding font " + (embedFile != null ? embedFile + ", " : "") + + "metric file " + embedFontInfo.getMetricsFile()); + for (int j = 0; j < tripletList.size(); ++j) { + FontTriplet triplet = (FontTriplet) tripletList.get(j); + log.debug(" Font triplet " + + triplet.getName() + ", " + + triplet.getStyle() + ", " + + triplet.getWeight()); + } + } + return embedFontInfo; + } + + /** + * Creates a new FontTriplet given a triple Configuration + * + * @param tripletCfg a triplet configuration + * @return a font triplet font key + * @throws FOPException thrown if a FOP exception occurs + */ + private FontTriplet getFontTriplet(Configuration tripletCfg) throws FOPException { + try { + String name = tripletCfg.getAttribute("name"); + if (name == null) { + LogUtil.handleError(log, "font-triplet without name", strict); + return null; + } + + String weightStr = tripletCfg.getAttribute("weight"); + if (weightStr == null) { + LogUtil.handleError(log, "font-triplet without weight", strict); + return null; + } + int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr)); + + String style = tripletCfg.getAttribute("style"); + if (style == null) { + LogUtil.handleError(log, "font-triplet without style", strict); + return null; + } else { + style = FontUtil.stripWhiteSpace(style); + } + return FontInfo.createFontKey(name, style, weight); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + } + return null; + } + +} diff --git a/src/java/org/apache/fop/fonts/FontManager.java b/src/java/org/apache/fop/fonts/FontManager.java index b3833cf50..daf0a0cdb 100644 --- a/src/java/org/apache/fop/fonts/FontManager.java +++ b/src/java/org/apache/fop/fonts/FontManager.java @@ -20,6 +20,8 @@ package org.apache.fop.fonts; import java.net.MalformedURLException; +import java.util.Iterator; +import java.util.List; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; @@ -186,4 +188,27 @@ public class FontManager { public Matcher getReferencedFontsMatcher() { return this.referencedFontsMatcher; } + + /** + * Updates the referenced font list + * @param fontInfoList a font info list + */ + public void updateReferencedFonts(List fontInfoList) { + Matcher matcher = getReferencedFontsMatcher(); + if (matcher == null) { + return; //No referenced fonts + } + Iterator iter = fontInfoList.iterator(); + while (iter.hasNext()) { + EmbedFontInfo fontInfo = (EmbedFontInfo)iter.next(); + Iterator triplets = fontInfo.getFontTriplets().iterator(); + while (triplets.hasNext()) { + FontTriplet triplet = (FontTriplet)triplets.next(); + if (matcher.matches(triplet)) { + fontInfo.setEmbedded(false); + break; + } + } + } + } } diff --git a/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java index 0d3bd6dcd..bd0f1ee14 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontFileFinder.java @@ -130,7 +130,7 @@ public class FontFileFinder extends DirectoryWalker implements FontFinder { * @throws IOException io exception * {@inheritDoc} */ - public List find() throws IOException { + public List/*<URL>*/ find() throws IOException { final FontFinder fontDirFinder; final String osName = System.getProperty("os.name"); if (osName.startsWith("Windows")) { @@ -142,8 +142,8 @@ public class FontFileFinder extends DirectoryWalker implements FontFinder { fontDirFinder = new UnixFontDirFinder(); } } - List fontDirs = fontDirFinder.find(); - List results = new java.util.ArrayList(); + List/*<URL>*/ fontDirs = fontDirFinder.find(); + List/*<URL>*/ results = new java.util.ArrayList/*<URL>*/(); for (Iterator iter = fontDirs.iterator(); iter.hasNext();) { final File dir = (File)iter.next(); super.walk(dir, results); diff --git a/src/java/org/apache/fop/fonts/autodetect/FontFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontFinder.java index ee0d1e07f..0ab71daec 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontFinder.java @@ -37,6 +37,6 @@ public interface FontFinder { * @throws IOException * In case of an I/O problem */ - List find() throws IOException; + List/*<URL>*/ find() throws IOException; } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index a5393ced2..8dca1c749 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -26,7 +26,9 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.area.Area; +import org.apache.fop.area.AreaTreeObject; import org.apache.fop.area.PageViewport; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; @@ -266,8 +268,10 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager childLMs = new java.util.ArrayList(10); } childLMs.add(lm); - log.trace(this.getClass().getName() - + ": Adding child LM " + lm.getClass().getName()); + if (log.isTraceEnabled()) { + log.trace(this.getClass().getName() + + ": Adding child LM " + lm.getClass().getName()); + } } /** @@ -300,6 +304,13 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager return pos; } + private void verifyNonNullPosition(Position pos) { + if (pos == null || pos.getIndex() < 0) { + throw new IllegalArgumentException( + "Only non-null Positions with an index can be checked"); + } + } + /** * Indicates whether the given Position is the first area-generating Position of this LM. * @param pos the Position (must be one with a position index) @@ -307,9 +318,7 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager */ public boolean isFirst(Position pos) { //log.trace("isFirst() smallestPosNumberChecked=" + smallestPosNumberChecked + " " + pos); - if (pos == null || pos.getIndex() < 0) { - throw new IllegalArgumentException("Only non-null Positions with an index can be checked"); - } + verifyNonNullPosition(pos); if (pos.getIndex() == this.smallestPosNumberChecked) { return true; } else if (pos.getIndex() < this.smallestPosNumberChecked) { @@ -326,10 +335,7 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager * @return True if it is the last Position */ public boolean isLast(Position pos) { - //log.trace("isLast() lastGenPos=" + lastGeneratedPosition + " " + pos); - if (pos == null || pos.getIndex() < 0) { - throw new IllegalArgumentException("Only non-null Positions with an index can be checked"); - } + verifyNonNullPosition(pos); return (pos.getIndex() == this.lastGeneratedPosition && isFinished()); } @@ -338,12 +344,32 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager * Transfers foreign attributes from the formatting object to the area. * @param targetArea the area to set the attributes on */ - protected void transferForeignAttributes(Area targetArea) { + protected void transferForeignAttributes(AreaTreeObject targetArea) { Map atts = fobj.getForeignAttributes(); targetArea.setForeignAttributes(atts); } /** + * Transfers extension attachments from the formatting object to the area. + * @param targetArea the area to set the extensions on + */ + protected void transferExtensionAttachments(AreaTreeObject targetArea) { + if (fobj.hasExtensionAttachments()) { + targetArea.setExtensionAttachments(fobj.getExtensionAttachments()); + } + } + + /** + * Transfers extensions (foreign attributes and extension attachments) from + * the formatting object to the area. + * @param targetArea the area to set the extensions on + */ + protected void transferExtensions(AreaTreeObject targetArea) { + transferForeignAttributes(targetArea); + transferExtensionAttachments(targetArea); + } + + /** * Registers the FO's markers on the current PageViewport * * @param isStarting boolean indicating whether the markers qualify as 'starting' diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 64b8bbc0d..5a44c8391 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -167,6 +167,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager addChildToArea(childArea, getCurrentArea()); } + /** {@inheritDoc} */ + protected void notifyEndOfLayout() { + super.notifyEndOfLayout(); + // Free memory of the area tree + //this.parentArea = null; + } + /** * Force current area to be added to parent area. */ @@ -340,18 +347,19 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager return returnList; } else { - if (prevLM != null) { - // there is a block handled by prevLM - // before the one handled by curLM - addInBetweenBreak(contentList, context, childLC); - } if (returnedList == null || returnedList.isEmpty()) { //Avoid NoSuchElementException below (happens with empty blocks) continue; } + if (prevLM != null + && !ElementListUtils.startsWithForcedBreak(returnedList)) { + // there is a block handled by prevLM before the one + // handled by curLM, and the one handled + // by the current LM does not begin with a break + addInBetweenBreak(contentList, context, childLC); + } contentList.addAll(returnedList); - if (((ListElement) ListUtil.getLast(returnedList)) - .isForcedBreak()) { + if (ElementListUtils.endsWithForcedBreak(returnedList)) { // a descendant of this block has break-after if (curLM.isFinished() && !hasNextChildLM()) { forcedBreakAfterLast = (BreakElement) ListUtil @@ -486,7 +494,7 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager // ? "penalty" : (lastElement.isGlue() ? "glue" : "box" ))); /*LF*/ //log.debug(" position e' " + lastElement.getPosition().getClass().getName()); /*LF*/ //log.debug(" " + (bpUnit > 0 ? "unit" : "")); - Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition(); + Position innerPosition = lastElement.getPosition().getPosition(); if (innerPosition == null && lastElement.isGlue()) { // this adjustment applies to space-before or space-after of this block @@ -536,7 +544,7 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> chiamata passata"); return ((BlockLevelLayoutManager)storedPenalty.getLayoutManager()) .negotiateBPDAdjustment(storedPenalty.getW(), - (KnuthElement)storedPenalty); + storedPenalty); } else { // the original penalty has width = 0 // the adjustment involves only the spaces before and after @@ -787,12 +795,12 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager returnList.add(new KnuthGlue(0, 0, 0, SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null), - (!spaceAfterIsConditional) ? false : true)); + spaceAfterIsConditional)); } else { returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0, SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null), - (!spaceAfterIsConditional) ? false : true)); + spaceAfterIsConditional)); } if (!spaceAfterIsConditional) { returnList.add(new KnuthBox(0, @@ -1201,8 +1209,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager totalLength.add(new MinOptMax(element.getW())); //log.debug("box " + element.getW()); } else if (element.isGlue()) { - totalLength.min -= ((KnuthGlue) element).getZ(); - totalLength.max += ((KnuthGlue) element).getY(); + totalLength.min -= element.getZ(); + totalLength.max += element.getY(); //leafValue = ((LeafPosition) element.getPosition()).getLeafPos(); //log.debug("glue " + element.getW() + " + " // + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ()); @@ -1239,10 +1247,10 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager lengthAfterBreak.subtract(new MinOptMax(element.getW())); bPrevIsBox = true; } else if (element.isGlue()) { - lengthBeforeBreak.min -= ((KnuthGlue) element).getZ(); - lengthAfterBreak.min += ((KnuthGlue) element).getZ(); - lengthBeforeBreak.max += ((KnuthGlue) element).getY(); - lengthAfterBreak.max -= ((KnuthGlue) element).getY(); + lengthBeforeBreak.min -= element.getZ(); + lengthAfterBreak.min += element.getZ(); + lengthBeforeBreak.max += element.getY(); + lengthAfterBreak.max -= element.getY(); bPrevIsBox = false; } else { lengthBeforeBreak.add(new MinOptMax(element.getW())); @@ -1250,7 +1258,7 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager } // create the new elements - if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE + if (element.isPenalty() && element.getP() < KnuthElement.INFINITE || element.isGlue() && bPrevIsBox || !oldListIterator.hasNext()) { // suppress elements after the breaking point @@ -1260,8 +1268,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager iStepsForward++; if (el.isGlue()) { // suppressed glue - lengthAfterBreak.min += ((KnuthGlue) el).getZ(); - lengthAfterBreak.max -= ((KnuthGlue) el).getY(); + lengthAfterBreak.min += el.getZ(); + lengthAfterBreak.max -= el.getY(); } else if (el.isPenalty()) { // suppressed penalty, do nothing } else { @@ -1281,8 +1289,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager for (int i = 0; i < iStepsForward; i++) { KnuthElement el = (KnuthElement) oldListIterator.previous(); if (el.isGlue()) { - lengthAfterBreak.min -= ((KnuthGlue) el).getZ(); - lengthAfterBreak.max += ((KnuthGlue) el).getY(); + lengthAfterBreak.min -= el.getZ(); + lengthAfterBreak.max += el.getY(); } } diff --git a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java index d7f854a42..d3403cd6b 100644 --- a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java +++ b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java @@ -194,6 +194,15 @@ public final class ElementListUtils { } /** + * Indicates whether the given element list starts with a forced break. + * @param elems the element list + * @return true if the list starts with a forced break + */ + public static boolean startsWithForcedBreak(List elems) { + return !elems.isEmpty() && ((ListElement) elems.get(0)).isForcedBreak(); + } + + /** * Indicates whether the given element list ends with a penalty with a non-infinite penalty * value. * @param elems the element list @@ -229,4 +238,4 @@ public final class ElementListUtils { return prevBreak; } -} +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java index 4dcb5e14c..d6c727f68 100644 --- a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java @@ -105,7 +105,9 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan Dimension intrinsicSize = info.getSize().getDimensionMpt(); ImageLayout layout = new ImageLayout(getExternalDocument(), this, intrinsicSize); - areaTreeHandler.getAreaTreeModel().startPageSequence(new PageSequence(null)); + PageSequence pageSequence = new PageSequence(null); + transferExtensions(pageSequence); + areaTreeHandler.getAreaTreeModel().startPageSequence(pageSequence); if (log.isDebugEnabled()) { log.debug("Starting layout"); } diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 245fa7235..dd23d2e85 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -88,8 +88,8 @@ public class FlowLayoutManager extends BlockStackingLayoutManager .getDisableColumnBalancing(); } else if (curLM instanceof BlockContainerLayoutManager) { span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); - disableColumnBalancing = ((BlockContainerLayoutManager) curLM) - .getBlockContainerFO().getDisableColumnBalancing(); + disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO() + .getDisableColumnBalancing(); } int currentSpan = context.getCurrentSpan(); @@ -130,24 +130,23 @@ public class FlowLayoutManager extends BlockStackingLayoutManager returnList.addAll(returnedList); SpaceResolver.resolveElementList(returnList); return returnList; - } else { - if (returnList.size() > 0) { + } else if (returnedList.size() > 0) { + if (returnList.size() > 0 + && !ElementListUtils.startsWithForcedBreak(returnedList)) { addInBetweenBreak(returnList, context, childLC); } - if (returnedList.size() > 0) { - returnList.addAll(returnedList); - if (ElementListUtils.endsWithForcedBreak(returnList)) { - if (curLM.isFinished() && !hasNextChildLM()) { - //If the layout manager is finished at this point, the pending - //marks become irrelevant. - childLC.clearPendingMarks(); - //setFinished(true); - break; - } - // a descendant of this flow has break-after - SpaceResolver.resolveElementList(returnList); - return returnList; + returnList.addAll(returnedList); + if (ElementListUtils.endsWithForcedBreak(returnList)) { + if (curLM.isFinished() && !hasNextChildLM()) { + //If the layout manager is finished at this point, the pending + //marks become irrelevant. + childLC.clearPendingMarks(); + //setFinished(true); + break; } + // a descendant of this flow has break-after + SpaceResolver.resolveElementList(returnList); + return returnList; } } diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java index 5dc1576c7..1137065ab 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -273,7 +273,8 @@ public class PageBreaker extends AbstractBreaker { //Get page break from which we restart PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(restartPoint - 1); - newStartPos = pbp.getLeafPos(); + //Set starting position to the first element *after* the page-break + newStartPos = pbp.getLeafPos() + 1; //Handle page break right here to avoid any side-effects if (newStartPos > 0) { handleBreakTrait(Constants.EN_PAGE); @@ -306,16 +307,15 @@ public class PageBreaker extends AbstractBreaker { 1, true, BreakingAlgorithm.ALL_BREAKS); AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount + " pageBreaks.size()= " + algRestart.getPageBreaks().size()); + //Make sure we only add the areas we haven't added already + effectiveList.ignoreAtStart = newStartPos; boolean replaceLastPage = iOptPageCount <= pslm.getCurrentPV().getBodyRegion().getColumnCount(); if (replaceLastPage) { //Replace last page pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum)); - //Make sure we only add the areas we haven't added already - effectiveList.ignoreAtStart = newStartPos; addAreas(algRestart, iOptPageCount, originalList, effectiveList); } else { - effectiveList.ignoreAtStart = newStartPos; addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList); //Add blank last page pageProvider.setLastPageIndex(currentPageNum + 1); @@ -430,8 +430,8 @@ public class PageBreaker extends AbstractBreaker { childLC); } // set the offset from the top margin - Footnote parentArea = (Footnote) pslm.getCurrentPV().getBodyRegion().getFootnote(); - int topOffset = (int) pslm.getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD(); + Footnote parentArea = pslm.getCurrentPV().getBodyRegion().getFootnote(); + int topOffset = pslm.getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD(); if (separatorArea != null) { topOffset -= separatorArea.getBPD(); } @@ -451,7 +451,7 @@ public class PageBreaker extends AbstractBreaker { /** {@inheritDoc} */ protected void observeElementList(List elementList) { ElementListObserver.observe(elementList, "breaker", - ((PageSequence)pslm.getFObj()).getId()); + pslm.getFObj().getId()); } /** diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index f9517554f..67c41ccf0 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -92,6 +92,7 @@ public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); org.apache.fop.area.PageSequence pageSequenceAreaObject = new org.apache.fop.area.PageSequence(title); + transferExtensions(pageSequenceAreaObject); pageSequenceAreaObject.setLanguage(getPageSequence().getLanguage()); pageSequenceAreaObject.setCountry(getPageSequence().getCountry()); areaTreeModel.startPageSequence(pageSequenceAreaObject); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java index f3bbc454a..fe82245b9 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java @@ -121,16 +121,15 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { = new org.apache.fop.area.inline.Leader(); leader.setRuleStyle(fobj.getRuleStyle()); leader.setRuleThickness(fobj.getRuleThickness().getValue(this)); - leader.setBPD(fobj.getRuleThickness().getValue(this)); leaderArea = leader; } else { leaderArea = new Space(); - leaderArea.setBPD(1); } + leaderArea.setBPD(fobj.getRuleThickness().getValue(this)); leaderArea.addTrait(Trait.COLOR, fobj.getColor()); } else if (fobj.getLeaderPattern() == EN_SPACE) { leaderArea = new Space(); - leaderArea.setBPD(1); + leaderArea.setBPD(fobj.getRuleThickness().getValue(this)); } else if (fobj.getLeaderPattern() == EN_DOTS) { TextArea t = new TextArea(); char dot = '.'; // userAgent.getLeaderDotCharacter(); @@ -198,7 +197,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { } else { //Content collapsed to nothing, so use a space leaderArea = new Space(); - leaderArea.setBPD(1); + leaderArea.setBPD(fobj.getRuleThickness().getValue(this)); } } TraitSetter.setProducerID(leaderArea, fobj.getId()); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index ba5e232e9..239a1a88e 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -201,8 +201,6 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager p.setP(0); } - notifyEndOfLayout(); - setFinished(true); return returnList; } @@ -427,6 +425,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager flush(); curBlockArea = null; + + notifyEndOfLayout(); } /** Adds background areas for the column, body and row, if any. */ diff --git a/src/java/org/apache/fop/pdf/AbstractPDFFontStream.java b/src/java/org/apache/fop/pdf/AbstractPDFFontStream.java index f3d005bd3..34c9c5dee 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFFontStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFFontStream.java @@ -33,9 +33,8 @@ public abstract class AbstractPDFFontStream extends AbstractPDFStream { } /** {@inheritDoc} */ - protected void setupFilterList() { - addDefaultFilter(PDFFilterList.FONT_FILTER); - super.setupFilterList(); + protected String getDefaultFilterName() { + return PDFFilterList.FONT_FILTER; } } diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java index fc853b512..4107c7e7a 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -47,21 +47,23 @@ public abstract class AbstractPDFStream extends PDFDictionary { * from outside. */ protected void setupFilterList() { - addDefaultFilter(PDFFilterList.DEFAULT_FILTER); + if (!getFilterList().isInitialized()) { + getFilterList().addDefaultFilters( + getDocumentSafely().getFilterMap(), + getDefaultFilterName()); + } prepareImplicitFilters(); getDocument().applyEncryption(this); } /** - * Adds the default filter to the filter list if the filter list hasn't been initialized, yet. - * @param filterName the name of the default filter to use + * Returns the name of a suitable filter for this PDF object. + * + * @return the default filter + * @see PDFFilterList */ - protected void addDefaultFilter(String filterName) { - if (!getFilterList().isInitialized()) { - getFilterList().addDefaultFilters( - getDocumentSafely().getFilterMap(), - filterName); - } + protected String getDefaultFilterName() { + return PDFFilterList.DEFAULT_FILTER; } /** diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java index a69d9e8de..9ec8f1595 100644 --- a/src/java/org/apache/fop/pdf/PDFImageXObject.java +++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java @@ -159,14 +159,11 @@ public class PDFImageXObject extends PDFXObject { } /** - * This sets up the default filters for XObjects. It uses the PDFImage - * instance to determine what default filters to apply. * {@inheritDoc} + * This class uses the PDFImage instance to determine the default filter. */ - protected void setupFilterList() { - addDefaultFilter(pdfimage.getFilterHint()); - super.setupFilterList(); + protected String getDefaultFilterName() { + return pdfimage.getFilterHint(); } - } diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java index 5008183ef..92796c0d1 100644 --- a/src/java/org/apache/fop/pdf/PDFMetadata.java +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -59,9 +59,8 @@ public class PDFMetadata extends PDFStream { } /** {@inheritDoc} */ - protected void setupFilterList() { - addDefaultFilter(PDFFilterList.METADATA_FILTER); - super.setupFilterList(); + protected String getDefaultFilterName() { + return PDFFilterList.METADATA_FILTER; } /** diff --git a/src/java/org/apache/fop/render/AbstractConfigurator.java b/src/java/org/apache/fop/render/AbstractConfigurator.java new file mode 100644 index 000000000..b1ac1c61a --- /dev/null +++ b/src/java/org/apache/fop/render/AbstractConfigurator.java @@ -0,0 +1,89 @@ +/* + * 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; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOUserAgent; + +/** + * An abstract configurator + */ +public abstract class AbstractConfigurator { + /** logger instance */ + protected static Log log = LogFactory.getLog(AbstractConfigurator.class); + + private static final String MIME = "mime"; + + /** fop factory configuration */ + protected FOUserAgent userAgent = null; + + /** + * Default constructor + * @param userAgent user agent + */ + public AbstractConfigurator(FOUserAgent userAgent) { + super(); + this.userAgent = userAgent; + } + + /** + * Returns the configuration subtree for a specific renderer. + * @param mimeType the MIME type of the renderer + * @return the requested configuration subtree, null if there's no configuration + */ + protected Configuration getConfig(String mimeType) { + Configuration cfg = userAgent.getFactory().getUserConfig(); + if (cfg == null) { + if (log.isDebugEnabled()) { + log.debug("userconfig is null"); + } + return null; + } + + Configuration userConfig = null; + + String type = getType(); + Configuration[] cfgs + = cfg.getChild(type + "s").getChildren(type); + for (int i = 0; i < cfgs.length; ++i) { + Configuration child = cfgs[i]; + try { + if (child.getAttribute(MIME).equals(mimeType)) { + userConfig = child; + break; + } + } catch (ConfigurationException e) { + // silently pass over configurations without mime type + } + } + log.debug((userConfig == null ? "No u" : "U") + + "ser configuration found for MIME type " + mimeType); + return userConfig; + } + + /** + * Returns the configurator type + * @return the configurator type + */ + public abstract String getType(); +} diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index d7df7596d..8f60ace2f 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -29,8 +29,11 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import org.w3c.dom.Document; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.area.Area; @@ -66,7 +69,6 @@ import org.apache.fop.area.inline.WordArea; import org.apache.fop.events.ResourceEventProducer; import org.apache.fop.fo.Constants; import org.apache.fop.fonts.FontInfo; -import org.w3c.dom.Document; /** * Abstract base class for all renderers. The Abstract renderer does all the @@ -648,7 +650,6 @@ public abstract class AbstractRenderer * @param space the space to render */ protected void renderInlineSpace(Space space) { - space.setBPD(0); renderInlineAreaBackAndBorders(space); // an inline space moves the inline progression position // for the current block by the width or height of the space @@ -831,15 +832,6 @@ public abstract class AbstractRenderer } /** - * Get the MIME type of the renderer. - * - * @return The MIME type of the renderer - */ - public String getMimeType() { - return null; - } - - /** * Converts a millipoint-based transformation matrix to points. * @param at a millipoint-based transformation matrix * @return a point-based transformation matrix diff --git a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java index 09540dfbb..c1a8bc182 100644 --- a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java +++ b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java @@ -20,9 +20,6 @@ package org.apache.fop.render; import org.apache.avalon.framework.configuration.Configuration; -import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.fop.apps.FOUserAgent; @@ -30,21 +27,16 @@ import org.apache.fop.apps.FOUserAgent; * Abstract base classes for renderer-related configurator classes. This class basically just * provides an accessor to the specific renderer configuration object. */ -public abstract class AbstractRendererConfigurator { - - /** logger instance */ - protected static Log log = LogFactory.getLog(AbstractRendererConfigurator.class); - - /** fop factory configuration */ - protected FOUserAgent userAgent = null; +public abstract class AbstractRendererConfigurator extends AbstractConfigurator { + private static final String TYPE = "renderer"; + /** * Default constructor * @param userAgent user agent */ public AbstractRendererConfigurator(FOUserAgent userAgent) { - super(); - this.userAgent = userAgent; + super(userAgent); } /** @@ -53,16 +45,8 @@ public abstract class AbstractRendererConfigurator { * @return the requested configuration subtree, null if there's no configuration */ protected Configuration getRendererConfig(Renderer renderer) { - String mimeType = renderer.getMimeType(); - if (mimeType == null) { - if (log.isInfoEnabled()) { - log.info("renderer mimeType is null"); + return super.getConfig(renderer.getMimeType()); } - return null; - } - - return getRendererConfig(mimeType); - } /** * Returns the configuration subtree for a specific renderer. @@ -70,31 +54,14 @@ public abstract class AbstractRendererConfigurator { * @return the requested configuration subtree, null if there's no configuration */ protected Configuration getRendererConfig(String mimeType) { - Configuration cfg = userAgent.getFactory().getUserConfig(); - if (cfg == null) { - if (log.isDebugEnabled()) { - log.debug("userconfig is null"); + return super.getConfig(mimeType); } - return null; + + /** + * {@inheritDoc} + */ + public String getType() { + return TYPE; } - Configuration userRendererConfig = null; - - Configuration[] cfgs - = cfg.getChild("renderers").getChildren("renderer"); - for (int i = 0; i < cfgs.length; ++i) { - Configuration child = cfgs[i]; - try { - if (child.getAttribute("mime").equals(mimeType)) { - userRendererConfig = child; - break; - } - } catch (ConfigurationException e) { - // silently pass over configurations without mime type - } - } - log.debug((userRendererConfig == null ? "No u" : "U") - + "ser configuration found for MIME type " + mimeType); - return userRendererConfig; - } } diff --git a/src/java/org/apache/fop/render/DefaultFontResolver.java b/src/java/org/apache/fop/render/DefaultFontResolver.java index 6fb7e0f01..a4960c665 100644 --- a/src/java/org/apache/fop/render/DefaultFontResolver.java +++ b/src/java/org/apache/fop/render/DefaultFontResolver.java @@ -41,7 +41,7 @@ public class DefaultFontResolver implements FontResolver { /** {@inheritDoc} */ public Source resolve(String href) { - return userAgent.resolveURI(href, userAgent.getFontBaseURL()); + return userAgent.resolveURI(href, userAgent.getFactory().getFontManager().getFontBaseURL()); } } diff --git a/src/java/org/apache/fop/render/PrintRendererConfigurator.java b/src/java/org/apache/fop/render/PrintRendererConfigurator.java index f1a76be1d..66618b093 100644 --- a/src/java/org/apache/fop/render/PrintRendererConfigurator.java +++ b/src/java/org/apache/fop/render/PrintRendererConfigurator.java @@ -19,46 +19,26 @@ package org.apache.fop.render; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; import java.util.List; -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; - import org.apache.avalon.framework.configuration.Configuration; -import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.xmlgraphics.util.ClasspathResource; - import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; import org.apache.fop.fonts.CustomFontCollection; -import org.apache.fop.fonts.EmbedFontInfo; -import org.apache.fop.fonts.EncodingMode; -import org.apache.fop.fonts.FontCache; import org.apache.fop.fonts.FontCollection; import org.apache.fop.fonts.FontEventAdapter; import org.apache.fop.fonts.FontEventListener; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontInfoConfigurator; import org.apache.fop.fonts.FontManager; import org.apache.fop.fonts.FontResolver; -import org.apache.fop.fonts.FontTriplet; -import org.apache.fop.fonts.FontUtil; -import org.apache.fop.fonts.autodetect.FontFileFinder; -import org.apache.fop.fonts.autodetect.FontInfoFinder; import org.apache.fop.fonts.base14.Base14FontCollection; import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; -import org.apache.fop.util.LogUtil; /** * Base Print renderer configurator (mostly handles font configuration) @@ -95,7 +75,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator FontEventListener listener = new FontEventAdapter( renderer.getUserAgent().getEventBroadcaster()); - List embedFontInfoList = buildFontList(cfg, fontResolver, listener); + List/*<EmbedFontInfo>*/ embedFontInfoList = buildFontList(cfg, fontResolver, listener); printRenderer.addFontList(embedFontInfoList); } @@ -107,7 +87,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator * @return the list of {@code EmbedFontInfo} objects * @throws FOPException if an error occurs while processing the configuration */ - protected List buildFontList(Configuration cfg, FontResolver fontResolver, + protected List/*<EmbedFontInfo>*/ buildFontList(Configuration cfg, FontResolver fontResolver, FontEventListener listener) throws FOPException { FopFactory factory = userAgent.getFactory(); FontManager fontManager = factory.getFontManager(); @@ -117,346 +97,15 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator } boolean strict = factory.validateUserConfigStrictly(); - FontCache fontCache = fontManager.getFontCache(); - - List/*<EmbedFontInfo>*/ embedFontInfoList = buildFontListFromConfiguration(cfg, - fontResolver, strict, fontManager, listener); - - if (fontCache != null && fontCache.hasChanged()) { - fontCache.save(); - } - return embedFontInfoList; - } - - /** - * Builds a list of EmbedFontInfo objects for use with the setup() method. - * - * @param cfg Configuration object - * @param fontResolver the FontResolver to use - * @param strict true if an Exception should be thrown if an error is found. - * @param fontManager the font manager - * @param listener a font event listener - * @return a List of EmbedFontInfo objects. - * @throws FOPException If an error occurs while processing the configuration - */ - public static List/*<EmbedFontInfo>*/ buildFontListFromConfiguration(Configuration cfg, - FontResolver fontResolver, - boolean strict, FontManager fontManager, - FontEventListener listener) throws FOPException { - FontCache fontCache = fontManager.getFontCache(); - String fontBaseURL = fontManager.getFontBaseURL(); - List/*<EmbedFontInfo>*/ fontInfoList - = new java.util.ArrayList/*<EmbedFontInfo>*/(); - - Configuration fonts = cfg.getChild("fonts", false); - if (fonts != null) { - long start = 0; - if (log.isDebugEnabled()) { - log.debug("Starting font configuration..."); - start = System.currentTimeMillis(); - } - - // native o/s search (autodetect) configuration - boolean autodetectFonts = (fonts.getChild("auto-detect", false) != null); - if (autodetectFonts) { - // search in font base if it is defined and - // is a directory but don't recurse - FontFileFinder fontFileFinder = new FontFileFinder(); - if (fontBaseURL != null) { - try { - File fontBase = FileUtils.toFile(new URL(fontBaseURL)); - if (fontBase != null) { - //Can only use the font base URL if it's a file URL - addFontInfoListFromFileList( - fontFileFinder.find(fontBase.getAbsolutePath()), - fontInfoList, - fontResolver, - fontCache, - listener - ); - } - } catch (IOException e) { - LogUtil.handleException(log, e, strict); - } - } - - // native o/s font directory finder - try { - addFontInfoListFromFileList( - fontFileFinder.find(), - fontInfoList, - fontResolver, - fontCache, - listener - ); - } catch (IOException e) { - LogUtil.handleException(log, e, strict); - } - - // load fonts from classpath - addFontInfoListFromFileList(ClasspathResource.getInstance() - .listResourcesOfMimeType("application/x-font"), - fontInfoList, fontResolver, fontCache, listener); - addFontInfoListFromFileList( - ClasspathResource.getInstance() - .listResourcesOfMimeType( - "application/x-font-truetype"), - fontInfoList, fontResolver, fontCache, listener); - } - - // directory (multiple font) configuration - Configuration[] directories = fonts.getChildren("directory"); - for (int i = 0; i < directories.length; i++) { - boolean recursive = directories[i].getAttributeAsBoolean("recursive", false); - String directory = null; - try { - directory = directories[i].getValue(); - } catch (ConfigurationException e) { - LogUtil.handleException(log, e, strict); - continue; - } - if (directory == null) { - LogUtil.handleException(log, - new FOPException("directory defined without value"), strict); - continue; - } - FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1); - try { - addFontInfoListFromFileList( - fontFileFinder.find(directory), - fontInfoList, - fontResolver, - fontCache, - listener - ); - } catch (IOException e) { - LogUtil.handleException(log, e, strict); - } - } - // font file (singular) configuration - Configuration[] font = fonts.getChildren("font"); - for (int i = 0; i < font.length; i++) { - EmbedFontInfo embedFontInfo = getFontInfoFromConfiguration( - font[i], fontResolver, strict, fontCache, listener); - if (embedFontInfo != null) { - fontInfoList.add(embedFontInfo); - } - } - - // Update referenced fonts (fonts which are not to be embedded) - updateReferencedFonts(fontInfoList, fontManager.getReferencedFontsMatcher()); - - if (log.isDebugEnabled()) { - log.debug("Finished font configuration in " - + (System.currentTimeMillis() - start) + "ms"); - } - } + //Read font configuration + FontInfoConfigurator fontInfoConfigurator + = new FontInfoConfigurator(cfg, fontManager, fontResolver, listener, strict); + List/*<EmbedFontInfo>*/ fontInfoList = new java.util.ArrayList/*<EmbedFontInfo>*/(); + fontInfoConfigurator.configure(fontInfoList); return fontInfoList; } - private static void updateReferencedFonts(List fontInfoList, FontTriplet.Matcher matcher) { - if (matcher == null) { - return; //No referenced fonts - } - Iterator iter = fontInfoList.iterator(); - while (iter.hasNext()) { - EmbedFontInfo fontInfo = (EmbedFontInfo)iter.next(); - Iterator triplets = fontInfo.getFontTriplets().iterator(); - while (triplets.hasNext()) { - FontTriplet triplet = (FontTriplet)triplets.next(); - if (matcher.matches(triplet)) { - fontInfo.setEmbedded(false); - break; - } - } - } - } - - - /** - * Iterates over font file list adding font info to list - * @param fontFileList font file list - * @param embedFontInfoList a configured font info list - * @param resolver font resolver - */ - private static void addFontInfoListFromFileList( - List fontFileList, List/*<EmbedFontInfo>*/ embedFontInfoList, - FontResolver resolver, FontCache fontCache, FontEventListener listener) { - for (Iterator iter = fontFileList.iterator(); iter.hasNext();) { - URL fontUrl = (URL)iter.next(); - // parse font to ascertain font info - FontInfoFinder finder = new FontInfoFinder(); - finder.setEventListener(listener); - //EmbedFontInfo fontInfo = finder.find(fontUrl, resolver, fontCache); - - //List<EmbedFontInfo> embedFontInfoList = finder.find(fontUrl, resolver, fontCache); - EmbedFontInfo[] embedFontInfos = finder.find(fontUrl, resolver, fontCache); - - if (embedFontInfos == null) { - continue; - } - - for (int i = 0, c = embedFontInfos.length; i < c; i++) { - EmbedFontInfo fontInfo = embedFontInfos[i]; - if (fontInfo != null) { - embedFontInfoList.add(fontInfo); - } - } - } - } - - private static void closeSource(Source src) { - if (src instanceof StreamSource) { - StreamSource streamSource = (StreamSource)src; - IOUtils.closeQuietly(streamSource.getInputStream()); - IOUtils.closeQuietly(streamSource.getReader()); - } - } - - /** - * Creates a new FontTriplet given a triple Configuration - * - * @param tripletCfg a triplet configuration - * @param strict use strict validation - * @return a font triplet font key - * @throws FOPException thrown if a FOP exception occurs - */ - private static FontTriplet getFontTripletFromConfiguration( - Configuration tripletCfg, boolean strict) throws FOPException { - try { - String name = tripletCfg.getAttribute("name"); - if (name == null) { - LogUtil.handleError(log, "font-triplet without name", strict); - return null; - } - - String weightStr = tripletCfg.getAttribute("weight"); - if (weightStr == null) { - LogUtil.handleError(log, "font-triplet without weight", strict); - return null; - } - int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr)); - - String style = tripletCfg.getAttribute("style"); - if (style == null) { - LogUtil.handleError(log, "font-triplet without style", strict); - return null; - } else { - style = FontUtil.stripWhiteSpace(style); - } - return FontInfo.createFontKey(name, style, weight); - } catch (ConfigurationException e) { - LogUtil.handleException(log, e, strict); - } - return null; - } - - /** - * Returns a font info from a font node Configuration definition - * - * @param fontCfg Configuration object (font node) - * @param fontResolver font resolver used to resolve font - * @param strict validate configuration strictly - * @param fontCache the font cache (or null if it is disabled) - * @return the embedded font info - * @throws FOPException if something's wrong with the config data - */ - private static EmbedFontInfo getFontInfoFromConfiguration( - Configuration fontCfg, FontResolver fontResolver, boolean strict, - FontCache fontCache, FontEventListener listener) - throws FOPException { - String metricsUrl = fontCfg.getAttribute("metrics-url", null); - String embedUrl = fontCfg.getAttribute("embed-url", null); - String subFont = fontCfg.getAttribute("sub-font", null); - - if (metricsUrl == null && embedUrl == null) { - LogUtil.handleError(log, - "Font configuration without metric-url or embed-url attribute", - strict); - return null; - } - if (strict) { - //This section just checks early whether the URIs can be resolved - //Stream are immediately closed again since they will never be used anyway - if (embedUrl != null) { - Source source = fontResolver.resolve(embedUrl); - closeSource(source); - if (source == null) { - LogUtil.handleError(log, - "Failed to resolve font with embed-url '" + embedUrl + "'", strict); - return null; - } - } - if (metricsUrl != null) { - Source source = fontResolver.resolve(metricsUrl); - closeSource(source); - if (source == null) { - LogUtil.handleError(log, - "Failed to resolve font with metric-url '" + metricsUrl + "'", strict); - return null; - } - } - } - - Configuration[] tripletCfg = fontCfg.getChildren("font-triplet"); - - // no font triplet info - if (tripletCfg.length == 0) { - LogUtil.handleError(log, "font without font-triplet", strict); - - File fontFile = FontCache.getFileFromUrls(new String[] {embedUrl, metricsUrl}); - URL fontUrl; - try { - fontUrl = fontFile.toURI().toURL(); - } catch (MalformedURLException e) { - // Should never happen - log.debug("Malformed Url: " + e.getMessage()); - return null; - } - if (fontFile != null) { - FontInfoFinder finder = new FontInfoFinder(); - finder.setEventListener(listener); - EmbedFontInfo[] infos = finder.find(fontUrl, fontResolver, fontCache); - return infos[0]; //When subFont is set, only one font is returned - } else { - return null; - } - } - - List/*<FontTriplet>*/ tripletList = new java.util.ArrayList/*<FontTriplet>*/(); - for (int j = 0; j < tripletCfg.length; j++) { - FontTriplet fontTriplet = getFontTripletFromConfiguration(tripletCfg[j], strict); - tripletList.add(fontTriplet); - } - - boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true); - EncodingMode encodingMode = EncodingMode.valueOf( - fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName())); - EmbedFontInfo embedFontInfo - = new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont); - embedFontInfo.setEncodingMode(encodingMode); - if (fontCache != null) { - if (!fontCache.containsFont(embedFontInfo)) { - fontCache.addFont(embedFontInfo); - } - } - - if (log.isDebugEnabled()) { - String embedFile = embedFontInfo.getEmbedFile(); - log.debug("Adding font " + (embedFile != null ? embedFile + ", " : "") - + "metric file " + embedFontInfo.getMetricsFile()); - for (int j = 0; j < tripletList.size(); ++j) { - FontTriplet triplet = (FontTriplet) tripletList.get(j); - log.debug(" Font triplet " - + triplet.getName() + ", " - + triplet.getStyle() + ", " - + triplet.getWeight()); - } - } - return embedFontInfo; - } - // ---=== IFDocumentHandler configuration ===--- /** {@inheritDoc} */ @@ -485,5 +134,4 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator new FontCollection[fontCollections.size()])); documentHandler.setFontInfo(fontInfo); } - } diff --git a/src/java/org/apache/fop/render/RendererFactory.java b/src/java/org/apache/fop/render/RendererFactory.java index b12664818..3832a0714 100644 --- a/src/java/org/apache/fop/render/RendererFactory.java +++ b/src/java/org/apache/fop/render/RendererFactory.java @@ -51,6 +51,8 @@ public class RendererFactory { private Map eventHandlerMakerMapping = new java.util.HashMap(); private Map documentHandlerMakerMapping = new java.util.HashMap(); + private boolean rendererPreferred = false; + /** * Main constructor. */ @@ -61,6 +63,26 @@ public class RendererFactory { } /** + * Controls whether a {@link Renderer} is preferred over a {@link IFDocumentHandler} if + * both are available for the same MIME type. + * @param value true to prefer the {@link Renderer}, + * false to prefer the {@link IFDocumentHandler}. + */ + public void setRendererPreferred(boolean value) { + this.rendererPreferred = value; + } + + /** + * Indicates whether a {@link Renderer} is preferred over a {@link IFDocumentHandler} if + * both are available for the same MIME type. + * @return true if the {@link Renderer} is preferred, + * false if the {@link IFDocumentHandler} is preferred. + */ + public boolean isRendererPreferred() { + return this.rendererPreferred; + } + + /** * Add a new RendererMaker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param maker the RendererMaker @@ -236,27 +258,54 @@ public class RendererFactory { } else if (userAgent.getRendererOverride() != null) { return userAgent.getRendererOverride(); } else { - AbstractRendererMaker maker = getRendererMaker(outputFormat); - if (maker != null) { - Renderer rend = maker.makeRenderer(userAgent); - rend.setUserAgent(userAgent); - RendererConfigurator configurator = maker.getConfigurator(userAgent); - if (configurator != null) { - configurator.configure(rend); + Renderer renderer; + if (isRendererPreferred()) { + //Try renderer first + renderer = tryRendererMaker(userAgent, outputFormat); + if (renderer == null) { + renderer = tryIFDocumentHandlerMaker(userAgent, outputFormat); } - return rend; } else { - AbstractIFDocumentHandlerMaker documentHandlerMaker - = getDocumentHandlerMaker(outputFormat); - if (documentHandlerMaker != null) { - IFDocumentHandler documentHandler = createDocumentHandler( - userAgent, outputFormat); - return createRendererForDocumentHandler(documentHandler); - } else { - throw new UnsupportedOperationException( - "No renderer for the requested format available: " + outputFormat); + //Try document handler first + renderer = tryIFDocumentHandlerMaker(userAgent, outputFormat); + if (renderer == null) { + renderer = tryRendererMaker(userAgent, outputFormat); } } + if (renderer == null) { + throw new UnsupportedOperationException( + "No renderer for the requested format available: " + outputFormat); + } + return renderer; + } + } + + private Renderer tryIFDocumentHandlerMaker(FOUserAgent userAgent, String outputFormat) + throws FOPException { + AbstractIFDocumentHandlerMaker documentHandlerMaker + = getDocumentHandlerMaker(outputFormat); + if (documentHandlerMaker != null) { + IFDocumentHandler documentHandler = createDocumentHandler( + userAgent, outputFormat); + return createRendererForDocumentHandler(documentHandler); + } else { + return null; + } + } + + private Renderer tryRendererMaker(FOUserAgent userAgent, String outputFormat) + throws FOPException { + AbstractRendererMaker maker = getRendererMaker(outputFormat); + if (maker != null) { + Renderer rend = maker.makeRenderer(userAgent); + rend.setUserAgent(userAgent); + RendererConfigurator configurator = maker.getConfigurator(userAgent); + if (configurator != null) { + configurator.configure(rend); + } + return rend; + } else { + return null; } } @@ -327,6 +376,9 @@ public class RendererFactory { */ public IFDocumentHandler createDocumentHandler(FOUserAgent userAgent, String outputFormat) throws FOPException { + if (userAgent.getDocumentHandlerOverride() != null) { + return userAgent.getDocumentHandlerOverride(); + } AbstractIFDocumentHandlerMaker maker = getDocumentHandlerMaker(outputFormat); if (maker == null) { throw new UnsupportedOperationException( diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 8391422ff..8d29145cf 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -38,9 +38,9 @@ import org.apache.fop.fonts.FontEventAdapter; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontManager; import org.apache.fop.render.afp.extensions.AFPElementMapping; +import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap; import org.apache.fop.render.afp.extensions.AFPPageSetup; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; -import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFPainter; @@ -70,7 +70,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler private Map/*<String,String>*/pageSegmentMap = new java.util.HashMap/*<String,String>*/(); - private boolean inPageHeader; + private static final int LOC_ELSEWHERE = 0; + private static final int LOC_FOLLOWING_PAGE_SEQUENCE = 1; + private static final int LOC_IN_PAGE_HEADER = 2; + + private int location = LOC_ELSEWHERE; /** * Default constructor. @@ -92,11 +96,6 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** {@inheritDoc} */ - public void setContext(IFContext context) { - super.setContext(context); - } - - /** {@inheritDoc} */ public IFDocumentHandlerConfigurator getConfigurator() { return new AFPRendererConfigurator(getUserAgent()); } @@ -164,6 +163,7 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } catch (IOException ioe) { throw new IFException("I/O error in startPageSequence()", ioe); } + this.location = LOC_FOLLOWING_PAGE_SEQUENCE; } /** {@inheritDoc} */ @@ -186,6 +186,7 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler /** {@inheritDoc} */ public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException { + this.location = LOC_ELSEWHERE; paintingState.clear(); pageSegmentMap.clear(); @@ -208,12 +209,12 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler /** {@inheritDoc} */ public void startPageHeader() throws IFException { super.startPageHeader(); - this.inPageHeader = true; + this.location = LOC_IN_PAGE_HEADER; } /** {@inheritDoc} */ public void endPageHeader() throws IFException { - this.inPageHeader = false; + this.location = LOC_ELSEWHERE; super.endPageHeader(); } @@ -244,30 +245,49 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler public void handleExtensionObject(Object extension) throws IFException { if (extension instanceof AFPPageSetup) { AFPPageSetup aps = (AFPPageSetup)extension; - if (!inPageHeader) { - throw new IFException( - "AFP page setup extension encountered outside the page header: " + aps, null); - } String element = aps.getElementName(); - if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) { - String overlay = aps.getName(); - if (overlay != null) { - dataStream.createIncludePageOverlay(overlay); + if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) { + if (this.location != LOC_IN_PAGE_HEADER + && this.location != LOC_FOLLOWING_PAGE_SEQUENCE) { + throw new IFException( + "TLE extension must be in the page header or between page-sequence" + + " and the first page: " + aps, null); } - } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) { - String name = aps.getName(); - String source = aps.getValue(); - pageSegmentMap.put(source, name); - } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) { String name = aps.getName(); String value = aps.getValue(); dataStream.createTagLogicalElement(name, value); - } else if (AFPElementMapping.NO_OPERATION.equals(element)) { - String content = aps.getContent(); - if (content != null) { - dataStream.createNoOperation(content); + } else { + if (this.location != LOC_IN_PAGE_HEADER) { + throw new IFException( + "AFP page setup extension encountered outside the page header: " + aps, null); + } + if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) { + String overlay = aps.getName(); + if (overlay != null) { + dataStream.createIncludePageOverlay(overlay); + } + } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) { + String name = aps.getName(); + String source = aps.getValue(); + pageSegmentMap.put(source, name); + } else if (AFPElementMapping.NO_OPERATION.equals(element)) { + String content = aps.getContent(); + if (content != null) { + dataStream.createNoOperation(content); + } } } + } else if (extension instanceof AFPInvokeMediumMap) { + if (this.location != LOC_FOLLOWING_PAGE_SEQUENCE) { + throw new IFException( + "AFP IMM extension must be between page-sequence and the first page: " + + extension, null); + } + AFPInvokeMediumMap imm = (AFPInvokeMediumMap)extension; + String mediumMap = imm.getName(); + if (mediumMap != null) { + dataStream.createInvokeMediumMap(mediumMap); + } } } diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java index e0b13d979..440db3eda 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java @@ -30,9 +30,8 @@ import org.apache.fop.render.intermediate.IFDocumentHandler; */ public class AFPDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { - //TODO Revert to normal MIME after stabilization! private static final String[] MIMES = new String[] - {MimeConstants.MIME_AFP + ";mode=painter"}; + {MimeConstants.MIME_AFP}; /** {@inheritDoc} */ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 1816ff515..3efe5cbb4 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -49,6 +49,7 @@ import org.apache.fop.afp.modca.PresentationTextObject; import org.apache.fop.afp.modca.ResourceObject; import org.apache.fop.afp.ptoca.PtocaBuilder; import org.apache.fop.afp.ptoca.PtocaProducer; +import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; @@ -342,12 +343,13 @@ public class AFPPainter extends AbstractIFPainter { try { //Embed fonts (char sets and code pages) //TODO This should be moved to a place where it has less performance impact - if (charSet.getPath() != null) { + if (charSet.getResourceAccessor() != null) { + ResourceAccessor accessor = charSet.getResourceAccessor(); documentHandler.getResourceManager().createIncludedResource( - charSet.getName(), charSet.getPath(), + charSet.getName(), accessor, ResourceObject.TYPE_FONT_CHARACTER_SET); documentHandler.getResourceManager().createIncludedResource( - charSet.getCodePage(), charSet.getPath(), + charSet.getCodePage(), accessor, ResourceObject.TYPE_CODE_PAGE); } } catch (IOException ioe) { diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java index 1967ae607..7e34d361c 100644 --- a/src/java/org/apache/fop/render/afp/AFPRenderer.java +++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java @@ -63,8 +63,8 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.CTM; -import org.apache.fop.area.LineArea; import org.apache.fop.area.OffDocumentItem; +import org.apache.fop.area.PageSequence; import org.apache.fop.area.PageViewport; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Image; @@ -80,6 +80,7 @@ import org.apache.fop.render.AbstractPathOrientedRenderer; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.RendererContext; import org.apache.fop.render.afp.extensions.AFPElementMapping; +import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap; import org.apache.fop.render.afp.extensions.AFPPageSetup; /** @@ -213,12 +214,31 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust } /** {@inheritDoc} */ - public void startPageSequence(LineArea seqTitle) { + public void startPageSequence(PageSequence pageSequence) { + super.startPageSequence(pageSequence); try { dataStream.startPageGroup(); } catch (IOException e) { log.error(e.getMessage()); } + if (pageSequence.hasExtensionAttachments()) { + for (Iterator iter = pageSequence.getExtensionAttachments().iterator(); + iter.hasNext();) { + ExtensionAttachment attachment = (ExtensionAttachment)iter.next(); + if (attachment instanceof AFPInvokeMediumMap) { + AFPInvokeMediumMap imm = (AFPInvokeMediumMap)attachment; + String mediumMap = imm.getName(); + if (mediumMap != null) { + dataStream.createInvokeMediumMap(mediumMap); + } + } else if (attachment instanceof AFPPageSetup) { + AFPPageSetup aps = (AFPPageSetup)attachment; + String name = aps.getName(); + String value = aps.getValue(); + dataStream.createTagLogicalElement(name, value); + } + } + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index bd6df6136..fcc1140c7 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -20,6 +20,8 @@ package org.apache.fop.render.afp; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; import org.apache.avalon.framework.configuration.Configuration; @@ -33,6 +35,8 @@ import org.apache.fop.afp.fonts.CharacterSet; import org.apache.fop.afp.fonts.FopCharacterSet; import org.apache.fop.afp.fonts.OutlineFont; import org.apache.fop.afp.fonts.RasterFont; +import org.apache.fop.afp.util.DefaultFOPResourceAccessor; +import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fonts.FontCollection; @@ -85,7 +89,29 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator log.error("Mandatory font configuration element '<afp-font...' is missing"); return null; } - String path = afpFontCfg.getAttribute("path", fontPath); + + URI baseURI = null; + String uri = afpFontCfg.getAttribute("base-uri", fontPath); + if (uri == null) { + //Fallback for old attribute which only supports local filenames + String path = afpFontCfg.getAttribute("path", fontPath); + if (path != null) { + File f = new File(path); + baseURI = f.toURI(); + } + } else { + try { + baseURI = new URI(uri); + } catch (URISyntaxException e) { + log.error("Invalid URI: " + e.getMessage()); + return null; + } + } + ResourceAccessor accessor = new DefaultFOPResourceAccessor( + this.userAgent, + this.userAgent.getFactory().getFontManager().getFontBaseURL(), + baseURI); + String type = afpFontCfg.getAttribute("type"); if (type == null) { log.error("Mandatory afp-font configuration attribute 'type=' is missing"); @@ -147,7 +173,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator } } else { font.addCharacterSet(size, new CharacterSet( - codepage, encoding, characterset, path)); + codepage, encoding, characterset, accessor)); } } return new AFPFontInfo(font, tripletList); @@ -180,7 +206,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator log.error(msg); } } else { - characterSet = new CharacterSet(codepage, encoding, characterset, path); + characterSet = new CharacterSet(codepage, encoding, characterset, accessor); } // Create a new font object OutlineFont font = new OutlineFont(name, characterSet); diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java index c3ba2c43b..83615b75d 100755 --- a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java @@ -33,12 +33,6 @@ import org.apache.fop.fo.FONode; */ public class AFPElementMapping extends ElementMapping { - /** page element */ - public static final String PAGE = "page"; - - /** page group element */ -// public static final String PAGE_GROUP = "page-group"; - /** tag logical element */ public static final String TAG_LOGICAL_ELEMENT = "tag-logical-element"; @@ -51,8 +45,8 @@ public class AFPElementMapping extends ElementMapping { /** NOP */ public static final String NO_OPERATION = "no-operation"; - /** resource information (name, level, dest) */ -// public static final String RESOURCE_INFO = "resource-info"; + /** IMM: Invoke Medium Map (on fo:page-sequence) */ + public static final String INVOKE_MEDIUM_MAP = "invoke-medium-map"; /** * The namespace used for AFP extensions @@ -77,11 +71,6 @@ public class AFPElementMapping extends ElementMapping { if (foObjs == null) { super.foObjs = new java.util.HashMap(); - foObjs.put(PAGE, new AFPPageSetupMaker()); -// foObjs.put( -// PAGE_GROUP, -// new AFPPageGroupMaker() -// ); foObjs.put( TAG_LOGICAL_ELEMENT, new AFPTagLogicalElementMaker()); @@ -94,51 +83,40 @@ public class AFPElementMapping extends ElementMapping { foObjs.put( NO_OPERATION, new AFPNoOperationMaker()); -// foObjs.put( -// RESOURCE_INFO, -// new AFPResourceInfoMaker()); - } - } - - static class AFPPageSetupMaker extends ElementMapping.Maker { - public FONode make(FONode parent) { - return new AFPPageSetupElement(parent); + foObjs.put( + INVOKE_MEDIUM_MAP, + new AFPInvokeMediumMapMaker()); } } static class AFPIncludePageOverlayMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new AFPElement(parent, INCLUDE_PAGE_OVERLAY); + return new AFPPageSetupElement(parent, INCLUDE_PAGE_OVERLAY); } } static class AFPIncludePageSegmentMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new AFPElement(parent, INCLUDE_PAGE_SEGMENT); + return new AFPPageSetupElement(parent, INCLUDE_PAGE_SEGMENT); } } static class AFPTagLogicalElementMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new AFPElement(parent, TAG_LOGICAL_ELEMENT); + return new AFPPageSetupElement(parent, TAG_LOGICAL_ELEMENT); } } static class AFPNoOperationMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new AFPElement(parent, NO_OPERATION); + return new AFPPageSetupElement(parent, NO_OPERATION); + } + } + + static class AFPInvokeMediumMapMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new AFPInvokeMediumMapElement(parent); } } -// static class AFPResourceInfoMaker extends ElementMapping.Maker { -// public FONode make(FONode parent) { -// return new AFPResourceInfoElement(parent); -// } -// } - -// static class AFPPageGroupMaker extends ElementMapping.Maker { -// public FONode make(FONode parent) { -// return new AFPElement(parent, PAGE_GROUP); -// } -// } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java index 9a8429b00..e027e7f32 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java @@ -5,9 +5,9 @@ * 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. @@ -21,45 +21,37 @@ package org.apache.fop.render.afp.extensions; import java.io.Serializable; -import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.xmlgraphics.util.XMLizable; -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.fo.extensions.ExtensionAttachment; /** * This is the pass-through value object for the AFP extension. */ public abstract class AFPExtensionAttachment implements ExtensionAttachment, Serializable, XMLizable { + private static final long serialVersionUID = 7190606822558332901L; /** The category URI for this extension attachment. */ public static final String CATEGORY = "apache:fop:extensions:afp"; + /** name attribute */ + protected static final String ATT_NAME = "name"; + /** * the extension element name */ protected String elementName; /** - * the extension content - */ - protected String content; - - /** * the extension name attribute */ protected String name; /** - * the extension value attribute - */ - protected String value; - - /** * Default constructor. - * + * * @param elementName the name of the afp extension attachment, may be null */ public AFPExtensionAttachment(String elementName) { @@ -91,65 +83,9 @@ public abstract class AFPExtensionAttachment this.name = name; } - /** - * @return the value - */ - public String getValue() { - return value; - } - - /** - * Sets the value - * @param source The value name to set. - */ - public void setValue(String source) { - this.value = source; - } - /** {@inheritDoc} */ public String getCategory() { return CATEGORY; } - /** - * @return the data - */ - public String getContent() { - return content; - } - - /** - * Sets the data - * @param content The byte data to set. - */ - public void setContent(String content) { - this.content = content; - } - - /** - * name attribute - */ - protected static final String ATT_NAME = "name"; - - /** - * value attribute - */ - protected static final String ATT_VALUE = "value"; - - /** {@inheritDoc} */ - public void toSAX(ContentHandler handler) throws SAXException { - AttributesImpl atts = new AttributesImpl(); - if (name != null && name.length() > 0) { - atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", name); - } - if (value != null && value.length() > 0) { - atts.addAttribute(null, ATT_VALUE, ATT_VALUE, "CDATA", value); - } - handler.startElement(CATEGORY, elementName, elementName, atts); - if (content != null && content.length() > 0) { - char[] chars = content.toCharArray(); - handler.characters(chars, 0, chars.length); - } - handler.endElement(CATEGORY, elementName, elementName); - } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java index 08989c03c..1a8cfdf47 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java @@ -19,13 +19,15 @@ package org.apache.fop.render.afp.extensions; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; /** * ContentHandler (parser) for restoring AFPExtension objects from XML. @@ -39,7 +41,7 @@ public class AFPExtensionHandler extends DefaultHandler private StringBuffer content = new StringBuffer(); private Attributes lastAttributes; - private AFPPageSetup returnedObject; + private AFPExtensionAttachment returnedObject; private ObjectBuiltListener listener; /** {@inheritDoc} */ @@ -53,8 +55,7 @@ public class AFPExtensionHandler extends DefaultHandler || localName.equals(AFPElementMapping.TAG_LOGICAL_ELEMENT) || localName.equals(AFPElementMapping.INCLUDE_PAGE_OVERLAY) || localName.equals(AFPElementMapping.INCLUDE_PAGE_SEGMENT) - || localName.equals(AFPElementMapping.PAGE) - /*|| localName.equals(AFPElementMapping.PAGE_GROUP)*/) { + || localName.equals(AFPElementMapping.INVOKE_MEDIUM_MAP)) { //handled in endElement } else { handled = false; @@ -74,17 +75,24 @@ public class AFPExtensionHandler extends DefaultHandler /** {@inheritDoc} */ public void endElement(String uri, String localName, String qName) throws SAXException { if (AFPPageSetup.CATEGORY.equals(uri)) { - this.returnedObject = new AFPPageSetup(localName); + AFPPageSetup pageSetupExtn = null; + if (localName.equals(AFPElementMapping.INVOKE_MEDIUM_MAP)) { + this.returnedObject = new AFPInvokeMediumMap(); + } + else { + pageSetupExtn = new AFPPageSetup(localName); + this.returnedObject = pageSetupExtn; + } String name = lastAttributes.getValue("name"); if (name != null) { returnedObject.setName(name); } String value = lastAttributes.getValue("value"); - if (value != null) { - returnedObject.setValue(value); + if (value != null && pageSetupExtn != null) { + pageSetupExtn.setValue(value); } - if (content.length() > 0) { - returnedObject.setContent(content.toString()); + if (content.length() > 0 && pageSetupExtn != null) { + pageSetupExtn.setContent(content.toString()); content.setLength(0); //Reset text buffer (see characters()) } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMap.java b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMap.java new file mode 100644 index 000000000..83c5325aa --- /dev/null +++ b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMap.java @@ -0,0 +1,54 @@ +/* + * 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.afp.extensions; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * This is the pass-through value object for the AFP extension. + */ +public class AFPInvokeMediumMap extends AFPExtensionAttachment { + + private static final long serialVersionUID = -7493160084509249309L; + + /** + * Default constructor. + */ + public AFPInvokeMediumMap() { + super(AFPElementMapping.INVOKE_MEDIUM_MAP); + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + if (name != null && name.length() > 0) { + atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", name); + } + handler.startElement(CATEGORY, elementName, elementName, atts); + handler.endElement(CATEGORY, elementName, elementName); + } + + /** {@inheritDoc} */ + public String toString() { + return "AFPInvokeMediumMap(name=" + getName() + ")"; + } +} diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java index 3104ced6f..99805edd4 100755..100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPElement.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java @@ -22,37 +22,34 @@ package org.apache.fop.render.afp.extensions; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; -import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.extensions.ExtensionAttachment; /** - * This class extends the org.apache.fop.extensions.ExtensionObj class. The - * object faciliates extraction of elements from formatted objects based on - * the static list as defined in the AFPElementMapping implementation. - * <p/> + * This class represents an AFP-specific extension element to embed Invoke Medium Map (IMM) + * fields at the beginning of a page group. The element is optional and expected as a direct child + * of an fo:page-sequence. */ -public class AFPElement extends AbstractAFPExtensionObject { +public class AFPInvokeMediumMapElement extends AbstractAFPExtensionObject { /** - * Constructs an AFP object (called by Maker). - * + * Constructs the AFP extension object (called by Maker). * @param parent the parent formatting object - * @param name the name of the afp element */ - public AFPElement(FONode parent, String name) { - super(parent, name); + public AFPInvokeMediumMapElement(FONode parent) { + super(parent, AFPElementMapping.INVOKE_MEDIUM_MAP); } /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); - if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { - throw new ValidationException(getName() + " must be a child of fo:simple-page-master."); + if (parent.getNameId() != Constants.FO_PAGE_SEQUENCE) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), + "rule.childOfPageSequence"); } } - /** {@inheritDoc} */ + /** {@inheritDoc} */ protected ExtensionAttachment instantiateExtensionAttachment() { - return new AFPPageSetup(getLocalName()); + return new AFPInvokeMediumMap(); } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java index d4b8e8a59..b72a30c84 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java @@ -19,11 +19,28 @@ package org.apache.fop.render.afp.extensions; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + /** * This is the pass-through value object for the AFP extension. */ public class AFPPageSetup extends AFPExtensionAttachment { + /** value attribute */ + protected static final String ATT_VALUE = "value"; + + /** + * the extension content + */ + protected String content; + + /** + * the extension value attribute + */ + protected String value; + /** * Default constructor. * @@ -36,8 +53,55 @@ public class AFPPageSetup extends AFPExtensionAttachment { private static final long serialVersionUID = -549941295384013190L; /** - * {@inheritDoc} + * Returns the value of the extension. + * @return the value + */ + public String getValue() { + return value; + } + + /** + * Sets the value + * @param source The value name to set. */ + public void setValue(String source) { + this.value = source; + } + + /** + * Returns the content of the extension. + * @return the data + */ + public String getContent() { + return content; + } + + /** + * Sets the data + * @param content The byte data to set. + */ + public void setContent(String content) { + this.content = content; + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + if (name != null && name.length() > 0) { + atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", name); + } + if (value != null && value.length() > 0) { + atts.addAttribute(null, ATT_VALUE, ATT_VALUE, "CDATA", value); + } + handler.startElement(CATEGORY, elementName, elementName, atts); + if (content != null && content.length() > 0) { + char[] chars = content.toCharArray(); + handler.characters(chars, 0, chars.length); + } + handler.endElement(CATEGORY, elementName, elementName); + } + + /** {@inheritDoc} */ public String toString() { return "AFPPageSetup(element-name=" + getElementName() + " name=" + getName() + " value=" + getValue() + ")"; diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java index d167a8d4b..0c6dfadc4 100644..100755 --- a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java @@ -19,37 +19,91 @@ package org.apache.fop.render.afp.extensions; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + import org.apache.fop.apps.FOPException; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; -import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.extensions.ExtensionAttachment; /** - * Extension element for afp:ps-page-setup-code. + * This class extends the org.apache.fop.extensions.ExtensionObj class. The + * object faciliates extraction of elements from formatted objects based on + * the static list as defined in the AFPElementMapping implementation. + * <p/> */ public class AFPPageSetupElement extends AbstractAFPExtensionObject { /** - * Main constructor - * @param parent parent FO node + * Constructs an AFP object (called by Maker). + * + * @param parent the parent formatting object + * @param name the name of the afp element */ - public AFPPageSetupElement(FONode parent) { - super(parent, "page"); + public AFPPageSetupElement(FONode parent, String name) { + super(parent, name); + } + + private AFPPageSetup getPageSetupAttachment() { + return (AFPPageSetup)getExtensionAttachment(); } /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); - if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { - throw new ValidationException(getName() + " must be a child of fo:simple-page-master."); + if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(getLocalName())) { + if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER + && parent.getNameId() != Constants.FO_PAGE_SEQUENCE) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), + "rule.childOfPageSequenceOrSPM"); + } + } else { + if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), + "rule.childOfSPM"); + } } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ + protected void characters(char[] data, int start, int length, + PropertyList pList, Locator locator) throws FOPException { + StringBuffer sb = new StringBuffer(); + AFPPageSetup pageSetup = getPageSetupAttachment(); + if (pageSetup.getContent() != null) { + sb.append(pageSetup.getContent()); + } + sb.append(data, start, length); + pageSetup.setContent(sb.toString()); + } + + /** {@inheritDoc} */ + public void processNode(String elementName, Locator locator, + Attributes attlist, PropertyList propertyList) + throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + AFPPageSetup pageSetup = getPageSetupAttachment(); + if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(elementName)) { + String attr = attlist.getValue("src"); + if (attr != null && attr.length() > 0) { + pageSetup.setValue(attr); + } else { + throw new FOPException(elementName + " must have a src attribute."); + } + } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) { + String attr = attlist.getValue("value"); + if (attr != null && attr.length() > 0) { + pageSetup.setValue(attr); + } else { + throw new FOPException(elementName + " must have a value attribute."); + } + } + } + + /** {@inheritDoc} */ protected ExtensionAttachment instantiateExtensionAttachment() { - return new AFPPageSetup(this.name); + return new AFPPageSetup(getLocalName()); } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java b/src/java/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java index c0e9c2c89..e35a13ec6 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java +++ b/src/java/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java @@ -54,9 +54,7 @@ public abstract class AbstractAFPExtensionObject extends FONode { this.name = name; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { @@ -64,32 +62,17 @@ public abstract class AbstractAFPExtensionObject extends FONode { } } - /** - * {@inheritDoc} - */ - protected void characters(char[] data, int start, int length, - PropertyList pList, Locator locator) throws FOPException { - ((AFPExtensionAttachment)getExtensionAttachment()).setContent( - new String(data, start, length)); - } - - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String getNamespaceURI() { return AFPElementMapping.NAMESPACE; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String getNormalNamespacePrefix() { return AFPElementMapping.NAMESPACE_PREFIX; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { @@ -100,26 +83,9 @@ public abstract class AbstractAFPExtensionObject extends FONode { } else { throw new FOPException(elementName + " must have a name attribute."); } - if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(elementName)) { - attr = attlist.getValue("src"); - if (attr != null && attr.length() > 0) { - extensionAttachment.setValue(attr); - } else { - throw new FOPException(elementName + " must have a src attribute."); - } - } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) { - attr = attlist.getValue("value"); - if (attr != null && attr.length() > 0) { - extensionAttachment.setValue(attr); - } else { - throw new FOPException(elementName + " must have a value attribute."); - } - } } - - /** - * {@inheritDoc} - */ + + /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); } @@ -130,9 +96,7 @@ public abstract class AbstractAFPExtensionObject extends FONode { */ protected abstract ExtensionAttachment instantiateExtensionAttachment(); - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public ExtensionAttachment getExtensionAttachment() { if (extensionAttachment == null) { this.extensionAttachment = (AFPExtensionAttachment)instantiateExtensionAttachment(); @@ -140,9 +104,7 @@ public abstract class AbstractAFPExtensionObject extends FONode { return this.extensionAttachment; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String getLocalName() { return name; } diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandlerMaker.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandlerMaker.java index 4a6c9cbb7..b14ba3c18 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandlerMaker.java @@ -30,8 +30,7 @@ import org.apache.fop.render.intermediate.IFDocumentHandler; */ public class TIFFDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { - //TODO Revert to normal MIME after stabilization! - private static final String[] MIMES = new String[] {MimeConstants.MIME_TIFF + ";mode=painter"}; + private static final String[] MIMES = new String[] {MimeConstants.MIME_TIFF}; /** {@inheritDoc} */ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java index db47c9712..797daa3a1 100644 --- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java @@ -30,8 +30,7 @@ import org.apache.fop.render.intermediate.IFDocumentHandler; */ public class PCLDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { - //TODO Revert to normal MIME after stabilization! - private static final String[] MIMES = new String[] {MimeConstants.MIME_PCL + ";mode=painter"}; + private static final String[] MIMES = new String[] {MimeConstants.MIME_PCL}; /** {@inheritDoc} */ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 3e27fce97..7df6c9fee 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -279,6 +279,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { pdfDoc.outputTrailer(this.outputStream); } this.pdfDoc = null; + pdfResources = null; this.generator = null; currentContext = null; diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java index 532b50f80..f4ba03d5b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java @@ -30,8 +30,7 @@ import org.apache.fop.render.intermediate.IFDocumentHandler; */ public class PDFDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { - //TODO Revert to normal MIME after stabilization! - private static final String[] MIMES = new String[] {MimeConstants.MIME_PDF + ";mode=painter"}; + private static final String[] MIMES = new String[] {MimeConstants.MIME_PDF}; /** {@inheritDoc} */ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/print/PageableRenderer.java b/src/java/org/apache/fop/render/print/PageableRenderer.java index 01a60646b..89ab98071 100644 --- a/src/java/org/apache/fop/render/print/PageableRenderer.java +++ b/src/java/org/apache/fop/render/print/PageableRenderer.java @@ -29,6 +29,7 @@ import java.util.Map; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.PageViewport; import org.apache.fop.render.java2d.Java2DRenderer; @@ -76,6 +77,11 @@ public class PageableRenderer extends Java2DRenderer implements Pageable { } /** {@inheritDoc} */ + public String getMimeType() { + return MimeConstants.MIME_FOP_PRINT; + } + + /** {@inheritDoc} */ public void setUserAgent(FOUserAgent agent) { super.setUserAgent(agent); diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java b/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java index eb085c0a4..c581f9e02 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java @@ -30,9 +30,8 @@ import org.apache.fop.render.intermediate.IFDocumentHandler; */ public class PSDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { - //TODO Revert to normal MIME after stabilization! private static final String[] MIMES = new String[] - {MimeConstants.MIME_POSTSCRIPT + ";mode=painter"}; + {MimeConstants.MIME_POSTSCRIPT}; /** {@inheritDoc} */ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index 6e03d68dd..8e8ae3f1d 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -438,7 +438,9 @@ public class XMLRenderer extends AbstractXMLRenderer { if (pageSequence.getCountry() != null) { addAttribute("country", pageSequence.getCountry()); } + transferForeignObjects(pageSequence); startElement("pageSequence", atts); + handleExtensionAttachments(pageSequence.getExtensionAttachments()); LineArea seqTitle = pageSequence.getTitle(); if (seqTitle != null) { startElement("title"); diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java index 03ad4ee71..e101a9573 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java @@ -25,12 +25,13 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.fop.apps.FOPException; +import org.apache.fop.fonts.FontEventListener; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontInfoConfigurator; import org.apache.fop.fonts.FontManager; import org.apache.fop.fonts.FontResolver; import org.apache.fop.fonts.FontSetup; import org.apache.fop.pdf.PDFDocument; -import org.apache.fop.render.PrintRendererConfigurator; import org.apache.fop.render.pdf.PDFRendererConfigurator; /** @@ -61,15 +62,19 @@ public class PDFDocumentGraphics2DConfigurator { //TODO Make use of fontBaseURL, font substitution and referencing configuration //Requires a change to the expected configuration layout - List/*<EmbedFontInfo>*/ embedFontInfoList - = PrintRendererConfigurator.buildFontListFromConfiguration( - cfg, fontResolver, false, fontManager, null); //TODO Wire in the FontEventListener + final FontEventListener listener = null; + final boolean strict = false; + FontInfoConfigurator fontInfoConfigurator + = new FontInfoConfigurator(cfg, fontManager, fontResolver, listener, strict); + List/*<EmbedFontInfo>*/ fontInfoList = new java.util.ArrayList/*<EmbedFontInfo>*/(); + fontInfoConfigurator.configure(fontInfoList); + if (fontManager.useCache()) { fontManager.getFontCache().save(); } FontInfo fontInfo = new FontInfo(); - FontSetup.setup(fontInfo, embedFontInfoList, fontResolver); + FontSetup.setup(fontInfo, fontInfoList, fontResolver); graphics.setFontInfo(fontInfo); } catch (FOPException e) { throw new ConfigurationException("Error while setting up fonts", e); diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 554d26798..ede368f58 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -59,6 +59,15 @@ import org.apache.batik.ext.awt.RadialGradientPaint; import org.apache.batik.ext.awt.RenderingHintsKeyExt; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.PatternPaint; + +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.ImageSize; +import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; +import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; +import org.apache.xmlgraphics.image.loader.impl.ImageRendered; +import org.apache.xmlgraphics.java2d.AbstractGraphics2D; +import org.apache.xmlgraphics.java2d.GraphicContext; + import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontSetup; @@ -83,13 +92,6 @@ import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter; import org.apache.fop.render.pdf.ImageRawJPEGAdapter; import org.apache.fop.render.pdf.ImageRenderedAdapter; import org.apache.fop.util.ColorExt; -import org.apache.xmlgraphics.image.loader.ImageInfo; -import org.apache.xmlgraphics.image.loader.ImageSize; -import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; -import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; -import org.apache.xmlgraphics.image.loader.impl.ImageRendered; -import org.apache.xmlgraphics.java2d.AbstractGraphics2D; -import org.apache.xmlgraphics.java2d.GraphicContext; /** * PDF Graphics 2D. @@ -741,13 +743,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand throw new PDFConformanceException( "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK."); } - float[] cComps = c.getColorComponents(new float[3]); - double[] cmyk = new double[3]; - for (int i = 0; i < 3; i++) { - // convert the float elements to doubles for pdf - cmyk[i] = cComps[i]; - } - PDFColor currentColour = new PDFColor(cmyk[0], cmyk[1], cmyk[2], cmyk[3]); + PDFColor currentColour = new PDFColor(c); currentStream.write(currentColour.getColorSpaceOut(fill)); } else if (c.getColorSpace().getType() == ColorSpace.TYPE_2CLR) { diff --git a/src/java/org/apache/fop/tools/fontlist/FontListGenerator.java b/src/java/org/apache/fop/tools/fontlist/FontListGenerator.java new file mode 100644 index 000000000..ce44eee19 --- /dev/null +++ b/src/java/org/apache/fop/tools/fontlist/FontListGenerator.java @@ -0,0 +1,115 @@ +/* + * 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.tools.fontlist; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.fonts.FontEventListener; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.render.intermediate.IFDocumentHandler; +import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; + +/** + * Generates a list of available fonts. + */ +public class FontListGenerator { + + /** + * List all fonts configured for a particular output format (identified by MIME type). + * The sorted map returned looks like this: + * <code>SortedMap<String/font-family, List<{@link FontSpec}>></code> + * @param fopFactory the FOP factory (already configured) + * @param mime the MIME type identified the selected output format + * @param listener a font event listener to catch any font-related errors while listing fonts + * @return the map of font families + * @throws FOPException if an error occurs setting up the fonts + */ + public SortedMap listFonts(FopFactory fopFactory, String mime, FontEventListener listener) + throws FOPException { + FontInfo fontInfo = setupFonts(fopFactory, mime, listener); + SortedMap fontFamilies = buildFamilyMap(fontInfo); + return fontFamilies; + } + + private FontInfo setupFonts(FopFactory fopFactory, String mime, FontEventListener listener) + throws FOPException { + FOUserAgent userAgent = fopFactory.newFOUserAgent(); + + //The document handler is only instantiated to get access to its configurator! + IFDocumentHandler documentHandler + = fopFactory.getRendererFactory().createDocumentHandler(userAgent, mime); + IFDocumentHandlerConfigurator configurator = documentHandler.getConfigurator(); + + FontInfo fontInfo = new FontInfo(); + configurator.setupFontInfo(documentHandler, fontInfo); + return fontInfo; + } + + private SortedMap buildFamilyMap(FontInfo fontInfo) { + Map fonts = fontInfo.getFonts(); + Set keyBag = new java.util.HashSet(fonts.keySet()); + + Map keys = new java.util.HashMap(); + SortedMap fontFamilies = new java.util.TreeMap(); + //SortedMap<String/font-family, List<FontSpec>> + + Iterator iter = fontInfo.getFontTriplets().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + FontTriplet triplet = (FontTriplet)entry.getKey(); + String key = (String)entry.getValue(); + FontSpec container; + if (keyBag.contains(key)) { + keyBag.remove(key); + + FontMetrics metrics = (FontMetrics)fonts.get(key); + + container = new FontSpec(key, metrics); + container.addFamilyNames(metrics.getFamilyNames()); + keys.put(key, container); + String firstFamilyName = (String)container.getFamilyNames().first(); + List containers = (List)fontFamilies.get(firstFamilyName); + if (containers == null) { + containers = new java.util.ArrayList(); + fontFamilies.put(firstFamilyName, containers); + } + containers.add(container); + Collections.sort(containers); + + } else { + container = (FontSpec)keys.get(key); + } + container.addTriplet(triplet); + } + + return fontFamilies; + } + +} diff --git a/src/java/org/apache/fop/tools/fontlist/FontListMain.java b/src/java/org/apache/fop/tools/fontlist/FontListMain.java new file mode 100644 index 000000000..d31da92c2 --- /dev/null +++ b/src/java/org/apache/fop/tools/fontlist/FontListMain.java @@ -0,0 +1,305 @@ +/* + * 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.tools.fontlist; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.URL; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; + +import org.apache.fop.Version; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.fonts.FontEventListener; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.util.GenerationHelperContentHandler; + +/** + * Command-line application to list available fonts and to optionally produce sample pages + * with those fonts. + */ +public final class FontListMain { + + private static final int GENERATE_CONSOLE = 0; + private static final int GENERATE_XML = 1; + private static final int GENERATE_FO = 2; + private static final int GENERATE_RENDERED = 3; + + private FopFactory fopFactory = FopFactory.newInstance(); + + private File configFile; + private File outputFile; + private String configMime = MimeConstants.MIME_PDF; + private String outputMime; + private int mode = GENERATE_CONSOLE; + private String singleFamilyFilter; + + private FontListMain() throws SAXException, IOException { + } + + private void prepare() throws SAXException, IOException { + if (this.configFile != null) { + fopFactory.setUserConfig(this.configFile); + } + } + + private ContentHandler getFOPContentHandler(OutputStream out) throws FOPException { + Fop fop = fopFactory.newFop(this.outputMime, out); + return fop.getDefaultHandler(); + } + + private void generateXML(SortedMap fontFamilies, File outFile, String singleFamily) + throws TransformerConfigurationException, SAXException, IOException { + SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + TransformerHandler handler; + if (this.mode == GENERATE_XML) { + handler = tFactory.newTransformerHandler(); + } else { + URL url = getClass().getResource("fonts2fo.xsl"); + if (url == null) { + throw new FileNotFoundException("Did not find resource: fonts2fo.xsl"); + } + handler = tFactory.newTransformerHandler(new StreamSource(url.toExternalForm())); + } + + if (singleFamily != null) { + Transformer transformer = handler.getTransformer(); + transformer.setParameter("single-family", singleFamily); + } + + OutputStream out = new java.io.FileOutputStream(outFile); + out = new java.io.BufferedOutputStream(out); + if (this.mode == GENERATE_RENDERED) { + handler.setResult(new SAXResult(getFOPContentHandler(out))); + } else { + handler.setResult(new StreamResult(out)); + } + try { + GenerationHelperContentHandler helper = new GenerationHelperContentHandler( + handler, null); + FontListSerializer serializer = new FontListSerializer(); + serializer.generateSAX(fontFamilies, singleFamily, helper); + } finally { + IOUtils.closeQuietly(out); + } + } + + private void generate() throws Exception { + prepare(); + + FontEventListener listener = new FontEventListener() { + + public void fontLoadingErrorAtAutoDetection(Object source, + String fontURL, Exception e) { + System.err.println("Could not load " + fontURL + + " (" + e.getLocalizedMessage() + ")"); + } + + public void fontSubstituted(Object source, + FontTriplet requested, FontTriplet effective) { + //ignore + } + + public void glyphNotAvailable(Object source, char ch, String fontName) { + //ignore + } + + }; + + FontListGenerator listGenerator = new FontListGenerator(); + SortedMap fontFamilies = listGenerator.listFonts(fopFactory, configMime, listener); + + if (this.mode == GENERATE_CONSOLE) { + writeToConsole(fontFamilies); + } else { + writeOutput(fontFamilies); + } + } + + private void writeToConsole(SortedMap fontFamilies) + throws TransformerConfigurationException, SAXException, IOException { + Iterator iter = fontFamilies.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + String firstFamilyName = (String)entry.getKey(); + System.out.println(firstFamilyName + ":"); + List list = (List)entry.getValue(); + Iterator fonts = list.iterator(); + while (fonts.hasNext()) { + FontSpec f = (FontSpec)fonts.next(); + System.out.println(" " + f.getKey() + " " + f.getFamilyNames()); + Iterator triplets = f.getTriplets().iterator(); + while (triplets.hasNext()) { + FontTriplet triplet = (FontTriplet)triplets.next(); + System.out.println(" " + triplet.toString()); + } + } + } + } + + private void writeOutput(SortedMap fontFamilies) + throws TransformerConfigurationException, SAXException, IOException { + if (this.outputFile.isDirectory()) { + System.out.println("Creating one file for each family..."); + Iterator iter = fontFamilies.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + String familyName = (String)entry.getKey(); + System.out.println("Creating output file for " + familyName + "..."); + String filename; + switch(this.mode) { + case GENERATE_RENDERED: + filename = familyName + ".pdf"; + break; + case GENERATE_FO: + filename = familyName + ".fo"; + break; + case GENERATE_XML: + filename = familyName + ".xml"; + break; + default: + throw new IllegalStateException("Unsupported mode"); + } + File outFile = new File(this.outputFile, filename); + generateXML(fontFamilies, outFile, familyName); + } + } else { + System.out.println("Creating output file..."); + generateXML(fontFamilies, this.outputFile, this.singleFamilyFilter); + } + System.out.println(this.outputFile + " written."); + } + + private static void printVersion() { + System.out.println("Apache FOP " + Version.getVersion() + + " - http://xmlgraphics.apache.org/fop/\n"); + } + + private static void printHelp() { + printVersion(); + + String className = FontListMain.class.getName(); + PrintStream out = System.out; + out.println("USAGE"); + out.println(" java [vmargs] " + className + + " [-c <config-file>] [-f <mime>] [[output-dir|output-file] [font-family]]"); + out.println(); + out.println("PARAMETERS"); + out.println(" config-file: an optional FOP configuration file"); + out.println(" mime: The MIME type of the output format for which to"); + out.println(" create the font list (defaults to application/pdf)"); + out.println(" output-dir: Creates one sample PDF per font-family"); + out.println(" output-file: writes the list as file (valid file extensions: xml, fo, pdf)"); + out.println(" font-family: filters to a single font family"); + out.println(); + out.println("EXAMPLE"); + out.println(" java [vmargs] " + className + + " -c userconfig.xml all-fonts.pdf"); + out.println(" --> this generates a single PDF containing a sample"); + out.println(" of all configured fonts."); + out.println(" java [vmargs] " + className + + " -c userconfig.xml"); + out.println(" --> this prints all configured fonts to the console."); + out.println(); + } + + private void parseArguments(String[] args) { + if (args.length > 0) { + int idx = 0; + if ("--help".equals(args[idx]) || "-?".equals(args[idx]) || "-h".equals(args[idx])) { + printHelp(); + System.exit(0); + } + if (idx < args.length - 1 && "-c".equals(args[idx])) { + String filename = args[idx + 1]; + this.configFile = new File(filename); + idx += 2; + } + if (idx < args.length - 1 && "-f".equals(args[idx])) { + this.configMime = args[idx + 1]; + idx += 2; + } + if (idx < args.length) { + String name = args[idx]; + this.outputFile = new File(name); + if (this.outputFile.isDirectory()) { + this.mode = GENERATE_RENDERED; + this.outputMime = MimeConstants.MIME_PDF; + } else if (FilenameUtils.getExtension(name).equalsIgnoreCase("pdf")) { + this.mode = GENERATE_RENDERED; + this.outputMime = MimeConstants.MIME_PDF; + } else if (FilenameUtils.getExtension(name).equalsIgnoreCase("fo")) { + this.mode = GENERATE_FO; + } else if (FilenameUtils.getExtension(name).equalsIgnoreCase("xml")) { + this.mode = GENERATE_XML; + } else { + throw new IllegalArgumentException( + "Operating mode for the output file cannot be determined" + + " or is unsupported: " + name); + } + idx++; + } + if (idx < args.length) { + this.singleFamilyFilter = args[idx]; + } + } else { + System.out.println("use --help or -? for usage information."); + } + } + + /** + * The command-line interface. + * @param args the command-line arguments + */ + public static void main(String[] args) { + try { + FontListMain app = new FontListMain(); + app.parseArguments(args); + app.generate(); + } catch (Throwable t) { + printHelp(); + t.printStackTrace(); + System.exit(-1); + } + } + +} diff --git a/src/java/org/apache/fop/tools/fontlist/FontListSerializer.java b/src/java/org/apache/fop/tools/fontlist/FontListSerializer.java new file mode 100644 index 000000000..eab3caa56 --- /dev/null +++ b/src/java/org/apache/fop/tools/fontlist/FontListSerializer.java @@ -0,0 +1,142 @@ +/* + * 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.tools.fontlist; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.regex.Pattern; + +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.util.GenerationHelperContentHandler; + +/** + * Turns the font list into SAX events. + */ +public class FontListSerializer { + + private static final String FONTS = "fonts"; + private static final String FAMILY = "family"; + private static final String FONT = "font"; + private static final String TRIPLETS = "triplets"; + private static final String TRIPLET = "triplet"; + + private static final String NAME = "name"; + private static final String STRIPPED_NAME = "stripped-name"; + private static final String TYPE = "type"; + private static final String KEY = "key"; + private static final String STYLE = "style"; + private static final String WEIGHT = "weight"; + + private static final String CDATA = "CDATA"; + + /** + * Generates SAX events from the font damily map. + * @param fontFamilies the font families + * @param handler the target SAX handler + * @throws SAXException if an XML-related exception occurs + */ + public void generateSAX(SortedMap fontFamilies, + GenerationHelperContentHandler handler) throws SAXException { + generateSAX(fontFamilies, null, handler); + } + + /** + * Generates SAX events from the font damily map. + * @param fontFamilies the font families + * @param singleFamily if not null, the output will be filtered so only this single font family + * will be used + * @param handler the target SAX handler + * @throws SAXException if an XML-related exception occurs + */ + public void generateSAX(SortedMap fontFamilies, String singleFamily, + GenerationHelperContentHandler handler) throws SAXException { + handler.startDocument(); + AttributesImpl atts = new AttributesImpl(); + handler.startElement(FONTS, atts); + + Iterator iter = fontFamilies.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + String familyName = (String)entry.getKey(); + if (singleFamily != null && familyName != singleFamily) { + continue; + } + atts.clear(); + atts.addAttribute(null, NAME, NAME, CDATA, familyName); + atts.addAttribute(null, STRIPPED_NAME, STRIPPED_NAME, CDATA, + stripQuotes(familyName)); + handler.startElement(FAMILY, atts); + + List containers = (List)entry.getValue(); + generateXMLForFontContainers(handler, containers); + handler.endElement(FAMILY); + } + + handler.endElement(FONTS); + handler.endDocument(); + } + + private final Pattern quotePattern = Pattern.compile("'"); + + private String stripQuotes(String name) { + return quotePattern.matcher(name).replaceAll(""); + } + + private void generateXMLForFontContainers(GenerationHelperContentHandler handler, + List containers) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + Iterator fontIter = containers.iterator(); + while (fontIter.hasNext()) { + FontSpec cont = (FontSpec)fontIter.next(); + atts.clear(); + atts.addAttribute(null, KEY, KEY, CDATA, cont.getKey()); + atts.addAttribute(null, TYPE, TYPE, CDATA, + cont.getFontMetrics().getFontType().getName()); + handler.startElement(FONT, atts); + generateXMLForTriplets(handler, cont.getTriplets()); + handler.endElement(FONT); + } + } + + private void generateXMLForTriplets(GenerationHelperContentHandler handler, Collection triplets) + throws SAXException { + AttributesImpl atts = new AttributesImpl(); + atts.clear(); + handler.startElement(TRIPLETS, atts); + Iterator iter = triplets.iterator(); + while (iter.hasNext()) { + FontTriplet triplet = (FontTriplet)iter.next(); + atts.clear(); + atts.addAttribute(null, NAME, NAME, CDATA, triplet.getName()); + atts.addAttribute(null, STYLE, STYLE, CDATA, triplet.getStyle()); + atts.addAttribute(null, WEIGHT, WEIGHT, CDATA, + Integer.toString(triplet.getWeight())); + handler.element(TRIPLET, atts); + } + handler.endElement(TRIPLETS); + } + +} diff --git a/src/java/org/apache/fop/tools/fontlist/FontSpec.java b/src/java/org/apache/fop/tools/fontlist/FontSpec.java new file mode 100644 index 000000000..ce5c7a6c7 --- /dev/null +++ b/src/java/org/apache/fop/tools/fontlist/FontSpec.java @@ -0,0 +1,103 @@ +/* + * 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.tools.fontlist; + +import java.util.Collection; +import java.util.Collections; +import java.util.SortedSet; + +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.FontTriplet; + +/** + * Represents a font with information on how it can be used from XSL-FO. + */ +public class FontSpec implements Comparable { + + private String key; + private FontMetrics metrics; + private SortedSet familyNames = new java.util.TreeSet(); + private Collection triplets = new java.util.TreeSet(); + + /** + * Creates a new font spec. + * @param key the internal font key + * @param metrics the font metrics + */ + public FontSpec(String key, FontMetrics metrics) { + this.key = key; + this.metrics = metrics; + } + + /** + * Adds font family names. + * @param names the names + */ + public void addFamilyNames(Collection names) { + this.familyNames.addAll(names); + } + + /** + * Adds a font triplet. + * @param triplet the font triplet + */ + public void addTriplet(FontTriplet triplet) { + this.triplets.add(triplet); + } + + /** + * Returns the font family names. + * @return the font family names + */ + public SortedSet getFamilyNames() { + return Collections.unmodifiableSortedSet(this.familyNames); + } + + /** + * Returns the font triplets. + * @return the font triplets + */ + public Collection getTriplets() { + return Collections.unmodifiableCollection(this.triplets); + } + + /** + * Returns the internal font key. + * @return the internal font key + */ + public String getKey() { + return this.key; + } + + /** + * Returns the font metrics. + * @return the font metrics + */ + public FontMetrics getFontMetrics() { + return this.metrics; + } + + /** {@inheritDoc} */ + public int compareTo(Object o) { + FontSpec other = (FontSpec)o; + return metrics.getFullName().compareTo(other.metrics.getFullName()); + } + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/tools/fontlist/fonts2fo.xsl b/src/java/org/apache/fop/tools/fontlist/fonts2fo.xsl new file mode 100644 index 000000000..5c2b59e8e --- /dev/null +++ b/src/java/org/apache/fop/tools/fontlist/fonts2fo.xsl @@ -0,0 +1,200 @@ +<?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:svg="http://www.w3.org/2000/svg"> + + <xsl:output method="xml" indent="yes"/> + + <xsl:param name="single-family" select="''"/> + + <xsl:template match="fonts"> + <fo:root font-family="sans-serif" font-size="10pt"> + <!-- defines the layout master --> + <fo:layout-master-set> + <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" + margin="1.5cm"> + <fo:region-body/> + </fo:simple-page-master> + </fo:layout-master-set> + <!-- starts actual layout --> + <xsl:choose> + <xsl:when test="string-length($single-family) > 0"> + <xsl:apply-templates select="family[@name = $single-family]"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="bookmarks"/> + <xsl:call-template name="toc"/> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> + </fo:root> + </xsl:template> + + <xsl:template name="bookmarks"> + <fo:bookmark-tree> + <fo:bookmark internal-destination="toc"> + <fo:bookmark-title>Table of Contents</fo:bookmark-title> + </fo:bookmark> + <xsl:apply-templates mode="bookmark"/> + </fo:bookmark-tree> + </xsl:template> + + <xsl:template name="toc"> + <fo:page-sequence master-reference="A4" id="toc"> + <fo:flow flow-name="xsl-region-body"> + <fo:block> + <fo:block font-size="14pt" font-weight="bold" space-after="1em">FOP Font List</fo:block> + <fo:block space-after="0.5em">The number of font families: <xsl:value-of select="count(family)"/></fo:block> + </fo:block> + <xsl:if test="count(family) > 0"> + <fo:list-block provisional-distance-between-starts="1.6em" + provisional-label-separation="0.5em"> + <xsl:apply-templates mode="toc"/> + </fo:list-block> + </xsl:if> + </fo:flow> + </fo:page-sequence> + </xsl:template> + + <xsl:template match="family" mode="bookmark"> + <fo:bookmark internal-destination="{generate-id()}"> + <fo:bookmark-title> + <xsl:value-of select="@name"/> + </fo:bookmark-title> + </fo:bookmark> + </xsl:template> + + <xsl:template match="family" mode="toc"> + <fo:list-item> + <fo:list-item-label start-indent="2mm" end-indent="label-end()"> + <fo:block hyphenation-character="−" font-family="Symbol">•</fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()"> + <fo:block> + <fo:basic-link internal-destination="{generate-id()}"> + <xsl:value-of select="@name"/> + </fo:basic-link> + </fo:block> + </fo:list-item-body> + </fo:list-item> + </xsl:template> + + <xsl:template match="family"> + <fo:page-sequence master-reference="A4" id="{generate-id()}"> + <fo:flow flow-name="xsl-region-body"> + <fo:block> + <fo:block font-size="14pt" font-weight="bold" space-after="0.5em" border-bottom="solid 0.5mm"> + <xsl:value-of select="@name"/> + </fo:block> + <fo:block> + <fo:block font-weight="bold">Fonts:</fo:block> + <xsl:apply-templates/> + </fo:block> + </fo:block> + <xsl:call-template name="weight-sample"> + <xsl:with-param name="font-family" select="@stripped-name"/> + </xsl:call-template> + </fo:flow> + </fo:page-sequence> + </xsl:template> + + <xsl:template name="weight-sample"> + <xsl:param name="font-family" select="'sans-serif'"/> + <fo:block border="solid 0.25mm" start-indent="0.25mm" end-indent="0.25mm" + space-before="0.5em" + keep-together.within-column="always"> + <fo:block font-size="8pt" + background-color="black" color="white" + padding="0mm 1mm" start-indent="1.25mm" end-indent="1.25mm"> + Weight Sample: font-family="<xsl:value-of select="$font-family"/>" font-weight="100..900"</fo:block> + <fo:block padding="1mm 1mm" start-indent="1.25mm" end-indent="1.25mm"> + <xsl:attribute name="font-family"> + <xsl:value-of select="$font-family"/> + </xsl:attribute> + <fo:block font-weight="100">100: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="200">200: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="300">300: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="400">400: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="500">500: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="600">600: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="700">700: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="800">800: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block font-weight="900">900: The quick brown fox jumps over the lazy dog</fo:block> + <fo:block> + <fo:instream-foreign-object> + <svg xmlns="http://www.w3.org/2000/svg" width="16cm" height="108pt"> + <g font-family="sans-serif" font-weight="bold" font-size="80pt" + transform="translate(30, 100) rotate(-15)"> + <text fill="lightgray">SVG</text> + </g> + <g font-size="10pt"> + <xsl:attribute name="font-family"> + '<xsl:value-of select="$font-family"/>' + </xsl:attribute> + <text x="0" y="10"> + <tspan x="0" dy="0" font-weight="100">100: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="200">200: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="300">300: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="400">400: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="500">500: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="600">600: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="700">700: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="800">800: The quick brown fox jumps over the lazy dog</tspan> + <tspan x="0" dy="12" font-weight="900">900: The quick brown fox jumps over the lazy dog</tspan> + </text> + </g> + </svg> + </fo:instream-foreign-object> + </fo:block> + </fo:block> + </fo:block> + </xsl:template> + + <xsl:template match="font"> + <fo:block>Internal key: <xsl:value-of select="@key"/></fo:block> + <fo:block border="solid 0.25mm" start-indent="0.25mm" end-indent="0.25mm"> + <fo:block font-size="8pt" + background-color="black" color="white" + padding="0mm 1mm" start-indent="1.25mm" end-indent="1.25mm"> + Sample:</fo:block> + <fo:block font-family="{triplets/triplet[1]/@name}" + font-style="{triplets/triplet[1]/@style}" + font-weight="{triplets/triplet[1]/@weight}" + font-size="14pt" padding="1mm" start-indent="1.25mm" end-indent="1.25mm"> + The quick brown fox jumps over the lazy dog. + </fo:block> + </fo:block> + <fo:block start-indent="5mm"> + <fo:block>Accessible by:</fo:block> + <fo:block start-indent="10mm"> + <xsl:apply-templates select="triplets/triplet"/> + </fo:block> + </fo:block> + </xsl:template> + + <xsl:template match="triplet"> + <fo:block color="gray"> + font-family=<fo:inline color="black">"<xsl:value-of select="@name"/>"</fo:inline> + font-style=<fo:inline color="black"><xsl:value-of select="@style"/>"</fo:inline> + font-weight=<fo:inline color="black"><xsl:value-of select="@weight"/>"</fo:inline> + </fo:block> + </xsl:template> +</xsl:stylesheet> diff --git a/status.xml b/status.xml index 015597818..b6a2d23c0 100644 --- a/status.xml +++ b/status.xml @@ -61,6 +61,46 @@ <action context="Renderers" dev="JM" type="add" fixes-bug="46705" due-to="Jost Klopfstein"> Added basic accessibility and Tagged PDF support. </action> + <action context="Renderers" dev="JM" type="fix"> + Fixed a bug that left the PrintRenderer unconfigured even if a configuration was + specified for "application/X-fop-print". + </action> + <action context="Renderers" dev="JM" type="fix" fixes-bug="46882" due-to="Yegor Kozlov"> + Fixed the handling of CMYK colors in PDFGraphics2D. + </action> + <action context="Layout" dev="AD" type="fix" fixes-bug="46489"> + Fixed a bug when combining a forced break with a "last" page-master. The restart + of the algorithm would start at the index of the penalty corresponding to the last + page-break. This has been changed to the index of the first element after the last + page-break. + </action> + <action context="Fonts" dev="JM" type="add"> + Added a command-line tool to list all configured fonts + (org.apache.fop.tools.fontlist.FontListMain). + </action> + <action context="Code" dev="AD" type="add" fixes-bug="46828" due-to="Dario Laera"> + Added the possibility to use CachedRenderPagesModel, to conserve memory in case + of large documents with a lot of cross-references (area tree will be serialized to + disk to avoid keeping it entirely in memory). + </action> + <action context="Fonts" dev="JM" type="add"> + AFP Fonts: Added support for full URI resolution on configured AFP fonts. + </action> + <action context="Renderers" dev="JM" type="add"> + AFP Output: Tag Logical Element (TLE) is now also allowed on fo:page-sequence + (page group level). + </action> + <action context="Layout" dev="JM" type="fix"> + Fixed BPD trait and border painting for leaders with leader-pattern="space" + (and similar cases). + </action> + <action context="Renderers" dev="JM" type="add"> + AFP Output: Added support for Invoke Medium Map (IMM). + </action> + <action context="Renderers" dev="JM" type="add"> + Introduced a new, additional intermediate format optimized for performance. Please see + the intermediate format documentation for details. + </action> <action context="Fonts" dev="JM" type="fix" fixes-bug="46686" due-to="Alok Singh"> Use temporary directory for the font cache if the user home directory is not write-accessible. @@ -74,11 +114,11 @@ code. </action> <action context="Code" dev="VH" type="fix" fixes-bug="46638"> - MinOptMaxUtil.toMinOptMax was converting LengthRangeProperty objects into illegal MinOptMax + MinOptMaxUtil.toMinOptMax was converting LengthRangeProperty objects into illegal MinOptMax objects (in some cases opt could be inferior to min). </action> <action context="Layout" dev="VH" type="add" fixes-bug="46315" due-to="Georg Datterl"> - Added extension to disable column balancing before blocks spanning the whole page, in + Added extension to disable column balancing before blocks spanning the whole page, in multiple-column documents. </action> <action context="Renderers" dev="JM" type="add"> diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index 290b29050..e6a718643 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -25,6 +25,7 @@ import junit.framework.TestSuite; import org.apache.fop.fonts.TrueTypeAnsiTestCase; import org.apache.fop.image.loader.batik.ImageLoaderTestCase; import org.apache.fop.image.loader.batik.ImagePreloaderTestCase; +import org.apache.fop.intermediate.IFMimickingTestCase; import org.apache.fop.render.pdf.PDFAConformanceTestCase; import org.apache.fop.render.pdf.PDFCMapTestCase; import org.apache.fop.render.pdf.PDFEncodingTestCase; @@ -54,6 +55,7 @@ public class StandardTestSuite { suite.addTest(RichTextFormatTestSuite.suite()); suite.addTest(new TestSuite(ImageLoaderTestCase.class)); suite.addTest(new TestSuite(ImagePreloaderTestCase.class)); + suite.addTest(new TestSuite(IFMimickingTestCase.class)); //$JUnit-END$ return suite; } diff --git a/test/java/org/apache/fop/config/FOURIResolverTestCase.java b/test/java/org/apache/fop/config/FOURIResolverTestCase.java new file mode 100644 index 000000000..e6f8db712 --- /dev/null +++ b/test/java/org/apache/fop/config/FOURIResolverTestCase.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.config; + +import java.net.MalformedURLException; + +import junit.framework.TestCase; + +import org.apache.fop.apps.FOURIResolver; + +/** + * This tests some aspects of the {@link FOURIResolver} class. + */ +public class FOURIResolverTestCase extends TestCase { + + /** + * Checks the {@link FOURIResolver#checkBaseURL(String)} method. + * @throws Exception if an error occurs + */ + public void testCheckBaseURI() throws Exception { + FOURIResolver resolver = new FOURIResolver(true); + System.out.println(resolver.checkBaseURL("./test/config")); + System.out.println(resolver.checkBaseURL("file:test/config")); + System.out.println(resolver.checkBaseURL("fantasy:myconfig")); + try { + resolver.checkBaseURL("./doesnotexist"); + fail("Expected an exception for a inexistent base directory"); + } catch (MalformedURLException mfue) { + //expected + } + try { + resolver.checkBaseURL("file:doesnotexist"); + fail("Expected an exception for a inexistent base URI"); + } catch (MalformedURLException mfue) { + //expected + } + } + +} diff --git a/test/java/org/apache/fop/config/FontBaseBadTestCase.java b/test/java/org/apache/fop/config/FontBaseBadTestCase.java index b22d0f4f3..792acf59a 100644 --- a/test/java/org/apache/fop/config/FontBaseBadTestCase.java +++ b/test/java/org/apache/fop/config/FontBaseBadTestCase.java @@ -20,7 +20,7 @@ package org.apache.fop.config; /* - * this font base does not exist and a relative font path is used + * This font base does not exist and a relative font path is used. */ public class FontBaseBadTestCase extends BaseDestructiveUserConfigTestCase { @@ -28,14 +28,7 @@ public class FontBaseBadTestCase extends BaseDestructiveUserConfigTestCase { super(name); } - public void testUserConfig() throws Exception { - // Override this method from the super-class and do nothing as this test doesn't pass ATM - // TODO re-enable later - } - - /** - * @see org.apache.fop.config.BaseUserConfigTestCase#getUserConfigFilename() - */ + /** {@inheritDoc} */ public String getUserConfigFilename() { return "test_fontbase_bad.xconf"; } diff --git a/test/java/org/apache/fop/intermediate/IFMimickingTestCase.java b/test/java/org/apache/fop/intermediate/IFMimickingTestCase.java new file mode 100644 index 000000000..610bc600b --- /dev/null +++ b/test/java/org/apache/fop/intermediate/IFMimickingTestCase.java @@ -0,0 +1,149 @@ +/* + * 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.intermediate; + +import java.io.File; + +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import junit.framework.TestCase; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.events.Event; +import org.apache.fop.events.EventFormatter; +import org.apache.fop.events.EventListener; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.render.intermediate.IFDocumentHandler; +import org.apache.fop.render.intermediate.IFException; +import org.apache.fop.render.intermediate.IFSerializer; + +/** + * This test checks the correct mimicking of a different output format. + */ +public class IFMimickingTestCase extends TestCase { + + private FopFactory fopFactory; + + /** {@inheritDoc} */ + protected void setUp() throws Exception { + super.setUp(); + fopFactory = FopFactory.newInstance(); + File configFile = new File("test/test-no-xml-metrics.xconf"); + fopFactory.setUserConfig(configFile); + } + + /** + * Tests IF document handler mimicking with PDF output. + * @throws Exception if an error occurs + */ + public void testMimickingPDF() throws Exception { + doTestMimicking(MimeConstants.MIME_PDF); + } + + /** + * Tests IF document handler mimicking with PostScript output. + * @throws Exception if an error occurs + */ + public void testMimickingPS() throws Exception { + doTestMimicking(MimeConstants.MIME_POSTSCRIPT); + } + + /** + * Tests IF document handler mimicking with TIFF output. + * @throws Exception if an error occurs + */ + public void testMimickingTIFF() throws Exception { + doTestMimicking(MimeConstants.MIME_TIFF); + } + + private void doTestMimicking(String mime) throws FOPException, IFException, + TransformerException { + //Set up XMLRenderer to render to a DOM + DOMResult domResult = new DOMResult(); + + FOUserAgent userAgent = fopFactory.newFOUserAgent(); + userAgent.getEventBroadcaster().addEventListener(new EventListener() { + + public void processEvent(Event event) { + if (event.getEventGroupID().equals("org.apache.fop.fonts.FontEventAdapter")) { + fail("There must be no font-related event! Got: " + + EventFormatter.format(event)); + } + } + + }); + + //Create an instance of the target renderer so the XMLRenderer can use its font setup + IFDocumentHandler targetHandler = userAgent.getRendererFactory().createDocumentHandler( + userAgent, mime); + + //Setup painter + IFSerializer serializer = new IFSerializer(); + serializer.setContext(new IFContext(userAgent)); + serializer.mimicDocumentHandler(targetHandler); + serializer.setResult(domResult); + + userAgent.setDocumentHandlerOverride(serializer); + + Fop fop = fopFactory.newFop(userAgent); + + //minimal-pdf-a.fo uses the Gladiator font so is an ideal FO file for this test: + StreamSource src = new StreamSource(new File("test/xml/pdf-a/minimal-pdf-a.fo")); + + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + setErrorListener(transformer); + + transformer.transform(src, new SAXResult(fop.getDefaultHandler())); + } + + /** + * Sets an error listener which doesn't swallow errors like Xalan's default one. + * @param transformer the transformer to set the error listener on + */ + protected void setErrorListener(Transformer transformer) { + transformer.setErrorListener(new ErrorListener() { + + public void error(TransformerException exception) throws TransformerException { + throw exception; + } + + public void fatalError(TransformerException exception) throws TransformerException { + throw exception; + } + + public void warning(TransformerException exception) throws TransformerException { + //ignore + } + + }); + } + +} diff --git a/test/java/org/apache/fop/intermediate/IFParserTestCase.java b/test/java/org/apache/fop/intermediate/IFParserTestCase.java index 8a0268aa6..15fc74bc9 100644 --- a/test/java/org/apache/fop/intermediate/IFParserTestCase.java +++ b/test/java/org/apache/fop/intermediate/IFParserTestCase.java @@ -88,7 +88,7 @@ public class IFParserTestCase extends AbstractIntermediateTestCase { /** {@inheritDoc} */ protected String getTargetMIME() { - return MimeConstants.MIME_PDF + ";mode=painter"; + return MimeConstants.MIME_PDF; } /** {@inheritDoc} */ diff --git a/test/java/org/apache/fop/intermediate/IFTester.java b/test/java/org/apache/fop/intermediate/IFTester.java index a3291a373..7534309e7 100644 --- a/test/java/org/apache/fop/intermediate/IFTester.java +++ b/test/java/org/apache/fop/intermediate/IFTester.java @@ -48,12 +48,14 @@ import org.apache.fop.apps.FopFactory; import org.apache.fop.area.AreaTreeModel; import org.apache.fop.area.AreaTreeParser; import org.apache.fop.area.RenderPagesModel; +import org.apache.fop.events.model.EventSeverity; import org.apache.fop.fonts.FontInfo; import org.apache.fop.layoutengine.EvalCheck; import org.apache.fop.layoutengine.TrueCheck; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFRenderer; import org.apache.fop.render.intermediate.IFSerializer; +import org.apache.fop.util.ConsoleEventListenerForTests; import org.apache.fop.util.DelegatingContentHandler; /** @@ -106,9 +108,12 @@ public class IFTester { } } - private Document createIF(Document areaTreeXML) throws TransformerException { + private Document createIF(File testFile, Document areaTreeXML) throws TransformerException { try { FOUserAgent ua = fopFactory.newFOUserAgent(); + ua.setBaseURL(testFile.getParentFile().toURI().toURL().toExternalForm()); + ua.getEventBroadcaster().addEventListener( + new ConsoleEventListenerForTests(testFile.getName(), EventSeverity.WARN)); IFRenderer ifRenderer = new IFRenderer(); ifRenderer.setUserAgent(ua); @@ -160,7 +165,7 @@ public class IFTester { */ public void doIFChecks(File testFile, Element checksRoot, Document areaTreeXML) throws TransformerException { - Document ifDocument = createIF(areaTreeXML); + Document ifDocument = createIF(testFile, areaTreeXML); if (this.backupDir != null) { Transformer transformer = tfactory.newTransformer(); Source src = new DOMSource(ifDocument); diff --git a/test/java/org/apache/fop/render/RendererFactoryTest.java b/test/java/org/apache/fop/render/RendererFactoryTest.java new file mode 100644 index 000000000..4a40ac7c1 --- /dev/null +++ b/test/java/org/apache/fop/render/RendererFactoryTest.java @@ -0,0 +1,159 @@ +/* + * 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; + +import junit.framework.TestCase; + +import org.apache.commons.io.output.NullOutputStream; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.area.AreaTreeHandler; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.render.intermediate.IFDocumentHandler; +import org.apache.fop.render.intermediate.IFRenderer; +import org.apache.fop.render.pdf.PDFDocumentHandler; +import org.apache.fop.render.pdf.PDFRenderer; +import org.apache.fop.render.rtf.RTFHandler; + +/** + * Tests for {@link RendererFactory}. + */ +public class RendererFactoryTest extends TestCase { + + public void testDocumentHandlerLevel() throws Exception { + FopFactory fopFactory = FopFactory.newInstance(); + RendererFactory factory = fopFactory.getRendererFactory(); + FOUserAgent ua; + IFDocumentHandler handler; + IFDocumentHandler overrideHandler; + + ua = fopFactory.newFOUserAgent(); + handler = factory.createDocumentHandler(ua, MimeConstants.MIME_PDF); + assertTrue(handler instanceof PDFDocumentHandler); + + ua = fopFactory.newFOUserAgent(); + overrideHandler = new PDFDocumentHandler(); + overrideHandler.setContext(new IFContext(ua)); + ua.setDocumentHandlerOverride(overrideHandler); + handler = factory.createDocumentHandler(ua, null); + assertTrue(handler == overrideHandler); + + ua = fopFactory.newFOUserAgent(); + try { + handler = factory.createDocumentHandler(ua, "invalid/format"); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + //expected + } + } + + public void testRendererLevel() throws Exception { + FopFactory fopFactory = FopFactory.newInstance(); + RendererFactory factory = fopFactory.getRendererFactory(); + FOUserAgent ua; + Renderer renderer; + Renderer overrideRenderer; + + ua = fopFactory.newFOUserAgent(); + renderer = factory.createRenderer(ua, MimeConstants.MIME_PDF); + assertTrue(renderer instanceof IFRenderer); + + factory.setRendererPreferred(true); //Test legacy setting + ua = fopFactory.newFOUserAgent(); + renderer = factory.createRenderer(ua, MimeConstants.MIME_PDF); + assertTrue(renderer instanceof PDFRenderer); + + ua = fopFactory.newFOUserAgent(); + renderer = factory.createRenderer(ua, MimeConstants.MIME_FOP_IF); + assertTrue(renderer instanceof IFRenderer); + + factory.setRendererPreferred(false); + ua = fopFactory.newFOUserAgent(); + overrideRenderer = new PDFRenderer(); + overrideRenderer.setUserAgent(ua); + ua.setRendererOverride(overrideRenderer); + renderer = factory.createRenderer(ua, null); + assertTrue(renderer == overrideRenderer); + + ua = fopFactory.newFOUserAgent(); + IFDocumentHandler overrideHandler; + overrideHandler = new PDFDocumentHandler(); + overrideHandler.setContext(new IFContext(ua)); + ua.setDocumentHandlerOverride(overrideHandler); + renderer = factory.createRenderer(ua, null); + assertTrue(renderer instanceof IFRenderer); + + ua = fopFactory.newFOUserAgent(); + try { + renderer = factory.createRenderer(ua, "invalid/format"); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + //expected + } + } + + public void testFOEventHandlerLevel() throws Exception { + FopFactory fopFactory = FopFactory.newInstance(); + RendererFactory factory = fopFactory.getRendererFactory(); + FOUserAgent ua; + FOEventHandler foEventHandler; + FOEventHandler overrideFOEventHandler; + + ua = fopFactory.newFOUserAgent(); + foEventHandler = factory.createFOEventHandler( + ua, MimeConstants.MIME_PDF, new NullOutputStream()); + assertTrue(foEventHandler instanceof AreaTreeHandler); + + ua = fopFactory.newFOUserAgent(); + foEventHandler = factory.createFOEventHandler( + ua, MimeConstants.MIME_RTF, new NullOutputStream()); + assertTrue(foEventHandler instanceof RTFHandler); + + ua = fopFactory.newFOUserAgent(); + try { + foEventHandler = factory.createFOEventHandler( + ua, "invalid/format", new NullOutputStream()); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + //expected + } + + ua = fopFactory.newFOUserAgent(); + try { + foEventHandler = factory.createFOEventHandler( + ua, MimeConstants.MIME_PDF, null); + fail("Expected FOPException because of missing OutputStream"); + } catch (FOPException fe) { + //expected + } + + ua = fopFactory.newFOUserAgent(); + overrideFOEventHandler = new RTFHandler(ua, new NullOutputStream()); + ua.setFOEventHandlerOverride(overrideFOEventHandler); + foEventHandler = factory.createFOEventHandler( + ua, null, null); + assertTrue(foEventHandler == overrideFOEventHandler); + } + +} diff --git a/test/java/org/apache/fop/threading/FOPTestbed.java b/test/java/org/apache/fop/threading/FOPTestbed.java index 7ced868c4..737317bec 100644 --- a/test/java/org/apache/fop/threading/FOPTestbed.java +++ b/test/java/org/apache/fop/threading/FOPTestbed.java @@ -20,9 +20,9 @@ package org.apache.fop.threading; import java.io.File; -import java.io.InputStream; import java.io.OutputStream; import java.text.DecimalFormat; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -41,6 +41,8 @@ import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.container.ContainerUtil; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.CountingOutputStream; +import org.apache.commons.io.output.NullOutputStream; /** * Testbed for multi-threading tests. The class can run a configurable set of task a number of @@ -55,13 +57,17 @@ public class FOPTestbed extends AbstractLogEnabled private File outputDir; private Configuration fopCfg; private FOProcessor foprocessor; + private boolean writeToDevNull; private int counter = 0; + private List results = Collections.synchronizedList(new java.util.LinkedList()); + /** {@inheritDoc} */ public void configure(Configuration configuration) throws ConfigurationException { this.threads = configuration.getChild("threads").getValueAsInteger(10); this.outputDir = new File(configuration.getChild("output-dir").getValue()); + this.writeToDevNull = configuration.getChild("devnull").getValueAsBoolean(false); Configuration tasks = configuration.getChild("tasks"); this.repeat = tasks.getAttributeAsInteger("repeat", 1); Configuration[] entries = tasks.getChildren("task"); @@ -85,11 +91,12 @@ public class FOPTestbed extends AbstractLogEnabled this.counter = 0; //Initialize threads + ThreadGroup workerGroup = new ThreadGroup("FOP workers"); List threadList = new java.util.LinkedList(); for (int ti = 0; ti < this.threads; ti++) { TaskRunner runner = new TaskRunner(); ContainerUtil.enableLogging(runner, getLogger()); - Thread thread = new Thread(runner); + Thread thread = new Thread(workerGroup, runner, "Worker- " + ti); threadList.add(thread); } @@ -112,7 +119,38 @@ public class FOPTestbed extends AbstractLogEnabled //ignore } } - getLogger().info("Stress test duration: " + (System.currentTimeMillis() - start) + "ms"); + long duration = System.currentTimeMillis() - start; + + report(duration); + } + + private void report(long duration) { + int count = this.results.size(); + int failures = 0; + long bytesWritten = 0; + System.out.println("Report on " + count + " tasks:"); + Iterator iter = this.results.iterator(); + while (iter.hasNext()) { + Result res = (Result)iter.next(); + if (res.failure != null) { + System.out.println("FAIL: " + (res.end - res.start) + " " + res.task); + System.out.println(" -> " + res.failure.getMessage()); + failures++; + } else { + System.out.println("good: " + (res.end - res.start) + " " + res.filesize + + " " + res.task); + bytesWritten += res.filesize; + } + } + System.out.println("Stress test duration: " + duration + "ms"); + if (failures > 0) { + System.out.println(failures + " failures of " + count + " documents!!!"); + } else { + float mb = 1024f * 1024f; + System.out.println("Bytes written: " + (bytesWritten / mb) + " MB, " + + (bytesWritten * 1000 / duration) + " bytes / sec"); + System.out.println("NO failures with " + count + " documents."); + } } private class TaskRunner extends AbstractLogEnabled implements Runnable { @@ -222,29 +260,61 @@ public class FOPTestbed extends AbstractLogEnabled public void execute() throws Exception { getLogger().info("Processing: " + def); - DecimalFormat df = new DecimalFormat("00000"); - File outfile = new File(outputDir, df.format(num) + fop.getTargetFileExtension()); - OutputStream out = new java.io.FileOutputStream(outfile); + long start = System.currentTimeMillis(); try { - InputStream in; - Templates templates; - - if (def.getFO() != null) { - in = new java.io.FileInputStream(new File(def.getFO())); - templates = null; + DecimalFormat df = new DecimalFormat("00000"); + File outfile = new File(outputDir, df.format(num) + fop.getTargetFileExtension()); + OutputStream out; + if (writeToDevNull) { + out = new NullOutputStream(); } else { - in = new java.io.FileInputStream(new File(def.getXML())); - templates = def.getTemplates(); + out = new java.io.FileOutputStream(outfile); + out = new java.io.BufferedOutputStream(out); } + CountingOutputStream cout = new CountingOutputStream(out); try { - fop.process(in, templates, out); + Source src; + Templates templates; + + if (def.getFO() != null) { + src = new StreamSource(new File(def.getFO())); + templates = null; + } else { + src = new StreamSource(new File(def.getXML())); + templates = def.getTemplates(); + } + fop.process(src, templates, cout); } finally { - IOUtils.closeQuietly(in); + IOUtils.closeQuietly(cout); } - } finally { - IOUtils.closeQuietly(out); + results.add(new Result(def, start, System.currentTimeMillis(), + cout.getByteCount())); + } catch (Exception e) { + results.add(new Result(def, start, System.currentTimeMillis(), e)); + throw e; } } } + private static class Result { + + private TaskDef task; + private long start; + private long end; + private long filesize; + private Throwable failure; + + public Result(TaskDef task, long start, long end, long filesize) { + this(task, start, end, null); + this.filesize = filesize; + } + + public Result(TaskDef task, long start, long end, Throwable failure) { + this.task = task; + this.start = start; + this.end = end; + this.failure = failure; + } + } + }
\ No newline at end of file diff --git a/test/java/org/apache/fop/threading/FOProcessor.java b/test/java/org/apache/fop/threading/FOProcessor.java index 05c8f6fe1..dd663da05 100644 --- a/test/java/org/apache/fop/threading/FOProcessor.java +++ b/test/java/org/apache/fop/threading/FOProcessor.java @@ -19,9 +19,9 @@ package org.apache.fop.threading; -import java.io.InputStream; import java.io.OutputStream; +import javax.xml.transform.Source; import javax.xml.transform.Templates; /** @@ -31,12 +31,12 @@ public interface FOProcessor { /** * Process a file. - * @param in the InputStream for the FO or XML file + * @param src the Source for the FO or XML file * @param templates a JAXP Templates object for an XSLT transformation or null * @param out the OutputStream for the target file * @throws Exception if an error occurs */ - void process(InputStream in, Templates templates, OutputStream out) + void process(Source src, Templates templates, OutputStream out) throws Exception; /** diff --git a/test/java/org/apache/fop/threading/FOProcessorImpl.java b/test/java/org/apache/fop/threading/FOProcessorImpl.java index 4ba7da658..2b580bbd0 100644 --- a/test/java/org/apache/fop/threading/FOProcessorImpl.java +++ b/test/java/org/apache/fop/threading/FOProcessorImpl.java @@ -19,8 +19,9 @@ package org.apache.fop.threading; -import java.io.InputStream; import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; import javax.xml.transform.Result; import javax.xml.transform.Source; @@ -29,19 +30,23 @@ import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.stream.StreamSource; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.commons.io.FilenameUtils; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.MimeConstants; +import org.apache.fop.events.Event; +import org.apache.fop.events.EventFormatter; +import org.apache.fop.events.EventListener; +import org.apache.fop.events.model.EventSeverity; /** * Default implementation of the FOProcessor interface using FOP. @@ -71,9 +76,17 @@ public class FOProcessorImpl extends AbstractLogEnabled } /** {@inheritDoc} */ - public void process(InputStream in, Templates templates, OutputStream out) + public void process(Source src, Templates templates, OutputStream out) throws org.apache.fop.apps.FOPException, java.io.IOException { FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); + foUserAgent.setBaseURL(src.getSystemId()); + try { + URL url = new URL(src.getSystemId()); + String filename = FilenameUtils.getName(url.getPath()); + foUserAgent.getEventBroadcaster().addEventListener(new AvalonAdapter(filename)); + } catch (MalformedURLException mfue) { + throw new RuntimeException(mfue); + } Fop fop = fopFactory.newFop(this.mime, foUserAgent, out); try { @@ -83,7 +96,6 @@ public class FOProcessorImpl extends AbstractLogEnabled } else { transformer = templates.newTransformer(); } - Source src = new StreamSource(in); Result res = new SAXResult(fop.getDefaultHandler()); transformer.transform(src, res); } catch (TransformerException e) { @@ -96,4 +108,29 @@ public class FOProcessorImpl extends AbstractLogEnabled return this.fileExtension; } + private class AvalonAdapter implements EventListener { + + private String filename; + + public AvalonAdapter(String filename) { + this.filename = filename; + } + + public void processEvent(Event event) { + String msg = EventFormatter.format(event); + EventSeverity severity = event.getSeverity(); + if (severity == EventSeverity.INFO) { + //getLogger().info(filename + ": " + msg); + } else if (severity == EventSeverity.WARN) { + //getLogger().warn(filename + ": " + msg); + } else if (severity == EventSeverity.ERROR) { + getLogger().error(filename + ": " + msg); + } else if (severity == EventSeverity.FATAL) { + getLogger().fatalError(filename + ": " + msg); + } else { + assert false; + } + } + + } }
\ No newline at end of file diff --git a/test/layoutengine/standard-testcases/afp-extension_1.xml b/test/layoutengine/standard-testcases/afp-extension_1.xml index a6779177a..70bbef450 100644 --- a/test/layoutengine/standard-testcases/afp-extension_1.xml +++ b/test/layoutengine/standard-testcases/afp-extension_1.xml @@ -25,7 +25,8 @@ </info> <fo> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp"> + xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp" + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> <fo:layout-master-set> <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in"> <afp:include-page-overlay name="O1SAMP1 "/> @@ -35,7 +36,9 @@ <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="normal"> + <fo:page-sequence master-reference="normal" fox:test-ignore="this"> + <afp:invoke-medium-map name="MYMAP"/> + <afp:tag-logical-element name="foo" value="bar"/> <fo:flow flow-name="xsl-region-body"> <fo:block>Text on page <fo:page-number/>.</fo:block> <fo:block break-before="page">Text on page <fo:page-number/>.</fo:block> @@ -43,7 +46,7 @@ </fo:page-sequence> </fo:root> </fo> - <checks> + <checks xmlns:afp="apache:fop:extensions:afp"> <eval expected="4" xpath="count(/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*)"/> <eval expected="O1SAMP1 " xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*[1]/@name"/> <eval expected="S1ISLOGO" xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*[2]/@name"/> @@ -52,5 +55,29 @@ <eval expected="My NOP" xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*[4]/@name"/> <eval expected="4" xpath="count(/areaTree/pageSequence/pageViewport[@nr=2]/page/extension-attachments/child::*)"/> + + <eval expected="2" xpath="count(/areaTree/pageSequence/extension-attachments/child::*)"/> + <eval expected="MYMAP" xpath="/areaTree/pageSequence/extension-attachments/child::*[1]/@name"/> + <eval expected="bar" xpath="/areaTree/pageSequence/extension-attachments/afp:tag-logical-element[@name = 'foo']/@value"/> + + <!-- This just tests if extension attributes make it through to the PageSequence object. --> + <eval expected="this" xpath="/areaTree/pageSequence/@fox:test-ignore" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"/> </checks> + <if-checks xmlns:if="http://xmlgraphics.apache.org/fop/intermediate" + xmlns:afp="apache:fop:extensions:afp"> + <eval expected="4" xpath="count(//if:page[@name = '1']/if:page-header/child::*)"/> + <eval expected="O1SAMP1 " xpath="//if:page[@name = '1']/if:page-header/afp:include-page-overlay[1]/@name"/> + <eval expected="S1ISLOGO" xpath="//if:page[@name = '1']/if:page-header/afp:include-page-segment[1]/@name"/> + <eval expected="The TLE Value" xpath="//if:page[@name = '1']/if:page-header/afp:tag-logical-element[@name = 'The TLE Name']/@value"/> + <eval expected="My NOP" xpath="//if:page[@name = '1']/if:page-header/afp:no-operation[1]/@name"/> + <eval expected="insert up to 32k of character data here!" xpath="//if:page[@name = '1']/if:page-header/afp:no-operation[1]"/> + + <eval expected="4" xpath="count(//if:page[@name = '2']/if:page-header/child::*)"/> + + <eval expected="MYMAP" xpath="//if:page-sequence/afp:invoke-medium-map/@name"/> + <eval expected="bar" xpath="//if:page-sequence/afp:tag-logical-element[@name = 'foo']/@value"/> + + <!-- This just tests if extension attributes make it through to the PageSequence object. --> + <eval expected="this" xpath="//if:page-sequence/@fox:test-ignore" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"/> + </if-checks> </testcase> diff --git a/test/layoutengine/standard-testcases/leader_border_padding.xml b/test/layoutengine/standard-testcases/leader_border_padding.xml index e2bd0c231..18f300244 100644 --- a/test/layoutengine/standard-testcases/leader_border_padding.xml +++ b/test/layoutengine/standard-testcases/leader_border_padding.xml @@ -130,7 +130,9 @@ <eval expected="360000" xpath="//flow/block[4]/lineArea/@ipd"/> <eval expected="34000" xpath="//flow/block[4]/lineArea/space/@ipd"/> <eval expected="36000" xpath="//flow/block[4]/lineArea/space/@ipda"/> - <eval expected="8615" xpath="//flow/block[4]/lineArea/space/@offset"/> + <eval expected="1000" xpath="//flow/block[4]/lineArea/space/@bpd"/> + <eval expected="3000" xpath="//flow/block[4]/lineArea/space/@bpda"/> + <eval expected="7616" xpath="//flow/block[4]/lineArea/space/@offset"/> <eval expected="(solid,#ff0000,1000)" xpath="//flow/block[4]/lineArea/space/@border-after"/> <eval expected="(solid,#ff0000,1000)" xpath="//flow/block[4]/lineArea/space/@border-before"/> <eval expected="(solid,#ff0000,1000)" xpath="//flow/block[4]/lineArea/space/@border-end"/> @@ -192,7 +194,9 @@ <eval expected="360000" xpath="//flow/block[9]/lineArea/@ipd"/> <eval expected="16000" xpath="//flow/block[9]/lineArea/space/@ipd"/> <eval expected="36000" xpath="//flow/block[9]/lineArea/space/@ipda"/> - <eval expected="8615" xpath="//flow/block[9]/lineArea/space/@offset"/> + <eval expected="1000" xpath="//flow/block[9]/lineArea/space/@bpd"/> + <eval expected="21000" xpath="//flow/block[9]/lineArea/space/@bpda"/> + <eval expected="7616" xpath="//flow/block[9]/lineArea/space/@offset"/> <eval expected="(solid,#ff0000,5000)" xpath="//flow/block[9]/lineArea/space/@border-after"/> <eval expected="(solid,#ff0000,5000)" xpath="//flow/block[9]/lineArea/space/@border-before"/> <eval expected="(solid,#ff0000,5000)" xpath="//flow/block[9]/lineArea/space/@border-end"/> @@ -262,7 +266,9 @@ <eval expected="360000" xpath="//flow/block[14]/lineArea/@ipd"/> <eval expected="24000" xpath="//flow/block[14]/lineArea/space/@ipd"/> <eval expected="36000" xpath="//flow/block[14]/lineArea/space/@ipda"/> - <eval expected="8615" xpath="//flow/block[14]/lineArea/space/@offset"/> + <eval expected="1000" xpath="//flow/block[14]/lineArea/space/@bpd"/> + <eval expected="9000" xpath="//flow/block[14]/lineArea/space/@bpda"/> + <eval expected="7616" xpath="//flow/block[14]/lineArea/space/@offset"/> <eval expected="(solid,#ff0000,3000)" xpath="//flow/block[14]/lineArea/space/@border-after"/> <eval expected="(solid,#ff0000,1000)" xpath="//flow/block[14]/lineArea/space/@border-before"/> <eval expected="(solid,#ff0000,2000)" xpath="//flow/block[14]/lineArea/space/@border-end"/> diff --git a/test/layoutengine/standard-testcases/page-position-last_break-before_bugzilla46489.xml b/test/layoutengine/standard-testcases/page-position-last_break-before_bugzilla46489.xml new file mode 100644 index 000000000..d855ffd76 --- /dev/null +++ b/test/layoutengine/standard-testcases/page-position-last_break-before_bugzilla46489.xml @@ -0,0 +1,93 @@ +<?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$ --> +<testcase> + <info> + <p> + This test checks page-masters (reference orientation). + </p> + </info> + <fo> + <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> + <fo:layout-master-set> + <fo:simple-page-master master-name="FrontPage" + page-width="210mm" page-height="297mm" margin="12.7mm"> + <fo:region-body/> + </fo:simple-page-master> + <fo:simple-page-master master-name="Following" + page-width="210mm" page-height="297mm" margin="12.7mm"> + <fo:region-body/> + </fo:simple-page-master> + <fo:page-sequence-master master-name="pages"> + <fo:repeatable-page-master-alternatives> + <fo:conditional-page-master-reference page-position="first" + master-reference="FrontPage"/> + <fo:conditional-page-master-reference page-position="rest" + master-reference="Following"/> + <fo:conditional-page-master-reference page-position="last" + master-reference="Following"/> + </fo:repeatable-page-master-alternatives> + </fo:page-sequence-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="pages"> + <fo:flow flow-name="xsl-region-body"> + <fo:block> + <fo:table table-layout="fixed" width="100%"> + <fo:table-column column-width="proportional-column-width(30)"/> + <fo:table-column column-width="proportional-column-width(70)"/> + <fo:table-body> + <fo:table-row> + <fo:table-cell padding="4pt"> + <fo:block color="#6D6E71">Some text</fo:block> + </fo:table-cell> + <fo:table-cell padding="4pt"> + <fo:block>More text before break. More text before break. More text before + break.</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:table table-layout="fixed" width="100%"> + <fo:table-body> + <fo:table-row> + <fo:table-cell padding="4pt"> + <fo:block id="the-block" color="#6D6E71" page-break-before="always">TEXT AFTER + BREAK</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:table table-layout="fixed" width="100%"> + <fo:table-body> + <fo:table-row> + <fo:table-cell padding="4pt"> + <fo:block>blah blah blah</fo:block> + <fo:block>blah blah blah</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + </fo:block> + </fo:flow> + </fo:page-sequence> + </fo:root> + </fo> + <checks> + <true xpath="count(//page) = 2" /> + </checks> +</testcase> diff --git a/test/test-no-xml-metrics.xconf b/test/test-no-xml-metrics.xconf new file mode 100644 index 000000000..beb24c0ef --- /dev/null +++ b/test/test-no-xml-metrics.xconf @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<fop version="1.0"> + <!-- Switch off font caching for the purposes of the unit test --> + <use-cache>false</use-cache> + + <!-- Base URL for resolving relative URLs --> + <base>./</base> + + <!-- Font Base URL for resolving relative font URLs --> + <font-base>./test/resources/fonts</font-base> + + <renderers> + <renderer mime="application/pdf"> + <filterList type="content"> + <value>null</value> + </filterList> + <fonts> + <font embed-url="glb12.ttf" kerning="yes"> + <font-triplet name="Gladiator Bold" style="normal" weight="normal"/> + <font-triplet name="Gladiator" style="normal" weight="bold"/> + <font-triplet name="Gladiator" style="normal" weight="normal"/> + </font> + </fonts> + </renderer> + <renderer mime="application/postscript"> + <fonts> + <font embed-url="glb12.ttf" kerning="yes"> + <font-triplet name="Gladiator Bold" style="normal" weight="normal"/> + <font-triplet name="Gladiator" style="normal" weight="bold"/> + <font-triplet name="Gladiator" style="normal" weight="normal"/> + </font> + </fonts> + </renderer> + <renderer mime="image/tiff"> + <fonts> + <font embed-url="glb12.ttf" kerning="yes"> + <font-triplet name="Gladiator Bold" style="normal" weight="normal"/> + <font-triplet name="Gladiator" style="normal" weight="bold"/> + <font-triplet name="Gladiator" style="normal" weight="normal"/> + </font> + </fonts> + </renderer> + </renderers> +</fop> |