From 758c89f89e3d2ef88b8c5c76aae41f7e8ecbb26a Mon Sep 17 00:00:00 2001 From: Keiron Liddle Date: Fri, 22 Jun 2001 14:16:46 +0000 Subject: [PATCH] added support for rendering SVG graphics to ps not fully functional git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194313 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/render/ps/PSGraphics2D.java | 905 ++++++++++++++++++ src/org/apache/fop/render/ps/PSRenderer.java | 215 ++++- 2 files changed, 1115 insertions(+), 5 deletions(-) create mode 100644 src/org/apache/fop/render/ps/PSGraphics2D.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 index 000000000..c107405cc --- /dev/null +++ b/src/org/apache/fop/render/ps/PSGraphics2D.java @@ -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 AbstractGraphics2D is a + * simple help to programmers to get started with their own + * implementation of Graphics2D. + * DefaultGraphics2D implements all the abstract methods + * is AbstractGraphics2D and makes it easy to start + * implementing a Graphic2D piece-meal. + * + * @author Keiron Liddle + * @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 Graphics object that is + * a copy of this Graphics 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 + * (xy) in this graphics context's coordinate + * space. Transparent pixels in the image do not affect whatever + * pixels are already there. + *

+ * 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. + *

+ * If the image has not yet been completely loaded, then + * drawImage returns false. 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 x coordinate. + * @param y the y 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. + *

+ * 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. + *

+ * 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 + * drawImage returns false. As more of + * the image becomes available, the process that draws the image notifies + * the image observer by calling its imageUpdate method. + *

+ * 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 x coordinate. + * @param y the y 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 Graphics object cannot be used after + * disposehas been called. + *

+ * When a Java program runs, a large number of Graphics + * 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. + *

+ * Graphics objects which are provided as arguments to the + * paint and update methods + * of components are automatically released by the system when + * those methods return. For efficiency, programmers should + * call dispose when finished using + * a Graphics object only if it was created + * directly from a component or another Graphics 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 Shape using the settings of the + * current Graphics2D context. The rendering attributes + * applied include the Clip, Transform, + * Paint, Composite and + * Stroke attributes. + * @param s the Shape 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 Transform in the Graphics2D. + * The specified transformation is applied to the image before the + * transform attribute in the Graphics2D context is applied. + * The rendering attributes applied include the Clip, + * Transform, and Composite 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 Transform in the Graphics2D. + * The specified transformation is applied to the image before the + * transform attribute in the Graphics2D context is applied. + * The rendering attributes applied include the Clip, + * Transform, and Composite attributes. Note + * that no rendering is done if the specified transform is + * noninvertible. + *

+ * Rendering hints set on the Graphics2D object might + * be used in rendering the RenderableImage. + * If explicit control is required over specific hints recognized by a + * specific RenderableImage, or if knowledge of which hints + * are used is required, then a RenderedImage should be + * obtained directly from the RenderableImage + * 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 String, + * using the current Font and Paint attributes + * in the Graphics2D context. + * The baseline of the first character is at position + * (xy) in the User Space. + * The rendering attributes applied include the Clip, + * Transform, Paint, Font and + * Composite 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 String to be rendered + * @param x, y the coordinates where the String + * 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 + * Graphics2D context's current Paint. The + * iterator must specify a font + * for each character. The baseline of the + * first character is at position (xy) in the + * User Space. + * The rendering attributes applied include the Clip, + * Transform, Paint, and + * Composite 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, 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 Shape using the settings of the + * Graphics2D context. The rendering attributes applied + * include the Clip, Transform, + * Paint, and Composite. + * @param s the Shape 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 + * Graphics2D. + */ + 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. + *

+ * When drawing operations are performed, pixels which are the + * current color are changed to the specified color, and vice versa. + *

+ * 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 + * dx and dy. From the point specified + * by x and y, 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 + * dx or dy. + * If a portion of the source rectangle lies outside the bounds + * of the component, or is obscured by another window or component, + * copyArea will be unable to copy the associated + * pixels. The area that is omitted can be refreshed by calling + * the component's paint method. + * @param x the x coordinate of the source rectangle. + * @param y the y 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/org/apache/fop/render/ps/PSRenderer.java b/src/org/apache/fop/render/ps/PSRenderer.java index 1f080acac..442065110 100644 --- a/src/org/apache/fop/render/ps/PSRenderer.java +++ b/src/org/apache/fop/render/ps/PSRenderer.java @@ -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) { + } + } } -- 2.39.5