diff options
author | Jeremias Maerki <jeremias@apache.org> | 2010-11-15 13:58:40 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2010-11-15 13:58:40 +0000 |
commit | a46140eafd2005fac84de4a359ee484aadb3f968 (patch) | |
tree | 9fccf940d8c91ac917d1f3ca66658be54f6224d5 | |
parent | 327caf5863606991d03fc1e3e5c9880bec75b4ca (diff) | |
parent | 3dc848243b02e9a7b1c24b4dfd252dc545bfa0f7 (diff) | |
download | xmlgraphics-fop-a46140eafd2005fac84de4a359ee484aadb3f968.tar.gz xmlgraphics-fop-a46140eafd2005fac84de4a359ee484aadb3f968.zip |
Merged from Trunk revisions 990171 to 1035276.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript@1035278 13f79535-47bb-0310-9956-ffa450edef68
73 files changed, 2126 insertions, 508 deletions
@@ -149,8 +149,8 @@ list of possible build targets. <property name="javac.debug" value="on"/> <property name="javac.optimize" value="off"/> <property name="javac.deprecation" value="on"/> - <property name="javac.source" value="1.4"/> - <property name="javac.target" value="1.4"/> + <property name="javac.source" value="1.5"/> + <property name="javac.target" value="1.5"/> <property name="javac.fork" value="no"/> <property name="junit.fork" value="on"/> <property name="junit.haltonfailure" value="off"/> @@ -193,8 +193,11 @@ list of possible build targets. <property name="fotree.disabled" value="test/fotree/disabled-testcases.xml"/> <property name="dist.bin.dir" value="${basedir}/dist-bin"/> <property name="dist.src.dir" value="${basedir}/dist-src"/> + <property name="nightly.dir" value="${basedir}/nightly"/> <property name="dist.bin.result.dir" value="${dist.bin.dir}/${name}-${version}"/> <property name="dist.src.result.dir" value="${dist.src.dir}/${name}-${version}"/> + <tstamp/> + <property name="nightly.result.dir" value="${nightly.dir}/${name}-${DSTAMP}"/> <property name="samedir" value="${basedir}"/> <property name="junit.reports.dir" value="${build.dir}/test-reports"/> <property name="junit.html.reports.dir" value="${build.dir}/test-reports/html"/> @@ -1045,7 +1048,6 @@ NOTE: <pathelement path="${src.sandbox.dir}"/> <pathelement path="${build.gensrc.dir}"/> </sourcepath> - <tag name="asf.todo" scope="all" description="To do:"/> <tag name="event.severity" scope="all" description="Event severity level:"/> <group title="Control and Startup"> <package name="org.apache.fop"/> @@ -1356,6 +1358,50 @@ NOTE: <move file="${build.dir}/${name}-${version}-bundle.jar" todir="${basedir}"/> </target> <!-- =================================================================== --> +<!-- Nightly builds --> +<!-- =================================================================== --> + <target name="junit-nightly-build" depends="junit-userconfig,junit-text-linebreak,junit-fotree"> + <fail> + <condition> + <or> + <isset property="fop.junit.error"/> + <isset property="fop.junit.failure"/> + </or> + </condition> +NOTE: +************************************************************************** +* One or more of the Junit tests had Failures or Errors or were skipped! * +* Please check the output above for relevant messages. * +* Or use the "junit-reports" target to generate HTML test reports. * +************************************************************************** + </fail> + <echo>All Junit tests passed!</echo> + </target> + <target name="nightly-build" depends="clean,jar-main,junit-nightly-build"> + <echo message="Building the binary distribution files (zip,tar)"/> + <delete dir="${nightly.dir}"/> + <mkdir dir="${nightly.result.dir}"/> + <copy todir="${nightly.result.dir}"> + <fileset refid="dist.bin"/> + <fileset refid="dist.bin.lib"/> + </copy> + <mkdir dir="${nightly.result.dir}/build"/> + <copy todir="${nightly.result.dir}/build" file="build/fop.jar"/> + <chmod file="${nightly.result.dir}/fop" perm="ugo+rx"/> + <zip zipfile="${name}-${DSTAMP}-bin.zip" basedir="${nightly.dir}" includes="**"/> + <tar longfile="gnu" destfile="${name}-${DSTAMP}-bin.tar"> + <tarfileset dir="${nightly.dir}" mode="755"> + <include name="${name}-${DSTAMP}/fop"/> + </tarfileset> + <tarfileset dir="${nightly.dir}"> + <include name="**"/> + <exclude name="${name}-${DSTAMP}/fop"/> + </tarfileset> + </tar> + <gzip zipfile="${name}-${DSTAMP}-bin.tar.gz" src="${name}-${DSTAMP}-bin.tar"/> + <delete file="${name}-${DSTAMP}-bin.tar"/> + </target> +<!-- =================================================================== --> <!-- Generate examples --> <!-- =================================================================== --> <target name="examples" depends="package" description="Generates the example files"> diff --git a/src/documentation/content/xdocs/dev/release.xml b/src/documentation/content/xdocs/dev/release.xml index 4e54df872..da614bee0 100644 --- a/src/documentation/content/xdocs/dev/release.xml +++ b/src/documentation/content/xdocs/dev/release.xml @@ -69,6 +69,7 @@ The purpose of documenting it here is to facilitate consistency, ensure that the <li>Update the tab names and directories in tabs.xml</li> <li>Delete the previous version directory.</li> <li>Update index.xml in the new version directory.</li> + <li>Update compiling.xml in the new version directory: change the intro for trunk to that for a release.</li> <li>Build the dist files (<code>build[.sh] dist</code>) and upload them to your web directory on <code>people.apache.org</code></li> diff --git a/src/documentation/content/xdocs/download.xml b/src/documentation/content/xdocs/download.xml index d8fbe9978..9d7988e61 100644 --- a/src/documentation/content/xdocs/download.xml +++ b/src/documentation/content/xdocs/download.xml @@ -51,8 +51,8 @@ <p> Binary distributions include "-bin" in their names, and can be downloaded from a <link href="http://www.apache.org/dyn/closer.cgi/xmlgraphics/fop">FOP Distribution mirror</link>. - Nightly builds of trunk source code can be downloaded here: - <link href="http://vc.apache.org/snapshots/fop/">Snapshot Trunk Builds</link> + Nightly builds of trunk code can be downloaded here: + <link href="http://ci.apache.org/projects/xmlgraphics/fop/snapshots/">Nightly Snapshots</link>. </p> </section> <section id="source"> @@ -67,13 +67,12 @@ <link href="http://www.apache.org/dyn/closer.cgi/xmlgraphics/fop">FOP Distribution mirror</link>. Source distributions include "-src" in their names. </li> - <!--li> - Download a CVS snapshot from the cvs files - <link href="http://xml.apache.org/from-cvs/xml-fop/">here</link>. - These snapshots are built approximately every six hours, and have the GMT of - their creation time embedded in their names. Please note that CVS snapshots - are made only for the "redesign" branch. - </li--> + <li> + Download a Subversion snapshot + <link href="http://vc.apache.org/snapshots/fop/">here</link>. + These snapshots are checked out from Subversion approximately every six hours, and have the GMT of + their creation time embedded in their names. + </li> <li> Download directly from the SVN repository. Anyone can do this using the diff --git a/src/documentation/content/xdocs/trunk/compiling.xml b/src/documentation/content/xdocs/trunk/compiling.xml index 98a6dfb6d..0a8bf3486 100644 --- a/src/documentation/content/xdocs/trunk/compiling.xml +++ b/src/documentation/content/xdocs/trunk/compiling.xml @@ -25,16 +25,23 @@ <body> <section id="build-needed"> <title>Do You Need To Build?</title> - <p> +<!-- text for released versions --> +<!-- <p> FOP distributions are either pre-compiled binary or source. If you are using a binary distribution, it is already built and there is no need to build it again. See the <a href="../download.html">Download Instructions</a> for information about whether a binary or source distribution is best for your needs. - </p> + </p>--> +<!-- /text for released versions --> +<!-- text for trunk --> <p> - If you got the source code from a repository snapshot or via Subversion you will need to build FOP - in any case. + FOP snapshots are either pre-compiled binary or source. + If you are using a binary snapshot, it is already built and there is no need to build it again. + If you got the source code from a repository snapshot or via Subversion you will need to build FOP. + See the <a href="../download.html">Download Instructions</a> for information about where to obtain binary or repository snapshots, and whether a + binary or source snapshot is best for your needs. </p> +<!-- /text for trunk --> </section> <section id="env"> <title>Set Up Your Environment</title> diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml index aae7de7f0..3ef8795b7 100644 --- a/src/documentation/content/xdocs/trunk/configuration.xml +++ b/src/documentation/content/xdocs/trunk/configuration.xml @@ -89,6 +89,12 @@ </td> <td>disabled</td> </tr> + <tr> + <td>hyphenation-pattern</td> + <td>String, attribute lang, attribute country (optional)</td> + <td>Register a file name for the hyphenation pattern for the mentioned language and country. Language ll and country CC must both consist of two letters.</td> + <td>ll_CC</td> + </tr> <tr> <td>source-resolution</td> <td>Integer, dpi</td> @@ -215,6 +221,9 @@ <!-- default page-height and page-width, in case value is specified as auto --> <default-page-settings height="11in" width="8.26in"/> + + <!-- Use file name nl_Bel instead of the default nl_BE --> + <hyphenation-pattern lang="nl" country="BE">nl_Bel</hyphenation-pattern> <!-- etc. etc..... --> </fop>]]></source> diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index 1766cd493..226a2d96c 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -885,6 +885,13 @@ Note that the value of the encoding attribute in the example is the double-byte segment in the generated file. Please also note that page segments cannot be scaled. They are always rendered in their intrinsic size. </p> + <p> + The include-page-segment extension element has the optional attribute + <i>resource-file</i>. The value of this is a URI to a resource containing a page + segment with the declared name. In this case FOP embeds the page segment into the + generated document so that the external resource does not have to be supplied in the + print job. + </p> </section> <section id="afp-tag-logical-element"> <title>Tag Logical Element (TLE) Extension</title> diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index b7e1abc01..ae759e14a 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -19,12 +19,15 @@ package org.apache.fop.afp; +import java.io.BufferedInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -38,6 +41,7 @@ import org.apache.fop.afp.modca.PageSegment; import org.apache.fop.afp.modca.Registry; import org.apache.fop.afp.modca.ResourceGroup; import org.apache.fop.afp.modca.ResourceObject; +import org.apache.fop.afp.util.AFPResourceUtil; import org.apache.fop.afp.util.ResourceAccessor; /** @@ -314,6 +318,57 @@ public class AFPResourceManager { } /** + * Creates an included resource extracting the named resource from an external source. + * @param resourceName the name of the resource + * @param uri the URI for the resource + * @param accessor resource accessor to access the resource with + * @throws IOException if an I/O error occurs while loading the resource + */ + public void createIncludedResourceFromExternal(final String resourceName, + final URI uri, final ResourceAccessor accessor) throws IOException { + + AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE); + + AFPResourceInfo resourceInfo = new AFPResourceInfo(); + resourceInfo.setLevel(resourceLevel); + resourceInfo.setName(resourceName); + resourceInfo.setUri(uri.toASCIIString()); + + String resource = (String)includeNameMap.get(resourceInfo); + if (resource == null) { + + ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel); + + //resourceObject delegates write commands to copyNamedResource() + //The included resource may already be wrapped in a resource object + AbstractNamedAFPObject resourceObject = new AbstractNamedAFPObject(null) { + + protected void writeContent(OutputStream os) throws IOException { + InputStream inputStream = null; + try { + inputStream = accessor.createInputStream(uri); + BufferedInputStream bin = new BufferedInputStream(inputStream); + AFPResourceUtil.copyNamedResource(resourceName, bin, os); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + //bypass super.writeStart + protected void writeStart(OutputStream os) throws IOException { } + //bypass super.writeEnd + protected void writeEnd(OutputStream os) throws IOException { } + }; + + resourceGroup.addObject(resourceObject); + + includeNameMap.put(resourceInfo, resourceName); + + } + } + + + /** * Sets resource level defaults. The existing defaults over merged with the ones passed in * as parameter. * @param defaults the new defaults diff --git a/src/java/org/apache/fop/afp/AFPStreamer.java b/src/java/org/apache/fop/afp/AFPStreamer.java index 33d1dbf90..154ca4cc9 100644 --- a/src/java/org/apache/fop/afp/AFPStreamer.java +++ b/src/java/org/apache/fop/afp/AFPStreamer.java @@ -158,7 +158,7 @@ public class AFPStreamer implements Streamable { */ // write out any external resource groups public void close() throws IOException { - Iterator it = pathResourceGroupMap.entrySet().iterator(); + Iterator it = pathResourceGroupMap.values().iterator(); while (it.hasNext()) { StreamedResourceGroup resourceGroup = (StreamedResourceGroup)it.next(); resourceGroup.close(); diff --git a/src/java/org/apache/fop/afp/DataStream.java b/src/java/org/apache/fop/afp/DataStream.java index 843e3fa65..a437c3004 100644 --- a/src/java/org/apache/fop/afp/DataStream.java +++ b/src/java/org/apache/fop/afp/DataStream.java @@ -359,9 +359,9 @@ public class DataStream { * @param charSet is the AFP Character Set to use with the text * @throws UnsupportedEncodingException thrown if character encoding is not supported */ - public void createText - ( final AFPTextDataInfo textDataInfo, final int letterSpacing, final int wordSpacing, - final Font font, final CharacterSet charSet) throws UnsupportedEncodingException { + public void createText(final AFPTextDataInfo textDataInfo, final int letterSpacing, + final int wordSpacing, final Font font, final CharacterSet charSet) + throws UnsupportedEncodingException { int rotation = paintingState.getRotation(); if (rotation != 0) { textDataInfo.setRotation(rotation); diff --git a/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java b/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java new file mode 100644 index 000000000..0adffcd8b --- /dev/null +++ b/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java @@ -0,0 +1,157 @@ +/* + * 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.apps; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import org.apache.commons.io.HexDump; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; + +import org.apache.fop.afp.parser.MODCAParser; +import org.apache.fop.afp.parser.UnparsedStructuredField; + +/** + * This class represents a tool for extracting the Type 1 PFB file from an AFP outline font. + */ +public class FontPatternExtractor { + + private PrintStream printStream = System.out; + + /** + * Extracts the Type1 PFB file from the given AFP outline font. + * @param file the AFP file to read from + * @param targetDir the target directory where the PFB file is to be placed. + * @throws IOException if an I/O error occurs + */ + public void extract(File file, File targetDir) throws IOException { + InputStream in = new java.io.FileInputStream(file); + try { + MODCAParser parser = new MODCAParser(in); + ByteArrayOutputStream baout = new ByteArrayOutputStream(); + UnparsedStructuredField strucField; + while ((strucField = parser.readNextStructuredField()) != null) { + if (strucField.getSfTypeID() == 0xD3EE89) { + println(strucField.toString()); + HexDump.dump(strucField.getData(), 0, printStream, 0); + baout.write(strucField.getData()); + } + } + + ByteArrayInputStream bin = new ByteArrayInputStream(baout.toByteArray()); + DataInputStream din = new DataInputStream(bin); + long len = din.readInt() & 0xFFFFFFFFL; + println("Length: " + len); + din.skip(4); //checksum + int tidLen = din.readUnsignedShort() - 2; + byte[] tid = new byte[tidLen]; + din.readFully(tid); + String filename = new String(tid, "ISO-8859-1"); + int asciiCount1 = countUSAsciiCharacters(filename); + String filenameEBCDIC = new String(tid, "Cp1146"); + int asciiCount2 = countUSAsciiCharacters(filenameEBCDIC); + println("TID: " + filename + " " + filenameEBCDIC); + + if (asciiCount2 > asciiCount1) { + //Haven't found an indicator if the name is encoded in EBCDIC or not + //so we use a trick. + filename = filenameEBCDIC; + } + if (!filename.toLowerCase().endsWith(".pfb")) { + filename = filename + ".pfb"; + } + println("Output filename: " + filename); + File out = new File(targetDir, filename); + + OutputStream fout = new java.io.FileOutputStream(out); + try { + IOUtils.copyLarge(din, fout); + } finally { + IOUtils.closeQuietly(fout); + } + + + } finally { + IOUtils.closeQuietly(in); + } + } + + private void println(String s) { + printStream.println(s); + } + + private void println() { + printStream.println(); + } + + private int countUSAsciiCharacters(String filename) { + int count = 0; + for (int i = 0, c = filename.length(); i < c; i++) { + if (filename.charAt(i) < 128) { + count++; + } + } + return count; + } + + /** + * Main method + * @param args the command-line arguments + */ + public static void main(String[] args) { + try { + FontPatternExtractor app = new FontPatternExtractor(); + + app.println("Font Pattern Extractor"); + app.println(); + + if (args.length > 0) { + String filename = args[0]; + File file = new File(filename); + + File targetDir = file.getParentFile(); + if (args.length > 1) { + targetDir = new File(args[1]); + targetDir.mkdirs(); + } + + app.extract(file, targetDir); + } else { + app.println("This tool tries to extract the PFB file from an AFP outline font."); + app.println(); + app.println("Usage: Java -cp ... " + FontPatternExtractor.class.getName() + + " <afp-font-file> [<target-dir>]"); + System.exit(-1); + } + + + } catch (Exception e) { + e.printStackTrace(); + System.exit(-1); + } + } + +} diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java b/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java index 66b3f5564..b7db6a74e 100644 --- a/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java +++ b/src/java/org/apache/fop/afp/fonts/AFPFontCollection.java @@ -61,9 +61,9 @@ public class AFPFontCollection implements FontCollection { List/*<FontTriplet>*/ tripletList = afpFontInfo.getFontTriplets(); for (Iterator it2 = tripletList.iterator(); it2.hasNext();) { FontTriplet triplet = (FontTriplet)it2.next(); + fontInfo.addMetrics("F" + num, afpFont); fontInfo.addFontProperties("F" + num, triplet.getName(), triplet.getStyle(), triplet.getWeight()); - fontInfo.addMetrics("F" + num, afpFont); num++; } } diff --git a/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java b/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java index 296ab2d9d..dc841bd40 100644 --- a/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java +++ b/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java @@ -26,6 +26,7 @@ import java.net.URI; import org.apache.commons.io.IOUtils; +import org.apache.fop.afp.util.AFPResourceUtil; import org.apache.fop.afp.util.ResourceAccessor; @@ -54,7 +55,7 @@ public class IncludedResourceObject extends AbstractNamedAFPObject { public void writeToStream(OutputStream os) throws IOException { InputStream in = resourceAccessor.createInputStream(this.uri); try { - IOUtils.copy(in, os); + AFPResourceUtil.copyResourceFile(in, os); } finally { IOUtils.closeQuietly(in); } diff --git a/src/java/org/apache/fop/afp/modca/ObjectContainer.java b/src/java/org/apache/fop/afp/modca/ObjectContainer.java index 515425906..9bc8dc594 100644 --- a/src/java/org/apache/fop/afp/modca/ObjectContainer.java +++ b/src/java/org/apache/fop/afp/modca/ObjectContainer.java @@ -113,7 +113,7 @@ public class ObjectContainer extends AbstractDataObject { } /** - * Sets the data for the the object container + * Sets the data for the object container * * @param data a byte array */ diff --git a/src/java/org/apache/fop/afp/parser/MODCAParser.java b/src/java/org/apache/fop/afp/parser/MODCAParser.java new file mode 100644 index 000000000..356d4f169 --- /dev/null +++ b/src/java/org/apache/fop/afp/parser/MODCAParser.java @@ -0,0 +1,80 @@ +/* + * 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.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * An simple MO:DCA/AFP parser. + */ +public class MODCAParser { + + /** The carriage control character (0x5A) used to indicate the start of a structured field. */ + public static final byte CARRIAGE_CONTROL_CHAR = (byte)(0x5A & 0xFF); + + private DataInputStream din; + + /** + * Main constructor + * @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. + * <p> + * No structure validation of the MO:DCA file is performed. + * @return a new unparsed structured field (or null when parsing is finished). + * @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 + } + } + } catch (EOFException eof) { + return null; + } + return UnparsedStructuredField.readStructuredField(getDataInputStream()); + } + +} diff --git a/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java b/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java new file mode 100644 index 000000000..f775c05ee --- /dev/null +++ b/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java @@ -0,0 +1,355 @@ +/* + * 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.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); + } +} diff --git a/src/java/org/apache/fop/afp/util/AFPResourceUtil.java b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java new file mode 100644 index 000000000..ebb318046 --- /dev/null +++ b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java @@ -0,0 +1,214 @@ +/* + * 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.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Collection; + +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.parser.MODCAParser; +import org.apache.fop.afp.parser.UnparsedStructuredField; + +/** + * TODO better docs + * Utility for AFP resource handling + * + * + * A utility class to read structured fields from a MO:DCA document. Each + * component of a mixed object document is explicitly defined and delimited + * in the data. This is accomplished through the use of MO:DCA data structures, + * called structured fields. Structured fields are used to envelop document + * components and to provide commands and information to applications using + * the data. Structured fields may contain one or more parameters. Each + * parameter provides one value from a set of values defined by the architecture. + * <p/> + * MO:DCA structured fields consist of two parts: an introducer that identifies + * the length and type of the structured field, and data that provides the + * structured field's effect. The data is contained in a set of parameters, + * which can consist of other data structures and data elements. The maximum + * length of a structured field is 32767 bytes. + * <p/> + */ +public final class AFPResourceUtil { + + private static final byte TYPE_CODE_BEGIN = (byte)(0xA8 & 0xFF); + private static final byte TYPE_CODE_END = (byte)(0xA9 & 0xFF); + + private static final Log LOG = LogFactory.getLog(AFPResourceUtil.class); + + private AFPResourceUtil() { + //nop + } + + /** + * Get the next structured field as identified by the identifier + * parameter (this must be a valid MO:DCA structured field). + * @param identifier the three byte identifier + * @param inputStream the inputStream + * @throws IOException if an I/O exception occurred + * @return the next structured field or null when there are no more + */ + public static byte[] getNext(byte[] identifier, InputStream inputStream) throws IOException { + MODCAParser parser = new MODCAParser(inputStream); + while (true) { + UnparsedStructuredField field = parser.readNextStructuredField(); + if (field == null) { + return null; + } + if (field.getSfClassCode() == identifier[0] + && field.getSfTypeCode() == identifier[1] + && field.getSfCategoryCode() == identifier[2]) { + return field.getCompleteFieldAsBytes(); + } + } + } + + private static String getResourceName(UnparsedStructuredField field) + throws UnsupportedEncodingException { + //The first 8 bytes of the field data represent the resource name + byte[] nameBytes = new byte[8]; + System.arraycopy(field.getData(), 0, nameBytes, 0, 8); + String asciiName; + asciiName = new String(nameBytes, AFPConstants.EBCIDIC_ENCODING); + return asciiName; + } + + /** + * Copy a complete resource file to a given {@link OutputStream}. + * @param in external resource input + * @param out output destination + * @throws IOException if an I/O error occurs + */ + public static void copyResourceFile(final InputStream in, OutputStream out) + throws IOException { + MODCAParser parser = new MODCAParser(in); + while (true) { + UnparsedStructuredField field = parser.readNextStructuredField(); + if (field == null) { + break; + } + out.write(MODCAParser.CARRIAGE_CONTROL_CHAR); + field.writeTo(out); + } + } + + /** + * Copy a named resource to a given {@link OutputStream}. The MO:DCA fields read from the + * {@link InputStream} are scanned for the resource with the given name. + * @param name name of structured field + * @param in external resource input + * @param out output destination + * @throws IOException if an I/O error occurs + */ + public static void copyNamedResource(String name, + final InputStream in, final OutputStream out) throws IOException { + final MODCAParser parser = new MODCAParser(in); + Collection resourceNames = new java.util.HashSet(); + + //Find matching "Begin" field + final UnparsedStructuredField fieldBegin; + while (true) { + UnparsedStructuredField field = parser.readNextStructuredField(); + if (field == null) { + throw new IOException("Requested resource '" + name + + "' not found. Encountered resource names: " + resourceNames); + } + + if (field.getSfTypeCode() != TYPE_CODE_BEGIN) { //0xA8=Begin + continue; //Not a "Begin" field + } + String resourceName = getResourceName(field); + resourceNames.add(resourceName); + if (resourceName.equals(name)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Start of requested structured field found:\n" + + field); + } + fieldBegin = field; + break; //Name doesn't match + } + } + + //Decide whether the resource file has to be wrapped in a resource object + boolean wrapInResource; + if (fieldBegin.getSfCategoryCode() == Category.PAGE_SEGMENT) { + //A naked page segment must be wrapped in a resource object + wrapInResource = true; + } else if (fieldBegin.getSfCategoryCode() == Category.NAME_RESOURCE) { + //A resource object can be copied directly + wrapInResource = false; + } else { + throw new IOException("Cannot handle resource: " + fieldBegin); + } + + //Copy structured fields (wrapped or as is) + if (wrapInResource) { + ResourceObject resourceObject = new ResourceObject(name) { + protected void writeContent(OutputStream os) throws IOException { + copyStructuredFields(name, fieldBegin, parser, out); + } + }; + resourceObject.setType(ResourceObject.TYPE_PAGE_SEGMENT); + resourceObject.writeToStream(out); + } else { + copyStructuredFields(name, fieldBegin, parser, out); + } + } + + private static void copyStructuredFields(String name, UnparsedStructuredField fieldBegin, + MODCAParser parser, OutputStream out) throws IOException { + boolean inRequestedResource; + + //The "Begin" field first + out.write(MODCAParser.CARRIAGE_CONTROL_CHAR); + fieldBegin.writeTo(out); + UnparsedStructuredField field; + + //Then the rest of the fields until the corresponding "End" field + inRequestedResource = true; + do { + field = parser.readNextStructuredField(); + if (field == null) { + break; //Unexpected EOF + } + + if (field.getSfTypeCode() == TYPE_CODE_END) { + String resourceName = getResourceName(field); + if (resourceName.equals(name)) { + inRequestedResource = false; //Signal end of loop + } + } + out.write(MODCAParser.CARRIAGE_CONTROL_CHAR); + field.writeTo(out); + } while (inRequestedResource); + if (inRequestedResource) { + throw new IOException("Ending structured field not found for resource " + name); + } + } + +} diff --git a/src/java/org/apache/fop/afp/util/StructuredFieldReader.java b/src/java/org/apache/fop/afp/util/StructuredFieldReader.java index 34add3bbe..1fc6d8369 100644 --- a/src/java/org/apache/fop/afp/util/StructuredFieldReader.java +++ b/src/java/org/apache/fop/afp/util/StructuredFieldReader.java @@ -54,80 +54,27 @@ public class StructuredFieldReader { } /** - * Get the next structured field as identified by the identifer - * parameter (this must be a valid MO:DCA structured field. + * Get the next structured field as identified by the identifier + * parameter (this must be a valid MO:DCA structured field). + * Note: The returned data does not include the field length and identifier! * @param identifier the three byte identifier * @throws IOException if an I/O exception occurred * @return the next structured field or null when there are no more */ public byte[] getNext(byte[] identifier) throws IOException { - int bufferPointer = 0; - byte[] bufferData = new byte[identifier.length + 2]; - for (int x = 0; x < identifier.length; x++) { - bufferData[x] = 0x00; - } - - int c; - while ((c = inputStream.read()) > -1) { - - bufferData[bufferPointer] = (byte) c; - - // Check the last characters in the buffer - int index = 0; - boolean found = true; - - for (int i = identifier.length - 1; i > -1; i--) { - - int p = bufferPointer - index; - if (p < 0) { - p = bufferData.length + p; - } - - index++; - - if (identifier[i] != bufferData[p]) { - found = false; - break; - } - - } - - if (found) { - - byte[] length = new byte[2]; - - int a = bufferPointer - identifier.length; - if (a < 0) { - a = bufferData.length + a; - } - - int b = bufferPointer - identifier.length - 1; - if (b < 0) { - b = bufferData.length + b; - } - - length[0] = bufferData[b]; - length[1] = bufferData[a]; - - int reclength = ((length[0] & 0xFF) << 8) - + (length[1] & 0xFF) - identifier.length - 2; - - byte[] retval = new byte[reclength]; - - inputStream.read(retval, 0, reclength); - - return retval; - - } - - bufferPointer++; - if (bufferPointer >= bufferData.length) { - bufferPointer = 0; - } + byte[] bytes = AFPResourceUtil.getNext(identifier, this.inputStream); + if (bytes != null) { + //Users of this class expect the field data without length and identifier + int srcPos = 2 + identifier.length; + byte[] tmp = new byte[bytes.length - srcPos]; + System.arraycopy(bytes, srcPos, tmp, 0, tmp.length); + bytes = tmp; } - return null; + return bytes; + } + } diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index 907895c99..7bcdd2703 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -26,6 +26,8 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import javax.xml.transform.Source; @@ -109,6 +111,11 @@ public class FopFactory implements ImageContext { private String hyphenBase = null; /** + * Map of configured names of hyphenation pattern file names: ll_CC => name + */ + private Map/*<String,String>*/ hyphPatNames = null; + + /** * FOP has the ability, for some FO's, to continue processing even if the * input XSL violates that FO's content model. This is the default * behavior for FOP. However, this flag, if set, provides the user the @@ -407,6 +414,23 @@ public class FopFactory implements ImageContext { } /** + * @return the hyphPatNames + */ + public Map getHyphPatNames() { + return hyphPatNames; + } + + /** + * @param hyphPatNames the hyphPatNames to set + */ + public void setHyphPatNames(Map hyphPatNames) { + if (hyphPatNames == null) { + hyphPatNames = new HashMap(); + } + this.hyphPatNames = hyphPatNames; + } + + /** * Sets the URI Resolver. It is used for resolving factory-level URIs like hyphenation * patterns and as backup for URI resolution performed during a rendering run. * @param uriResolver the new URI resolver diff --git a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java index e46530177..ad0869ea1 100644 --- a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java +++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java @@ -22,6 +22,8 @@ package org.apache.fop.apps; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; import org.xml.sax.SAXException; @@ -35,6 +37,7 @@ import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry; import org.apache.xmlgraphics.image.loader.util.Penalty; import org.apache.fop.fonts.FontManagerConfigurator; +import org.apache.fop.hyphenation.HyphenationTreeCache; import org.apache.fop.util.LogUtil; /** @@ -89,20 +92,7 @@ public class FopFactoryConfigurator { * @param factory fop factory * @throws FOPException fop exception */ - public void configure(FopFactory factory) throws FOPException { - if (log.isDebugEnabled()) { - log.debug("Initializing FopFactory Configuration"); - } - - if (cfg.getChild("accessibility", false) != null) { - try { - this.factory.setAccessibility( - cfg.getChild("accessibility").getValueAsBoolean()); - } catch (ConfigurationException e) { - throw new FOPException(e); - } - } - + public void configure(FopFactory factory) throws FOPException { // CSOK: MethodLength // strict configuration if (cfg.getChild("strict-configuration", false) != null) { try { @@ -113,6 +103,19 @@ public class FopFactoryConfigurator { } } boolean strict = factory.validateUserConfigStrictly(); + if (log.isDebugEnabled()) { + log.debug("Initializing FopFactory Configuration" + + "with " + (strict ? "strict" : "permissive") + " validation"); + } + + if (cfg.getChild("accessibility", false) != null) { + try { + this.factory.setAccessibility( + cfg.getChild("accessibility").getValueAsBoolean()); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + } + } // strict fo validation if (cfg.getChild("strict-validation", false) != null) { @@ -142,6 +145,64 @@ public class FopFactoryConfigurator { } } + /** + * Read configuration elements hyphenation-pattern, + * construct a map ll_CC => filename, and set it on the factory + */ + Configuration[] hyphPatConfig = cfg.getChildren("hyphenation-pattern"); + if (hyphPatConfig.length != 0) { + Map/*<String,String>*/ hyphPatNames = new HashMap/*<String,String>*/(); + for (int i = 0; i < hyphPatConfig.length; ++i) { + String lang, country, filename; + StringBuffer error = new StringBuffer(); + String location = hyphPatConfig[i].getLocation(); + + lang = hyphPatConfig[i].getAttribute("lang", null); + if (lang == null) { + addError("The lang attribute of a hyphenation-pattern configuration" + + " element must exist (" + location + ")", error); + } else if (!lang.matches("[a-zA-Z]{2}")) { + addError("The lang attribute of a hyphenation-pattern configuration" + + " element must consist of exactly two letters (" + + location + ")", error); + } + lang = lang.toLowerCase(); + + country = hyphPatConfig[i].getAttribute("country", null); + if ("".equals(country)) { + country = null; + } + if (country != null) { + if (!country.matches("[a-zA-Z]{2}")) { + addError("The country attribute of a hyphenation-pattern configuration" + + " element must consist of exactly two letters (" + + location + ")", error); + } + country = country.toUpperCase(); + } + + filename = hyphPatConfig[i].getValue(null); + if (filename == null) { + addError("The value of a hyphenation-pattern configuration" + + " element may not be empty (" + location + ")", error); + } + + if (error.length() != 0) { + LogUtil.handleError(log, error.toString(), strict); + continue; + } + + String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); + hyphPatNames.put(llccKey, filename); + if (log.isDebugEnabled()) { + log.debug("Using hyphenation pattern filename " + filename + + " for lang=\"" + lang + "\"" + + (country != null ? ", country=\"" + country + "\"" : "")); + } + } + factory.setHyphPatNames(hyphPatNames); + } + // renderer options if (cfg.getChild("source-resolution", false) != null) { factory.setSourceResolution( @@ -203,6 +264,13 @@ public class FopFactoryConfigurator { configureImageLoading(cfg.getChild("image-loading", false), strict); } + private static void addError(String message, StringBuffer error) { + if (error.length() != 0) { + error.append(". "); + } + error.append(message); + } + private void configureImageLoading(Configuration parent, boolean strict) throws FOPException { if (parent == null) { return; diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java index 63740386e..bccae8754 100644 --- a/src/java/org/apache/fop/area/PageViewport.java +++ b/src/java/org/apache/fop/area/PageViewport.java @@ -95,8 +95,10 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl * @param pageNumber the page number * @param pageStr String representation of the page number * @param blank true if this is a blank page + * @param spanAll true if the first span area spans all columns */ - public PageViewport(SimplePageMaster spm, int pageNumber, String pageStr, boolean blank) { + public PageViewport(SimplePageMaster spm, int pageNumber, String pageStr, + boolean blank, boolean spanAll) { this.simplePageMasterName = spm.getMasterName(); setExtensionAttachments(spm.getExtensionAttachments()); setForeignAttributes(spm.getForeignAttributes()); @@ -107,7 +109,18 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl this.pageNumberString = pageStr; this.viewArea = new Rectangle(0, 0, pageWidth, pageHeight); this.page = new Page(spm); - createSpan(false); + createSpan(spanAll); + } + + /** + * Create a page viewport. + * @param spm SimplePageMaster indicating the page and region dimensions + * @param pageNumber the page number + * @param pageStr String representation of the page number + * @param blank true if this is a blank page + */ + public PageViewport(SimplePageMaster spm, int pageNumber, String pageStr, boolean blank) { + this(spm, pageNumber, pageStr, blank, false); } /** diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 6b4ec1a1d..2a5c0d272 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -1231,7 +1231,7 @@ public class CommandLineOptions { + " -out mime outfile input will be rendered using the given MIME type\n" + " (outfile req'd) Example: \"-out application/pdf D:\\out.pdf\"\n" + " (Tip: \"-out list\" prints the list of supported MIME types" - + " and exits)\n" + + " and exits)\n" //+ " -mif outfile input will be rendered as MIF (FrameMaker) (outfile req'd)\n" //+ " Experimental feature - requires additional fop-sandbox.jar.\n" + " -svg outfile input will be rendered as an SVG slides file (outfile req'd) \n" diff --git a/src/java/org/apache/fop/datatypes/Numeric.java b/src/java/org/apache/fop/datatypes/Numeric.java index c26cacd7c..2004c6721 100644 --- a/src/java/org/apache/fop/datatypes/Numeric.java +++ b/src/java/org/apache/fop/datatypes/Numeric.java @@ -29,15 +29,15 @@ import org.apache.fop.fo.expr.PropertyException; * Numerics can be either absolute or relative. Relative numerics * must be resolved against base value before the value can be used. * <p> - * To support relative numerics internally in the expresion parser and - * during evaulation one additional methods exists: isAbsolute() which + * To support relative numerics internally in the expression parser and + * during evaluation one additional methods exists: isAbsolute() which * return true for absolute numerics and false for relative numerics. */ public interface Numeric { /** * Return the value of this Numeric * @return the computed value. - * @throws PropertyException if a propert exception occurs + * @throws PropertyException if a property exception occurs */ double getNumericValue() throws PropertyException; @@ -45,7 +45,7 @@ public interface Numeric { * Return the value of this Numeric * @param context The context for the length calculation (for percentage based lengths) * @return the computed value. - * @throws PropertyException if a propert exception occurs + * @throws PropertyException if a property exception occurs */ double getNumericValue(PercentBaseContext context) throws PropertyException; @@ -78,9 +78,9 @@ public interface Numeric { int getValue(PercentBaseContext context); /** - * Return the resolved value. This method will becalled during evaluation + * Return the resolved value. This method will be called during evaluation * of the expression tree and relative numerics can then return a - * resolved absolute Numeric. Absolute numerics can just return themself. + * resolved absolute Numeric. Absolute numerics can just return themselves. * * @return A resolved value. * @throws PropertyException diff --git a/src/java/org/apache/fop/fo/expr/NumericOp.java b/src/java/org/apache/fop/fo/expr/NumericOp.java index d1f91d509..a2b203330 100644 --- a/src/java/org/apache/fop/fo/expr/NumericOp.java +++ b/src/java/org/apache/fop/fo/expr/NumericOp.java @@ -19,8 +19,8 @@ package org.apache.fop.fo.expr; -import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.datatypes.Numeric; +import org.apache.fop.datatypes.PercentBaseContext; /** * This class contains static methods to evaluate operations on Numeric @@ -43,8 +43,7 @@ public final class NumericOp { * @throws PropertyException If the dimension of the operand is different * from the dimension of this Numeric. */ - public static Numeric addition(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric addition(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return addition2(op1, op2, null); } else { @@ -80,8 +79,7 @@ public final class NumericOp { * @throws PropertyException If the dimension of the operand is different * from the dimension of this Numeric. */ - public static Numeric subtraction(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric subtraction(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return subtraction2(op1, op2, null); } else { @@ -100,7 +98,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric subtraction2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { if (op1.getDimension() != op2.getDimension()) { throw new PropertyException("Can't subtract Numerics of different dimensions"); } @@ -117,8 +115,7 @@ public final class NumericOp { * @throws PropertyException If the dimension of the operand is different * from the dimension of this Numeric. */ - public static Numeric multiply(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric multiply(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return multiply2(op1, op2, null); } else { @@ -137,7 +134,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric multiply2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { return numeric(op1.getNumericValue(context) * op2.getNumericValue(context), op1.getDimension() + op2.getDimension()); } @@ -171,7 +168,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric divide2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { return numeric(op1.getNumericValue(context) / op2.getNumericValue(context), op1.getDimension() - op2.getDimension()); } @@ -183,8 +180,7 @@ public final class NumericOp { * @return A new Numeric object representing the absolute value. * @throws PropertyException if a property exception occurs */ - public static Numeric modulo(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric modulo(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return modulo2(op1, op2, null); } else { @@ -202,7 +198,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric modulo2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { return numeric(op1.getNumericValue(context) % op2.getNumericValue(context), op1.getDimension()); } @@ -213,8 +209,7 @@ public final class NumericOp { * @return a new Numeric object representing the absolute value of the operand. * @throws PropertyException if a property exception occurs */ - public static Numeric abs(Numeric op) - throws PropertyException { + public static Numeric abs(Numeric op) throws PropertyException { if (op.isAbsolute()) { return abs2(op, null); } else { @@ -230,8 +225,7 @@ public final class NumericOp { * @throws PropertyException If the dimension of the operand is different * from the dimension of this Numeric. */ - public static Numeric abs2(Numeric op, PercentBaseContext context) - throws PropertyException { + public static Numeric abs2(Numeric op, PercentBaseContext context) throws PropertyException { return numeric(Math.abs(op.getNumericValue(context)), op.getDimension()); } @@ -241,8 +235,7 @@ public final class NumericOp { * @return a new Numeric object representing the negation of the operand. * @throws PropertyException if a property exception occurs */ - public static Numeric negate(Numeric op) - throws PropertyException { + public static Numeric negate(Numeric op) throws PropertyException { if (op.isAbsolute()) { return negate2(op, null); } else { @@ -259,8 +252,7 @@ public final class NumericOp { * @throws PropertyException If the dimension of the operand is different * from the dimension of this Numeric. */ - public static Numeric negate2(Numeric op, PercentBaseContext context) - throws PropertyException { + public static Numeric negate2(Numeric op, PercentBaseContext context) throws PropertyException { return numeric(-op.getNumericValue(context), op.getDimension()); } @@ -271,8 +263,7 @@ public final class NumericOp { * @return a Numeric which is the maximum of the two operands. * @throws PropertyException if the dimensions or value types of the operands are different. */ - public static Numeric max(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric max(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return max2(op1, op2, null); } else { @@ -290,7 +281,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric max2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { if (op1.getDimension() != op2.getDimension()) { throw new PropertyException("Arguments to max() must have same dimensions"); } @@ -304,8 +295,7 @@ public final class NumericOp { * @return a Numeric which is the minimum of the two operands. * @throws PropertyException if the dimensions or value types of the operands are different. */ - public static Numeric min(Numeric op1, Numeric op2) - throws PropertyException { + public static Numeric min(Numeric op1, Numeric op2) throws PropertyException { if (op1.isAbsolute() && op2.isAbsolute()) { return min2(op1, op2, null); } else { @@ -323,7 +313,7 @@ public final class NumericOp { * from the dimension of this Numeric. */ public static Numeric min2(Numeric op1, Numeric op2, PercentBaseContext context) - throws PropertyException { + throws PropertyException { if (op1.getDimension() != op2.getDimension()) { throw new PropertyException("Arguments to min() must have same dimensions"); } diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java index 6ba763933..fce82dcff 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java @@ -217,11 +217,10 @@ public abstract class TableFObj extends FObj implements StructurePointerProperty */ i = 1; } - TableEventProducer eventProducer - = TableEventProducer.Provider.get(fo.getUserAgent().getEventBroadcaster()); - eventProducer.forceNextColumnNumber - (this, propertyList.getFObj().getName(), - val, i, propertyList.getFObj().getLocator()); + TableEventProducer eventProducer = TableEventProducer.Provider.get( + fo.getUserAgent().getEventBroadcaster()); + eventProducer.forceNextColumnNumber(this, propertyList.getFObj().getName(), + val, i, propertyList.getFObj().getLocator()); } return NumberProperty.getInstance(i); } @@ -231,9 +230,8 @@ public abstract class TableFObj extends FObj implements StructurePointerProperty } /** {@inheritDoc} */ - public void processNode - (String elementName, Locator locator, Attributes attlist, PropertyList pList) - throws FOPException { + public void processNode(String elementName, Locator locator, Attributes attlist, + PropertyList pList) throws FOPException { super.processNode(elementName, locator, attlist, pList); Table table = getTable(); if (!inMarker() && !table.isSeparateBorderModel()) { diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java index 2a634c24a..8f8e0a509 100644 --- a/src/java/org/apache/fop/fo/pagination/Root.java +++ b/src/java/org/apache/fop/fo/pagination/Root.java @@ -212,7 +212,7 @@ public class Root extends FObj { * @throws IllegalArgumentException for negative additional page counts */ public void notifyPageSequenceFinished(int lastPageNumber, int additionalPages) - throws IllegalArgumentException { + throws IllegalArgumentException { if (additionalPages >= 0) { totalPagesGenerated += additionalPages; diff --git a/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java b/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java index 58f1ac210..3c0118181 100644 --- a/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java +++ b/src/java/org/apache/fop/fo/properties/BackgroundPositionShorthand.java @@ -58,7 +58,7 @@ public class BackgroundPositionShorthand extends ListProperty { * to "50%". */ public Property make(PropertyList propertyList, String value, FObj fo) - throws PropertyException { + throws PropertyException { Property p = super.make(propertyList, value, fo); if (p.getList().size() == 1) { /* only background-position-horizontal specified diff --git a/src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java b/src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java index 467682878..d2bab22ab 100644 --- a/src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java @@ -54,9 +54,8 @@ public class BorderWidthPropertyMaker extends LengthProperty.Maker { * {@inheritDoc} */ - public Property get(int subpropId, PropertyList propertyList, - boolean bTryInherit, boolean bTryDefault) - throws PropertyException { + public Property get(int subpropId, PropertyList propertyList, boolean bTryInherit, + boolean bTryDefault) throws PropertyException { Property p = super.get(subpropId, propertyList, bTryInherit, bTryDefault); diff --git a/src/java/org/apache/fop/fo/properties/ColorProperty.java b/src/java/org/apache/fop/fo/properties/ColorProperty.java index e7b8d5931..293f31577 100644 --- a/src/java/org/apache/fop/fo/properties/ColorProperty.java +++ b/src/java/org/apache/fop/fo/properties/ColorProperty.java @@ -99,7 +99,7 @@ public final class ColorProperty extends Property { * @see ColorUtil#parseColorString(FOUserAgent, String) */ public static ColorProperty getInstance(FOUserAgent foUserAgent, String value) - throws PropertyException { + throws PropertyException { ColorProperty instance = new ColorProperty( ColorUtil.parseColorString( foUserAgent, value)); diff --git a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java index 65b2cebbd..7b9b5bc82 100644 --- a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java +++ b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java @@ -92,7 +92,7 @@ public final class CommonHyphenation { * @throws PropertyException if a a property exception occurs */ public static CommonHyphenation getInstance(PropertyList propertyList) - throws PropertyException { + throws PropertyException { StringProperty language = (StringProperty) propertyList.get(Constants.PR_LANGUAGE); StringProperty country diff --git a/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java b/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java index 3edb84009..cbd34c9b3 100644 --- a/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/CompoundPropertyMaker.java @@ -139,9 +139,8 @@ public class CompoundPropertyMaker extends PropertyMaker { * @return the property * @throws PropertyException if a property exception occurs */ - public Property get(int subpropertyId, PropertyList propertyList, - boolean tryInherit, boolean tryDefault) - throws PropertyException { + public Property get(int subpropertyId, PropertyList propertyList, boolean tryInherit, + boolean tryDefault) throws PropertyException { Property p = super.get(subpropertyId, propertyList, tryInherit, tryDefault); if (subpropertyId != 0 && p != null) { p = getSubprop(p, subpropertyId); @@ -254,7 +253,7 @@ public class CompoundPropertyMaker extends PropertyMaker { * @throws PropertyException ... */ protected Property makeCompound(PropertyList propertyList, FObj parentFO) - throws PropertyException { + throws PropertyException { Property p = makeNewProperty(); CompoundDatatype data = (CompoundDatatype) p.getObject(); for (int i = 0; i < Constants.COMPOUND_COUNT; i++) { diff --git a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java index 330101ee7..f6fa806c9 100644 --- a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java +++ b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java @@ -52,7 +52,7 @@ public final class FontFamilyProperty extends ListProperty { * {@inheritDoc} */ public Property make(PropertyList propertyList, String value, FObj fo) - throws PropertyException { + throws PropertyException { if ("inherit".equals(value)) { return super.make(propertyList, value, fo); } else { diff --git a/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java b/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java index 60ef955ba..72884a177 100644 --- a/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java @@ -51,7 +51,7 @@ public class FontSizePropertyMaker * it is immediately replaced by the resolved {@link FixedLength}. */ public Property make(PropertyList propertyList, String value, FObj fo) - throws PropertyException { + throws PropertyException { Property p = super.make(propertyList, value, fo); if (p instanceof PercentLength) { Property pp = propertyList.getFromParent(this.propId); diff --git a/src/java/org/apache/fop/fo/properties/KeepProperty.java b/src/java/org/apache/fop/fo/properties/KeepProperty.java index 0bc44e459..9d04ce780 100644 --- a/src/java/org/apache/fop/fo/properties/KeepProperty.java +++ b/src/java/org/apache/fop/fo/properties/KeepProperty.java @@ -62,7 +62,7 @@ public final class KeepProperty extends Property implements CompoundDatatype { * {@inheritDoc} */ public Property convertProperty(Property p, PropertyList propertyList, FObj fo) - throws PropertyException { + throws PropertyException { if (p instanceof KeepProperty) { return p; } diff --git a/src/java/org/apache/fop/fo/properties/PageDimensionMaker.java b/src/java/org/apache/fop/fo/properties/PageDimensionMaker.java index 919dd84d0..c0c6a2ed7 100644 --- a/src/java/org/apache/fop/fo/properties/PageDimensionMaker.java +++ b/src/java/org/apache/fop/fo/properties/PageDimensionMaker.java @@ -44,7 +44,7 @@ public class PageDimensionMaker extends LengthProperty.Maker { * Return the default or user-defined fallback in case the value * was specified as "auto" * @param subpropId The subproperty id of the property being retrieved. - * Is 0 when retriving a base property. + * Is 0 when retrieving a base property. * @param propertyList The PropertyList object being built for this FO. * @param tryInherit true if inherited properties should be examined. * @param tryDefault true if the default value should be returned. diff --git a/src/java/org/apache/fop/fo/properties/ToBeImplementedProperty.java b/src/java/org/apache/fop/fo/properties/ToBeImplementedProperty.java index 387355623..59a6cafef 100644 --- a/src/java/org/apache/fop/fo/properties/ToBeImplementedProperty.java +++ b/src/java/org/apache/fop/fo/properties/ToBeImplementedProperty.java @@ -23,7 +23,7 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; /** - * A special property for representing an as yet implemented implemented property. + * A special property for representing an as yet unimplemented property. */ public class ToBeImplementedProperty extends Property { diff --git a/src/java/org/apache/fop/fonts/EncodingMode.java b/src/java/org/apache/fop/fonts/EncodingMode.java index 734292c54..8a40d6593 100644 --- a/src/java/org/apache/fop/fonts/EncodingMode.java +++ b/src/java/org/apache/fop/fonts/EncodingMode.java @@ -19,25 +19,19 @@ package org.apache.fop.fonts; -import java.io.ObjectStreamException; -import java.io.Serializable; - - /** * This class enumerates all supported encoding modes for fonts: auto, single-byte and CID. */ -public final class EncodingMode implements Serializable { - - private static final long serialVersionUID = 8311486102457779529L; +public enum EncodingMode { /** Automatic selection of encoding mode. */ - public static final EncodingMode AUTO = new EncodingMode("auto"); + AUTO("auto"), /** Single-byte encoding */ - public static final EncodingMode SINGLE_BYTE = new EncodingMode("single-byte"); + SINGLE_BYTE("single-byte"), /** CID encoding */ - public static final EncodingMode CID = new EncodingMode("cid"); + CID("cid"); private String name; @@ -58,25 +52,18 @@ public final class EncodingMode implements Serializable { * @param name the name of the encoding mode to look up * @return the encoding mode constant */ - public static EncodingMode valueOf(String name) { - if (name.equalsIgnoreCase(EncodingMode.AUTO.getName())) { - return EncodingMode.AUTO; - } else if (name.equalsIgnoreCase(EncodingMode.SINGLE_BYTE.getName())) { - return EncodingMode.SINGLE_BYTE; - } else if (name.equalsIgnoreCase(EncodingMode.CID.getName())) { - return EncodingMode.CID; - } else { - throw new IllegalArgumentException("Invalid encoding mode: " + name); + public static EncodingMode getEncodingMode(String name) { + for (EncodingMode em : EncodingMode.values()) { + if (name.equalsIgnoreCase(em.getName())) { + return em; + } } - } - - private Object readResolve() throws ObjectStreamException { - return valueOf(getName()); + throw new IllegalArgumentException("Invalid encoding mode: " + name); } /** {@inheritDoc} */ public String toString() { - return "EncodingMode:" + getName(); + return "EncodingMode: " + getName(); } } diff --git a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java index f8399b110..9a11f84bc 100644 --- a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java +++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java @@ -254,7 +254,7 @@ public class FontInfoConfigurator { } boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true); - EncodingMode encodingMode = EncodingMode.valueOf( + EncodingMode encodingMode = EncodingMode.getEncodingMode( fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName())); EmbedFontInfo embedFontInfo = new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont); diff --git a/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java b/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java index 5831e2b98..40a44a942 100644 --- a/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java +++ b/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java @@ -20,6 +20,7 @@ package org.apache.fop.hyphenation; import java.util.Hashtable; +import java.util.Map; import java.util.Set; /** @@ -39,7 +40,7 @@ public class HyphenationTreeCache { * @return the HyhenationTree instance or null if it's not in the cache */ public HyphenationTree getHyphenationTree(String lang, String country) { - String key = constructKey(lang, country); + String key = constructLlccKey(lang, country); // first try to find it in the cache if (hyphenTrees.containsKey(key)) { @@ -57,7 +58,7 @@ public class HyphenationTreeCache { * @param country the country (may be null or "none") * @return the resulting key */ - public static String constructKey(String lang, String country) { + public static String constructLlccKey(String lang, String country) { String key = lang; // check whether the country code has been used if (country != null && !country.equals("none")) { @@ -67,6 +68,24 @@ public class HyphenationTreeCache { } /** + * If the user configured a hyphenation pattern file name + * for this (lang,country) value, return it. If not, return null. + * @param lang the language + * @param country the country (may be null or "none") + * @param hyphPatNames the map of user-configured hyphenation pattern file names + * @return the hyphenation pattern file name or null + */ + public static String constructUserKey(String lang, String country, Map hyphPatNames) { + String userKey = null; + if (hyphPatNames != null) { + String key = constructLlccKey(lang, country); + key.replace('_', '-'); + userKey = (String) hyphPatNames.get(key); + } + return userKey; + } + + /** * Cache a hyphenation tree under its key. * @param key the key (ex. "de_CH" or "en") * @param hTree the hyphenation tree diff --git a/src/java/org/apache/fop/hyphenation/Hyphenator.java b/src/java/org/apache/fop/hyphenation/Hyphenator.java index 230f2ae20..76e8b6327 100644 --- a/src/java/org/apache/fop/hyphenation/Hyphenator.java +++ b/src/java/org/apache/fop/hyphenation/Hyphenator.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; +import java.util.Map; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; @@ -39,32 +40,20 @@ import org.xml.sax.InputSource; * * @author Carlos Villegas <cav@uniscope.co.jp> */ -public class Hyphenator { +public final class Hyphenator { /** logging instance */ protected static Log log = LogFactory.getLog(Hyphenator.class); private static HyphenationTreeCache hTreeCache = null; - private HyphenationTree hyphenTree = null; - private int remainCharCount = 2; - private int pushCharCount = 2; /** Enables a dump of statistics. Note: If activated content is sent to System.out! */ private static boolean statisticsDump = false; /** * Creates a new hyphenator. - * @param lang the language - * @param country the country (may be null or "none") - * @param leftMin the minimum number of characters before the hyphenation point - * @param rightMin the minimum number of characters after the hyphenation point */ - public Hyphenator(String lang, String country, int leftMin, - int rightMin) { - hyphenTree = getHyphenationTree(lang, country); - remainCharCount = leftMin; - pushCharCount = rightMin; - } + private Hyphenator() { } /** @return the default (static) hyphenation tree cache */ public static synchronized HyphenationTreeCache getHyphenationTreeCache() { @@ -75,35 +64,76 @@ public class Hyphenator { } /** - * Returns a hyphenation tree for a given language and country. The hyphenation trees are - * cached. + * Returns a hyphenation tree for a given language and country, + * with fallback from (lang,country) to (lang). + * The hyphenation trees are cached. * @param lang the language * @param country the country (may be null or "none") + * @param resolver resolver to find the hyphenation files + * @param hyphPatNames the map with user-configured hyphenation pattern file names * @return the hyphenation tree */ public static HyphenationTree getHyphenationTree(String lang, - String country) { - return getHyphenationTree(lang, country, null); + String country, HyphenationTreeResolver resolver, Map hyphPatNames) { + String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); + HyphenationTreeCache cache = getHyphenationTreeCache(); + + // If this hyphenation tree has been registered as missing, return immediately + if (cache.isMissing(llccKey)) { + return null; + } + + HyphenationTree hTree = getHyphenationTree2(lang, country, resolver, hyphPatNames); + + // fallback to lang only + if (hTree == null && country != null && !country.equals("none")) { + String llKey = HyphenationTreeCache.constructLlccKey(lang, null); + if (!cache.isMissing(llKey)) { + hTree = getHyphenationTree2(lang, null, resolver, hyphPatNames); + if (hTree != null && log.isDebugEnabled()) { + log.debug("Couldn't find hyphenation pattern " + + "for lang=\"" + lang + "\",country=\"" + country + "\"." + + " Using general language pattern " + + "for lang=\"" + lang + "\" instead."); + } + if (hTree == null) { + // no fallback; register as missing + cache.noteMissing(llKey); + } else { + // also register for (lang,country) + cache.cache(llccKey, hTree); + } + } + } + + if (hTree == null) { + // (lang,country) and (lang) tried; register as missing + cache.noteMissing(llccKey); + log.error("Couldn't find hyphenation pattern " + + "for lang=\"" + lang + "\"" + + (country != null && !country.equals("none") + ? ",country=\"" + country + "\"" + : "") + + "."); + } + + return hTree; } /** - * Returns a hyphenation tree for a given language and country. The hyphenation trees are - * cached. + * Returns a hyphenation tree for a given language and country + * The hyphenation trees are cached. * @param lang the language * @param country the country (may be null or "none") * @param resolver resolver to find the hyphenation files + * @param hyphPatNames the map with user-configured hyphenation pattern file names * @return the hyphenation tree */ - public static HyphenationTree getHyphenationTree(String lang, - String country, HyphenationTreeResolver resolver) { - String key = HyphenationTreeCache.constructKey(lang, country); + private static HyphenationTree getHyphenationTree2(String lang, + String country, HyphenationTreeResolver resolver, Map hyphPatNames) { + String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); HyphenationTreeCache cache = getHyphenationTreeCache(); - // See if there was an error finding this hyphenation tree before - if (cache.isMissing(key)) { - return null; - } - HyphenationTree hTree; // first try to find it in the cache hTree = getHyphenationTreeCache().getHyphenationTree(lang, country); @@ -111,6 +141,11 @@ public class Hyphenator { return hTree; } + String key = HyphenationTreeCache.constructUserKey(lang, country, hyphPatNames); + if (key == null) { + key = llccKey; + } + if (resolver != null) { hTree = getUserHyphenationTree(key, resolver); } @@ -120,11 +155,9 @@ public class Hyphenator { // put it into the pattern cache if (hTree != null) { - cache.cache(key, hTree); - } else { - log.error("Couldn't find hyphenation pattern " + key); - cache.noteMissing(key); + cache.cache(llccKey, hTree); } + return hTree; } @@ -179,31 +212,11 @@ public class Hyphenator { try { is = getResourceStream(key); if (is == null) { - if (key.length() == 5) { - String lang = key.substring(0, 2); - is = getResourceStream(lang); - if (is != null) { - if (log.isDebugEnabled()) { - log.debug("Couldn't find hyphenation pattern '" - + key - + "'. Using general language pattern '" - + lang - + "' instead."); - } - } else { - if (log.isDebugEnabled()) { - log.debug("Couldn't find precompiled hyphenation pattern " - + lang + " in resources."); - } - return null; - } - } else { - if (log.isDebugEnabled()) { - log.debug("Couldn't find precompiled hyphenation pattern " - + key + " in resources"); - } - return null; + if (log.isDebugEnabled()) { + log.debug("Couldn't find precompiled hyphenation pattern " + + key + " in resources"); } + return null; } hTree = readHyphenationTree(is); } finally { @@ -335,6 +348,7 @@ public class Hyphenator { * @param lang the language * @param country the optional country code (may be null or "none") * @param resolver resolver to find the hyphenation files + * @param hyphPatNames the map with user-configured hyphenation pattern file names * @param word the word to hyphenate * @param leftMin the minimum number of characters before the hyphenation point * @param rightMin the minimum number of characters after the hyphenation point @@ -342,121 +356,14 @@ public class Hyphenator { */ public static Hyphenation hyphenate(String lang, String country, HyphenationTreeResolver resolver, + Map hyphPatNames, String word, int leftMin, int rightMin) { - HyphenationTree hTree = getHyphenationTree(lang, country, resolver); + HyphenationTree hTree = getHyphenationTree(lang, country, resolver, hyphPatNames); if (hTree == null) { return null; } return hTree.hyphenate(word, leftMin, rightMin); } - /** - * Hyphenates a word. - * @param lang the language - * @param country the optional country code (may be null or "none") - * @param word the word to hyphenate - * @param leftMin the minimum number of characters before the hyphenation point - * @param rightMin the minimum number of characters after the hyphenation point - * @return the hyphenation result - */ - public static Hyphenation hyphenate(String lang, String country, - String word, - int leftMin, int rightMin) { - return hyphenate(lang, country, null, word, leftMin, rightMin); - } - - /** - * Hyphenates a word. - * @param lang the language - * @param country the optional country code (may be null or "none") - * @param resolver resolver to find the hyphenation files - * @param word the word to hyphenate - * @param offset the offset of the first character in the "word" character array - * @param len the length of the word - * @param leftMin the minimum number of characters before the hyphenation point - * @param rightMin the minimum number of characters after the hyphenation point - * @return the hyphenation result - */ - public static Hyphenation hyphenate(String lang, // CSOK: ParameterNumber - String country, - HyphenationTreeResolver resolver, - char[] word, int offset, int len, - int leftMin, int rightMin) { - HyphenationTree hTree = getHyphenationTree(lang, country, resolver); - if (hTree == null) { - return null; - } - return hTree.hyphenate(word, offset, len, leftMin, rightMin); - } - - /** - * Hyphenates a word. - * @param lang the language - * @param country the optional country code (may be null or "none") - * @param word the word to hyphenate - * @param offset the offset of the first character in the "word" character array - * @param len the length of the word - * @param leftMin the minimum number of characters before the hyphenation point - * @param rightMin the minimum number of characters after the hyphenation point - * @return the hyphenation result - */ - public static Hyphenation hyphenate(String lang, String country, - char[] word, int offset, int len, - int leftMin, int rightMin) { - return hyphenate(lang, country, null, word, offset, len, leftMin, rightMin); - } - - /** - * Sets the minimum number of characters before the hyphenation point - * @param min the number of characters - */ - public void setMinRemainCharCount(int min) { - remainCharCount = min; - } - - /** - * Sets the minimum number of characters after the hyphenation point - * @param min the number of characters - */ - public void setMinPushCharCount(int min) { - pushCharCount = min; - } - - /** - * Sets the language and country for the hyphenation process. - * @param lang the language - * @param country the country (may be null or "none") - */ - public void setLanguage(String lang, String country) { - hyphenTree = getHyphenationTree(lang, country); - } - - /** - * Hyphenates a word. - * @param word the word to hyphenate - * @param offset the offset of the first character in the "word" character array - * @param len the length of the word - * @return the hyphenation result - */ - public Hyphenation hyphenate(char[] word, int offset, int len) { - if (hyphenTree == null) { - return null; - } - return hyphenTree.hyphenate(word, offset, len, remainCharCount, - pushCharCount); - } - - /** - * Hyphenates a word. - * @param word the word to hyphenate - * @return the hyphenation result - */ - public Hyphenation hyphenate(String word) { - if (hyphenTree == null) { - return null; - } - return hyphenTree.hyphenate(word, remainCharCount, pushCharCount); - } - } diff --git a/src/java/org/apache/fop/layoutmgr/Page.java b/src/java/org/apache/fop/layoutmgr/Page.java index b183efa01..d8ec66e82 100644 --- a/src/java/org/apache/fop/layoutmgr/Page.java +++ b/src/java/org/apache/fop/layoutmgr/Page.java @@ -41,10 +41,12 @@ public class Page { * @param pageNumber the page number (as an int) * @param pageNumberStr the page number (as a String) * @param blank true if this is a blank page + * @param spanAll true if the first span area spans all columns */ - public Page(SimplePageMaster spm, int pageNumber, String pageNumberStr, boolean blank) { + public Page(SimplePageMaster spm, int pageNumber, String pageNumberStr, + boolean blank, boolean spanAll) { this.spm = spm; - this.pageViewport = new PageViewport(spm, pageNumber, pageNumberStr, blank); + this.pageViewport = new PageViewport(spm, pageNumber, pageNumberStr, blank, spanAll); } /** diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java index 171b3fd4f..3a41eb191 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -45,6 +45,7 @@ public class PageBreaker extends AbstractBreaker { private boolean needColumnBalancing; private PageProvider pageProvider; private Block separatorArea; + private boolean spanAllActive; /** * The FlowLayoutManager object, which processes @@ -148,8 +149,9 @@ public class PageBreaker extends AbstractBreaker { } firstPart = false; pageBreakHandled = true; + pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), - pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); + pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive); return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange, restartLM, firstElements); } @@ -342,8 +344,9 @@ public class PageBreaker extends AbstractBreaker { pageBreakHandled = true; //Update so the available BPD is reported correctly int currentPageNum = pslm.getCurrentPageNum(); + pageProvider.setStartOfNextElementList(currentPageNum, - pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); + pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive); //Make sure we only add the areas we haven't added already effectiveList.ignoreAtStart = newStartPos; @@ -387,7 +390,7 @@ public class PageBreaker extends AbstractBreaker { boolean fitsOnePage = optimalPageCount <= pslm.getCurrentPV() - .getBodyRegion().getMainReference().getCurrentSpan().getColumnCount(); + .getBodyRegion().getMainReference().getCurrentSpan().getColumnCount(); if (needColumnBalancing) { if (!fitsOnePage) { @@ -435,7 +438,8 @@ public class PageBreaker extends AbstractBreaker { handleBreakTrait(breakClass); } pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), - pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); + pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(), + this.spanAllActive); } pageBreakHandled = false; // add static areas and resolve any new id areas @@ -503,9 +507,11 @@ public class PageBreaker extends AbstractBreaker { case Constants.EN_ALL: //break due to span change in multi-column layout curPage.getPageViewport().createSpan(true); + this.spanAllActive = true; return; case Constants.EN_NONE: curPage.getPageViewport().createSpan(false); + this.spanAllActive = false; return; case Constants.EN_COLUMN: case Constants.EN_AUTO: @@ -554,7 +560,7 @@ public class PageBreaker extends AbstractBreaker { */ private boolean needBlankPageBeforeNew(int breakVal) { if (breakVal == Constants.EN_PAGE - || (pslm.getCurrentPage().getPageViewport().getPage().isEmpty())) { + || (pslm.getCurrentPage().getPageViewport().getPage().isEmpty())) { // any page is OK or we already have an empty page return false; } else { diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java index 2e531a8d8..8caafa72b 100644 --- a/src/java/org/apache/fop/layoutmgr/PageProvider.java +++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java @@ -51,6 +51,7 @@ public class PageProvider implements Constants { private int startPageOfPageSequence; private int startPageOfCurrentElementList; private int startColumnOfCurrentElementList; + private boolean spanAllForCurrentElementList; private List cachedPages = new java.util.ArrayList(); private int lastPageIndex = -1; @@ -88,12 +89,17 @@ public class PageProvider implements Constants { * on so it can later retrieve PageViewports relative to this first page. * @param startPage the number of the first page for the element list. * @param startColumn the starting column number for the element list. + * @param spanAll true if the current element list is for a column-spanning section */ - public void setStartOfNextElementList(int startPage, int startColumn) { - log.debug("start of the next element list is:" - + " page=" + startPage + " col=" + startColumn); + public void setStartOfNextElementList(int startPage, int startColumn, boolean spanAll) { + if (log.isDebugEnabled()) { + log.debug("start of the next element list is:" + + " page=" + startPage + " col=" + startColumn + + (spanAll ? ", column-spanning" : "")); + } this.startPageOfCurrentElementList = startPage - startPageOfPageSequence + 1; this.startColumnOfCurrentElementList = startColumn; + this.spanAllForCurrentElementList = spanAll; //Reset Cache this.lastRequestedIndex = -1; this.lastReportedBPD = -1; @@ -290,7 +296,7 @@ public class PageProvider implements Constants { if (log.isTraceEnabled()) { log.trace("Caching " + index); } - cacheNextPage(index, isBlank, isLastPage); + cacheNextPage(index, isBlank, isLastPage, this.spanAllForCurrentElementList); } Page page = (Page)cachedPages.get(intIndex); boolean replace = false; @@ -306,7 +312,7 @@ public class PageProvider implements Constants { } if (replace) { discardCacheStartingWith(intIndex); - page = cacheNextPage(index, isBlank, isLastPage); + page = cacheNextPage(index, isBlank, isLastPage, this.spanAllForCurrentElementList); } return page; } @@ -320,7 +326,7 @@ public class PageProvider implements Constants { } } - private Page cacheNextPage(int index, boolean isBlank, boolean isLastPage) { + private Page cacheNextPage(int index, boolean isBlank, boolean isLastPage, boolean spanAll) { String pageNumberString = pageSeq.makeFormattedPageNumber(index); boolean isFirstPage = (startPageOfPageSequence == index); SimplePageMaster spm = pageSeq.getNextSimplePageMaster( @@ -335,7 +341,7 @@ public class PageProvider implements Constants { eventProducer.flowNotMappingToRegionBody(this, pageSeq.getMainFlow().getFlowName(), spm.getMasterName(), spm.getLocator()); } - Page page = new Page(spm, index, pageNumberString, isBlank); + Page page = new Page(spm, index, pageNumberString, isBlank, spanAll); //Set unique key obtained from the AreaTreeHandler page.getPageViewport().setKey(areaTreeHandler.generatePageViewportKey()); page.getPageViewport().setForeignAttributes(spm.getForeignAttributes()); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index 83d286c15..cbae691b1 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -1415,6 +1415,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager = Hyphenator.hyphenate(hyphenationProperties.language.getString(), hyphenationProperties.country.getString(), getFObj().getUserAgent().getFactory().getHyphenationTreeResolver(), + getFObj().getUserAgent().getFactory().getHyphPatNames(), sbChars.toString(), hyphenationProperties.hyphenationRemainCharacterCount.getValue(), hyphenationProperties.hyphenationPushCharacterCount.getValue()); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 2b432d0d4..7e343697a 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -552,4 +552,11 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } + /** {@inheritDoc} */ + public void reset() { + super.reset(); + curBlockArea = null; + tableUnit = 0.0; + } + } diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java index 49f762708..b9612d11b 100644 --- a/src/java/org/apache/fop/pdf/PDFMetadata.java +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -151,6 +151,9 @@ public class PDFMetadata extends PDFStream { } dc.addDate(info.getCreationDate()); + //Somewhat redundant but some PDF/A checkers issue a warning without this. + dc.setFormat("application/pdf"); + //PDF/A identification PDFAMode pdfaMode = pdfDoc.getProfile().getPDFAMode(); if (pdfaMode.isPDFA1LevelB()) { diff --git a/src/java/org/apache/fop/pdf/PDFName.java b/src/java/org/apache/fop/pdf/PDFName.java index 8214cda76..3f9af7669 100644 --- a/src/java/org/apache/fop/pdf/PDFName.java +++ b/src/java/org/apache/fop/pdf/PDFName.java @@ -41,6 +41,7 @@ public class PDFName extends PDFObject { this.name = escapeName(name); } + private static final String ESCAPED_NAME_CHARS = "/()<>[]%#"; /** * Escapes a PDF name. It adds the leading slash and escapes characters as necessary. @@ -56,7 +57,8 @@ public class PDFName extends PDFObject { } for (int i = (skipFirst ? 1 : 0), c = name.length(); i < c; i++) { char ch = name.charAt(i); - if (ch < 33 || ch > 126 || ch == 0x2F) { + + if (ch < 33 || ch > 126 || ESCAPED_NAME_CHARS.indexOf(ch) >= 0) { sb.append('#'); toHex(ch, sb); } else { diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 21d4faf56..c37e0c37c 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -47,6 +47,7 @@ import org.apache.fop.render.afp.extensions.AFPElementMapping; import org.apache.fop.render.afp.extensions.AFPIncludeFormMap; 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.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandler; @@ -76,8 +77,8 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler private DataStream dataStream; /** the map of page segments */ - private Map/*<String,String>*/pageSegmentMap - = new java.util.HashMap/*<String,String>*/(); + private Map/*<String,PageSegmentDescriptor>*/pageSegmentMap + = new java.util.HashMap/*<String,PageSegmentDescriptor>*/(); /** Medium Map referenced on previous page **/ private String lastMediumMap; @@ -213,7 +214,6 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler throws IFException { this.location = LOC_ELSEWHERE; paintingState.clear(); - pageSegmentMap.clear(); AffineTransform baseTransform = getBaseTransform(); paintingState.concatenate(baseTransform); @@ -288,9 +288,12 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler null); } if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) { - String name = aps.getName(); - String source = aps.getValue(); - pageSegmentMap.put(source, name); + AFPPageSegmentElement.AFPPageSegmentSetup apse + = (AFPPageSegmentElement.AFPPageSegmentSetup)aps; + String name = apse.getName(); + String source = apse.getValue(); + String uri = apse.getResourceSrc(); + pageSegmentMap.put(source, new PageSegmentDescriptor(name, uri)); } else if (AFPElementMapping.NO_OPERATION.equals(element)) { String content = aps.getContent(); if (content != null) { @@ -392,13 +395,13 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** - * Returns the page segment name for a given URI if it actually represents a page segment. + * Returns the page segment descriptor for a given URI if it actually represents a page segment. * Otherwise, it just returns null. * @param uri the URI that identifies the page segment - * @return the page segment name or null if there's no page segment for the given URI + * @return the page segment descriptor or null if there's no page segment for the given URI */ - String getPageSegmentNameFor(String uri) { - return (String)pageSegmentMap.get(uri); + PageSegmentDescriptor getPageSegmentNameFor(String uri) { + return (PageSegmentDescriptor)pageSegmentMap.get(uri); } } diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 0bbfef3e8..ed16a923b 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -26,6 +26,8 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Map; import org.w3c.dom.Document; @@ -48,6 +50,8 @@ import org.apache.fop.afp.modca.AbstractPageObject; import org.apache.fop.afp.modca.PresentationTextObject; import org.apache.fop.afp.ptoca.PtocaBuilder; import org.apache.fop.afp.ptoca.PtocaProducer; +import org.apache.fop.afp.util.DefaultFOPResourceAccessor; +import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; @@ -183,14 +187,34 @@ public class AFPPainter extends AbstractIFPainter { /** {@inheritDoc} */ public void drawImage(String uri, Rectangle rect) throws IFException { - String name = documentHandler.getPageSegmentNameFor(uri); - if (name != null) { + PageSegmentDescriptor pageSegment = documentHandler.getPageSegmentNameFor(uri); + + if (pageSegment != null) { float[] srcPts = {rect.x, rect.y}; int[] coords = unitConv.mpts2units(srcPts); int width = Math.round(unitConv.mpt2units(rect.width)); int height = Math.round(unitConv.mpt2units(rect.height)); - getDataStream().createIncludePageSegment(name, coords[X], coords[Y], width, height); + getDataStream().createIncludePageSegment(pageSegment.getName(), + coords[X], coords[Y], width, height); + + //Do we need to embed an external page segment? + if (pageSegment.getURI() != null) { + ResourceAccessor accessor = new DefaultFOPResourceAccessor ( + documentHandler.getUserAgent(), null, null); + try { + URI resourceUri = new URI(pageSegment.getURI()); + documentHandler.getResourceManager().createIncludedResourceFromExternal( + pageSegment.getName(), resourceUri, accessor); + + } catch (URISyntaxException urie) { + throw new IFException("Could not handle resource url" + + pageSegment.getURI(), urie); + } catch (IOException ioe) { + throw new IFException("Could not handle resource" + pageSegment.getURI(), ioe); + } + } + } else { drawImageUsingURI(uri, rect); } diff --git a/src/java/org/apache/fop/render/afp/PageSegmentDescriptor.java b/src/java/org/apache/fop/render/afp/PageSegmentDescriptor.java new file mode 100644 index 000000000..8915a7a59 --- /dev/null +++ b/src/java/org/apache/fop/render/afp/PageSegmentDescriptor.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * Class holding information on a page segment. + */ +class PageSegmentDescriptor { + + private String name; + private String uri; + + /** + * Creates a new page segment descriptor. + * @param name the page segment name + * @param uri the URI identifying the external resource file (may be null if the page segment + * shall be referenced rather than embedded) + */ + public PageSegmentDescriptor(String name, String uri) { + this.name = name; + this.uri = uri; + } + + /** + * Returns the name of the page segment (usually 8 upper case letters). + * @return the name of the page segment + */ + public String getName() { + return this.name; + } + + /** + * Returns the URI of the external resource containing the page segment. + * @return the URI of the external resource (or null if the resource is not to be embedded) + */ + public String getURI() { + return this.uri; + } + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java index d77e21db2..580e9f800 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java @@ -103,7 +103,7 @@ public class AFPElementMapping extends ElementMapping { static class AFPIncludePageSegmentMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new AFPPageSetupElement(parent, INCLUDE_PAGE_SEGMENT); + return new AFPPageSegmentElement(parent, INCLUDE_PAGE_SEGMENT); } } 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 254b68f1f..f2e77ec33 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java @@ -24,11 +24,13 @@ import java.net.URISyntaxException; import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.render.afp.extensions.AFPPageSegmentElement.AFPPageSegmentSetup; import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; @@ -52,7 +54,7 @@ public class AFPExtensionHandler extends DefaultHandler throws SAXException { boolean handled = false; if (AFPExtensionAttachment.CATEGORY.equals(uri)) { - lastAttributes = attributes; + lastAttributes = new AttributesImpl(attributes); handled = true; if (localName.equals(AFPElementMapping.NO_OPERATION) || localName.equals(AFPElementMapping.TAG_LOGICAL_ELEMENT) @@ -96,6 +98,30 @@ public class AFPExtensionHandler extends DefaultHandler if (name != null) { returnedObject.setName(name); } + } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(localName)) { + AFPPageSegmentSetup pageSetupExtn = null; + + pageSetupExtn = new AFPPageSegmentSetup(localName); + this.returnedObject = pageSetupExtn; + + String name = lastAttributes.getValue("name"); + if (name != null) { + returnedObject.setName(name); + } + String value = lastAttributes.getValue("value"); + if (value != null && pageSetupExtn != null) { + pageSetupExtn.setValue(value); + } + + String resourceSrc = lastAttributes.getValue("resource-file"); + if (resourceSrc != null && pageSetupExtn != null) { + pageSetupExtn.setResourceSrc(resourceSrc); + } + + if (content.length() > 0 && pageSetupExtn != null) { + pageSetupExtn.setContent(content.toString()); + content.setLength(0); //Reset text buffer (see characters()) + } } else { AFPPageSetup pageSetupExtn = null; if (AFPElementMapping.INVOKE_MEDIUM_MAP.equals(localName)) { @@ -117,6 +143,7 @@ public class AFPExtensionHandler extends DefaultHandler content.setLength(0); //Reset text buffer (see characters()) } } + } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPPageSegmentElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPPageSegmentElement.java new file mode 100644 index 000000000..f7379e02c --- /dev/null +++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSegmentElement.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.afp.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.extensions.ExtensionAttachment; + +/** + * This class extends the org.apache.fop.extensions.ExtensionObj class. The + * object faciliates extraction of elements from formatted objects based on + * the static list as defined in the AFPElementMapping implementation. + * <p/> + */ +public class AFPPageSegmentElement extends AFPPageSetupElement { + + private static final String ATT_RESOURCE_SRC = "resource-file"; + + /** + * Constructs an AFP object (called by Maker). + * + * @param parent the parent formatting object + * @param name the name of the afp element + */ + public AFPPageSegmentElement(FONode parent, String name) { + super(parent, name); + } + + + private AFPPageSegmentSetup getPageSetupAttachment() { + return (AFPPageSegmentSetup)getExtensionAttachment(); + } + + + /** {@inheritDoc} */ + public void processNode(String elementName, Locator locator, + Attributes attlist, PropertyList propertyList) + throws FOPException { + + AFPPageSegmentSetup pageSetup = getPageSetupAttachment(); + super.processNode(elementName, locator, attlist, propertyList); + + + String attr = attlist.getValue(ATT_RESOURCE_SRC); + + if (attr != null && attr.length() > 0) { + pageSetup.setResourceSrc(attr); + } + + } + + /** {@inheritDoc} */ + protected ExtensionAttachment instantiateExtensionAttachment() { + return new AFPPageSegmentSetup(getLocalName()); + } + + /** + * This is the pass-through value object for the AFP extension. + */ + public static class AFPPageSegmentSetup extends AFPPageSetup { + + private static final long serialVersionUID = 1L; + + private String resourceSrc; + + /** + * Default constructor. + * + * @param elementName the name of the setup code object, may be null + */ + public AFPPageSegmentSetup(String elementName) { + super(elementName); + } + + /** + * Returns the source URI for the page segment. + * @return the source URI + */ + public String getResourceSrc() { + return resourceSrc; + } + + /** + * Sets the source URI for the page segment. + * @param resourceSrc the source URI + */ + public void setResourceSrc(String resourceSrc) { + this.resourceSrc = resourceSrc.trim(); + } + + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + if (name != null && name.length() > 0) { + atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", name); + } + if (value != null && value.length() > 0) { + atts.addAttribute(null, ATT_VALUE, ATT_VALUE, "CDATA", value); + } + + if (resourceSrc != null && resourceSrc.length() > 0) { + atts.addAttribute(null, ATT_RESOURCE_SRC, ATT_RESOURCE_SRC, "CDATA", resourceSrc); + } + + handler.startElement(CATEGORY, elementName, elementName, atts); + if (content != null && content.length() > 0) { + char[] chars = content.toCharArray(); + handler.characters(chars, 0, chars.length); + } + handler.endElement(CATEGORY, elementName, elementName); + } + + /** {@inheritDoc} */ + public String toString() { + return "AFPPageSegmentSetup(element-name=" + getElementName() + + " name=" + getName() + + " value=" + getValue() + + " resource=" + getResourceSrc() + ")"; + } + + } + + +} diff --git a/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java b/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java index 4b0a3fe68..71ee7a0b0 100644 --- a/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java +++ b/src/java/org/apache/fop/render/intermediate/util/IFConcatenator.java @@ -19,7 +19,6 @@ package org.apache.fop.render.intermediate.util; - import java.awt.Dimension; import javax.xml.transform.Source; @@ -39,12 +38,17 @@ import org.apache.fop.render.intermediate.IFParser; * <p> * Note: This class will filter/ignore any document navigation events. Support for this may be * added later. + * <p> + * Note: document-level extensions will only be transferred from the first document passed in. + * If you need to merge extensions from all the concatenated documents, you may have to merge + * these manually on the XML level, for example using XSLT. */ public class IFConcatenator { private IFDocumentHandler targetHandler; private int nextPageIndex = 0; + private boolean inFirstDocument = true; /** * Creates a new IF concatenator. @@ -163,14 +167,17 @@ public class IFConcatenator { /** {@inheritDoc} */ public void endDocument() throws IFException { //ignore + inFirstDocument = false; } /** {@inheritDoc} */ public void handleExtensionObject(Object extension) throws IFException { - if (inPageSequence) { + if (inPageSequence || inFirstDocument) { //Only pass through when inside page-sequence + //or for the first document (for document-level extensions). super.handleExtensionObject(extension); } + //Note:Extensions from non-first documents are ignored! } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java index 99233abb6..05726be22 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java @@ -38,6 +38,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; 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.RenderingContext; import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; @@ -46,6 +47,7 @@ import org.apache.fop.svg.PDFBridgeContext; import org.apache.fop.svg.PDFGraphics2D; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; +import org.w3c.dom.Document; /** * Image Handler implementation which handles SVG images. @@ -82,10 +84,14 @@ public class PDFImageHandlerSVG implements ImageHandler { userAgent.getFactory().getImageManager(), userAgent.getImageSessionContext(), new AffineTransform()); + + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); GraphicsNode root; try { - root = builder.build(ctx, imageSVG.getDocument()); + root = builder.build(ctx, clonedDoc); builder = null; } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java index d285904a8..04c95545c 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java @@ -21,6 +21,7 @@ package org.apache.fop.render.pdf.extensions; import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; @@ -48,7 +49,7 @@ public class PDFExtensionHandler extends DefaultHandler throws SAXException { boolean handled = false; if (PDFExtensionAttachment.CATEGORY.equals(uri)) { - lastAttributes = attributes; + lastAttributes = new AttributesImpl(attributes); handled = false; if (localName.equals(PDFEmbeddedFileExtensionAttachment.ELEMENT)) { //handled in endElement diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java index 41cba7563..839a0cab6 100644 --- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java @@ -23,6 +23,8 @@ import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.io.IOException; +import org.w3c.dom.Document; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.gvt.GraphicsNode; @@ -34,6 +36,7 @@ import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; import org.apache.xmlgraphics.ps.PSGenerator; 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.RenderingContext; import org.apache.fop.svg.SVGEventProducer; @@ -70,10 +73,14 @@ public class PSImageHandlerSVG implements ImageHandler { context.getUserAgent().getFactory().getImageManager(), context.getUserAgent().getImageSessionContext()); + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); + GraphicsNode root; try { GVTBuilder builder = new GVTBuilder(); - root = builder.build(ctx, imageSVG.getDocument()); + root = builder.build(ctx, clonedDoc); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java index dee918f19..bf970d0a6 100644 --- a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java +++ b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java @@ -21,6 +21,7 @@ package org.apache.fop.render.ps.extensions; import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; @@ -49,7 +50,7 @@ public class PSExtensionHandler extends DefaultHandler throws SAXException { boolean handled = false; if (PSExtensionAttachment.CATEGORY.equals(uri)) { - lastAttributes = attributes; + lastAttributes = new AttributesImpl(attributes); handled = false; if (localName.equals(PSSetupCode.ELEMENT) || localName.equals(PSSetPageDevice.ELEMENT) diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 5698329c8..35f58e170 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -80,11 +80,11 @@ import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fo.flow.PageNumberCitation; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; -import org.apache.fop.fo.flow.table.TableFooter; -import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableFooter; import org.apache.fop.fo.flow.table.TableHeader; +import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.fo.pagination.PageSequence; @@ -436,7 +436,8 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.popBlockAttributes(); + int breakValue = toRtfBreakValue(bl.getBreakAfter()); + textrun.popBlockAttributes(breakValue); } catch (IOException ioe) { handleIOTrouble(ioe); @@ -488,7 +489,8 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addParagraphBreak(); - textrun.popBlockAttributes(); + int breakValue = toRtfBreakValue(bl.getBreakAfter()); + textrun.popBlockAttributes(breakValue); } catch (IOException ioe) { handleIOTrouble(ioe); @@ -498,6 +500,21 @@ public class RTFHandler extends FOEventHandler { } } + private int toRtfBreakValue(int foBreakValue) { + switch (foBreakValue) { + case Constants.EN_PAGE: + return RtfTextrun.BREAK_PAGE; + case Constants.EN_EVEN_PAGE: + return RtfTextrun.BREAK_EVEN_PAGE; + case Constants.EN_ODD_PAGE: + return RtfTextrun.BREAK_ODD_PAGE; + case Constants.EN_COLUMN: + return RtfTextrun.BREAK_COLUMN; + default: + return RtfTextrun.BREAK_NONE; + } + } + /** {@inheritDoc} */ public void startTable(Table tbl) { if (bDefer) { diff --git a/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java b/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java index 1b7e21bd3..d377f740d 100644 --- a/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java +++ b/src/java/org/apache/fop/render/rtf/TextAttributesConverter.java @@ -35,6 +35,7 @@ import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.flow.Inline; import org.apache.fop.fo.flow.Leader; import org.apache.fop.fo.flow.PageNumber; +import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginBlock; @@ -80,10 +81,48 @@ final class TextAttributesConverter { attrBlockMargin(fobj.getCommonMarginBlock(), attrib); attrBlockTextAlign(fobj.getTextAlign(), attrib); attrBorder(fobj.getCommonBorderPaddingBackground(), attrib, fobj); + attrBreak(fobj, attrib); return attrib; } + private static void attrBreak(Block fobj, FOPRtfAttributes attrib) { + int breakValue = fobj.getBreakBefore(); + if (breakValue != Constants.EN_AUTO) { + //"sect" Creates a new section and a page break, + //a simple page break with control word "page" caused + //some problems + boolean bHasTableCellParent = false; + FONode f = fobj; + while (f.getParent() != null) { + f = f.getParent(); + if (f instanceof TableCell) { + bHasTableCellParent = true; + break; + } + } + if (!bHasTableCellParent) { + attrib.set("sect"); + switch (breakValue) { + case Constants.EN_EVEN_PAGE: + attrib.set("sbkeven"); + break; + case Constants.EN_ODD_PAGE: + attrib.set("sbkodd"); + break; + case Constants.EN_COLUMN: + attrib.set("sbkcol"); + break; + default: + attrib.set("sbkpage"); + } + } else { + log.warn("Cannot create break-before for a block inside a table."); + } + } + //Break after is handled in RtfCloseGroupMark + } + /** * Converts all known text FO properties to RtfAttributes * @param fobj FObj whose properties are to be converted 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 4831ffd86..128c9a3df 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 @@ -25,9 +25,10 @@ import java.io.Writer; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Stack; -// FOP -import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfExternalGraphic; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Class which contains a linear text run. It has methods to add attributes, @@ -35,9 +36,26 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfExternalGraphic; * @author Peter Herweg, pherweg@web.de */ public class RtfTextrun extends RtfContainer { + + /** Constant for no page break */ + public static final int BREAK_NONE = 0; + /** Constant for a normal page break */ + public static final int BREAK_PAGE = 1; + /** Constant for a column break */ + public static final int BREAK_COLUMN = 2; + /** Constant for a even page break */ + public static final int BREAK_EVEN_PAGE = 3; + /** Constant for a odd page break */ + public static final int BREAK_ODD_PAGE = 4; + private boolean bSuppressLastPar = false; private RtfListItem rtfListItem; + /** + * logging instance + */ + protected static Log log = LogFactory.getLog(RtfTextrun.class); + /** Manager for handling space-* property. */ private RtfSpaceManager rtfSpaceManager = new RtfSpaceManager(); @@ -68,10 +86,12 @@ public class RtfTextrun extends RtfContainer { /** Class which represents the closing of a RTF group mark.*/ private class RtfCloseGroupMark extends RtfElement { + private int breakType = BREAK_NONE; - RtfCloseGroupMark(RtfContainer parent, Writer w) - throws IOException { + RtfCloseGroupMark(RtfContainer parent, Writer w, int breakType) + throws IOException { super(parent, w); + this.breakType = breakType; } /** @@ -82,11 +102,44 @@ public class RtfTextrun extends RtfContainer { } /** - * write RTF code of all our children + * Returns the break type. + * @return the break type (BREAK_* constants) + */ + public int getBreakType() { + return breakType; + } + + /** + * Write RTF code of all our children. * @throws IOException for I/O problems */ protected void writeRtfContent() throws IOException { writeGroupMark(false); + boolean bHasTableCellParent = this.getParentOfClass(RtfTableCell.class) != null; + + //Unknown behavior when a table starts a new section, + //Word may crash + if (breakType != BREAK_NONE) { + if (!bHasTableCellParent) { + writeControlWord("sect"); + /* The following modifiers don't seem to appear in the right place */ + switch (breakType) { + case BREAK_EVEN_PAGE: + writeControlWord("sbkeven"); + break; + case BREAK_ODD_PAGE: + writeControlWord("sbkodd"); + break; + case BREAK_COLUMN: + writeControlWord("sbkcol"); + break; + default: + writeControlWord("sbkpage"); + } + } else { + log.warn("Cannot create break-after for a paragraph inside a table."); + } + } } } @@ -135,8 +188,18 @@ public class RtfTextrun extends RtfContainer { * * @throws IOException for I/O problems */ + private void addCloseGroupMark(int breakType) throws IOException { + RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, breakType); + } + + /** + * Adds instance of <code>CloseGroupMark</code> as a child, but without a break option. + * Inline attributes do not need that for example + * + * @throws IOException for I/O problems + */ private void addCloseGroupMark() throws IOException { - RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer); + RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, BREAK_NONE); } /** @@ -155,14 +218,14 @@ public class RtfTextrun extends RtfContainer { /** * Pops block attributes, notifies all opened blocks about pushing block * attributes, adds <code>CloseGroupMark</code> as a child. - * + * @param breakType the break type * @throws IOException for I/O problems */ - public void popBlockAttributes() throws IOException { - rtfSpaceManager.popRtfSpaceSplitter(); - rtfSpaceManager.stopUpdatingSpaceBefore(); - addCloseGroupMark(); - } + public void popBlockAttributes(int breakType) throws IOException { + rtfSpaceManager.popRtfSpaceSplitter(); + rtfSpaceManager.stopUpdatingSpaceBefore(); + addCloseGroupMark(breakType); + } /** * Pushes inline attributes. @@ -228,28 +291,30 @@ public class RtfTextrun extends RtfContainer { * @throws IOException for I/O problems */ public void addParagraphBreak() throws IOException { - // get copy of children list - List children = getChildren(); - - // delete all previous CloseGroupMark - int deletedCloseGroupCount = 0; - - ListIterator lit = children.listIterator(children.size()); - while (lit.hasPrevious() - && (lit.previous() instanceof RtfCloseGroupMark)) { - lit.remove(); - deletedCloseGroupCount++; - } - - if (children.size() != 0) { - // add paragraph break and restore all deleted close group marks - setChildren(children); - new RtfParagraphBreak(this, writer); - for (int i = 0; i < deletedCloseGroupCount; i++) { - addCloseGroupMark(); - } - } - } + // get copy of children list + List children = getChildren(); + Stack tmp = new Stack(); + + // delete all previous CloseGroupMark + int deletedCloseGroupCount = 0; + + ListIterator lit = children.listIterator(children.size()); + while (lit.hasPrevious() + && (lit.previous() instanceof RtfCloseGroupMark)) { + tmp.push(new Integer(((RtfCloseGroupMark)lit.next()).getBreakType())); + lit.remove(); + deletedCloseGroupCount++; + } + + if (children.size() != 0) { + // add paragraph break and restore all deleted close group marks + setChildren(children); + new RtfParagraphBreak(this, writer); + for (int i = 0; i < deletedCloseGroupCount; i++) { + addCloseGroupMark(((Integer)tmp.pop()).intValue()); + } + } + } /** * Inserts a leader. diff --git a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java index fc007b90a..aa8c5238c 100644 --- a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java +++ b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java @@ -29,6 +29,7 @@ import org.w3c.dom.DOMImplementation; import org.xml.sax.EntityResolver; +import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.configuration.DefaultConfiguration; @@ -55,7 +56,7 @@ import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext; /** * This is the common base class of all of FOP's transcoders. */ -public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder { +public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder implements Configurable { /** * The key is used to specify the resolution for on-the-fly images generated diff --git a/src/java/org/apache/fop/svg/NativeTextPainter.java b/src/java/org/apache/fop/svg/NativeTextPainter.java index 14fa6460d..94d426396 100644 --- a/src/java/org/apache/fop/svg/NativeTextPainter.java +++ b/src/java/org/apache/fop/svg/NativeTextPainter.java @@ -111,9 +111,9 @@ public abstract class NativeTextPainter extends StrokingTextPainter { String style = ((posture != null) && (posture.floatValue() > 0.0)) ? Font.STYLE_ITALIC : Font.STYLE_NORMAL; - int weight = ((taWeight != null) - && (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD - : Font.WEIGHT_NORMAL; + int weight = toCSSWeight(taWeight != null + ? taWeight.floatValue() + : TextAttribute.WEIGHT_REGULAR.floatValue()); String firstFontFamily = null; @@ -176,6 +176,28 @@ public abstract class NativeTextPainter extends StrokingTextPainter { 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; + } + } + /** * Collects all characters from an {@link AttributedCharacterIterator}. * @param runaci the character iterator diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java index cf3053e19..409b8dd9f 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java @@ -256,6 +256,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D { if (!pdfContext.isPagePending()) { return; //ignore } + currentStream.write("Q\n"); //Finish page PDFStream pdfStream = this.pdfDoc.getFactory().makeStream( PDFFilterList.CONTENT_FILTER, false); @@ -321,6 +322,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D { pdfContext.setCurrentPage(page); pageRef = page.referencePDF(); + currentStream.write("q\n"); AffineTransform at = new AffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, height); currentStream.write("1 0 0 -1 0 " + height + " cm\n"); diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java index b77518ab0..24974b01a 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java @@ -25,12 +25,15 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.fop.apps.FOPException; +import org.apache.fop.fonts.CustomFontCollection; +import org.apache.fop.fonts.FontCollection; import org.apache.fop.fonts.FontEventListener; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontInfoConfigurator; import org.apache.fop.fonts.FontManager; +import org.apache.fop.fonts.FontManagerConfigurator; import org.apache.fop.fonts.FontResolver; -import org.apache.fop.fonts.FontSetup; +import org.apache.fop.fonts.base14.Base14FontCollection; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.render.pdf.PDFRendererConfigurator; @@ -70,29 +73,30 @@ public class PDFDocumentGraphics2DConfigurator { */ public static FontInfo createFontInfo(Configuration cfg) throws FOPException { FontInfo fontInfo = new FontInfo(); + final boolean strict = false; + FontResolver fontResolver = FontManager.createMinimalFontResolver(); + //TODO The following could be optimized by retaining the FontManager somewhere + FontManager fontManager = new FontManager(); if (cfg != null) { - FontResolver fontResolver = FontManager.createMinimalFontResolver(); - //TODO The following could be optimized by retaining the FontManager somewhere - FontManager fontManager = new FontManager(); + FontManagerConfigurator fmConfigurator = new FontManagerConfigurator(cfg); + fmConfigurator.configure(fontManager, strict); + } - //TODO Make use of fontBaseURL, font substitution and referencing configuration - //Requires a change to the expected configuration layout + List fontCollections = new java.util.ArrayList(); + fontCollections.add(new Base14FontCollection(fontManager.isBase14KerningEnabled())); + if (cfg != null) { //TODO Wire in the FontEventListener - final FontEventListener listener = null; - final boolean strict = false; + FontEventListener listener = null; //new FontEventAdapter(eventBroadcaster); FontInfoConfigurator fontInfoConfigurator = new FontInfoConfigurator(cfg, fontManager, fontResolver, listener, strict); List/*<EmbedFontInfo>*/ fontInfoList = new java.util.ArrayList/*<EmbedFontInfo>*/(); fontInfoConfigurator.configure(fontInfoList); - - if (fontManager.useCache()) { - fontManager.getFontCache().save(); - } - FontSetup.setup(fontInfo, fontInfoList, fontResolver); - } else { - FontSetup.setup(fontInfo); + fontCollections.add(new CustomFontCollection(fontResolver, fontInfoList)); } + fontManager.setup(fontInfo, + (FontCollection[])fontCollections.toArray( + new FontCollection[fontCollections.size()])); return fontInfo; } diff --git a/src/java/org/apache/fop/svg/PDFTranscoder.java b/src/java/org/apache/fop/svg/PDFTranscoder.java index fc27cb48a..bf08b2fcf 100644 --- a/src/java/org/apache/fop/svg/PDFTranscoder.java +++ b/src/java/org/apache/fop/svg/PDFTranscoder.java @@ -27,7 +27,6 @@ import java.io.OutputStream; import org.w3c.dom.Document; import org.w3c.dom.svg.SVGLength; -import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.UnitProcessor; @@ -73,8 +72,7 @@ import org.apache.fop.fonts.FontInfo; * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> * @version $Id$ */ -public class PDFTranscoder extends AbstractFOPTranscoder - implements Configurable { +public class PDFTranscoder extends AbstractFOPTranscoder { /** Graphics2D instance that is used to paint to */ protected PDFDocumentGraphics2D graphics = null; diff --git a/src/java/org/apache/fop/tools/anttasks/Fop.java b/src/java/org/apache/fop/tools/anttasks/Fop.java index c0a1ba9f7..58dd1fb5b 100644 --- a/src/java/org/apache/fop/tools/anttasks/Fop.java +++ b/src/java/org/apache/fop/tools/anttasks/Fop.java @@ -20,14 +20,6 @@ package org.apache.fop.tools.anttasks; // Ant -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.DirectoryScanner; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.types.FileSet; -import org.apache.tools.ant.util.GlobPatternMapper; - -// Java import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; @@ -36,17 +28,23 @@ import java.net.MalformedURLException; import java.util.List; import java.util.Vector; -// FOP +import org.xml.sax.SAXException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.impl.SimpleLog; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.GlobPatternMapper; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.MimeConstants; import org.apache.fop.cli.InputHandler; -import org.apache.commons.logging.impl.SimpleLog; -import org.apache.commons.logging.Log; -import org.xml.sax.SAXException; - /** * Wrapper for FOP which allows it to be accessed from within an Ant task. * Accepts the inputs: @@ -156,7 +154,7 @@ public class Fop extends Task { } /** - * Sets the XSLT parameters + * Sets the XSLT parameters * @param xsltParams the XSLT parameters */ public void setXsltParams(String xsltParams) { @@ -267,7 +265,7 @@ public class Fop extends Task { /** * Set whether exceptions are thrown. * default is false. - * @param throwExceptions true if should be thrown + * @param throwExceptions true if exceptions should be thrown */ public void setThrowexceptions(boolean throwExceptions) { this.throwExceptions = throwExceptions; @@ -553,7 +551,7 @@ class FOPTaskStarter { // OR output file doesn't exist OR // output file is older than input file if (task.getForce() || !outf.exists() - || (task.getXmlFile().lastModified() > outf.lastModified() + || (task.getXmlFile().lastModified() > outf.lastModified() || task.getXsltFile().lastModified() > outf.lastModified())) { render(task.getXmlFile(), task.getXsltFile(), outf, outputFormat); actioncount++; @@ -639,8 +637,8 @@ class FOPTaskStarter { } } - private void renderInputHandler - (InputHandler inputHandler, File outFile, String outputFormat) throws Exception { + private void renderInputHandler(InputHandler inputHandler, File outFile, String outputFormat) + throws Exception { OutputStream out = null; try { out = new java.io.FileOutputStream(outFile); diff --git a/src/java/org/apache/fop/traits/MinOptMax.java b/src/java/org/apache/fop/traits/MinOptMax.java index 99fab1adf..45b55695f 100644 --- a/src/java/org/apache/fop/traits/MinOptMax.java +++ b/src/java/org/apache/fop/traits/MinOptMax.java @@ -56,8 +56,7 @@ public final class MinOptMax implements Serializable { * @return the corresponding instance * @throws IllegalArgumentException if <code>min > opt || max < opt</code>. */ - public static MinOptMax getInstance(int min, int opt, int max) - throws IllegalArgumentException { + public static MinOptMax getInstance(int min, int opt, int max) throws IllegalArgumentException { if (min > opt) { throw new IllegalArgumentException("min (" + min + ") > opt (" + opt + ")"); } @@ -168,8 +167,7 @@ public final class MinOptMax implements Serializable { * @throws ArithmeticException if this instance has strictly less shrink or stretch * than the operand */ - public MinOptMax minus(MinOptMax operand) - throws ArithmeticException { + public MinOptMax minus(MinOptMax operand) throws ArithmeticException { checkCompatibility(getShrink(), operand.getShrink(), "shrink"); checkCompatibility(getStretch(), operand.getStretch(), "stretch"); return new MinOptMax(min - operand.min, opt - operand.opt, max - operand.max); @@ -194,58 +192,54 @@ public final class MinOptMax implements Serializable { } /** - * Returns an instance with the given value added to the minimal value. + * Do not use, backwards compatibility only. Returns an instance with the + * given value added to the minimal value. * * @param minOperand the minimal value to be added. * @return an instance with the given value added to the minimal value. - * @throws IllegalArgumentException if <code>min + minOperand > opt || max < opt</code>. + * @throws IllegalArgumentException if + * <code>min + minOperand > opt || max < opt</code>. */ - // [GA] remove deprecation - no alternative specified - // @deprecated Do not use! It's only for backwards compatibility. - public MinOptMax plusMin(int minOperand) - throws IllegalArgumentException { + public MinOptMax plusMin(int minOperand) throws IllegalArgumentException { return getInstance(min + minOperand, opt, max); } /** - * Returns an instance with the given value subtracted to the minimal value. + * Do not use, backwards compatibility only. Returns an instance with the + * given value subtracted to the minimal value. * * @param minOperand the minimal value to be subtracted. * @return an instance with the given value subtracted to the minimal value. - * @throws IllegalArgumentException if <code>min - minOperand > opt || max < opt</code>. + * @throws IllegalArgumentException if + * <code>min - minOperand > opt || max < opt</code>. */ - // [GA] remove deprecation - no alternative specified - // @deprecated Do not use! It's only for backwards compatibility. - public MinOptMax minusMin(int minOperand) - throws IllegalArgumentException { + public MinOptMax minusMin(int minOperand) throws IllegalArgumentException { return getInstance(min - minOperand, opt, max); } /** - * Returns an instance with the given value added to the maximal value. + * Do not use, backwards compatibility only. Returns an instance with the + * given value added to the maximal value. * * @param maxOperand the maximal value to be added. * @return an instance with the given value added to the maximal value. - * @throws IllegalArgumentException if <code>min > opt || max < opt + maxOperand</code>. + * @throws IllegalArgumentException if + * <code>min > opt || max < opt + maxOperand</code>. */ - // [GA] remove deprecation - no alternative specified - // @deprecated Do not use! It's only for backwards compatibility. - public MinOptMax plusMax(int maxOperand) - throws IllegalArgumentException { + public MinOptMax plusMax(int maxOperand) throws IllegalArgumentException { return getInstance(min, opt, max + maxOperand); } /** - * Returns an instance with the given value subtracted to the maximal value. + * Do not use, backwards compatibility only. Returns an instance with the + * given value subtracted to the maximal value. * * @param maxOperand the maximal value to be subtracted. * @return an instance with the given value subtracted to the maximal value. - * @throws IllegalArgumentException if <code>min > opt || max < opt - maxOperand</code>. + * @throws IllegalArgumentException if + * <code>min > opt || max < opt - maxOperand</code>. */ - // [GA] remove deprecation - no alternative specified - // @deprecated Do not use! It's only for backwards compatibility. - public MinOptMax minusMax(int maxOperand) - throws IllegalArgumentException { + public MinOptMax minusMax(int maxOperand) throws IllegalArgumentException { return getInstance(min, opt, max - maxOperand); } @@ -256,8 +250,7 @@ public final class MinOptMax implements Serializable { * @return the product of this <code>MinOptMax</code> and the given factor * @throws IllegalArgumentException if the factor is negative */ - public MinOptMax mult(int factor) - throws IllegalArgumentException { + public MinOptMax mult(int factor) throws IllegalArgumentException { if (factor < 0) { throw new IllegalArgumentException("factor < 0; was: " + factor); } else if (factor == 1) { diff --git a/status.xml b/status.xml index 1f0916753..b651ddf89 100644 --- a/status.xml +++ b/status.xml @@ -58,6 +58,18 @@ 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="JM" type="add" fixes-bug="42600" due-to="Maximilian Aster"> + Added some support for break-before/-after for RTF output. + </action> + <action context="Renderers" dev="JM" type="add" fixes-bug="49379" due-to="Peter Hancock"> + Added ability to embed an external AFP page segment resource file (AFP output only). + </action> + <action context="Renderers" dev="JM" type="fix" fixes-bug="46360" due-to="Alexis Giotis"> + Fixed a multi-threading issue when rendering SVG. + </action> + <action context="Layout" dev="JM" type="fix" fixes-bug="49885"> + Fixed retrieval of available BPD for cases spanning columns and multiple pages with differing page masters. + </action> <action context="Renderers" dev="VH" type="remove"> Removed old Renderer implementations for those output formats that have a version based on the new DocumentHandler architecture available (AFP, PCL, PDF, PS). @@ -395,6 +407,9 @@ Fixed a problem where the BPD or a block area could be wrong if there is a nested, absolutely positioned area (for example a block-container). </action> + <action context="Code" dev="VH" type="fix" fixes-bug="45971" due-to="Tow Browder"> + Improved the behaviour of the command line interface. + </action> <action context="Layout" dev="AD" type="fix" fixes-bug="40798"> Bugzilla 40798: A conditional-page-master-reference with page-position="last" qualifies for a first page, if it is also the last. Additionally: also added support for diff --git a/test/java/org/apache/fop/fonts/EncodingModeTest.java b/test/java/org/apache/fop/fonts/EncodingModeTest.java new file mode 100644 index 000000000..4e81c46d6 --- /dev/null +++ b/test/java/org/apache/fop/fonts/EncodingModeTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fonts; + +import junit.framework.TestCase; + +public class EncodingModeTest extends TestCase { + public void testGetName() { + assertEquals("auto", EncodingMode.AUTO.getName()); + assertEquals("single-byte", EncodingMode.SINGLE_BYTE.getName()); + assertEquals("cid", EncodingMode.CID.getName()); + } + + public void testGetValue() { + assertEquals(EncodingMode.AUTO, EncodingMode.getEncodingMode("auto")); + assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getEncodingMode("single-byte")); + assertEquals(EncodingMode.CID, EncodingMode.getEncodingMode("cid")); + } +} diff --git a/test/layoutengine/hyphenation-testcases/hyphenation-pattern_fallback.xml b/test/layoutengine/hyphenation-testcases/hyphenation-pattern_fallback.xml new file mode 100644 index 000000000..3af71a3ea --- /dev/null +++ b/test/layoutengine/hyphenation-testcases/hyphenation-pattern_fallback.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- $Id$ --> +<testcase> + <info> + <p> + Checks fallback from hyphenation pattern for (lang,country) to hyphenation pattern for (lang) + </p> + </info> + <fo> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> + + <fo:layout-master-set> + <fo:simple-page-master master-name="simple" + page-height="29.7cm" + page-width="21cm" + margin-top="1cm" + margin-bottom="2cm" + margin-left="7cm" + margin-right="3cm"> + <fo:region-body margin-top="1.5cm"/> + </fo:simple-page-master> + </fo:layout-master-set> + <!-- end: defines page layout --> + + + <fo:page-sequence master-reference="simple"> + + <fo:flow flow-name="xsl-region-body" + text-align="justify"> + + <fo:block font-size="12pt" + font-family="sans-serif" + background-color="blue" + color="white" + text-align="start"> + language="en",country="IN" + </fo:block> + <fo:block font-size="12pt" + font-family="sans-serif" + space-after="3pt" + language="en" + country="IN" + hyphenate="true" + hyphenation-ladder-count="no-limit" + > + This document has been reviewed by W3C Members and other interested parties and has been endorsed by the Director as a + W3C Recommendation. It is a stable document and may be used as reference material or cited as a normative reference from + another document. W3C's role in making the Recommendation is to draw attention to the specification and to promote its + widespread deployment. This enhances the functionality and interoperability of the Web. + </fo:block> + + <fo:block font-size="12pt" + font-family="sans-serif" + background-color="blue" + color="white" + text-align="start"> + language="em" + </fo:block> + <fo:block font-size="12pt" + font-family="sans-serif" + space-after="3pt" + language="em" + hyphenate="true" + hyphenation-ladder-count="no-limit" + > + This document has been reviewed by W3C Members and other interested parties and has been endorsed by the Director as a + W3C Recommendation. It is a stable document and may be used as reference material or cited as a normative reference from + another document. W3C's role in making the Recommendation is to draw attention to the specification and to promote its + widespread deployment. This enhances the functionality and interoperability of the Web. + </fo:block> + + <fo:block font-size="12pt" + font-family="sans-serif" + background-color="blue" + color="white" + text-align="start"> + language="em",country="IN" + </fo:block> + <fo:block font-size="12pt" + font-family="sans-serif" + space-after="3pt" + language="em" + country="IN" + hyphenate="true" + hyphenation-ladder-count="no-limit" + > + This document has been reviewed by W3C Members and other interested parties and has been endorsed by the Director as a + W3C Recommendation. It is a stable document and may be used as reference material or cited as a normative reference from + another document. W3C's role in making the Recommendation is to draw attention to the specification and to promote its + widespread deployment. This enhances the functionality and interoperability of the Web. + </fo:block> + + </fo:flow> + </fo:page-sequence> +</fo:root> + + </fo> + + <checks> + <eval expected="8" xpath="count(//pageViewport[1]//flow/block[2]/lineArea)"/> + <eval expected="Di-" xpath="//pageViewport[1]//flow/block[2]/lineArea[2]/text/word[10]"/> + <eval expected="norma-" xpath="//pageViewport[1]//flow/block[2]/lineArea[4]/text/word[12]"/> + <eval expected="mak-" xpath="//pageViewport[1]//flow/block[2]/lineArea[5]/text/word[9]"/> + <eval expected="specifi-" xpath="//pageViewport[1]//flow/block[2]/lineArea[6]/text/word[10]"/> + <eval expected="en-" xpath="//pageViewport[1]//flow/block[2]/lineArea[7]/text/word[9]"/> + + <eval expected="9" xpath="count(//pageViewport[1]//flow/block[4]/lineArea)"/> + <eval expected="by" xpath="//pageViewport[1]//flow/block[4]/lineArea[2]/text/word[9]"/> + <eval expected="cited" xpath="//pageViewport[1]//flow/block[4]/lineArea[4]/text/word[10]"/> + <eval expected="document." xpath="//pageViewport[1]//flow/block[4]/lineArea[5]/text/word[7]"/> + <eval expected="to" xpath="//pageViewport[1]//flow/block[4]/lineArea[6]/text/word[10]"/> + <eval expected="deployment." xpath="//pageViewport[1]//flow/block[4]/lineArea[7]/text/word[8]"/> + + <eval expected="9" xpath="count(//pageViewport[1]//flow/block[6]/lineArea)"/> + <eval expected="by" xpath="//pageViewport[1]//flow/block[6]/lineArea[2]/text/word[9]"/> + <eval expected="cited" xpath="//pageViewport[1]//flow/block[6]/lineArea[4]/text/word[10]"/> + <eval expected="document." xpath="//pageViewport[1]//flow/block[6]/lineArea[5]/text/word[7]"/> + <eval expected="to" xpath="//pageViewport[1]//flow/block[6]/lineArea[6]/text/word[10]"/> + <eval expected="deployment." xpath="//pageViewport[1]//flow/block[6]/lineArea[7]/text/word[8]"/> + </checks> +</testcase> diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml index 956563e20..1b10d722c 100644 --- a/test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_table-after-break.xml @@ -56,13 +56,19 @@ <fo:table table-layout="fixed" width="100%"> <fo:table-body> <fo:table-row> - <fo:table-cell> - <fo:block>After the table 1</fo:block> + <fo:table-cell border="1pt solid black"> + <fo:block>Cell 1.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black"> + <fo:block>Cell 1.2</fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> - <fo:table-cell> - <fo:block>After the table 2</fo:block> + <fo:table-cell border="1pt solid black"> + <fo:block>Cell 2.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black"> + <fo:block>Cell 2.2</fo:block> </fo:table-cell> </fo:table-row> </fo:table-body> @@ -74,7 +80,12 @@ <checks> <eval expected="Block before the page break." xpath="//pageViewport[1]//flow/block[2]//text"/> <eval expected="Block after the page break." xpath="//pageViewport[2]//flow/block[1]//text"/> - <eval expected="After the table 1" xpath="//pageViewport[2]//flow/block[2]/block[1]//text"/> - <eval expected="After the table 2" xpath="//pageViewport[2]//flow/block[2]/block[2]//text"/> + <eval expected="400000" xpath="//pageViewport[2]//flow/block[2]/@ipd"/> + <eval expected="199000" xpath="//pageViewport[2]//flow/block[2]/block[1]/@ipd"/> + <eval expected="Cell 1.1" xpath="//pageViewport[2]//flow/block[2]/block[1]//text"/> + <eval expected="199000" xpath="//pageViewport[2]//flow/block[2]/block[2]/@ipd"/> + <eval expected="Cell 1.2" xpath="//pageViewport[2]//flow/block[2]/block[2]//text"/> + <eval expected="Cell 2.1" xpath="//pageViewport[2]//flow/block[2]/block[3]//text"/> + <eval expected="Cell 2.2" xpath="//pageViewport[2]//flow/block[2]/block[4]//text"/> </checks> </testcase> diff --git a/test/layoutengine/standard-testcases/region-body_column-count_span_4.xml b/test/layoutengine/standard-testcases/region-body_column-count_span_4.xml new file mode 100644 index 000000000..8bc4652ce --- /dev/null +++ b/test/layoutengine/standard-testcases/region-body_column-count_span_4.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- $Id$ --> +<testcase> + <info> + <p> + This test checks multi-column documents. Check that spanned section that are broken over + to multiple pages still respect the span setting. This particular test makes sure that pages with different region-body + height are handled properly. + </p> + </info> + <fo> + <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> + <fo:layout-master-set> + <fo:simple-page-master master-name="odd-page" page-height="3.5cm" page-width="4cm" margin="0.45cm"> + <fo:region-body margin-top="12pt * 1.2 * 2" column-count="2" background-color="yellow"/> + </fo:simple-page-master> + <fo:simple-page-master master-name="even-page" page-height="3.5cm" page-width="4cm" margin="0.45cm"> + <fo:region-body column-count="2" background-color="orange"/> + </fo:simple-page-master> + <fo:page-sequence-master master-name="master"> + <fo:repeatable-page-master-alternatives> + <fo:conditional-page-master-reference master-reference="odd-page" odd-or-even="odd" blank-or-not-blank="not-blank"/> + <fo:conditional-page-master-reference master-reference="even-page" odd-or-even="even" blank-or-not-blank="not-blank"/> + </fo:repeatable-page-master-alternatives> + </fo:page-sequence-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="master"> + <fo:flow flow-name="xsl-region-body"> + <fo:block span="all"> + <fo:block>Line 1</fo:block> + <fo:block>Line 2</fo:block> + <fo:block>Line 3</fo:block> + <fo:block>Line 4</fo:block> + <fo:block>Line 5</fo:block> + <fo:block>Line 6</fo:block> + <fo:block>Line 7</fo:block> + <fo:block>Line 8</fo:block> + <fo:block>Line 9</fo:block> + <fo:block>Line 10</fo:block> + <fo:block>Line 11</fo:block> + <fo:block>Line 12</fo:block> + <fo:block>Line 13</fo:block> + <fo:block>Line 14</fo:block> + <fo:block>Line 15</fo:block> + <fo:block>Line 16</fo:block> + <fo:block>Line 17</fo:block> + <fo:block>Line 18</fo:block> + <fo:block>Line 19</fo:block> + <fo:block>Line 20</fo:block> + <fo:block>Line 21</fo:block> + <fo:block>Line 22</fo:block> + </fo:block> + </fo:flow> + </fo:page-sequence> + </fo:root> + </fo> + <checks> + <eval expected="6" xpath="count(//pageViewport)"/> + + <eval expected="3" xpath="count(//pageViewport[@nr=1]//lineArea)"/> + <eval expected="5" xpath="count(//pageViewport[@nr=2]//lineArea)"/> + <eval expected="3" xpath="count(//pageViewport[@nr=3]//lineArea)"/> + <eval expected="5" xpath="count(//pageViewport[@nr=4]//lineArea)"/> + <eval expected="3" xpath="count(//pageViewport[@nr=5]//lineArea)"/> + <eval expected="3" xpath="count(//pageViewport[@nr=6]//lineArea)"/> + + </checks> +</testcase> diff --git a/xmlgraphics-fop-pom-template.pom b/xmlgraphics-fop-pom-template.pom index f4389cb96..efa30f67f 100644 --- a/xmlgraphics-fop-pom-template.pom +++ b/xmlgraphics-fop-pom-template.pom @@ -19,7 +19,7 @@ <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
-http://maven.apache.org/maven-v4_0_0.xsd">
+http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
@@ -59,18 +59,23 @@ http://maven.apache.org/maven-v4_0_0.xsd"> <scm>
<connection>scm:svn:http://svn.apache.org/repos/asf/xmlgraphics/fop/trunk</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk</developerConnection>
- <url>http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/?root=Apache-SVN</url>
+ <url>http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/?root=Apache-SVN</url>
</scm>
<organization>
<name>Apache Software Foundation</name>
<url>http://www.apache.org/</url>
</organization>
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>7</version>
+ </parent>
<dependencies>
<!-- XML Graphics -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
- <version>1.3</version>
+ <version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
|