diff options
author | Jeremias Maerki <jeremias@apache.org> | 2003-03-11 13:05:43 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2003-03-11 13:05:43 +0000 |
commit | 1e5d512c216d329effa693b91ef64652945def6a (patch) | |
tree | 5bd3521ee8121eade7bf1909ceaf29cfc0263fd1 /src/java/org/apache/fop/svg | |
parent | 73c824d39411bf11ad0c2f4e1c57cd9c484665f9 (diff) | |
download | xmlgraphics-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/svg')
-rw-r--r-- | src/java/org/apache/fop/svg/PDFAElementBridge.java | 124 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFANode.java | 156 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java | 279 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFGraphics2D.java | 1660 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFGraphicsConfiguration.javat | 178 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFGraphicsDevice.java | 127 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFImageElementBridge.java | 181 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFTextElementBridge.java | 154 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFTextPainter.java | 434 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFTranscoder.java | 439 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/SVGElement.java | 314 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/SVGElementMapping.java | 115 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/SVGObj.java | 79 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/SVGUserAgent.java | 184 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/SVGUtilities.java | 301 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/package.html | 31 |
16 files changed, 4756 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/svg/PDFAElementBridge.java b/src/java/org/apache/fop/svg/PDFAElementBridge.java new file mode 100644 index 000000000..2969501f3 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFAElementBridge.java @@ -0,0 +1,124 @@ +/* + * $Id: PDFAElementBridge.java,v 1.6 2003/03/07 09:51:25 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.svg; + +import java.awt.geom.AffineTransform; + +import org.apache.batik.bridge.AbstractGraphicsNodeBridge; +import org.apache.batik.bridge.BridgeContext; + +import org.apache.batik.gvt.GraphicsNode; + +import org.w3c.dom.Element; +import org.w3c.dom.svg.SVGAElement; + +/** + * Bridge class for the <a> element. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public class PDFAElementBridge extends AbstractGraphicsNodeBridge { + private AffineTransform transform; + + /** + * Constructs a new bridge for the <a> element. + */ + public PDFAElementBridge() { + } + + /** + * Set the current transform of this element. + * @param tf the transform + */ + public void setCurrentTransform(AffineTransform tf) { + transform = tf; + } + + /** + * Returns 'a'. + * @return the name of this node + */ + public String getLocalName() { + return SVG_A_TAG; + } + + /** + * Creates a <tt>CompositeGraphicsNode</tt>. + * @return a new PDFANode + */ + protected GraphicsNode instantiateGraphicsNode() { + return new PDFANode(); + } + + /** + * Builds using the specified BridgeContext and element, the + * specified graphics node. + * + * @param ctx the bridge context to use + * @param e the element that describes the graphics node to build + * @return node the new graphics node + */ + public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) { + PDFANode aNode = (PDFANode)super.createGraphicsNode(ctx, e); + aNode.setDestination(((SVGAElement)e).getHref().getBaseVal()); + aNode.setTransform(transform); + return aNode; + } + + /** + * Returns true as the <a> element is a container. + * @return true if the a element is a container + */ + public boolean isComposite() { + return true; + } + +} diff --git a/src/java/org/apache/fop/svg/PDFANode.java b/src/java/org/apache/fop/svg/PDFANode.java new file mode 100644 index 000000000..a200da27d --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFANode.java @@ -0,0 +1,156 @@ +/* + * $Id: PDFANode.java,v 1.11 2003/03/07 09:51:25 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.svg; + +import org.apache.batik.gvt.CompositeGraphicsNode; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.geom.AffineTransform; + +import java.util.StringTokenizer; + +/** + * A graphics node that represents an image described as a graphics node. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public class PDFANode extends CompositeGraphicsNode { + private String destination; + private AffineTransform transform; + + /** + * Constructs a new empty <tt>PDFANode</tt>. + */ + public PDFANode() { + } + + /** + * Set the destination String. + * @param dest the target destination + */ + public void setDestination(String dest) { + destination = dest; + } + + /** + * Set the current transform of this node. + * @param tf the transform + */ + public void setTransform(AffineTransform tf) { + transform = tf; + } + + /** + * Paints this node if visible. + * + * @param g2d the Graphics2D to use + */ + public void paint(Graphics2D g2d) { + if (isVisible) { + super.paint(g2d); + if (g2d instanceof PDFGraphics2D) { + PDFGraphics2D pdfg = (PDFGraphics2D)g2d; + int type = org.apache.fop.pdf.PDFLink.EXTERNAL; + Shape outline = getOutline(); + if (destination.startsWith("#svgView(viewBox(")) { + type = org.apache.fop.pdf.PDFLink.INTERNAL; + String nums = destination.substring(17, destination.length() - 2); + float x = 0; + float y = 0; + float width = 0; + float height = 0; + int count = 0; + try { + StringTokenizer st = new StringTokenizer(nums, ","); + while (st.hasMoreTokens()) { + String tok = st.nextToken(); + count++; + switch(count) { + case 1: + x = Float.parseFloat(tok); + break; + case 2: + y = Float.parseFloat(tok); + break; + case 3: + width = Float.parseFloat(tok); + break; + case 4: + height = Float.parseFloat(tok); + break; + default: + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + Rectangle2D destRect = new Rectangle2D.Float(x, y, width, height); + destRect = transform.createTransformedShape(destRect).getBounds(); + // these numbers need conversion to current + // svg position and scaled for the page + x = (float)destRect.getX(); + y = (float)destRect.getY(); + width = (float)destRect.getWidth(); + height = (float)destRect.getHeight(); + + destination = "" + x + " " + y + " " + + (x + width) + " " + (y + height); + } + pdfg.addLink(getBounds(), transform, destination, type); + } + } + } + +} + diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java new file mode 100644 index 000000000..0ac52f075 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java @@ -0,0 +1,279 @@ +/* + * $Id: PDFDocumentGraphics2D.java,v 1.27 2003/03/07 09:51:26 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.svg; + +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFPage; +import org.apache.fop.pdf.PDFStream; +import org.apache.fop.pdf.PDFState; +import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.pdf.PDFResources; +import org.apache.fop.pdf.PDFColor; +import org.apache.fop.pdf.PDFAnnotList; +import org.apache.fop.render.pdf.FontSetup; +import org.apache.fop.layout.FontInfo; + +import java.awt.Graphics; +import java.awt.Font; +import java.awt.Color; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.io.OutputStream; +import java.io.IOException; + +/** + * This class is a wrapper for the <tt>PDFGraphics2D</tt> that + * is used to create a full document around the pdf rendering from + * <tt>PDFGraphics2D</tt>. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * @version $Id: PDFDocumentGraphics2D.java,v 1.27 2003/03/07 09:51:26 jeremias Exp $ + * @see org.apache.fop.svg.PDFGraphics2D + */ +public class PDFDocumentGraphics2D extends PDFGraphics2D { + private PDFPage currentPage; + private PDFStream pdfStream; + private int width; + private int height; + + /** + * Create a new PDFDocumentGraphics2D. + * This is used to create a new pdf document, the height, + * width and output stream can be setup later. + * For use by the transcoder which needs font information + * for the bridge before the document size is known. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + */ + PDFDocumentGraphics2D(boolean textAsShapes) { + super(textAsShapes); + + if (!textAsShapes) { + fontInfo = new FontInfo(); + FontSetup.setup(fontInfo, null); + //FontState fontState = new FontState("Helvetica", "normal", + // FontInfo.NORMAL, 12, 0); + } + + this.pdfDoc = new PDFDocument("FOP SVG Renderer"); + + graphicsState = new PDFState(); + + currentFontName = ""; + currentFontSize = 0; + + pdfStream = this.pdfDoc.makeStream(PDFStream.CONTENT_FILTER, false); + } + + /** + * Setup the document. + * @param stream the output stream to write the document + * @param width the width of the page + * @param height the height of the page + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public void setupDocument(OutputStream stream, int width, int height) throws IOException { + this.width = width; + this.height = height; + + PDFResources pdfResources = this.pdfDoc.getResources(); + currentPage = this.pdfDoc.makePage(pdfResources, + width, height); + resourceContext = currentPage; + pageRef = currentPage.referencePDF(); + currentStream.write("1 0 0 -1 0 " + height + " cm\n"); + graphicsState.setTransform(new AffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, (double)height)); + pdfDoc.outputHeader(stream); + + setOutputStream(stream); + } + + /** + * Create a new PDFDocumentGraphics2D. + * This is used to create a new pdf document of the given height + * and width. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + * @param stream the stream that the final document should be written to. + * @param width the width of the document + * @param height the height of the document + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public PDFDocumentGraphics2D(boolean textAsShapes, OutputStream stream, + int width, int height) throws IOException { + this(textAsShapes); + setupDocument(stream, width, height); + } + + /** + * Get the font info for this pdf document. + * @return the font information + */ + public FontInfo getFontInfo() { + return fontInfo; + } + + /** + * Get the pdf document created by this class. + * @return the pdf document + */ + public PDFDocument getPDFDocument() { + return this.pdfDoc; + } + + /** + * Set the dimensions of the svg document that will be drawn. + * This is useful if the dimensions of the svg document are different + * from the pdf document that is to be created. + * The result is scaled so that the svg fits correctly inside the + * pdf document. + * @param w the width of the page + * @param h the height of the page + */ + public void setSVGDimension(float w, float h) { + currentStream.write("" + PDFNumber.doubleOut(width / w) + " 0 0 " + + PDFNumber.doubleOut(height / h) + " 0 0 cm\n"); + } + + /** + * Set the background of the pdf document. + * This is used to set the background for the pdf document + * Rather than leaving it as the default white. + * @param col the background colour to fill + */ + public void setBackgroundColor(Color col) { + Color c = col; + PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(), c.getBlue()); + currentStream.write("q\n"); + currentStream.write(currentColour.getColorSpaceOut(true)); + + currentStream.write("0 0 " + width + " " + height + " re\n"); + + currentStream.write("f\n"); + currentStream.write("Q\n"); + } + + /** + * The rendering process has finished. + * This should be called after the rendering has completed as there is + * no other indication it is complete. + * This will then write the results to the output stream. + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public void finish() throws IOException { + // restorePDFState(); + + pdfStream.add(getString()); + this.pdfDoc.addStream(pdfStream); + currentPage.setContents(pdfStream); + PDFAnnotList annots = currentPage.getAnnotations(); + if (annots != null) { + this.pdfDoc.addAnnotList(annots); + } + this.pdfDoc.addPage(currentPage); + if (fontInfo != null) { + FontSetup.addToResources(pdfDoc, pdfDoc.getResources(), fontInfo); + } + this.pdfDoc.output(outputStream); + pdfDoc.outputTrailer(outputStream); + + outputStream.flush(); + } + + /** + * This constructor supports the create method + * @param g the pdf document graphics to make a copy of + */ + public PDFDocumentGraphics2D(PDFDocumentGraphics2D g) { + super(g); + } + + /** + * Creates a new <code>Graphics</code> object that is + * a copy of this <code>Graphics</code> object. + * @return a new graphics context that is a copy of + * this graphics context. + */ + public Graphics create() { + return new PDFDocumentGraphics2D(this); + } + + /** + * Draw a string to the pdf document. + * This either draws the string directly or if drawing text as + * shapes it converts the string into shapes and draws that. + * @param s the string to draw + * @param x the x position + * @param y the y position + */ + public void drawString(String s, float x, float y) { + if (super.textAsShapes) { + Font font = super.getFont(); + FontRenderContext frc = super.getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(frc, s); + Shape glyphOutline = gv.getOutline(x, y); + super.fill(glyphOutline); + } else { + super.drawString(s, x, y); + } + } + +} + diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java new file mode 100644 index 000000000..64fbdcfe9 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -0,0 +1,1660 @@ +/* + * $Id: PDFGraphics2D.java,v 1.48 2003/03/07 09:51:26 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.svg; + +import org.apache.fop.pdf.PDFResourceContext; +import org.apache.fop.pdf.PDFResources; +import org.apache.fop.pdf.PDFGState; +import org.apache.fop.pdf.PDFColorSpace; +import org.apache.fop.pdf.PDFColor; +import org.apache.fop.pdf.PDFState; +import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.pdf.PDFPattern; +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFLink; +import org.apache.fop.pdf.PDFAnnotList; +import org.apache.fop.pdf.BitmapImage; +import org.apache.fop.layout.FontInfo; +import org.apache.fop.layout.FontState; +import org.apache.fop.render.pdf.FontSetup; +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.LazyFont; +import org.apache.fop.image.JpegImage; +import org.apache.fop.fonts.CIDFont; +import org.apache.fop.render.pdf.FopPDFImage; + +import org.apache.batik.ext.awt.g2d.AbstractGraphics2D; +import org.apache.batik.ext.awt.g2d.GraphicContext; +//import org.apache.batik.ext.awt.MultipleGradientPaint; +import org.apache.batik.ext.awt.RadialGradientPaint; +import org.apache.batik.ext.awt.LinearGradientPaint; +import org.apache.batik.gvt.PatternPaint; +import org.apache.batik.gvt.GraphicsNode; + +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.GraphicsConfiguration; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.Dimension; +import java.awt.BasicStroke; +import java.awt.AlphaComposite; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.Raster; +import java.awt.image.renderable.RenderableImage; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.color.ColorSpace; +import java.io.StringWriter; +import java.io.IOException; +import java.io.OutputStream; + +import java.util.Map; +import java.util.List; + +/** + * PDF Graphics 2D. + * Used for drawing into a pdf document as if it is a graphics object. + * This takes a pdf document and draws into it. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * @version $Id: PDFGraphics2D.java,v 1.48 2003/03/07 09:51:26 jeremias Exp $ + * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D + */ +public class PDFGraphics2D extends AbstractGraphics2D { + /** + * the PDF Document being created + */ + protected PDFDocument pdfDoc; + + /** + * The current resource context for adding fonts, patterns etc. + */ + protected PDFResourceContext resourceContext; + + /** + * The PDF reference of the current page. + */ + protected String pageRef; + + /** + * the current state of the pdf graphics + */ + protected PDFState graphicsState; + + /** + * The PDF graphics state level that this svg is being drawn into. + */ + protected int baseLevel = 0; + + /** + * The current font information. + */ + protected FontInfo fontInfo; + + /** + * The override font state used when drawing text and the font cannot be + * set using java fonts. + */ + protected FontState ovFontState = null; + + /** + * the current stream to add PDF commands to + */ + protected StringWriter currentStream = new StringWriter(); + + /** + * the current (internal) font name + */ + protected String currentFontName; + + /** + * the current font size in millipoints + */ + protected float currentFontSize; + + /** + * The output stream for the pdf document. + * If this is set then it can progressively output + * the pdf document objects to reduce memory. + * Especially with images. + */ + protected OutputStream outputStream = null; + + /** + * Create a new PDFGraphics2D with the given pdf document info. + * This is used to create a Graphics object for use inside an already + * existing document. + * + * @param textAsShapes if true then draw text as shapes + * @param fi the current font information + * @param doc the pdf document for creating pdf objects + * @param page the current resource context or page + * @param pref the PDF reference of the current page + * @param font the current font name + * @param size the current font size + */ + public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc, + PDFResourceContext page, String pref, String font, float size) { + super(textAsShapes); + pdfDoc = doc; + resourceContext = page; + currentFontName = font; + currentFontSize = size; + fontInfo = fi; + pageRef = pref; + graphicsState = new PDFState(); + } + + /** + * Create a new PDFGraphics2D. + * + * @param textAsShapes true if drawing text as shapes + */ + protected PDFGraphics2D(boolean textAsShapes) { + super(textAsShapes); + } + + /** + * Set the PDF state to use when starting to draw + * into the PDF graphics. + * + * @param state the PDF state + */ + public void setPDFState(PDFState state) { + graphicsState = state; + baseLevel = graphicsState.getStackLevel(); + } + + /** + * Set the output stream that this PDF document is + * being drawn to. This is so that it can progressively + * use the PDF document to output data such as images. + * This results in a significant saving on memory. + * + * @param os the output stream that is being used for the PDF document + */ + public void setOutputStream(OutputStream os) { + outputStream = os; + } + + /** + * Get the string containing all the commands written into this + * Grpahics. + * @return the string containing the PDF markup + */ + public String getString() { + return currentStream.toString(); + } + + /** + * Set the Grpahics context. + * @param c the graphics context to use + */ + public void setGraphicContext(GraphicContext c) { + gc = c; + } + + /** + * Set the override font state for drawing text. + * This is used by the PDF text painter so that it can temporarily + * set the font state when a java font cannot be used. + * The next drawString will use this font state. + * + * @param infont the font state to use + */ + public void setOverrideFontState(FontState infont) { + ovFontState = infont; + } + + /** + * This constructor supports the create method. + * This is not implemented properly. + * + * @param g the PDF graphics to make a copy of + */ + public PDFGraphics2D(PDFGraphics2D g) { + super(g); + } + + /** + * Creates a new <code>Graphics</code> object that is + * a copy of this <code>Graphics</code> object. + * @return a new graphics context that is a copy of + * this graphics context. + */ + public Graphics create() { + return new PDFGraphics2D(this); + } + + /** + * Restore the PDF graphics state to the starting state level. + */ + public void restorePDFState() { + for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) { + currentStream.write("Q\n"); + } + graphicsState.restoreLevel(baseLevel); + } + + /** + * This is a pdf specific method used to add a link to the + * pdf document. + * + * @param bounds the bounds of the link in user coordinates + * @param trans the transform of the current drawing position + * @param dest the PDF destination + * @param linkType the type of link, internal or external + */ + public void addLink(Rectangle2D bounds, AffineTransform trans, String dest, int linkType) { + AffineTransform at = getTransform(); + Shape b = at.createTransformedShape(bounds); + b = trans.createTransformedShape(b); + Rectangle rect = b.getBounds(); + + if (linkType != PDFLink.EXTERNAL) { + String pdfdest = "/FitR " + dest; + resourceContext.addAnnotation(pdfDoc.makeLink(rect, pageRef, pdfdest)); + } else { + resourceContext.addAnnotation(pdfDoc.makeLink(rect, + dest, linkType, 0)); + } + } + + /** + * Add a JPEG image directly to the PDF document. + * This is used by the PDFImageElementBridge to draw a JPEG + * directly into the pdf document rather than converting the image into + * a bitmap and increasing the size. + * + * @param jpeg the jpeg image to draw + * @param x the x position + * @param y the y position + * @param width the width to draw the image + * @param height the height to draw the image + */ + public void addJpegImage(JpegImage jpeg, float x, float y, float width, float height) { + FopPDFImage fopimage = new FopPDFImage(jpeg, ""); + int xObjectNum = this.pdfDoc.addImage(resourceContext, fopimage).getXNumber(); + + AffineTransform at = getTransform(); + double[] matrix = new double[6]; + at.getMatrix(matrix); + currentStream.write("q\n"); + Shape imclip = getClip(); + writeClip(imclip); + if (!at.isIdentity()) { + currentStream.write("" + matrix[0] + " " + matrix[1] + " " + + matrix[2] + " " + matrix[3] + " " + + matrix[4] + " " + matrix[5] + " cm\n"); + } + + currentStream.write("" + width + " 0 0 " + + (-height) + " " + + x + " " + + (y + height) + " cm\n" + "/Im" + + xObjectNum + " Do\nQ\n"); + + if (outputStream != null) { + try { + this.pdfDoc.output(outputStream); + } catch (IOException ioe) { + // ignore exception, will be thrown again later + } + } + } + + /** + * Draws as much of the specified image as is currently available. + * The image is drawn with its top-left corner at + * (<i>x</i>, <i>y</i>) in this graphics context's coordinate + * space. Transparent pixels in the image do not affect whatever + * pixels are already there. + * <p> + * This method returns immediately in all cases, even if the + * complete image has not yet been loaded, and it has not been dithered + * and converted for the current output device. + * <p> + * If the image has not yet been completely loaded, then + * <code>drawImage</code> returns <code>false</code>. As more of + * the image becomes available, the process that draws the image notifies + * the specified image observer. + * @param img the specified image to be drawn. + * @param x the <i>x</i> coordinate. + * @param y the <i>y</i> coordinate. + * @param observer object to be notified as more of + * the image is converted. + * @return true if the image was drawn + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ + public boolean drawImage(Image img, int x, int y, + ImageObserver observer) { + // System.err.println("drawImage:x, y"); + + int width = img.getWidth(observer); + int height = img.getHeight(observer); + + if (width == -1 || height == -1) { + return false; + } + + // first we look to see if we've already added this image to + // the pdf document. If so, we just reuse the reference; + // otherwise we have to build a FopImage and add it to the pdf + // document + PDFXObject imageInfo = pdfDoc.getImage("TempImage:" + img.toString()); + if (imageInfo == null) { + // OK, have to build and add a PDF image + + // scale factor + final int scaleFactor = 3; + + Dimension size = new Dimension(width * scaleFactor, height * scaleFactor); + BufferedImage buf = buildBufferedImage(size); + + java.awt.Graphics2D g = buf.createGraphics(); + g.setComposite(AlphaComposite.SrcOver); + g.setBackground(new Color(1, 1, 1, 0)); + g.setPaint(new Color(1, 1, 1, 0)); + g.fillRect(0, 0, width * scaleFactor, height * scaleFactor); + g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight())); + + if (!g.drawImage(img, 0, 0, buf.getWidth(), buf.getHeight(), observer)) { + return false; + } + g.dispose(); + + final byte[] result = new byte[buf.getWidth() * buf.getHeight() * 3]; + byte[] mask = new byte[buf.getWidth() * buf.getHeight()]; + boolean hasMask = false; + //boolean binaryMask = true; + + Raster raster = buf.getData(); + DataBuffer bd = raster.getDataBuffer(); + + int count = 0; + int maskpos = 0; + int[] iarray; + int i, j, val, alpha, add, mult; + switch (bd.getDataType()) { + case DataBuffer.TYPE_INT: + int[][] idata = ((DataBufferInt)bd).getBankData(); + for (i = 0; i < idata.length; i++) { + iarray = idata[i]; + for (j = 0; j < iarray.length; j++) { + val = iarray[j]; + alpha = val >>> 24; + mask[maskpos++] = (byte)(alpha & 0xFF); + if (alpha != 255) { + hasMask = true; + /* + if (alpha != 0) { + binaryMask = false; + }*/ + + // System.out.println("Alpha: " + alpha); + // Composite with opaque white... + add = (255 - alpha); + mult = (alpha << 16) / 255; + result[count++] = + (byte)(add + + ((((val >> 16) & 0xFF) * mult) >> 16)); + result[count++] = + (byte)(add + + ((((val >> 8) & 0xFF) * mult) >> 16)); + result[count++] = (byte)(add + + ((((val) & 0xFF) * mult) + >> 16)); + } else { + result[count++] = (byte)((val >> 16) & 0xFF); + result[count++] = (byte)((val >> 8) & 0xFF); + result[count++] = (byte)((val) & 0xFF); + } + } + } + break; + default: + // error + break; + } + String ref = null; + if (hasMask) { + // if the mask is binary then we could convert it into a bitmask + BitmapImage fopimg = new BitmapImage("TempImageMask:" + + img.toString(), buf.getWidth(), + buf.getHeight(), mask, null); + fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY)); + PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg); + ref = xobj.referencePDF(); + + if (outputStream != null) { + try { + this.pdfDoc.output(outputStream); + } catch (IOException ioe) { + // ignore exception, will be thrown again later + } + } + } else { + mask = null; + } + + BitmapImage fopimg = new BitmapImage("TempImage:" + + img.toString(), buf.getWidth(), + buf.getHeight(), result, ref); + fopimg.setTransparent(new PDFColor(255, 255, 255)); + imageInfo = pdfDoc.addImage(resourceContext, fopimg); + //int xObjectNum = imageInfo.getXNumber(); + + if (outputStream != null) { + try { + this.pdfDoc.output(outputStream); + } catch (IOException ioe) { + // ignore exception, will be thrown again later + } + } + } else { + resourceContext.getPDFResources().addXObject(imageInfo); + } + + // now do any transformation required and add the actual image + // placement instance + AffineTransform at = getTransform(); + double[] matrix = new double[6]; + at.getMatrix(matrix); + currentStream.write("q\n"); + Shape imclip = getClip(); + writeClip(imclip); + if (!at.isIdentity()) { + currentStream.write("" + matrix[0] + " " + matrix[1] + " " + + matrix[2] + " " + matrix[3] + " " + + matrix[4] + " " + matrix[5] + " cm\n"); + } + currentStream.write("" + width + " 0 0 " + (-height) + " " + x + + " " + (y + height) + " cm\n" + "/Im" + + imageInfo.getXNumber() + " Do\nQ\n"); + return true; + } + + private BufferedImage buildBufferedImage(Dimension size) { + return new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_ARGB); + } + + /** + * Draws as much of the specified image as has already been scaled + * to fit inside the specified rectangle. + * <p> + * The image is drawn inside the specified rectangle of this + * graphics context's coordinate space, and is scaled if + * necessary. Transparent pixels do not affect whatever pixels + * are already there. + * <p> + * This method returns immediately in all cases, even if the + * entire image has not yet been scaled, dithered, and converted + * for the current output device. + * If the current output representation is not yet complete, then + * <code>drawImage</code> returns <code>false</code>. As more of + * the image becomes available, the process that draws the image notifies + * the image observer by calling its <code>imageUpdate</code> method. + * <p> + * A scaled version of an image will not necessarily be + * available immediately just because an unscaled version of the + * image has been constructed for this output device. Each size of + * the image may be cached separately and generated from the original + * data in a separate image production sequence. + * @param img the specified image to be drawn. + * @param x the <i>x</i> coordinate. + * @param y the <i>y</i> coordinate. + * @param width the width of the rectangle. + * @param height the height of the rectangle. + * @param observer object to be notified as more of + * the image is converted. + * @return true if the image was drawn + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) { + System.out.println("drawImage"); + return true; + } + + /** + * Disposes of this graphics context and releases + * any system resources that it is using. + * A <code>Graphics</code> object cannot be used after + * <code>dispose</code>has been called. + * <p> + * When a Java program runs, a large number of <code>Graphics</code> + * objects can be created within a short time frame. + * Although the finalization process of the garbage collector + * also disposes of the same system resources, it is preferable + * to manually free the associated resources by calling this + * method rather than to rely on a finalization process which + * may not run to completion for a long period of time. + * <p> + * Graphics objects which are provided as arguments to the + * <code>paint</code> and <code>update</code> methods + * of components are automatically released by the system when + * those methods return. For efficiency, programmers should + * call <code>dispose</code> when finished using + * a <code>Graphics</code> object only if it was created + * directly from a component or another <code>Graphics</code> object. + * @see java.awt.Graphics#finalize + * @see java.awt.Component#paint + * @see java.awt.Component#update + * @see java.awt.Component#getGraphics + * @see java.awt.Graphics#create + */ + public void dispose() { + // System.out.println("dispose"); + pdfDoc = null; + fontInfo = null; + currentStream = null; + currentFontName = null; + } + + /** + * Strokes the outline of a <code>Shape</code> using the settings of the + * current <code>Graphics2D</code> context. The rendering attributes + * applied include the <code>Clip</code>, <code>Transform</code>, + * <code>Paint</code>, <code>Composite</code> and + * <code>Stroke</code> attributes. + * @param s the <code>Shape</code> to be rendered + * @see #setStroke + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #transform + * @see #setTransform + * @see #clip + * @see #setClip + * @see #setComposite + */ + public void draw(Shape s) { + // System.out.println("draw(Shape)"); + Color c; + c = getColor(); + if (c.getAlpha() == 0) { + return; + } + + AffineTransform trans = getTransform(); + double[] tranvals = new double[6]; + trans.getMatrix(tranvals); + + Shape imclip = getClip(); + boolean newClip = graphicsState.checkClip(imclip); + boolean newTransform = graphicsState.checkTransform(trans) + && !trans.isIdentity(); + + if (newClip || newTransform) { + currentStream.write("q\n"); + graphicsState.push(); + if (newClip) { + writeClip(imclip); + } + if (newTransform) { + currentStream.write(PDFNumber.doubleOut(tranvals[0], 5) + " " + + PDFNumber.doubleOut(tranvals[1], 5) + " " + + PDFNumber.doubleOut(tranvals[2], 5) + " " + + PDFNumber.doubleOut(tranvals[3], 5) + " " + + PDFNumber.doubleOut(tranvals[4], 5) + " " + + PDFNumber.doubleOut(tranvals[5], 5) + " cm\n"); + } + } + + if (c.getAlpha() != 255) { + Map vals = new java.util.HashMap(); + vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(c.getAlpha() / 255f)); + PDFGState gstate = pdfDoc.makeGState(vals, graphicsState.getGState()); + //gstate.setAlpha(c.getAlpha() / 255f, false); + resourceContext.addGState(gstate); + currentStream.write("/" + gstate.getName() + " gs\n"); + } + + applyColor(c, false); + + applyPaint(getPaint(), false); + applyStroke(getStroke()); + + PathIterator iter = s.getPathIterator(new AffineTransform()); + while (!iter.isDone()) { + double vals[] = new double[6]; + int type = iter.currentSegment(vals); + switch (type) { + case PathIterator.SEG_CUBICTO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " " + + PDFNumber.doubleOut(vals[2], 5) + " " + + PDFNumber.doubleOut(vals[3], 5) + " " + + PDFNumber.doubleOut(vals[4], 5) + " " + + PDFNumber.doubleOut(vals[5], 5) + " c\n"); + break; + case PathIterator.SEG_LINETO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " l\n"); + break; + case PathIterator.SEG_MOVETO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " m\n"); + break; + case PathIterator.SEG_QUADTO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " " + + PDFNumber.doubleOut(vals[2], 5) + " " + + PDFNumber.doubleOut(vals[3], 5) + " y\n"); + break; + case PathIterator.SEG_CLOSE: + currentStream.write("h\n"); + break; + default: + break; + } + iter.next(); + } + doDrawing(false, true, false); + if (newClip || newTransform) { + currentStream.write("Q\n"); + graphicsState.pop(); + } + } + +/* + // in theory we could set the clip using these methods + // it doesn't seem to improve the file sizes much + // and makes everything more complicated + + Shape lastClip = null; + + public void clip(Shape cl) { + super.clip(cl); + Shape newClip = getClip(); + if (newClip == null || lastClip == null + || !(new Area(newClip).equals(new Area(lastClip)))) { + graphicsState.setClip(newClip); + writeClip(newClip); + } + + lastClip = newClip; + } + + public void setClip(Shape cl) { + super.setClip(cl); + Shape newClip = getClip(); + if (newClip == null || lastClip == null + || !(new Area(newClip).equals(new Area(lastClip)))) { + for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) { + currentStream.write("Q\n"); + } + graphicsState.restoreLevel(baseLevel); + currentStream.write("q\n"); + graphicsState.push(); + if (newClip != null) { + graphicsState.setClip(newClip); + } + writeClip(newClip); + } + + lastClip = newClip; + } +*/ + + /** + * Set the clipping shape for future PDF drawing in the current graphics state. + * This sets creates and writes a clipping shape that will apply + * to future drawings in the current graphics state. + * + * @param s the clipping shape + */ + protected void writeClip(Shape s) { + if (s == null) { + return; + } + PathIterator iter = s.getPathIterator(getTransform()); + while (!iter.isDone()) { + double vals[] = new double[6]; + int type = iter.currentSegment(vals); + switch (type) { + case PathIterator.SEG_CUBICTO: + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " " + + PDFNumber.doubleOut(vals[2]) + " " + + PDFNumber.doubleOut(vals[3]) + " " + + PDFNumber.doubleOut(vals[4]) + " " + + PDFNumber.doubleOut(vals[5]) + " c\n"); + break; + case PathIterator.SEG_LINETO: + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " l\n"); + break; + case PathIterator.SEG_MOVETO: + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " m\n"); + break; + case PathIterator.SEG_QUADTO: + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " " + + PDFNumber.doubleOut(vals[2]) + " " + + PDFNumber.doubleOut(vals[3]) + " y\n"); + break; + case PathIterator.SEG_CLOSE: + currentStream.write("h\n"); + break; + default: + break; + } + iter.next(); + } + // clip area + currentStream.write("W\n"); + currentStream.write("n\n"); + } + + /** + * Apply the java Color to PDF. + * This converts the java colour to a PDF colour and + * sets it for the next drawing. + * + * @param col the java colour + * @param fill true if the colour will be used for filling + */ + protected void applyColor(Color col, boolean fill) { + Color c = col; + if (c.getColorSpace().getType() + == ColorSpace.TYPE_RGB) { + PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(), + c.getBlue()); + currentStream.write(currentColour.getColorSpaceOut(fill)); + } else if (c.getColorSpace().getType() + == ColorSpace.TYPE_CMYK) { + float[] cComps = c.getColorComponents(new float[3]); + double[] cmyk = new double[3]; + for (int i = 0; i < 3; i++) { + // convert the float elements to doubles for pdf + cmyk[i] = cComps[i]; + } + PDFColor currentColour = new PDFColor(cmyk[0], cmyk[1], cmyk[2], cmyk[3]); + currentStream.write(currentColour.getColorSpaceOut(fill)); + } else if (c.getColorSpace().getType() + == ColorSpace.TYPE_2CLR) { + // used for black/magenta + float[] cComps = c.getColorComponents(new float[1]); + double[] blackMagenta = new double[1]; + for (int i = 0; i < 1; i++) { + blackMagenta[i] = cComps[i]; + } + //PDFColor currentColour = new PDFColor(blackMagenta[0], blackMagenta[1]); + //currentStream.write(currentColour.getColorSpaceOut(fill)); + } else { + System.err.println("Color Space not supported by PDFGraphics2D"); + } + } + + /** + * Apply the java paint to the PDF. + * This takes the java paint sets up the appropraite PDF commands + * for the drawing with that paint. + * Currently this supports the gradients and patterns from batik. + * + * @param paint the paint to convert to PDF + * @param fill true if the paint should be set for filling + */ + protected void applyPaint(Paint paint, boolean fill) { + + if (paint instanceof LinearGradientPaint) { + LinearGradientPaint gp = (LinearGradientPaint)paint; + Color[] cols = gp.getColors(); + float[] fractions = gp.getFractions(); + Point2D p1 = gp.getStartPoint(); + Point2D p2 = gp.getEndPoint(); + //MultipleGradientPaint.CycleMethodEnum cycenum = gp.getCycleMethod(); + //boolean cyclic = (cycenum == MultipleGradientPaint.REPEAT); + AffineTransform transform = graphicsState.getTransform(); + transform.concatenate(gp.getTransform()); + + p1 = transform.transform(p1, null); + p2 = transform.transform(p2, null); + + List theCoords = new java.util.ArrayList(); + theCoords.add(new Double(p1.getX())); + theCoords.add(new Double(p1.getY())); + theCoords.add(new Double(p2.getX())); + theCoords.add(new Double(p2.getY())); + + List theExtend = new java.util.ArrayList(); + theExtend.add(new Boolean(true)); + theExtend.add(new Boolean(true)); + + List theDomain = new java.util.ArrayList(); + theDomain.add(new Double(0)); + theDomain.add(new Double(1)); + + List theEncode = new java.util.ArrayList(); + theEncode.add(new Double(0)); + theEncode.add(new Double(1)); + theEncode.add(new Double(0)); + theEncode.add(new Double(1)); + + List theBounds = new java.util.ArrayList(); + + List someColors = new java.util.ArrayList(); + + for (int count = 0; count < cols.length; count++) { + Color c1 = cols[count]; + PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(), + c1.getBlue()); + someColors.add(color1); + if (count > 0 && count < cols.length - 1) { + theBounds.add(new Double(fractions[count])); + } + } + + PDFColorSpace aColorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB); + PDFPattern myPat = pdfDoc.createGradient(resourceContext, false, aColorSpace, + someColors, theBounds, theCoords); + currentStream.write(myPat.getColorSpaceOut(fill)); + + } else if (paint instanceof RadialGradientPaint) { + RadialGradientPaint rgp = (RadialGradientPaint)paint; + + double ar = rgp.getRadius(); + Point2D ac = rgp.getCenterPoint(); + Point2D af = rgp.getFocusPoint(); + AffineTransform transform = graphicsState.getTransform(); + AffineTransform gradt = rgp.getTransform(); + transform.concatenate(gradt); + + // find largest scaling for the radius + double scale = gradt.getScaleX(); + if (gradt.getScaleY() > scale) { + scale = gradt.getScaleY(); + } + ar = ar * scale; + ac = transform.transform(ac, null); + af = transform.transform(af, null); + + List theCoords = new java.util.ArrayList(); + // the center point af must be within the circle with + // radius ar centered at ac + theCoords.add(new Double(af.getX())); + theCoords.add(new Double(af.getY())); + theCoords.add(new Double(0)); + theCoords.add(new Double(ac.getX())); // Fx + theCoords.add(new Double(ac.getY())); // Fy + theCoords.add(new Double(ar)); + + Color[] cols = rgp.getColors(); + List someColors = new java.util.ArrayList(); + for (int count = 0; count < cols.length; count++) { + Color cc = cols[count]; + someColors.add(new PDFColor(cc.getRed(), cc.getGreen(), cc.getBlue())); + } + + float[] fractions = rgp.getFractions(); + List theBounds = new java.util.ArrayList(); + for (int count = 1; count < fractions.length - 1; count++) { + float offset = fractions[count]; + theBounds.add(new Double(offset)); + } + PDFColorSpace colSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB); + PDFPattern myPat = pdfDoc.createGradient(resourceContext, true, colSpace, + someColors, theBounds, theCoords); + + currentStream.write(myPat.getColorSpaceOut(fill)); + + } else if (paint instanceof PatternPaint) { + PatternPaint pp = (PatternPaint)paint; + createPattern(pp, fill); + } + } + + private void createPattern(PatternPaint pp, boolean fill) { + Rectangle2D rect = pp.getPatternRect(); + + FontInfo fi = new FontInfo(); + FontSetup.setup(fi, null); + + PDFResources res = pdfDoc.makeResources(); + PDFResourceContext context = new PDFResourceContext(0, pdfDoc, res); + PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, fi, + pdfDoc, context, pageRef, + "", 0); + pattGraphic.gc = (GraphicContext)this.gc.clone(); + pattGraphic.gc.validateTransformStack(); + pattGraphic.setOutputStream(outputStream); + + GraphicsNode gn = pp.getGraphicsNode(); + gn.paint(pattGraphic); + + StringWriter pattStream = new StringWriter(); + pattStream.write("q\n"); + + // this makes the pattern the right way up, since + // it is outside the original transform around the + // whole svg document + pattStream.write("1 0 0 -1 0 " + (rect.getHeight() + rect.getY()) + " cm\n"); + + pattStream.write(pattGraphic.getString()); + pattStream.write("Q"); + + List bbox = new java.util.ArrayList(); + bbox.add(new Double(0)); + bbox.add(new Double(0)); + bbox.add(new Double(rect.getWidth() + rect.getX())); + bbox.add(new Double(rect.getHeight() + rect.getY())); + + List translate = new java.util.ArrayList(); + AffineTransform pattt = pp.getPatternTransform(); + pattt.translate(rect.getWidth() + rect.getX(), rect.getHeight() + rect.getY()); + double[] flatmatrix = new double[6]; + pattt.getMatrix(flatmatrix); + translate.add(new Double(flatmatrix[0])); + translate.add(new Double(flatmatrix[1])); + translate.add(new Double(flatmatrix[2])); + translate.add(new Double(flatmatrix[3])); + translate.add(new Double(flatmatrix[4])); + translate.add(new Double(flatmatrix[5])); + + FontSetup.addToResources(pdfDoc, res, fi); + + PDFPattern myPat = pdfDoc.makePattern(resourceContext, 1, res, 1, 1, bbox, + rect.getWidth(), rect.getHeight(), + translate, null, pattStream.getBuffer()); + + currentStream.write(myPat.getColorSpaceOut(fill)); + + PDFAnnotList annots = context.getAnnotations(); + if (annots != null) { + this.pdfDoc.addAnnotList(annots); + } + + if (outputStream != null) { + try { + this.pdfDoc.output(outputStream); + } catch (IOException ioe) { + // ignore exception, will be thrown again later + } + } + } + + /** + * Apply the stroke to the PDF. + * This takes the java stroke and outputs the appropriate settings + * to the PDF so that the stroke attributes are handled. + * + * @param stroke the java stroke + */ + protected void applyStroke(Stroke stroke) { + if (stroke instanceof BasicStroke) { + BasicStroke bs = (BasicStroke)stroke; + + float[] da = bs.getDashArray(); + if (da != null) { + currentStream.write("["); + for (int count = 0; count < da.length; count++) { + if (((int)da[count]) == 0) { + // the dasharray units in pdf are (whole) numbers + // in user space units, cannot be 0 + currentStream.write("1"); + } else { + currentStream.write("" + ((int)da[count])); + } + if (count < da.length - 1) { + currentStream.write(" "); + } + } + currentStream.write("] "); + float offset = bs.getDashPhase(); + currentStream.write(((int)offset) + " d\n"); + } + int ec = bs.getEndCap(); + switch (ec) { + case BasicStroke.CAP_BUTT: + currentStream.write(0 + " J\n"); + break; + case BasicStroke.CAP_ROUND: + currentStream.write(1 + " J\n"); + break; + case BasicStroke.CAP_SQUARE: + currentStream.write(2 + " J\n"); + break; + } + + int lj = bs.getLineJoin(); + switch (lj) { + case BasicStroke.JOIN_MITER: + currentStream.write(0 + " j\n"); + break; + case BasicStroke.JOIN_ROUND: + currentStream.write(1 + " j\n"); + break; + case BasicStroke.JOIN_BEVEL: + currentStream.write(2 + " j\n"); + break; + } + float lw = bs.getLineWidth(); + currentStream.write(PDFNumber.doubleOut(lw) + " w\n"); + + float ml = bs.getMiterLimit(); + currentStream.write(PDFNumber.doubleOut(ml) + " M\n"); + } + } + + /** + * Renders a {@link RenderedImage}, + * applying a transform from image + * space into user space before drawing. + * The transformation from user space into device space is done with + * the current <code>Transform</code> in the <code>Graphics2D</code>. + * The specified transformation is applied to the image before the + * transform attribute in the <code>Graphics2D</code> context is applied. + * The rendering attributes applied include the <code>Clip</code>, + * <code>Transform</code>, and <code>Composite</code> attributes. Note + * that no rendering is done if the specified transform is + * noninvertible. + * @param img the image to be rendered + * @param xform the transformation from image space into user space + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + */ + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + System.out.println("drawRenderedImage"); + } + + /** + * Renders a + * {@link RenderableImage}, + * applying a transform from image space into user space before drawing. + * The transformation from user space into device space is done with + * the current <code>Transform</code> in the <code>Graphics2D</code>. + * The specified transformation is applied to the image before the + * transform attribute in the <code>Graphics2D</code> context is applied. + * The rendering attributes applied include the <code>Clip</code>, + * <code>Transform</code>, and <code>Composite</code> attributes. Note + * that no rendering is done if the specified transform is + * noninvertible. + * <p> + * Rendering hints set on the <code>Graphics2D</code> object might + * be used in rendering the <code>RenderableImage</code>. + * If explicit control is required over specific hints recognized by a + * specific <code>RenderableImage</code>, or if knowledge of which hints + * are used is required, then a <code>RenderedImage</code> should be + * obtained directly from the <code>RenderableImage</code> + * and rendered using + * {@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}. + * @param img the image to be rendered + * @param xform the transformation from image space into user space + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + * @see #drawRenderedImage + */ + public void drawRenderableImage(RenderableImage img, + AffineTransform xform) { + System.out.println("drawRenderableImage"); + } + + /** + * Renders the text specified by the specified <code>String</code>, + * using the current <code>Font</code> and <code>Paint</code> attributes + * in the <code>Graphics2D</code> context. + * The baseline of the first character is at position + * (<i>x</i>, <i>y</i>) in the User Space. + * The rendering attributes applied include the <code>Clip</code>, + * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and + * <code>Composite</code> attributes. For characters in script systems + * such as Hebrew and Arabic, the glyphs can be rendered from right to + * left, in which case the coordinate supplied is the location of the + * leftmost character on the baseline. + * @param s the <code>String</code> to be rendered + * @param x the coordinate where the <code>String</code> + * should be rendered + * @param y the coordinate where the <code>String</code> + * should be rendered + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see java.awt.Graphics#setFont + * @see #setTransform + * @see #setComposite + * @see #setClip + */ + public void drawString(String s, float x, float y) { + // System.out.println("drawString(String)"); + + FontState fontState; + if (ovFontState == null) { + Font gFont = getFont(); + String n = gFont.getFamily(); + if (n.equals("sanserif")) { + n = "sans-serif"; + } + int siz = gFont.getSize(); + String style = gFont.isItalic() ? "italic" : "normal"; + int weight = gFont.isBold() ? FontInfo.BOLD : FontInfo.NORMAL; + String fname = fontInfo.fontLookup(n, style, weight); + FontMetrics metrics = fontInfo.getMetricsFor(fname); + fontState = new FontState(fname, metrics, siz * 1000); + } else { + FontMetrics metrics = fontInfo.getMetricsFor(ovFontState.getFontName()); + fontState = new FontState(ovFontState.getFontName(), + metrics, ovFontState.getFontSize()); + ovFontState = null; + } + String name; + float size; + name = fontState.getFontName(); + size = (float)fontState.getFontSize() / 1000f; + + if ((!name.equals(this.currentFontName)) + || (size != this.currentFontSize)) { + this.currentFontName = name; + this.currentFontSize = size; + currentStream.write("/" + name + " " + size + " Tf\n"); + + } + + currentStream.write("q\n"); + + Shape imclip = getClip(); + writeClip(imclip); + Color c = getColor(); + applyColor(c, true); + int salpha = c.getAlpha(); + + if (salpha != 255) { + Map vals = new java.util.HashMap(); + vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f)); + PDFGState gstate = pdfDoc.makeGState(vals, graphicsState.getGState()); + resourceContext.addGState(gstate); + currentStream.write("/" + gstate.getName() + " gs\n"); + } + + currentStream.write("BT\n"); + + Map kerning = null; + boolean kerningAvailable = false; + + kerning = fontState.getKerning(); + if (kerning != null && !kerning.isEmpty()) { + kerningAvailable = true; + } + + // This assumes that *all* CIDFonts use a /ToUnicode mapping + boolean useMultiByte = false; + org.apache.fop.fonts.Font f = + (org.apache.fop.fonts.Font)fontInfo.getFonts().get(name); + if (f instanceof LazyFont) { + if (((LazyFont) f).getRealFont() instanceof CIDFont) { + useMultiByte = true; + } + } else if (f instanceof CIDFont) { + useMultiByte = true; + } + + // String startText = useMultiByte ? "<FEFF" : "("; + String startText = useMultiByte ? "<" : "("; + String endText = useMultiByte ? "> " : ") "; + + AffineTransform trans = getTransform(); + trans.translate(x, y); + double[] vals = new double[6]; + trans.getMatrix(vals); + + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " " + + PDFNumber.doubleOut(vals[2]) + " " + + PDFNumber.doubleOut(vals[3]) + " " + + PDFNumber.doubleOut(vals[4]) + " " + + PDFNumber.doubleOut(vals[5]) + " cm\n"); + currentStream.write("1 0 0 -1 0 0 Tm [" + startText); + + int l = s.length(); + + for (int i = 0; i < l; i++) { + char ch = fontState.mapChar(s.charAt(i)); + + if (!useMultiByte) { + if (ch > 127) { + currentStream.write("\\"); + currentStream.write(Integer.toOctalString((int)ch)); + } else { + switch (ch) { + case '(': + case ')': + case '\\': + currentStream.write("\\"); + break; + } + currentStream.write(ch); + } + } else { + currentStream.write(getUnicodeString(ch)); + } + + if (kerningAvailable && (i + 1) < l) { + addKerning(currentStream, (new Integer((int)ch)), + (new Integer((int)fontState.mapChar(s.charAt(i + 1)))), + kerning, startText, endText); + } + + } + currentStream.write(endText); + + + currentStream.write("] TJ\n"); + + currentStream.write("ET\n"); + currentStream.write("Q\n"); + } + + private void addKerning(StringWriter 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) { + currentStream.write(endText + (-width.intValue()) + " " + startText); + } + } + } + + /** + * 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 (Exception 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(); + } + + /** + * Renders the text of the specified iterator, using the + * <code>Graphics2D</code> context's current <code>Paint</code>. The + * iterator must specify a font + * for each character. The baseline of the + * first character is at position (<i>x</i>, <i>y</i>) in the + * User Space. + * The rendering attributes applied include the <code>Clip</code>, + * <code>Transform</code>, <code>Paint</code>, and + * <code>Composite</code> attributes. + * For characters in script systems such as Hebrew and Arabic, + * the glyphs can be rendered from right to left, in which case the + * coordinate supplied is the location of the leftmost character + * on the baseline. + * @param iterator the iterator whose text is to be rendered + * @param x the coordinate where the iterator's text is to be + * rendered + * @param y the coordinate where the iterator's text is to be + * rendered + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #setTransform + * @see #setComposite + * @see #setClip + */ + public void drawString(AttributedCharacterIterator iterator, float x, + float y) { + System.err.println("drawString(AttributedCharacterIterator)"); + + FontState fontState = null; + + Shape imclip = getClip(); + writeClip(imclip); + Color c = getColor(); + applyColor(c, true); + + boolean fill = true; + boolean stroke = false; + if (true) { + Stroke currentStroke = getStroke(); + stroke = true; + applyStroke(currentStroke); + applyColor(c, false); + } + + currentStream.write("BT\n"); + + // set text rendering mode: + // 0 - fill, 1 - stroke, 2 - fill then stroke + int textr = 0; + if (fill && stroke) { + textr = 2; + } else if (stroke) { + textr = 1; + } + currentStream.write(textr + " Tr\n"); + + AffineTransform trans = getTransform(); + trans.translate(x, y); + double[] vals = new double[6]; + trans.getMatrix(vals); + + for (char ch = iterator.first(); ch != CharacterIterator.DONE; + ch = iterator.next()) { + //Map attr = iterator.getAttributes(); + + String name = fontState.getFontName(); + int size = fontState.getFontSize(); + if ((!name.equals(this.currentFontName)) + || (size != this.currentFontSize)) { + this.currentFontName = name; + this.currentFontSize = size; + currentStream.write("/" + name + " " + (size / 1000) + + " Tf\n"); + + } + + currentStream.write(PDFNumber.doubleOut(vals[0]) + " " + + PDFNumber.doubleOut(vals[1]) + " " + + PDFNumber.doubleOut(vals[2]) + " " + + PDFNumber.doubleOut(vals[3]) + " " + + PDFNumber.doubleOut(vals[4]) + " " + + PDFNumber.doubleOut(vals[5]) + " Tm (" + ch + + ") Tj\n"); + } + + currentStream.write("ET\n"); + } + + /** + * Fills the interior of a <code>Shape</code> using the settings of the + * <code>Graphics2D</code> context. The rendering attributes applied + * include the <code>Clip</code>, <code>Transform</code>, + * <code>Paint</code>, and <code>Composite</code>. + * @param s the <code>Shape</code> to be filled + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + */ + public void fill(Shape s) { + // System.err.println("fill"); + Color c; + c = getBackground(); + if (c.getAlpha() == 0) { + c = getColor(); + if (c.getAlpha() == 0) { + return; + } + } + Shape imclip = getClip(); + boolean newState = graphicsState.checkClip(imclip); + + if (newState) { + currentStream.write("q\n"); + graphicsState.push(); + writeClip(imclip); + graphicsState.setClip(imclip); + } + + if (c.getAlpha() != 255) { + Map vals = new java.util.HashMap(); + vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(c.getAlpha() / 255f)); + PDFGState gstate = pdfDoc.makeGState(vals, graphicsState.getGState()); + resourceContext.addGState(gstate); + currentStream.write("/" + gstate.getName() + " gs\n"); + } + + c = getColor(); + if (graphicsState.setColor(c)) { + applyColor(c, true); + } + c = getBackground(); + if (graphicsState.setBackColor(c)) { + applyColor(c, false); + } + + Paint paint = getPaint(); + if (graphicsState.setPaint(paint)) { + applyPaint(paint, true); + } + + PathIterator iter = s.getPathIterator(getTransform()); + while (!iter.isDone()) { + double vals[] = new double[6]; + int type = iter.currentSegment(vals); + switch (type) { + case PathIterator.SEG_CUBICTO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " " + + PDFNumber.doubleOut(vals[2], 5) + " " + + PDFNumber.doubleOut(vals[3], 5) + " " + + PDFNumber.doubleOut(vals[4], 5) + " " + + PDFNumber.doubleOut(vals[5], 5) + " c\n"); + break; + case PathIterator.SEG_LINETO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " l\n"); + break; + case PathIterator.SEG_MOVETO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " m\n"); + break; + case PathIterator.SEG_QUADTO: + currentStream.write(PDFNumber.doubleOut(vals[0], 5) + " " + + PDFNumber.doubleOut(vals[1], 5) + " " + + PDFNumber.doubleOut(vals[2], 5) + " " + + PDFNumber.doubleOut(vals[3], 5) + " y\n"); + break; + case PathIterator.SEG_CLOSE: + currentStream.write("h\n"); + break; + default: + break; + } + iter.next(); + } + doDrawing(true, false, + iter.getWindingRule() == PathIterator.WIND_EVEN_ODD); + if (newState) { + currentStream.write("Q\n"); + graphicsState.pop(); + } + } + + /** + * Do the PDF drawing command. + * This does the PDF drawing command according to fill + * stroke and winding rule. + * + * @param fill true if filling the path + * @param stroke true if stroking the path + * @param nonzero true if using the non-zero winding rule + */ + protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) { + if (fill) { + if (stroke) { + if (nonzero) { + currentStream.write("B*\n"); + } else { + currentStream.write("B\n"); + } + } else { + if (nonzero) { + currentStream.write("f*\n"); + } else { + currentStream.write("f\n"); + } + } + } else { + // if (stroke) + currentStream.write("S\n"); + } + } + + /** + * Returns the device configuration associated with this + * <code>Graphics2D</code>. + * + * @return the PDF graphics configuration + */ + public GraphicsConfiguration getDeviceConfiguration() { + return new PDFGraphicsConfiguration(); + } + + /** + * Used to create proper font metrics + */ + private Graphics2D fmg; + + { + BufferedImage bi = new BufferedImage(1, 1, + BufferedImage.TYPE_INT_ARGB); + + fmg = bi.createGraphics(); + } + + /** + * Gets the font metrics for the specified font. + * @return the font metrics for the specified font. + * @param f the specified font + * @see java.awt.Graphics#getFont + * @see java.awt.FontMetrics + * @see java.awt.Graphics#getFontMetrics() + */ + public java.awt.FontMetrics getFontMetrics(Font f) { + return fmg.getFontMetrics(f); + } + + /** + * Sets the paint mode of this graphics context to alternate between + * this graphics context's current color and the new specified color. + * This specifies that logical pixel operations are performed in the + * XOR mode, which alternates pixels between the current color and + * a specified XOR color. + * <p> + * When drawing operations are performed, pixels which are the + * current color are changed to the specified color, and vice versa. + * <p> + * Pixels that are of colors other than those two colors are changed + * in an unpredictable but reversible manner; if the same figure is + * drawn twice, then all pixels are restored to their original values. + * @param c1 the XOR alternation color + */ + public void setXORMode(Color c1) { + System.out.println("setXORMode"); + } + + + /** + * Copies an area of the component by a distance specified by + * <code>dx</code> and <code>dy</code>. From the point specified + * by <code>x</code> and <code>y</code>, this method + * copies downwards and to the right. To copy an area of the + * component to the left or upwards, specify a negative value for + * <code>dx</code> or <code>dy</code>. + * If a portion of the source rectangle lies outside the bounds + * of the component, or is obscured by another window or component, + * <code>copyArea</code> will be unable to copy the associated + * pixels. The area that is omitted can be refreshed by calling + * the component's <code>paint</code> method. + * @param x the <i>x</i> coordinate of the source rectangle. + * @param y the <i>y</i> coordinate of the source rectangle. + * @param width the width of the source rectangle. + * @param height the height of the source rectangle. + * @param dx the horizontal distance to copy the pixels. + * @param dy the vertical distance to copy the pixels. + */ + public void copyArea(int x, int y, int width, int height, int dx, + int dy) { + System.out.println("copyArea"); + } + +} diff --git a/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.javat b/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.javat new file mode 100644 index 000000000..8b24b18c3 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.javat @@ -0,0 +1,178 @@ +/* + * $Id: PDFGraphicsConfiguration.javat,v 1.2 2003/03/07 09:51:25 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.svg; + +import java.awt.GraphicsConfiguration; +import java.awt.Rectangle; +import java.awt.GraphicsDevice; +import java.awt.Transparency; +import java.awt.image.ColorModel; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; + +/** + * Our implementation of the class that returns information about + * roughly what we can handle and want to see (alpha for example). + */ +class PDFGraphicsConfiguration extends GraphicsConfiguration { + // We use this to get a good colormodel.. + private static final BufferedImage BI_WITH_ALPHA = + new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + // We use this to get a good colormodel.. + private static final BufferedImage BI_WITHOUT_ALPHA = + new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + + /** + * Construct a buffered image with an alpha channel, unless + * transparencty is OPAQUE (no alpha at all). + * + * @param width the width of the image + * @param height the height of the image + * @param transparency the alpha value of the image + * @return the new buffered image + */ + public BufferedImage createCompatibleImage(int width, int height, + int transparency) { + if (transparency == Transparency.OPAQUE) { + return new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + } else { + return new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + } + } + + /** + * Construct a buffered image with an alpha channel. + * + * @param width the width of the image + * @param height the height of the image + * @return the new buffered image + */ + public BufferedImage createCompatibleImage(int width, int height) { + return new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + } + + /** + * FIXX ME: This should return the page bounds in Pts, + * I couldn't figure out how to get this for the current + * page from the PDFDocument (this still works for now, + * but it should be fixed...). + * + * @return the bounds of the PDF document page + */ + public Rectangle getBounds() { + System.out.println("getting getBounds"); + return null; + } + + /** + * Return a good default color model for this 'device'. + * @return the colour model for the configuration + */ + public ColorModel getColorModel() { + return BI_WITH_ALPHA.getColorModel(); + } + + /** + * Return a good color model given <tt>transparency</tt> + * + * @param transparency the alpha value for the colour model + * @return the colour model for the configuration + */ + public ColorModel getColorModel(int transparency) { + if (transparency == Transparency.OPAQUE) { + return BI_WITHOUT_ALPHA.getColorModel(); + } else { + return BI_WITH_ALPHA.getColorModel(); + } + } + + /** + * The default transform (1:1). + * + * @return the default transform for the configuration + */ + public AffineTransform getDefaultTransform() { + System.out.println("getting getDefaultTransform"); + return new AffineTransform(); + } + + /** + * The normalizing transform (1:1) (since we currently + * render images at 72dpi, which we might want to change + * in the future). + * + * @return the normalizing transform for the configuration + */ + public AffineTransform getNormalizingTransform() { + System.out.println("getting getNormalizingTransform"); + return new AffineTransform(2, 0, 0, 2, 0, 0); + } + + /** + * Return our dummy instance of GraphicsDevice + * + * @return the PDF graphics device + */ + public GraphicsDevice getDevice() { + return new PDFGraphicsDevice(this); + } + + // needed for compiling under jdk1.4 + @jdk14codestart@ + public java.awt.image.VolatileImage createCompatibleVolatileImage(int width, int height) { + return null; + } + @jdk14codeend@ +} + diff --git a/src/java/org/apache/fop/svg/PDFGraphicsDevice.java b/src/java/org/apache/fop/svg/PDFGraphicsDevice.java new file mode 100644 index 000000000..6a2e337a6 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFGraphicsDevice.java @@ -0,0 +1,127 @@ +/* + * $Id: PDFGraphicsDevice.java,v 1.3 2003/03/07 09:51:25 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.svg; + +import java.awt.GraphicsDevice; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsConfigTemplate; + +/** + * This implements the GraphicsDevice interface as appropriate for + * a PDFGraphics2D. This is quite simple since we only have one + * GraphicsConfiguration for now (this might change in the future + * I suppose). + */ +class PDFGraphicsDevice extends GraphicsDevice { + + /** + * The Graphics Config that created us... + */ + protected GraphicsConfiguration gc; + + /** + * Create a new PDF graphics device. + * + * @param The gc we should reference + */ + PDFGraphicsDevice(PDFGraphicsConfiguration gc) { + this.gc = gc; + } + + /** + * Ignore template and return the only config we have + * + * @param gct the template configuration + * @return the best configuration which is the only one + */ + public GraphicsConfiguration getBestConfiguration( + GraphicsConfigTemplate gct) { + return gc; + } + + /** + * Return an array of our one GraphicsConfig + * + * @return an array containing the one graphics configuration + */ + public GraphicsConfiguration[] getConfigurations() { + return new GraphicsConfiguration[]{ gc }; + } + + /** + * Return out sole GraphicsConfig. + * + * @return the grpahics configuration that created this object + */ + public GraphicsConfiguration getDefaultConfiguration() { + return gc; + } + + /** + * Generate an IdString.. + * + * @return the ID string for this device, uses toString + */ + public String getIDstring() { + return toString(); + } + + /** + * Let the caller know that we are "a printer" + * + * @return the type which is always printer + */ + public int getType() { + return GraphicsDevice.TYPE_PRINTER; + } + +} + diff --git a/src/java/org/apache/fop/svg/PDFImageElementBridge.java b/src/java/org/apache/fop/svg/PDFImageElementBridge.java new file mode 100644 index 000000000..b5222f657 --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFImageElementBridge.java @@ -0,0 +1,181 @@ +/* + * $Id: PDFImageElementBridge.java,v 1.4 2003/03/07 09:51:25 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.svg; + +import org.apache.batik.bridge.SVGImageElementBridge; + +import org.apache.fop.image.JpegImage; + +import java.awt.Shape; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import org.apache.batik.gvt.AbstractGraphicsNode; + +/** + * Bridge class for the <image> element when jpeg images. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public class PDFImageElementBridge extends SVGImageElementBridge { + + /** + * Constructs a new bridge for the <image> element. + */ + public PDFImageElementBridge() { } + +/* + /** + * Create the raster image node. + * THis checks if it is a jpeg file and creates a jpeg node + * so the jpeg can be inserted directly into the pdf document. + * @param ctx the bridge context + * @param e the svg element for the image + * @param purl the parsed url for the image resource + * @return a new graphics node + * + protected GraphicsNode createRasterImageNode(BridgeContext ctx, + Element e, ParsedURL purl) { + + try { + JpegImage jpeg = new JpegImage(new URL(purl.toString())); + PDFFilter filter = jpeg.getPDFFilter(); + PDFJpegNode node = new PDFJpegNode(jpeg); + Rectangle2D bounds = node.getPrimitiveBounds(); + float [] vb = new float[4]; + vb[0] = 0; // x + vb[1] = 0; // y + vb[2] = (float) bounds.getWidth(); // width + vb[3] = (float) bounds.getHeight(); // height + + // handles the 'preserveAspectRatio', 'overflow' and 'clip' and sets the + // appropriate AffineTransform to the image node + initializeViewport(ctx, e, node, vb, bounds); + + return node; + } catch (Exception ex) { + } + + return super.createRasterImageNode(ctx, e, purl); + } +*/ + + /** + * A PDF jpeg node. + * This holds a jpeg image so that it can be drawn into + * the PDFGraphics2D. + */ + public static class PDFJpegNode extends AbstractGraphicsNode { + private JpegImage jpeg; + + /** + * Create a new pdf jpeg node for drawing jpeg images + * into pdf graphics. + * @param j the jpeg image + */ + public PDFJpegNode(JpegImage j) { + jpeg = j; + } + + /** + * Get the outline of this image. + * @return the outline shape which is the primitive bounds + */ + public Shape getOutline() { + return getPrimitiveBounds(); + } + + /** + * Paint this jpeg image. + * As this is used for inserting jpeg into pdf + * it adds the jpeg image to the PDFGraphics2D. + * @param g2d the graphics to draw the image on + */ + public void primitivePaint(Graphics2D g2d) { + if (g2d instanceof PDFGraphics2D) { + PDFGraphics2D pdfg = (PDFGraphics2D) g2d; + pdfg.setTransform(getTransform()); + float x = 0; + float y = 0; + try { + float width = jpeg.getWidth(); + float height = jpeg.getHeight(); + pdfg.addJpegImage(jpeg, x, y, width, height); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * Get the geometrix bounds of the image. + * @return the primitive bounds + */ + public Rectangle2D getGeometryBounds() { + return getPrimitiveBounds(); + } + + /** + * Get the primitive bounds of this bridge element. + * @return the bounds of the jpeg image + */ + public Rectangle2D getPrimitiveBounds() { + try { + return new Rectangle2D.Double(0, 0, jpeg.getWidth(), + jpeg.getHeight()); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } + +} diff --git a/src/java/org/apache/fop/svg/PDFTextElementBridge.java b/src/java/org/apache/fop/svg/PDFTextElementBridge.java new file mode 100644 index 000000000..664324a2b --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFTextElementBridge.java @@ -0,0 +1,154 @@ +/* + * $Id: PDFTextElementBridge.java,v 1.11 2003/03/07 09:51:26 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.svg; + +import org.apache.batik.gvt.TextNode; +import org.apache.batik.bridge.SVGTextElementBridge; +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.TextUtilities; +import org.apache.batik.gvt.GraphicsNode; + +import org.apache.fop.layout.FontInfo; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Bridge class for the <text> element. + * This bridge will use the direct text painter if the text + * for the element is simple. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public class PDFTextElementBridge extends SVGTextElementBridge { + private PDFTextPainter pdfTextPainter; + + /** + * Constructs a new bridge for the <text> element. + * @param fi the font infomration + */ + public PDFTextElementBridge(FontInfo fi) { + pdfTextPainter = new PDFTextPainter(fi); + } + + /** + * Create a text element bridge. + * This set the text painter on the node if the text is simple. + * @param ctx the bridge context + * @param e the svg element + * @return the text graphics node created by the super class + */ + public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) { + GraphicsNode node = super.createGraphicsNode(ctx, e); + if (node != null && isSimple(ctx, e, node)) { + ((TextNode)node).setTextPainter(getTextPainter()); + } + return node; + } + + private PDFTextPainter getTextPainter() { + return pdfTextPainter; + } + + /** + * Check if text element contains simple text. + * This checks the children of the text element to determine + * if the text is simple. The text is simple if it can be rendered + * with basic text drawing algorithms. This means there are no + * alternate characters, the font is known and there are no effects + * applied to the text. + * + * @param ctx the bridge context + * @param element the svg text element + * @param node the graphics node + * @return true if this text is simple of false if it cannot be + * easily rendered using normal drawString on the PDFGraphics2D + */ + private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) { + // Font size, in user space units. + float fs = TextUtilities.convertFontSize(element).floatValue(); + // PDF cannot display fonts over 36pt + if (fs > 36) { + return false; + } + + Element nodeElement; + for (Node n = element.getFirstChild(); + n != null; + n = n.getNextSibling()) { + + switch (n.getNodeType()) { + case Node.ELEMENT_NODE: + + nodeElement = (Element)n; + + if (n.getLocalName().equals(SVG_TSPAN_TAG) + || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) { + return false; + } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) { + return false; + } else if (n.getLocalName().equals(SVG_TREF_TAG)) { + return false; + } + break; + case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: + } + } + + /*if (CSSUtilities.convertFilter(element, node, ctx) != null) { + return false; + }*/ + + return true; + } +} + diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java new file mode 100644 index 000000000..153ee5cba --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -0,0 +1,434 @@ +/* + * $Id: PDFTextPainter.java,v 1.16 2003/03/07 09:51:25 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.svg; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.Font; + +import java.text.AttributedCharacterIterator; +import java.awt.font.TextAttribute; +import java.awt.Shape; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.Color; +import java.util.List; +import java.util.Iterator; + +import org.apache.batik.gvt.text.Mark; +import org.apache.batik.gvt.TextPainter; +import org.apache.batik.gvt.TextNode; +import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.bridge.SVGFontFamily; +import org.apache.batik.gvt.renderer.StrokingTextPainter; + +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.layout.FontState; +import org.apache.fop.layout.FontInfo; + +/** + * Renders the attributed character iterator of a <tt>TextNode</tt>. + * This class draws the text directly into the PDFGraphics2D so that + * the text is not drawn using shapes which makes the PDF files larger. + * If the text is simple enough to draw then it sets the font and calls + * drawString. If the text is complex or the cannot be translated + * into a simple drawString the StrokingTextPainter is used instead. + * + * @todo handle underline, overline and strikethrough + * @todo use drawString(AttributedCharacterIterator iterator...) for some + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * @version $Id: PDFTextPainter.java,v 1.16 2003/03/07 09:51:25 jeremias Exp $ + */ +public class PDFTextPainter implements TextPainter { + private FontInfo fontInfo; + + /** + * Use the stroking text painter to get the bounds and shape. + * Also used as a fallback to draw the string with strokes. + */ + protected static final TextPainter PROXY_PAINTER = + StrokingTextPainter.getInstance(); + + /** + * Create a new PDF text painter with the given font information. + * @param fi the fint info + */ + public PDFTextPainter(FontInfo fi) { + fontInfo = fi; + } + + /** + * Paints the specified attributed character iterator using the + * specified Graphics2D and context and font context. + * @param node the TextNode to paint + * @param g2d the Graphics2D to use + */ + public void paint(TextNode node, Graphics2D g2d) { + // System.out.println("PDFText paint"); + String txt = node.getText(); + Point2D loc = node.getLocation(); + + AttributedCharacterIterator aci = + node.getAttributedCharacterIterator(); + // reset position to start of char iterator + if (aci.getBeginIndex() == aci.getEndIndex()) { + return; + } + char ch = aci.first(); + if (ch == AttributedCharacterIterator.DONE) { + return; + } + TextNode.Anchor anchor; + anchor = (TextNode.Anchor) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + + List gvtFonts; + gvtFonts = (List) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + Paint forg = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); + Paint strokePaint; + strokePaint = (Paint) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT); + Float size = (Float) aci.getAttribute(TextAttribute.SIZE); + if (size == null) { + return; + } + Stroke stroke = (Stroke) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.STROKE); + /* + Float xpos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.X); + Float ypos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.Y); + */ + + Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); + Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); + + boolean useStrokePainter = false; + + if (forg instanceof Color) { + Color col = (Color) forg; + if (col.getAlpha() != 255) { + useStrokePainter = true; + } + g2d.setColor(col); + } + g2d.setPaint(forg); + g2d.setStroke(stroke); + + if (strokePaint != null) { + // need to draw using AttributedCharacterIterator + useStrokePainter = true; + } + + if (hasUnsupportedAttributes(aci)) { + useStrokePainter = true; + } + + // text contains unsupported information + if (useStrokePainter) { + PROXY_PAINTER.paint(node, g2d); + return; + } + + String style = ((posture != null) && (posture.floatValue() > 0.0)) + ? "italic" : "normal"; + int weight = ((taWeight != null) + && (taWeight.floatValue() > 1.0)) ? FontInfo.BOLD + : FontInfo.NORMAL; + + FontState fontState = null; + FontInfo fi = fontInfo; + boolean found = false; + String fontFamily = null; + if (gvtFonts != null) { + Iterator i = gvtFonts.iterator(); + while (i.hasNext()) { + GVTFontFamily fam = (GVTFontFamily) i.next(); + if (fam instanceof SVGFontFamily) { + PROXY_PAINTER.paint(node, g2d); + return; + } + fontFamily = fam.getFamilyName(); + if (fi.hasFont(fontFamily, style, weight)) { + String fname = fontInfo.fontLookup(fontFamily, style, + weight); + FontMetrics metrics = fontInfo.getMetricsFor(fname); + int fsize = (int)(size.floatValue() * 1000); + fontState = new FontState(fname, metrics, fsize); + found = true; + break; + } + } + } + if (!found) { + String fname = + fontInfo.fontLookup("any", style, FontInfo.NORMAL); + FontMetrics metrics = fontInfo.getMetricsFor(fname); + int fsize = (int)(size.floatValue() * 1000); + fontState = new FontState(fname, metrics, fsize); + } else { + if (g2d instanceof PDFGraphics2D) { + ((PDFGraphics2D) g2d).setOverrideFontState(fontState); + } + } + int fStyle = Font.PLAIN; + if (weight == FontInfo.BOLD) { + if (style.equals("italic")) { + fStyle = Font.BOLD | Font.ITALIC; + } else { + fStyle = Font.BOLD; + } + } else { + if (style.equals("italic")) { + fStyle = Font.ITALIC; + } else { + fStyle = Font.PLAIN; + } + } + Font font = new Font(fontFamily, fStyle, + (int)(fontState.getFontSize() / 1000)); + + g2d.setFont(font); + + float advance = getStringWidth(txt, fontState); + float tx = 0; + if (anchor != null) { + switch (anchor.getType()) { + case TextNode.Anchor.ANCHOR_MIDDLE: + tx = -advance / 2; + break; + case TextNode.Anchor.ANCHOR_END: + tx = -advance; + } + } + g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); + } + + private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { + boolean hasunsupported = false; + Object letSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); + if (letSpace != null) { + hasunsupported = true; + } + + Object wordSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); + if (wordSpace != null) { + hasunsupported = true; + } + + AttributedCharacterIterator.Attribute key; + key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE; + Object writeMod = aci.getAttribute(key); + if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( + writeMod)) { + hasunsupported = true; + } + + Object vertOr = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); + if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( + vertOr)) { + hasunsupported = true; + } + return hasunsupported; + } + + private float getStringWidth(String str, FontState fontState) { + float wordWidth = 0; + float whitespaceWidth = fontState.getWidth(fontState.mapChar(' ')); + + for (int i = 0; i < str.length(); i++) { + float charWidth; + char c = str.charAt(i); + if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { + charWidth = fontState.getWidth(fontState.mapChar(c)); + if (charWidth <= 0) { + charWidth = whitespaceWidth; + } + } else { + charWidth = whitespaceWidth; + } + wordWidth += charWidth; + } + return wordWidth / 1000f; + } + + /** + * Get the outline shape of the text characters. + * This uses the StrokingTextPainter to get the outline + * shape since in theory it should be the same. + * + * @param node the text node + * @return the outline shape of the text characters + */ + public Shape getOutline(TextNode node) { + return PROXY_PAINTER.getOutline(node); + } + + /** + * Get the bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getBounds2D(TextNode node) { + return PROXY_PAINTER.getBounds2D(node); + } + + /** + * Get the geometry bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getGeometryBounds(TextNode node) { + return PROXY_PAINTER.getGeometryBounds(node); + } + + // Methods that have no purpose for PDF + + /** + * Get the mark. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @param pos the position + * @param all select all + * @return null + */ + public Mark getMark(TextNode node, int pos, boolean all) { + System.out.println("PDFText getMark"); + return null; + } + + /** + * Select at. + * This does nothing since the output is pdf and not interactive. + * @param x the x position + * @param y the y position + * @param node the text node + * @return null + */ + public Mark selectAt(double x, double y, TextNode node) { + System.out.println("PDFText selectAt"); + return null; + } + + /** + * Select to. + * This does nothing since the output is pdf and not interactive. + * @param x the x position + * @param y the y position + * @param beginMark the start mark + * @return null + */ + public Mark selectTo(double x, double y, Mark beginMark) { + System.out.println("PDFText selectTo"); + return null; + } + + /** + * Selec first. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @return null + */ + public Mark selectFirst(TextNode node) { + System.out.println("PDFText selectFirst"); + return null; + } + + /** + * Select last. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @return null + */ + public Mark selectLast(TextNode node) { + System.out.println("PDFText selectLast"); + return null; + } + + /** + * Get selected. + * This does nothing since the output is pdf and not interactive. + * @param start the start mark + * @param finish the finish mark + * @return null + */ + public int[] getSelected(Mark start, Mark finish) { + System.out.println("PDFText getSelected"); + return null; + } + + /** + * Get the highlighted shape. + * This does nothing since the output is pdf and not interactive. + * @param beginMark the start mark + * @param endMark the end mark + * @return null + */ + public Shape getHighlightShape(Mark beginMark, Mark endMark) { + System.out.println("PDFText getHighlightShape"); + return null; + } + +} + diff --git a/src/java/org/apache/fop/svg/PDFTranscoder.java b/src/java/org/apache/fop/svg/PDFTranscoder.java new file mode 100644 index 000000000..eb5bfb12b --- /dev/null +++ b/src/java/org/apache/fop/svg/PDFTranscoder.java @@ -0,0 +1,439 @@ +/* + * $Id: PDFTranscoder.java,v 1.24 2003/03/07 09:51:26 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.svg; + +import java.awt.Dimension; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.Rectangle2D; + +import java.awt.Color; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.io.IOException; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.BridgeException; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.bridge.UserAgent; +import org.apache.batik.bridge.UserAgentAdapter; +import org.apache.batik.bridge.ViewBox; + +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.apache.batik.dom.svg.SVGOMDocument; +import org.apache.batik.dom.util.DocumentFactory; + +import org.apache.batik.gvt.GraphicsNode; + +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.XMLAbstractTranscoder; +import org.apache.batik.transcoder.image.resources.Messages; + +import org.apache.batik.transcoder.image.ImageTranscoder; + +import org.apache.batik.util.SVGConstants; +import org.apache.batik.util.XMLResourceDescriptor; + +import org.apache.batik.gvt.TextPainter; +import org.apache.batik.gvt.renderer.StrokingTextPainter; + +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGSVGElement; + +/** + * This class enables to transcode an input to a pdf document. + * + * <p>Two transcoding hints (<tt>KEY_WIDTH</tt> and + * <tt>KEY_HEIGHT</tt>) can be used to respectively specify the image + * width and the image height. If only one of these keys is specified, + * the transcoder preserves the aspect ratio of the original image. + * + * <p>The <tt>KEY_BACKGROUND_COLOR</tt> defines the background color + * to use for opaque image formats, or the background color that may + * be used for image formats that support alpha channel. + * + * <p>The <tt>KEY_AOI</tt> represents the area of interest to paint + * in device space. + * + * <p>Three additional transcoding hints that act on the SVG + * processor can be specified: + * + * <p><tt>KEY_LANGUAGE</tt> to set the default language to use (may be + * used by a <switch> SVG element for example), + * <tt>KEY_USER_STYLESHEET_URI</tt> to fix the URI of a user + * stylesheet, and <tt>KEY_PIXEL_TO_MM</tt> to specify the pixel to + * millimeter conversion factor. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * @version $Id: PDFTranscoder.java,v 1.24 2003/03/07 09:51:26 jeremias Exp $ + */ +public class PDFTranscoder extends XMLAbstractTranscoder { + /* + public static final TranscodingHints.Key KEY_STROKE_TEXT = + new BooleanKey(); + */ + + /** + * The user agent dedicated to an <tt>ImageTranscoder</tt>. + */ + protected UserAgent userAgent = new ImageTranscoderUserAgent(); + + /** + * Constructs a new <tt>ImageTranscoder</tt>. + */ + public PDFTranscoder() { + hints.put(KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, + SVGConstants.SVG_NAMESPACE_URI); + hints.put(KEY_DOCUMENT_ELEMENT, SVGConstants.SVG_SVG_TAG); + hints.put(KEY_DOM_IMPLEMENTATION, + SVGDOMImplementation.getDOMImplementation()); + } + + /** + * Transcodes the specified Document as an image in the specified output. + * + * @param document the document to transcode + * @param uri the uri of the document or null if any + * @param output the ouput where to transcode + * @exception TranscoderException if an error occured while transcoding + */ + protected void transcode(Document document, String uri, + TranscoderOutput output) throws TranscoderException { + + if (!(document instanceof SVGOMDocument)) { + throw new TranscoderException(Messages.formatMessage("notsvg", + null)); + } + SVGDocument svgDoc = (SVGDocument)document; + SVGSVGElement root = svgDoc.getRootElement(); + // initialize the SVG document with the appropriate context + String parserClassname = (String)hints.get(KEY_XML_PARSER_CLASSNAME); + + /*boolean stroke = true; + if (hints.containsKey(KEY_STROKE_TEXT)) { + stroke = ((Boolean)hints.get(KEY_STROKE_TEXT)).booleanValue(); + }*/ + PDFDocumentGraphics2D graphics = new PDFDocumentGraphics2D(false); + + // build the GVT tree + GVTBuilder builder = new GVTBuilder(); + BridgeContext ctx = new BridgeContext(userAgent); + TextPainter textPainter = null; + textPainter = new StrokingTextPainter(); + ctx.setTextPainter(textPainter); + + PDFTextElementBridge pdfTextElementBridge; + pdfTextElementBridge = new PDFTextElementBridge(graphics.getFontInfo()); + ctx.putBridge(pdfTextElementBridge); + + PDFAElementBridge pdfAElementBridge = new PDFAElementBridge(); + AffineTransform currentTransform = new AffineTransform(1, 0, 0, 1, 0, 0); + pdfAElementBridge.setCurrentTransform(currentTransform); + ctx.putBridge(pdfAElementBridge); + ctx.putBridge(new PDFImageElementBridge()); + GraphicsNode gvtRoot; + try { + gvtRoot = builder.build(ctx, svgDoc); + } catch (BridgeException ex) { + throw new TranscoderException(ex); + } + // get the 'width' and 'height' attributes of the SVG document + float docWidth = (float)ctx.getDocumentSize().getWidth(); + float docHeight = (float)ctx.getDocumentSize().getHeight(); + ctx = null; + builder = null; + + // compute the image's width and height according the hints + float imgWidth = -1; + if (hints.containsKey(ImageTranscoder.KEY_WIDTH)) { + imgWidth = + ((Float)hints.get(ImageTranscoder.KEY_WIDTH)).floatValue(); + } + float imgHeight = -1; + if (hints.containsKey(ImageTranscoder.KEY_HEIGHT)) { + imgHeight = + ((Float)hints.get(ImageTranscoder.KEY_HEIGHT)).floatValue(); + } + float width, height; + if (imgWidth > 0 && imgHeight > 0) { + width = imgWidth; + height = imgHeight; + } else if (imgHeight > 0) { + width = (docWidth * imgHeight) / docHeight; + height = imgHeight; + } else if (imgWidth > 0) { + width = imgWidth; + height = (docHeight * imgWidth) / docWidth; + } else { + width = docWidth; + height = docHeight; + } + // compute the preserveAspectRatio matrix + AffineTransform px; + String ref = null; + try { + ref = new URL(uri).getRef(); + } catch (MalformedURLException ex) { + // nothing to do, catched previously + } + + try { + px = ViewBox.getViewTransform(ref, root, width, height); + } catch (BridgeException ex) { + throw new TranscoderException(ex); + } + + if (px.isIdentity() && (width != docWidth || height != docHeight)) { + // The document has no viewBox, we need to resize it by hand. + // we want to keep the document size ratio + float d = Math.max(docWidth, docHeight); + float dd = Math.max(width, height); + float scale = dd / d; + px = AffineTransform.getScaleInstance(scale, scale); + } + // take the AOI into account if any + if (hints.containsKey(ImageTranscoder.KEY_AOI)) { + Rectangle2D aoi = (Rectangle2D)hints.get(ImageTranscoder.KEY_AOI); + // transform the AOI into the image's coordinate system + aoi = px.createTransformedShape(aoi).getBounds2D(); + AffineTransform mx = new AffineTransform(); + double sx = width / aoi.getWidth(); + double sy = height / aoi.getHeight(); + mx.scale(sx, sy); + double tx = -aoi.getX(); + double ty = -aoi.getY(); + mx.translate(tx, ty); + // take the AOI transformation matrix into account + // we apply first the preserveAspectRatio matrix + px.preConcatenate(mx); + } + // prepare the image to be painted + int w = (int)width; + int h = (int)height; + + try { + graphics.setupDocument(output.getOutputStream(), w, h); + } catch (IOException ex) { + throw new TranscoderException(ex); + } + graphics.setSVGDimension(docWidth, docHeight); + currentTransform.setTransform(1, 0, 0, -1, 0, height); + /*if (!stroke) { + textPainter = new PDFTextPainter(graphics.getFontInfo()); + ctx.setTextPainter(textPainter); + }*/ + + if (hints.containsKey(ImageTranscoder.KEY_BACKGROUND_COLOR)) { + graphics.setBackgroundColor((Color)hints.get(ImageTranscoder.KEY_BACKGROUND_COLOR)); + } + graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext()); + graphics.setTransform(px); + + gvtRoot.paint(graphics); + + try { + graphics.finish(); + } catch (IOException ex) { + throw new TranscoderException(ex); + } + } + + /** + * Creates a <tt>DocumentFactory</tt> that is used to create an SVG DOM + * tree. The specified DOM Implementation is ignored and the Batik + * SVG DOM Implementation is automatically used. + * + * @param domImpl the DOM Implementation (not used) + * @param parserClassname the XML parser classname + * @return the document factory + */ + protected DocumentFactory createDocumentFactory(DOMImplementation domImpl, + String parserClassname) { + return new SAXSVGDocumentFactory(parserClassname); + } + + // -------------------------------------------------------------------- + // UserAgent implementation + // -------------------------------------------------------------------- + + /** + * A user agent implementation for <tt>ImageTranscoder</tt>. + */ + protected class ImageTranscoderUserAgent extends UserAgentAdapter { + + /** + * Returns the default size of this user agent (400x400). + * @return the default viewport size + */ + public Dimension2D getViewportSize() { + return new Dimension(400, 400); + } + + /** + * Displays the specified error message using the <tt>ErrorHandler</tt>. + * @param message the message to display + */ + public void displayError(String message) { + try { + getErrorHandler().error(new TranscoderException(message)); + } catch (TranscoderException ex) { + throw new RuntimeException(); + } + } + + /** + * Displays the specified error using the <tt>ErrorHandler</tt>. + * @param e the exception to display + */ + public void displayError(Exception e) { + try { + getErrorHandler().error(new TranscoderException(e)); + } catch (TranscoderException ex) { + throw new RuntimeException(); + } + } + + /** + * Displays the specified message using the <tt>ErrorHandler</tt>. + * @param message the message to display + */ + public void displayMessage(String message) { + try { + getErrorHandler().warning(new TranscoderException(message)); + } catch (TranscoderException ex) { + throw new RuntimeException(); + } + } + + /** + * Returns the pixel to millimeter conversion factor specified in the + * <tt>TranscodingHints</tt> or 0.3528 if any. + * @return the pixel unit to millimeter factor + */ + public float getPixelUnitToMillimeter() { + Object key = ImageTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER; + if (getTranscodingHints().containsKey(key)) { + return ((Float)getTranscodingHints().get(key)).floatValue(); + } else { + // return 0.3528f; // 72 dpi + return 0.26458333333333333333333333333333f; // 96dpi + } + } + + /** + * Returns the user language specified in the + * <tt>TranscodingHints</tt> or "en" (english) if any. + * @return the languages for the transcoder + */ + public String getLanguages() { + Object key = ImageTranscoder.KEY_LANGUAGE; + if (getTranscodingHints().containsKey(key)) { + return (String)getTranscodingHints().get(key); + } else { + return "en"; + } + } + + /** + * Get the media for this transcoder. Which is always print. + * @return PDF media is "print" + */ + public String getMedia() { + return "print"; + } + + /** + * Returns the user stylesheet specified in the + * <tt>TranscodingHints</tt> or null if any. + * @return the user style sheet URI specified in the hints + */ + public String getUserStyleSheetURI() { + return (String)getTranscodingHints() + .get(ImageTranscoder.KEY_USER_STYLESHEET_URI); + } + + /** + * Returns the XML parser to use from the TranscodingHints. + * @return the XML parser class name + */ + public String getXMLParserClassName() { + Object key = KEY_XML_PARSER_CLASSNAME; + if (getTranscodingHints().containsKey(key)) { + return (String)getTranscodingHints().get(key); + } else { + return XMLResourceDescriptor.getXMLParserClassName(); + } + } + + /** + * Check if the XML parser is validating. + * @return true if the XML parser is validating + */ + public boolean isXMLParserValidating() { + return false; + } + + /** + * Unsupported operation. + * @return null since this is unsupported + */ + public AffineTransform getTransform() { + return null; + } + } +} diff --git a/src/java/org/apache/fop/svg/SVGElement.java b/src/java/org/apache/fop/svg/SVGElement.java new file mode 100644 index 000000000..24708d3db --- /dev/null +++ b/src/java/org/apache/fop/svg/SVGElement.java @@ -0,0 +1,314 @@ +/* + * $Id: SVGElement.java,v 1.34 2003/03/05 15:08:45 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.svg; + +// FOP +import org.apache.fop.fo.FONode; +import org.apache.fop.apps.FOPException; + +import org.apache.batik.dom.svg.SVGOMDocument; +import org.apache.batik.dom.svg.SVGOMElement; +import org.apache.batik.dom.svg.SVGContext; +import org.apache.batik.dom.util.XMLSupport; +import org.w3c.dom.Element; +import org.w3c.dom.svg.SVGDocument; +import org.xml.sax.Attributes; +import org.apache.batik.bridge.UnitProcessor; +import org.apache.batik.util.SVGConstants; + +import org.w3c.dom.DOMImplementation; + +import org.apache.batik.dom.svg.SVGDOMImplementation; + +import java.net.URL; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * class representing the SVG root element + * for constructing an svg document. + */ +public class SVGElement extends SVGObj { + + /** + * Constructs an SVG object + * + * @param parent the parent formatting object + */ + public SVGElement(FONode parent) { + super(parent); + } + + /** + * Handle the xml attributes from SAX. + * @param attlist the attribute list + * @throws FOPException not thrown from here + */ + public void handleAttrs(Attributes attlist) throws FOPException { + super.handleAttrs(attlist); + init(); + } + + /** + * Get the dimensions of this XML document. + * @param view the viewport dimensions + * @return the dimensions of this SVG document + */ + public Point2D getDimension(final Point2D view) { + + // TODO - change so doesn't hold onto fo,area tree + Element svgRoot = element; + /* create an SVG area */ + /* if width and height are zero, get the bounds of the content. */ + + try { + String baseDir = userAgent.getBaseURL(); + if (baseDir != null) { + ((SVGOMDocument)doc).setURLObject(new URL(baseDir)); + } + } catch (Exception e) { + getLogger().error("Could not set base URL for svg", e); + } + + Element e = ((SVGDocument)doc).getRootElement(); + final float ptmm = userAgent.getPixelUnitToMillimeter(); + // temporary svg context + SVGContext dc = new SVGContext() { + public float getPixelToMM() { + return ptmm; + } + public float getPixelUnitToMillimeter() { + return ptmm; + } + + public Rectangle2D getBBox() { + return new Rectangle2D.Double(0, 0, view.getX(), view.getY()); + } + + /** + * Returns the transform from the global transform space to pixels. + */ + public AffineTransform getScreenTransform() { + throw new UnsupportedOperationException("NYI"); + } + + /** + * Sets the transform to be used from the global transform space + * to pixels. + */ + public void setScreenTransform(AffineTransform at) { + throw new UnsupportedOperationException("NYI"); + } + + public AffineTransform getCTM() { + return new AffineTransform(); + } + + public AffineTransform getGlobalTransform() { + return new AffineTransform(); + } + + public float getViewportWidth() { + return (float)view.getX(); + } + + public float getViewportHeight() { + return (float)view.getY(); + } + + public float getFontSize() { + return 12; + } + }; + ((SVGOMElement)e).setSVGContext(dc); + + //if (!e.hasAttributeNS(XMLSupport.XMLNS_NAMESPACE_URI, "xmlns")) { + e.setAttributeNS(XMLSupport.XMLNS_NAMESPACE_URI, "xmlns", + SVGDOMImplementation.SVG_NAMESPACE_URI); + //} + int fontSize = 12; + Point2D p2d = getSize(fontSize, svgRoot, userAgent.getPixelUnitToMillimeter()); + ((SVGOMElement)e).setSVGContext(null); + + return p2d; + } + + private void init() { + DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); + String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; + doc = impl.createDocument(svgNS, "svg", null); + + element = doc.getDocumentElement(); + + buildTopLevel(doc, element); + } + + /** + * Get the size of the SVG root element. + * @param size the font size + * @param svgRoot the svg root element + * @param ptmm the pixel to millimeter conversion factor + * @return the size of the SVG document + */ + public static Point2D getSize(int size, Element svgRoot, float ptmm) { + String str; + UnitProcessor.Context ctx; + ctx = new PDFUnitContext(size, svgRoot, ptmm); + str = svgRoot.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE); + if (str.length() == 0) { + str = "100%"; + } + float width = UnitProcessor.svgHorizontalLengthToUserSpace + (str, SVGConstants.SVG_WIDTH_ATTRIBUTE, ctx); + + str = svgRoot.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE); + if (str.length() == 0) { + str = "100%"; + } + float height = UnitProcessor.svgVerticalLengthToUserSpace + (str, SVGConstants.SVG_HEIGHT_ATTRIBUTE, ctx); + return new Point2D.Float(width, height); + } + + /** + * This class is the default context for a particular + * element. Informations not available on the element are get from + * the bridge context (such as the viewport or the pixel to + * millimeter factor. + */ + public static class PDFUnitContext implements UnitProcessor.Context { + + /** The element. */ + private Element e; + private int fontSize; + private float pixeltoMM; + + /** + * Create a PDF unit context. + * @param size the font size. + * @param e the svg element + * @param ptmm the pixel to millimeter factor + */ + public PDFUnitContext(int size, Element e, float ptmm) { + this.e = e; + this.fontSize = size; + this.pixeltoMM = ptmm; + } + + /** + * Returns the element. + * @return the element + */ + public Element getElement() { + return e; + } + + /** + * Returns the context of the parent element of this context. + * Since this is always for the root SVG element there never + * should be one... + * @return null + */ + public UnitProcessor.Context getParentElementContext() { + return null; + } + + /** + * Returns the pixel to mm factor. (this is deprecated) + * @return the pixel to millimeter factor + */ + public float getPixelToMM() { + return pixeltoMM; + } + + /** + * Returns the pixel to mm factor. + * @return the pixel to millimeter factor + */ + public float getPixelUnitToMillimeter() { + return pixeltoMM; + } + + /** + * Returns the font-size value. + * @return the default font size + */ + public float getFontSize() { + return fontSize; + } + + /** + * Returns the x-height value. + * @return the x-height value + */ + public float getXHeight() { + return 0.5f; + } + + /** + * Returns the viewport width used to compute units. + * @return the default viewport width of 100 + */ + public float getViewportWidth() { + return 100; + } + + /** + * Returns the viewport height used to compute units. + * @return the default viewport height of 100 + */ + public float getViewportHeight() { + return 100; + } + } +} + diff --git a/src/java/org/apache/fop/svg/SVGElementMapping.java b/src/java/org/apache/fop/svg/SVGElementMapping.java new file mode 100644 index 000000000..dd6296cd5 --- /dev/null +++ b/src/java/org/apache/fop/svg/SVGElementMapping.java @@ -0,0 +1,115 @@ +/* + * $Id: SVGElementMapping.java,v 1.24 2003/03/07 09:51:25 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.svg; + +import java.util.HashMap; + +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FOTreeBuilder; +import org.apache.fop.fo.ElementMapping; +import org.apache.fop.apps.Driver; + +import org.apache.batik.util.XMLResourceDescriptor; +import org.apache.batik.dom.svg.SVGDOMImplementation; + +/** + * Setup the SVG element mapping. + * This adds the svg element mappings used to create the objects + * that create the SVG Document. + */ +public class SVGElementMapping implements ElementMapping { + private static HashMap foObjs = null; + private static boolean batik = true; + + private static synchronized void setupSVG() { + if (foObjs == null) { + // this sets the parser that will be used + // by default (SVGBrokenLinkProvider) + // normally the user agent value is used + XMLResourceDescriptor.setXMLParserClassName( + Driver.getParserClassName()); + + foObjs = new HashMap(); + foObjs.put("svg", new SE()); + foObjs.put(DEFAULT, new SVGMaker()); + } + } + + /** + * Add the SVG element mappings to the tree builder. + * @param builder the FOTreeBuilder to add the mappings to + */ + public void addToBuilder(FOTreeBuilder builder) { + if (batik) { + try { + setupSVG(); + String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; + builder.addMapping(svgNS, foObjs); + } catch (Throwable t) { + // if the classes are not available + // the DISPLAY is not checked + batik = false; + } + } + } + + static class SVGMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new SVGObj(parent); + } + } + + static class SE extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new SVGElement(parent); + } + } +} diff --git a/src/java/org/apache/fop/svg/SVGObj.java b/src/java/org/apache/fop/svg/SVGObj.java new file mode 100644 index 000000000..07019acd4 --- /dev/null +++ b/src/java/org/apache/fop/svg/SVGObj.java @@ -0,0 +1,79 @@ +/* + * $Id: SVGObj.java,v 1.12 2003/03/07 09:51:25 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.svg; + +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.XMLObj; + +/** + * Class for SVG element objects. + * This aids in the construction of the SVG Document. + */ +public class SVGObj extends XMLObj { + /** + * constructs an svg object (called by Maker). + * + * @param parent the parent formatting object + */ + public SVGObj(FONode parent) { + super(parent); + } + + /** + * Get the namespace for svg. + * @return the svg namespace + */ + public String getNameSpace() { + return "http://www.w3.org/2000/svg"; + } + +} + diff --git a/src/java/org/apache/fop/svg/SVGUserAgent.java b/src/java/org/apache/fop/svg/SVGUserAgent.java new file mode 100644 index 000000000..b90609d81 --- /dev/null +++ b/src/java/org/apache/fop/svg/SVGUserAgent.java @@ -0,0 +1,184 @@ +/* + * $Id: SVGUserAgent.java,v 1.14 2003/03/07 09:51:25 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.svg; + +import org.apache.fop.fo.FOUserAgent; + +import org.apache.batik.bridge.UserAgentAdapter; + +import org.apache.avalon.framework.logger.Logger; + +// Java +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.Dimension; + +/** + * The SVG user agent. + * This is an implementation of the batik svg user agent + * for handling errors and getting user agent values. + */ +public class SVGUserAgent extends UserAgentAdapter { + private AffineTransform currentTransform = null; + private Logger log; + private FOUserAgent userAgent; + + /** + * Creates a new SVGUserAgent. + * @param ua the FO user agent + * @param at the current transform + */ + public SVGUserAgent(FOUserAgent ua, AffineTransform at) { + currentTransform = at; + userAgent = ua; + log = userAgent.getLogger(); + } + + /** + * Displays an error message. + * @param message the message to display + */ + public void displayError(String message) { + log.error(message); + } + + /** + * Displays an error resulting from the specified Exception. + * @param ex the exception to display + */ + public void displayError(Exception ex) { + log.error("SVG Error" + ex.getMessage(), ex); + } + + /** + * Displays a message in the User Agent interface. + * The given message is typically displayed in a status bar. + * @param message the message to display + */ + public void displayMessage(String message) { + log.info(message); + } + + /** + * Shows an alert dialog box. + * @param message the message to display + */ + public void showAlert(String message) { + log.warn(message); + } + + /** + * Returns a customized the pixel to mm factor. + * @return the pixel unit to millimeter conversion factor + */ + public float getPixelUnitToMillimeter() { + return userAgent.getPixelUnitToMillimeter(); + } + + /** + * Returns the language settings. + * @return the languages supported + */ + public String getLanguages() { + return "en"; // userLanguages; + } + + /** + * Returns the media type for this rendering. + * @return the media for fo documents is "print" + */ + public String getMedia() { + return "print"; + } + + /** + * Returns the user stylesheet uri. + * @return null if no user style sheet was specified. + */ + public String getUserStyleSheetURI() { + return null; // userStyleSheetURI; + } + + /** + * Returns the class name of the XML parser. + * @return the XML parser class name + */ + public String getXMLParserClassName() { + return org.apache.fop.apps.Driver.getParserClassName(); + } + + /** + * Is the XML parser validating. + * @return true if the xml parser is validating + */ + public boolean isXMLParserValidating() { + return false; + } + + /** + * Get the transform of the svg document. + * @return the transform + */ + public AffineTransform getTransform() { + return currentTransform; + } + + /** + * Get the default viewport size for an svg document. + * This returns a default value of 100x100. + * @return the default viewport size + */ + public Dimension2D getViewportSize() { + return new Dimension(100, 100); + } + +} + diff --git a/src/java/org/apache/fop/svg/SVGUtilities.java b/src/java/org/apache/fop/svg/SVGUtilities.java new file mode 100644 index 000000000..b82771c57 --- /dev/null +++ b/src/java/org/apache/fop/svg/SVGUtilities.java @@ -0,0 +1,301 @@ +/* + * $Id: SVGUtilities.java,v 1.6 2003/03/07 09:51:26 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.svg; + +import java.util.StringTokenizer; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.font.FontRenderContext; + +//import org.apache.fop.fo.*; +//import org.apache.fop.datatypes.*; +import org.w3c.dom.Element; +import org.w3c.dom.Document; +import org.w3c.dom.DOMImplementation; + +import org.apache.batik.dom.svg.SVGDOMImplementation; + +/** + * Some utilities for creating svg DOM documents and elements. + */ +public class SVGUtilities { + private static final String SVG_NS = SVGDOMImplementation.SVG_NAMESPACE_URI; + + /** + * Create a new svg document with batik. + * @param width the width of the root svg element + * @param height the height of the root svg element + * @return a new SVG Document + */ + public static final Document createSVGDocument(float width, + float height) { + DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); + Document doc = impl.createDocument(SVG_NS, "svg", null); + + Element svgRoot = doc.getDocumentElement(); + svgRoot.setAttributeNS(null, "width", "" + width); + svgRoot.setAttributeNS(null, "height", "" + height); + return doc; + } + + /** + * Get the string width for a particular string given the font. + * @param str the string + * @param font the font + * @return the width of the string in the given font + */ + public static final float getStringWidth(String str, java.awt.Font font) { + Rectangle2D rect = + font.getStringBounds(str, 0, str.length(), + new FontRenderContext(new AffineTransform(), + true, true)); + return (float)rect.getWidth(); + } + + /** + * Get the string height for a particular string given the font. + * @param str the string + * @param font the font + * @return the height of the string in the given font + */ + public static final float getStringHeight(String str, + java.awt.Font font) { + Rectangle2D rect = + font.getStringBounds(str, 0, str.length(), + new FontRenderContext(new AffineTransform(), + true, true)); + return (float)rect.getHeight(); + } + + /** + * Get the string bounds for a particular string given the font. + * @param str the string + * @param font the font + * @return the bounds of the string + */ + public static final Rectangle2D getStringBounds(String str, + java.awt.Font font) { + return font.getStringBounds(str, 0, str.length(), + new FontRenderContext(new AffineTransform(), + true, true)); + } + + /** + * Create an SVG Line + * @param doc the document to create the element + * @param x the start x position + * @param y the start y position + * @param x2 the end x position + * @param y2 the end y position + * @return the new line element + */ + public static final Element createLine(Document doc, float x, float y, + float x2, float y2) { + Element ellipse = doc.createElementNS(SVG_NS, "line"); + ellipse.setAttributeNS(null, "x1", "" + x); + ellipse.setAttributeNS(null, "x2", "" + x2); + ellipse.setAttributeNS(null, "y1", "" + y); + ellipse.setAttributeNS(null, "y2", "" + y2); + return ellipse; + } + + /** + * Create an SVG Ellipse + * @param doc the document to create the element + * @param cx the centre x position + * @param cy the centre y position + * @param rx the x axis radius + * @param ry the y axis radius + * @return the new ellipse element + */ + public static final Element createEllipse(Document doc, float cx, + float cy, float rx, float ry) { + Element ellipse = doc.createElementNS(SVG_NS, "ellipse"); + ellipse.setAttributeNS(null, "cx", "" + cx); + ellipse.setAttributeNS(null, "rx", "" + rx); + ellipse.setAttributeNS(null, "cy", "" + cy); + ellipse.setAttributeNS(null, "ry", "" + ry); + return ellipse; + } + + /** + * Create an SVG Path. + * @param doc the document to create the element + * @param str the string for the d attribute on the path + * @return the new path element + */ + public static final Element createPath(Document doc, String str) { + Element path = doc.createElementNS(SVG_NS, "path"); + path.setAttributeNS(null, "d", str); + return path; + } + + /** + * Create an SVG Text object. + * @param doc the document to create the element + * @param x the start x position + * @param y the start y position + * @param str the string + * @return the new text element + */ + public static final Element createText(Document doc, float x, float y, + String str) { + Element textGraph = doc.createElementNS(SVG_NS, "text"); + textGraph.setAttributeNS(null, "x", "" + x); + textGraph.setAttributeNS(null, "y", "" + y); + org.w3c.dom.Text text = doc.createTextNode(str); + textGraph.appendChild(text); + return textGraph; + } + + /** + * Create an SVG Rectangle. + * @param doc the document to create the element + * @param x the start x position + * @param y the start y position + * @param width the width of the rectangle + * @param height the height of the rectangle + * @return the new rectangle element + */ + public static final Element createRect(Document doc, float x, float y, + float width, float height) { + Element border = doc.createElementNS(SVG_NS, "rect"); + border.setAttributeNS(null, "x", "" + x); + border.setAttributeNS(null, "y", "" + y); + border.setAttributeNS(null, "width", "" + width); + border.setAttributeNS(null, "height", "" + height); + return border; + } + + /** + * Create an SVG G. + * @param doc the document to create the element + * @return the new g element + */ + public static final Element createG(Document doc) { + Element border = doc.createElementNS(SVG_NS, "g"); + return border; + } + + /** + * Create an SVG Clip. + * @param doc the document to create the element + * @param els the child elements that make the clip + * @param id the id of the clipping path + * @return the new clip element + */ + public static final Element createClip(Document doc, Element els, + String id) { + Element border = doc.createElementNS(SVG_NS, "clipPath"); + border.setAttributeNS(null, "id", id); + border.appendChild(els); + return border; + } + + /** + * Create and svg image element. + * @param doc the document to create the element + * @param ref the href link to the image + * @param width the width to set on the image + * @param height the height to set on the image + * @return a new image element + */ + public static final Element createImage(Document doc, String ref, + float width, float height) { + Element border = doc.createElementNS(SVG_NS, "image"); + border.setAttributeNS("http://www.w3.org/1999/xlink", "href", + ref); + border.setAttributeNS(null, "width", "" + width); + border.setAttributeNS(null, "height", "" + height); + return border; + } + + /** + * Create some SVG text that is wrapped into a specified width. + * @param doc the document to create the elements + * @param str the string to wrap + * @param font the font + * @param width the width to wrap + * @return the new element containing the wrapped text + */ + public static final Element wrapText(Document doc, String str, + java.awt.Font font, float width) { + Element g = createG(doc); + Element text; + StringTokenizer st = new StringTokenizer(str, " \t\r\n"); + float totalWidth = 0; + String totalStr = ""; + int line = 0; + float height = getStringHeight(str, font); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + float strwidth = getStringWidth(token, font); + totalWidth += strwidth; + if (totalWidth > width) { + if (totalStr.equals("")) { + totalStr = token; + token = ""; + strwidth = 0; + } + text = createText(doc, 0, line * (height + 5), totalStr); + g.appendChild(text); + totalStr = token; + totalWidth = strwidth; + line++; + } else { + totalStr = totalStr + " " + token; + } + } + + return g; + } + +} diff --git a/src/java/org/apache/fop/svg/package.html b/src/java/org/apache/fop/svg/package.html new file mode 100644 index 000000000..1f7cfb6be --- /dev/null +++ b/src/java/org/apache/fop/svg/package.html @@ -0,0 +1,31 @@ +<HTML> +<TITLE>org.apache.fop.svg Package</TITLE> +<BODY> +<P>Classes that add SVG support to FOP and SVG->PDF conversion for Batik.</P> +<P> +This package contains classes for drawing to PDF using +a Graphics2D implementation. +</P> +<P> +The classes: PDFAElementBridge, PDFANode, PDFImageElementBridge, +PDFTextElementBridge and PDFTextPainter are used in conjunction +with batik to draw the SVG into the PDF document. +</P> +<P> +The PDFTranscoder is a transcoder for use with batik to convert +from SVG to a single page PDF document. +</P> +<P> +SVGElement, SVGElementMapping, SVGObj and SVGUserAgent are used by +FOP for handling embedded SVG or external SVG graphics. +</P> +<P> +The PDFGraphics2D does all the work to draw into a PDF document and +the PDFDocumentGraphics2D is used when drawing into a single document. +</P> +<P> +SVGUtilities contains some useful svg element creation methods. +</P> +</BODY> +</HTML> + |