--- /dev/null
+/*-- $Id$ --
+
+ ============================================================================
+ The Apache Software License, Version 1.1
+ ============================================================================
+
+ Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modifica-
+ tion, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ 3. The end-user documentation included with the redistribution, if any, must
+ include the following acknowledgment: "This product includes software
+ developed by the Apache Software Foundation (http://www.apache.org/)."
+ Alternately, this acknowledgment may appear in the software itself, if
+ and wherever such third-party acknowledgments normally appear.
+
+ 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ endorse or promote products derived from this software without prior
+ written permission. For written permission, please contact
+ apache@apache.org.
+
+ 5. Products derived from this software may not be called "Apache", nor may
+ "Apache" appear in their name, without prior written permission of the
+ Apache Software Foundation.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many individuals
+ on behalf of the Apache Software Foundation and was originally created by
+ James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ Software Foundation, please see <http://www.apache.org/>.
+
+ */
+
+package org.apache.fop.render.pdf;
+
+// FOP
+import org.apache.fop.messaging.MessageHandler;
+import org.apache.fop.image.ImageArea;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.properties.*;
+import org.apache.fop.layout.*;
+import org.apache.fop.datatypes.*;
+import org.apache.fop.svg.PathPoint;
+import org.apache.fop.pdf.*;
+import org.apache.fop.layout.*;
+import org.apache.fop.image.*;
+
+import org.w3c.dom.*;
+import org.w3c.dom.svg.*;
+import org.w3c.dom.css.*;
+import org.w3c.dom.svg.SVGLength;
+
+import org.apache.fop.dom.svg.*;
+import org.apache.fop.dom.svg.SVGRectElementImpl;
+import org.apache.fop.dom.svg.SVGTextElementImpl;
+import org.apache.fop.dom.svg.SVGLineElementImpl;
+import org.apache.fop.dom.svg.SVGArea;
+
+// Java
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.awt.Rectangle;
+import java.util.Vector;
+import java.util.Hashtable;
+
+/**
+ * Renderer that renders SVG to PDF
+ */
+public class SVGRenderer {
+
+ /** the PDF Document being created */
+ protected PDFDocument pdfDoc;
+
+ protected FontState fontState;
+
+ /** the /Resources object of the PDF document being created */
+ // protected PDFResources pdfResources;
+
+ /** the current stream to add PDF commands to */
+ StringWriter currentStream = new StringWriter();
+
+ /** 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 */
+ private PDFColor currentColour = new PDFColor(0, 0, 0);
+
+ /**
+ * create the SVG renderer
+ */
+ public SVGRenderer(FontState fs, PDFDocument doc, String font,
+ int size, int xpos, int ypos) {
+ pdfDoc = doc;
+ currentFontName = font;
+ currentFontSize = size;
+ currentYPosition = ypos;
+ currentXPosition = xpos;
+ fontState = fs;
+ }
+
+ public String getString() {
+ return currentStream.toString();
+ }
+
+ /**
+ * Renders an SVG element in an SVG document.
+ * This renders each of the child elements.
+ */
+ protected void renderSVG(SVGSVGElement svg, int x, int y) {
+ NodeList nl = svg.getChildNodes();
+ for (int count = 0; count < nl.getLength(); count++) {
+ Node n = nl.item(count);
+ if (n instanceof SVGElement) {
+ renderElement((SVGElement) n, x, y);
+ }
+ }
+ }
+
+ public void renderGArea(SVGGElement area, int posx, int posy) {
+ NodeList nl = area.getChildNodes();
+ for (int count = 0; count < nl.getLength(); count++) {
+ Node n = nl.item(count);
+ if (n instanceof SVGElement) {
+ renderElement((SVGElement) n, posx, posy);
+ }
+ }
+ }
+
+ /**
+ * Handles the SVG switch element.
+ * The switch determines which of its child elements should be rendered
+ * according to the required extensions, required features and system language.
+ */
+ protected void handleSwitchElement(int posx, int posy,
+ SVGSwitchElement ael) {
+ SVGList relist = ael.getRequiredExtensions();
+ SVGList rflist = ael.getRequiredFeatures();
+ SVGList sllist = ael.getSystemLanguage();
+ NodeList nl = ael.getChildNodes();
+ choices:
+ for (int count = 0; count < nl.getLength(); count++) {
+ org.w3c.dom.Node n = nl.item(count);
+ // only render the first child that has a valid
+ // test data
+ if (n instanceof GraphicElement) {
+ GraphicElement graphic = (GraphicElement) n;
+ SVGList grelist = graphic.getRequiredExtensions();
+ // if null it evaluates to true
+ if (grelist != null) {
+ if (grelist.getNumberOfItems() == 0) {
+ if ((relist != null) &&
+ relist.getNumberOfItems() != 0) {
+ continue choices;
+ }
+ }
+ for (int i = 0; i < grelist.getNumberOfItems(); i++) {
+ String str = (String) grelist.getItem(i);
+ if (relist == null) {
+ // use default extension set
+ // currently no extensions are supported
+ // if(!(str.equals("http:// ??"))) {
+ continue choices;
+ // }
+ } else {
+ }
+ }
+ }
+ SVGList grflist = graphic.getRequiredFeatures();
+ if (grflist != null) {
+ if (grflist.getNumberOfItems() == 0) {
+ if ((rflist != null) &&
+ rflist.getNumberOfItems() != 0) {
+ continue choices;
+ }
+ }
+ for (int i = 0; i < grflist.getNumberOfItems(); i++) {
+ String str = (String) grflist.getItem(i);
+ if (rflist == null) {
+ // use default feature set
+ if (!(str.equals("org.w3c.svg.static") ||
+ str.equals("org.w3c.dom.svg.all"))) {
+ continue choices;
+ }
+ } else {
+ boolean found = false;
+ for (int j = 0;
+ j < rflist.getNumberOfItems(); j++) {
+ if (rflist.getItem(j).equals(str)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ continue choices;
+ }
+ }
+ }
+ SVGList gsllist = graphic.getSystemLanguage();
+ if (gsllist != null) {
+ if (gsllist.getNumberOfItems() == 0) {
+ if ((sllist != null) &&
+ sllist.getNumberOfItems() != 0) {
+ continue choices;
+ }
+ }
+ for (int i = 0; i < gsllist.getNumberOfItems(); i++) {
+ String str = (String) gsllist.getItem(i);
+ if (sllist == null) {
+ // use default feature set
+ if (!(str.equals("en"))) {
+ continue choices;
+ }
+ } else {
+ boolean found = false;
+ for (int j = 0;
+ j < sllist.getNumberOfItems(); j++) {
+ if (sllist.getItem(j).equals(str)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ continue choices;
+ }
+ }
+ }
+ renderElement((SVGElement) n, posx, posy);
+ // only render the first valid one
+ break;
+ }
+ }
+ }
+
+ /**
+ * add a line to the current stream
+ *
+ * @param x1 the start x location in millipoints
+ * @param y1 the start y location in millipoints
+ * @param x2 the end x location in millipoints
+ * @param y2 the end y location in millipoints
+ * @param th the thickness in millipoints
+ * @param r the red component
+ * @param g the green component
+ * @param b the blue component
+ */
+ protected void addLine(float x1, float y1, float x2, float y2,
+ DrawingInstruction di) {
+ String str;
+ str = "" + x1 + " " + y1 + " m " + x2 + " " + y2 + " l";
+ if (di != null && di.fill)
+ currentStream.write(str + " f\n"); // ??
+ currentStream.write(str + " S\n");
+ }
+
+ /**
+ * Add an SVG circle
+ * Uses bezier curves to approximate the shape of a circle.
+ */
+ protected void addCircle(float cx, float cy, float r,
+ DrawingInstruction di) {
+ String str;
+ str = "" + cx + " " + (cy - r) + " m\n" + "" +
+ (cx + 21 * r / 40f) + " " + (cy - r) + " " +
+ (cx + r) + " " + (cy - 21 * r / 40f) + " " +
+ (cx + r) + " " + cy + " c\n" + "" + (cx + r) + " " +
+ (cy + 21 * r / 40f) + " " + (cx + 21 * r / 40f) +
+ " " + (cy + r) + " " + cx + " " + (cy + r) + " c\n" +
+ "" + (cx - 21 * r / 40f) + " " + (cy + r) + " " +
+ (cx - r) + " " + (cy + 21 * r / 40f) + " " +
+ (cx - r) + " " + cy + " c\n" + "" + (cx - r) + " " +
+ (cy - 21 * r / 40f) + " " + (cx - 21 * r / 40f) +
+ " " + (cy - r) + " " + cx + " " + (cy - r) + " c\n";
+
+ currentStream.write(str);
+ doDrawing(di);
+ }
+
+ /**
+ * Add an SVG ellips
+ * Uses bezier curves to approximate the shape of an ellipse.
+ */
+ protected void addEllipse(float cx, float cy, float rx, float ry,
+ DrawingInstruction di) {
+ String str;
+ str = "" + cx + " " + (cy - ry) + " m\n" + "" +
+ (cx + 21 * rx / 40f) + " " + (cy - ry) + " " +
+ (cx + rx) + " " + (cy - 21 * ry / 40f) + " " +
+ (cx + rx) + " " + cy + " c\n" + "" + (cx + rx) + " " +
+ (cy + 21 * ry / 40f) + " " + (cx + 21 * rx / 40f) +
+ " " + (cy + ry) + " " + cx + " " + (cy + ry) +
+ " c\n" + "" + (cx - 21 * rx / 40f) + " " + (cy + ry) +
+ " " + (cx - rx) + " " + (cy + 21 * ry / 40f) + " " +
+ (cx - rx) + " " + cy + " c\n" + "" + (cx - rx) + " " +
+ (cy - 21 * ry / 40f) + " " + (cx - 21 * rx / 40f) +
+ " " + (cy - ry) + " " + cx + " " + (cy - ry) + " c\n";
+ currentStream.write(str);
+ doDrawing(di);
+ }
+
+ /**
+ * add an SVG rectangle to the current stream.
+ * If there are curved edges then these are rendered using bezier curves.
+ *
+ * @param x the x position of left edge
+ * @param y the y position of top edge
+ * @param w the width
+ * @param h the height
+ * @param rx the x radius curved edge
+ * @param ry the y radius curved edge
+ */
+ protected void addRect(float x, float y, float w, float h,
+ float rx, float ry, DrawingInstruction di) {
+ String str = "";
+ if (rx == 0.0 && ry == 0.0) {
+ str = "" + x + " " + y + " " + w + " " + h + " re\n";
+ } else {
+ if (ry == 0.0)
+ ry = rx;
+ if (rx > w / 2.0f)
+ rx = w / 2.0f;
+ if (ry > h / 2.0f)
+ ry = h / 2.0f;
+ str = "" + (x + rx) + " " + y + " m\n";
+ str += "" + (x + w - rx) + " " + y + " l\n";
+ str += "" + (x + w - 19 * rx / 40) + " " + y + " " +
+ (x + w) + " " + (y + 19 * ry / 40) + " " +
+ (x + w) + " " + (y + ry) + " c\n";
+ str += "" + (x + w) + " " + (y + h - ry) + " l\n";
+ str += "" + (x + w) + " " + (y + h - 19 * ry / 40) + " " +
+ (x + w - 19 * rx / 40) + " " + (y + h) + " " +
+ (x + w - rx) + " " + (y + h) + " c\n";
+ str += "" + (x + rx) + " " + (y + h) + " l\n";
+ str += "" + (x + 19 * rx / 40) + " " + (y + h) + " " + x +
+ " " + (y + h - 19 * ry / 40) + " " + x + " " +
+ (y + h - ry) + " c\n";
+ str += "" + x + " " + (y + ry) + " l\n";
+ str += "" + x + " " + (y + 19 * ry / 40) + " " +
+ (x + 19 * rx / 40) + " " + y + " " + (x + rx) +
+ " " + y + " c\n";
+ }
+ currentStream.write(str);
+ doDrawing(di);
+ }
+
+ /**
+ * Adds an SVG path to the current streem.
+ * An SVG path is made up of a list of drawing instructions that are rendered
+ * out in order.
+ * Arcs don't work.
+ */
+ protected void addPath(Vector points, int posx, int posy,
+ DrawingInstruction di) {
+ SVGPathSegImpl pathmoveto = null;
+ float lastx = 0;
+ float lasty = 0;
+ for (Enumeration e = points.elements(); e.hasMoreElements();) {
+ SVGPathSegImpl pc = (SVGPathSegImpl) e.nextElement();
+ float[] vals = pc.getValues();
+ float lastcx = 0;
+ float lastcy = 0;
+ switch (pc.getPathSegType()) {
+ case SVGPathSeg.PATHSEG_MOVETO_ABS:
+ pathmoveto = pc;
+ lastx = vals[0];
+ lasty = vals[1];
+ currentStream.write(lastx + " " + lasty + " m\n");
+ break;
+ case SVGPathSeg.PATHSEG_MOVETO_REL:
+ if (pathmoveto == null) {
+ lastx = vals[0];
+ lasty = vals[1];
+ pathmoveto = pc;
+ currentStream.write(lastx + " " + lasty + " m\n");
+ } else {
+ lastx += vals[0];
+ lasty += vals[1];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ }
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_ABS:
+ lastx = vals[0];
+ lasty = vals[1];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_REL:
+ lastx += vals[0];
+ lasty += vals[1];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
+ lasty = vals[0];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
+ lasty += vals[0];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
+ lastx = vals[0];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
+ lastx += vals[0];
+ currentStream.write(lastx + " " + lasty + " l\n");
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
+ lastx = vals[4];
+ lasty = vals[5];
+ lastcx = vals[2];
+ lastcy = vals[3];
+ currentStream.write((vals[0]) + " " + (vals[1]) +
+ " " + (vals[2]) + " " + (vals[3]) + " " +
+ lastx + " " + lasty + " c\n");
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
+ currentStream.write((vals[0] + lastx) + " " +
+ (vals[1] + lasty) + " " +
+ (vals[2] + lastx) + " " +
+ (vals[3] + lasty) + " " +
+ (vals[4] + lastx) + " " +
+ (vals[5] + lasty) + " c\n");
+ lastcx = vals[2] + lastx;
+ lastcy = vals[3] + lasty;
+ lastx += vals[4];
+ lasty += vals[5];
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
+ if (lastcx == 0) {
+ lastcx = lastx;
+ }
+ if (lastcy == 0) {
+ lastcy = lasty;
+ }
+ lastx = vals[2];
+ lasty = vals[3];
+ currentStream.write(lastcx + " " + lastcy + " " +
+ (vals[0]) + " " + (vals[1]) + " " +
+ lastx + " " + lasty + " c\n");
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
+ if (lastcx == 0) {
+ lastcx = lastx;
+ }
+ if (lastcy == 0) {
+ lastcy = lasty;
+ }
+ currentStream.write(lastcx + " " + lastcy + " " +
+ (vals[0] + lastx) + " " +
+ (vals[1] + lasty) + " " +
+ (vals[2] + lastx) + " " +
+ (vals[3] + lasty) + " c\n");
+ lastx += vals[2];
+ lasty += vals[3];
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
+ if (lastcx == 0) {
+ lastcx = lastx;
+ }
+ if (lastcy == 0) {
+ lastcy = lasty;
+ }
+ lastx = vals[0];
+ lasty = vals[1];
+ lastcx = 0;
+ lastcy = 0;
+ currentStream.write(lastcx + " " + lastcy + " " +
+ lastx + " " + lasty + " y\n");
+ break;
+ case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
+ if (lastcx == 0) {
+ lastcx = lastx;
+ }
+ if (lastcy == 0) {
+ lastcy = lasty;
+ }
+ currentStream.write(lastcx + " " + lastcy + " " +
+ (vals[0] + lastx) + " " +
+ (vals[1] + lasty) + " y\n");
+ lastcx = 0;
+ lastcy = 0;
+ lastx += vals[0];
+ lasty += vals[1];
+ break;
+ // get angle between the two points
+ // then get angle of points to centre (ie. both points are on the
+ // apogee and perigee of the ellipse)
+ // then work out the direction from flags
+ case SVGPathSeg.PATHSEG_ARC_ABS:
+ {
+ double rx = vals[0];
+ double ry = vals[1];
+ double theta = vals[2];
+ boolean largearcflag = (vals[3] == 1.0);
+ boolean sweepflag = (vals[4] == 1.0);
+
+ double angle = Math.atan((vals[6] - lasty) /
+ (vals[5] - lastx));
+ double relangle = Math.acos(rx /
+ Math.sqrt((vals[6] - lasty) *
+ (vals[6] - lasty) +
+ (vals[5] - lastx) * (vals[5] - lastx)));
+ double absangle = angle + relangle;
+ // change sign depending on flags
+ double contrx1;
+ double contry1;
+ double contrx2;
+ double contry2;
+ if (largearcflag) {
+ if (sweepflag) {
+ contrx1 = lastx - rx * Math.cos(absangle);
+ contry1 = lasty + rx * Math.sin(absangle);
+ contrx2 = vals[5] + ry * Math.cos(absangle);
+ contry2 = vals[6] + ry * Math.sin(absangle);
+ } else {
+ contrx1 = lastx - rx * Math.cos(absangle);
+ contry1 = lasty - rx * Math.sin(absangle);
+ contrx2 = vals[5] + ry * Math.cos(absangle);
+ contry2 = vals[6] - ry * Math.sin(absangle);
+ }
+ } else {
+ if (sweepflag) {
+ contrx1 = lastx + rx * Math.cos(absangle);
+ contry1 = lasty + rx * Math.sin(absangle);
+ contrx2 = contrx1;
+ contry2 = contry1;
+ } else {
+ contrx1 = lastx + ry * Math.cos(absangle);
+ contry1 = lasty - ry * Math.sin(absangle);
+ contrx2 = contrx1;
+ contry2 = contry1;
+ }
+ }
+
+ double cx = lastx;
+ double cy = lasty;
+ currentStream.write(contrx1 + " " + contry1 +
+ " " + contrx2 + " " + contry2 + " " +
+ vals[5] + " " + vals[6] + " c\n");
+ lastcx = 0; //??
+ lastcy = 0; //??
+ lastx = vals[5];
+ lasty = vals[6];
+ }
+ break;
+ case SVGPathSeg.PATHSEG_ARC_REL:
+ {
+ double rx = vals[0];
+ double ry = vals[1];
+ double theta = vals[2];
+ boolean largearcflag = (vals[3] == 1.0);
+ boolean sweepflag = (vals[4] == 1.0);
+
+ double angle = Math.atan(vals[6] / vals[5]);
+ double relangle = Math.atan(ry / rx);
+ // System.out.println((theta * Math.PI / 180f) + ":" + relangle + ":" + largearcflag + ":" + sweepflag);
+ double absangle = (theta * Math.PI / 180f);//angle + relangle;
+ // change sign depending on flags
+ double contrx1;
+ double contry1;
+ double contrx2;
+ double contry2;
+ if (largearcflag) {
+ // in a large arc we need to do at least 2 and a bit
+ // segments or curves.
+ if (sweepflag) {
+ contrx1 = lastx + rx * Math.cos(absangle);
+ contry1 = lasty + rx * Math.sin(absangle);
+ contrx2 = lastx + vals[5] +
+ ry * Math.cos(absangle);
+ contry2 = lasty + vals[6] -
+ ry * Math.sin(absangle);
+ } else {
+ contrx1 = lastx + rx * Math.sin(absangle);
+ contry1 = lasty + rx * Math.cos(absangle);
+ contrx2 = lastx + vals[5] +
+ ry * Math.cos(absangle);
+ contry2 = lasty + vals[6] +
+ ry * Math.sin(absangle);
+ }
+ } else {
+ // only need at most two segments
+ if (sweepflag) {
+ contrx1 = lastx + rx * Math.cos(absangle);
+ contry1 = lasty - rx * Math.sin(absangle);
+ contrx2 = contrx1;
+ contry2 = contry1;
+ } else {
+ contrx1 = lastx - ry * Math.cos(absangle);
+ contry1 = lasty + ry * Math.sin(absangle);
+ contrx2 = contrx1;
+ contry2 = contry1;
+ }
+ }
+ //System.out.println(contrx1 + ":" + contry1 + ":" + contrx2 + ":" + contry2);
+
+ double cx = lastx;
+ double cy = lasty;
+ currentStream.write(contrx1 + " " + contry1 +
+ " " + contrx2 + " " + contry2 + " " +
+ (vals[5] + lastx) + " " +
+ (vals[6] + lasty) + " c\n");
+
+ lastcx = 0; //??
+ lastcy = 0; //??
+ lastx += vals[5];
+ lasty += vals[6];
+ }
+ break;
+ case SVGPathSeg.PATHSEG_CLOSEPATH:
+ currentStream.write("h\n");
+ break;
+ }
+ }
+ doDrawing(di);
+ }
+
+ /**
+ * Adds an SVG polyline or polygon.
+ * A polygon is merely a closed polyline.
+ * This is made up from a set of points that straight lines are drawn between.
+ */
+ protected void addPolyline(Vector points, DrawingInstruction di,
+ boolean close) {
+ PathPoint pc;
+ float lastx = 0;
+ float lasty = 0;
+ Enumeration e = points.elements();
+ if (e.hasMoreElements()) {
+ pc = (PathPoint) e.nextElement();
+ lastx = pc.x;
+ lasty = pc.y;
+ currentStream.write(lastx + " " + lasty + " m\n");
+ }
+ while (e.hasMoreElements()) {
+ pc = (PathPoint) e.nextElement();
+ lastx = pc.x;
+ lasty = pc.y;
+ currentStream.write(lastx + " " + lasty + " l\n");
+ }
+ if (close)
+ currentStream.write("h\n");
+ doDrawing(di);
+ }
+
+ /**
+ * Writes the drawing instruction out to the current stream
+ * depending on what type of drawing is required.
+ */
+ protected void doDrawing(DrawingInstruction di) {
+ if (di == null) {
+ currentStream.write("S\n");
+ } else {
+ if (di.fill) {
+ if (di.stroke) {
+ if (!di.nonzero)
+ currentStream.write("B*\n");
+ else
+ currentStream.write("B\n");
+ } else {
+ if (!di.nonzero)
+ currentStream.write("f*\n");
+ else
+ currentStream.write("f\n");
+ }
+ } else {
+ // if(di.stroke)
+ currentStream.write("S\n");
+ }
+ }
+ }
+
+ /**
+ * Renders an svg image to the current stream.
+ * This uses the FopImageFactory to load the image and then renders it.
+ */
+ public void renderImage(String href, float x, float y, float width,
+ float height) {
+ try {
+ if (href.indexOf(":") == -1) {
+ href = "file:" + href;
+ }
+ FopImage img = FopImageFactory.Make(href);
+ if (img instanceof SVGImage) {
+ SVGSVGElement svg =
+ ((SVGImage) img).getSVGDocument().getRootElement();
+ currentStream.write("q\n" + width /
+ svg.getWidth().getBaseVal().getValue() + " 0 0 " +
+ height /
+ svg.getHeight().getBaseVal().getValue() + " 0 0 cm\n");
+ renderSVG(svg, (int) x * 1000, (int) y * 1000);
+ currentStream.write("Q\n");
+ // renderSVG(svg);
+ } else if (img != null) {
+ int xObjectNum = this.pdfDoc.addImage(img);
+ currentStream.write("q\n1 0 0 -1 " + 0 + " " +
+ (2 * y + height) + " cm\n" + width + " 0 0 " +
+ height + " " + x + " " + y + " cm\n" + "/Im" +
+ xObjectNum + " Do\nQ\n");
+ // img.close();
+ }
+ } catch (Exception e) {
+ System.err.println("could not add image to SVG: " + href);
+ }
+ }
+
+ /**
+ * A symbol has a viewbox and preserve aspect ratio.
+ */
+ protected void renderSymbol(SVGSymbolElement symbol, int x, int y) {
+ NodeList nl = symbol.getChildNodes();
+ for (int count = 0; count < nl.getLength(); count++) {
+ Node n = nl.item(count);
+ if (n instanceof SVGElement) {
+ renderElement((SVGElement) n, x, y);
+ }
+ }
+ }
+
+ /**
+ * Handles the construction of an SVG gradient.
+ * This gets the gradient element and creates the pdf info
+ * in the pdf document.
+ * The type of gradient is determined by what class the gradient element is.
+ */
+ protected void handleGradient(String sp, DrawingInstruction di,
+ boolean fill, SVGElement area) {
+ // should be a url to a gradient
+ String url = (String) sp;
+ if (url.startsWith("url(")) {
+ String address;
+ int b1 = url.indexOf("(");
+ int b2 = url.indexOf(")");
+ address = url.substring(b1 + 1, b2);
+ SVGElement gi = null;
+ gi = locateDef(address, area);
+ if (gi instanceof SVGLinearGradientElement) {
+ SVGLinearGradientElement linear =
+ (SVGLinearGradientElement) gi;
+ handleLinearGradient(linear, di, fill, area);
+ } else if (gi instanceof SVGRadialGradientElement) {
+ SVGRadialGradientElement radial =
+ (SVGRadialGradientElement) gi;
+ handleRadialGradient(radial, di, fill, area);
+ } else if (gi instanceof SVGPatternElement) {
+ SVGPatternElement pattern = (SVGPatternElement) gi;
+ handlePattern(pattern, di, fill, area);
+ } else {
+ System.err.println("WARNING Invalid fill reference :" +
+ gi + ":" + address);
+ }
+ }
+ }
+
+ protected void handlePattern(SVGPatternElement pattern,
+ DrawingInstruction di, boolean fill, SVGElement area) {
+ SVGAnimatedLength x, y, width, height;
+ short pattUnits = SVGUnitTypes.SVG_UNIT_TYPE_UNKNOWN;
+ NodeList stops = null;
+ x = pattern.getX();
+ y = pattern.getY();
+ width = pattern.getWidth();
+ height = pattern.getHeight();
+ NodeList nl = pattern.getChildNodes();
+ SVGPatternElement ref = (SVGPatternElement) locateDef(
+ pattern.getHref().getBaseVal(), pattern);
+ while (ref != null) {
+ if (x == null) {
+ x = ref.getX();
+ pattUnits = ref.getPatternUnits().getBaseVal();
+ }
+ if (y == null) {
+ y = ref.getY();
+ }
+ if (width == null) {
+ width = ref.getWidth();
+ }
+ if (height == null) {
+ height = ref.getHeight();
+ }
+ if (nl.getLength() == 0) {
+ nl = ref.getChildNodes();
+ }
+ ref = (SVGPatternElement) locateDef(
+ ref.getHref().getBaseVal(), ref);
+ }
+ if (x == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0);
+ x = new SVGAnimatedLengthImpl(length);
+ }
+ if (y == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0);
+ y = new SVGAnimatedLengthImpl(length);
+ }
+ if (width == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 1);
+ width = new SVGAnimatedLengthImpl(length);
+ }
+ if (height == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 1);
+ height = new SVGAnimatedLengthImpl(length);
+ }
+
+ StringWriter realStream = currentStream;
+ currentStream = new StringWriter();
+
+ currentStream.write("q\n");
+ // this makes the pattern the right way up, since it is outside the original
+ // transform around the whole svg document
+ currentStream.write("1 0 0 -1 0 " +
+ height.getBaseVal().getValue() + " cm\n");
+ for (int count = 0; count < nl.getLength(); count++) {
+ Node n = nl.item(count);
+ if (n instanceof SVGElement) {
+ renderElement((SVGElement) n, 0, 0);
+ }
+ }
+ currentStream.write("Q\n");
+
+ double xval = x.getBaseVal().getValue() + currentXPosition / 1000f;
+ double yval = -y.getBaseVal().getValue() + currentYPosition / 1000f;
+ if (area instanceof GraphicElement) {
+ SVGRect bbox = ((GraphicElement) area).getBBox();
+ if (bbox != null) {
+ // xval += bbox.getX();
+ // yval -= bbox.getY();
+ }
+ }
+ double widthval = width.getBaseVal().getValue();
+ double heightval = height.getBaseVal().getValue();
+ Vector bbox = new Vector();
+ bbox.addElement(new Double(0));
+ bbox.addElement(new Double(0));
+ bbox.addElement(new Double(widthval));
+ bbox.addElement(new Double(heightval));
+ Vector translate = new Vector();
+ // combine with pattern transform
+ translate.addElement(new Double(1));
+ translate.addElement(new Double(0));
+ translate.addElement(new Double(0));
+ translate.addElement(new Double(1));
+ translate.addElement(new Double(xval));
+ translate.addElement(new Double(yval));
+ // need to handle PDFResources
+ PDFPattern myPat =
+ this.pdfDoc.makePattern(1, null, 1, 1, bbox, widthval,
+ heightval, translate, null, currentStream.getBuffer());
+
+ currentStream = realStream;
+ currentStream.write(myPat.getColorSpaceOut(fill));
+ if (fill)
+ di.fill = true;
+ else
+ di.stroke = true;
+ }
+
+ protected void handleLinearGradient(
+ SVGLinearGradientElement linear, DrawingInstruction di,
+ boolean fill, SVGElement area) {
+ // first get all the gradient values
+ // if values not present follow the href
+ // the gradient units will be where the vals are specified
+ // the spread method will be where there are stop elements
+ SVGAnimatedLength ax1, ax2, ay1, ay2;
+ short spread = SVGGradientElement.SVG_SPREADMETHOD_UNKNOWN;
+ short gradUnits = SVGUnitTypes.SVG_UNIT_TYPE_UNKNOWN;
+ NodeList stops = null;
+ ax1 = linear.getX1();
+ ax2 = linear.getX2();
+ ay1 = linear.getY1();
+ ay2 = linear.getY2();
+ stops = linear.getChildNodes();
+ SVGLinearGradientElement ref = (SVGLinearGradientElement) locateDef(
+ linear.getHref().getBaseVal(), linear);
+ while (ref != null) {
+ if (ax1 == null) {
+ ax1 = ref.getX1();
+ gradUnits = ref.getGradientUnits().getBaseVal();
+ }
+ if (ax2 == null) {
+ ax2 = ref.getX2();
+ }
+ if (ay1 == null) {
+ ay1 = ref.getY1();
+ }
+ if (ay2 == null) {
+ ay2 = ref.getY2();
+ }
+ if (stops.getLength() == 0) {
+ stops = ref.getChildNodes();
+ }
+ ref = (SVGLinearGradientElement) locateDef(
+ ref.getHref().getBaseVal(), ref);
+ }
+ if (ax1 == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0);
+ ax1 = new SVGAnimatedLengthImpl(length);
+ }
+ if (ax2 == null) {
+ // if x2 is not specified then it should be 100%
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 1);
+ ax2 = new SVGAnimatedLengthImpl(length);
+ }
+ if (ay1 == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0);
+ ay1 = new SVGAnimatedLengthImpl(length);
+ }
+ if (ay2 == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0);
+ ay2 = new SVGAnimatedLengthImpl(length);
+ }
+ Vector theCoords = null;
+ if (gradUnits == SVGUnitTypes.SVG_UNIT_TYPE_UNKNOWN)
+ gradUnits = linear.getGradientUnits().getBaseVal();
+ // spread: pad (normal), reflect, repeat
+ spread = linear.getSpreadMethod().getBaseVal();
+ if (gradUnits == SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE) {
+ if (area instanceof SVGTransformable) {
+ SVGTransformable tf = (SVGTransformable) area;
+ double x1, y1, x2, y2;
+ x1 = ax1.getBaseVal().getValue();
+ y1 = -ay1.getBaseVal().getValue();
+ x2 = ax2.getBaseVal().getValue();
+ y2 = -ay2.getBaseVal().getValue();
+ SVGMatrix matrix = tf.getScreenCTM();
+ double oldx = x1;
+ x1 = matrix.getA() * x1 + matrix.getB() * y1 +
+ matrix.getE();
+ y1 = matrix.getC() * oldx + matrix.getD() * y1 +
+ matrix.getF();
+ oldx = x2;
+ x2 = matrix.getA() * x2 + matrix.getB() * y2 +
+ matrix.getE();
+ y2 = matrix.getC() * oldx + matrix.getD() * y2 +
+ matrix.getF();
+ theCoords = new Vector();
+ if (spread == SVGGradientElement.SVG_SPREADMETHOD_REFLECT) {
+ } else if (spread ==
+ SVGGradientElement.SVG_SPREADMETHOD_REFLECT) {
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f + x1));
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f - y1));
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f + x2));
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f - y2));
+ }
+ }
+ } else if (area instanceof GraphicElement) {
+ SVGRect rect = ((GraphicElement) area).getBBox();
+ if (rect != null) {
+ theCoords = new Vector();
+ SVGLength val;
+ val = ax1.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = ay1.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ val = ax2.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = ay2.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ }
+ }
+ if (theCoords == null) {
+ theCoords = new Vector();
+ theCoords.addElement( new Double(currentXPosition / 1000f +
+ ax1.getBaseVal().getValue()));
+ theCoords.addElement( new Double(currentYPosition / 1000f -
+ ay1.getBaseVal().getValue()));
+ theCoords.addElement( new Double(currentXPosition / 1000f +
+ ax2.getBaseVal().getValue()));
+ theCoords.addElement( new Double(currentYPosition / 1000f -
+ ay2.getBaseVal().getValue()));
+ }
+
+ 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();
+
+ NodeList nl = stops;
+ Vector someColors = new Vector();
+ float lastoffset = 0;
+ Vector lastVector = null;
+ SVGStopElementImpl stop;
+ if (nl.getLength() == 0) {
+ // the color should be "none"
+ if (fill)
+ di.fill = false;
+ else
+ di.stroke = false;
+ return;
+ } else if (nl.getLength() == 1) {
+ stop = (SVGStopElementImpl) nl.item(0);
+ CSSValue cv = stop.getPresentationAttribute("stop-color");
+ if (cv == null) {
+ // maybe using color
+ cv = stop.getPresentationAttribute("color");
+ }
+ if (cv == null) {
+ // problems
+ System.err.println("no stop-color or color in stop element");
+ return;
+ }
+ PDFColor color = new PDFColor(0, 0, 0);
+ if (cv != null &&
+ cv.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) cv).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) cv).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ color = new PDFColor(red, green, blue);
+ }
+ }
+ currentStream.write(color.getColorSpaceOut(fill));
+ if (fill)
+ di.fill = true;
+ else
+ di.stroke = true;
+ return;
+ }
+ for (int count = 0; count < nl.getLength(); count++) {
+ stop = (SVGStopElementImpl) nl.item(count);
+ CSSValue cv = stop.getPresentationAttribute("stop-color");
+ if (cv == null) {
+ // maybe using color
+ cv = stop.getPresentationAttribute("color");
+ }
+ if (cv == null) {
+ // problems
+ System.err.println("no stop-color or color in stop element");
+ continue;
+ }
+ PDFColor color = new PDFColor(0, 0, 0);
+ if (cv != null &&
+ cv.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) cv).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) cv).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ color = new PDFColor(red, green, blue);
+ currentColour = color;
+ }
+ }
+ float offset = stop.getOffset().getBaseVal();
+ Vector colVector = color.getVector();
+ // create bounds from last to offset
+ if (lastVector != null) {
+ Vector theCzero = lastVector;
+ Vector theCone = colVector;
+ PDFFunction myfunc =
+ this.pdfDoc.makeFunction(2, theDomain, null,
+ theCzero, theCone, 1.0);
+ theFunctions.addElement(myfunc);
+ }
+ lastoffset = offset;
+ lastVector = colVector;
+ someColors.addElement(color);
+ }
+ ColorSpace aColorSpace = new ColorSpace(ColorSpace.DEVICE_RGB);
+ /* PDFFunction myfunky = this.pdfDoc.makeFunction(3,
+ theDomain, null,
+ theFunctions, null,
+ theEncode);
+ PDFShading myShad = null;
+ myShad = this.pdfDoc.makeShading(
+ 2, aColorSpace,
+ null, null, false,
+ theCoords, null, myfunky,theExtend);
+ PDFPattern myPat = this.pdfDoc.makePattern(2, myShad, null, null, null);*/
+ PDFPattern myPat = this.pdfDoc.createGradient(false, aColorSpace,
+ someColors, null, theCoords);
+ currentStream.write(myPat.getColorSpaceOut(fill));
+ if (fill)
+ di.fill = true;
+ else
+ di.stroke = true;
+ }
+
+ protected void handleRadialGradient(
+ SVGRadialGradientElement radial, DrawingInstruction di,
+ boolean fill, SVGElement area) {
+ // first get all the gradient values
+ // if values not present follow the href
+ // the gradient units will be where the vals are specified
+ SVGAnimatedLength acx, acy, ar, afx, afy;
+ short gradUnits = radial.getGradientUnits().getBaseVal();
+ NodeList stops = null;
+ acx = radial.getCx();
+ acy = radial.getCy();
+ ar = radial.getR();
+ afx = radial.getFx();
+ afy = radial.getFy();
+ stops = radial.getChildNodes();
+ SVGRadialGradientElement ref = (SVGRadialGradientElement) locateDef(
+ radial.getHref().getBaseVal(), radial);
+ while (ref != null) {
+ if (acx == null) {
+ acx = ref.getCx();
+ gradUnits = ref.getGradientUnits().getBaseVal();
+ }
+ if (acy == null) {
+ acy = ref.getCy();
+ }
+ if (ar == null) {
+ ar = ref.getR();
+ }
+ if (afx == null) {
+ afx = ref.getFx();
+ }
+ if (afy == null) {
+ afy = ref.getFy();
+ }
+ if (stops.getLength() == 0) {
+ stops = ref.getChildNodes();
+ }
+ ref = (SVGRadialGradientElement) locateDef(
+ ref.getHref().getBaseVal(), ref);
+ }
+ if (acx == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0.5f);
+ acx = new SVGAnimatedLengthImpl(length);
+ }
+ if (acy == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0.5f);
+ acy = new SVGAnimatedLengthImpl(length);
+ }
+ if (ar == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 1);
+ ar = new SVGAnimatedLengthImpl(length);
+ }
+ if (afx == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0.5f);
+ afx = new SVGAnimatedLengthImpl(length);
+ }
+ if (afy == null) {
+ SVGLength length = new SVGLengthImpl();
+ length.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 0.5f);
+ afy = new SVGAnimatedLengthImpl(length);
+ }
+ ColorSpace aColorSpace = new ColorSpace(ColorSpace.DEVICE_RGB);
+ org.w3c.dom.NodeList nl = stops;
+ SVGStopElementImpl stop;
+ if (nl.getLength() == 0) {
+ // the color should be "none"
+ if (fill)
+ di.fill = false;
+ else
+ di.stroke = false;
+ return;
+ } else if (nl.getLength() == 1) {
+ stop = (SVGStopElementImpl) nl.item(0);
+ CSSValue cv = stop.getPresentationAttribute("stop-color");
+ if (cv == null) {
+ // maybe using color
+ cv = stop.getPresentationAttribute("color");
+ }
+ if (cv == null) {
+ // problems
+ System.err.println("no stop-color or color in stop element");
+ return;
+ }
+ PDFColor color = new PDFColor(0, 0, 0);
+ if (cv != null &&
+ cv.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) cv).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) cv).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ color = new PDFColor(red, green, blue);
+ }
+ }
+ currentStream.write(color.getColorSpaceOut(fill));
+ if (fill)
+ di.fill = true;
+ else
+ di.stroke = true;
+ return;
+ }
+ Hashtable table = null;
+ Vector someColors = new Vector();
+ Vector theCoords = null;
+ Vector theBounds = new Vector();
+ // the coords should be relative to the current object
+ // check value types, eg. %
+ if (gradUnits == SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE) {
+ if (area instanceof SVGTransformable) {
+ SVGTransformable tf = (SVGTransformable) area;
+ double x1, y1, x2, y2;
+ x1 = acx.getBaseVal().getValue();
+ y1 = -acy.getBaseVal().getValue();
+ x2 = afx.getBaseVal().getValue();
+ y2 = -afy.getBaseVal().getValue();
+ SVGMatrix matrix = tf.getScreenCTM();
+ double oldx = x1;
+ x1 = matrix.getA() * x1 + matrix.getB() * y1 +
+ matrix.getE();
+ y1 = matrix.getC() * oldx + matrix.getD() * y1 +
+ matrix.getF();
+ oldx = x2;
+ x2 = matrix.getA() * x2 + matrix.getB() * y2 +
+ matrix.getE();
+ y2 = matrix.getC() * oldx + matrix.getD() * y2 +
+ matrix.getF();
+ theCoords = new Vector();
+ // if(spread == SVGGradientElement.SVG_SPREADMETHOD_REFLECT) {
+ // } else if(spread== SVGGradientElement.SVG_SPREADMETHOD_REFLECT) {
+ // } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f + x1));
+ // the y val needs to be adjust by 2 * R * rotation
+ // depending on if this value is from an x or y coord
+ // before transformation
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f - y1 +
+ (matrix.getC() - matrix.getD()) * 2 *
+ ar.getBaseVal().getValue()));
+ theCoords.addElement(new Double(0));
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f + x2));
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f - y2 +
+ (matrix.getC() - matrix.getD()) * 2 *
+ ar.getBaseVal().getValue()));
+ theCoords.addElement(
+ new Double(ar.getBaseVal().getValue()));
+ // }
+ }
+ } else if (gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX &&
+ area instanceof GraphicElement) {
+ SVGRect rect = ((GraphicElement) area).getBBox();
+ if (rect != null) {
+ theCoords = new Vector();
+ SVGLength val;
+ val = acx.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = acy.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ theCoords.addElement(new Double(0));
+ val = afx.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = afy.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ val = ar.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE || gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(new Double(val.getValue()));
+ }
+ }
+ }
+ if (theCoords == null) {
+ // percentage values are expressed according to the viewport.
+ SVGElement vp =
+ ((GraphicElement) area).getNearestViewportElement();
+ if (area instanceof GraphicElement) {
+ SVGRect rect = ((GraphicElement) area).getBBox();
+ if (rect != null) {
+ theCoords = new Vector();
+ SVGLength val = acx.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE ||
+ gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = acy.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE ||
+ gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ theCoords.addElement(new Double(0));
+ val = afx.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE ||
+ gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ rect.getX() +
+ val.getValue() * rect.getWidth()));
+ } else {
+ theCoords.addElement(
+ new Double(currentXPosition / 1000f +
+ val.getValue()));
+ }
+ val = afy.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE ||
+ gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ rect.getY() -
+ val.getValue() * rect.getHeight()));
+ } else {
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ val.getValue()));
+ }
+ val = ar.getBaseVal();
+ if (val.getUnitType() ==
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE ||
+ gradUnits ==
+ SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ theCoords.addElement( new Double(val.getValue() *
+ rect.getHeight()));
+ } else {
+ theCoords.addElement(new Double(val.getValue()));
+ }
+ }
+ }
+ }
+ if (theCoords == null) {
+ theCoords = new Vector();
+ theCoords.addElement( new Double(currentXPosition / 1000f +
+ acx.getBaseVal().getValue()));
+ theCoords.addElement( new Double(currentYPosition / 1000f -
+ acy.getBaseVal().getValue()));
+ theCoords.addElement(new Double(0));
+ theCoords.addElement( new Double(currentXPosition / 1000f +
+ afx.getBaseVal().getValue())); // Fx
+ theCoords.addElement(
+ new Double(currentYPosition / 1000f -
+ afy.getBaseVal().getValue())); // Fy
+ theCoords.addElement(
+ new Double(ar.getBaseVal().getValue()));
+ }
+ float lastoffset = 0;
+ for (int count = 0; count < nl.getLength(); count++) {
+ stop = (SVGStopElementImpl) nl.item(count);
+ CSSValue cv = stop.getPresentationAttribute("stop-color");
+ if (cv == null) {
+ // maybe using color
+ cv = stop.getPresentationAttribute("color");
+ }
+ if (cv == null) {
+ // problems
+ System.err.println("no stop-color or color in stop element");
+ continue;
+ }
+ PDFColor color = new PDFColor(0, 0, 0);
+ if (cv != null &&
+ cv.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) cv).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) cv).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ color = new PDFColor(red, green, blue);
+ }
+ }
+ float offset = stop.getOffset().getBaseVal();
+ // create bounds from last to offset
+ lastoffset = offset;
+ someColors.addElement(color);
+ }
+ PDFPattern myPat = this.pdfDoc.createGradient(true, aColorSpace,
+ someColors, theBounds, theCoords);
+
+ currentStream.write(myPat.getColorSpaceOut(fill));
+ if (fill)
+ di.fill = true;
+ else
+ di.stroke = true;
+ }
+
+ /*
+ * This sets up the style for drawing objects.
+ * Should only set style for elements that have changes.
+ *
+ */
+ // need mask drawing
+ class DrawingInstruction {
+ boolean stroke = false;
+ boolean nonzero = false; // non-zero fill rule "f*", "B*" operator
+ boolean fill = false;
+ int linecap = 0; // butt
+ int linejoin = 0; // miter
+ int miterwidth = 8;
+ }
+ protected DrawingInstruction applyStyle(SVGElement area,
+ SVGStylable style) {
+ DrawingInstruction di = new DrawingInstruction();
+ CSSValue sp;
+ sp = style.getPresentationAttribute("fill");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) sp).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ PDFColor fillColour = new PDFColor(red, green, blue);
+ currentColour = fillColour;
+ currentStream.write(fillColour.getColorSpaceOut(true));
+ di.fill = true;
+ } else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_URI) {
+ // gradient
+ String str = ((CSSPrimitiveValue) sp).getCssText();
+ handleGradient(str, di, true, area);
+ } else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ String str = ((CSSPrimitiveValue) sp).getCssText();
+ if (str.equals("none")) {
+ di.fill = false;
+ } else if (str.equals("currentColor")) {
+ currentStream.write(
+ currentColour.getColorSpaceOut(true));
+ di.fill = true;
+ // } else {
+ // handleGradient(str, true, area);
+ }
+ }
+ }
+ } else {
+ PDFColor fillColour = new PDFColor(0, 0, 0);
+ currentStream.write(fillColour.getColorSpaceOut(true));
+ }
+ sp = style.getPresentationAttribute("fill-rule");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ if (sp.getCssText().equals("nonzero")) {
+ di.nonzero = true;
+ }
+ }
+ }
+ } else {
+ }
+ sp = style.getPresentationAttribute("stroke");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_RGBCOLOR) {
+ RGBColor col =
+ ((CSSPrimitiveValue) sp).getRGBColorValue();
+ CSSPrimitiveValue val;
+ val = col.getRed();
+ float red = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getGreen();
+ float green = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ val = col.getBlue();
+ float blue = val.getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER);
+ PDFColor fillColour = new PDFColor(red, green, blue);
+ currentStream.write(
+ fillColour.getColorSpaceOut(false));
+ di.stroke = true;
+ } else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_URI) {
+ // gradient
+ String str = ((CSSPrimitiveValue) sp).getCssText();
+ handleGradient(str, di, false, area);
+ } else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ String str = ((CSSPrimitiveValue) sp).getCssText();
+ if (str.equals("none")) {
+ di.stroke = false;
+ // } else {
+ // handleGradient(str, false, area);
+ }
+ }
+ }
+ } else {
+ PDFColor fillColour = new PDFColor(0, 0, 0);
+ currentStream.write(fillColour.getColorSpaceOut(false));
+ }
+ sp = style.getPresentationAttribute("stroke-linecap");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ String str = sp.getCssText();
+ // butt, round ,square
+ if (str.equals("butt")) {
+ currentStream.write(0 + " J\n");
+ } else if (str.equals("round")) {
+ currentStream.write(1 + " J\n");
+ } else if (str.equals("square")) {
+ currentStream.write(2 + " J\n");
+ }
+ }
+ }
+ } else {
+ }
+ sp = style.getPresentationAttribute("stroke-linejoin");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ String str = sp.getCssText();
+ if (str.equals("miter")) {
+ currentStream.write(0 + " j\n");
+ } else if (str.equals("round")) {
+ currentStream.write(1 + " j\n");
+ } else if (str.equals("bevel")) {
+ currentStream.write(2 + " j\n");
+ }
+ }
+ }
+ } else {
+ }
+ sp = style.getPresentationAttribute("stroke-miterlimit");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ float width;
+ width = ((CSSPrimitiveValue) sp).getFloatValue(
+ CSSPrimitiveValue.CSS_PT);
+ PDFNumber pdfNumber = new PDFNumber();
+ currentStream.write(pdfNumber.doubleOut(width) + " M\n");
+ }
+ } else {
+ }
+ sp = style.getPresentationAttribute("stroke-width");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ float width;
+ width = ((CSSPrimitiveValue) sp).getFloatValue(
+ CSSPrimitiveValue.CSS_PT);
+ PDFNumber pdfNumber = new PDFNumber();
+ currentStream.write(pdfNumber.doubleOut(width) + " w\n");
+ }
+ }
+ sp = style.getPresentationAttribute("stroke-dasharray");
+ if (sp != null) {
+ if (sp.getValueType() == CSSValue.CSS_VALUE_LIST) {
+ currentStream.write("[ ");
+ CSSValueList list = (CSSValueList) sp;
+ for (int count = 0; count < list.getLength(); count++) {
+ CSSValue val = list.item(count);
+ if (val.getValueType() ==
+ CSSValue.CSS_PRIMITIVE_VALUE) {
+ currentStream.write(
+ ((CSSPrimitiveValue) val).getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER) + " ");
+ }
+ }
+ currentStream.write("] ");
+ sp = style.getPresentationAttribute("stroke-offset");
+ if (sp != null && sp.getValueType() ==
+ CSSValue.CSS_PRIMITIVE_VALUE) {
+ currentStream.write(
+ ((CSSPrimitiveValue) sp).getFloatValue(
+ CSSPrimitiveValue.CSS_NUMBER) + " d\n");
+ } else {
+ currentStream.write("0 d\n");
+ }
+ }
+ /* Vector list;
+ list = (Vector)sp;
+ currentStream.add("[ ");
+ for(Enumeration e = list.elements(); e.hasMoreElements(); ) {
+ Integer val = (Integer)e.nextElement();
+ currentStream.add(val.intValue() + " ");
+ }
+ sp = style.getPropertyCSSValue("stroke-offset");
+ if(sp != null) {
+ float width;
+ width = ((SVGLengthImpl)sp).getValue();
+ PDFNumber pdfNumber = new PDFNumber();
+ currentStream.add("] " + pdfNumber.doubleOut(width) + " d\n");
+ } else {
+ currentStream.add("] 0 d\n");
+ }*/
+
+ }
+ sp = style.getPresentationAttribute("mask");
+ if (sp != null) {
+ String maskurl;
+ if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ maskurl = ((CSSPrimitiveValue) sp).getCssText();
+ // System.out.println("mask: " + maskurl);
+ // get def of mask and set mask
+ SVGElement graph = null;
+ graph = locateDef(maskurl, area);
+ if (graph != null) {
+ System.out.println("mask: " + graph);
+ // SVGElement parent = graph.getGraphicParent();
+ // graph.setParent(area);
+ // renderElement(svgarea, graph, posx, posy);
+ // graph.setParent(parent);
+ }
+ }
+ }
+ }
+ return di;
+ }
+
+ protected void applyTransform(SVGAnimatedTransformList trans) {
+ PDFNumber pdfNumber = new PDFNumber();
+ SVGTransformList list = trans.getBaseVal();
+ for (int count = 0; count < list.getNumberOfItems(); count++) {
+ SVGMatrix matrix =
+ ((SVGTransform) list.getItem(count)).getMatrix();
+ currentStream.write(pdfNumber.doubleOut(matrix.getA()) +
+ " " + pdfNumber.doubleOut(matrix.getB()) + " " +
+ pdfNumber.doubleOut(matrix.getC()) + " " +
+ pdfNumber.doubleOut(matrix.getD()) + " " +
+ pdfNumber.doubleOut(matrix.getE()) + " " +
+ pdfNumber.doubleOut(matrix.getF()) + " cm\n");
+ }
+ }
+
+ public void renderElement(SVGElement area, int posx, int posy) {
+ int x = posx;
+ int y = posy;
+ // CSSStyleDeclaration style = null;
+ // if(area instanceof SVGStylable)
+ // style = ((SVGStylable)area).getStyle();
+ DrawingInstruction di = null;
+
+ currentStream.write("q\n");
+ if (area instanceof SVGTransformable) {
+ SVGTransformable tf = (SVGTransformable) area;
+ SVGAnimatedTransformList trans = tf.getTransform();
+ if (trans != null) {
+ applyTransform(trans);
+ }
+ }
+
+ if (area instanceof SVGStylable) {
+ di = applyStyle(area, (SVGStylable) area);
+ }
+
+ if (area instanceof SVGRectElement) {
+ SVGRectElement rg = (SVGRectElement) area;
+ float rectx = rg.getX().getBaseVal().getValue();
+ float recty = rg.getY().getBaseVal().getValue();
+ float rx = rg.getRx().getBaseVal().getValue();
+ float ry = rg.getRy().getBaseVal().getValue();
+ float rw = rg.getWidth().getBaseVal().getValue();
+ float rh = rg.getHeight().getBaseVal().getValue();
+ addRect(rectx, recty, rw, rh, rx, ry, di);
+ } else if (area instanceof SVGLineElement) {
+ SVGLineElement lg = (SVGLineElement) area;
+ float x1 = lg.getX1().getBaseVal().getValue();
+ float y1 = lg.getY1().getBaseVal().getValue();
+ float x2 = lg.getX2().getBaseVal().getValue();
+ float y2 = lg.getY2().getBaseVal().getValue();
+ addLine(x1, y1, x2, y2, di);
+ } else if (area instanceof SVGTextElementImpl) {
+ // currentStream.add("q\n");
+ // currentStream.add(1 + " " + 0 + " " + 0 + " " + 1 + " " + 0 + " " + 0 + " cm\n");
+ currentStream.write("BT\n");
+ renderText((SVGTextElementImpl) area, 0, 0, di);
+ currentStream.write("ET\n");
+ // currentStream.add("Q\n");
+ } else if (area instanceof SVGCircleElement) {
+ SVGCircleElement cg = (SVGCircleElement) area;
+ float cx = cg.getCx().getBaseVal().getValue();
+ float cy = cg.getCy().getBaseVal().getValue();
+ float r = cg.getR().getBaseVal().getValue();
+ addCircle(cx, cy, r, di);
+ } else if (area instanceof SVGEllipseElement) {
+ SVGEllipseElement cg = (SVGEllipseElement) area;
+ float cx = cg.getCx().getBaseVal().getValue();
+ float cy = cg.getCy().getBaseVal().getValue();
+ float rx = cg.getRx().getBaseVal().getValue();
+ float ry = cg.getRy().getBaseVal().getValue();
+ addEllipse(cx, cy, rx, ry, di);
+ } else if (area instanceof SVGPathElementImpl) {
+ addPath(((SVGPathElementImpl) area).pathElements, posx,
+ posy, di);
+ } else if (area instanceof SVGPolylineElementImpl) {
+ addPolyline(((SVGPolylineElementImpl) area).points, di, false);
+ } else if (area instanceof SVGPolygonElementImpl) {
+ addPolyline(((SVGPolygonElementImpl) area).points, di, true);
+ } else if (area instanceof SVGGElementImpl) {
+ renderGArea((SVGGElementImpl) area, x, y);
+ } else if (area instanceof SVGUseElementImpl) {
+ SVGUseElementImpl ug = (SVGUseElementImpl) area;
+ String ref = ug.link;
+ // ref = ref.substring(1, ref.length());
+ SVGElement graph = null;
+ graph = locateDef(ref, ug);
+ if (graph != null) {
+ // probably not the best way to do this, should be able
+ // to render without the style being set.
+ // SVGElement parent = graph.getGraphicParent();
+ // graph.setParent(area);
+ // need to clip (if necessary) to the use area
+ // the style of the linked element is as if it was
+ // a direct descendant of the use element.
+
+ // scale to the viewBox
+
+ if (graph instanceof SVGSymbolElement) {
+ currentStream.write("q\n");
+ SVGSymbolElement symbol = (SVGSymbolElement) graph;
+ SVGRect view = symbol.getViewBox().getBaseVal();
+ float usex = ug.getX().getBaseVal().getValue();
+ float usey = ug.getY().getBaseVal().getValue();
+ float usewidth = ug.getWidth().getBaseVal().getValue();
+ float useheight =
+ ug.getHeight().getBaseVal().getValue();
+ float scaleX;
+ float scaleY;
+ scaleX = usewidth / view.getWidth();
+ scaleY = useheight / view.getHeight();
+ currentStream.write(usex + " " + usey + " m\n");
+ currentStream.write((usex + usewidth) + " " +
+ usey + " l\n");
+ currentStream.write((usex + usewidth) + " " +
+ (usey + useheight) + " l\n");
+ currentStream.write(usex + " " +
+ (usey + useheight) + " l\n");
+ currentStream.write("h\n");
+ currentStream.write("W\n");
+ currentStream.write("n\n");
+ currentStream.write(scaleX + " 0 0 " + scaleY +
+ " " + usex + " " + usey + " cm\n");
+ renderSymbol(symbol, posx, posy);
+ currentStream.write("Q\n");
+ } else {
+ renderElement(graph, posx, posy);
+ }
+ // graph.setParent(parent);
+ }
+ else {
+ MessageHandler.logln("Use Element: " + ref + " not found");
+ }
+ } else if (area instanceof SVGImageElementImpl) {
+ SVGImageElementImpl ig = (SVGImageElementImpl) area;
+ renderImage(ig.link, ig.x, ig.y, ig.width, ig.height);
+ } else if (area instanceof SVGSVGElement) {
+ currentStream.write("q\n");
+ SVGSVGElement svgel = (SVGSVGElement) area;
+ float svgx = 0;
+ if (svgel.getX() != null)
+ svgx = svgel.getX().getBaseVal().getValue();
+ float svgy = 0;
+ if (svgel.getY() != null)
+ svgy = svgel.getY().getBaseVal().getValue();
+ currentStream.write(1 + " 0 0 " + 1 + " " + svgx + " " +
+ svgy + " cm\n");
+ renderSVG(svgel, (int)(x + 1000 * svgx),
+ (int)(y + 1000 * svgy));
+ currentStream.write("Q\n");
+ // } else if (area instanceof SVGSymbolElement) {
+ // 'symbol' element is not rendered (except by 'use')
+ } else if (area instanceof SVGAElement) {
+ SVGAElement ael = (SVGAElement) area;
+ org.w3c.dom.NodeList nl = ael.getChildNodes();
+ for (int count = 0; count < nl.getLength(); count++) {
+ org.w3c.dom.Node n = nl.item(count);
+ if (n instanceof SVGElement) {
+ if (n instanceof GraphicElement) {
+ SVGRect rect = ((GraphicElement) n).getBBox();
+ if (rect != null) {
+ /* currentAnnotList = this.pdfDoc.makeAnnotList();
+ currentPage.setAnnotList(currentAnnotList);
+ String dest = linkSet.getDest();
+ int linkType = linkSet.getLinkType();
+ currentAnnotList.addLink(
+ this.pdfDoc.makeLink(lrect.getRectangle(), dest, linkType));
+ currentAnnotList = null;
+ */ }
+ }
+ renderElement((SVGElement) n, posx, posy);
+ }
+ }
+ } else if (area instanceof SVGSwitchElement) {
+ handleSwitchElement(posx, posy, (SVGSwitchElement) area);
+ }
+ // should be done with some cleanup code, so only
+ // required values are reset.
+ currentStream.write("Q\n");
+ }
+
+ /**
+ * Todo: underline, linethrough, textpath, tref
+ */
+ public void renderText(SVGTextElementImpl tg, float x, float y,
+ DrawingInstruction di) {
+ SVGTextRenderer str = new SVGTextRenderer(fontState, tg, x, y);
+ if (di.fill) {
+ if (di.stroke) {
+ currentStream.write("2 Tr\n");
+ } else {
+ currentStream.write("0 Tr\n");
+ }
+ } else if (di.stroke) {
+ currentStream.write("1 Tr\n");
+ }
+ str.renderText(tg);
+ }
+
+ /**
+ * Adds an svg string to the output.
+ * This handles the escaping of special pdf chars and deals with
+ * whitespace.
+ */
+ protected float addSVGStr(FontState fs, float currentX, String str,
+ boolean spacing) {
+ boolean inbetween = false;
+ boolean addedspace = false;
+ StringBuffer pdf = new StringBuffer();
+ for (int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ if (ch > 127) {
+ pdf = pdf.append("\\");
+ pdf = pdf.append(Integer.toOctalString((int) ch));
+ currentX += fs.width(ch) / 1000f;
+ inbetween = true;
+ addedspace = false;
+ } else {
+ switch (ch) {
+ case '(' :
+ pdf = pdf.append("\\(");
+ currentX += fs.width(ch) / 1000f;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case ')' :
+ pdf = pdf.append("\\)");
+ currentX += fs.width(ch) / 1000f;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case '\\' :
+ pdf = pdf.append("\\\\");
+ currentX += fs.width(ch) / 1000f;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case ' ':
+ case ' ':
+ if (spacing) {
+ pdf = pdf.append(' ');
+ currentX += fs.width(' ') / 1000f;
+ } else {
+ if (inbetween && !addedspace) {
+ addedspace = true;
+ pdf = pdf.append(' ');
+ currentX += fs.width(' ') / 1000f;
+ }
+ }
+ break;
+ case '\n':
+ case '\r':
+ if (spacing) {
+ pdf = pdf.append(' ');
+ currentX += fs.width(' ') / 1000f;
+ }
+ break;
+ default:
+ addedspace = false;
+ pdf = pdf.append(ch);
+ currentX += fs.width(ch) / 1000f;
+ inbetween = true;
+ break;
+ }
+ }
+ }
+ currentStream.write(pdf.toString());
+ return currentX;
+ }
+
+ /**
+ * Locates a defined element in an svg document.
+ * Either gets the element defined by its "id" in the current
+ * SVGDocument, or if the uri reference is to an external
+ * document it loads the document and returns the element.
+ */
+ protected SVGElement locateDef(String ref, SVGElement currentElement) {
+ int pos;
+ ref = ref.trim();
+ pos = ref.indexOf("#");
+ if (pos == 0) {
+ // local doc
+ Document doc = currentElement.getOwnerDocument();
+ Element ele =
+ doc.getElementById(ref.substring(1, ref.length()));
+ if (ele instanceof SVGElement) {
+ return (SVGElement) ele;
+ }
+ } else if (pos != -1) {
+ String href = ref.substring(0, pos);
+ if (href.indexOf(":") == -1) {
+ href = "file:" + href;
+ }
+ try {
+ // this is really only to get a cached svg image
+ FopImage img = FopImageFactory.Make(href);
+ if (img instanceof SVGImage) {
+ SVGDocument doc = ((SVGImage) img).getSVGDocument();
+ Element ele = doc.getElementById(
+ ref.substring(pos + 1, ref.length()));
+ if (ele instanceof SVGElement) {
+ return (SVGElement) ele;
+ }
+ }
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This class is used to handle the rendering of svg text.
+ * This is so that it can deal with the recursive rendering
+ * of text markup, while keeping track of the state and position.
+ */
+ class SVGTextRenderer {
+ FontState fs;
+ String transstr;
+ float currentX;
+ float currentY;
+ float baseX;
+ float baseY;
+ SVGMatrix matrix;
+ float x;
+ float y;
+
+ SVGTextRenderer(FontState fontState, SVGTextElementImpl tg,
+ float x, float y) {
+ fs = fontState;
+
+ PDFNumber pdfNumber = new PDFNumber();
+ SVGTransformList trans = tg.getTransform().getBaseVal();
+ matrix = trans.consolidate().getMatrix();
+ transstr = (pdfNumber.doubleOut(matrix.getA()) + " " +
+ pdfNumber.doubleOut(matrix.getB()) + " " +
+ pdfNumber.doubleOut(matrix.getC()) + " " +
+ pdfNumber.doubleOut(-matrix.getD()) + " ");
+ this.x = x;
+ this.y = y;
+ }
+
+ void renderText(SVGTextElementImpl te) {
+ DrawingInstruction di = applyStyle(te, te);
+ if (di.fill) {
+ if (di.stroke) {
+ currentStream.write("2 Tr\n");
+ } else {
+ currentStream.write("0 Tr\n");
+ }
+ } else if (di.stroke) {
+ currentStream.write("1 Tr\n");
+ }
+ updateFont(te, fs);
+
+ float tx = te.x;
+ float ty = te.y;
+ currentX = x + tx;
+ currentY = y + ty;
+ baseX = currentX;
+ baseY = currentY;
+ NodeList nodel = te.getChildNodes();
+ // Vector list = te.textList;
+ for (int count = 0; count < nodel.getLength(); count++) {
+ Object o = nodel.item(count);
+ applyStyle(te, te);
+ if (o instanceof CharacterData) {
+ String str = ((CharacterData) o).getData();
+ currentStream.write(transstr +
+ (currentX + matrix.getE()) + " " +
+ (baseY + matrix.getF()) + " Tm " + "(");
+ boolean spacing = "preserve".equals(te.getXMLspace());
+ currentX = addSVGStr(fs, currentX, str, spacing);
+ currentStream.write(") Tj\n");
+ } else if (o instanceof SVGTextPathElementImpl) {
+ SVGTextPathElementImpl tpg = (SVGTextPathElementImpl) o;
+ String ref = tpg.str;
+ SVGElement graph = null;
+ graph = locateDef(ref, tpg);
+ if (graph instanceof SVGPathElementImpl) {
+ // probably not the best way to do this, should be able
+ // to render without the style being set.
+ // GraphicImpl parent = graph.getGraphicParent();
+ // graph.setParent(tpg);
+ // set text path??
+ // how should this work
+ // graph.setParent(parent);
+ }
+ } else if (o instanceof SVGTRefElementImpl) {
+ SVGTRefElementImpl trg = (SVGTRefElementImpl) o;
+ String ref = trg.ref;
+ SVGElement element = locateDef(ref, trg);
+ if (element instanceof SVGTextElementImpl) {
+ // GraphicImpl parent = graph.getGraphicParent();
+ // graph.setParent(trg);
+ SVGTextElementImpl tele =
+ (SVGTextElementImpl) element;
+ // the style should be from tele, but it needs to be placed as a child
+ // of trg to work
+ di = applyStyle(trg, trg);
+ if (di.fill) {
+ if (di.stroke) {
+ currentStream.write("2 Tr\n");
+ } else {
+ currentStream.write("0 Tr\n");
+ }
+ } else if (di.stroke) {
+ currentStream.write("1 Tr\n");
+ }
+ boolean changed = false;
+ FontState oldfs = fs;
+ changed = updateFont(te, fs);
+ NodeList nl = tele.getChildNodes();
+ boolean spacing =
+ "preserve".equals(trg.getXMLspace());
+ renderTextNodes(spacing, nl,
+ trg.getX().getBaseVal(),
+ trg.getY().getBaseVal(),
+ trg.getDx().getBaseVal(),
+ trg.getDy().getBaseVal());
+
+ if (changed) {
+ fs = oldfs;
+ currentStream.write("/" +
+ fs.getFontName() + " " +
+ fs.getFontSize() / 1000f + " Tf\n");
+ }
+ // graph.setParent(parent);
+ }
+ } else if (o instanceof SVGTSpanElementImpl) {
+ SVGTSpanElementImpl tsg = (SVGTSpanElementImpl) o;
+ applyStyle(tsg, tsg);
+ boolean changed = false;
+ FontState oldfs = fs;
+ changed = updateFont(tsg, fs);
+ boolean spacing = "preserve".equals(tsg.getXMLspace());
+ renderTextNodes(spacing, tsg.getChildNodes(),
+ tsg.getX().getBaseVal(),
+ tsg.getY().getBaseVal(),
+ tsg.getDx().getBaseVal(),
+ tsg.getDy().getBaseVal());
+
+ // currentX += fs.width(' ') / 1000f;
+ if (changed) {
+ fs = oldfs;
+ currentStream.write("/" + fs.getFontName() +
+ " " + fs.getFontSize() / 1000f + " Tf\n");
+ }
+ } else {
+ System.err.println("Error: unknown text element " + o);
+ }
+ }
+ }
+
+ void renderTextNodes(boolean spacing, NodeList nl,
+ SVGLengthList xlist, SVGLengthList ylist,
+ SVGLengthList dxlist, SVGLengthList dylist) {
+ boolean inbetween = false;
+ boolean addedspace = false;
+ int charPos = 0;
+ float xpos = currentX;
+ float ypos = currentY;
+
+ for (int count = 0; count < nl.getLength(); count++) {
+ Node n = nl.item(count);
+ if (n instanceof CharacterData) {
+ StringBuffer pdf = new StringBuffer();
+ String str = ((CharacterData) n).getData();
+ for (int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ xpos = currentX;
+ ypos = currentY;
+ if (ylist.getNumberOfItems() > charPos) {
+ ypos = baseY + ((Float) ylist.getItem(charPos)).
+ floatValue();
+ }
+ if (dylist.getNumberOfItems() > charPos) {
+ ypos = ypos + ((Float) dylist.getItem(charPos)).
+ floatValue();
+ }
+ if (xlist.getNumberOfItems() > charPos) {
+ xpos = baseX + ((Float) xlist.getItem(charPos)).
+ floatValue();
+ }
+ if (dxlist.getNumberOfItems() > charPos) {
+ xpos = xpos + ((Float) dxlist.getItem(charPos)).
+ floatValue();
+ }
+ if (ch > 127) {
+ pdf = pdf.append(transstr +
+ (xpos + matrix.getE()) + " " +
+ (ypos + matrix.getF()) + " Tm " +
+ "(" + "\\" +
+ Integer.toOctalString((int) ch) +
+ ") Tj\n");
+ currentX = xpos + fs.width(ch) / 1000f;
+ currentY = ypos;
+ charPos++;
+ inbetween = true;
+ addedspace = false;
+ } else {
+ switch (ch) {
+ case '(' :
+ pdf = pdf.append(transstr +
+ (xpos + matrix.getE()) +
+ " " + (ypos +
+ matrix.getF()) + " Tm " +
+ "(" + "\\(" + ") Tj\n");
+ currentX = xpos + fs.width(ch) / 1000f;
+ currentY = ypos;
+ charPos++;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case ')' :
+ pdf = pdf.append(transstr +
+ (xpos + matrix.getE()) +
+ " " + (ypos +
+ matrix.getF()) + " Tm " +
+ "(" + "\\)" + ") Tj\n");
+ currentX = xpos + fs.width(ch) / 1000f;
+ currentY = ypos;
+ charPos++;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case '\\' :
+ pdf = pdf.append(transstr +
+ (xpos + matrix.getE()) +
+ " " + (ypos +
+ matrix.getF()) + " Tm " +
+ "(" + "\\\\" + ") Tj\n");
+ currentX = xpos + fs.width(ch) / 1000f;
+ currentY = ypos;
+ charPos++;
+ inbetween = true;
+ addedspace = false;
+ break;
+ case ' ':
+ case ' ':
+ if (spacing) {
+ currentX = xpos + fs.width(' ') /
+ 1000f;
+ currentY = ypos;
+ charPos++;
+ } else {
+ if (inbetween && !addedspace) {
+ addedspace = true;
+ currentX = xpos + fs.width(' ')
+ / 1000f;
+ currentY = ypos;
+ charPos++;
+ }
+ }
+ break;
+ case '\n':
+ case '\r':
+ if (spacing) {
+ currentX = xpos + fs.width(' ') /
+ 1000f;
+ currentY = ypos;
+ charPos++;
+ }
+ break;
+ default:
+ addedspace = false;
+ pdf = pdf.append(transstr +
+ (xpos + matrix.getE()) +
+ " " + (ypos +
+ matrix.getF()) + " Tm " +
+ "(" + ch + ") Tj\n");
+ currentX = xpos + fs.width(ch) / 1000f;
+ currentY = ypos;
+ charPos++;
+ inbetween = true;
+ break;
+ }
+ }
+ currentStream.write(pdf.toString());
+ }
+ }
+ }
+ }
+
+ protected boolean updateFont(SVGStylable style, FontState fs) {
+ boolean changed = false;
+ String fontFamily = fs.getFontFamily();
+ CSSValue sp = style.getPresentationAttribute("font-family");
+ if (sp != null &&
+ sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ fontFamily = sp.getCssText();
+ }
+ }
+ if (!fontFamily.equals(fs.getFontFamily())) {
+ changed = true;
+ }
+ String fontStyle = fs.getFontStyle();
+ sp = style.getPresentationAttribute("font-style");
+ if (sp != null &&
+ sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ fontStyle = sp.getCssText();
+ }
+ }
+ if (!fontStyle.equals(fs.getFontStyle())) {
+ changed = true;
+ }
+ String fontWeight = fs.getFontWeight();
+ sp = style.getPresentationAttribute("font-weight");
+ if (sp != null &&
+ sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
+ CSSPrimitiveValue.CSS_STRING) {
+ fontWeight = sp.getCssText();
+ }
+ }
+ if (!fontWeight.equals(fs.getFontWeight())) {
+ changed = true;
+ }
+ float newSize = fs.getFontSize() / 1000f;
+ sp = style.getPresentationAttribute("font-size");
+ if (sp != null &&
+ sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
+ // if(((CSSPrimitiveValue)sp).getPrimitiveType() == CSSPrimitiveValue.CSS_NUMBER) {
+ newSize = ((CSSPrimitiveValue) sp).getFloatValue(
+ CSSPrimitiveValue.CSS_PT);
+ // }
+ }
+ if (fs.getFontSize() / 1000f != newSize) {
+ changed = true;
+ }
+ if (changed) {
+ try {
+ fs = new FontState(fs.getFontInfo(), fontFamily,
+ fontStyle, fontWeight, (int)(newSize * 1000));
+ } catch (Exception fope) {
+ }
+ this.fs = fs;
+
+ currentStream.write("/" + fs.getFontName() + " " +
+ newSize + " Tf\n");
+ } else {
+ if (!currentFontName.equals(fs.getFontName()) ||
+ currentFontSize != fs.getFontSize()) {
+ // currentFontName = fs.getFontName();
+ // currentFontSize = fs.getFontSize();
+ currentStream.write("/" + fs.getFontName() + " " +
+ (fs.getFontSize() / 1000) + " Tf\n");
+ }
+ }
+ return changed;
+ }
+ }
+}