diff options
author | Simon Pepping <spepping@apache.org> | 2011-06-15 11:44:43 +0000 |
---|---|---|
committer | Simon Pepping <spepping@apache.org> | 2011-06-15 11:44:43 +0000 |
commit | 613fa7fe70fb6b47da38b2783b41a14987df42e5 (patch) | |
tree | 1d20a74559df8dc17186d8f4176902268669ac55 | |
parent | 4465eac09ff580741af26abe86dacc8bd089b040 (diff) | |
parent | 82163b6a159d14c838f9c3a3b8d40137f93fc11b (diff) | |
download | xmlgraphics-fop-613fa7fe70fb6b47da38b2783b41a14987df42e5.tar.gz xmlgraphics-fop-613fa7fe70fb6b47da38b2783b41a14987df42e5.zip |
Merged /xmlgraphics/fop/trunk:r1094917-1135999
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ComplexScripts@1136002 13f79535-47bb-0310-9956-ffa450edef68
62 files changed, 2050 insertions, 786 deletions
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 99fb1df20..efe992714 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -454,6 +454,29 @@ <Bug pattern="NM_CONFUSING"/> </Match> <!-- /The names of public methods will not be modified --> + <!-- These casts are checked in the code --> + <Match> + <Class name="org.apache.fop.render.afp.AFPImageHandlerGraphics2D"/> + <Method name="isCompatible"/> + <Bug pattern="BC_UNCONFIRMED_CAST"/> + </Match> + <Match> + <Class name="org.apache.fop.render.afp.AFPImageHandlerSVG"/> + <Method name="isCompatible"/> + <Bug pattern="BC_UNCONFIRMED_CAST"/> + </Match> + <Match> + <Class name="org.apache.fop.render.afp.AbstractPDFImageHandler"/> + <Method name="handleImage"/> + <Bug pattern="BC_UNCONFIRMED_CAST"/> + </Match> + <!-- /These casts are checked in the code --> + <!-- return null is OK --> + <Match> + <Class name="org.apache.fop.svg.ACIUtils"/> + <Method name="findFontsForBatikACI"/> + <Bug pattern="PZLA_PREFER_ZERO_LENGTH_ARRAYS"/> + </Match> <!-- Automatically generated list of exclusions --> <Match> <Class name="org.apache.fop.area.BodyRegion"/> diff --git a/src/documentation/content/xdocs/team.xml b/src/documentation/content/xdocs/team.xml index 561fdfc73..587058889 100644 --- a/src/documentation/content/xdocs/team.xml +++ b/src/documentation/content/xdocs/team.xml @@ -37,6 +37,8 @@ is an XP/Agile practitioner with interests in open source content management and electronic document generation. He is a UK expat now residing in Singapore.</li> <li id="ad"><link href="mailto:adelmelle@apache.org">Andreas Delmelle</link> (AD)</li> + <li id="pjfh"> <a href="mailto:phancock.at.apache.org">Peter Hancock</a> + (PH) is a Java developer from England.</li> <li id="vh"><link href="mailto:vhennebert@apache.org">Vincent Hennebert</link> (VH), a Java developer from France; with an interest in typography, and in using Fop as a pdf renderer for book-like documents.</li> @@ -55,6 +57,10 @@ from the TeX/LaTeX world. See his <fork href="http://www.leverkruid.eu">home page</fork> for some of his private projects.</li> <li id="jp"><link href="mailto:pietsch@apache.org">Jörg Pietschmann</link> (JP)</li> + <li id="ps"><link href="mailto:tcho@club-internet.fr">Pascal Sancho</link> (PS) + is an XML developper from Aix-en-Provence (France). He works on software solutions for + rendering various kind of documents on various supports, more specifically taking care + of PDF generation with FOP.</li> </ul> </section> <section id="contribute-active"> @@ -101,7 +107,7 @@ <li id="lf"><link href="mailto:lfurini AT cs.unibo.it">Luca Furini</link> (LF)</li> <li id="cg"><link href="mailto:chrisg@apache.org">Christian Geisert</link> (CG)</li> <li id="sg"><link href="mailto:stanislav@apache.org">Stanislav Gorkhover</link></li> - <li id="ph"><link href="mailto:pherweg@web.de">Peter Herweg</link> (PH)</li> + <li id="ph"><link href="mailto:pherweg@web.de">Peter Herweg</link></li> <li id="fj"><link href="mailto:fotis@apache.org">Fotis Jannidis</link></li> <li id="kl"><link href="mailto:klease@apache.org">Karen Lease</link></li> <li id="kll"><link href="mailto:keiron@apache.org">Keiron Liddle</link></li> @@ -109,7 +115,6 @@ <li id="gm"><link href="mailto:gmazza@apache.org">Glen Mazza</link></li> <li id="wvm"><link href="mailto:vmote@apache.org">Victor Mote</link></li> <li id="jn"><link href="mailto:jordan@apache.org">Jordan Naftolin</link></li> - <li id="ps"><link href="mailto:tcho@club-internet.fr">Pascal Sancho</link> (PS)</li> <li id="as"><link href="mailto:arved@apache.org">Arved Sandstrom</link></li> <li id="es"><link href="mailto:eschaeffer@apache.org">Eric Schaeffer</link></li> <li id="ot"><link href="mailto:olegt@apache.org">Oleg Tkachenko</link> (OT)</li> diff --git a/src/documentation/content/xdocs/trunk/extensions.xml b/src/documentation/content/xdocs/trunk/extensions.xml index 107f6c5b3..ef9b6f6c2 100644 --- a/src/documentation/content/xdocs/trunk/extensions.xml +++ b/src/documentation/content/xdocs/trunk/extensions.xml @@ -24,14 +24,14 @@ </header> <body> <p> - By "extension", we mean any data that can be placed in the input XML document that + By "extension", we mean any data that can be placed in the input XML document that is not addressed by the XSL-FO standard. - By having a mechanism for supporting extensions, FOP is able to add features that + By having a mechanism for supporting extensions, FOP is able to add features that are not covered in the specification. </p> <p> - The extensions documented here are included with FOP, and are automatically available - to you. If you wish to add an extension of your own to FOP, please see the + The extensions documented here are included with FOP, and are automatically available + to you. If you wish to add an extension of your own to FOP, please see the <a href="../dev/extensions.html">Developers' Extension Page</a>. </p> <note>All extensions require the correct use of an appropriate namespace in your input document.</note> @@ -47,7 +47,7 @@ <title>Namespace</title> <p> By convention, FO extensions in FOP use the "fox" namespace prefix. - To use any of the FO extensions, add a namespace entry for + To use any of the FO extensions, add a namespace entry for <code>http://xmlgraphics.apache.org/fop/extensions</code> to the root element: </p> <source><![CDATA[<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" @@ -56,9 +56,9 @@ <section id="bookmarks"> <title>PDF Bookmarks</title> <p> - In previous versions of Apache FOP there was a <code>fox:outline</code> element + In old versions of Apache FOP there was a <code>fox:outline</code> element which was used to create outlines in PDF files. The redesigned code makes use - of the new <a href="http://www.w3.org/TR/xsl11/#fo_bookmark-tree">bookmark feature defined in the latest XSL 1.1 working draft</a>. + of the <a href="http://www.w3.org/TR/xsl11/#fo_bookmark-tree">bookmark feature defined in the W3C XSL 1.1 standard</a>. </p> </section> <section id="named-destinations"> @@ -131,7 +131,7 @@ to following pages. Here is an example of FO code creating such a table-header:< The <code>fox:external-document</code> element is structurally a peer to <code>fo:page-sequence</code>, so wherever you can put an <code>fo:page-sequence</code> you could also place a <code>fox:external-document</code>. - Therefore, the specified contents for <code>fo:root</code> change to: + Therefore, the specified contents for <code>fo:root</code> change to: </p> <p> <code> @@ -246,7 +246,7 @@ to following pages. Here is an example of FO code creating such a table-header:< </section> <section id="prepress"> <title>Prepress Support</title> - + <p> This section defines a number of extensions related to <a href="http://en.wikipedia.org/wiki/Prepress">prepress</a> support. @@ -260,15 +260,15 @@ to following pages. Here is an example of FO code creating such a table-header:< <note> Those extensions have been implemented in the PDF and Java2D renderers only. </note> - + <section id="scale"> <title>fox:scale</title> <p>Value: <number>{1,2}</p> <p>Initial: 1</p> <p>Applies to: fo:simple-page-master</p> <p> - This property specifies a scale factor along resp. the x and y axes. If only one number - is provided it is used for both the x and y scales. A scale factor smaller than 1 + This property specifies a scale factor along resp. the x and y axes. If only one number + is provided it is used for both the x and y scales. A scale factor smaller than 1 shrinks the page. A scale factor greater than 1 enlarges the page. </p> </section> @@ -287,7 +287,7 @@ to following pages. Here is an example of FO code creating such a table-header:< If there are three values, the top is set to the first value, the left and right are set to the second, and the bottom is set to the third. If there are four values, they apply to the top, right, bottom, and left, respectively. - (Corresponds to <a href="http://www.w3.org/TR/xsl11/#padding">the definition of + (Corresponds to <a href="http://www.w3.org/TR/xsl11/#padding">the definition of padding</a>). </p> <p> @@ -305,12 +305,12 @@ to following pages. Here is an example of FO code creating such a table-header:< </p> <p>Applies to: fo:simple-page-master</p> <p> - Same behaviour as with fox:bleed. The initial value is set to the same values as the + Same behaviour as with fox:bleed. The initial value is set to the same values as the fox:bleed property. </p> <p> This extension indirectly defines the MediaBox and is calculated by expanding - the TrimBox by the crop offsets. The lengths must be non-negative. + the TrimBox by the crop offsets. The lengths must be non-negative. </p> </section> <section id="cropBox"> @@ -326,7 +326,7 @@ to following pages. Here is an example of FO code creating such a table-header:< The crop box controls how Acrobat displays the page (CropBox in PDF) or how the Java2DRenderer sizes the output media. The PDF specification defines that the CropBox defaults to the MediaBox. This extension follows that definition. To simplify usage and cover most use cases, the three supported enumeration - values "trim-box", "bleed-box" and "media-box" set the CropBox to one of those three other boxes. + values "trim-box", "bleed-box" and "media-box" set the CropBox to one of those three other boxes. </p> <p> If requested in the future, we could offer to specify the CropBox in absolute coordinates rather diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index ea994b521..c71f6abf8 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -287,6 +287,7 @@ out = proc.getOutputStream();]]></source> <optimize-resources>false</optimize-resources> <safe-set-page-device>false</safe-set-page-device> <dsc-compliant>true</dsc-compliant> + <rendering>quality</rendering> </renderer>]]></source> <p> The default value for the "auto-rotate-landscape" setting is "false". Setting it @@ -319,6 +320,12 @@ out = proc.getOutputStream();]]></source> the particular postscript implementation issuing unwanted postscript subsystem initgraphics/erasepage calls on each setpagedevice call. </p> + <p> + The default value for the "rendering" setting is "quality". Setting it to "size" + optimizes rendering for smaller file sizes which can involve minor compromises in + rendering quality. For example, solid borders are then painted as plain rectangles + instead of the elaborate painting instructions required for mixed-color borders. + </p> </section> <section id="ps-limitations"> <title>Limitations</title> @@ -784,6 +791,29 @@ Note that the value of the encoding attribute in the example is the double-byte <source><![CDATA[ <images mode="b+w" bits-per-pixel="1" dithering-quality="maximum"/>]]></source> </section> + <section id="afp-goca-config"> + <title>GOCA (Vector Graphics)</title> + <p> + Not all AFP implementations support GOCA. Some also have bugs related to GOCA. Therefore, + it is desirable to have some control over the generation of GOCA graphics. + </p> + <p> + GOCA is enabled by default. You can disable GOCA entirely in which case the AFP support + falls back to generating bitmaps for vector graphics. Example: + </p> + <source><![CDATA[ + <goca enabled="false"/>]]></source> + <p> + Some AFP implementations have trouble rendering text in GOCA. You can instruct the AFP + support to render text as shapes (i.e. use vector graphics to paint text). Example: + </p> + <source><![CDATA[ + <goca enabled="true" text="shapes"/>]]></source> + <p> + If you disable GOCA or let text render as shapes, the size of the generated AFP usually + increases considerably. + </p> + </section> <section id="afp-shading-config"> <title>Shading</title> <p> @@ -932,14 +962,19 @@ Note that the value of the encoding attribute in the example is the double-byte </fo:layout-master-set> ]]></source> <p>The no-operation extension element can appear as child of - simple-page-master (appears after "Begin Page" BPG), - page-sequence (appears after "Begin Named Page Group" BNG - and declarations (appears after "Begin Document" BDT). + <code>simple-page-master</code> (appears after "Begin Page" BPG), + <code>page-sequence</code> (appears after "Begin Named Page Group" BNG + and <code>declarations</code> (appears after "Begin Document" BDT). Multiple no-operation extension elements inside the same formatting object are allowed. Each NOP will appear right after the respective "Begin" field indicated above even if it - is specified as the last child under its parent. However, the order inside the parent + is specified as the last child under its parent. The order inside the parent will be maintained. - The name attribute is mandatory but will not appear inside the AFP stream. + The "placement" attribute can be used to have the NOP appear before + the "End" field of the object rather than after the "Begin" field. Specify + <code>placement="before-end"</code> to do that. Please note that, at the moment, this only + has an effect for NOPs that are children of the <code>page-sequence</code> formatting + object. + The "name" attribute is mandatory but will not appear inside the AFP stream. </p> </section> <section id="afp-invoke-medium-map"> diff --git a/src/documentation/content/xdocs/trunk/upgrading.xml b/src/documentation/content/xdocs/trunk/upgrading.xml index 9f5879174..ceb5b120b 100644 --- a/src/documentation/content/xdocs/trunk/upgrading.xml +++ b/src/documentation/content/xdocs/trunk/upgrading.xml @@ -31,12 +31,6 @@ </p> <ul> <li> - More than half of the codebase has been rewritten over the - last three years. With version 0.93 the code has reached - <strong>production level</strong>, and continues to improve with - version 0.94. - </li> - <li> The API of FOP has changed considerably and is not backwards-compatible with versions 0.20.5 and 0.91beta. Version 0.92 introduced the <strong>new stable diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-datatypes.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-datatypes.xsd index 3f7a33ad6..29443ea99 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-datatypes.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-datatypes.xsd @@ -92,7 +92,7 @@ <!-- TODO refine me: \w+ will not be good enough for CMYK color, for example <xs:pattern value="\((solid|dotted|dashed|double|groove|ridge|inset|outset),\w+,\d+(,collapse-(inner|outer))?"/> --> - <xs:pattern value="\((solid|dotted|dashed|double|groove|ridge|inset|outset),.+)"/> + <xs:pattern value="\((solid|dotted|dashed|double|groove|ridge|inset|outset),.+\)"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="lengthListType"> 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 9de37a81a..b6fa00bd2 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd @@ -21,7 +21,7 @@ xmlns:mf="http://xmlgraphics.apache.org/fop/intermediate" xmlns:nav="http://xmlgraphics.apache.org/fop/intermediate/document-navigation"> <xs:import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2001/xml.xsd"/> + schemaLocation="xml.xsd"/> <xs:import namespace="http://xmlgraphics.apache.org/fop/intermediate/document-navigation" schemaLocation="fop-intermediate-format-ng-nav.xsd"/> <xs:import namespace="adobe:ns:meta/" schemaLocation="xmp-metadata.xsd"/> diff --git a/src/documentation/intermediate-format-ng/xml.xsd b/src/documentation/intermediate-format-ng/xml.xsd new file mode 100644 index 000000000..aea7d0db0 --- /dev/null +++ b/src/documentation/intermediate-format-ng/xml.xsd @@ -0,0 +1,287 @@ +<?xml version='1.0'?> +<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?> +<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns ="http://www.w3.org/1999/xhtml" + xml:lang="en"> + + <xs:annotation> + <xs:documentation> + <div> + <h1>About the XML namespace</h1> + + <div class="bodytext"> + <p> + This schema document describes the XML namespace, in a form + suitable for import by other schema documents. + </p> + <p> + See <a href="http://www.w3.org/XML/1998/namespace.html"> + http://www.w3.org/XML/1998/namespace.html</a> and + <a href="http://www.w3.org/TR/REC-xml"> + http://www.w3.org/TR/REC-xml</a> for information + about this namespace. + </p> + <p> + Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. + </p> + <p> + See further below in this document for more information about <a + href="#usage">how to refer to this schema document from your own + XSD schema documents</a> and about <a href="#nsversioning">the + namespace-versioning policy governing this schema document</a>. + </p> + </div> + </div> + </xs:documentation> + </xs:annotation> + + <xs:attribute name="lang"> + <xs:annotation> + <xs:documentation> + <div> + + <h3>lang (as an attribute name)</h3> + <p> + denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.</p> + + </div> + <div> + <h4>Notes</h4> + <p> + Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. + </p> + <p> + See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt"> + http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a> + and the IANA language subtag registry at + <a href="http://www.iana.org/assignments/language-subtag-registry"> + http://www.iana.org/assignments/language-subtag-registry</a> + for further information. + </p> + <p> + The union allows for the 'un-declaration' of xml:lang with + the empty string. + </p> + </div> + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:union memberTypes="xs:language"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value=""/> + </xs:restriction> + </xs:simpleType> + </xs:union> + </xs:simpleType> + </xs:attribute> + + <xs:attribute name="space"> + <xs:annotation> + <xs:documentation> + <div> + + <h3>space (as an attribute name)</h3> + <p> + denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.</p> + + </div> + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:NCName"> + <xs:enumeration value="default"/> + <xs:enumeration value="preserve"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + + <xs:attribute name="base" type="xs:anyURI"> <xs:annotation> + <xs:documentation> + <div> + + <h3>base (as an attribute name)</h3> + <p> + denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.</p> + + <p> + See <a + href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a> + for information about this attribute. + </p> + </div> + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="id" type="xs:ID"> + <xs:annotation> + <xs:documentation> + <div> + + <h3>id (as an attribute name)</h3> + <p> + denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.</p> + + <p> + See <a + href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a> + for information about this attribute. + </p> + </div> + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attributeGroup name="specialAttrs"> + <xs:attribute ref="xml:base"/> + <xs:attribute ref="xml:lang"/> + <xs:attribute ref="xml:space"/> + <xs:attribute ref="xml:id"/> + </xs:attributeGroup> + + <xs:annotation> + <xs:documentation> + <div> + + <h3>Father (in any context at all)</h3> + + <div class="bodytext"> + <p> + denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: + </p> + <blockquote> + <p> + In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". + </p> + </blockquote> + </div> + </div> + </xs:documentation> + </xs:annotation> + + <xs:annotation> + <xs:documentation> + <div xml:id="usage" id="usage"> + <h2><a name="usage">About this schema document</a></h2> + + <div class="bodytext"> + <p> + This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow <code>xml:base</code>, + <code>xml:lang</code>, <code>xml:space</code> or + <code>xml:id</code> attributes on elements they define. + </p> + <p> + To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: + </p> + <pre> + <schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/xml.xsd"/> + </pre> + <p> + or + </p> + <pre> + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2009/01/xml.xsd"/> + </pre> + <p> + Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. + </p> + <pre> + <type . . .> + . . . + <attributeGroup ref="xml:specialAttrs"/> + </pre> + <p> + will define a type which will schema-validate an instance element + with any of those attributes. + </p> + </div> + </div> + </xs:documentation> + </xs:annotation> + + <xs:annotation> + <xs:documentation> + <div id="nsversioning" xml:id="nsversioning"> + <h2><a name="nsversioning">Versioning policy for this schema document</a></h2> + <div class="bodytext"> + <p> + In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + <a href="http://www.w3.org/2009/01/xml.xsd"> + http://www.w3.org/2009/01/xml.xsd</a>. + </p> + <p> + At the date of issue it can also be found at + <a href="http://www.w3.org/2001/xml.xsd"> + http://www.w3.org/2001/xml.xsd</a>. + </p> + <p> + The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at <a href="http://www.w3.org/2001/xml.xsd"> + http://www.w3.org/2001/xml.xsd + </a> + will change accordingly; the version at + <a href="http://www.w3.org/2009/01/xml.xsd"> + http://www.w3.org/2009/01/xml.xsd + </a> + will not change. + </p> + <p> + Previous dated (and unchanging) versions of this schema + document are at: + </p> + <ul> + <li><a href="http://www.w3.org/2009/01/xml.xsd"> + http://www.w3.org/2009/01/xml.xsd</a></li> + <li><a href="http://www.w3.org/2007/08/xml.xsd"> + http://www.w3.org/2007/08/xml.xsd</a></li> + <li><a href="http://www.w3.org/2004/10/xml.xsd"> + http://www.w3.org/2004/10/xml.xsd</a></li> + <li><a href="http://www.w3.org/2001/03/xml.xsd"> + http://www.w3.org/2001/03/xml.xsd</a></li> + </ul> + </div> + </div> + </xs:documentation> + </xs:annotation> + +</xs:schema> + diff --git a/src/documentation/skinconf.xml b/src/documentation/skinconf.xml index 8190982e3..7a15fce72 100644 --- a/src/documentation/skinconf.xml +++ b/src/documentation/skinconf.xml @@ -103,9 +103,8 @@ which will be used to configure the chosen Forrest skin. For some skins just set the attributes to blank. --> <trail location="alt"> - <link1 name="apache.org" href="http://www.apache.org/"/> - <link2 name="XML Federation" href="http://xml.apache.org/"/> - <link3 name="xmlgraphics.apache.org" href="http://xmlgraphics.apache.org/"/> + <link1 name="The Apache Software Foundation" href="http://www.apache.org/"/> + <link2 name="Apache XML Graphics Project" href="http://xmlgraphics.apache.org/"/> </trail> <!-- Configure the TOC, i.e. the Table of Contents. diff --git a/src/java/org/apache/fop/afp/AFPGraphics2D.java b/src/java/org/apache/fop/afp/AFPGraphics2D.java index af58b5471..d0f1b41b0 100644 --- a/src/java/org/apache/fop/afp/AFPGraphics2D.java +++ b/src/java/org/apache/fop/afp/AFPGraphics2D.java @@ -166,6 +166,14 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** + * Returns the AFP resource manager associated with this {@link Graphics2D} instance. + * @return the resource manager + */ + public AFPResourceManager getResourceManager() { + return this.resourceManager; + } + + /** * Sets the AFP resource info * * @param resourceInfo the AFP resource info @@ -497,12 +505,14 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void draw(Shape shape) { LOG.debug("draw() shape=" + shape); doDrawing(shape, false); } /** {@inheritDoc} */ + @Override public void fill(Shape shape) { LOG.debug("fill() shape=" + shape); doDrawing(shape, true); @@ -521,6 +531,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void drawString(String str, float x, float y) { try { if (customTextHandler != null && !textAsShapes) { @@ -534,21 +545,25 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public GraphicsConfiguration getDeviceConfiguration() { return graphicsConfig; } /** {@inheritDoc} */ + @Override public Graphics create() { return new AFPGraphics2D(this); } /** {@inheritDoc} */ + @Override public void dispose() { this.graphicsObj = null; } /** {@inheritDoc} */ + @Override public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), observer); } @@ -595,6 +610,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { // draw with AWT Graphics2D @@ -609,6 +625,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void drawRenderedImage(RenderedImage img, AffineTransform xform) { int imgWidth = img.getWidth(); int imgHeight = img.getHeight(); @@ -658,17 +675,20 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { LOG.debug("drawRenderableImage() NYI: img=" + img + ", xform=" + xform); } /** {@inheritDoc} */ + @Override public FontMetrics getFontMetrics(Font f) { LOG.debug("getFontMetrics() NYI: f=" + f); return null; } /** {@inheritDoc} */ + @Override public void setXORMode(Color col) { LOG.debug("setXORMode() NYI: col=" + col); } @@ -681,6 +701,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void copyArea(int x, int y, int width, int height, int dx, int dy) { LOG.debug("copyArea() NYI: "); } diff --git a/src/java/org/apache/fop/afp/AFPPaintingState.java b/src/java/org/apache/fop/afp/AFPPaintingState.java index 85d3e914b..2be3a85ac 100644 --- a/src/java/org/apache/fop/afp/AFPPaintingState.java +++ b/src/java/org/apache/fop/afp/AFPPaintingState.java @@ -73,6 +73,11 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState /** the output resolution */ private int resolution = 240; // 240 dpi + /** determines whether GOCA is enabled or disabled */ + private boolean gocaEnabled = true; + /** determines whether to stroke text in GOCA mode or to use text operators where possible */ + private boolean strokeGocaText = false; + /** the current page */ private transient AFPPagePaintingState pagePaintingState = new AFPPagePaintingState(); @@ -276,12 +281,46 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState return this.resolution; } + /** + * Controls whether GOCA is enabled or disabled. + * @param enabled true if GOCA is enabled, false if it is disabled + */ + public void setGOCAEnabled(boolean enabled) { + this.gocaEnabled = enabled; + } + + /** + * Indicates whether GOCA is enabled or disabled. + * @return true if GOCA is enabled, false if GOCA is disabled + */ + public boolean isGOCAEnabled() { + return this.gocaEnabled; + } + + /** + * Controls whether to stroke text in GOCA mode or to use text operators where possible. + * @param stroke true to stroke, false to paint with text operators where possible + */ + public void setStrokeGOCAText(boolean stroke) { + this.strokeGocaText = stroke; + } + + /** + * Indicates whether to stroke text in GOCA mode or to use text operators where possible. + * @return true to stroke, false to paint with text operators where possible + */ + public boolean isStrokeGOCAText() { + return this.strokeGocaText; + } + /** {@inheritDoc} */ + @Override protected AbstractData instantiateData() { return new AFPData(); } /** {@inheritDoc} */ + @Override protected AbstractPaintingState instantiate() { return new AFPPaintingState(); } @@ -423,6 +462,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public Object clone() { AFPPaintingState paintingState = (AFPPaintingState) super.clone(); paintingState.pagePaintingState = (AFPPagePaintingState) this.pagePaintingState.clone(); @@ -436,6 +476,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public String toString() { return "AFPPaintingState{" + "portraitRotation=" + portraitRotation + ", landscapeRotation=" + landscapeRotation + ", colorImages=" + colorImages @@ -548,6 +589,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public Object clone() { AFPPagePaintingState state = new AFPPagePaintingState(); state.width = this.width; @@ -559,6 +601,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public String toString() { return "AFPPagePaintingState{width=" + width + ", height=" + height + ", orientation=" + orientation + ", fonts=" + fonts + ", fontCount=" + fontCount + "}"; @@ -577,6 +620,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState private String imageUri = null; /** {@inheritDoc} */ + @Override public Object clone() { AFPData obj = (AFPData) super.clone(); obj.filled = this.filled; @@ -585,12 +629,14 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public String toString() { return "AFPData{" + super.toString() + ", filled=" + filled + ", imageUri=" + imageUri + "}"; } /** {@inheritDoc} */ + @Override protected AbstractData instantiate() { return new AFPData(); } diff --git a/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java b/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java index 0adffcd8b..7a2b8ed36 100644 --- a/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java +++ b/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java @@ -55,9 +55,10 @@ public class FontPatternExtractor { UnparsedStructuredField strucField; while ((strucField = parser.readNextStructuredField()) != null) { if (strucField.getSfTypeID() == 0xD3EE89) { + byte[] sfData = strucField.getData(); println(strucField.toString()); - HexDump.dump(strucField.getData(), 0, printStream, 0); - baout.write(strucField.getData()); + HexDump.dump(sfData, 0, printStream, 0); + baout.write(sfData); } } diff --git a/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java b/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java index 8d94bf9fc..2ce7e3442 100644 --- a/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java +++ b/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java @@ -33,6 +33,8 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo protected static final int MAX_DATA_LEN = 8192; private byte[] predecessorNameBytes; + private boolean appended; + private boolean prologPresent; /** * Main constructor @@ -41,7 +43,7 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo * the name of this graphics segment */ public GraphicsChainedSegment(String name) { - super(name); + this(name, null, false, false); } /** @@ -51,24 +53,35 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo * the name of this graphics segment * @param predecessorNameBytes * the name of the predecessor in this chain + * @param appended true if this segment is appended to the previous one + * @param prologPresent true if this segment starts with a prolog */ - public GraphicsChainedSegment(String name, byte[] predecessorNameBytes) { + public GraphicsChainedSegment(String name, byte[] predecessorNameBytes, + boolean appended, boolean prologPresent) { super(name); - this.predecessorNameBytes = predecessorNameBytes; + if (predecessorNameBytes != null) { + this.predecessorNameBytes = new byte[predecessorNameBytes.length]; + System.arraycopy(predecessorNameBytes, 0, + this.predecessorNameBytes, 0, predecessorNameBytes.length); + } + this.appended = appended; + this.prologPresent = prologPresent; } /** {@inheritDoc} */ + @Override public int getDataLength() { return 14 + super.getDataLength(); } private static final byte APPEND_NEW_SEGMENT = 0; -// private static final byte PROLOG = 4; -// private static final byte APPEND_TO_EXISING = 48; + private static final byte APPEND_TO_EXISING = 6; + private static final byte PROLOG = 0x10; private static final int NAME_LENGTH = 4; /** {@inheritDoc} */ + @Override protected int getNameLength() { return NAME_LENGTH; } @@ -78,6 +91,7 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo } /** {@inheritDoc} */ + @Override public void writeToStream(OutputStream os) throws IOException { byte[] data = new byte[14]; data[0] = getOrderCode(); // BEGIN_SEGMENT @@ -88,7 +102,12 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo System.arraycopy(nameBytes, 0, data, 2, NAME_LENGTH); data[6] = 0x00; // FLAG1 (ignored) - data[7] = APPEND_NEW_SEGMENT; + + //FLAG2 + data[7] |= this.appended ? APPEND_TO_EXISING : APPEND_NEW_SEGMENT; + if (this.prologPresent) { + data[7] |= PROLOG; + } int dataLength = super.getDataLength(); byte[] len = BinaryUtils.convert(dataLength, 2); @@ -105,7 +124,8 @@ public final class GraphicsChainedSegment extends AbstractGraphicsDrawingOrderCo } /** {@inheritDoc} */ + @Override public String toString() { - return "GraphicsChainedSegment(name=" + super.getName() + ")"; + return "GraphicsChainedSegment(name=" + super.getName() + ", len: " + getDataLength() + ")"; } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/afp/goca/GraphicsData.java b/src/java/org/apache/fop/afp/goca/GraphicsData.java index 1ba757e4b..de10d3fbb 100644 --- a/src/java/org/apache/fop/afp/goca/GraphicsData.java +++ b/src/java/org/apache/fop/afp/goca/GraphicsData.java @@ -32,11 +32,14 @@ import org.apache.fop.afp.util.StringUtils; public final class GraphicsData extends AbstractGraphicsDrawingOrderContainer { /** the maximum graphics data length */ - public static final int MAX_DATA_LEN = 8192; + public static final int MAX_DATA_LEN = GraphicsChainedSegment.MAX_DATA_LEN + 16; + //+16 to avoid unnecessary, practically empty GraphicsData instances. /** the graphics segment */ private GraphicsChainedSegment currentSegment = null; + private boolean segmentedData; + /** * Main constructor */ @@ -44,11 +47,21 @@ public final class GraphicsData extends AbstractGraphicsDrawingOrderContainer { } /** {@inheritDoc} */ + @Override public int getDataLength() { return 8 + super.getDataLength(); } /** + * Sets the indicator that this instance is a part of a series of segmented data chunks. + * This indirectly sets the SegFlag on the SFI header. + * @param segmented true if this data object is not the last of the series + */ + public void setSegmentedData(boolean segmented) { + this.segmentedData = segmented; + } + + /** * Returns a new segment name * * @return a new segment name @@ -60,28 +73,40 @@ public final class GraphicsData extends AbstractGraphicsDrawingOrderContainer { } /** - * Creates a new graphics segment + * Creates a new graphics segment. * * @return a newly created graphics segment */ public GraphicsChainedSegment newSegment() { + return newSegment(false, false); + } + + /** + * Creates a new graphics segment. + * @param appended true if this segment is appended to the previous one + * @param prologPresent true if started with a prolog + * @return a newly created graphics segment + */ + public GraphicsChainedSegment newSegment(boolean appended, boolean prologPresent) { String segmentName = createSegmentName(); if (currentSegment == null) { currentSegment = new GraphicsChainedSegment(segmentName); } else { currentSegment.setComplete(true); - currentSegment = new GraphicsChainedSegment(segmentName, currentSegment.getNameBytes()); + currentSegment = new GraphicsChainedSegment(segmentName, + currentSegment.getNameBytes(), appended, prologPresent); } super.addObject(currentSegment); return currentSegment; } /** {@inheritDoc} */ + @Override public void addObject(StructuredData object) { if (currentSegment == null || (currentSegment.getDataLength() + object.getDataLength()) >= GraphicsChainedSegment.MAX_DATA_LEN) { - newSegment(); + newSegment(true, false); } currentSegment.addObject(object); } @@ -97,6 +122,7 @@ public final class GraphicsData extends AbstractGraphicsDrawingOrderContainer { } /** {@inheritDoc} */ + @Override public void writeToStream(OutputStream os) throws IOException { byte[] data = new byte[9]; copySF(data, SF_CLASS, Type.DATA, Category.GRAPHICS); @@ -104,14 +130,18 @@ public final class GraphicsData extends AbstractGraphicsDrawingOrderContainer { byte[] len = BinaryUtils.convert(dataLength, 2); data[1] = len[0]; // Length byte 1 data[2] = len[1]; // Length byte 2 + if (this.segmentedData) { + data[6] |= 32; //Data is segmented + } os.write(data); writeObjects(objects, os); } /** {@inheritDoc} */ + @Override public String toString() { - return "GraphicsData"; + return "GraphicsData(len: " + getDataLength() + ")"; } /** diff --git a/src/java/org/apache/fop/afp/goca/GraphicsEndProlog.java b/src/java/org/apache/fop/afp/goca/GraphicsEndProlog.java new file mode 100644 index 000000000..d20ff5b29 --- /dev/null +++ b/src/java/org/apache/fop/afp/goca/GraphicsEndProlog.java @@ -0,0 +1,55 @@ +/* + * 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.afp.goca; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * The end of a prolog. + */ +public class GraphicsEndProlog extends AbstractGraphicsDrawingOrder { + + /** {@inheritDoc} */ + public void writeToStream(OutputStream os) throws IOException { + byte[] data = new byte[] { + getOrderCode(), // GEPROL order code + 0x00, // Reserved + }; + os.write(data); + } + + /** {@inheritDoc} */ + public int getDataLength() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "GraphicsEndProlog (GEPROL)"; + } + + /** {@inheritDoc} */ + @Override + byte getOrderCode() { + return 0x3E; + } +} diff --git a/src/java/org/apache/fop/afp/modca/GraphicsObject.java b/src/java/org/apache/fop/afp/modca/GraphicsObject.java index c94ad5ffc..cf732c77a 100644 --- a/src/java/org/apache/fop/afp/modca/GraphicsObject.java +++ b/src/java/org/apache/fop/afp/modca/GraphicsObject.java @@ -39,6 +39,7 @@ import org.apache.fop.afp.goca.GraphicsBox; import org.apache.fop.afp.goca.GraphicsChainedSegment; import org.apache.fop.afp.goca.GraphicsCharacterString; import org.apache.fop.afp.goca.GraphicsData; +import org.apache.fop.afp.goca.GraphicsEndProlog; import org.apache.fop.afp.goca.GraphicsFillet; import org.apache.fop.afp.goca.GraphicsFullArc; import org.apache.fop.afp.goca.GraphicsImage; @@ -62,8 +63,8 @@ public class GraphicsObject extends AbstractDataObject { private GraphicsData currentData = null; /** list of objects contained within this container */ - protected List/*<GraphicsData>*/ objects - = new java.util.ArrayList/*<GraphicsData>*/(); + protected List<GraphicsData> objects + = new java.util.ArrayList<GraphicsData>(); /** the graphics state */ private final GraphicsState graphicsState = new GraphicsState(); @@ -325,6 +326,10 @@ public class GraphicsObject extends AbstractDataObject { * @param y the y coordinate */ public void addString(String str, int x, int y) { + //Work-around for InfoPrint's AFP which loses character set state over Graphics Data + //boundaries. + addObject(new GraphicsSetCharacterSet(graphicsState.characterSet)); + addObject(new GraphicsCharacterString(str, x, y)); } @@ -342,6 +347,13 @@ public class GraphicsObject extends AbstractDataObject { addObject(new GraphicsAreaEnd()); } + /** + * Ends the prolog. + */ + public void endProlog() { + addObject(new GraphicsEndProlog()); + } + /** {@inheritDoc} */ @Override public String toString() { @@ -359,9 +371,9 @@ public class GraphicsObject extends AbstractDataObject { /** {@inheritDoc} */ @Override public void setComplete(boolean complete) { - Iterator it = objects.iterator(); + Iterator<GraphicsData> it = objects.iterator(); while (it.hasNext()) { - Completable completedObject = (Completable)it.next(); + Completable completedObject = it.next(); completedObject.setComplete(true); } super.setComplete(complete); diff --git a/src/java/org/apache/fop/afp/parser/MODCAParser.java b/src/java/org/apache/fop/afp/parser/MODCAParser.java index 356d4f169..d0d0eced9 100644 --- a/src/java/org/apache/fop/afp/parser/MODCAParser.java +++ b/src/java/org/apache/fop/afp/parser/MODCAParser.java @@ -20,15 +20,23 @@ package org.apache.fop.afp.parser; import java.io.DataInputStream; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.afp.parser.UnparsedStructuredField.Introducer; + /** * An simple MO:DCA/AFP parser. */ public class MODCAParser { + private static final Log LOG = LogFactory.getLog(MODCAParser.class); + + private static final int INTRODUCER_LENGTH = 8; + /** The carriage control character (0x5A) used to indicate the start of a structured field. */ public static final byte CARRIAGE_CONTROL_CHAR = (byte)(0x5A & 0xFF); @@ -39,19 +47,9 @@ public class MODCAParser { * @param in the {@link InputStream} to read the AFP file from. */ public MODCAParser(InputStream in) { - if (!in.markSupported()) { - in = new java.io.BufferedInputStream(in); - } this.din = new DataInputStream(in); } - /** - * Returns the {@link DataInputStream} used for parsing structured fields. - * @return the data input stream - */ - public DataInputStream getDataInputStream() { - return this.din; - } /** * Reads the next structured field from the input stream. @@ -61,20 +59,46 @@ public class MODCAParser { * @throws IOException if an I/O error occurs */ public UnparsedStructuredField readNextStructuredField() throws IOException { - try { - while (true) { - byte b = din.readByte(); //Skip 0x5A character if necessary (ex. AFP) - if (b == 0x0D || b == 0x0A) { - //CR and LF may be used as field delimiters - continue; - } else if (b == CARRIAGE_CONTROL_CHAR) { - break; //Signals the start of a new structured field - } + + //Find the SF delimiter + do { + //Exhausted streams and so no next SF + // - null return represents this case + // TODO should this happen? + if (din.available() == 0) { + return null; + } + } while (din.readByte() != CARRIAGE_CONTROL_CHAR); + + //Read introducer as byte array to preserve any data not parsed below + byte[] introducerData = new byte[INTRODUCER_LENGTH]; //Length of introducer + din.readFully(introducerData); + + Introducer introducer = new Introducer(introducerData); + + int dataLength = introducer.getLength() - INTRODUCER_LENGTH; + + //Handle optional extension + byte[] extData = null; + if (introducer.isExtensionPresent()) { + short extLength = 0; + extLength = (short)((din.readByte()) & 0xFF); + if (extLength > 0) { + extData = new byte[extLength - 1]; + din.readFully(extData); + dataLength -= extLength; } - } catch (EOFException eof) { - return null; } - return UnparsedStructuredField.readStructuredField(getDataInputStream()); - } + //Read payload + byte[] data = new byte[dataLength]; + din.readFully(data); + UnparsedStructuredField sf = new UnparsedStructuredField(introducer, data, extData); + + if (LOG.isTraceEnabled()) { + LOG.trace(sf); + } + + return sf; + } } diff --git a/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java b/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java index f775c05ee..0e2eeeb0c 100644 --- a/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java +++ b/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java @@ -19,337 +19,331 @@ package org.apache.fop.afp.parser; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; -import java.io.EOFException; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintStream; import java.text.DecimalFormat; -import org.apache.commons.io.HexDump; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** - * Represents an unparsed (generic) AFP structured field. - */ -public class UnparsedStructuredField { - - private static final Log LOG = LogFactory.getLog(UnparsedStructuredField.class); - - private static final int INTRODUCER_LENGTH = 8; - - private short sfLength; - private byte sfClassCode; - private byte sfTypeCode; - private byte sfCategoryCode; - private boolean sfiExtensionPresent; - private boolean sfiSegmentedData; - private boolean sfiPaddingPresent; - private short extLength; - private byte[] introducerData; - private byte[] extData; - private byte[] data; - - /** - * Default constructor. - */ - public UnparsedStructuredField() { - //nop - } - - /** - * Reads a structured field from a {@link DataInputStream}. The resulting object can be - * further interpreted be follow-up code. - * @param din the stream to read from - * @return the generic structured field - * @throws IOException if an I/O error occurs - */ - public static UnparsedStructuredField readStructuredField(DataInputStream din) - throws IOException { - UnparsedStructuredField sf = new UnparsedStructuredField(); - - //Read introducer as byte array to preserve any data not parsed below - din.mark(INTRODUCER_LENGTH); - sf.introducerData = new byte[INTRODUCER_LENGTH]; //Length of introducer - din.readFully(sf.introducerData); - din.reset(); - - //Parse the introducer - short len; - try { - len = din.readShort(); - } catch (EOFException eof) { - return null; - } - sf.sfLength = len; - sf.sfClassCode = din.readByte(); - sf.sfTypeCode = din.readByte(); - sf.sfCategoryCode = din.readByte(); - - //Flags - byte f = din.readByte(); - sf.sfiExtensionPresent = (f & 0x01) != 0; - sf.sfiSegmentedData = (f & 0x04) != 0; - sf.sfiPaddingPresent = (f & 0x10) != 0; - din.skip(2); //Reserved - - int dataLength = sf.sfLength - INTRODUCER_LENGTH; - - //Handle optional extension - if (sf.sfiExtensionPresent) { - sf.extLength = (short)(((short)din.readByte()) & 0xFF); - if (sf.extLength > 0) { - sf.extData = new byte[sf.extLength - 1]; - din.readFully(sf.extData); - dataLength -= sf.extLength; - } - } - - //Read payload - sf.data = new byte[dataLength]; - din.readFully(sf.data); - - if (LOG.isTraceEnabled()) { - LOG.trace(sf); - } - - return sf; - } - - /** {@inheritDoc} */ - public String toString() { - StringBuffer sb = new StringBuffer("Structured Field: "); - sb.append(Integer.toHexString(getSfTypeID()).toUpperCase()); - sb.append(", len="); - sb.append(new DecimalFormat("00000").format(getSfLength())); - sb.append(" ").append(getTypeCodeAsString()); - sb.append(" ").append(getCategoryCodeAsString()); - if (isSfiExtensionPresent()) { - sb.append(", SFI extension present"); - } - if (isSfiSegmentedData()) { - sb.append(", segmented data"); - } - if (isSfiPaddingPresent()) { - sb.append(", with padding"); - } - return sb.toString(); - } - - /** - * Dump the structured field as hex data to the given {@link PrintStream}. - * @param out the {@link PrintStream} to dump to - * @throws IOException if an I/O error occurs - */ - public void dump(PrintStream out) throws IOException { - out.println(toString()); - HexDump.dump(getData(), 0, out, 0); - } - - /** - * Dump the structured field as hex data to <code>System.out</code>. - * @throws IOException if an I/O error occurs - */ - public void dump() throws IOException { - dump(System.out); - } - - /** - * Returns type code function name for this field. - * @return the type code function name - */ - public String getTypeCodeAsString() { - switch ((int)getSfTypeCode() & 0xFF) { - case 0xA0: return "Attribute"; - case 0xA2: return "CopyCount"; - case 0xA6: return "Descriptor"; - case 0xA7: return "Control"; - case 0xA8: return "Begin"; - case 0xA9: return "End"; - case 0xAB: return "Map"; - case 0xAC: return "Position"; - case 0xAD: return "Process"; - case 0xAF: return "Include"; - case 0xB0: return "Table"; - case 0xB1: return "Migration"; - case 0xB2: return "Variable"; - case 0xB4: return "Link"; - case 0xEE: return "Data"; - default: return "Unknown:" + Integer.toHexString((int)getSfTypeCode()).toUpperCase(); - } - } - - /** - * Returns category code function name for this field. - * @return the category code function name - */ - public String getCategoryCodeAsString() { - switch ((int)getSfCategoryCode() & 0xFF) { - case 0x5F: return "Page Segment"; - case 0x6B: return "Object Area"; - case 0x77: return "Color Attribute Table"; - case 0x7B: return "IM Image"; - case 0x88: return "Medium"; - case 0x89: return "Font"; - case 0x8A: return "Coded Font"; - case 0x90: return "Process Element"; - case 0x92: return "Object Container"; - case 0x9B: return "Presentation Text"; - case 0xA7: return "Index"; - case 0xA8: return "Document"; - case 0xAD: return "Page Group"; - case 0xAF: return "Page"; - case 0xBB: return "Graphics"; - case 0xC3: return "Data Resource"; - case 0xC4: return "Document Environment Group (DEG)"; - case 0xC6: return "Resource Group"; - case 0xC7: return "Object Environment Group (OEG)"; - case 0xC9: return "Active Environment Group (AEG)"; - case 0xCC: return "Medium Map"; - case 0xCD: return "Form Map"; - case 0xCE: return "Name Resource"; - case 0xD8: return "Page Overlay"; - case 0xD9: return "Resource Environment Group (REG)"; - case 0xDF: return "Overlay"; - case 0xEA: return "Data Supression"; - case 0xEB: return "Bar Code"; - case 0xEE: return "No Operation"; - case 0xFB: return "Image"; - default: return "Unknown:" + Integer.toHexString((int)getSfTypeCode()).toUpperCase(); - } - } - - /** - * Returns the structured field's length. - * @return the field length - */ - public short getSfLength() { - return this.sfLength; - } - - /** - * Returns the structured field's identifier. - * @return the field identifier - */ - public int getSfTypeID() { - return ((getSfClassCode() & 0xFF) << 16) - | ((getSfTypeCode() & 0xFF) << 8) - | (getSfCategoryCode() & 0xFF); - } - - /** - * Returns the structured field's class code. - * @return the field class code - */ - public byte getSfClassCode() { - return this.sfClassCode; - } - - /** - * Returns the structured field's type code. - * @return the type code - */ - public byte getSfTypeCode() { - return this.sfTypeCode; - } - - /** - * Returns the structured field's category code. - * @return the sfCategoryCode - */ - public byte getSfCategoryCode() { - return this.sfCategoryCode; - } - - /** - * Indicates whether an field introducer extension is present. - * @return true if an field introducer extension is present - */ - public boolean isSfiExtensionPresent() { - return this.sfiExtensionPresent && (this.extData != null); - } - - /** - * Indicates whether segmented data is present. - * @return true if the data is segmented - */ - public boolean isSfiSegmentedData() { - return this.sfiSegmentedData; - } - - /** - * Indicates whether the data is padded. - * @return true if the data is padded - */ - public boolean isSfiPaddingPresent() { - return this.sfiPaddingPresent; - } - - /** - * Returns the length of the extension if present. - * @return the length of the extension (or 0 if no extension is present) - */ - public short getExtLength() { - return this.extLength; - } - - /** - * Returns the extension data if present. - * @return the extension data (or null if no extension is present) - */ - public byte[] getExtData() { - return this.extData; - } - - /** - * Returns the structured field's payload. - * @return the field's data - */ - public byte[] getData() { - return this.data; - } - - /** - * Returns the structured field's introducer data. - * @return the introducer data - */ - public byte[] getIntroducerData() { - return this.introducerData; - } - - /** - * Returns the complete structured field as a byte array. - * @return the complete field data - */ - public byte[] getCompleteFieldAsBytes() { - int len = INTRODUCER_LENGTH; - if (isSfiExtensionPresent()) { - len += getExtLength(); - } - len += getData().length; - byte[] bytes = new byte[len]; - int pos = 0; - System.arraycopy(getIntroducerData(), 0, bytes, pos, INTRODUCER_LENGTH); - pos += INTRODUCER_LENGTH; - if (isSfiExtensionPresent()) { - System.arraycopy(getExtData(), 0, bytes, pos, getExtLength()); - pos += getExtLength(); - } - System.arraycopy(getData(), 0, bytes, pos, getData().length); - return bytes; - } - - /** - * Writes this structured field to the given {@link OutputStream}. - * @param out the output stream - * @throws IOException if an I/O error occurs - */ - public void writeTo(OutputStream out) throws IOException { - out.write(this.introducerData); - if (isSfiExtensionPresent()) { - out.write(this.extData); - } - out.write(this.data); - } +* Represents an unparsed (generic) AFP structured field. +*/ +public final class UnparsedStructuredField { + + private final Introducer introducer; + private final byte[] extData; + private final byte[] data; + + /** + * + * @param Structured field introducer + * @param data Structured field data + * @param extData Structured field extension data + */ + UnparsedStructuredField(Introducer introducer, + byte[] data, byte[] extData) { + this.introducer = introducer; + this.data = data; + if (extData != null) { + this.extData = extData; + } else { + this.extData = null; + } + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer("Structured Field: "); + sb.append(Integer.toHexString(getSfTypeID()).toUpperCase()); + sb.append(", len="); + sb.append(new DecimalFormat("00000").format(getSfLength())); + sb.append(" ").append(getTypeCodeAsString()); + sb.append(" ").append(getCategoryCodeAsString()); + if (isSfiExtensionPresent()) { + sb.append(", SFI extension present"); + } + if (isSfiSegmentedData()) { + sb.append(", segmented data"); + } + if (isSfiPaddingPresent()) { + sb.append(", with padding"); + } + return sb.toString(); + } + + + /** + * Returns type code function name for this field. + * @return the type code function name + */ + private String getTypeCodeAsString() { + switch (getSfTypeCode() & 0xFF) { + case 0xA0: return "Attribute"; + case 0xA2: return "CopyCount"; + case 0xA6: return "Descriptor"; + case 0xA7: return "Control"; + case 0xA8: return "Begin"; + case 0xA9: return "End"; + case 0xAB: return "Map"; + case 0xAC: return "Position"; + case 0xAD: return "Process"; + case 0xAF: return "Include"; + case 0xB0: return "Table"; + case 0xB1: return "Migration"; + case 0xB2: return "Variable"; + case 0xB4: return "Link"; + case 0xEE: return "Data"; + default: return "Unknown:" + Integer.toHexString(getSfTypeCode()).toUpperCase(); + } + } + + /** + * Returns category code function name for this field. + * @return the category code function name + */ + private String getCategoryCodeAsString() { + switch (getSfCategoryCode() & 0xFF) { + case 0x5F: return "Page Segment"; + case 0x6B: return "Object Area"; + case 0x77: return "Color Attribute Table"; + case 0x7B: return "IM Image"; + case 0x88: return "Medium"; + case 0x89: return "Font"; + case 0x8A: return "Coded Font"; + case 0x90: return "Process Element"; + case 0x92: return "Object Container"; + case 0x9B: return "Presentation Text"; + case 0xA7: return "Index"; + case 0xA8: return "Document"; + case 0xAD: return "Page Group"; + case 0xAF: return "Page"; + case 0xBB: return "Graphics"; + case 0xC3: return "Data Resource"; + case 0xC4: return "Document Environment Group (DEG)"; + case 0xC6: return "Resource Group"; + case 0xC7: return "Object Environment Group (OEG)"; + case 0xC9: return "Active Environment Group (AEG)"; + case 0xCC: return "Medium Map"; + case 0xCD: return "Form Map"; + case 0xCE: return "Name Resource"; + case 0xD8: return "Page Overlay"; + case 0xD9: return "Resource Environment Group (REG)"; + case 0xDF: return "Overlay"; + case 0xEA: return "Data Supression"; + case 0xEB: return "Bar Code"; + case 0xEE: return "No Operation"; + case 0xFB: return "Image"; + default: return "Unknown:" + Integer.toHexString(getSfTypeCode()).toUpperCase(); + } + } + + /** + * Returns the structured field's length. + * @return the field length + */ + public short getSfLength() { + return introducer.length; + } + + /** + * Returns the structured field's identifier. + * @return the field identifier + */ + public int getSfTypeID() { + return ((getSfClassCode() & 0xFF) << 16) + | ((getSfTypeCode() & 0xFF) << 8) + | (getSfCategoryCode() & 0xFF); + } + + /** + * Returns the structured field's class code. + * @return the field class code + */ + public byte getSfClassCode() { + return introducer.classCode; + } + + /** + * Returns the structured field's type code. + * @return the type code + */ + public byte getSfTypeCode() { + return introducer.typeCode; + } + + /** + * Returns the structured field's category code. + * @return the sfCategoryCode + */ + public byte getSfCategoryCode() { + return introducer.categoryCode; + } + + /** + * Indicates whether an field introducer extension is present. + * @return true if an field introducer extension is present + */ + public boolean isSfiExtensionPresent() { + return introducer.extensionPresent && (this.extData != null); + } + + /** + * Indicates whether segmented data is present. + * @return true if the data is segmented + */ + public boolean isSfiSegmentedData() { + return introducer.segmentedData; + } + + /** + * Indicates whether the data is padded. + * @return true if the data is padded + */ + public boolean isSfiPaddingPresent() { + return introducer.paddingPresent; + } + + /** + * Returns the length of the extension if present. + * @return the length of the extension (or 0 if no extension is present) + */ + public short getExtLength() { + return (extData != null) ? (short)(extData.length + 1) : 0; + + } + + /** + * Returns the extension data if present. + * @return the extension data (or null if no extension is present) + */ + byte[] getExtData() { + if (this.extData == null) { + return new byte[0]; + } + byte[] rtn = new byte[this.extData.length]; + System.arraycopy(this.extData, 0, rtn, 0, rtn.length); + return rtn; + } + + /** + * Returns the structured field's payload. + * @return the field's data + */ + public byte[] getData() { + if (this.data == null) { + return new byte[0]; + } + byte[] rtn = new byte[this.data.length]; + System.arraycopy(this.data, 0, rtn, 0, rtn.length); + return rtn; + } + + //For unit testing + byte[] getIntroducerData() { + return introducer.getIntroducerData(); + } + + /** + * Returns the complete structured field as a byte array. + * @return the complete field data + */ + public byte[] getCompleteFieldAsBytes() { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(getSfLength()); + try { + writeTo(baos); + } catch (IOException ioe) { + //nop + } + return baos.toByteArray(); + + } + + /** + * Writes this structured field to the given {@link OutputStream}. + * @param out the output stream + * @throws IOException if an I/O error occurs + */ + public void writeTo(OutputStream out) throws IOException { + out.write(introducer.introducerData); + if (isSfiExtensionPresent()) { + out.write(this.extData.length + 1); + out.write(this.extData); + } + out.write(this.data); + } + + static final class Introducer { + + private final short length; + private final byte classCode; + private final byte typeCode; + private final byte categoryCode; + private final boolean extensionPresent; + private final boolean segmentedData; + private final boolean paddingPresent; + private final byte[] introducerData; + + Introducer(byte[] introducerData) throws IOException { + + this.introducerData = introducerData; + + // Parse the introducer; the 8 bytes have already been read from the stream just + // before, so we parse the introducer from the byte array + DataInputStream iis = new DataInputStream( + new ByteArrayInputStream(introducerData)); + + length = iis.readShort(); + + classCode = iis.readByte(); + typeCode = iis.readByte(); + categoryCode = iis.readByte(); + + //Flags + byte f = iis.readByte(); + + extensionPresent = (f & 0x01) != 0; + segmentedData = (f & 0x04) != 0; + paddingPresent = (f & 0x10) != 0; + + } + + + public short getLength() { + return length; + } + + public byte getClassCode() { + return classCode; + } + + public byte getTypeCode() { + return typeCode; + } + + public byte getCategoryCode() { + return categoryCode; + } + + public boolean isExtensionPresent() { + return extensionPresent; + } + + + public boolean isSegmentedData() { + return segmentedData; + } + + public boolean isPaddingPresent() { + return paddingPresent; + } + + public byte[] getIntroducerData() { + byte[] rtn = new byte[introducerData.length]; + System.arraycopy(introducerData, 0, rtn, 0, rtn.length); + return rtn; + } + } + } diff --git a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java index 883414fda..2c6668454 100644 --- a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java +++ b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java @@ -25,11 +25,13 @@ import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.UserAgent; import org.apache.batik.gvt.TextPainter; + +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; + import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.fonts.FontInfo; import org.apache.fop.svg.AbstractFOPBridgeContext; -import org.apache.xmlgraphics.image.loader.ImageManager; -import org.apache.xmlgraphics.image.loader.ImageSessionContext; /** * An AFP specific implementation of a Batik BridgeContext @@ -79,11 +81,12 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { } /** {@inheritDoc} */ + @Override public void registerSVGBridges() { super.registerSVGBridges(); if (fontInfo != null) { - AFPTextHandler textHandler = new AFPTextHandler(fontInfo); + AFPTextHandler textHandler = new AFPTextHandler(fontInfo, g2d.getResourceManager()); g2d.setCustomTextHandler(textHandler); TextPainter textPainter = new AFPTextPainter(textHandler); @@ -96,6 +99,7 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { } /** {@inheritDoc} */ + @Override public BridgeContext createBridgeContext() { return new AFPBridgeContext(getUserAgent(), getDocumentLoader(), fontInfo, diff --git a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java index 13a32ee78..eb75e33ff 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java @@ -21,15 +21,18 @@ package org.apache.fop.afp.svg; import java.awt.Color; import java.awt.Graphics2D; +import java.io.IOException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.afp.AFPPaintingState; +import org.apache.fop.afp.AFPResourceManager; import org.apache.fop.afp.fonts.AFPFont; import org.apache.fop.afp.fonts.AFPFontAttributes; import org.apache.fop.afp.fonts.AFPPageFonts; +import org.apache.fop.afp.fonts.CharacterSet; import org.apache.fop.afp.modca.GraphicsObject; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; @@ -50,13 +53,18 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { /** Font information */ private final FontInfo fontInfo; + /** the resource manager */ + private AFPResourceManager resourceManager; + /** * Main constructor. * * @param fontInfo the AFPGraphics2D instance + * @param resourceManager the AFPResourceManager instance */ - public AFPTextHandler(FontInfo fontInfo) { + public AFPTextHandler(FontInfo fontInfo, AFPResourceManager resourceManager) { this.fontInfo = fontInfo; + this.resourceManager = resourceManager; } /** @@ -83,6 +91,14 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { afpFont, fontSize ); + if (afpFont.isEmbeddable()) { + try { + final CharacterSet charSet = afpFont.getCharacterSet(fontSize); + this.resourceManager.embedFont(afpFont, charSet); + } catch (IOException ioe) { + throw new RuntimeException("Error while embedding font resources", ioe); + } + } return afpFontAttributes.getFontReference(); } @@ -92,6 +108,7 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { * * {@inheritDoc} */ + @Override public void drawString(Graphics2D g, String str, float x, float y) { if (log.isDebugEnabled()) { log.debug("drawString() str=" + str + ", x=" + x + ", y=" + y); @@ -115,9 +132,15 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { if (overrideFont != null) { internalFontName = overrideFont.getFontName(); fontSize = overrideFont.getFontSize(); + if (log.isDebugEnabled()) { + log.debug(" with overriding font: " + internalFontName + ", " + fontSize); + } } else { java.awt.Font awtFont = g2d.getFont(); Font fopFont = fontInfo.getFontInstanceForAWTFont(awtFont); + if (log.isDebugEnabled()) { + log.debug(" with font: " + fopFont); + } internalFontName = fopFont.getFontName(); fontSize = fopFont.getFontSize(); } diff --git a/src/java/org/apache/fop/afp/util/AFPResourceUtil.java b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java index ebb318046..979376b3e 100644 --- a/src/java/org/apache/fop/afp/util/AFPResourceUtil.java +++ b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java @@ -29,8 +29,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.AFPConstants; -import org.apache.fop.afp.modca.ResourceObject; import org.apache.fop.afp.modca.AbstractAFPObject.Category; +import org.apache.fop.afp.modca.ResourceObject; import org.apache.fop.afp.parser.MODCAParser; import org.apache.fop.afp.parser.UnparsedStructuredField; diff --git a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java index 1235f4c4f..eaf932961 100644 --- a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java +++ b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java @@ -41,6 +41,7 @@ class ICCColorFunction extends FunctionBase { } /** {@inheritDoc} */ + @Override public PercentBase getPercentBase() { return new ICCPercentBase(); } @@ -50,7 +51,9 @@ class ICCColorFunction extends FunctionBase { PropertyInfo pInfo) throws PropertyException { // Map color profile NCNAME to src from declarations/color-profile element String colorProfileName = args[3].getString(); - Declarations decls = pInfo.getFO().getRoot().getDeclarations(); + Declarations decls = (pInfo.getFO() != null + ? pInfo.getFO().getRoot().getDeclarations() + : null); ColorProfile cp = null; if (decls == null) { //function used in a color-specification diff --git a/src/java/org/apache/fop/fo/expr/NamedColorFunction.java b/src/java/org/apache/fop/fo/expr/NamedColorFunction.java index 53b9effdb..e4f8bc643 100644 --- a/src/java/org/apache/fop/fo/expr/NamedColorFunction.java +++ b/src/java/org/apache/fop/fo/expr/NamedColorFunction.java @@ -40,6 +40,7 @@ class NamedColorFunction extends FunctionBase { } /** {@inheritDoc} */ + @Override public PercentBase getPercentBase() { return new NamedPercentBase(); } @@ -51,7 +52,9 @@ class NamedColorFunction extends FunctionBase { String colorProfileName = args[3].getString(); String colorName = args[4].getString(); - Declarations decls = pInfo.getFO().getRoot().getDeclarations(); + Declarations decls = (pInfo.getFO() != null + ? pInfo.getFO().getRoot().getDeclarations() + : null); ColorProfile cp = null; if (decls != null) { cp = decls.getColorProfile(colorProfileName); diff --git a/src/java/org/apache/fop/fo/properties/PropertyMaker.java b/src/java/org/apache/fop/fo/properties/PropertyMaker.java index 0f4632110..ba17b1850 100644 --- a/src/java/org/apache/fop/fo/properties/PropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/PropertyMaker.java @@ -445,7 +445,9 @@ public class PropertyMaker implements Cloneable { } return newProp; } catch (PropertyException propEx) { - propEx.setLocator(fo.getLocator()); + if (fo != null) { + propEx.setLocator(fo.getLocator()); + } propEx.setPropertyName(getName()); throw propEx; } @@ -653,6 +655,7 @@ public class PropertyMaker implements Cloneable { * subproperty makers of the generic compound makers. * {@inheritDoc} */ + @Override public Object clone() { try { return super.clone(); diff --git a/src/java/org/apache/fop/fonts/CIDSubset.java b/src/java/org/apache/fop/fonts/CIDSubset.java index b152ad38e..372a638d1 100644 --- a/src/java/org/apache/fop/fonts/CIDSubset.java +++ b/src/java/org/apache/fop/fonts/CIDSubset.java @@ -62,18 +62,11 @@ public class CIDSubset { } /** - * Adds the initial 3 glyphs which are the same for all CID subsets. + * Adds the first glyph which is reserved for .notdef for all CID subsets. */ - public void setupFirstThreeGlyphs() { - // Make sure that the 3 first glyphs are included - usedGlyphs.put(new Integer(0), new Integer(0)); - usedGlyphsIndex.put(new Integer(0), new Integer(0)); - usedGlyphsCount++; - usedGlyphs.put(new Integer(1), new Integer(1)); - usedGlyphsIndex.put(new Integer(1), new Integer(1)); - usedGlyphsCount++; - usedGlyphs.put(new Integer(2), new Integer(2)); - usedGlyphsIndex.put(new Integer(2), new Integer(2)); + public void setupFirstGlyph() { + usedGlyphs.put(Integer.valueOf(0), Integer.valueOf(0)); + usedGlyphsIndex.put(Integer.valueOf(0), Integer.valueOf(0)); usedGlyphsCount++; } @@ -84,7 +77,7 @@ public class CIDSubset { * @return the original index (or -1 if no glyph index is available for the subset index) */ public int getGlyphIndexForSubsetIndex(int subsetIndex) { - Integer glyphIndex = usedGlyphsIndex.get(new Integer(subsetIndex)); + Integer glyphIndex = usedGlyphsIndex.get(Integer.valueOf(subsetIndex)); if (glyphIndex != null) { return glyphIndex.intValue(); } else { @@ -99,7 +92,7 @@ public class CIDSubset { * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) */ public char getUnicodeForSubsetIndex(int subsetIndex) { - Character mapValue = usedCharsIndex.get(new Integer(subsetIndex)); + Character mapValue = usedCharsIndex.get(Integer.valueOf(subsetIndex)); if (mapValue != null) { return mapValue.charValue(); } else { @@ -118,15 +111,15 @@ public class CIDSubset { public int mapSubsetChar(int glyphIndex, char unicode) { // Reencode to a new subset font or get the reencoded value // IOW, accumulate the accessed characters and build a character map for them - Integer subsetCharSelector = usedGlyphs.get(new Integer(glyphIndex)); + Integer subsetCharSelector = usedGlyphs.get(Integer.valueOf(glyphIndex)); if (subsetCharSelector == null) { int selector = usedGlyphsCount; - usedGlyphs.put(new Integer(glyphIndex), - new Integer(selector)); - usedGlyphsIndex.put(new Integer(selector), - new Integer(glyphIndex)); - usedCharsIndex.put(new Integer(selector), - new Character(unicode)); + usedGlyphs.put(Integer.valueOf(glyphIndex), + Integer.valueOf(selector)); + usedGlyphsIndex.put(Integer.valueOf(selector), + Integer.valueOf(glyphIndex)); + usedCharsIndex.put(Integer.valueOf(selector), + Character.valueOf(unicode)); usedGlyphsCount++; return selector; } else { diff --git a/src/java/org/apache/fop/fonts/FontInfo.java b/src/java/org/apache/fop/fonts/FontInfo.java index c0bf8db8d..5c9c63a98 100644 --- a/src/java/org/apache/fop/fonts/FontInfo.java +++ b/src/java/org/apache/fop/fonts/FontInfo.java @@ -152,7 +152,7 @@ public class FontInfo { } } this.triplets.put(triplet, internalFontKey); - this.tripletPriorities.put(triplet, new Integer(newPriority)); + this.tripletPriorities.put(triplet, Integer.valueOf(newPriority)); } /** @@ -316,7 +316,7 @@ public class FontInfo { sizes = new HashMap<Integer, Font>(); getFontInstanceCache().put(triplet, sizes); } - Integer size = new Integer(fontSize); + Integer size = Integer.valueOf(fontSize); Font font = sizes.get(size); if (font == null) { String fontKey = getInternalFontKey(triplet); diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index 601bf3970..213d2f77d 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -62,8 +62,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl * Default constructor */ public MultiByteFont() { - // Make sure that the 3 first glyphs are included - subset.setupFirstThreeGlyphs(); + subset.setupFirstGlyph(); // Create a quasiunique prefix for fontname synchronized (this.getClass()) { diff --git a/src/java/org/apache/fop/pdf/PDFTextUtil.java b/src/java/org/apache/fop/pdf/PDFTextUtil.java index afa86ae72..ed475fcb8 100644 --- a/src/java/org/apache/fop/pdf/PDFTextUtil.java +++ b/src/java/org/apache/fop/pdf/PDFTextUtil.java @@ -84,7 +84,7 @@ public abstract class PDFTextUtil { private static void writeChar(char ch, StringBuffer sb, boolean multibyte) { if (!multibyte) { if (ch < 32 || ch > 127) { - sb.append("\\").append(Integer.toOctalString((int)ch)); + sb.append("\\").append(Integer.toOctalString(ch)); } else { switch (ch) { case '(': @@ -151,21 +151,6 @@ public abstract class PDFTextUtil { } /** - * Creates a "q" command, pushing a copy of the entire graphics state onto the stack. - */ - public void saveGraphicsState() { - write("q\n"); - } - - /** - * Creates a "Q" command, restoring the entire graphics state to its former value by popping - * it from the stack. - */ - public void restoreGraphicsState() { - write("Q\n"); - } - - /** * Creates a "cm" command. * @param at the transformation matrix */ diff --git a/src/java/org/apache/fop/render/afp/AFPCustomizable.java b/src/java/org/apache/fop/render/afp/AFPCustomizable.java index 93aaa8b1a..04f3a6eeb 100644 --- a/src/java/org/apache/fop/render/afp/AFPCustomizable.java +++ b/src/java/org/apache/fop/render/afp/AFPCustomizable.java @@ -87,6 +87,30 @@ public interface AFPCustomizable { int getResolution(); /** + * Controls whether GOCA is enabled or disabled. + * @param enabled true if GOCA is enabled, false if it is disabled + */ + void setGOCAEnabled(boolean enabled); + + /** + * Indicates whether GOCA is enabled or disabled. + * @return true if GOCA is enabled, false if GOCA is disabled + */ + boolean isGOCAEnabled(); + + /** + * Controls whether to stroke text in GOCA mode or to use text operators where possible. + * @param stroke true to stroke, false to paint with text operators where possible + */ + void setStrokeGOCAText(boolean stroke); + + /** + * Indicates whether to stroke text in GOCA mode or to use text operators where possible. + * @return true to stroke, false to paint with text operators where possible + */ + boolean isStrokeGOCAText(); + + /** * Sets the default resource group file path * @param filePath the default resource group file path */ diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 87651defd..4dd7df324 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -23,6 +23,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.io.IOException; +import java.util.Iterator; +import java.util.List; import java.util.Map; import org.apache.fop.afp.AFPDitheredRectanglePainter; @@ -49,6 +51,7 @@ import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap; import org.apache.fop.render.afp.extensions.AFPPageOverlay; import org.apache.fop.render.afp.extensions.AFPPageSegmentElement; import org.apache.fop.render.afp.extensions.AFPPageSetup; +import org.apache.fop.render.afp.extensions.ExtensionPlacement; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; @@ -89,6 +92,10 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler private Location location = Location.ELSEWHERE; + /** temporary holds extensions that have to be deferred until the end of the page-sequence */ + private List<AFPPageSetup> deferredPageSequenceExtensions + = new java.util.LinkedList<AFPPageSetup>(); + /** the shading mode for filled rectangles */ private AFPShadingMode shadingMode = AFPShadingMode.COLOR; @@ -208,7 +215,25 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler /** {@inheritDoc} */ public void endPageSequence() throws IFException { - //nop + try { + //Process deferred page-sequence-level extensions + Iterator<AFPPageSetup> iter = this.deferredPageSequenceExtensions.iterator(); + while (iter.hasNext()) { + AFPPageSetup aps = iter.next(); + iter.remove(); + if (AFPElementMapping.NO_OPERATION.equals(aps.getElementName())) { + handleNOP(aps); + } else { + throw new UnsupportedOperationException("Don't know how to handle " + aps); + } + } + + //End page sequence + dataStream.endPageGroup(); + } catch (IOException ioe) { + throw new IFException("I/O error in endPageSequence()", ioe); + } + this.location = Location.ELSEWHERE; } /** @@ -302,13 +327,14 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } } else if (AFPElementMapping.NO_OPERATION.equals(element)) { switch (this.location) { - case IN_DOCUMENT_HEADER: case FOLLOWING_PAGE_SEQUENCE: - case IN_PAGE_HEADER: - String content = aps.getContent(); - if (content != null) { - dataStream.createNoOperation(content); + if (aps.getPlacement() == ExtensionPlacement.BEFORE_END) { + this.deferredPageSequenceExtensions.add(aps); + break; } + case IN_DOCUMENT_HEADER: + case IN_PAGE_HEADER: + handleNOP(aps); break; default: throw new IFException( @@ -372,6 +398,13 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } } + private void handleNOP(AFPPageSetup nop) { + String content = nop.getContent(); + if (content != null) { + dataStream.createNoOperation(content); + } + } + // ---=== AFPCustomizable ===--- /** {@inheritDoc} */ @@ -415,6 +448,26 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** {@inheritDoc} */ + public void setGOCAEnabled(boolean enabled) { + this.paintingState.setGOCAEnabled(enabled); + } + + /** {@inheritDoc} */ + public boolean isGOCAEnabled() { + return this.paintingState.isGOCAEnabled(); + } + + /** {@inheritDoc} */ + public void setStrokeGOCAText(boolean stroke) { + this.paintingState.setStrokeGOCAText(stroke); + } + + /** {@inheritDoc} */ + public boolean isStrokeGOCAText() { + return this.paintingState.isStrokeGOCAText(); + } + + /** {@inheritDoc} */ public void setDefaultResourceGroupFilePath(String filePath) { resourceManager.setDefaultResourceGroupFilePath(filePath); } diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java index 07b712ceb..f60e271cb 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java @@ -26,8 +26,6 @@ import java.io.IOException; import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; -import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; -import org.apache.xmlgraphics.util.MimeConstants; import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.AFPGraphics2D; @@ -74,6 +72,7 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH } /** {@inheritDoc} */ + @Override protected AFPDataObjectInfo createDataObjectInfo() { return new AFPGraphicsObjectInfo(); } @@ -104,13 +103,13 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH // Image content ImageGraphics2D imageG2D = (ImageGraphics2D)image; - boolean textAsShapes = false; //TODO Make configurable + final boolean textAsShapes = paintingState.isStrokeGOCAText(); AFPGraphics2D g2d = new AFPGraphics2D( textAsShapes, afpContext.getPaintingState(), afpContext.getResourceManager(), graphicsObjectInfo.getResourceInfo(), - afpContext.getFontInfo()); + (textAsShapes ? null : afpContext.getFontInfo())); g2d.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); graphicsObjectInfo.setGraphics2D(g2d); @@ -127,6 +126,10 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler implements ImageH boolean supported = (image == null || image instanceof ImageGraphics2D) && targetContext instanceof AFPRenderingContext; if (supported) { + AFPRenderingContext afpContext = (AFPRenderingContext)targetContext; + if (!afpContext.getPaintingState().isGOCAEnabled()) { + return false; + } String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE); if (ImageHandlerUtil.isConversionModeBitmap(mode)) { //Disabling this image handler automatically causes a bitmap to be generated diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerSVG.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerSVG.java index 4e4e1de31..834304f6b 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerSVG.java @@ -82,16 +82,16 @@ public class AFPImageHandlerSVG implements ImageHandler { setDefaultToInlineResourceLevel(graphicsObjectInfo); // Create a new AFPGraphics2D - final boolean textAsShapes = false; //afpInfo.strokeText(); //TODO make configurable + AFPPaintingState paintingState = afpContext.getPaintingState(); + final boolean textAsShapes = paintingState.isStrokeGOCAText(); AFPGraphics2D g2d = new AFPGraphics2D( textAsShapes, afpContext.getPaintingState(), afpContext.getResourceManager(), resourceInfo, - afpContext.getFontInfo()); + (textAsShapes ? null : afpContext.getFontInfo())); g2d.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); - AFPPaintingState paintingState = g2d.getPaintingState(); paintingState.setImageUri(image.getInfo().getOriginalURI()); // Create an AFPBridgeContext @@ -167,6 +167,10 @@ public class AFPImageHandlerSVG implements ImageHandler { && image.getFlavor().isCompatible(BatikImageFlavors.SVG_DOM))) && targetContext instanceof AFPRenderingContext; if (supported) { + AFPRenderingContext afpContext = (AFPRenderingContext)targetContext; + if (!afpContext.getPaintingState().isGOCAEnabled()) { + return false; + } String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE); if (ImageHandlerUtil.isConversionModeBitmap(mode)) { //Disabling this image handler automatically causes a bitmap to be generated diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index 8d4b3370c..8cc381c18 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -76,7 +76,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator FontManager fontManager = this.userAgent.getFactory().getFontManager(); Configuration[] triple = fontCfg.getChildren("font-triplet"); - List/*<FontTriplet>*/ tripletList = new java.util.ArrayList/*<FontTriplet>*/(); + List<FontTriplet> tripletList = new java.util.ArrayList<FontTriplet>(); if (triple.length == 0) { log.error("Mandatory font configuration element '<font-triplet...' is missing"); return null; @@ -184,8 +184,8 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator if (base14 != null) { try { - Class clazz = Class.forName("org.apache.fop.fonts.base14." - + base14); + Class<?> clazz = Class.forName( + "org.apache.fop.fonts.base14." + base14); try { Typeface tf = (Typeface)clazz.newInstance(); font.addCharacterSet(sizeMpt, @@ -223,7 +223,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator String base14 = afpFontCfg.getAttribute("base14-font", null); if (base14 != null) { try { - Class clazz = Class.forName("org.apache.fop.fonts.base14." + Class<?> clazz = Class.forName("org.apache.fop.fonts.base14." + base14); try { Typeface tf = (Typeface)clazz.newInstance(); @@ -291,7 +291,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator * @return List the newly created list of fonts * @throws ConfigurationException if something's wrong with the config data */ - private List/*<AFPFontInfo>*/ buildFontListFromConfiguration(Configuration cfg) + private List<AFPFontInfo> buildFontListFromConfiguration(Configuration cfg) throws FOPException, ConfigurationException { Configuration fonts = cfg.getChild("fonts"); @@ -309,7 +309,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator referencedFontsCfg, this.userAgent.getFactory().validateUserConfigStrictly()); } - List/*<AFPFontInfo>*/ fontList = new java.util.ArrayList(); + List<AFPFontInfo> fontList = new java.util.ArrayList<AFPFontInfo>(); Configuration[] font = fonts.getChildren("font"); final String fontPath = null; for (int i = 0; i < font.length; i++) { @@ -352,6 +352,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator * * @param renderer not used */ + @Override public void configure(Renderer renderer) { throw new UnsupportedOperationException(); } @@ -400,6 +401,16 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator shadingCfg.getValue(AFPShadingMode.COLOR.getName())); customizable.setShadingMode(shadingMode); + // GOCA Support + Configuration gocaCfg = cfg.getChild("goca"); + boolean gocaEnabled = gocaCfg.getAttributeAsBoolean( + "enabled", customizable.isGOCAEnabled()); + customizable.setGOCAEnabled(gocaEnabled); + String gocaText = gocaCfg.getAttribute( + "text", customizable.isStrokeGOCAText() ? "stroke" : "default"); + customizable.setStrokeGOCAText("stroke".equalsIgnoreCase(gocaText) + || "shapes".equalsIgnoreCase(gocaText)); + // renderer resolution Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false); if (rendererResolutionCfg != null) { @@ -415,8 +426,8 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator resourceGroupDest = resourceGroupFileCfg.getValue(); if (resourceGroupDest != null) { File resourceGroupFile = new File(resourceGroupDest); - resourceGroupFile.createNewFile(); - if (resourceGroupFile.canWrite()) { + boolean created = resourceGroupFile.createNewFile(); + if (created && resourceGroupFile.canWrite()) { customizable.setDefaultResourceGroupFilePath(resourceGroupDest); } else { log.warn("Unable to write to default external resource group file '" @@ -454,6 +465,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator } /** {@inheritDoc} */ + @Override public void configure(IFDocumentHandler documentHandler) throws FOPException { Configuration cfg = super.getRendererConfig(documentHandler.getMimeType()); if (cfg != null) { @@ -463,15 +475,16 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator } /** {@inheritDoc} */ + @Override public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo) throws FOPException { FontManager fontManager = userAgent.getFactory().getFontManager(); - List fontCollections = new java.util.ArrayList(); + List<FontCollection> fontCollections = new java.util.ArrayList<FontCollection>(); Configuration cfg = super.getRendererConfig(documentHandler.getMimeType()); if (cfg != null) { try { - List fontList = buildFontListFromConfiguration(cfg); + List<AFPFontInfo> fontList = buildFontListFromConfiguration(cfg); fontCollections.add(new AFPFontCollection( userAgent.getEventBroadcaster(), fontList)); } catch (ConfigurationException e) { @@ -483,7 +496,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator } fontManager.setup(fontInfo, - (FontCollection[])fontCollections.toArray( + fontCollections.toArray( new FontCollection[fontCollections.size()])); documentHandler.setFontInfo(fontInfo); } 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 d916738eb..14a34a0b3 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java @@ -50,6 +50,7 @@ public class AFPExtensionHandler extends DefaultHandler private ObjectBuiltListener listener; /** {@inheritDoc} */ + @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { boolean handled = false; @@ -79,6 +80,7 @@ public class AFPExtensionHandler extends DefaultHandler } /** {@inheritDoc} */ + @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (AFPExtensionAttachment.CATEGORY.equals(uri)) { if (AFPElementMapping.INCLUDE_FORM_MAP.equals(localName)) { @@ -130,14 +132,18 @@ public class AFPExtensionHandler extends DefaultHandler pageSetupExtn = new AFPPageSetup(localName); this.returnedObject = pageSetupExtn; } - String name = lastAttributes.getValue("name"); + String name = lastAttributes.getValue(AFPPageSetup.ATT_NAME); if (name != null) { returnedObject.setName(name); } - String value = lastAttributes.getValue("value"); + String value = lastAttributes.getValue(AFPPageSetup.ATT_VALUE); if (value != null && pageSetupExtn != null) { pageSetupExtn.setValue(value); } + String placement = lastAttributes.getValue(AFPPageSetup.ATT_PLACEMENT); + if (placement != null && placement.length() > 0) { + pageSetupExtn.setPlacement(ExtensionPlacement.fromXMLValue(placement)); + } if (content.length() > 0 && pageSetupExtn != null) { pageSetupExtn.setContent(content.toString()); content.setLength(0); //Reset text buffer (see characters()) @@ -148,6 +154,7 @@ public class AFPExtensionHandler extends DefaultHandler } /** {@inheritDoc} */ + @Override public void characters(char[] ch, int start, int length) throws SAXException { content.append(ch, start, length); } @@ -155,6 +162,7 @@ public class AFPExtensionHandler extends DefaultHandler /** * {@inheritDoc} */ + @Override public void endDocument() throws SAXException { if (listener != null) { listener.notifyObjectBuilt(getObject()); 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 b72a30c84..2d30f3ae8 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetup.java @@ -31,6 +31,9 @@ public class AFPPageSetup extends AFPExtensionAttachment { /** value attribute */ protected static final String ATT_VALUE = "value"; + /** placement attribute */ + protected static final String ATT_PLACEMENT = "placement"; + /** * the extension content */ @@ -41,6 +44,9 @@ public class AFPPageSetup extends AFPExtensionAttachment { */ protected String value; + /** defines where to place the extension in the generated file */ + protected ExtensionPlacement placement = ExtensionPlacement.DEFAULT; + /** * Default constructor. * @@ -84,6 +90,26 @@ public class AFPPageSetup extends AFPExtensionAttachment { this.content = content; } + /** + * Returns the intended placement of the extension inside the generated file. + * @return the intended placement + */ + public ExtensionPlacement getPlacement() { + return this.placement; + } + + /** + * Sets the intended placement of the extension inside the generated file. + * @param placement the intended placement + */ + public void setPlacement(ExtensionPlacement placement) { + if (!AFPElementMapping.NO_OPERATION.equals(getElementName())) { + throw new UnsupportedOperationException( + "The attribute 'placement' can currently only be set for NOPs!"); + } + this.placement = placement; + } + /** {@inheritDoc} */ public void toSAX(ContentHandler handler) throws SAXException { AttributesImpl atts = new AttributesImpl(); @@ -93,6 +119,9 @@ public class AFPPageSetup extends AFPExtensionAttachment { if (value != null && value.length() > 0) { atts.addAttribute(null, ATT_VALUE, ATT_VALUE, "CDATA", value); } + if (this.placement != ExtensionPlacement.DEFAULT) { + atts.addAttribute(null, ATT_PLACEMENT, ATT_PLACEMENT, "CDATA", placement.getXMLValue()); + } handler.startElement(CATEGORY, elementName, elementName, atts); if (content != null && content.length() > 0) { char[] chars = content.toCharArray(); @@ -102,8 +131,16 @@ public class AFPPageSetup extends AFPExtensionAttachment { } /** {@inheritDoc} */ + @Override public String toString() { - return "AFPPageSetup(element-name=" + getElementName() - + " name=" + getName() + " value=" + getValue() + ")"; + StringBuilder sb = new StringBuilder("AFPPageSetup("); + sb.append("element-name=").append(getElementName()); + sb.append(" name=").append(getName()); + sb.append(" value=").append(getValue()); + if (getPlacement() != ExtensionPlacement.DEFAULT) { + sb.append(" placement=").append(getPlacement()); + } + sb.append(")"); + return sb.toString(); } } 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 b5b05191a..42be9bfe0 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java @@ -30,13 +30,11 @@ 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 + * object facilitates extraction of elements from formatted objects based on * the static list as defined in the AFPElementMapping implementation. - * <p/> */ public class AFPPageSetupElement extends AbstractAFPExtensionObject { - private static final String ATT_VALUE = "value"; private static final String ATT_SRC = "src"; /** @@ -101,13 +99,17 @@ public class AFPPageSetupElement extends AbstractAFPExtensionObject { missingPropertyError(ATT_SRC); } } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) { - String attr = attlist.getValue(ATT_VALUE); + String attr = attlist.getValue(AFPPageSetup.ATT_VALUE); if (attr != null && attr.length() > 0) { pageSetup.setValue(attr); } else { - missingPropertyError(ATT_VALUE); + missingPropertyError(AFPPageSetup.ATT_VALUE); } } + String placement = attlist.getValue(AFPPageSetup.ATT_PLACEMENT); + if (placement != null && placement.length() > 0) { + pageSetup.setPlacement(ExtensionPlacement.fromXMLValue(placement)); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/afp/extensions/ExtensionPlacement.java b/src/java/org/apache/fop/render/afp/extensions/ExtensionPlacement.java new file mode 100644 index 000000000..9c593d52f --- /dev/null +++ b/src/java/org/apache/fop/render/afp/extensions/ExtensionPlacement.java @@ -0,0 +1,55 @@ +/* + * 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 java.util.Locale; + +/** + * An enumeration for placement instruction for AFP extensions. + */ +public enum ExtensionPlacement { + + /** Place the extension at its default/usual position. */ + DEFAULT, + /** Place the extension right before the "End" structured field. */ + BEFORE_END; + + /** + * Returns the XML value that corresponds to this enum value. + * @return the XML value + */ + public String getXMLValue() { + String xmlName = name().toLowerCase(Locale.ENGLISH); + xmlName = xmlName.replace('_', '-'); + return xmlName; + } + + /** + * Returns the enum value from the given XML value for this enumeration. + * @param value the XML value + * @return the enum value + */ + public static ExtensionPlacement fromXMLValue(String value) { + String name = value.toUpperCase(Locale.ENGLISH); + name = name.replace('-', '_'); + return ExtensionPlacement.valueOf(name); + } + +} diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 1b870a7c5..2226274ab 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -46,6 +46,7 @@ import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.fop.ResourceEventProducer; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; +import org.apache.fop.fo.Constants; import org.apache.fop.render.ImageHandler; import org.apache.fop.render.ImageHandlerRegistry; import org.apache.fop.render.ImageHandlerUtil; @@ -342,6 +343,32 @@ public abstract class AbstractIFPainter implements IFPainter { } } + /** + * Indicates whether the given border segments (if present) have only solid borders, i.e. + * could be painted in a simplified fashion keeping the output file smaller. + * @param before the border segment on the before-side (top) + * @param after the border segment on the after-side (bottom) + * @param start the border segment on the start-side (left) + * @param end the border segment on the end-side (right) + * @return true if any border segment has a non-solid border style + */ + protected boolean hasOnlySolidBorders(BorderProps before, BorderProps after, + BorderProps start, BorderProps end) { + if (before != null && before.style != Constants.EN_SOLID) { + return false; + } + if (after != null && after.style != Constants.EN_SOLID) { + return false; + } + if (start != null && start.style != Constants.EN_SOLID) { + return false; + } + if (end != null && end.style != Constants.EN_SOLID) { + return false; + } + return true; + } + /** {@inheritDoc} */ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index d076e6ab9..59d3930f7 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -127,7 +127,12 @@ public abstract class AbstractImageAdapter implements PDFImage { pdfICCStream = cs.getICCStream(); } } else { - if (cs == null && desc.startsWith("sRGB")) { + if (cs == null) { + if (desc == null || !desc.startsWith("sRGB")) { + log.warn("The default sRGB profile was indicated," + + " but the profile description does not match what was expected: " + + desc); + } //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer cs = (PDFICCBasedColorSpace)doc.getResources().getColorSpace( new PDFName("DefaultRGB")); diff --git a/src/java/org/apache/fop/render/pdf/AbstractPDFImageHandler.java b/src/java/org/apache/fop/render/pdf/AbstractPDFImageHandler.java new file mode 100644 index 000000000..5bc83fee3 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/AbstractPDFImageHandler.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf; + +import java.awt.Rectangle; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; + +import org.apache.fop.pdf.PDFImage; +import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; +import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; + +/** + * A partial implementation of a PDF-specific image handler, containing the code that is + * common between image flavors. + */ +abstract class AbstractPDFImageHandler implements ImageHandler { + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + assert context instanceof PDFRenderingContext; + PDFRenderingContext pdfContext = (PDFRenderingContext)context; + PDFContentGenerator generator = pdfContext.getGenerator(); + PDFImage pdfimage = createPDFImage(image, image.getInfo().getOriginalURI()); + PDFXObject xobj = generator.getDocument().addImage( + generator.getResourceContext(), pdfimage); + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + if (context.getUserAgent().isAccessibilityEnabled()) { + MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); + generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid); + } else { + generator.placeImage(x, y, w, h, xobj); + } + } + + /** + * Creates a PDF image object out of the given image. + * + * @param image an image + * @param xobjectKey a key for retrieval of the image from the document's XObject collection + * @return a suitable {@link PDFImage} implementation that can handle the flavour of + * the given image + */ + abstract PDFImage createPDFImage(Image image, String xobjectKey); +} diff --git a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java index e34165d5a..c38a2edaf 100644 --- a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java +++ b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java @@ -27,6 +27,7 @@ import java.awt.image.RenderedImage; import java.io.IOException; import java.io.OutputStream; +import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -78,12 +79,14 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override public int getWidth() { RenderedImage ri = getImage().getRenderedImage(); return ri.getWidth(); } /** {@inheritDoc} */ + @Override public int getHeight() { RenderedImage ri = getImage().getRenderedImage(); return ri.getHeight(); @@ -94,11 +97,13 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override protected ColorSpace getImageColorSpace() { return getEffectiveColorModel().getColorSpace(); } /** {@inheritDoc} */ + @Override protected ICC_Profile getEffectiveICCProfile() { ColorSpace cs = getImageColorSpace(); if (cs instanceof ICC_ColorSpace) { @@ -110,6 +115,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override public void setup(PDFDocument doc) { RenderedImage ri = getImage().getRenderedImage(); @@ -145,6 +151,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override public boolean isTransparent() { ColorModel cm = getEffectiveColorModel(); if (cm instanceof IndexColorModel) { @@ -172,7 +179,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { i < ((IndexColorModel) cm).getMapSize(); i++) { if ((alphas[i] & 0xFF) == 0) { - return new Integer(i); + return Integer.valueOf(i); } } } @@ -180,6 +187,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override public PDFColor getTransparentColor() { ColorModel cm = getEffectiveColorModel(); if (cm instanceof IndexColorModel) { @@ -196,11 +204,13 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + @Override public String getMask() { return maskRef; } /** {@inheritDoc} */ + @Override public PDFReference getSoftMaskReference() { return softMask; } @@ -212,12 +222,18 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { /** {@inheritDoc} */ public void outputContents(OutputStream out) throws IOException { + long start = System.currentTimeMillis(); encodingHelper.encode(out); + long duration = System.currentTimeMillis() - start; + if (log.isDebugEnabled()) { + log.debug("Image encoding took " + duration + "ms"); + } } private static final int MAX_HIVAL = 255; /** {@inheritDoc} */ + @Override public void populateXObjectDictionary(PDFDictionary dict) { ColorModel cm = getEffectiveColorModel(); if (cm instanceof IndexColorModel) { @@ -237,7 +253,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { if (hival > MAX_HIVAL) { throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL); } - indexed.add(new Integer(hival)); + indexed.add(Integer.valueOf(hival)); int[] palette = new int[c]; icm.getRGBs(palette); ByteArrayOutputStream baout = new ByteArrayOutputStream(); @@ -250,6 +266,7 @@ public class ImageRenderedAdapter extends AbstractImageAdapter { baout.write(entry & 0xFF); } indexed.add(baout.toByteArray()); + IOUtils.closeQuietly(baout); dict.put("ColorSpace", indexed); dict.put("BitsPerComponent", icm.getPixelSize()); diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java index d309a00aa..a71ade911 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java @@ -31,6 +31,7 @@ import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; import org.apache.fop.render.AbstractImageHandlerGraphics2D; +import org.apache.fop.render.ImageHandlerUtil; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; import org.apache.fop.svg.PDFGraphics2D; @@ -60,8 +61,8 @@ public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D { float imw = (float)dim.getWidth() / 1000f; float imh = (float)dim.getHeight() / 1000f; - float sx = fwidth / (float)imw; - float sy = fheight / (float)imh; + float sx = fwidth / imw; + float sy = fheight / imh; generator.comment("G2D start"); boolean accessibilityEnabled = context.getUserAgent().isAccessibilityEnabled(); @@ -123,8 +124,16 @@ public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D { /** {@inheritDoc} */ public boolean isCompatible(RenderingContext targetContext, Image image) { - return (image == null || image instanceof ImageGraphics2D) + boolean supported = (image == null || image instanceof ImageGraphics2D) && targetContext instanceof PDFRenderingContext; + if (supported) { + String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE); + if (ImageHandlerUtil.isConversionModeBitmap(mode)) { + //Disabling this image handler automatically causes a bitmap to be generated + return false; + } + } + return supported; } } diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java index b7d47ad58..c0971a8d0 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java @@ -19,44 +19,26 @@ package org.apache.fop.render.pdf; -import java.awt.Rectangle; -import java.io.IOException; - import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; import org.apache.fop.pdf.PDFImage; -import org.apache.fop.pdf.PDFXObject; -import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RenderingContext; /** * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4) * for PDF output. */ -public class PDFImageHandlerRawCCITTFax implements ImageHandler { +public class PDFImageHandlerRawCCITTFax extends AbstractPDFImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.RAW_CCITTFAX, }; - /** {@inheritDoc} */ - public void handleImage(RenderingContext context, Image image, Rectangle pos) - throws IOException { - PDFRenderingContext pdfContext = (PDFRenderingContext)context; - PDFContentGenerator generator = pdfContext.getGenerator(); - ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image; - - PDFImage pdfimage = new ImageRawCCITTFaxAdapter(ccitt, image.getInfo().getOriginalURI()); - PDFXObject xobj = generator.getDocument().addImage( - generator.getResourceContext(), pdfimage); - - float x = (float)pos.getX() / 1000f; - float y = (float)pos.getY() / 1000f; - float w = (float)pos.getWidth() / 1000f; - float h = (float)pos.getHeight() / 1000f; - generator.placeImage(x, y, w, h, xobj); + @Override + PDFImage createPDFImage(Image image, String xobjectKey) { + return new ImageRawCCITTFaxAdapter((ImageRawCCITTFax) image, xobjectKey); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java index 7ac181014..d09ae9804 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java @@ -19,56 +19,32 @@ package org.apache.fop.render.pdf; -import java.awt.Rectangle; -import java.io.IOException; - import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; import org.apache.fop.pdf.PDFImage; -import org.apache.fop.pdf.PDFXObject; -import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RenderingContext; -import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; /** * Image handler implementation which handles raw JPEG images for PDF output. */ -public class PDFImageHandlerRawJPEG implements ImageHandler { +public class PDFImageHandlerRawJPEG extends AbstractPDFImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.RAW_JPEG, }; /** {@inheritDoc} */ - public void handleImage(RenderingContext context, Image image, Rectangle pos) - throws IOException { - PDFRenderingContext pdfContext = (PDFRenderingContext)context; - PDFContentGenerator generator = pdfContext.getGenerator(); - ImageRawJPEG imageJPEG = (ImageRawJPEG)image; - - PDFImage pdfimage = new ImageRawJPEGAdapter(imageJPEG, image.getInfo().getOriginalURI()); - PDFXObject xobj = generator.getDocument().addImage( - generator.getResourceContext(), pdfimage); - - float x = (float)pos.getX() / 1000f; - float y = (float)pos.getY() / 1000f; - float w = (float)pos.getWidth() / 1000f; - float h = (float)pos.getHeight() / 1000f; - if (context.getUserAgent().isAccessibilityEnabled()) { - MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); - generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid); - } else { - generator.placeImage(x, y, w, h, xobj); - } - } - - /** {@inheritDoc} */ public int getPriority() { return 100; } + @Override + PDFImage createPDFImage(Image image, String xobjectKey) { + return new ImageRawJPEGAdapter((ImageRawJPEG) image, xobjectKey); + } + /** {@inheritDoc} */ public Class getSupportedImageClass() { return ImageRawJPEG.class; diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java index e0ff3cdd4..7b34e7bd7 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java @@ -19,50 +19,26 @@ package org.apache.fop.render.pdf; -import java.awt.Rectangle; -import java.io.IOException; - import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageRendered; import org.apache.fop.pdf.PDFImage; -import org.apache.fop.pdf.PDFXObject; -import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RenderingContext; -import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; /** * Image handler implementation which handles RenderedImage instances for PDF output. */ -public class PDFImageHandlerRenderedImage implements ImageHandler { +public class PDFImageHandlerRenderedImage extends AbstractPDFImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.BUFFERED_IMAGE, ImageFlavor.RENDERED_IMAGE }; - /** {@inheritDoc} */ - public void handleImage(RenderingContext context, Image image, Rectangle pos) - throws IOException { - PDFRenderingContext pdfContext = (PDFRenderingContext)context; - PDFContentGenerator generator = pdfContext.getGenerator(); - ImageRendered imageRend = (ImageRendered)image; - - PDFImage pdfimage = new ImageRenderedAdapter(imageRend, image.getInfo().getOriginalURI()); - PDFXObject xobj = generator.getDocument().addImage( - generator.getResourceContext(), pdfimage); - - float x = (float)pos.getX() / 1000f; - float y = (float)pos.getY() / 1000f; - float w = (float)pos.getWidth() / 1000f; - float h = (float)pos.getHeight() / 1000f; - if (context.getUserAgent().isAccessibilityEnabled()) { - MarkedContentInfo mci = pdfContext.getMarkedContentInfo(); - generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid); - } else { - generator.placeImage(x, y, w, h, xobj); - } + @Override + PDFImage createPDFImage(Image image, String xobjectKey) { + return new ImageRenderedAdapter((ImageRendered) image, xobjectKey); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java index d47142fb6..2b02b60fb 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java @@ -43,6 +43,7 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.image.loader.batik.BatikImageFlavors; import org.apache.fop.image.loader.batik.BatikUtil; import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.ImageHandlerUtil; import org.apache.fop.render.RenderingContext; import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; import org.apache.fop.svg.PDFAElementBridge; @@ -200,6 +201,7 @@ public class PDFImageHandlerSVG implements ImageHandler { graphics.setOutputStream(generator.getOutputStream()); try { root.paint(graphics); + ctx.dispose(); generator.add(graphics.getString()); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( @@ -236,10 +238,18 @@ public class PDFImageHandlerSVG implements ImageHandler { /** {@inheritDoc} */ public boolean isCompatible(RenderingContext targetContext, Image image) { - return (image == null + boolean supported = (image == null || (image instanceof ImageXMLDOM && image.getFlavor().isCompatible(BatikImageFlavors.SVG_DOM))) && targetContext instanceof PDFRenderingContext; + if (supported) { + String mode = (String)targetContext.getHint(ImageHandlerUtil.CONVERSION_MODE); + if (ImageHandlerUtil.isConversionModeBitmap(mode)) { + //Disabling this image handler automatically causes a bitmap to be generated + return false; + } + } + return supported; } } diff --git a/src/java/org/apache/fop/render/ps/PSBorderPainter.java b/src/java/org/apache/fop/render/ps/PSBorderPainter.java index 1e2964eed..a52974d36 100644 --- a/src/java/org/apache/fop/render/ps/PSBorderPainter.java +++ b/src/java/org/apache/fop/render/ps/PSBorderPainter.java @@ -62,9 +62,10 @@ public class PSBorderPainter extends BorderPainter { private static void drawLine(PSGenerator gen, float startx, float starty, float endx, float endy) throws IOException { gen.writeln(gen.formatDouble(startx) + " " - + gen.formatDouble(starty) + " M " + + gen.formatDouble(starty) + " " + gen.mapCommand("moveto") + " " + gen.formatDouble(endx) + " " - + gen.formatDouble(endy) + " lineto stroke newpath"); + + gen.formatDouble(endy) + " " + gen.mapCommand("lineto") + " " + + gen.mapCommand("stroke") + " " + gen.mapCommand("newpath")); } /** @@ -260,7 +261,8 @@ public class PSBorderPainter extends BorderPainter { lineTo(end.x, starty + 2 * half); lineTo(start.x, starty + 2 * half); closePath(); - generator.writeln(" fill newpath"); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); generator.useColor(color); if (style == RuleStyle.GROOVE) { moveTo(start.x, starty); @@ -276,7 +278,8 @@ public class PSBorderPainter extends BorderPainter { lineTo(end.x - half, starty + half); } closePath(); - generator.writeln(" fill newpath"); + generator.write(" " + generator.mapCommand("fill")); + generator.writeln(" " + generator.mapCommand("newpath")); break; default: throw new UnsupportedOperationException("rule style not supported"); @@ -293,13 +296,13 @@ public class PSBorderPainter extends BorderPainter { /** {@inheritDoc} */ protected void moveTo(int x, int y) throws IOException { generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " M"); + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("moveto")); } /** {@inheritDoc} */ protected void lineTo(int x, int y) throws IOException { generator.writeln(generator.formatDouble(toPoints(x)) + " " - + generator.formatDouble(toPoints(y)) + " lineto"); + + generator.formatDouble(toPoints(y)) + " " + generator.mapCommand("lineto")); } /** {@inheritDoc} */ @@ -314,7 +317,7 @@ public class PSBorderPainter extends BorderPainter { /** {@inheritDoc} */ protected void clip() throws IOException { - generator.writeln("clip newpath"); + generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index f9351a260..c28ec7549 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -203,7 +203,7 @@ public class PSPainter extends AbstractIFPainter { endTextObject(); generator.defineRect(rect.x / 1000.0, rect.y / 1000.0, rect.width / 1000.0, rect.height / 1000.0); - generator.writeln("clip newpath"); + generator.writeln(generator.mapCommand("clip") + " " + generator.mapCommand("newpath")); } catch (IOException ioe) { throw new IFException("I/O error in clipRect()", ioe); } @@ -227,7 +227,7 @@ public class PSPainter extends AbstractIFPainter { } generator.defineRect(rect.x / 1000.0, rect.y / 1000.0, rect.width / 1000.0, rect.height / 1000.0); - generator.writeln("fill"); + generator.writeln(generator.mapCommand("fill")); } catch (IOException ioe) { throw new IFException("I/O error in fillRect()", ioe); } @@ -240,7 +240,12 @@ public class PSPainter extends AbstractIFPainter { if (before != null || after != null || start != null || end != null) { try { endTextObject(); - this.borderPainter.drawBorders(rect, before, after, start, end); + if (getPSUtil().getRenderingMode() == PSRenderingMode.SIZE + && hasOnlySolidBorders(before, after, start, end)) { + super.drawBorderRect(rect, before, after, start, end); + } else { + this.borderPainter.drawBorders(rect, before, after, start, end); + } } catch (IOException ioe) { throw new IFException("I/O error in drawBorderRect()", ioe); } @@ -479,9 +484,9 @@ public class PSPainter extends AbstractIFPainter { spb.append(formatMptAsPt(generator, letterSpacing)) .append(" 0 "); sb.insert(0, spb.toString()); - sb.append(" ashow"); + sb.append(" " + generator.mapCommand("ashow")); } else { - sb.append(" show"); + sb.append(" " + generator.mapCommand("show")); } } generator.writeln(sb.toString()); diff --git a/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java b/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java index 2280bf0dc..00f3fc154 100644 --- a/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java +++ b/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java @@ -19,6 +19,8 @@ package org.apache.fop.render.ps; +import java.util.Locale; + import org.apache.avalon.framework.configuration.Configuration; import org.apache.xmlgraphics.ps.PSGenerator; @@ -66,6 +68,12 @@ public class PSRendererConfigurator extends PrintRendererConfigurator if (child != null) { psUtil.setOptimizeResources(child.getValueAsBoolean(false)); } + child = cfg.getChild("rendering"); + if (child != null) { + psUtil.setRenderingMode(PSRenderingMode.valueOf( + child.getValue(psUtil.getRenderingMode().toString()) + .toUpperCase(Locale.ENGLISH))); + } psUtil.setSafeSetPageDevice( cfg.getChild("safe-set-page-device").getValueAsBoolean(false)); psUtil.setDSCComplianceEnabled( diff --git a/src/java/org/apache/fop/render/ps/PSRenderingMode.java b/src/java/org/apache/fop/render/ps/PSRenderingMode.java new file mode 100644 index 000000000..dc48b56ac --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSRenderingMode.java @@ -0,0 +1,31 @@ +/* + * 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.ps; + +/** + * Enumeration that specifies the rendering mode for PostScript output. {@link #SIZE} tries + * to produce smaller files at the expense of quality, whereas {@link #QUALITY} tries to + * produce the best possible quality. + */ +enum PSRenderingMode { + + SIZE, QUALITY; + +} diff --git a/src/java/org/apache/fop/render/ps/PSRenderingUtil.java b/src/java/org/apache/fop/render/ps/PSRenderingUtil.java index d7890d144..b22391718 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderingUtil.java +++ b/src/java/org/apache/fop/render/ps/PSRenderingUtil.java @@ -56,6 +56,12 @@ public class PSRenderingUtil implements PSConfigurationConstants { /** Determines whether the PS file is generated in two passes to minimize file size */ private boolean optimizeResources = false; + /** + * Determines whether the generated PostScript code is optimized for minimum file size + * of best quality. + */ + private PSRenderingMode renderingMode = PSRenderingMode.QUALITY; + PSRenderingUtil(FOUserAgent userAgent) { this.userAgent = userAgent; initialize(); @@ -287,5 +293,19 @@ public class PSRenderingUtil implements PSConfigurationConstants { return optimizeResources; } + /** + * Sets the rendering mode. + * @param renderingMode the rendering mode + */ + public void setRenderingMode(PSRenderingMode renderingMode) { + this.renderingMode = renderingMode; + } + /** + * Returns the rendering mode. + * @return the rendering mode + */ + public PSRenderingMode getRenderingMode() { + return this.renderingMode; + } } diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 35f58e170..ac060caff 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -78,6 +78,7 @@ import org.apache.fop.fo.flow.ListItemBody; import org.apache.fop.fo.flow.ListItemLabel; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fo.flow.PageNumberCitation; +import org.apache.fop.fo.flow.PageNumberCitationLast; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.fo.flow.table.TableCell; @@ -99,6 +100,7 @@ import org.apache.fop.layoutmgr.inline.ImageLayout; import org.apache.fop.layoutmgr.table.ColumnSetup; import org.apache.fop.render.DefaultFontResolver; import org.apache.fop.render.RendererEventProducer; +import org.apache.fop.render.rtf.rtflib.exceptions.RtfException; import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfAfterContainer; import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfBeforeContainer; import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfListContainer; @@ -1448,6 +1450,29 @@ public class RTFHandler extends FOEventHandler { } } + /** {@inheritDoc} */ + public void startPageNumberCitationLast(PageNumberCitationLast l) { + if (bDefer) { + return; + } + try { + + IRtfTextrunContainer container + = (IRtfTextrunContainer)builderContext.getContainer( + IRtfTextrunContainer.class, true, this); + RtfTextrun textrun = container.getTextrun(); + + textrun.addPageNumberCitation(l.getRefId()); + + } catch (RtfException e) { + log.error("startPageNumberCitationLast: " + e.getMessage()); + throw new RuntimeException(e.getMessage()); + } catch (IOException e) { + log.error("startPageNumberCitationLast: " + e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + private void prepareTable(Table tab) { // Allows to receive the available width of the table percentManager.setDimension(tab); @@ -1641,6 +1666,12 @@ public class RTFHandler extends FOEventHandler { } else { endPageNumberCitation((PageNumberCitation) foNode); } + } else if (foNode instanceof PageNumberCitationLast) { + if (bStart) { + startPageNumberCitationLast((PageNumberCitationLast) foNode); + } else { + endPageNumberCitationLast((PageNumberCitationLast) foNode); + } } else { RTFEventProducer eventProducer = RTFEventProducer.Provider.get( getUserAgent().getEventBroadcaster()); diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java index 7a9245dd5..0cbd1f686 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfTextrun.java @@ -437,10 +437,16 @@ public class RtfTextrun extends RtfContainer { //get last RtfParagraphBreak, which is not followed by any visible child RtfParagraphBreak lastParagraphBreak = null; if (bLast) { + RtfElement aBefore = null; for (Iterator it = getChildren().iterator(); it.hasNext();) { final RtfElement e = (RtfElement)it.next(); if (e instanceof RtfParagraphBreak) { - lastParagraphBreak = (RtfParagraphBreak)e; + //If the element before was a paragraph break or a bookmark + //they will be hidden and are therefore not considered as visible + if (!(aBefore instanceof RtfParagraphBreak) + && !(aBefore instanceof RtfBookmark)) { + lastParagraphBreak = (RtfParagraphBreak)e; + } } else { if (!(e instanceof RtfOpenGroupMark) && !(e instanceof RtfCloseGroupMark) @@ -448,6 +454,7 @@ public class RtfTextrun extends RtfContainer { lastParagraphBreak = null; } } + aBefore = e; } } @@ -460,6 +467,7 @@ public class RtfTextrun extends RtfContainer { //write all children boolean bPrevPar = false; + boolean bBookmark = false; boolean bFirst = true; for (Iterator it = getChildren().iterator(); it.hasNext();) { final RtfElement e = (RtfElement)it.next(); @@ -484,7 +492,8 @@ public class RtfTextrun extends RtfContainer { && (bPrevPar || bFirst || (bSuppressLastPar && bLast && lastParagraphBreak != null - && e == lastParagraphBreak)); + && e == lastParagraphBreak) + || bBookmark); if (!bHide) { newLine(); @@ -497,6 +506,8 @@ public class RtfTextrun extends RtfContainer { if (e instanceof RtfParagraphBreak) { bPrevPar = true; + } else if (e instanceof RtfBookmark) { + bBookmark = true; } else if (e instanceof RtfCloseGroupMark) { //do nothing } else if (e instanceof RtfOpenGroupMark) { @@ -504,6 +515,7 @@ public class RtfTextrun extends RtfContainer { } else { bPrevPar = bPrevPar && e.isEmpty(); bFirst = bFirst && e.isEmpty(); + bBookmark = false; } } //for (Iterator it = ...) diff --git a/src/java/org/apache/fop/svg/ACIUtils.java b/src/java/org/apache/fop/svg/ACIUtils.java index 91dfb85ca..91361432a 100644 --- a/src/java/org/apache/fop/svg/ACIUtils.java +++ b/src/java/org/apache/fop/svg/ACIUtils.java @@ -19,29 +19,161 @@ package org.apache.fop.svg; +import java.awt.font.TextAttribute; import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; -import java.util.Iterator; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.apache.batik.bridge.SVGFontFamily; +import org.apache.batik.gvt.font.GVTFont; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; /** * Utilities for java.text.AttributedCharacterIterator. */ public final class ACIUtils { + /** the logger for this class */ + private static final Log LOG = LogFactory.getLog(ACIUtils.class); + private ACIUtils() { //This class shouldn't be instantiated. } /** + * Tries to find matching fonts in FOP's {@link FontInfo} instance for fonts used by + * Apache Batik. The method inspects the various GVT attributes found in the ACI. + * @param aci the ACI to find matching fonts for + * @param fontInfo the font info instance with FOP's fonts + * @return an array of matching fonts + */ + public static Font[] findFontsForBatikACI(AttributedCharacterIterator aci, FontInfo fontInfo) { + List<Font> fonts = new java.util.ArrayList<Font>(); + @SuppressWarnings("unchecked") + List<GVTFontFamily> gvtFonts = (List<GVTFontFamily>) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); + Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); + Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE); + + String style = toStyle(posture); + int weight = toCSSWeight(taWeight); + int fsize = (int)(fontSize.floatValue() * 1000); + + String firstFontFamily = null; + + //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES + //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set + /* The following code section is not available until Batik 1.7 is released. */ + GVTFont gvtFont = (GVTFont)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT); + if (gvtFont != null) { + String gvtFontFamily = gvtFont.getFamilyName(); + if (fontInfo.hasFont(gvtFontFamily, style, weight)) { + FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style, + weight); + Font f = fontInfo.getFontInstance(triplet, fsize); + if (LOG.isDebugEnabled()) { + LOG.debug("Found a font that matches the GVT font: " + + gvtFontFamily + ", " + weight + ", " + style + + " -> " + f); + } + fonts.add(f); + } + firstFontFamily = gvtFontFamily; + } + + if (gvtFonts != null) { + for (GVTFontFamily fam : gvtFonts) { + if (fam instanceof SVGFontFamily) { + return null; //Let Batik paint this text! + } + String fontFamily = fam.getFamilyName(); + if (fontInfo.hasFont(fontFamily, style, weight)) { + FontTriplet triplet = fontInfo.fontLookup(fontFamily, style, + weight); + Font f = fontInfo.getFontInstance(triplet, fsize); + if (LOG.isDebugEnabled()) { + LOG.debug("Found a font that matches the GVT font family: " + + fontFamily + ", " + weight + ", " + style + + " -> " + f); + } + fonts.add(f); + } + if (firstFontFamily == null) { + firstFontFamily = fontFamily; + } + } + } + if (fonts.isEmpty()) { + if (firstFontFamily == null) { + //This will probably never happen. Just to be on the safe side. + firstFontFamily = "any"; + } + //lookup with fallback possibility (incl. substitution notification) + FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight); + Font f = fontInfo.getFontInstance(triplet, fsize); + if (LOG.isDebugEnabled()) { + LOG.debug("Falling back to adjustable font lookup up for: " + + firstFontFamily + ", " + weight + ", " + style + + " -> " + f); + } + fonts.add(f); + } + return fonts.toArray(new Font[fonts.size()]); + } + + private static int toCSSWeight(Float weight) { + if (weight == null) { + return 400; + } else if (weight <= TextAttribute.WEIGHT_EXTRA_LIGHT.floatValue()) { + return 100; + } else if (weight <= TextAttribute.WEIGHT_LIGHT.floatValue()) { + return 200; + } else if (weight <= TextAttribute.WEIGHT_DEMILIGHT.floatValue()) { + return 300; + } else if (weight <= TextAttribute.WEIGHT_REGULAR.floatValue()) { + return 400; + } else if (weight <= TextAttribute.WEIGHT_SEMIBOLD.floatValue()) { + return 500; + } else if (weight < TextAttribute.WEIGHT_BOLD.floatValue()) { + return 600; + } else if (weight == TextAttribute.WEIGHT_BOLD.floatValue()) { + return 700; + } else if (weight <= TextAttribute.WEIGHT_HEAVY.floatValue()) { + return 800; + } else if (weight <= TextAttribute.WEIGHT_EXTRABOLD.floatValue()) { + return 900; + } else { + return 900; + } + } + + private static String toStyle(Float posture) { + return ((posture != null) && (posture.floatValue() > 0.0)) + ? Font.STYLE_ITALIC + : Font.STYLE_NORMAL; + } + + /** * Dumps the contents of an ACI to System.out. Used for debugging only. * @param aci the ACI to dump */ public static void dumpAttrs(AttributedCharacterIterator aci) { aci.first(); - Iterator i = aci.getAttributes().entrySet().iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); + Set<Entry<Attribute, Object>> entries = aci.getAttributes().entrySet(); + for (Map.Entry<Attribute, Object> entry : entries) { if (entry.getValue() != null) { System.out.println(entry.getKey() + ": " + entry.getValue()); } diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java index 69d02d814..77179b381 100644 --- a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java +++ b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java @@ -36,7 +36,6 @@ import java.util.List; import org.apache.batik.dom.svg.SVGOMTextElement; import org.apache.batik.gvt.TextNode; import org.apache.batik.gvt.TextPainter; -import org.apache.batik.gvt.font.GVTFontFamily; import org.apache.batik.gvt.renderer.StrokingTextPainter; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.Mark; @@ -46,8 +45,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.fonts.Font; -import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; /** * Renders the attributed character iterator of a {@link TextNode}. @@ -336,53 +333,9 @@ public abstract class AbstractFOPTextPainter implements TextPainter { } } - private String getStyle(AttributedCharacterIterator aci) { - Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); - return ((posture != null) && (posture.floatValue() > 0.0)) - ? Font.STYLE_ITALIC - : Font.STYLE_NORMAL; - } - - private int getWeight(AttributedCharacterIterator aci) { - Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); - return ((taWeight != null) && (taWeight.floatValue() > 1.0)) - ? Font.WEIGHT_BOLD - : Font.WEIGHT_NORMAL; - } - private Font getFont(AttributedCharacterIterator aci) { - Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); - if (fontSize == null) { - fontSize = new Float(10f); - } - String style = getStyle(aci); - int weight = getWeight(aci); - - FontInfo fontInfo = nativeTextHandler.getFontInfo(); - String fontFamily = null; - List gvtFonts = (List) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - if (gvtFonts != null) { - Iterator i = gvtFonts.iterator(); - while (i.hasNext()) { - GVTFontFamily fam = (GVTFontFamily) i.next(); - /* (todo) Enable SVG Font painting - if (fam instanceof SVGFontFamily) { - PROXY_PAINTER.paint(node, g2d); - return; - }*/ - fontFamily = fam.getFamilyName(); - if (fontInfo.hasFont(fontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup( - fontFamily, style, weight); - int fsize = (int)(fontSize.floatValue() * 1000); - return fontInfo.getFontInstance(triplet, fsize); - } - } - } - FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL); - int fsize = (int)(fontSize.floatValue() * 1000); - return fontInfo.getFontInstance(triplet, fsize); + Font[] fonts = ACIUtils.findFontsForBatikACI(aci, nativeTextHandler.getFontInfo()); + return fonts[0]; } private float getStringWidth(String str, Font font) { diff --git a/src/java/org/apache/fop/svg/NativeTextPainter.java b/src/java/org/apache/fop/svg/NativeTextPainter.java index 94d426396..b641337de 100644 --- a/src/java/org/apache/fop/svg/NativeTextPainter.java +++ b/src/java/org/apache/fop/svg/NativeTextPainter.java @@ -20,24 +20,17 @@ package org.apache.fop.svg; import java.awt.Graphics2D; -import java.awt.font.TextAttribute; import java.io.IOException; import java.text.AttributedCharacterIterator; -import java.util.Iterator; import java.util.List; -import org.apache.batik.bridge.SVGFontFamily; -import org.apache.batik.gvt.font.GVTFont; -import org.apache.batik.gvt.font.GVTFontFamily; import org.apache.batik.gvt.renderer.StrokingTextPainter; -import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.TextSpanLayout; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; import org.apache.fop.util.CharUtilities; /** @@ -77,6 +70,7 @@ public abstract class NativeTextPainter extends StrokingTextPainter { protected abstract void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException; /** {@inheritDoc} */ + @Override protected void paintTextRuns(List textRuns, Graphics2D g2d) { if (log.isTraceEnabled()) { log.trace("paintTextRuns: count = " + textRuns.size()); @@ -102,100 +96,8 @@ public abstract class NativeTextPainter extends StrokingTextPainter { * @return the array of fonts */ protected Font[] findFonts(AttributedCharacterIterator aci) { - List fonts = new java.util.ArrayList(); - List gvtFonts = (List) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); - Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); - Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE); - - String style = ((posture != null) && (posture.floatValue() > 0.0)) - ? Font.STYLE_ITALIC : Font.STYLE_NORMAL; - int weight = toCSSWeight(taWeight != null - ? taWeight.floatValue() - : TextAttribute.WEIGHT_REGULAR.floatValue()); - - String firstFontFamily = null; - - //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES - //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set - /* The following code section is not available until Batik 1.7 is released. */ - GVTFont gvtFont = (GVTFont)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT); - if (gvtFont != null) { - try { - String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6! - if (log.isDebugEnabled()) { - log.debug("Matching font family: " + gvtFontFamily); - } - if (fontInfo.hasFont(gvtFontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style, - weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - firstFontFamily = gvtFontFamily; - } catch (Exception e) { - //Most likely NoSuchMethodError here when using Batik 1.6 - //Just skip this section in this case - } - } - - if (gvtFonts != null) { - Iterator i = gvtFonts.iterator(); - while (i.hasNext()) { - GVTFontFamily fam = (GVTFontFamily) i.next(); - if (fam instanceof SVGFontFamily) { - return null; //Let Batik paint this text! - } - String fontFamily = fam.getFamilyName(); - if (log.isDebugEnabled()) { - log.debug("Matching font family: " + fontFamily); - } - if (fontInfo.hasFont(fontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(fontFamily, style, - weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - if (firstFontFamily == null) { - firstFontFamily = fontFamily; - } - } - } - if (fonts.size() == 0) { - if (firstFontFamily == null) { - //This will probably never happen. Just to be on the safe side. - firstFontFamily = "any"; - } - //lookup with fallback possibility (incl. substitution notification) - FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - return (Font[])fonts.toArray(new Font[fonts.size()]); - } - - private int toCSSWeight(float weight) { - if (weight <= TextAttribute.WEIGHT_EXTRA_LIGHT.floatValue()) { - return 100; - } else if (weight <= TextAttribute.WEIGHT_LIGHT.floatValue()) { - return 200; - } else if (weight <= TextAttribute.WEIGHT_DEMILIGHT.floatValue()) { - return 300; - } else if (weight <= TextAttribute.WEIGHT_REGULAR.floatValue()) { - return 400; - } else if (weight <= TextAttribute.WEIGHT_SEMIBOLD.floatValue()) { - return 500; - } else if (weight <= TextAttribute.WEIGHT_BOLD.floatValue()) { - return 600; - } else if (weight <= TextAttribute.WEIGHT_HEAVY.floatValue()) { - return 700; - } else if (weight <= TextAttribute.WEIGHT_EXTRABOLD.floatValue()) { - return 800; - } else { - return 900; - } + Font[] fonts = ACIUtils.findFontsForBatikACI(aci, fontInfo); + return fonts; } /** diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index e93914258..4ae8e72d7 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -243,6 +243,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @return a new graphics context that is a copy of * this graphics context. */ + @Override public Graphics create() { return new PDFGraphics2D(this); } @@ -486,6 +487,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ + @Override public boolean drawImage(Image img, int x, int y, ImageObserver observer) { preparePainting(); @@ -506,6 +508,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { preparePainting(); @@ -576,6 +579,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see java.awt.Component#getGraphics * @see java.awt.Graphics#create */ + @Override public void dispose() { pdfDoc = null; fontInfo = null; @@ -599,6 +603,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see #setClip * @see #setComposite */ + @Override public void draw(Shape s) { preparePainting(); @@ -619,8 +624,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand && !trans.isIdentity(); if (newClip || newTransform) { - currentStream.write("q\n"); - paintingState.save(); + saveGraphicsState(); if (newTransform) { concatMatrix(tranvals); } @@ -645,8 +649,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand applyUnknownPaint(paint, ss); if (newClip || newTransform) { - currentStream.write("Q\n"); - paintingState.restore(); + restoreGraphicsState(); } return; } @@ -657,8 +660,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand processPathIterator(iter); doDrawing(false, true, false); if (newClip || newTransform) { - currentStream.write("Q\n"); - paintingState.restore(); + restoreGraphicsState(); } } @@ -748,9 +750,17 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } } - StringBuffer sb = new StringBuffer(); - colorHandler.establishColor(sb, col, fill); - currentStream.write(sb.toString()); + boolean doWrite = false; + if (fill && paintingState.setBackColor(col)) { + doWrite = true; + } else if (paintingState.setColor(col)) { + doWrite = true; + } + if (doWrite) { + StringBuffer sb = new StringBuffer(); + colorHandler.establishColor(sb, col, fill); + currentStream.write(sb.toString()); + } } /** @@ -805,7 +815,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand transform.concatenate(getTransform()); transform.concatenate(gp.getTransform()); - List theMatrix = new java.util.ArrayList(); + List<Double> theMatrix = new java.util.ArrayList<Double>(); double [] mat = new double[6]; transform.getMatrix(mat); for (int idx = 0; idx < mat.length; idx++) { @@ -814,29 +824,29 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand Point2D p1 = gp.getStartPoint(); Point2D p2 = gp.getEndPoint(); - List theCoords = new java.util.ArrayList(); + List<Double> theCoords = new java.util.ArrayList<Double>(); theCoords.add(new Double(p1.getX())); theCoords.add(new Double(p1.getY())); theCoords.add(new Double(p2.getX())); theCoords.add(new Double(p2.getY())); - List theExtend = new java.util.ArrayList(); - theExtend.add(new Boolean(true)); - theExtend.add(new Boolean(true)); + List<Boolean> theExtend = new java.util.ArrayList<Boolean>(); + theExtend.add(Boolean.TRUE); + theExtend.add(Boolean.TRUE); - List theDomain = new java.util.ArrayList(); + List<Double> theDomain = new java.util.ArrayList<Double>(); theDomain.add(new Double(0)); theDomain.add(new Double(1)); - List theEncode = new java.util.ArrayList(); + List<Double> theEncode = new java.util.ArrayList<Double>(); theEncode.add(new Double(0)); theEncode.add(new Double(1)); theEncode.add(new Double(0)); theEncode.add(new Double(1)); - List theBounds = new java.util.ArrayList(); + List<Double> theBounds = new java.util.ArrayList<Double>(); - List someColors = new java.util.ArrayList(); + List<Color> someColors = new java.util.ArrayList<Color>(); for (int count = 0; count < cols.length; count++) { Color c1 = cols[count]; @@ -882,7 +892,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand transform.concatenate(getTransform()); transform.concatenate(rgp.getTransform()); - List theMatrix = new java.util.ArrayList(); + List<Double> theMatrix = new java.util.ArrayList<Double>(); double [] mat = new double[6]; transform.getMatrix(mat); for (int idx = 0; idx < mat.length; idx++) { @@ -893,7 +903,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand Point2D ac = rgp.getCenterPoint(); Point2D af = rgp.getFocusPoint(); - List theCoords = new java.util.ArrayList(); + List<Double> theCoords = new java.util.ArrayList<Double>(); double dx = af.getX() - ac.getX(); double dy = af.getY() - ac.getY(); double d = Math.sqrt(dx * dx + dy * dy); @@ -913,7 +923,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand theCoords.add(new Double(ar)); Color[] cols = rgp.getColors(); - List someColors = new java.util.ArrayList(); + List<Color> someColors = new java.util.ArrayList<Color>(); for (int count = 0; count < cols.length; count++) { Color cc = cols[count]; if (cc.getAlpha() != 255) { @@ -924,7 +934,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } float[] fractions = rgp.getFractions(); - List theBounds = new java.util.ArrayList(); + List<Double> theBounds = new java.util.ArrayList<Double>(); for (int count = 1; count < fractions.length - 1; count++) { float offset = fractions[count]; theBounds.add(new Double(offset)); @@ -964,7 +974,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand pattGraphic.setOutputStream(outputStream); GraphicsNode gn = pp.getGraphicsNode(); - Rectangle2D gnBBox = gn.getBounds(); + //Rectangle2D gnBBox = gn.getBounds(); Rectangle2D rect = pp.getPatternRect(); // if (!pp.getOverflow()) { @@ -1000,7 +1010,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand // } // } - List bbox = new java.util.ArrayList(); + List<Double> bbox = new java.util.ArrayList<Double>(); bbox.add(new Double(rect.getX())); bbox.add(new Double(rect.getHeight() + rect.getY())); bbox.add(new Double(rect.getWidth() + rect.getX())); @@ -1011,7 +1021,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand transform.concatenate(getTransform()); transform.concatenate(pp.getPatternTransform()); - List theMatrix = new java.util.ArrayList(); + List<Double> theMatrix = new java.util.ArrayList<Double>(); double [] mat = new double[6]; transform.getMatrix(mat); for (int idx = 0; idx < mat.length; idx++) { @@ -1219,6 +1229,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void drawRenderedImage(RenderedImage img, AffineTransform xform) { String key = "TempImage:" + img.toString(); drawInnerRenderedImage(key, img, xform); @@ -1268,6 +1279,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** {@inheritDoc} */ + @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { //TODO Check if this is good enough @@ -1298,6 +1310,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see #setComposite * @see #setClip */ + @Override public void drawString(String s, float x, float y) { preparePainting(); @@ -1321,7 +1334,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand applyPaint(getPaint(), true); applyAlpha(c.getAlpha(), OPAQUE); - Map kerning = fontState.getKerning(); + Map<Integer, Map<Integer, Integer>> kerning = fontState.getKerning(); boolean kerningAvailable = (kerning != null && !kerning.isEmpty()); boolean useMultiByte = isMultiByteFont(currentFontName); @@ -1379,8 +1392,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } if (kerningAvailable && (i + 1) < l) { - addKerning(currentStream, (new Integer(ch)), - (new Integer(fontState.mapChar(s.charAt(i + 1)))), + addKerning(currentStream, (Integer.valueOf(ch)), + (Integer.valueOf(fontState.mapChar(s.charAt(i + 1)))), kerning, startText, endText); } @@ -1400,7 +1413,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand protected void applyAlpha(int fillAlpha, int strokeAlpha) { if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) { checkTransparencyAllowed(); - Map vals = new java.util.HashMap(); + Map<String, Float> vals = new java.util.HashMap<String, Float>(); if (fillAlpha != OPAQUE) { vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f)); } @@ -1437,6 +1450,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @return the internal Font * @deprecated use FontInfo.getFontInstanceForAWTFont(java.awt.Font awtFont) instead */ + @Deprecated protected Font getInternalFontForAWTFont(java.awt.Font awtFont) { return fontInfo.getFontInstanceForAWTFont(awtFont); } @@ -1449,18 +1463,18 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand protected boolean isMultiByteFont(String name) { // This assumes that *all* CIDFonts use a /ToUnicode mapping org.apache.fop.fonts.Typeface f - = (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name); + = fontInfo.getFonts().get(name); return f.isMultiByte(); } private void addKerning(StringWriter buf, Integer ch1, Integer ch2, - Map kerning, String startText, + Map<Integer, Map<Integer, Integer>> kerning, String startText, String endText) { preparePainting(); - Map kernPair = (Map)kerning.get(ch1); + Map<Integer, Integer> kernPair = kerning.get(ch1); if (kernPair != null) { - Integer width = (Integer)kernPair.get(ch2); + Integer width = kernPair.get(ch2); if (width != null) { currentStream.write(endText + (-width.intValue()) + " " + startText); } @@ -1572,6 +1586,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see #clip * @see #setClip */ + @Override public void fill(Shape s) { preparePainting(); @@ -1595,8 +1610,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand && !trans.isIdentity(); if (newClip || newTransform) { - currentStream.write("q\n"); - paintingState.save(); + saveGraphicsState(); if (newTransform) { concatMatrix(tranvals); } @@ -1619,24 +1633,40 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand applyUnknownPaint(paint, s); if (newClip || newTransform) { - currentStream.write("Q\n"); - paintingState.restore(); + restoreGraphicsState(); } return; } } - //PathIterator iter = s.getPathIterator(getTransform()); - PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM); - processPathIterator(iter); - doDrawing(true, false, - iter.getWindingRule() == PathIterator.WIND_EVEN_ODD); + if (s instanceof Rectangle2D) { + Rectangle2D rect = (Rectangle2D)s; + currentStream.write(PDFNumber.doubleOut(rect.getMinX(), DEC) + " " + + PDFNumber.doubleOut(rect.getMinY(), DEC) + " "); + currentStream.write(PDFNumber.doubleOut(rect.getWidth(), DEC) + " " + + PDFNumber.doubleOut(rect.getHeight(), DEC) + " re "); + doDrawing(true, false, false); + } else { + PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM); + processPathIterator(iter); + doDrawing(true, false, + iter.getWindingRule() == PathIterator.WIND_EVEN_ODD); + } if (newClip || newTransform) { - currentStream.write("Q\n"); - paintingState.restore(); + restoreGraphicsState(); } } + void saveGraphicsState() { + currentStream.write("q\n"); + paintingState.save(); + } + + void restoreGraphicsState() { + currentStream.write("Q\n"); + paintingState.restore(); + } + /** Checks whether the use of transparency is allowed. */ protected void checkTransparencyAllowed() { pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics"); @@ -1720,6 +1750,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * * @return the PDF graphics configuration */ + @Override public GraphicsConfiguration getDeviceConfiguration() { return new PDFGraphicsConfiguration(); } @@ -1744,6 +1775,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @see java.awt.FontMetrics * @see java.awt.Graphics#getFontMetrics() */ + @Override public java.awt.FontMetrics getFontMetrics(java.awt.Font f) { return fmg.getFontMetrics(f); } @@ -1763,6 +1795,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * drawn twice, then all pixels are restored to their original values. * @param c1 the XOR alternation color */ + @Override public void setXORMode(Color c1) { //NYI } @@ -1787,6 +1820,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @param dx the horizontal distance to copy the pixels. * @param dy the vertical distance to copy the pixels. */ + @Override public void copyArea(int x, int y, int width, int height, int dx, int dy) { //NYI diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java index fe8ad1c7e..00160f091 100644 --- a/src/java/org/apache/fop/svg/PDFTextPainter.java +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -62,11 +62,13 @@ class PDFTextPainter extends NativeTextPainter { } /** {@inheritDoc} */ + @Override protected boolean isSupported(Graphics2D g2d) { return g2d instanceof PDFGraphics2D; } /** {@inheritDoc} */ + @Override protected void paintTextRun(TextRun textRun, Graphics2D g2d) { AttributedCharacterIterator runaci = textRun.getACI(); runaci.first(); @@ -86,7 +88,9 @@ class PDFTextPainter extends NativeTextPainter { runaci.first(); //Reset ACI final PDFGraphics2D pdf = (PDFGraphics2D)g2d; + PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) { + @Override protected void write(String code) { pdf.currentStream.write(code); } @@ -109,7 +113,7 @@ class PDFTextPainter extends NativeTextPainter { return; } - textUtil.saveGraphicsState(); + pdf.saveGraphicsState(); textUtil.concatMatrix(g2d.getTransform()); Shape imclip = g2d.getClip(); pdf.writeClip(imclip); @@ -197,7 +201,7 @@ class PDFTextPainter extends NativeTextPainter { } textUtil.writeTJ(); textUtil.endTextObject(); - textUtil.restoreGraphicsState(); + pdf.restoreGraphicsState(); if (DEBUG) { g2d.setStroke(new BasicStroke(0)); g2d.setColor(Color.LIGHT_GRAY); diff --git a/status.xml b/status.xml index 9378e627f..f1b5de7b7 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,49 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> <release version="FOP Trunk" date="TBD"> + <action context="Renderers" dev="VH" type="fix"> + Bugfix: alternative text not working in tagged PDF for TIFF images. + </action> + <action context="Renderers" dev="PH" type="fix" fixes-bug="50909"> + Fixed io exception in MODCAParser caused by the improper use of mark() and reset() on the + MODCA data input stream. Added unit test. </action> + <action context="Fonts" dev="JM" type="fix" fixes-bug="51144" due-to="Mehdi Houshmand"> + Removed invalid entries in ToUnicode table of CID subset fonts. + </action> + <action context="Renderers" dev="JM" type="fix" fixes-bug="50899" due-to="Glenn Adams"> + Fixed mapping of font weights between CSS values and TextAttribute.WEIGHT_*. + </action> + <action context="Renderers" dev="JM" type="fix"> + AFP GOCA: fonts were not embedded from within AFPGraphics2D. + </action> + <action context="Renderers" dev="JM" type="fix"> + AFP GOCA: Changed the way FOP fonts are selected based on Batik's GVT fonts to match + the behaviour of PDF/PS output. + </action> + <action context="Renderers" dev="JM" type="add"> + Added option to place AFP NOPs right before the end of a named page group (page-sequence), + rather than after the start. + </action> + <action context="Renderers" dev="JM" type="add"> + Added option for PostScript output to optimize for file size rather than quality. + </action> + <action context="Renderers" dev="JM" type="add"> + AFP GOCA: Added option to disable GOCA and to control text painting inside GOCA graphics. + </action> + <action context="Renderers" dev="JM" type="fix"> + AFP GOCA: Work-around for InfoPrint's AFP implementation which seems to lose + the character set state over Graphics Data (GAD) boundaries. + </action> + <action context="Renderers" dev="JM" type="fix"> + Bugfix for AFP GOCA segments: they were not properly marked as appended which could + lead to graphics state changes in some implementations. + </action> + <action context="Renderers" dev="CB" type="fix" fixes-bug="51010" due-to="Max Aster"> + Bugzilla 51010: Bookmarks create useless lines in RTF + </action> + <action context="Renderers" dev="CB" type="fix" fixes-bug="51008" due-to="Max Aster"> + Bugzilla 51008: page-number-citation-last does not work in RTF + </action> <action context="Renderers" dev="VH" type="add"> Added id element to intermediate format to track the origin of content. </action> diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index 480ab8bd0..d6a6f8367 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -23,6 +23,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.apache.fop.area.ViewportTestSuite; +import org.apache.fop.afp.parser.MODCAParserTestCase; import org.apache.fop.fonts.DejaVuLGCSerifTest; import org.apache.fop.image.loader.batik.ImageLoaderTestCase; import org.apache.fop.image.loader.batik.ImagePreloaderTestCase; @@ -57,6 +58,7 @@ public class StandardTestSuite { suite.addTest(new TestSuite(PDFCMapTestCase.class)); suite.addTest(new TestSuite(PDFsRGBSettingsTestCase.class)); suite.addTest(new TestSuite(DejaVuLGCSerifTest.class)); + suite.addTest(new TestSuite(MODCAParserTestCase.class)); suite.addTest(AFPTestSuite.suite()); suite.addTest(PSTestSuite.suite()); suite.addTest(RichTextFormatTestSuite.suite()); diff --git a/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java b/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java new file mode 100644 index 000000000..e079820b7 --- /dev/null +++ b/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java @@ -0,0 +1,238 @@ +/* + * 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.afp.parser; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.InputStream; +import java.util.Arrays; + +import junit.framework.TestCase; + +/** + * MODCAParser and MODCAParser.UnparsedStructuredField Unit tests + */ +public class MODCAParserTestCase extends TestCase { + + /** The carriage control character (0x5A) used to indicate the start of a structured field. */ + public static final byte CARRIAGE_CONTROL_CHAR = (byte)0x5A; + /**ASCII carriage return control character*/ + public static final byte CARRIAGE_RETURN = (byte)0x0A; + /**ASCII line feed control character */ + public static final byte LINE_FEED = (byte)0x0D; + /** 8 byte introducer describe the SF */ + private static final int INTRODUCER_LENGTH = 8; + + /** + * Test that the MODCA parser recognises carriage control (0x5A) as the Structured Field + * delimeter + * + * @throws Exception * + */ + public void testReadNextStructuredField1() throws Exception { + + // carriage control (0x5A) delimits structured fields, + // and control is handed to readStructuredField(DataInputStream) + byte[][] goodInputStream = new byte[][]{ + new byte[]{CARRIAGE_CONTROL_CHAR} + }; + + for (byte[] b : goodInputStream) { + try { + new MODCAParser(new ByteArrayInputStream(b)) + .readNextStructuredField(); + fail("BAD SF should throw EOF: " + byteArrayToString(b)); + } catch (EOFException eof) { + //passed + } + } + + // EOFException thrown when reading the input stream are caught and + // a null value is returned + byte[][] badInputStream = new byte[][]{ + new byte[]{}, + new byte[]{CARRIAGE_RETURN}, + new byte[]{LINE_FEED} + }; + + for (byte[] b : badInputStream) { + UnparsedStructuredField usf = new MODCAParser(new ByteArrayInputStream(b)) + .readNextStructuredField(); + assertNull(usf); + } + } + + + /** + * Test that the MODCA parser correctly constructs an UnparsedStructuredField + * from a well formed structured field + * + * @throws Exception * + */ + public void testReadNextStructuredField2() throws Exception { + + // no extension data + testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID + (byte)0, //flags excluding the bits for + //extension present, segmented data and padding present + false, false, + new byte[]{0, 0}, + new byte[]{1}, null); + + // with extension data + testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID + (byte)0, //flags excluding the bits for + //extension present, segmented data and padding present + false, false, + new byte[]{0, 0}, + new byte[]{1}, new byte[]{10}); + + // with ignored reserved bits + testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID + (byte)0, //flags excluding the bits for + //extension present, segmented data and padding present + false, false, + new byte[]{1, 2}, + new byte[]{1}, null); + + // with padding present and segmented data + testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID + (byte)(1 << 3), //flags excluding the bits for + //extension present, segmented data and padding present + true, true, + new byte[]{0, 0}, + new byte[]{1}, null); + + // with flags non zero + testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID + (byte)(1 << 3), //flags excluding the bits for + //extension present, segmented data and padding present + false, false, + new byte[]{0, 0}, + new byte[]{1}, null); + } + + + private void testSF(byte classCode, byte typeCode, byte categoryCode, + byte flags, boolean segmentedData, boolean paddingPresent, byte[] reserved, + byte[] data, byte[] extData) throws Exception { + + byte extDataLength = 0; + boolean extensionPresent = (extData != null); + + if (extensionPresent) { + flags = (byte)(flags | 0x01); + extDataLength = (byte)(extData.length + 1); //length includes length byte + } + + if (segmentedData) { + flags = (byte)(flags | 0x04); + } + + if (paddingPresent) { + flags = (byte)(flags | 0x10); + } + + short length = (short)(INTRODUCER_LENGTH + data.length + extDataLength); + byte[] lengthBytes = new byte[]{(byte)(length >> 8), (byte)(length & 0xFF)}; + + byte[] sfBytes = new byte[length]; + + //introducer bytes + sfBytes[0] = lengthBytes[0]; + sfBytes[1] = lengthBytes[1]; + sfBytes[2] = classCode; + sfBytes[3] = typeCode; + sfBytes[4] = categoryCode; + sfBytes[5] = flags; + sfBytes[6] = reserved[0]; + sfBytes[7] = reserved[1]; + + if (extDataLength > 0) { + sfBytes[8] = (byte)(extData.length + 1); + System.arraycopy(extData, 0, sfBytes, 9, extData.length); + } + + System.arraycopy(data, 0, sfBytes, length - data.length, data.length); + + + byte[] delimiteredSF = new byte[length + 1]; + + delimiteredSF[0] = (byte)0x5A; + + System.arraycopy(sfBytes, 0, delimiteredSF, 1, length); + + InputStream bis = new ByteArrayInputStream(delimiteredSF); + + UnparsedStructuredField actual = new MODCAParser(bis) + .readNextStructuredField(); + + //check introducer + assertEquals(length, actual.getSfLength()); + assertEquals(classCode, actual.getSfClassCode()); + assertEquals(typeCode, actual.getSfTypeCode()); + assertEquals(categoryCode, actual.getSfCategoryCode()); + assertEquals(extensionPresent, actual.isSfiExtensionPresent()); + assertEquals(segmentedData, actual.isSfiSegmentedData()); + assertEquals(paddingPresent, actual.isSfiPaddingPresent()); + + byte[] introducerData = new byte[]{(byte)(length >> 8), (byte)(length & 0xFF), + classCode, typeCode, categoryCode, flags, reserved[0], reserved[1]}; + + assertTrue(Arrays.equals(introducerData, actual.getIntroducerData())); + + //check data + assertTrue(Arrays.equals(data, actual.getData())); + + //check extension data + if (extData != null) { + assertTrue(Arrays.equals(extData, actual.getExtData())); + } + assertEquals( + (extData == null) ? 0 : extData.length + 1, // 1 byte for length byte + actual.getExtLength()); + + assertTrue(Arrays.equals(data, actual.getData())); + + int expectedSfTypeID = ((classCode & 0xFF) << 16) + | ((typeCode & 0xFF) << 8) + | (categoryCode & 0xFF); + + assertEquals(expectedSfTypeID, actual.getSfTypeID()); + + assertTrue(Arrays.equals(sfBytes, actual.getCompleteFieldAsBytes())); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + actual.writeTo(baos); + assertTrue(Arrays.equals(sfBytes, baos.toByteArray())); + + } + + + private static String byteArrayToString(byte[] byteArray) { + StringBuilder sb = new StringBuilder(); + for (byte b : byteArray) { + sb.append(Integer.toHexString(b & 0xFF)).append(" "); + } + return sb.toString(); + } + +} diff --git a/test/java/org/apache/fop/intermediate/AbstractIFTestCase.java b/test/java/org/apache/fop/intermediate/AbstractIFTestCase.java index 56d613e20..77662a5a9 100644 --- a/test/java/org/apache/fop/intermediate/AbstractIFTestCase.java +++ b/test/java/org/apache/fop/intermediate/AbstractIFTestCase.java @@ -34,6 +34,7 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.w3c.dom.Document; + import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -55,8 +56,23 @@ abstract class AbstractIFTestCase extends AbstractIntermediateTestCase { Schema ifSchema = null; try { SchemaFactory sFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + sFactory.setErrorHandler(new ErrorHandler() { + + public void error(SAXParseException exception) throws SAXException { + throw exception; + } + + public void fatalError(SAXParseException exception) throws SAXException { + throw exception; + } + + public void warning(SAXParseException exception) throws SAXException { + throw exception; + } + + }); File ifSchemaFile = new File( - "src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd"); + "src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd"); ifSchema = sFactory.newSchema(ifSchemaFile); } catch (IllegalArgumentException iae) { System.err.println("No suitable SchemaFactory for XML Schema validation found!"); |