]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
added support for rendering SVG graphics to ps
authorKeiron Liddle <keiron@apache.org>
Fri, 22 Jun 2001 14:16:46 +0000 (14:16 +0000)
committerKeiron Liddle <keiron@apache.org>
Fri, 22 Jun 2001 14:16:46 +0000 (14:16 +0000)
not fully functional

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194313 13f79535-47bb-0310-9956-ffa450edef68

src/org/apache/fop/render/ps/PSGraphics2D.java [new file with mode: 0644]
src/org/apache/fop/render/ps/PSRenderer.java

diff --git a/src/org/apache/fop/render/ps/PSGraphics2D.java b/src/org/apache/fop/render/ps/PSGraphics2D.java
new file mode 100644 (file)
index 0000000..c107405
--- /dev/null
@@ -0,0 +1,905 @@
+/* $Id$
+ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources."
+ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.fop.pdf.*;
+import org.apache.fop.layout.*;
+import org.apache.fop.fonts.*;
+import org.apache.fop.render.pdf.*;
+import org.apache.fop.image.*;
+import org.apache.fop.datatypes.ColorSpace;
+
+import org.apache.batik.ext.awt.g2d.*;
+
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
+import java.awt.*;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.image.*;
+import java.awt.font.*;
+import java.awt.geom.*;
+import java.awt.image.renderable.*;
+import java.io.*;
+
+import java.util.Map;
+import java.util.Vector;
+
+/**
+ * This concrete implementation of <tt>AbstractGraphics2D</tt> is a
+ * simple help to programmers to get started with their own
+ * implementation of <tt>Graphics2D</tt>.
+ * <tt>DefaultGraphics2D</tt> implements all the abstract methods
+ * is <tt>AbstractGraphics2D</tt> and makes it easy to start
+ * implementing a <tt>Graphic2D</tt> piece-meal.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @version $Id$
+ * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
+ */
+public class PSGraphics2D extends AbstractGraphics2D {
+    boolean standalone = false;
+
+    /** the PDF Document being created */
+    protected PSRenderer psRenderer;
+
+    protected FontState fontState;
+
+    /** the current (internal) font name */
+    protected String currentFontName;
+
+    /** the current font size in millipoints */
+    protected int currentFontSize;
+
+    /** the current vertical position in millipoints from bottom */
+    protected int currentYPosition = 0;
+
+    /** the current horizontal position in millipoints from left */
+    protected int currentXPosition = 0;
+
+    /** the current colour for use in svg */
+    PDFColor currentColour = new PDFColor(0, 0, 0);
+
+    FontInfo fontInfo;
+
+    /**
+     * 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.
+     */
+    public PSGraphics2D(boolean textAsShapes, FontState fs,
+                         PSRenderer ren, String font, int size, int xpos, int ypos) {
+        super(textAsShapes);
+        psRenderer = ren;
+        currentFontName = font;
+        currentFontSize = size;
+        currentYPosition = ypos;
+        currentXPosition = xpos;
+        fontState = fs;
+    }
+
+    public PSGraphics2D(boolean textAsShapes) {
+        super(textAsShapes);
+    }
+
+    public void setGraphicContext(GraphicContext c) {
+        gc = c;
+    }
+
+    /**
+     * This constructor supports the create method
+     */
+    public PSGraphics2D(PSGraphics2D 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 PSGraphics2D(this);
+    }
+
+    /**
+     * Draws as much of the specified image as is currently available.
+     * The image is drawn with its top-left corner at
+     * (<i>x</i>,&nbsp;<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.
+     * @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");
+
+        final int width = img.getWidth(observer);
+        final int height = img.getHeight(observer);
+        if (width == -1 || height == -1) {
+            return false;
+        }
+
+        Dimension size = new Dimension(width, height);
+        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, height);
+        g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
+
+        if (!g.drawImage(img, 0, 0, observer)) {
+            return false;
+        }
+        g.dispose();
+
+        final byte[] result =
+          new byte[buf.getWidth() * buf.getHeight() * 3];
+        final byte[] mask =
+          new byte[buf.getWidth() * buf.getHeight()];
+
+        Raster raster = buf.getData();
+        DataBuffer bd = raster.getDataBuffer();
+
+        int count = 0;
+        int maskpos = 0;
+        switch (bd.getDataType()) {
+            case DataBuffer.TYPE_INT:
+                int[][] idata = ((DataBufferInt) bd).getBankData();
+                for (int i = 0; i < idata.length; i++) {
+                    for (int j = 0; j < idata[i].length; j++) {
+                        //mask[maskpos++] = (byte)((idata[i][j] >> 24) & 0xFF);
+                        if(((idata[i][j] >> 24) & 0xFF) != 255) {
+                            result[count++] = (byte)0xFF;
+                            result[count++] = (byte)0xFF;
+                            result[count++] = (byte)0xFF;
+                        } else {
+                            result[count++] =
+                              (byte)((idata[i][j] >> 16) & 0xFF);
+                            result[count++] =
+                              (byte)((idata[i][j] >> 8) & 0xFF);
+                            result[count++] = (byte)((idata[i][j]) & 0xFF);
+                        }
+                    }
+                }
+                break;
+            default:
+                // error
+                break;
+        }
+
+        try {
+            FopImage fopimg = new TempImage(width, height, result, mask);
+            AffineTransform at = getTransform();
+            double[] matrix = new double[6];
+            at.getMatrix(matrix);
+            psRenderer.write("gsave");
+                       Shape imclip = getClip();
+                       writeClip(imclip);
+            //psRenderer.write("" + matrix[0] + " " + matrix[1] +
+            //                    " " + matrix[2] + " " + matrix[3] + " " +
+            //                    matrix[4] + " " + matrix[5] + " cm\n");
+psRenderer.renderBitmap(fopimg, x, y, width, height);
+            psRenderer.write("grestore");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    public BufferedImage buildBufferedImage(Dimension size) {
+        return new BufferedImage(size.width, size.height,
+                                 BufferedImage.TYPE_INT_ARGB);
+    }
+
+    class TempImage implements FopImage {
+        int m_height;
+        int m_width;
+        int m_bitsPerPixel;
+        ColorSpace m_colorSpace;
+        int m_bitmapSiye;
+        byte[] m_bitmaps;
+        byte[] m_mask;
+        PDFColor transparent = new PDFColor(255, 255, 255);
+
+        TempImage(int width, int height,
+                  byte[] result, byte[] mask) throws FopImageException {
+            this.m_height = height;
+            this.m_width = width;
+            this.m_bitsPerPixel = 8;
+            this.m_colorSpace = new ColorSpace(ColorSpace.DEVICE_RGB);
+            //this.m_isTransparent = false;
+            //this.m_bitmapsSize = this.m_width * this.m_height * 3;
+            this.m_bitmaps = result;
+            this.m_mask = mask;
+        }
+
+        public String getURL() {
+            return "" + m_bitmaps;
+        }
+
+        // image size
+        public int getWidth() throws FopImageException {
+            return m_width;
+        }
+
+        public int getHeight() throws FopImageException {
+            return m_height;
+        }
+
+        // DeviceGray, DeviceRGB, or DeviceCMYK
+        public ColorSpace getColorSpace() throws FopImageException {
+            return m_colorSpace;
+        }
+
+        // bits per pixel
+        public int getBitsPerPixel() throws FopImageException {
+            return m_bitsPerPixel;
+        }
+
+        // For transparent images
+        public boolean isTransparent() throws FopImageException {
+            return transparent != null;
+        }
+        public PDFColor getTransparentColor() throws FopImageException {
+            return transparent;
+        }
+        public byte[] getMask()  throws FopImageException {
+            return m_mask;
+        }
+        // get the image bytes, and bytes properties
+
+        // get uncompressed image bytes
+        public byte[] getBitmaps() throws FopImageException {
+            return m_bitmaps;
+        }
+        // width * (bitsPerPixel / 8) * height, no ?
+        public int getBitmapsSize() throws FopImageException {
+            return m_width * m_height * 3;
+        }
+
+        // get compressed image bytes
+        // I don't know if we really need it, nor if it
+        // should be changed...
+        public byte[] getRessourceBytes() throws FopImageException {
+            return null;
+        }
+        public int getRessourceBytesSize() throws FopImageException {
+            return 0;
+        }
+        // return null if no corresponding PDFFilter
+        public PDFFilter getPDFFilter() throws FopImageException {
+            return null;
+        }
+
+        // release memory
+        public void close() {}
+
+    }
+
+
+    /**
+     * 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.
+     * @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");
+        psRenderer = null;
+        fontState = null;
+        currentFontName = null;
+        currentColour = null;
+        fontInfo = 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)");
+            psRenderer.write("gsave");
+                       Shape imclip = getClip();
+                       writeClip(imclip);
+        Color c = getColor();
+        psRenderer.write(c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " setrgbcolor");
+
+        applyPaint(getPaint(), false);
+        applyStroke(getStroke());
+
+        psRenderer.write("newpath");
+        PathIterator iter = s.getPathIterator(getTransform());
+        while (!iter.isDone()) {
+            double vals[] = new double[6];
+            int type = iter.currentSegment(vals);
+            switch (type) {
+                case PathIterator.SEG_CUBICTO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[2]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[3]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[4]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[5]) + " curveto");
+                    break;
+                case PathIterator.SEG_LINETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " lineto");
+                    break;
+                case PathIterator.SEG_MOVETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " M");
+                    break;
+                case PathIterator.SEG_QUADTO:
+//                    psRenderer.write((1000 * PDFNumber.doubleOut(vals[0])) +
+//                                        " " + (1000 * PDFNumber.doubleOut(vals[1])) + " " +
+//                                        (1000 * PDFNumber.doubleOut(vals[2])) + " " +
+//                                        (1000 * PDFNumber.doubleOut(vals[3])) + " y\n");
+                    break;
+                case PathIterator.SEG_CLOSE:
+                    psRenderer.write("closepath");
+                    break;
+                default:
+                    break;
+            }
+            iter.next();
+        }
+        doDrawing(false, true, false);
+            psRenderer.write("grestore");
+    }
+
+    protected void writeClip(Shape s) {
+        PathIterator iter = s.getPathIterator(getTransform());
+        psRenderer.write("newpath");
+        while (!iter.isDone()) {
+            double vals[] = new double[6];
+            int type = iter.currentSegment(vals);
+            switch (type) {
+                case PathIterator.SEG_CUBICTO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[2]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[3]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[4]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[5]) + " curveto");
+                    break;
+                case PathIterator.SEG_LINETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " lineto");
+                    break;
+                case PathIterator.SEG_MOVETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " M");
+                    break;
+                case PathIterator.SEG_QUADTO:
+//                    psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) +
+//                                        " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " +
+//                                        1000 * PDFNumber.doubleOut(vals[2]) + " " +
+//                                        1000 * PDFNumber.doubleOut(vals[3]) + " y\n");
+                    break;
+                case PathIterator.SEG_CLOSE:
+                    psRenderer.write("closepath");
+                    break;
+                default:
+                    break;
+            }
+            iter.next();
+        }
+        // clip area
+               psRenderer.write("clippath");
+    }
+
+    protected void applyPaint(Paint paint, boolean fill) {
+        if(paint instanceof GradientPaint) {
+            GradientPaint gp = (GradientPaint)paint;
+            Color c1 = gp.getColor1();
+            Color c2 = gp.getColor2();
+            Point2D p1 = gp.getPoint1();
+            Point2D p2 = gp.getPoint2();
+            boolean cyclic = gp.isCyclic();
+
+                       Vector theCoords = new Vector();
+                                               theCoords.addElement( new Double(p1.getX()));
+            theCoords.addElement( new Double(p1.getY()));
+            theCoords.addElement( new Double(p2.getX()));
+            theCoords.addElement( new Double(p2.getY()));
+
+                               Vector theExtend = new Vector();
+                               theExtend.addElement(new Boolean(true));
+                               theExtend.addElement(new Boolean(true));
+
+                               Vector theDomain = new Vector();
+                               theDomain.addElement(new Double(0));
+                               theDomain.addElement(new Double(1));
+
+                               Vector theEncode = new Vector();
+                               theEncode.addElement(new Double(0));
+                               theEncode.addElement(new Double(1));
+                               theEncode.addElement(new Double(0));
+                               theEncode.addElement(new Double(1));
+
+                               Vector theBounds = new Vector();
+                               theBounds.addElement(new Double(0));
+                               theBounds.addElement(new Double(1));
+
+                               Vector theFunctions = new Vector();
+
+            Vector someColors = new Vector();
+
+            PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(), c1.getBlue());
+            someColors.addElement(color1);
+            PDFColor color2 = new PDFColor(c2.getRed(), c2.getGreen(), c2.getBlue());
+            someColors.addElement(color2);
+
+            ColorSpace aColorSpace = new ColorSpace(ColorSpace.DEVICE_RGB);
+        } else if(paint instanceof TexturePaint) {
+        }
+    }
+
+    protected void applyStroke(Stroke stroke) {
+        if(stroke instanceof BasicStroke) {
+            BasicStroke bs = (BasicStroke)stroke;
+
+            float[] da = bs.getDashArray();
+            if(da != null) {
+                psRenderer.write("[");
+                for(int count = 0; count < da.length; count++) {
+                    psRenderer.write("" + (1000 * (int)da[count]));
+                    if(count < da.length - 1) {
+                        psRenderer.write(" ");
+                    }
+                }
+                psRenderer.write("] ");
+                float offset = bs.getDashPhase();
+                psRenderer.write((1000 * (int)offset) + " setdash");
+            }
+            int ec = bs.getEndCap();
+            switch(ec) {
+                case BasicStroke.CAP_BUTT:
+                    psRenderer.write(0 + " setlinecap");
+                break;
+                case BasicStroke.CAP_ROUND:
+                    psRenderer.write(1 + " setlinecap");
+                break;
+                case BasicStroke.CAP_SQUARE:
+                    psRenderer.write(2 + " setlinecap");
+                break;
+            }
+
+            int lj = bs.getLineJoin();
+            switch(lj) {
+                case BasicStroke.JOIN_MITER:
+                    psRenderer.write(0 + " setlinejoin");
+                break;
+                case BasicStroke.JOIN_ROUND:
+                    psRenderer.write(1 + " setlinejoin");
+                break;
+                case BasicStroke.JOIN_BEVEL:
+                    psRenderer.write(2 + " setlinejoin");
+                break;
+            }
+            float lw = bs.getLineWidth();
+            psRenderer.write(PDFNumber.doubleOut(1000 * lw) + " setlinewidth");
+
+            float ml = bs.getMiterLimit();
+            psRenderer.write(PDFNumber.doubleOut(1000 * ml) + " setmiterlimit");  
+        }
+    }
+
+    /**
+     * 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>,&nbsp;<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,&nbsp;y the coordinates 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)");
+        psRenderer.write("BT");
+      Shape imclip = getClip();
+      writeClip(imclip);
+        Color c = getColor();
+        psRenderer.write(c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " setrgbcolor");
+
+        AffineTransform trans = getTransform();
+        trans.translate(x, y);
+        double[] vals = new double[6];
+        trans.getMatrix(vals);
+
+        psRenderer.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]) + " " +
+                           PDFNumber.doubleOut(vals[6]) + " Tm [" + s + "]");
+
+        psRenderer.write("ET");
+    }
+
+    /**
+     * 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>,&nbsp;<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,&nbsp;y the coordinates 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)");
+
+        psRenderer.write("BT");
+      Shape imclip = getClip();
+      writeClip(imclip);
+        Color c = getColor();
+        currentColour = new PDFColor(c.getRed(), c.getGreen(), c.getBlue());
+        psRenderer.write(currentColour.getColorSpaceOut(true));
+        c = getBackground();
+        PDFColor col = new PDFColor(c.getRed(), c.getGreen(), c.getBlue());
+        psRenderer.write(col.getColorSpaceOut(false));
+
+        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();
+
+            psRenderer.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]) + " " +
+                           PDFNumber.doubleOut(vals[6]) + " Tm [" + ch + "]");
+        }
+
+        psRenderer.write("ET");
+    }
+
+    /**
+     * 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");
+            psRenderer.write("gsave");
+                       Shape imclip = getClip();
+                       writeClip(imclip);
+        Color c = getColor();
+        psRenderer.write(c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " setrgbcolor");
+
+        applyPaint(getPaint(), true);
+
+        psRenderer.write("newpath");
+        PathIterator iter = s.getPathIterator(getTransform());
+        while (!iter.isDone()) {
+            double vals[] = new double[6];
+            int type = iter.currentSegment(vals);
+            switch (type) {
+                case PathIterator.SEG_CUBICTO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[2]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[3]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[4]) + " " +
+                                        PDFNumber.doubleOut(1000 * vals[5]) + " curveto");
+                    break;
+                case PathIterator.SEG_LINETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " lineto");
+                    break;
+                case PathIterator.SEG_MOVETO:
+                    psRenderer.write(PDFNumber.doubleOut(1000 * vals[0]) +
+                                        " " + PDFNumber.doubleOut(1000 * vals[1]) + " M");
+                    break;
+                case PathIterator.SEG_QUADTO:
+                    //psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) +
+                    //                    " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " +
+                    //                    1000 * PDFNumber.doubleOut(vals[2]) + " " +
+                    //                    1000 * PDFNumber.doubleOut(vals[3]) + " y\n");
+                    break;
+                case PathIterator.SEG_CLOSE:
+                    psRenderer.write("closepath");
+                    break;
+                default:
+                    break;
+            }
+            iter.next();
+        }
+        doDrawing(true, false,
+                  iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
+            psRenderer.write("grestore");
+    }
+
+    protected void doDrawing(boolean fill, boolean stroke,
+                             boolean nonzero) {
+        if (fill) {
+            if (stroke) {
+                if (!nonzero)
+                    psRenderer.write("stroke");
+                else
+                    psRenderer.write("stroke");
+            } else {
+                if (!nonzero)
+                    psRenderer.write("fill");
+                else
+                    psRenderer.write("fill");
+            }
+        } else {
+            //if(stroke)
+            psRenderer.write("stroke");
+        }
+    }
+
+    /**
+     * Returns the device configuration associated with this
+     * <code>Graphics2D</code>.
+     */
+    public GraphicsConfiguration getDeviceConfiguration() {
+        //System.out.println("getDeviceConviguration");
+        return GraphicsEnvironment.getLocalGraphicsEnvironment().
+               getDefaultScreenDevice().getDefaultConfiguration();
+    }
+
+    /**
+     * 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 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");
+    }
+
+}
index 1f080acac390c8873514c77a3d05b74c5318e40f..442065110327e8cc734135463573629ef5fed4c1 100644 (file)
@@ -18,14 +18,36 @@ import org.apache.fop.layout.inline.*;
 import org.apache.fop.datatypes.*;
 import org.apache.fop.fo.properties.*;
 import org.apache.fop.render.pdf.Font;
+import org.apache.fop.image.*;
+
+import org.apache.batik.bridge.*;
+import org.apache.batik.swing.svg.*;
+import org.apache.batik.swing.gvt.*;
+import org.apache.batik.gvt.*;
+import org.apache.batik.gvt.renderer.*;
+import org.apache.batik.gvt.filter.*;
+import org.apache.batik.gvt.event.*;
 
 // SVG
 import org.w3c.dom.svg.SVGSVGElement;
 import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.*;
+import org.w3c.dom.svg.*;
 
 // Java
 import java.io.*;
 import java.util.*;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.font.FontRenderContext;
+import java.awt.Dimension;
 
 /**
  * Renderer that renders to PostScript.
@@ -401,17 +423,94 @@ public class PSRenderer implements Renderer {
     public void renderSVGArea(SVGArea area) {
         int x = this.currentXPosition;
         int y = this.currentYPosition;
-        SVGSVGElement svg =
-          ((SVGDocument) area.getSVGDocument()).getRootElement();
+        Document doc = area.getSVGDocument();
+        SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
         int w = (int)(svg.getWidth().getBaseVal().getValue() * 1000);
         int h = (int)(svg.getHeight().getBaseVal().getValue() * 1000);
-
+        float sx = 1, sy = -1;
+        int xOffset = x, yOffset = y;
+
+        /*
+         * Clip to the svg area.
+         * Note: To have the svg overlay (under) a text area then use
+         * an fo:block-container
+         */
         comment("% --- SVG Area");
-        /**@todo Implement SVG */
+        write("gsave");
+        if (w != 0 && h != 0) {
+/*            write("newpath");
+            write(x / 1000f + " " + y / 1000f + " M");
+            write((x + w) / 1000f + " " + y / 1000f + " rlineto");
+            write((x + w) / 1000f + " " + (y - h) / 1000f +
+                              " rlineto");
+            write(x / 1000f + " " + (y - h) / 1000f + " rlineto");
+            write("closepath");
+            write("clippath");
+*/        }
+        // transform so that the coordinates (0,0) is from the top left
+        // and positive is down and to the right. (0,0) is where the
+        // viewBox puts it.
+        write(xOffset +
+                          " " + yOffset + " translate");
+        write(sx + " " + sy + " " + " scale");
+
+
+        UserAgent userAgent = new MUserAgent(new AffineTransform());
+
+        GVTBuilder builder = new GVTBuilder();
+        GraphicsNodeRenderContext rc = getRenderContext();
+        BridgeContext ctx = new BridgeContext(userAgent, rc);
+        GraphicsNode root;
+        PSGraphics2D graphics =
+          new PSGraphics2D(false, area.getFontState(), this,
+                            currentFontName, currentFontSize, currentXPosition,
+                            currentYPosition);
+        graphics.setGraphicContext(
+          new org.apache.batik.ext.awt.g2d.GraphicContext());
+        graphics.setRenderingHints(rc.getRenderingHints());
+        try {
+            root = builder.build(ctx, doc);
+            root.paint(graphics, rc);
+        } catch (Exception e) {
+            MessageHandler.errorln("Error: svg graphic could not be rendered: " + e.getMessage());
+            //e.printStackTrace();
+        }
+
+        write("grestore");
+
         comment("% --- SVG Area end");
         movetoCurrPosition();
     }
 
+    public GraphicsNodeRenderContext getRenderContext() {
+        GraphicsNodeRenderContext nodeRenderContext = null;
+        if (nodeRenderContext == null) {
+            RenderingHints hints = new RenderingHints(null);
+            hints.put(RenderingHints.KEY_ANTIALIASING,
+                      RenderingHints.VALUE_ANTIALIAS_ON);
+
+            hints.put(RenderingHints.KEY_INTERPOLATION,
+                      RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+
+            FontRenderContext fontRenderContext =
+              new FontRenderContext(new AffineTransform(), true,
+                                    true);
+
+            TextPainter textPainter = new StrokingTextPainter();
+            //TextPainter textPainter = new PDFTextPainter();
+
+            GraphicsNodeRableFactory gnrFactory =
+              new ConcreteGraphicsNodeRableFactory();
+
+            nodeRenderContext = new GraphicsNodeRenderContext(
+                                  new AffineTransform(), null, hints,
+                                  fontRenderContext, textPainter, gnrFactory);
+            nodeRenderContext.setTextPainter(textPainter);
+        }
+
+        return nodeRenderContext;
+    }
+
     public void renderBitmap(FopImage img, int x, int y, int w, int h) {
         try {
             boolean iscolor = img.getColorSpace().getColorSpace() !=
@@ -502,7 +601,10 @@ public class PSRenderer implements Renderer {
         //if (imagecount!=4) return;
 
         comment("% --- ImageArea");
-        renderBitmap(area.getImage(), x, y, w, h);
+        if(area.getImage() instanceof SVGImage) {
+        } else {
+            renderBitmap(area.getImage(), x, y, w, h);
+        }
         comment("% --- ImageArea end");
     }
 
@@ -853,4 +955,107 @@ public class PSRenderer implements Renderer {
         }
     }
 
+    protected class MUserAgent implements UserAgent {
+        AffineTransform currentTransform = null;
+        /**
+         * Creates a new SVGUserAgent.
+         */
+        protected MUserAgent(AffineTransform at) {
+            currentTransform = at;
+        }
+
+        /**
+         * Displays an error message.
+         */
+        public void displayError(String message) {
+            System.err.println(message);
+        }
+
+        /**
+         * Displays an error resulting from the specified Exception.
+         */
+        public void displayError(Exception ex) {
+            ex.printStackTrace(System.err);
+        }
+
+        /**
+         * Displays a message in the User Agent interface.
+         * The given message is typically displayed in a status bar.
+         */
+        public void displayMessage(String message) {
+            System.out.println(message);
+        }
+
+        /**
+         * Returns a customized the pixel to mm factor.
+         */
+        public float getPixelToMM() {
+            return 0.264583333333333333333f; // 72 dpi
+        }
+
+        /**
+         * Returns the language settings.
+         */
+        public String getLanguages() {
+            return "en";//userLanguages;
+        }
+
+        /**
+         * 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.
+         */
+        public String getXMLParserClassName() {
+            String parserClassName = System.getProperty("org.xml.sax.parser");
+            if (parserClassName == null) {
+                parserClassName = "org.apache.xerces.parsers.SAXParser";
+            }
+            return parserClassName;//application.getXMLParserClassName();
+        }
+
+        /**
+         * Opens a link in a new component.
+         * @param doc The current document.
+         * @param uri The document URI.
+         */
+        public void openLink(SVGAElement elt) {
+            //application.openLink(uri);
+        }
+
+        public Point getClientAreaLocationOnScreen() {
+            return new Point(0, 0);
+        }
+
+        public void setSVGCursor(java.awt.Cursor cursor) {
+        }
+
+        public AffineTransform getTransform() {
+            return currentTransform;
+        }
+
+        public Dimension2D getViewportSize() {
+            return new Dimension(100, 100);
+        }
+
+        public EventDispatcher getEventDispatcher() {
+            return null;
+        }
+
+        public boolean supportExtension(String str) {
+            return false;
+        }
+
+        public boolean hasFeature(String str) {
+            return false;
+        }
+
+        public void registerExtension(BridgeExtension be) {
+        }
+    }
 }