aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/render/pdf
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2003-03-11 13:05:43 +0000
committerJeremias Maerki <jeremias@apache.org>2003-03-11 13:05:43 +0000
commit1e5d512c216d329effa693b91ef64652945def6a (patch)
tree5bd3521ee8121eade7bf1909ceaf29cfc0263fd1 /src/java/org/apache/fop/render/pdf
parent73c824d39411bf11ad0c2f4e1c57cd9c484665f9 (diff)
downloadxmlgraphics-fop-1e5d512c216d329effa693b91ef64652945def6a.tar.gz
xmlgraphics-fop-1e5d512c216d329effa693b91ef64652945def6a.zip
Moved sources from src/org/** to src/java/org/**
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196061 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/render/pdf')
-rw-r--r--src/java/org/apache/fop/render/pdf/CTMHelper.java145
-rw-r--r--src/java/org/apache/fop/render/pdf/EmbedFontInfo.java112
-rw-r--r--src/java/org/apache/fop/render/pdf/FontReader.java321
-rw-r--r--src/java/org/apache/fop/render/pdf/FontSetup.java270
-rw-r--r--src/java/org/apache/fop/render/pdf/FontTriplet.java96
-rw-r--r--src/java/org/apache/fop/render/pdf/FopPDFImage.java307
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java1361
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFXMLHandler.java338
-rw-r--r--src/java/org/apache/fop/render/pdf/package.html6
9 files changed, 2956 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/render/pdf/CTMHelper.java b/src/java/org/apache/fop/render/pdf/CTMHelper.java
new file mode 100644
index 000000000..4a3103a01
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/CTMHelper.java
@@ -0,0 +1,145 @@
+/*
+ * $Id: CTMHelper.java,v 1.2 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+import org.apache.fop.area.CTM;
+
+/**
+ * CTMHelper converts FOP transformation matrixis to those
+ * suitable for use by the PDFRender. The e and f elements
+ * of the matrix will be divided by 1000 as FOP uses millipoints
+ * as it's default user space and PDF uses points.
+ *
+ * @see org.apache.fop.area.CTM
+ *
+ * @author <a href="kevin@rocketred.com>Kevin O'Neill</a>
+ */
+public final class CTMHelper {
+ /**
+ * <p>Converts the sourceMatrix to a string for use in the PDFRender cm operations.</p>
+ * <p>For example:
+ * <pre>
+ * org.apache.fop.area.CTM ctm =
+ * new org.apache.fop.area.CTM(1.0, 0.0, 0.0, 1.0, 1000.0, 1000.0);
+ * String pdfMatrix = org.apache.fop.render.pdf.CTMHelper.toPDFString(ctm);
+ * </pre>
+ * will return the string "<code>1.0 0.0 0.0 1.0 1.0 1.0</code>".
+ *
+ * @param sourceMatrix - The matrix to convert.
+ *
+ * @return a space seperated string containing the matrix elements.
+ */
+ public static String toPDFString(CTM sourceMatrix) {
+ if (null == sourceMatrix) {
+ throw new NullPointerException("sourceMatrix must not be null");
+ }
+
+ final double matrix[] = toPDFArray(sourceMatrix);
+
+ return matrix[0] + " " + matrix[1] + " "
+ + matrix[2] + " " + matrix[3] + " "
+ + matrix[4] + " " + matrix[5];
+ }
+
+ /**
+ * <p>Creates a new CTM based in the sourceMatrix.</p>
+ * <p>For example:
+ * <pre>
+ * org.apache.fop.area.CTM inCTM =
+ * new org.apache.fop.area.CTM(1.0, 0.0, 0.0, 1.0, 1000.0, 1000.0);
+ * org.apache.fop.area.CTM outCTM =
+ * org.apache.fop.render.pdf.CTMHelper.toPDFCTM(ctm);
+ * </pre>
+ * will return a new CTM where a == 1.0, b == 0.0, c == 0.0, d == 1.0, e == 1.0 and f == 1.0.
+ *
+ * @param sourceMatrix - The matrix to convert.
+ *
+ * @return a new converted matrix.
+ */
+ public static CTM toPDFCTM(CTM sourceMatrix) {
+ if (null == sourceMatrix) {
+ throw new NullPointerException("sourceMatrix must not be null");
+ }
+
+ final double matrix[] = toPDFArray(sourceMatrix);
+
+ return new CTM(matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5]);
+ }
+
+ /**
+ * <p>Creates an array of six doubles from the source CTM.</p>
+ * <p>For example:
+ * <pre>
+ * org.apache.fop.area.CTM inCTM =
+ * new org.apache.fop.area.CTM(1.0, 0.0, 0.0, 1.0, 1000.0, 1000.0);
+ * double matrix[] = org.apache.fop.render.pdf.CTMHelper.toPDFArray(ctm);
+ * </pre>
+ * will return a new array where matrix[0] == 1.0, matrix[1] == 0.0,
+ * matrix[2] == 0.0, matrix[3] == 1.0,
+ * matrix[4] == 1.0 and matrix[5] == 1.0.
+ *
+ * @param sourceMatrix - The matrix to convert.
+ * @return an array of doubles containing the converted matrix.
+ */
+ public static double[] toPDFArray(CTM sourceMatrix) {
+ if (null == sourceMatrix) {
+ throw new NullPointerException("sourceMatrix must not be null");
+ }
+
+ final double matrix[] = sourceMatrix.toArray();
+
+ return new double[]{matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4] / 1000.0, matrix[5] / 1000.0};
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/EmbedFontInfo.java b/src/java/org/apache/fop/render/pdf/EmbedFontInfo.java
new file mode 100644
index 000000000..a78dbd8e0
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/EmbedFontInfo.java
@@ -0,0 +1,112 @@
+/*
+ * $Id: EmbedFontInfo.java,v 1.4 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+import java.util.List;
+
+/**
+ * FontInfo contains meta information on fonts (where is the metrics file etc.)
+ */
+public class EmbedFontInfo {
+
+ private String metricsFile, embedFile;
+ private boolean kerning;
+ private List fontTriplets;
+
+ /**
+ * Main constructor
+ * @param metricsFile Path to the xml file containing font metrics
+ * @param kerning True if kerning should be enabled
+ * @param fontTriplets List of font triplets to associate with this font
+ * @param embedFile Path to the embeddable font file (may be null)
+ */
+ public EmbedFontInfo(String metricsFile, boolean kerning,
+ List fontTriplets, String embedFile) {
+ this.metricsFile = metricsFile;
+ this.embedFile = embedFile;
+ this.kerning = kerning;
+ this.fontTriplets = fontTriplets;
+ }
+
+ /**
+ * Returns the path to the metrics file
+ * @return the metrics file path
+ */
+ public String getMetricsFile() {
+ return metricsFile;
+ }
+
+ /**
+ * Returns the path to the embeddable font file
+ * @return the font file path
+ */
+ public String getEmbedFile() {
+ return embedFile;
+ }
+
+ /**
+ * Determines if kerning is enabled
+ * @return True if enabled
+ */
+ public boolean getKerning() {
+ return kerning;
+ }
+
+ /**
+ * Returns the list of font triplets associated with this font.
+ * @return List of font triplets
+ */
+ public List getFontTriplets() {
+ return fontTriplets;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/FontReader.java b/src/java/org/apache/fop/render/pdf/FontReader.java
new file mode 100644
index 000000000..375c4e56a
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/FontReader.java
@@ -0,0 +1,321 @@
+/*
+ * $Id: FontReader.java,v 1.8 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+//Java
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+
+//SAX
+import org.xml.sax.XMLReader;
+import org.xml.sax.SAXException;
+import org.xml.sax.Locator;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+//FOP
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fonts.BFEntry;
+import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontType;
+import org.apache.fop.fonts.MultiByteFont;
+import org.apache.fop.fonts.SingleByteFont;
+
+/**
+ * Class for reading a metric.xml file and creating a font object.
+ * Typical usage:
+ * <pre>
+ * FontReader reader = new FontReader(<path til metrics.xml>);
+ * reader.setFontEmbedPath(<path to a .ttf or .pfb file or null to diable embedding>);
+ * reader.useKerning(true);
+ * Font f = reader.getFont();
+ * </pre>
+ */
+public class FontReader extends DefaultHandler {
+
+ private Locator locator = null;
+ private boolean isCID = false;
+ private CustomFont returnFont = null;
+ private MultiByteFont multiFont = null;
+ private SingleByteFont singleFont = null;
+ private StringBuffer text = new StringBuffer();
+
+ private List cidWidths = null;
+ private int cidWidthIndex = 0;
+
+ private Map currentKerning = null;
+
+ private List bfranges = null;
+
+ private void createFont(String path) throws FOPException {
+ XMLReader parser = null;
+
+ try {
+ parser = javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+ } catch (Exception e) {
+ throw new FOPException(e);
+ }
+ if (parser == null) {
+ throw new FOPException("Unable to create SAX parser");
+ }
+
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
+ false);
+ } catch (SAXException e) {
+ throw new FOPException("You need a SAX parser which supports SAX version 2",
+ e);
+ }
+
+ parser.setContentHandler(this);
+
+ try {
+ parser.parse(path);
+ } catch (SAXException e) {
+ throw new FOPException(e);
+ } catch (IOException e) {
+ throw new FOPException(e);
+ }
+
+ }
+
+ /**
+ * Sets the path to embed a font. A null value disables font embedding.
+ * @param path URI for the embeddable file
+ */
+ public void setFontEmbedPath(String path) {
+ returnFont.setEmbedFileName(path);
+ }
+
+ /**
+ * Enable/disable use of kerning for the font
+ * @param enabled true to enable kerning, false to disable
+ */
+ public void setKerningEnabled(boolean enabled) {
+ returnFont.setKerningEnabled(enabled);
+ }
+
+
+ /**
+ * Get the generated font object
+ * @return the font
+ */
+ public Font getFont() {
+ return returnFont;
+ }
+
+ /**
+ * Construct a FontReader object from a path to a metric.xml file
+ * and read metric data
+ * @param path URI to the font metric file
+ * @throws FOPException if loading the font fails
+ */
+ public FontReader(String path) throws FOPException {
+ createFont(path);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#startDocument()
+ */
+ public void startDocument() {
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
+ */
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
+ */
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) {
+ if (localName.equals("font-metrics")) {
+ if ("TYPE0".equals(attributes.getValue("type"))) {
+ multiFont = new MultiByteFont();
+ returnFont = multiFont;
+ isCID = true;
+ } else if ("TRUETYPE".equals(attributes.getValue("type"))) {
+ singleFont = new SingleByteFont();
+ singleFont.setFontType(FontType.TRUETYPE);
+ returnFont = singleFont;
+ isCID = false;
+ } else {
+ singleFont = new SingleByteFont();
+ singleFont.setFontType(FontType.TYPE1);
+ returnFont = singleFont;
+ isCID = false;
+ }
+ } else if ("embed".equals(localName)) {
+ returnFont.setEmbedFileName(attributes.getValue("file"));
+ returnFont.setEmbedResourceName(attributes.getValue("class"));
+ } else if ("cid-widths".equals(localName)) {
+ cidWidthIndex = getInt(attributes.getValue("start-index"));
+ cidWidths = new java.util.ArrayList();
+ } else if ("kerning".equals(localName)) {
+ currentKerning = new java.util.HashMap();
+ returnFont.putKerningEntry(new Integer(attributes.getValue("kpx1")),
+ currentKerning);
+ } else if ("bfranges".equals(localName)) {
+ bfranges = new java.util.ArrayList();
+ } else if ("bf".equals(localName)) {
+ BFEntry entry = new BFEntry(getInt(attributes.getValue("us")),
+ getInt(attributes.getValue("ue")),
+ getInt(attributes.getValue("gi")));
+ bfranges.add(entry);
+ } else if ("wx".equals(localName)) {
+ cidWidths.add(new Integer(attributes.getValue("w")));
+ } else if ("widths".equals(localName)) {
+ //singleFont.width = new int[256];
+ } else if ("char".equals(localName)) {
+ try {
+ singleFont.setWidth(Integer.parseInt(attributes.getValue("idx")),
+ Integer.parseInt(attributes.getValue("wdt")));
+ } catch (NumberFormatException ne) {
+ System.out.println("Malformed width in metric file: "
+ + ne.getMessage());
+ }
+ } else if ("pair".equals(localName)) {
+ currentKerning.put(new Integer(attributes.getValue("kpx2")),
+ new Integer(attributes.getValue("kern")));
+ }
+ }
+
+ private int getInt(String str) {
+ int ret = 0;
+ try {
+ ret = Integer.parseInt(str);
+ } catch (Exception e) {
+ /**@todo log this exception */
+ }
+ return ret;
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+ */
+ public void endElement(String uri, String localName, String qName) {
+ if ("font-name".equals(localName)) {
+ returnFont.setFontName(text.toString());
+ } else if ("ttc-name".equals(localName) && isCID) {
+ multiFont.setTTCName(text.toString());
+ } else if ("cap-height".equals(localName)) {
+ returnFont.setCapHeight(getInt(text.toString()));
+ } else if ("x-height".equals(localName)) {
+ returnFont.setXHeight(getInt(text.toString()));
+ } else if ("ascender".equals(localName)) {
+ returnFont.setAscender(getInt(text.toString()));
+ } else if ("descender".equals(localName)) {
+ returnFont.setDescender(getInt(text.toString()));
+ } else if ("left".equals(localName)) {
+ int[] bbox = returnFont.getFontBBox();
+ bbox[0] = getInt(text.toString());
+ returnFont.setFontBBox(bbox);
+ } else if ("bottom".equals(localName)) {
+ int[] bbox = returnFont.getFontBBox();
+ bbox[1] = getInt(text.toString());
+ returnFont.setFontBBox(bbox);
+ } else if ("right".equals(localName)) {
+ int[] bbox = returnFont.getFontBBox();
+ bbox[2] = getInt(text.toString());
+ returnFont.setFontBBox(bbox);
+ } else if ("top".equals(localName)) {
+ int[] bbox = returnFont.getFontBBox();
+ bbox[3] = getInt(text.toString());
+ returnFont.setFontBBox(bbox);
+ } else if ("first-char".equals(localName)) {
+ returnFont.setFirstChar(getInt(text.toString()));
+ } else if ("last-char".equals(localName)) {
+ returnFont.setLastChar(getInt(text.toString()));
+ } else if ("flags".equals(localName)) {
+ returnFont.setFlags(getInt(text.toString()));
+ } else if ("stemv".equals(localName)) {
+ returnFont.setStemV(getInt(text.toString()));
+ } else if ("italic-angle".equals(localName)) {
+ returnFont.setItalicAngle(getInt(text.toString()));
+ } else if ("missing-width".equals(localName)) {
+ returnFont.setMissingWidth(getInt(text.toString()));
+ } else if ("cid-type".equals(localName)) {
+ multiFont.setCIDType(CIDFontType.byName(text.toString()));
+ } else if ("default-width".equals(localName)) {
+ multiFont.setDefaultWidth(getInt(text.toString()));
+ } else if ("cid-widths".equals(localName)) {
+ int[] wds = new int[cidWidths.size()];
+ int j = 0;
+ for (int count = 0; count < cidWidths.size(); count++) {
+ Integer i = (Integer)cidWidths.get(count);
+ wds[j++] = i.intValue();
+ }
+
+ multiFont.addCIDWidthEntry(cidWidthIndex, wds);
+ multiFont.setWidthArray(wds);
+
+ } else if ("bfranges".equals(localName)) {
+ multiFont.setBFEntries((BFEntry[])bfranges.toArray(new BFEntry[0]));
+ }
+ text.setLength(0); //Reset text buffer (see characters())
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] ch, int start, int length) {
+ text.append(ch, start, length);
+ }
+
+}
+
+
diff --git a/src/java/org/apache/fop/render/pdf/FontSetup.java b/src/java/org/apache/fop/render/pdf/FontSetup.java
new file mode 100644
index 000000000..684fa3652
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/FontSetup.java
@@ -0,0 +1,270 @@
+/*
+ * $Id: FontSetup.java,v 1.22 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+// FOP
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontDescriptor;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.layout.FontInfo;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFResources;
+// FOP (base 14 fonts)
+import org.apache.fop.fonts.base14.Helvetica;
+import org.apache.fop.fonts.base14.HelveticaBold;
+import org.apache.fop.fonts.base14.HelveticaOblique;
+import org.apache.fop.fonts.base14.HelveticaBoldOblique;
+import org.apache.fop.fonts.base14.TimesRoman;
+import org.apache.fop.fonts.base14.TimesBold;
+import org.apache.fop.fonts.base14.TimesItalic;
+import org.apache.fop.fonts.base14.TimesBoldItalic;
+import org.apache.fop.fonts.base14.Courier;
+import org.apache.fop.fonts.base14.CourierBold;
+import org.apache.fop.fonts.base14.CourierOblique;
+import org.apache.fop.fonts.base14.CourierBoldOblique;
+import org.apache.fop.fonts.base14.Symbol;
+import org.apache.fop.fonts.base14.ZapfDingbats;
+
+// Java
+import java.util.Map;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * sets up the PDF fonts.
+ *
+ * Assigns the font (with metrics) to internal names like "F1" and
+ * assigns family-style-weight triplets to the fonts
+ */
+public class FontSetup {
+
+ /**
+ * Sets up the font info object.
+ *
+ * Adds metrics for basic fonts and useful family-style-weight
+ * triplets for lookup.
+ *
+ * @param fontInfo the font info object to set up
+ * @param embedList ???
+ */
+ public static void setup(FontInfo fontInfo, List embedList) {
+
+ fontInfo.addMetrics("F1", new Helvetica());
+ fontInfo.addMetrics("F2", new HelveticaOblique());
+ fontInfo.addMetrics("F3", new HelveticaBold());
+ fontInfo.addMetrics("F4", new HelveticaBoldOblique());
+ fontInfo.addMetrics("F5", new TimesRoman());
+ fontInfo.addMetrics("F6", new TimesItalic());
+ fontInfo.addMetrics("F7", new TimesBold());
+ fontInfo.addMetrics("F8", new TimesBoldItalic());
+ fontInfo.addMetrics("F9", new Courier());
+ fontInfo.addMetrics("F10", new CourierOblique());
+ fontInfo.addMetrics("F11", new CourierBold());
+ fontInfo.addMetrics("F12", new CourierBoldOblique());
+ fontInfo.addMetrics("F13", new Symbol());
+ fontInfo.addMetrics("F14", new ZapfDingbats());
+
+ // Custom type 1 fonts step 1/2
+ // fontInfo.addMetrics("F15", new OMEP());
+ // fontInfo.addMetrics("F16", new GaramondLightCondensed());
+ // fontInfo.addMetrics("F17", new BauerBodoniBoldItalic());
+
+ /* any is treated as serif */
+ fontInfo.addFontProperties("F5", "any", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "any", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "any", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F7", "any", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "any", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "any", "oblique", FontInfo.BOLD);
+
+ fontInfo.addFontProperties("F1", "sans-serif", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F2", "sans-serif", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F2", "sans-serif", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F3", "sans-serif", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F4", "sans-serif", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F4", "sans-serif", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F5", "serif", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "serif", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "serif", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F7", "serif", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "serif", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "serif", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F9", "monospace", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F10", "monospace", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F10", "monospace", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F11", "monospace", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F12", "monospace", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F12", "monospace", "italic", FontInfo.BOLD);
+
+ fontInfo.addFontProperties("F1", "Helvetica", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F2", "Helvetica", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F2", "Helvetica", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F3", "Helvetica", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F4", "Helvetica", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F4", "Helvetica", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F5", "Times", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F7", "Times", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F9", "Courier", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F10", "Courier", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F10", "Courier", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F11", "Courier", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F12", "Courier", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F12", "Courier", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F13", "Symbol", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F14", "ZapfDingbats", "normal", FontInfo.NORMAL);
+
+ // Custom type 1 fonts step 2/2
+ // fontInfo.addFontProperties("F15", "OMEP", "normal", FontInfo.NORMAL);
+ // fontInfo.addFontProperties("F16", "Garamond-LightCondensed", "normal", FontInfo.NORMAL);
+ // fontInfo.addFontProperties("F17", "BauerBodoni", "italic", FontInfo.BOLD);
+
+ /* for compatibility with PassiveTex */
+ fontInfo.addFontProperties("F5", "Times-Roman", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times-Roman", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times-Roman", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F7", "Times-Roman", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times-Roman", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times-Roman", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F5", "Times Roman", "normal", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times Roman", "oblique", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F6", "Times Roman", "italic", FontInfo.NORMAL);
+ fontInfo.addFontProperties("F7", "Times Roman", "normal", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times Roman", "oblique", FontInfo.BOLD);
+ fontInfo.addFontProperties("F8", "Times Roman", "italic", FontInfo.BOLD);
+ fontInfo.addFontProperties("F9", "Computer-Modern-Typewriter",
+ "normal", FontInfo.NORMAL);
+
+ /* Add configured fonts */
+ addConfiguredFonts(fontInfo, embedList, 15);
+ }
+
+ /**
+ * Add fonts from configuration file starting with
+ * internalnames F<num>
+ * @param fontInfo the font info object to set up
+ * @param fontInfos ???
+ * @param num starting index for internal font numbering
+ */
+ public static void addConfiguredFonts(FontInfo fontInfo, List fontInfos, int num) {
+ if (fontInfos == null) {
+ return; //No fonts to process
+ }
+
+ String internalName = null;
+ //FontReader reader = null;
+
+ for (int i = 0; i < fontInfos.size(); i++) {
+ EmbedFontInfo configFontInfo = (EmbedFontInfo)fontInfos.get(i);
+
+ String metricsFile = configFontInfo.getMetricsFile();
+ if (metricsFile != null) {
+ internalName = "F" + num;
+ num++;
+ /*
+ reader = new FontReader(metricsFile);
+ reader.useKerning(configFontInfo.getKerning());
+ reader.setFontEmbedPath(configFontInfo.getEmbedFile());
+ fontInfo.addMetrics(internalName, reader.getFont());
+ */
+ LazyFont font = new LazyFont(configFontInfo.getEmbedFile(),
+ metricsFile,
+ configFontInfo.getKerning());
+ fontInfo.addMetrics(internalName, font);
+
+ List triplets = configFontInfo.getFontTriplets();
+ for (int c = 0; c < triplets.size(); c++) {
+ FontTriplet triplet = (FontTriplet)triplets.get(c);
+
+ int weight = 400;
+ try {
+ weight = Integer.parseInt(triplet.getWeight());
+ weight = ((int)weight / 100) * 100;
+ weight = Math.min(weight, 100);
+ weight = Math.max(weight, 900);
+ } catch (NumberFormatException nfe) {
+ /**@todo log this exception */
+ }
+ fontInfo.addFontProperties(internalName,
+ triplet.getName(),
+ triplet.getStyle(),
+ weight);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add the fonts in the font info to the PDF document
+ *
+ * @param doc PDF document to add fonts to
+ * @param resources PDFResources object to attach the font to
+ * @param fontInfo font info object to get font information from
+ */
+ public static void addToResources(PDFDocument doc, PDFResources resources, FontInfo fontInfo) {
+ Map fonts = fontInfo.getUsedFonts();
+ Iterator e = fonts.keySet().iterator();
+ while (e.hasNext()) {
+ String f = (String)e.next();
+ Font font = (Font)fonts.get(f);
+ FontDescriptor desc = null;
+ if (font instanceof FontDescriptor) {
+ desc = (FontDescriptor)font;
+ }
+ resources.addFont(doc.makeFont(f, font.getFontName(),
+ font.getEncoding(), font, desc));
+ }
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/FontTriplet.java b/src/java/org/apache/fop/render/pdf/FontTriplet.java
new file mode 100644
index 000000000..5faeee887
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/FontTriplet.java
@@ -0,0 +1,96 @@
+/*
+ * $Id: FontTriplet.java,v 1.2 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+/**
+ * FontTriplet contains information on name, weight, style of one font
+ */
+public class FontTriplet {
+
+ private String name, weight, style;
+
+ /**
+ * Creates a new font triplet.
+ * @param name font name
+ * @param weight font weight (normal, bold etc.)
+ * @param style font style (normal, italic etc.)
+ */
+ public FontTriplet(String name, String weight, String style) {
+ this.name = name;
+ this.weight = weight;
+ this.style = style;
+ }
+
+ /**
+ * Returns the font name.
+ * @return the font name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the font weight.
+ * @return the font weight
+ */
+ public String getWeight() {
+ return weight;
+ }
+
+ /**
+ * Returns the font style.
+ * @return the font style
+ */
+ public String getStyle() {
+ return style;
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/FopPDFImage.java b/src/java/org/apache/fop/render/pdf/FopPDFImage.java
new file mode 100644
index 000000000..4021a26ad
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/FopPDFImage.java
@@ -0,0 +1,307 @@
+/*
+ * $Id: FopPDFImage.java,v 1.7 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFFilter;
+import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.DCTFilter;
+import org.apache.fop.pdf.PDFColorSpace;
+
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.JpegImage;
+import org.apache.fop.image.EPSImage;
+
+import java.io.IOException;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.util.Map;
+
+/**
+ * PDFImage implementation for the PDF renderer.
+ */
+public class FopPDFImage implements PDFImage {
+
+ private FopImage fopImage;
+ private PDFICCStream pdfICCStream = null;
+ private PDFFilter pdfFilter = null;
+ private String maskRef;
+ private String softMaskRef;
+ private boolean isPS = false;
+ private Map filters;
+ private String key;
+
+ /**
+ * Creates a new PDFImage from a FopImage
+ * @param image Image
+ * @param key XObject key
+ */
+ public FopPDFImage(FopImage image, String key) {
+ fopImage = image;
+ this.key = key;
+ isPS = (fopImage instanceof EPSImage);
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getKey()
+ */
+ public String getKey() {
+ // key to look up XObject
+ return this.key;
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#setup(PDFDocument)
+ */
+ public void setup(PDFDocument doc) {
+ filters = doc.getFilterMap();
+ if ("image/jpeg".equals(fopImage.getMimeType())) {
+ pdfFilter = new DCTFilter();
+ pdfFilter.setApplied(true);
+
+ JpegImage jpegimage = (JpegImage) fopImage;
+ ICC_Profile prof = jpegimage.getICCProfile();
+ PDFColorSpace pdfCS = toPDFColorSpace(jpegimage.getColorSpace());
+ if (prof != null) {
+ pdfICCStream = doc.makePDFICCStream();
+ pdfICCStream.setColorSpace(prof, pdfCS);
+ pdfICCStream.addDefaultFilters(filters, PDFStream.CONTENT_FILTER);
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#isPS()
+ */
+ public boolean isPS() {
+ return isPS;
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getWidth()
+ */
+ public int getWidth() {
+ return fopImage.getWidth();
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getHeight()
+ */
+ public int getHeight() {
+ return fopImage.getHeight();
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getColorSpace()
+ */
+ public PDFColorSpace getColorSpace() {
+ // DeviceGray, DeviceRGB, or DeviceCMYK
+ return toPDFColorSpace(fopImage.getColorSpace());
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getBitsPerPixel()
+ */
+ public int getBitsPerPixel() {
+ return fopImage.getBitsPerPixel();
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#isTransparent()
+ */
+ public boolean isTransparent() {
+ return fopImage.isTransparent();
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getTransparentColor()
+ */
+ public PDFColor getTransparentColor() {
+ return fopImage.getTransparentColor();
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getMask()
+ */
+ public String getMask() {
+ return maskRef;
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getSoftMask()
+ */
+ public String getSoftMask() {
+ return softMaskRef;
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getDataStream()
+ */
+ public PDFStream getDataStream() throws IOException {
+ if (isPS) {
+ return getPSDataStream();
+ } else {
+ // delegate the stream work to PDFStream
+ PDFStream imgStream = new PDFStream(0);
+
+ imgStream.setData(fopImage.getBitmaps());
+
+ /*
+ * Added by Eric Dalquist
+ * If the DCT filter hasn't been added to the object we add it here
+ */
+ if (pdfFilter != null) {
+ imgStream.addFilter(pdfFilter);
+ }
+
+ imgStream.addDefaultFilters(filters, PDFStream.IMAGE_FILTER);
+ return imgStream;
+ }
+ }
+
+ /**
+ * Returns a PDFStream for an EPS image.
+ * @return PDFStream the newly creates PDFStream
+ * @throws IOException in case of an I/O problem
+ */
+ protected PDFStream getPSDataStream() throws IOException {
+ int length = 0;
+ int i = 0;
+ EPSImage epsImage = (EPSImage) fopImage;
+ int[] bbox = epsImage.getBBox();
+ int bboxw = bbox[2] - bbox[0];
+ int bboxh = bbox[3] - bbox[1];
+
+ // delegate the stream work to PDFStream
+ PDFStream imgStream = new PDFStream(0);
+
+ StringBuffer preamble = new StringBuffer();
+ preamble.append("%%BeginDocument: " + epsImage.getDocName() + "\n");
+
+ preamble.append("userdict begin % Push userdict on dict stack\n");
+ preamble.append("/PreEPS_state save def\n");
+ preamble.append("/dict_stack countdictstack def\n");
+ preamble.append("/ops_count count 1 sub def\n");
+ preamble.append("/showpage {} def\n");
+
+
+ preamble.append((double)(1f / (double) bboxw) + " "
+ + (double)(1f / (double) bboxh) + " scale\n");
+ preamble.append(-bbox[0] + " " + (-bbox[1]) + " translate\n");
+ preamble.append(bbox[0] + " " + bbox[1] + " "
+ + bboxw + " " + bboxh + " rectclip\n");
+ preamble.append("newpath\n");
+
+ StringBuffer post = new StringBuffer();
+ post.append("%%EndDocument\n");
+ post.append("count ops_count sub {pop} repeat\n");
+ post.append("countdictstack dict_stack sub {end} repeat\n");
+ post.append("PreEPS_state restore\n");
+ post.append("end % userdict\n");
+
+ byte[] preBytes = preamble.toString().getBytes();
+ byte[] postBytes = post.toString().getBytes();
+ byte[] epsBytes = ((EPSImage)fopImage).getEPSImage();
+ int epsLength = epsBytes.length;
+ byte[] imgData = new byte[preBytes.length
+ + postBytes.length
+ + epsLength];
+
+ System.arraycopy (preBytes, 0, imgData, 0, preBytes.length);
+ System.arraycopy (epsBytes, 0, imgData,
+ preBytes.length, epsBytes.length);
+ System.arraycopy (postBytes, 0, imgData,
+ preBytes.length + epsBytes.length,
+ postBytes.length);
+
+
+ imgStream.setData(imgData);
+ imgStream.addDefaultFilters(filters, PDFStream.CONTENT_FILTER);
+
+ return imgStream;
+ }
+
+ /**
+ * @see org.apache.fop.pdf.PDFImage#getICCStream()
+ */
+ public PDFICCStream getICCStream() {
+ return pdfICCStream;
+ }
+
+ /**
+ * Converts a ColorSpace object to a PDFColorSpace object.
+ * @param cs ColorSpace instance
+ * @return PDFColorSpace new converted object
+ */
+ public static PDFColorSpace toPDFColorSpace(ColorSpace cs) {
+ if (cs == null) {
+ return null;
+ }
+
+ PDFColorSpace pdfCS = new PDFColorSpace(0);
+ switch(cs.getType()) {
+ case ColorSpace.TYPE_CMYK:
+ pdfCS.setColorSpace(PDFColorSpace.DEVICE_CMYK);
+ break;
+ case ColorSpace.TYPE_RGB:
+ pdfCS.setColorSpace(PDFColorSpace.DEVICE_RGB);
+ break;
+ case ColorSpace.TYPE_GRAY:
+ pdfCS.setColorSpace(PDFColorSpace.DEVICE_GRAY);
+ break;
+ }
+ return pdfCS;
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
new file mode 100644
index 000000000..be3734964
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -0,0 +1,1361 @@
+/*
+ * $Id: PDFRenderer.java,v 1.137 2003/03/05 20:38:27 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+// Java
+import java.io.IOException;
+import java.io.OutputStream;
+import java.awt.Color;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.AffineTransform;
+import java.util.Map;
+import java.util.List;
+
+// XML
+import org.w3c.dom.Document;
+
+// Avalon
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+
+// FOP
+import org.apache.fop.render.PrintRenderer;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.fo.FOUserAgent;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.XMLImage;
+import org.apache.fop.image.ImageFactory;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.Version;
+import org.apache.fop.fo.properties.RuleStyle;
+import org.apache.fop.fo.properties.BackgroundRepeat;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontMetrics;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFInfo;
+import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFState;
+import org.apache.fop.pdf.PDFLink;
+import org.apache.fop.pdf.PDFOutline;
+import org.apache.fop.pdf.PDFAnnotList;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.extensions.BookmarkData;
+
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.TreeExt;
+import org.apache.fop.area.CTM;
+import org.apache.fop.area.Title;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.Page;
+import org.apache.fop.area.RegionViewport;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.LineArea;
+import org.apache.fop.area.inline.Character;
+import org.apache.fop.area.inline.Word;
+import org.apache.fop.area.inline.Viewport;
+import org.apache.fop.area.inline.ForeignObject;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.Leader;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.layout.FontState;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.datatypes.ColorType;
+
+/*
+todo:
+
+word rendering and optimistion
+pdf state optimisation
+line and border
+background pattern
+writing mode
+text decoration
+
+ */
+
+/**
+ * Renderer that renders areas to PDF
+ *
+ */
+public class PDFRenderer extends PrintRenderer {
+ /**
+ * The mime type for pdf
+ */
+ public static final String MIME_TYPE = "application/pdf";
+
+ /**
+ * the PDF Document being created
+ */
+ protected PDFDocument pdfDoc;
+
+ /**
+ * Map of pages using the PageViewport as the key
+ * this is used for prepared pages that cannot be immediately
+ * rendered
+ */
+ protected Map pages = null;
+
+ /**
+ * Page references are stored using the PageViewport as the key
+ * when a reference is made the PageViewport is used
+ * for pdf this means we need the pdf page reference
+ */
+ protected Map pageReferences = new java.util.HashMap();
+ /** Page viewport references */
+ protected Map pvReferences = new java.util.HashMap();
+
+ private String producer = "FOP";
+
+ private String creator = null;
+
+ /**
+ * The output stream to write the document to
+ */
+ protected OutputStream ostream;
+
+ /**
+ * the /Resources object of the PDF document being created
+ */
+ protected PDFResources pdfResources;
+
+ /**
+ * the current stream to add PDF commands to
+ */
+ protected PDFStream currentStream;
+
+ /**
+ * the current annotation list to add annotations to
+ */
+ protected PDFResourceContext currentContext = null;
+
+ /**
+ * the current page to add annotations to
+ */
+ protected PDFPage currentPage;
+
+ /** drawing state */
+ protected PDFState currentState = null;
+
+ /** Name of currently selected font */
+ protected String currentFontName = "";
+ /** Size of currently selected font */
+ protected int currentFontSize = 0;
+ /** page height */
+ protected int pageHeight;
+
+ /** Registry of PDF filters */
+ protected Map filterMap = new java.util.HashMap();
+
+ /**
+ * true if a TJ command is left to be written
+ */
+ protected boolean textOpen = false;
+
+ /**
+ * the previous Y coordinate of the last word written.
+ * Used to decide if we can draw the next word on the same line.
+ */
+ protected int prevWordY = 0;
+
+ /**
+ * the previous X coordinate of the last word written.
+ * used to calculate how much space between two words
+ */
+ protected int prevWordX = 0;
+
+ /**
+ * The width of the previous word. Used to calculate space between
+ */
+ protected int prevWordWidth = 0;
+
+ /**
+ * reusable word area string buffer to reduce memory usage
+ */
+ private StringBuffer wordAreaPDF = new StringBuffer();
+
+ /**
+ * create the PDF renderer
+ */
+ public PDFRenderer() {
+ }
+
+ /**
+ * Configure the PDF renderer.
+ * Get the configuration to be used for pdf stream filters,
+ * fonts etc.
+ * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
+ */
+ public void configure(Configuration conf) throws ConfigurationException {
+ Configuration filters = conf.getChild("filterList");
+ Configuration[] filt = filters.getChildren("value");
+ List filterList = new java.util.ArrayList();
+ for (int i = 0; i < filt.length; i++) {
+ String name = filt[i].getValue();
+ filterList.add(name);
+ }
+
+ filterMap.put(PDFStream.DEFAULT_FILTER, filterList);
+
+ Configuration[] font = conf.getChildren("font");
+ for (int i = 0; i < font.length; i++) {
+ Configuration[] triple = font[i].getChildren("font-triplet");
+ List tripleList = new java.util.ArrayList();
+ for (int j = 0; j < triple.length; j++) {
+ tripleList.add(new FontTriplet(triple[j].getAttribute("name"),
+ triple[j].getAttribute("style"),
+ triple[j].getAttribute("weight")));
+ }
+
+ EmbedFontInfo efi;
+ efi = new EmbedFontInfo(font[i].getAttribute("metrics-url"),
+ font[i].getAttributeAsBoolean("kerning"),
+ tripleList, font[i].getAttribute("embed-url"));
+
+ if (fontList == null) {
+ fontList = new java.util.ArrayList();
+ }
+ fontList.add(efi);
+ }
+
+ }
+
+ /**
+ * Set the document creator.
+ *
+ * @param creator string indicating application that is creating the document
+ */
+ public void setCreator(String creator) {
+ this.creator = creator;
+ }
+
+ /**
+ * Set the PDF document's producer.
+ *
+ * @param producer string indicating application producing PDF
+ */
+ public void setProducer(String producer) {
+ this.producer = producer;
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
+ */
+ public void setUserAgent(FOUserAgent agent) {
+ super.setUserAgent(agent);
+ PDFXMLHandler xmlHandler = new PDFXMLHandler();
+ //userAgent.setDefaultXMLHandler(MIME_TYPE, xmlHandler);
+ String svg = "http://www.w3.org/2000/svg";
+ userAgent.addXMLHandler(MIME_TYPE, svg, xmlHandler);
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
+ */
+ public void startRenderer(OutputStream stream) throws IOException {
+ ostream = stream;
+ producer = "FOP " + Version.getVersion();
+ this.pdfDoc = new PDFDocument(producer);
+ this.pdfDoc.setCreator(creator);
+ this.pdfDoc.setFilterMap(filterMap);
+ pdfDoc.outputHeader(stream);
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#stopRenderer()
+ */
+ public void stopRenderer() throws IOException {
+ FontSetup.addToResources(pdfDoc, pdfDoc.getResources(), fontInfo);
+ pdfDoc.outputTrailer(ostream);
+
+ this.pdfDoc = null;
+ ostream = null;
+
+ pages = null;
+
+ pageReferences.clear();
+ pvReferences.clear();
+ pdfResources = null;
+ currentStream = null;
+ currentContext = null;
+ currentPage = null;
+ currentState = null;
+ currentFontName = "";
+ wordAreaPDF = new StringBuffer();
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#supportsOutOfOrder()
+ */
+ public boolean supportsOutOfOrder() {
+ return true;
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#renderExtension(TreeExt)
+ */
+ public void renderExtension(TreeExt ext) {
+ // render bookmark extension
+ if (ext instanceof BookmarkData) {
+ renderRootExtensions((BookmarkData)ext);
+ }
+ }
+
+ /**
+ * Renders the root extension elements
+ * @param bookmarks the bookmarks to render
+ */
+ protected void renderRootExtensions(BookmarkData bookmarks) {
+ for (int i = 0; i < bookmarks.getCount(); i++) {
+ BookmarkData ext = bookmarks.getSubData(i);
+ renderOutline(ext, null);
+ }
+ }
+
+ private void renderOutline(BookmarkData outline, PDFOutline parentOutline) {
+ PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
+ PDFOutline pdfOutline = null;
+ PageViewport pv = outline.getPage();
+ if (pv != null) {
+ Rectangle2D bounds = pv.getViewArea();
+ double h = bounds.getHeight();
+ float yoffset = (float)h / 1000f;
+ String intDest = (String)pageReferences.get(pv.getKey());
+ if (parentOutline == null) {
+ pdfOutline = pdfDoc.makeOutline(outlineRoot,
+ outline.getLabel(), intDest, yoffset);
+ } else {
+ PDFOutline pdfParentOutline = parentOutline;
+ pdfOutline = pdfDoc.makeOutline(pdfParentOutline,
+ outline.getLabel(), intDest, yoffset);
+ }
+ }
+
+ for (int i = 0; i < outline.getCount(); i++) {
+ renderOutline(outline.getSubData(i), pdfOutline);
+ }
+ }
+
+ /** Saves the graphics state of the rendering engine. */
+ protected void saveGraphicsState() {
+ currentStream.add("q\n");
+ }
+
+ /** Restores the last graphics state of the rendering engine. */
+ protected void restoreGraphicsState() {
+ currentStream.add("Q\n");
+ }
+
+ /** Indicates the beginning of a text object. */
+ protected void beginTextObject() {
+ currentStream.add("BT\n");
+ }
+
+ /** Indicates the end of a text object. */
+ protected void endTextObject() {
+ currentStream.add("ET\n");
+ }
+
+ /**
+ * Start the next page sequence.
+ * For the pdf renderer there is no concept of page sequences
+ * but it uses the first available page sequence title to set
+ * as the title of the pdf document.
+ *
+ * @param seqTitle the title of the page sequence
+ */
+ public void startPageSequence(Title seqTitle) {
+ if (seqTitle != null) {
+ String str = convertTitleToString(seqTitle);
+ PDFInfo info = this.pdfDoc.getInfo();
+ info.setTitle(str);
+ }
+ }
+
+ /**
+ * The pdf page is prepared by making the page.
+ * The page is made in the pdf document without any contents
+ * and then stored to add the contents later.
+ * The page objects is stored using the area tree PageViewport
+ * as a key.
+ *
+ * @param page the page to prepare
+ */
+ public void preparePage(PageViewport page) {
+ this.pdfResources = this.pdfDoc.getResources();
+
+ Rectangle2D bounds = page.getViewArea();
+ double w = bounds.getWidth();
+ double h = bounds.getHeight();
+ currentPage = this.pdfDoc.makePage(this.pdfResources,
+ (int) Math.round(w / 1000), (int) Math.round(h / 1000));
+ if (pages == null) {
+ pages = new java.util.HashMap();
+ }
+ pages.put(page, currentPage);
+ pageReferences.put(page.getKey(), currentPage.referencePDF());
+ pvReferences.put(page.getKey(), page);
+ }
+
+ /**
+ * This method creates a pdf stream for the current page
+ * uses it as the contents of a new page. The page is written
+ * immediately to the output stream.
+ * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
+ */
+ public void renderPage(PageViewport page)
+ throws IOException, FOPException {
+ if (pages != null
+ && (currentPage = (PDFPage) pages.get(page)) != null) {
+ pages.remove(page);
+ Rectangle2D bounds = page.getViewArea();
+ double h = bounds.getHeight();
+ pageHeight = (int) h;
+ } else {
+ this.pdfResources = this.pdfDoc.getResources();
+ Rectangle2D bounds = page.getViewArea();
+ double w = bounds.getWidth();
+ double h = bounds.getHeight();
+ pageHeight = (int) h;
+ currentPage = this.pdfDoc.makePage(this.pdfResources,
+ (int) Math.round(w / 1000), (int) Math.round(h / 1000));
+ pageReferences.put(page.getKey(), currentPage.referencePDF());
+ pvReferences.put(page.getKey(), page);
+ }
+ currentStream =
+ this.pdfDoc.makeStream(PDFStream.CONTENT_FILTER, false);
+
+ currentState = new PDFState();
+ currentState.setTransform(new AffineTransform(1, 0, 0, -1, 0,
+ (int) Math.round(pageHeight / 1000)));
+ // Transform origin at top left to origin at bottom left
+ currentStream.add("1 0 0 -1 0 "
+ + (int) Math.round(pageHeight / 1000) + " cm\n");
+ currentFontName = "";
+
+ Page p = page.getPage();
+ renderPageAreas(p);
+
+ this.pdfDoc.addStream(currentStream);
+ currentPage.setContents(currentStream);
+ PDFAnnotList annots = currentPage.getAnnotations();
+ if (annots != null) {
+ this.pdfDoc.addAnnotList(annots);
+ }
+ this.pdfDoc.addPage(currentPage);
+ this.pdfDoc.output(ostream);
+ }
+
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
+ */
+ protected void startVParea(CTM ctm) {
+ // Set the given CTM in the graphics state
+ currentState.push();
+ currentState.setTransform(
+ new AffineTransform(CTMHelper.toPDFArray(ctm)));
+
+ saveGraphicsState();
+ // multiply with current CTM
+ currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
+ // Set clip?
+ beginTextObject();
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#endVParea()
+ */
+ protected void endVParea() {
+ endTextObject();
+ restoreGraphicsState();
+ currentState.pop();
+ }
+
+ /**
+ * Handle the viewport traits.
+ * This is used to draw the traits for a viewport.
+ *
+ * @param region the viewport region to handle
+ */
+ protected void handleViewportTraits(RegionViewport region) {
+ currentFontName = "";
+ float startx = 0;
+ float starty = 0;
+ Rectangle2D viewArea = region.getViewArea();
+ float width = (float)(viewArea.getWidth() / 1000f);
+ float height = (float)(viewArea.getHeight() / 1000f);
+ /*
+ Trait.Background back;
+ back = (Trait.Background)region.getTrait(Trait.BACKGROUND);
+ */
+ drawBackAndBorders(region, startx, starty, width, height);
+ }
+
+ /**
+ * Handle block traits.
+ * The block could be any sort of block with any positioning
+ * so this should render the traits such as border and background
+ * in its position.
+ *
+ * @param block the block to render the traits
+ */
+ protected void handleBlockTraits(Block block) {
+ float startx = currentIPPosition / 1000f;
+ float starty = currentBPPosition / 1000f;
+ drawBackAndBorders(block, startx, starty,
+ block.getWidth() / 1000f, block.getHeight() / 1000f);
+ }
+
+ /**
+ * Draw the background and borders.
+ * This draws the background and border traits for an area given
+ * the position.
+ *
+ * @param block the area to get the traits from
+ * @param startx the start x position
+ * @param starty the start y position
+ * @param width the width of the area
+ * @param height the height of the area
+ */
+ protected void drawBackAndBorders(Area block,
+ float startx, float starty,
+ float width, float height) {
+ // draw background then border
+
+ boolean started = false;
+ Trait.Background back;
+ back = (Trait.Background)block.getTrait(Trait.BACKGROUND);
+ if (back != null) {
+ started = true;
+ closeText();
+ endTextObject();
+ //saveGraphicsState();
+
+ if (back.getColor() != null) {
+ updateColor(back.getColor(), true, null);
+ currentStream.add(startx + " " + starty + " "
+ + width + " " + height + " re\n");
+ currentStream.add("f\n");
+ }
+ if (back.getURL() != null) {
+ ImageFactory fact = ImageFactory.getInstance();
+ FopImage fopimage = fact.getImage(back.getURL(), userAgent);
+ if (fopimage != null && fopimage.load(FopImage.DIMENSIONS, userAgent)) {
+ if (back.getRepeat() == BackgroundRepeat.REPEAT) {
+ // create a pattern for the image
+ } else {
+ // place once
+ Rectangle2D pos;
+ pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000,
+ (starty + back.getVertical()) * 1000,
+ fopimage.getWidth() * 1000,
+ fopimage.getHeight() * 1000);
+ putImage(back.getURL(), pos);
+ }
+ }
+ }
+ }
+
+ BorderProps bps = (BorderProps)block.getTrait(Trait.BORDER_BEFORE);
+ if (bps != null) {
+ float endx = startx + width;
+
+ if (!started) {
+ started = true;
+ closeText();
+ endTextObject();
+ //saveGraphicsState();
+ }
+
+ float bwidth = bps.width / 1000f;
+ updateColor(bps.color, false, null);
+ currentStream.add(bwidth + " w\n");
+
+ drawLine(startx, starty + bwidth / 2, endx, starty + bwidth / 2);
+ }
+ bps = (BorderProps)block.getTrait(Trait.BORDER_START);
+ if (bps != null) {
+ float endy = starty + height;
+
+ if (!started) {
+ started = true;
+ closeText();
+ endTextObject();
+ //saveGraphicsState();
+ }
+
+ float bwidth = bps.width / 1000f;
+ updateColor(bps.color, false, null);
+ currentStream.add(bwidth + " w\n");
+
+ drawLine(startx + bwidth / 2, starty, startx + bwidth / 2, endy);
+ }
+ bps = (BorderProps)block.getTrait(Trait.BORDER_AFTER);
+ if (bps != null) {
+ float sy = starty + height;
+ float endx = startx + width;
+
+ if (!started) {
+ started = true;
+ closeText();
+ endTextObject();
+ //saveGraphicsState();
+ }
+
+ float bwidth = bps.width / 1000f;
+ updateColor(bps.color, false, null);
+ currentStream.add(bwidth + " w\n");
+
+ drawLine(startx, sy - bwidth / 2, endx, sy - bwidth / 2);
+ }
+ bps = (BorderProps)block.getTrait(Trait.BORDER_END);
+ if (bps != null) {
+ float sx = startx + width;
+ float endy = starty + height;
+
+ if (!started) {
+ started = true;
+ closeText();
+ endTextObject();
+ //saveGraphicsState();
+ }
+
+ float bwidth = bps.width / 1000f;
+ updateColor(bps.color, false, null);
+ currentStream.add(bwidth + " w\n");
+ drawLine(sx - bwidth / 2, starty, sx - bwidth / 2, endy);
+ }
+ if (started) {
+ //restoreGraphicsState();
+ beginTextObject();
+ // font last set out of scope in text section
+ currentFontName = "";
+ }
+ }
+
+ /**
+ * Draw a line.
+ *
+ * @param startx the start x position
+ * @param starty the start y position
+ * @param endx the x end position
+ * @param endy the y end position
+ */
+ private void drawLine(float startx, float starty, float endx, float endy) {
+ currentStream.add(startx + " " + starty + " m\n");
+ currentStream.add(endx + " " + endy + " l\n");
+ currentStream.add("S\n");
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
+ */
+ protected void renderBlockViewport(BlockViewport bv, List children) {
+ // clip and position viewport if necessary
+
+ // save positions
+ int saveIP = currentIPPosition;
+ int saveBP = currentBPPosition;
+ String saveFontName = currentFontName;
+
+ CTM ctm = bv.getCTM();
+
+ if (bv.getPositioning() == Block.ABSOLUTE) {
+
+ currentIPPosition = 0;
+ currentBPPosition = 0;
+
+ closeText();
+ endTextObject();
+
+ if (bv.getClip()) {
+ saveGraphicsState();
+ float x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
+ float y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
+ float width = (float)bv.getWidth() / 1000f;
+ float height = (float)bv.getHeight() / 1000f;
+ clip(x, y, width, height);
+ }
+
+ CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
+ ctm = tempctm.multiply(ctm);
+
+ startVParea(ctm);
+ handleBlockTraits(bv);
+ renderBlocks(children);
+ endVParea();
+
+ if (bv.getClip()) {
+ restoreGraphicsState();
+ }
+ beginTextObject();
+
+ // clip if necessary
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+ } else {
+
+ if (ctm != null) {
+ currentIPPosition = 0;
+ currentBPPosition = 0;
+
+ closeText();
+ endTextObject();
+
+ double[] vals = ctm.toArray();
+ //boolean aclock = vals[2] == 1.0;
+ if (vals[2] == 1.0) {
+ ctm = ctm.translate(-saveBP - bv.getHeight(), -saveIP);
+ } else if (vals[0] == -1.0) {
+ ctm = ctm.translate(-saveIP - bv.getWidth(), -saveBP - bv.getHeight());
+ } else {
+ ctm = ctm.translate(saveBP, saveIP - bv.getWidth());
+ }
+ }
+
+ // clip if necessary
+ if (bv.getClip()) {
+ if (ctm == null) {
+ closeText();
+ endTextObject();
+ }
+ saveGraphicsState();
+ float x = (float)bv.getXOffset() / 1000f;
+ float y = (float)bv.getYOffset() / 1000f;
+ float width = (float)bv.getWidth() / 1000f;
+ float height = (float)bv.getHeight() / 1000f;
+ clip(x, y, width, height);
+ }
+
+ if (ctm != null) {
+ startVParea(ctm);
+ }
+ handleBlockTraits(bv);
+ renderBlocks(children);
+ if (ctm != null) {
+ endVParea();
+ }
+
+ if (bv.getClip()) {
+ restoreGraphicsState();
+ if (ctm == null) {
+ beginTextObject();
+ }
+ }
+ if (ctm != null) {
+ beginTextObject();
+ }
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+ currentBPPosition += (int)(bv.getHeight());
+ }
+ currentFontName = saveFontName;
+ }
+
+ /**
+ * Clip an area.
+ * write a clipping operation given coordinates in the current
+ * transform.
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width of the area
+ * @param height the height of the area
+ */
+ protected void clip(float x, float y, float width, float height) {
+ currentStream.add(x + " " + y + " m\n");
+ currentStream.add((x + width) + " " + y + " l\n");
+ currentStream.add((x + width) + " " + (y + height) + " l\n");
+ currentStream.add(x + " " + (y + height) + " l\n");
+ currentStream.add("h\n");
+ currentStream.add("W\n");
+ currentStream.add("n\n");
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderLineArea(LineArea)
+ */
+ protected void renderLineArea(LineArea line) {
+ super.renderLineArea(line);
+ closeText();
+ }
+
+ /**
+ * Render inline parent area.
+ * For pdf this handles the inline parent area traits such as
+ * links, border, background.
+ * @param ip the inline parent area
+ */
+ public void renderInlineParent(InlineParent ip) {
+ float start = currentBlockIPPosition / 1000f;
+ float top = (ip.getOffset() + currentBPPosition) / 1000f;
+ float width = ip.getWidth() / 1000f;
+ float height = ip.getHeight() / 1000f;
+ drawBackAndBorders(ip, start, top, width, height);
+
+ // render contents
+ super.renderInlineParent(ip);
+
+ // place the link over the top
+ Object tr = ip.getTrait(Trait.INTERNAL_LINK);
+ boolean internal = false;
+ String dest = null;
+ float yoffset = 0;
+ if (tr == null) {
+ dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
+ } else {
+ String pvKey = (String)tr;
+ dest = (String)pageReferences.get(pvKey);
+ if (dest != null) {
+ PageViewport pv = (PageViewport)pvReferences.get(pvKey);
+ Rectangle2D bounds = pv.getViewArea();
+ double h = bounds.getHeight();
+ yoffset = (float)h / 1000f;
+ internal = true;
+ }
+ }
+ if (dest != null) {
+ // add link to pdf document
+ Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
+ // transform rect to absolute coords
+ AffineTransform transform = currentState.getTransform();
+ rect = transform.createTransformedShape(rect).getBounds();
+
+ int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
+ PDFLink pdflink = pdfDoc.makeLink(rect, dest, type, yoffset);
+ currentPage.addAnnotation(pdflink);
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#renderCharacter(Character)
+ */
+ public void renderCharacter(Character ch) {
+
+ super.renderCharacter(ch);
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#renderWord(Word)
+ */
+ public void renderWord(Word word) {
+ StringBuffer pdf = new StringBuffer();
+
+ String name = (String) word.getTrait(Trait.FONT_NAME);
+ int size = ((Integer) word.getTrait(Trait.FONT_SIZE)).intValue();
+
+ // This assumes that *all* CIDFonts use a /ToUnicode mapping
+ Font f = (Font) fontInfo.getFonts().get(name);
+ boolean useMultiByte = f.isMultiByte();
+
+ // String startText = useMultiByte ? "<FEFF" : "(";
+ String startText = useMultiByte ? "<" : "(";
+ String endText = useMultiByte ? "> " : ") ";
+
+ updateFont(name, size, pdf);
+ ColorType ct = (ColorType)word.getTrait(Trait.COLOR);
+ if (ct != null) {
+ updateColor(ct, true, pdf);
+ }
+
+ int rx = currentBlockIPPosition;
+ // int bl = pageHeight - currentBPPosition;
+ int bl = currentBPPosition + word.getOffset();
+
+ // Set letterSpacing
+ //float ls = fs.getLetterSpacing() / this.currentFontSize;
+ //pdf.append(ls).append(" Tc\n");
+
+ if (!textOpen || bl != prevWordY) {
+ closeText();
+
+ pdf.append("1 0 0 -1 " + (rx / 1000f) + " "
+ + (bl / 1000f) + " Tm [" + startText);
+ prevWordY = bl;
+ textOpen = true;
+ } else {
+ // express the space between words in thousandths of an em
+ int space = prevWordX - rx + prevWordWidth;
+ float emDiff = (float) space / (float) currentFontSize * 1000f;
+ // this prevents a problem in Acrobat Reader and other viewers
+ // where large numbers cause text to disappear or default to
+ // a limit
+ if (emDiff < -33000) {
+ closeText();
+
+ pdf.append("1 0 0 1 " + (rx / 1000f) + " "
+ + (bl / 1000f) + " Tm [" + startText);
+ textOpen = true;
+ } else {
+ pdf.append(Float.toString(emDiff));
+ pdf.append(" ");
+ pdf.append(startText);
+ }
+ }
+ prevWordWidth = word.getWidth();
+ prevWordX = rx;
+
+ String s = word.getWord();
+
+ FontMetrics metrics = fontInfo.getMetricsFor(name);
+ FontState fs = new FontState(name, metrics, size);
+ escapeText(s, fs, useMultiByte, pdf);
+ pdf.append(endText);
+
+ currentStream.add(pdf.toString());
+
+ super.renderWord(word);
+ }
+
+ /**
+ * Escapes text according to PDF rules.
+ * @param s Text to escape
+ * @param fs Font state
+ * @param useMultiByte Indicates the use of multi byte convention
+ * @param pdf target buffer for the escaped text
+ */
+ public void escapeText(String s, FontState fs,
+ boolean useMultiByte, StringBuffer pdf) {
+ String startText = useMultiByte ? "<" : "(";
+ String endText = useMultiByte ? "> " : ") ";
+
+ boolean kerningAvailable = false;
+ Map kerning = fs.getKerning();
+ if (kerning != null && !kerning.isEmpty()) {
+ kerningAvailable = true;
+ }
+
+ int l = s.length();
+
+ for (int i = 0; i < l; i++) {
+ char ch = fs.mapChar(s.charAt(i));
+
+ if (!useMultiByte) {
+ if (ch > 127) {
+ pdf.append("\\");
+ pdf.append(Integer.toOctalString((int) ch));
+ } else {
+ switch (ch) {
+ case '(':
+ case ')':
+ case '\\':
+ pdf.append("\\");
+ break;
+ }
+ pdf.append(ch);
+ }
+ } else {
+ pdf.append(getUnicodeString(ch));
+ }
+
+ if (kerningAvailable && (i + 1) < l) {
+ addKerning(pdf, (new Integer((int) ch)),
+ (new Integer((int) fs.mapChar(s.charAt(i + 1)))
+ ), kerning, startText, endText);
+ }
+ }
+ }
+
+ /**
+ * Convert a char to a multibyte hex representation
+ */
+ private String getUnicodeString(char c) {
+ StringBuffer buf = new StringBuffer(4);
+
+ byte[] uniBytes = null;
+ try {
+ char[] a = {c};
+ uniBytes = new String(a).getBytes("UnicodeBigUnmarked");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // This should never fail
+ }
+
+ for (int i = 0; i < uniBytes.length; i++) {
+ int b = (uniBytes[i] < 0) ? (int)(256 + uniBytes[i])
+ : (int) uniBytes[i];
+
+ String hexString = Integer.toHexString(b);
+ if (hexString.length() == 1) {
+ buf = buf.append("0" + hexString);
+ } else {
+ buf = buf.append(hexString);
+ }
+ }
+ return buf.toString();
+ }
+
+ private void addKerning(StringBuffer buf, Integer ch1, Integer ch2,
+ Map kerning, String startText, String endText) {
+ Map kernPair = (Map) kerning.get(ch1);
+
+ if (kernPair != null) {
+ Integer width = (Integer) kernPair.get(ch2);
+ if (width != null) {
+ buf.append(endText).append(-width.intValue());
+ buf.append(' ').append(startText);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if we have some text rendering commands open
+ * still and writes out the TJ command to the stream if we do
+ */
+ protected void closeText() {
+ if (textOpen) {
+ currentStream.add("] TJ\n");
+ textOpen = false;
+ prevWordX = 0;
+ prevWordY = 0;
+ }
+ }
+
+ private void updateColor(ColorType col, boolean fill, StringBuffer pdf) {
+ Color newCol = new Color(col.getRed(), col.getGreen(), col.getBlue());
+ boolean update = false;
+ if (fill) {
+ update = currentState.setBackColor(newCol);
+ } else {
+ update = currentState.setColor(newCol);
+ }
+
+ if (update) {
+ PDFColor color = new PDFColor((double)col.getRed(),
+ (double)col.getGreen(),
+ (double)col.getBlue());
+
+ closeText();
+
+ if (pdf != null) {
+ pdf.append(color.getColorSpaceOut(fill));
+ } else {
+ currentStream.add(color.getColorSpaceOut(fill));
+ }
+ }
+ }
+
+ private void updateFont(String name, int size, StringBuffer pdf) {
+ if ((!name.equals(this.currentFontName))
+ || (size != this.currentFontSize)) {
+ closeText();
+
+ this.currentFontName = name;
+ this.currentFontSize = size;
+ pdf = pdf.append("/" + name + " " + ((float) size / 1000f)
+ + " Tf\n");
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
+ */
+ public void renderImage(Image image, Rectangle2D pos) {
+ String url = image.getURL();
+ putImage(url, pos);
+ }
+
+ /**
+ * Adds a PDF XObject (a bitmap) to the PDF that will later be referenced.
+ * @param url URL of the bitmap
+ * @param pos Position of the bitmap
+ */
+ protected void putImage(String url, Rectangle2D pos) {
+ PDFXObject xobject = pdfDoc.getImage(url);
+ if (xobject != null) {
+ int w = (int) pos.getWidth() / 1000;
+ int h = (int) pos.getHeight() / 1000;
+ placeImage((int) pos.getX() / 1000,
+ (int) pos.getY() / 1000, w, h, xobject.getXNumber());
+ return;
+ }
+
+ ImageFactory fact = ImageFactory.getInstance();
+ FopImage fopimage = fact.getImage(url, userAgent);
+ if (fopimage == null) {
+ return;
+ }
+ if (!fopimage.load(FopImage.DIMENSIONS, userAgent)) {
+ return;
+ }
+ String mime = fopimage.getMimeType();
+ if ("text/xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ String ns = ((XMLImage) fopimage).getNameSpace();
+
+ renderDocument(doc, ns, pos);
+ } else if ("image/svg+xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ String ns = ((XMLImage) fopimage).getNameSpace();
+
+ renderDocument(doc, ns, pos);
+ } else if ("image/eps".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
+ return;
+ }
+ FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
+ int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ fact.releaseImage(url, userAgent);
+ } else if ("image/jpeg".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
+ return;
+ }
+ FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
+ int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ fact.releaseImage(url, userAgent);
+
+ int w = (int) pos.getWidth() / 1000;
+ int h = (int) pos.getHeight() / 1000;
+ placeImage((int) pos.getX() / 1000,
+ (int) pos.getY() / 1000, w, h, xobj);
+ } else {
+ if (!fopimage.load(FopImage.BITMAP, userAgent)) {
+ return;
+ }
+ FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
+ int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ fact.releaseImage(url, userAgent);
+
+ int w = (int) pos.getWidth() / 1000;
+ int h = (int) pos.getHeight() / 1000;
+ placeImage((int) pos.getX() / 1000,
+ (int) pos.getY() / 1000, w, h, xobj);
+ }
+
+ // output new data
+ try {
+ this.pdfDoc.output(ostream);
+ } catch (IOException ioe) {
+ // ioexception will be caught later
+ }
+ }
+
+ /**
+ * Places a previously registered image at a certain place on the page.
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param w width for image
+ * @param h height for image
+ * @param xobj object number of the referenced image
+ */
+ protected void placeImage(int x, int y, int w, int h, int xobj) {
+ saveGraphicsState();
+ currentStream.add(((float) w) + " 0 0 "
+ + ((float) -h) + " "
+ + (((float) currentBlockIPPosition) / 1000f + x) + " "
+ + (((float)(currentBPPosition + 1000 * h)) / 1000f
+ + y) + " cm\n" + "/Im" + xobj + " Do\n");
+ restoreGraphicsState();
+
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
+ */
+ public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
+ Document doc = fo.getDocument();
+ String ns = fo.getNameSpace();
+ renderDocument(doc, ns, pos);
+ }
+
+ /**
+ * Renders an XML document (SVG for example).
+ * @param doc DOM document representing the XML document
+ * @param ns Namespace for the document
+ * @param pos Position on the page
+ */
+ public void renderDocument(Document doc, String ns, Rectangle2D pos) {
+ RendererContext context;
+ context = new RendererContext(MIME_TYPE);
+ context.setUserAgent(userAgent);
+
+ context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc);
+ context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream);
+ context.setProperty(PDFXMLHandler.PDF_STATE, currentState);
+ context.setProperty(PDFXMLHandler.PDF_PAGE, currentPage);
+ context.setProperty(PDFXMLHandler.PDF_CONTEXT,
+ currentContext == null ? currentPage : currentContext);
+ context.setProperty(PDFXMLHandler.PDF_CONTEXT, currentContext);
+ context.setProperty(PDFXMLHandler.PDF_STREAM, currentStream);
+ context.setProperty(PDFXMLHandler.PDF_XPOS,
+ new Integer(currentBlockIPPosition + (int) pos.getX()));
+ context.setProperty(PDFXMLHandler.PDF_YPOS,
+ new Integer(currentBPPosition + (int) pos.getY()));
+ context.setProperty(PDFXMLHandler.PDF_FONT_INFO, fontInfo);
+ context.setProperty(PDFXMLHandler.PDF_FONT_NAME, currentFontName);
+ context.setProperty(PDFXMLHandler.PDF_FONT_SIZE,
+ new Integer(currentFontSize));
+ context.setProperty(PDFXMLHandler.PDF_WIDTH,
+ new Integer((int) pos.getWidth()));
+ context.setProperty(PDFXMLHandler.PDF_HEIGHT,
+ new Integer((int) pos.getHeight()));
+ userAgent.renderXML(context, doc, ns);
+
+ }
+
+ /**
+ * Render an inline viewport.
+ * This renders an inline viewport by clipping if necessary.
+ * @param viewport the viewport to handle
+ */
+ public void renderViewport(Viewport viewport) {
+ closeText();
+
+ float x = currentBlockIPPosition / 1000f;
+ float y = (currentBPPosition + viewport.getOffset()) / 1000f;
+ float width = viewport.getWidth() / 1000f;
+ float height = viewport.getHeight() / 1000f;
+ drawBackAndBorders(viewport, x, y, width, height);
+
+ endTextObject();
+
+ if (viewport.getClip()) {
+ saveGraphicsState();;
+
+ clip(x, y, width, height);
+ }
+ super.renderViewport(viewport);
+
+ if (viewport.getClip()) {
+ restoreGraphicsState();
+ }
+ beginTextObject();
+ }
+
+ /**
+ * Render leader area.
+ * This renders a leader area which is an area with a rule.
+ * @param area the leader area to render
+ */
+ public void renderLeader(Leader area) {
+ closeText();
+ endTextObject();
+ saveGraphicsState();
+ int style = area.getRuleStyle();
+ boolean alt = false;
+ switch(style) {
+ case RuleStyle.SOLID:
+ currentStream.add("[] 0 d\n");
+ break;
+ case RuleStyle.DOTTED:
+ currentStream.add("[2] 0 d\n");
+ break;
+ case RuleStyle.DASHED:
+ currentStream.add("[6 4] 0 d\n");
+ break;
+ case RuleStyle.DOUBLE:
+ case RuleStyle.GROOVE:
+ case RuleStyle.RIDGE:
+ alt = true;
+ break;
+ }
+ float startx = ((float) currentBlockIPPosition) / 1000f;
+ float starty = ((currentBPPosition + area.getOffset()) / 1000f);
+ float endx = (currentBlockIPPosition + area.getWidth()) / 1000f;
+ if (!alt) {
+ currentStream.add(area.getRuleThickness() / 1000f + " w\n");
+ drawLine(startx, starty, endx, starty);
+ } else {
+ if (style == RuleStyle.DOUBLE) {
+ float third = area.getRuleThickness() / 3000f;
+ currentStream.add(third + " w\n");
+ drawLine(startx, starty, endx, starty);
+
+ drawLine(startx, (starty + 2 * third), endx, (starty + 2 * third));
+ } else {
+ float half = area.getRuleThickness() / 2000f;
+
+ currentStream.add("1 g\n");
+ currentStream.add(startx + " " + starty + " m\n");
+ currentStream.add(endx + " " + starty + " l\n");
+ currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
+ currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
+ currentStream.add("h\n");
+ currentStream.add("f\n");
+ if (style == RuleStyle.GROOVE) {
+ currentStream.add("0 g\n");
+ currentStream.add(startx + " " + starty + " m\n");
+ currentStream.add(endx + " " + starty + " l\n");
+ currentStream.add(endx + " " + (starty + half) + " l\n");
+ currentStream.add((startx + half) + " " + (starty + half) + " l\n");
+ currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
+ } else {
+ currentStream.add("0 g\n");
+ currentStream.add(endx + " " + starty + " m\n");
+ currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
+ currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
+ currentStream.add(startx + " " + (starty + half) + " l\n");
+ currentStream.add((endx - half) + " " + (starty + half) + " l\n");
+ }
+ currentStream.add("h\n");
+ currentStream.add("f\n");
+ }
+
+ }
+
+ restoreGraphicsState();
+ beginTextObject();
+ super.renderLeader(area);
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/PDFXMLHandler.java b/src/java/org/apache/fop/render/pdf/PDFXMLHandler.java
new file mode 100644
index 000000000..f58d71da6
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFXMLHandler.java
@@ -0,0 +1,338 @@
+/*
+ * $Id: PDFXMLHandler.java,v 1.13 2003/03/07 09:46:32 jeremias Exp $
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.render.pdf;
+
+import org.apache.fop.render.XMLHandler;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFState;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.svg.PDFTextElementBridge;
+import org.apache.fop.svg.PDFAElementBridge;
+import org.apache.fop.svg.PDFGraphics2D;
+import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.layout.FontInfo;
+
+import org.w3c.dom.Document;
+
+import java.io.OutputStream;
+
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.ViewBox;
+
+import org.apache.batik.gvt.GraphicsNode;
+
+import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.svg.SVGSVGElement;
+
+import java.awt.geom.AffineTransform;
+
+/**
+ * PDF XML handler.
+ * This handler handles XML for foreign objects when rendering to PDF.
+ * It renders SVG to the PDF document using the PDFGraphics2D.
+ * The properties from the PDF renderer are subject to change.
+ */
+public class PDFXMLHandler implements XMLHandler {
+ /**
+ * The PDF document that is being drawn into.
+ */
+ public static final String PDF_DOCUMENT = "pdfDoc";
+
+ /**
+ * The output stream that the document is being sent to.
+ */
+ public static final String OUTPUT_STREAM = "outputStream";
+
+ /**
+ * The current pdf state.
+ */
+ public static final String PDF_STATE = "pdfState";
+
+ /**
+ * The current PDF page for page renference and as a resource context.
+ */
+ public static final String PDF_PAGE = "pdfPage";
+
+ /**
+ * The current PDF page for page renference and as a resource context.
+ */
+ public static final String PDF_CONTEXT = "pdfContext";
+
+ /**
+ * The current PDF stream to draw directly to.
+ */
+ public static final String PDF_STREAM = "pdfStream";
+
+ /**
+ * The width of the current pdf page.
+ */
+ public static final String PDF_WIDTH = "width";
+
+ /**
+ * The height of the current pdf page.
+ */
+ public static final String PDF_HEIGHT = "height";
+
+ /**
+ * The current font information for the pdf renderer.
+ */
+ public static final String PDF_FONT_INFO = "fontInfo";
+
+ /**
+ * The current pdf font name.
+ */
+ public static final String PDF_FONT_NAME = "fontName";
+
+ /**
+ * The current pdf font size.
+ */
+ public static final String PDF_FONT_SIZE = "fontSize";
+
+ /**
+ * The x position that this is being drawn at.
+ */
+ public static final String PDF_XPOS = "xpos";
+
+ /**
+ * The y position that this is being drawn at.
+ */
+ public static final String PDF_YPOS = "ypos";
+
+ /**
+ * Create a new PDF XML handler for use by the PDF renderer.
+ */
+ public PDFXMLHandler() {
+ }
+
+ /**
+ * Handle the XML.
+ * This checks the type of XML and handles appropraitely.
+ *
+ * @param context the renderer context
+ * @param doc the XML document to render
+ * @param ns the namespace of the XML document
+ * @throws Exception any sort of exception could be thrown and shuld be handled
+ */
+ public void handleXML(RendererContext context, Document doc,
+ String ns) throws Exception {
+ PDFInfo pdfi = getPDFInfo(context);
+
+ String svg = "http://www.w3.org/2000/svg";
+ if (svg.equals(ns)) {
+ SVGHandler svghandler = new SVGHandler();
+ svghandler.renderSVGDocument(context, doc, pdfi);
+ } else {
+ }
+ }
+
+ /**
+ * Get the pdf information from the render context.
+ *
+ * @param context the renderer context
+ * @return the pdf information retrieved from the context
+ */
+ public static PDFInfo getPDFInfo(RendererContext context) {
+ PDFInfo pdfi = new PDFInfo();
+ pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT);
+ pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM);
+ pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE);
+ pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE);
+ pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT);
+ pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM);
+ pdfi.width = ((Integer)context.getProperty(PDF_WIDTH)).intValue();
+ pdfi.height = ((Integer)context.getProperty(PDF_HEIGHT)).intValue();
+ pdfi.fi = (FontInfo)context.getProperty(PDF_FONT_INFO);
+ pdfi.currentFontName = (String)context.getProperty(PDF_FONT_NAME);
+ pdfi.currentFontSize = ((Integer)context.getProperty(PDF_FONT_SIZE)).intValue();
+ pdfi.currentXPosition = ((Integer)context.getProperty(PDF_XPOS)).intValue();
+ pdfi.currentYPosition = ((Integer)context.getProperty(PDF_YPOS)).intValue();
+ return pdfi;
+ }
+
+ /**
+ * PDF information structure for drawing the XML document.
+ */
+ public static class PDFInfo {
+ /** see PDF_DOCUMENT */
+ public PDFDocument pdfDoc;
+ /** see OUTPUT_STREAM */
+ public OutputStream outputStream;
+ /** see PDF_STATE */
+ public PDFState pdfState;
+ /** see PDF_PAGE */
+ public PDFPage pdfPage;
+ /** see PDF_CONTEXT */
+ public PDFResourceContext pdfContext;
+ /** see PDF_STREAM */
+ public PDFStream currentStream;
+ /** see PDF_WIDTH */
+ public int width;
+ /** see PDF_HEIGHT */
+ public int height;
+ /** see PDF_FONT_INFO */
+ public FontInfo fi;
+ /** see PDF_FONT_NAME */
+ public String currentFontName;
+ /** see PDF_FONT_SIZE */
+ public int currentFontSize;
+ /** see PDF_XPOS */
+ public int currentXPosition;
+ /** see PDF_YPOS */
+ public int currentYPosition;
+ }
+
+ /**
+ * This method is placed in an inner class so that we don't get class
+ * loading errors if batik is not present.
+ */
+ protected class SVGHandler {
+ /**
+ * Render the svg document.
+ * @param context the renderer context
+ * @param doc the svg document
+ * @param pdfInfo the pdf information of the current context
+ */
+ protected void renderSVGDocument(RendererContext context, Document doc, PDFInfo pdfInfo) {
+ int xOffset = pdfInfo.currentXPosition;
+ int yOffset = pdfInfo.currentYPosition;
+
+ SVGUserAgent ua
+ = new SVGUserAgent(context.getUserAgent(), new AffineTransform());
+
+ GVTBuilder builder = new GVTBuilder();
+ BridgeContext ctx = new BridgeContext(ua);
+ PDFTextElementBridge tBridge = new PDFTextElementBridge(pdfInfo.fi);
+ ctx.putBridge(tBridge);
+
+ PDFAElementBridge aBridge = new PDFAElementBridge();
+ // to get the correct transform we need to use the PDFState
+ AffineTransform transform = pdfInfo.pdfState.getTransform();
+ transform.translate(xOffset / 1000f, yOffset / 1000f);
+ aBridge.setCurrentTransform(transform);
+ ctx.putBridge(aBridge);
+
+ GraphicsNode root;
+ try {
+ root = builder.build(ctx, doc);
+ } catch (Exception e) {
+ context.getUserAgent().getLogger().error("svg graphic could not be built: "
+ + e.getMessage(), e);
+ return;
+ }
+ // get the 'width' and 'height' attributes of the SVG document
+ float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
+ float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
+
+ float sx = pdfInfo.width / (float)w;
+ float sy = pdfInfo.height / (float)h;
+
+ ctx = null;
+ builder = null;
+
+ /*
+ * Clip to the svg area.
+ * Note: To have the svg overlay (under) a text area then use
+ * an fo:block-container
+ */
+ pdfInfo.currentStream.add("q\n");
+ // transform so that the coordinates (0,0) is from the top left
+ // and positive is down and to the right. (0,0) is where the
+ // viewBox puts it.
+ pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
+ + yOffset / 1000f + " cm\n");
+
+ SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
+ AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, w / 1000f, h / 1000f);
+ if (!at.isIdentity()) {
+ double[] vals = new double[6];
+ at.getMatrix(vals);
+ pdfInfo.currentStream.add(PDFNumber.doubleOut(vals[0], 5) + " "
+ + PDFNumber.doubleOut(vals[1], 5) + " "
+ + PDFNumber.doubleOut(vals[2], 5) + " "
+ + PDFNumber.doubleOut(vals[3], 5) + " "
+ + PDFNumber.doubleOut(vals[4]) + " "
+ + PDFNumber.doubleOut(vals[5]) + " cm\n");
+ }
+
+ if (pdfInfo.pdfContext == null) {
+ pdfInfo.pdfContext = pdfInfo.pdfPage;
+ }
+ PDFGraphics2D graphics = new PDFGraphics2D(true, pdfInfo.fi, pdfInfo.pdfDoc,
+ pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
+ pdfInfo.currentFontName,
+ pdfInfo.currentFontSize);
+ graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
+ pdfInfo.pdfState.push();
+ transform = new AffineTransform();
+ // scale to viewbox
+ transform.translate(xOffset / 1000f, yOffset / 1000f);
+ pdfInfo.pdfState.setTransform(transform);
+ graphics.setPDFState(pdfInfo.pdfState);
+ graphics.setOutputStream(pdfInfo.outputStream);
+ try {
+ root.paint(graphics);
+ pdfInfo.currentStream.add(graphics.getString());
+ } catch (Exception e) {
+ context.getUserAgent().getLogger().error("svg graphic could not be rendered: "
+ + e.getMessage(), e);
+ }
+
+ pdfInfo.currentStream.add("Q\n");
+ pdfInfo.pdfState.pop();
+ }
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/package.html b/src/java/org/apache/fop/render/pdf/package.html
new file mode 100644
index 000000000..1bcf2644f
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/package.html
@@ -0,0 +1,6 @@
+<HTML>
+<TITLE>org.apache.fop.render.pdf Package</TITLE>
+<BODY>
+<P>classes for rendering to PDF</P>
+</BODY>
+</HTML> \ No newline at end of file