/*-- $Id: SVGRenderer.java,v 1.2 2000/11/14 05:14:03 keiron Exp $ --
============================================================================
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;
PDFNumber pdfNumber = new PDFNumber();
str = "" + pdfNumber.doubleOut(x1) + " " + pdfNumber.doubleOut(y1) + " m " + pdfNumber.doubleOut(x2) + " " + pdfNumber.doubleOut(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) {
PDFNumber pdfNumber = new PDFNumber();
String str;
str = "" + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut((cy - r)) + " m\n" + "" +
pdfNumber.doubleOut((cx + 21 * r / 40f)) + " " + pdfNumber.doubleOut((cy - r)) + " " +
pdfNumber.doubleOut((cx + r)) + " " + pdfNumber.doubleOut((cy - 21 * r / 40f)) + " " +
pdfNumber.doubleOut((cx + r)) + " " + pdfNumber.doubleOut(cy) + " c\n" + "" + pdfNumber.doubleOut((cx + r)) + " " +
pdfNumber.doubleOut((cy + 21 * r / 40f)) + " " + pdfNumber.doubleOut((cx + 21 * r / 40f)) +
" " + pdfNumber.doubleOut((cy + r)) + " " + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut((cy + r)) + " c\n" +
"" + pdfNumber.doubleOut((cx - 21 * r / 40f)) + " " + pdfNumber.doubleOut((cy + r)) + " " +
pdfNumber.doubleOut(cx - r) + " " + pdfNumber.doubleOut(cy + 21 * r / 40f) + " " +
pdfNumber.doubleOut(cx - r) + " " + pdfNumber.doubleOut(cy) + " c\n" + "" + pdfNumber.doubleOut(cx - r) + " " +
pdfNumber.doubleOut(cy - 21 * r / 40f) + " " + pdfNumber.doubleOut(cx - 21 * r / 40f) +
" " + pdfNumber.doubleOut(cy - r) + " " + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut(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) {
PDFNumber pdfNumber = new PDFNumber();
String str;
str = "" + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut(cy - ry) + " m\n" + "" +
pdfNumber.doubleOut(cx + 21 * rx / 40f) + " " + pdfNumber.doubleOut(cy - ry) + " " +
pdfNumber.doubleOut(cx + rx) + " " + pdfNumber.doubleOut(cy - 21 * ry / 40f) + " " +
pdfNumber.doubleOut(cx + rx) + " " + pdfNumber.doubleOut(cy) + " c\n" + "" + pdfNumber.doubleOut(cx + rx) + " " +
pdfNumber.doubleOut(cy + 21 * ry / 40f) + " " + pdfNumber.doubleOut(cx + 21 * rx / 40f) +
" " + pdfNumber.doubleOut(cy + ry) + " " + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut(cy + ry) +
" c\n" + "" + pdfNumber.doubleOut(cx - 21 * rx / 40f) + " " + pdfNumber.doubleOut(cy + ry) +
" " + pdfNumber.doubleOut(cx - rx) + " " + pdfNumber.doubleOut(cy + 21 * ry / 40f) + " " +
pdfNumber.doubleOut(cx - rx) + " " + pdfNumber.doubleOut(cy) + " c\n" + "" + pdfNumber.doubleOut(cx - rx) + " " +
pdfNumber.doubleOut(cy - 21 * ry / 40f) + " " + pdfNumber.doubleOut(cx - 21 * rx / 40f) +
" " + pdfNumber.doubleOut(cy - ry) + " " + pdfNumber.doubleOut(cx) + " " + pdfNumber.doubleOut(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) {
PDFNumber pdfNumber = new PDFNumber();
String str = "";
if (rx == 0.0 && ry == 0.0) {
str = "" + pdfNumber.doubleOut(x) + " " + pdfNumber.doubleOut(y) + " " + pdfNumber.doubleOut(w) + " " + pdfNumber.doubleOut(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 = "" + pdfNumber.doubleOut(x + rx) + " " + pdfNumber.doubleOut(y) + " m\n";
str += "" + pdfNumber.doubleOut(x + w - rx) + " " + pdfNumber.doubleOut(y) + " l\n";
str += "" + pdfNumber.doubleOut(x + w - 19 * rx / 40) + " " + pdfNumber.doubleOut(y) + " " +
pdfNumber.doubleOut(x + w) + " " + pdfNumber.doubleOut(y + 19 * ry / 40) + " " +
pdfNumber.doubleOut(x + w) + " " + pdfNumber.doubleOut(y + ry) + " c\n";
str += "" + pdfNumber.doubleOut(x + w) + " " + pdfNumber.doubleOut(y + h - ry) + " l\n";
str += "" + pdfNumber.doubleOut(x + w) + " " + pdfNumber.doubleOut(y + h - 19 * ry / 40) + " " +
pdfNumber.doubleOut(x + w - 19 * rx / 40) + " " + pdfNumber.doubleOut(y + h) + " " +
pdfNumber.doubleOut(x + w - rx) + " " + pdfNumber.doubleOut(y + h) + " c\n";
str += "" + pdfNumber.doubleOut(x + rx) + " " + pdfNumber.doubleOut(y + h) + " l\n";
str += "" + pdfNumber.doubleOut(x + 19 * rx / 40) + " " + pdfNumber.doubleOut(y + h) + " " + pdfNumber.doubleOut(x) +
" " + pdfNumber.doubleOut(y + h - 19 * ry / 40) + " " + pdfNumber.doubleOut(x) + " " +
pdfNumber.doubleOut(y + h - ry) + " c\n";
str += "" + pdfNumber.doubleOut(x) + " " + pdfNumber.doubleOut(y + ry) + " l\n";
str += "" + pdfNumber.doubleOut(x) + " " + pdfNumber.doubleOut(y + 19 * ry / 40) + " " +
pdfNumber.doubleOut(x + 19 * rx / 40) + " " + pdfNumber.doubleOut(y) + " " + pdfNumber.doubleOut(x + rx) +
" " + pdfNumber.doubleOut(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) {
PDFNumber pdfNumber = new PDFNumber();
SVGPathSegImpl pathmoveto = null;
float lastx = 0;
float lasty = 0;
float lastmovex = 0;
float lastmovey = 0;
float[] cxs;
float tempx;
float tempy;
float lastcx = 0;
float lastcy = 0;
for (Enumeration e = points.elements(); e.hasMoreElements();) {
SVGPathSegImpl pc = (SVGPathSegImpl) e.nextElement();
float[] vals = pc.getValues();
switch (pc.getPathSegType()) {
case SVGPathSeg.PATHSEG_MOVETO_ABS:
pathmoveto = pc;
lastx = vals[0];
lasty = vals[1];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " m\n");
lastcx = 0;
lastcy = 0;
lastmovex = lastx;
lastmovey = lasty;
break;
case SVGPathSeg.PATHSEG_MOVETO_REL:
// the test cases seem to interprete this command differently
// it seems if there is an 'm' then the current path is closed
// then the point is move to a place relative to the point
// after doing the close
if (pathmoveto == null) {
lastx += vals[0];
lasty += vals[1];
pathmoveto = pc;
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " m\n");
} else {
lastx += vals[0];
lasty += vals[1];
pathmoveto = pc;
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
}
lastmovex = lastx;
lastmovey = lasty;
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_ABS:
lastx = vals[0];
lasty = vals[1];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_REL:
lastx += vals[0];
lasty += vals[1];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
lasty = vals[0];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
lasty += vals[0];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
lastx = vals[0];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
lastx += vals[0];
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " l\n");
lastcx = 0;
lastcy = 0;
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
lastx = vals[4];
lasty = vals[5];
lastcx = vals[2];
lastcy = vals[3];
currentStream.write(pdfNumber.doubleOut(vals[0]) + " " + pdfNumber.doubleOut(vals[1]) +
" " + pdfNumber.doubleOut(vals[2]) + " " + pdfNumber.doubleOut(vals[3]) + " " +
pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " c\n");
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
currentStream.write(pdfNumber.doubleOut(vals[0] + lastx) + " " +
pdfNumber.doubleOut(vals[1] + lasty) + " " +
pdfNumber.doubleOut(vals[2] + lastx) + " " +
pdfNumber.doubleOut(vals[3] + lasty) + " " +
pdfNumber.doubleOut(vals[4] + lastx) + " " +
pdfNumber.doubleOut(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;
}
lastcx = lastx + (lastx - lastcx);
lastcy = lasty + (lasty - lastcy);
lastx = vals[2];
lasty = vals[3];
currentStream.write(pdfNumber.doubleOut(lastcx) + " " + pdfNumber.doubleOut(lastcy) + " " +
pdfNumber.doubleOut(vals[0]) + " " + pdfNumber.doubleOut(vals[1]) + " " +
pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " c\n");
lastcx = vals[0];
lastcy = vals[1];
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
if (lastcx == 0) {
lastcx = lastx;
}
if (lastcy == 0) {
lastcy = lasty;
}
lastcx = lastx + (lastx - lastcx);
lastcy = lasty + (lasty - lastcy);
currentStream.write(pdfNumber.doubleOut(lastcx) + " " + pdfNumber.doubleOut(lastcy) + " " +
pdfNumber.doubleOut(vals[0] + lastx) + " " +
pdfNumber.doubleOut(vals[1] + lasty) + " " +
pdfNumber.doubleOut(vals[2] + lastx) + " " +
pdfNumber.doubleOut(vals[3] + lasty) + " c\n");
lastcx = (vals[0] + lastx);
lastcy = (vals[1] + lasty);
lastx += vals[2];
lasty += vals[3];
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
if (lastcx == 0) {
lastcx = lastx;
}
if (lastcy == 0) {
lastcy = lasty;
}
tempx = lastx;
tempy = lasty;
lastx = vals[2];
lasty = vals[3];
currentStream.write(pdfNumber.doubleOut(vals[0]) + " " + pdfNumber.doubleOut(vals[1]) + " " +
pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " y\n");
cxs = calculateLastControl(tempx, tempy, lastx, lasty, -tempx + vals[0], -tempy + vals[1]);
lastcx = cxs[0];
lastcy = cxs[1];
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
if (lastcx == 0) {
lastcx = lastx;
}
if (lastcy == 0) {
lastcy = lasty;
}
currentStream.write(pdfNumber.doubleOut(vals[0] + lastx) + " " + pdfNumber.doubleOut(vals[1] + lasty) + " " +
pdfNumber.doubleOut(vals[2] + lastx) + " " +
pdfNumber.doubleOut(vals[3] + lasty) + " y\n");
cxs = calculateLastControl(lastx, lasty, lastx + vals[2], lasty + vals[3], vals[0], vals[1]);
lastcx = cxs[0];
lastcy = cxs[1];
lastx += vals[2];
lasty += vals[3];
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
if (lastcx == 0) {
lastcx = lastx;
}
if (lastcy == 0) {
lastcy = lasty;
}
tempx = lastx;
tempy = lasty;
lastcx = lastx + (lastx - lastcx);
lastcy = lasty + (lasty - lastcy);
lastx = vals[0];
lasty = vals[1];
currentStream.write(pdfNumber.doubleOut(lastcx) + " " + pdfNumber.doubleOut(lastcy) + " " +
pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " y\n");
cxs = calculateLastControl(tempx, tempy, lastx, lasty, -tempx + lastcx, -tempy + lastcy);
lastcx = cxs[0];
lastcy = cxs[1];
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
if (lastcx == 0) {
lastcx = lastx;
}
if (lastcy == 0) {
lastcy = lasty;
}
lastcx = lastx + (lastx - lastcx);
lastcy = lasty + (lasty - lastcy);
currentStream.write(pdfNumber.doubleOut(lastcx) + " " + pdfNumber.doubleOut(lastcy) + " " +
pdfNumber.doubleOut(vals[0] + lastx) + " " +
pdfNumber.doubleOut(vals[1] + lasty) + " y\n");
cxs = calculateLastControl(lastx, lasty, lastx + vals[0], lasty + vals[1], -lastx + lastcx, -lasty + lastcy);
lastcx = cxs[0];
lastcy = cxs[1];
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(pdfNumber.doubleOut(contrx1) + " " + pdfNumber.doubleOut(contry1) +
" " + pdfNumber.doubleOut(contrx2) + " " + pdfNumber.doubleOut(contry2) + " " +
pdfNumber.doubleOut(vals[5]) + " " + pdfNumber.doubleOut(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(pdfNumber.doubleOut(contrx1) + " " + pdfNumber.doubleOut(contry1) +
" " + pdfNumber.doubleOut(contrx2) + " " + pdfNumber.doubleOut(contry2) + " " +
pdfNumber.doubleOut(vals[5] + lastx) + " " +
pdfNumber.doubleOut(vals[6] + lasty) + " c\n");
lastcx = 0; //??
lastcy = 0; //??
lastx += vals[5];
lasty += vals[6];*/
}
break;
case SVGPathSeg.PATHSEG_CLOSEPATH:
currentStream.write("h\n");
pathmoveto = null;
lastx = lastmovex;
lasty = lastmovey;
break;
}
}
doDrawing(di);
}
protected float[] calculateLastControl(float x1, float y1, float x2, float y2, float relx, float rely)
{
float vals[] = new float[2];
relx = -relx;
rely = -rely;
float dist = (float)Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
float costheta = (float)(x2 - x1) / dist;
float sinetheta = (float)(y2 - y1) / dist;
float temp = relx;
relx = relx * costheta + rely * sinetheta;
rely = -temp * sinetheta + rely * costheta;
relx = -relx;
temp = relx;
relx = relx * costheta - rely * sinetheta;
rely = temp * sinetheta + rely * costheta;
vals[0] = x2 - relx;
vals[1] = y2 - rely;
return vals;
}
/**
* 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) {
PDFNumber pdfNumber = new PDFNumber();
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(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(lasty) + " m\n");
}
while (e.hasMoreElements()) {
pc = (PathPoint) e.nextElement();
lastx = pc.x;
lasty = pc.y;
currentStream.write(pdfNumber.doubleOut(lastx) + " " + pdfNumber.doubleOut(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);
PDFNumber pdfNumber = new PDFNumber();
if (img instanceof SVGImage) {
SVGSVGElement svg =
((SVGImage) img).getSVGDocument().getRootElement();
currentStream.write("q\n" + pdfNumber.doubleOut(width /
svg.getWidth().getBaseVal().getValue()) + " 0 0 " +
pdfNumber.doubleOut(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 " +
pdfNumber.doubleOut(2 * y + height) + " cm\n" + pdfNumber.doubleOut(width) + " 0 0 " +
pdfNumber.doubleOut(height) + " " + pdfNumber.doubleOut(x) + " " + pdfNumber.doubleOut(y) + " cm\n" + "/Im" +
xObjectNum + " Do\nQ\n");
// img.close();
}
} catch (Exception e) {
MessageHandler.errorln("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 {
MessageHandler.errorln("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
MessageHandler.errorln("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
MessageHandler.errorln("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
MessageHandler.errorln("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
MessageHandler.errorln("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-dashoffset");
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");
}
}
}
sp = style.getPresentationAttribute("clip-path");
if (sp != null) {
String clipurl;
if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
CSSPrimitiveValue.CSS_URI) {
clipurl = ((CSSPrimitiveValue) sp).getCssText();
if (clipurl.startsWith("url(")) {
int b1 = clipurl.indexOf("(");
int b2 = clipurl.indexOf(")");
clipurl = clipurl.substring(b1 + 1, b2);
}
// get def of mask and set mask
SVGElement graph = null;
graph = locateDef(clipurl, area);
if (graph != null) {
MessageHandler.logln("clip path: " + graph);
// render the clip path elements and make it the clip
// renderElement(svgarea, graph, posx, posy);
}
}
}
}
sp = style.getPresentationAttribute("mask");
if (sp != null) {
String maskurl;
if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
if (((CSSPrimitiveValue) sp).getPrimitiveType() ==
CSSPrimitiveValue.CSS_URI) {
maskurl = ((CSSPrimitiveValue) sp).getCssText();
// System.out.println("mask: " + maskurl);
// get def of mask and set mask
if (maskurl.startsWith("url(")) {
int b1 = maskurl.indexOf("(");
int b2 = maskurl.indexOf(")");
maskurl = maskurl.substring(b1 + 1, b2);
}
SVGElement graph = null;
graph = locateDef(maskurl, area);
if (graph != null) {
MessageHandler.logln("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");
}
}
/**
* Main rendering selection.
* This applies any transform and style and then calls the appropriate
* rendering method depending on the type of element.
*/
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
*/
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) {
MessageHandler.errorln(e.toString());
}
}
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 {
MessageHandler.errorln("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;
}
}
}