// Copyright 2000-2003 FreeHEP
package org.freehep.graphicsio.svg;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.renderable.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import org.freehep.graphics2d.TagString;
import org.freehep.graphicsio.AbstractVectorGraphicsIO;
import org.freehep.graphicsio.PageConstants;
import org.freehep.graphicsio.InfoConstants;
import org.freehep.graphicsio.ImageConstants;
import org.freehep.graphicsio.ImageGraphics2D;
import org.freehep.graphicsio.font.FontUtilities;
import org.freehep.util.UserProperties;
import org.freehep.util.Value;
import org.freehep.util.io.Base64OutputStream;
import org.freehep.util.io.WriterOutputStream;
import org.freehep.xml.util.XMLWriter;
/**
* This class implements the Scalable Vector Graphics output.
* SVG specifications can be found at http://www.w3c.org/Graphics/SVG/
*
* The current implementation is based on REC-SVG-20010904
* but can generate also files for the older specs CR-SVG-20000802, WD-SVG-20000303
*
* @author Mark Donszelmann
* @version $Id: SVGGraphics2D.java,v 1.16 2003/05/27 22:35:47 duns Exp $
*/
public class SVGGraphics2D
extends AbstractVectorGraphicsIO {
public static final String VERSION_1_0 = "Version 1.0 (REC-SVG-20010904)";
public static final String VERSION_1_1 = "Version 1.1 (REC-SVG11-20030114)";
private static final String rootKey = SVGGraphics2D.class.getName();
public static final String TRANSPARENT = rootKey+"."+PageConstants.TRANSPARENT;
public static final String BACKGROUND = rootKey+"."+PageConstants.BACKGROUND;
public static final String BACKGROUND_COLOR = rootKey+"."+PageConstants.BACKGROUND_COLOR;
public static final String VERSION = rootKey+".Version";
public static final String COMPRESS = rootKey+".Binary";
public static final String STYLABLE = rootKey+".Stylable";
public static final String IMAGE_SIZE = rootKey+"."+ImageConstants.IMAGE_SIZE;
public static final String EXPORT_IMAGES = rootKey+".ExportImages";
public static final String EXPORT_SUFFIX = rootKey+".ExportSuffix";
public static final String WRITE_IMAGES_AS = rootKey+"."+ImageConstants.WRITE_IMAGES_AS;
public static final String FOR = rootKey+"."+InfoConstants.FOR;
public static final String TITLE = rootKey+"."+InfoConstants.TITLE;
private static final UserProperties defaultProperties = new UserProperties();
static {
defaultProperties.setProperty(TRANSPARENT, true);
defaultProperties.setProperty(BACKGROUND, false);
defaultProperties.setProperty(BACKGROUND_COLOR, Color.GRAY);
defaultProperties.setProperty(VERSION, VERSION_1_0);
defaultProperties.setProperty(COMPRESS, true);
defaultProperties.setProperty(STYLABLE, true);
defaultProperties.setProperty(IMAGE_SIZE, new Dimension(0, 0)); // ImageSize
defaultProperties.setProperty(EXPORT_IMAGES, false);
defaultProperties.setProperty(EXPORT_SUFFIX, "image");
defaultProperties.setProperty(WRITE_IMAGES_AS, ImageConstants.SMALLEST);
defaultProperties.setProperty(FOR, "");
defaultProperties.setProperty(TITLE, "");
}
public static Properties getDefaultProperties() {
return defaultProperties;
}
public static void setDefaultProperties(Properties newProperties) {
defaultProperties.setProperties(newProperties);
}
public static final String version = "$Revision: 1.16 $";
// shift to make draw routines draw in the middle
private static final double bias = 0.5;
// current filename including path
private String filename;
// The lowerleft and upper right points of the bounding box.
private int bbx, bby, bbw, bbh;
// The private writer used for this file.
private OutputStream ros;
private PrintWriter os;
// table for gradients
Hashtable gradients = new Hashtable();
// table for textures
Hashtable textures = new Hashtable();
private Stack closeTags = new Stack();
private int imageNumber = 0;
private Value clipNumber;
private int currentClipNumber;
private int width, height;
/*================================================================================
| 1. Constructors & Factory Methods
*================================================================================*/
public SVGGraphics2D(File file, Dimension size) throws IOException {
this(new FileOutputStream(file), size);
this.filename = file.getPath();
}
public SVGGraphics2D(File file, Component component) throws IOException {
this(new FileOutputStream(file), component);
this.filename = file.getPath();
}
public SVGGraphics2D(OutputStream os, Dimension size) throws IOException {
super(size, false);
init(os);
width = size.width;
height = size.height;
}
public SVGGraphics2D(OutputStream os, Component component) throws IOException {
super(component, false);
init(os);
width = getSize().width;
height = getSize().height;
}
private void init(OutputStream os) {
this.ros = os;
initProperties(getDefaultProperties());
this.filename = null;
this.clipNumber = new Value().set(0);
this.currentClipNumber = -1;
}
protected SVGGraphics2D(SVGGraphics2D graphics, boolean doRestoreOnDispose) {
super(graphics, doRestoreOnDispose);
// Now initialize the new object.
filename = graphics.filename;
os = graphics.os;
bbx = graphics.bbx;
bby = graphics.bby;
bbw = graphics.bbw;
bbh = graphics.bbh;
gradients = graphics.gradients;
textures = graphics.textures;
clipNumber = graphics.clipNumber;
currentClipNumber = -1;
}
/*================================================================================
| 2. Document Settings
*================================================================================*/
/**
* Get the bounding box for this image. */
public void setBoundingBox() {
bbx = 0;
bby = 0;
Dimension size = getSize();
bbw = size.width;
bbh = size.height;
}
/*================================================================================
| 3. Header, Trailer, Multipage & Comments
*================================================================================*/
/*--------------------------------------------------------------------------------
| 3.1 Header & Trailer
*--------------------------------------------------------------------------------*/
/**
* Write out the header of this SVG file. */
public void writeHeader() throws IOException {
ros = new BufferedOutputStream(ros);
if (isProperty(COMPRESS)) ros = new GZIPOutputStream(ros);
os = new PrintWriter(ros, true);
// Do the bounding box calculation.
setBoundingBox();
imageNumber = 0;
os.println("");
if (getProperty(VERSION).equals(VERSION_1_0)) {
os.println("");
} else if (getProperty(VERSION).equals(VERSION_1_1)) {
// FIXME disabled for now
} else {
// FIXME experimental version
}
os.println();
int x = 0;
int y = 0;
Dimension size = getPropertyDimension(IMAGE_SIZE);
int w = size.width;
if (w <= 0) w = width;
int h = size.height;
if (h <= 0) h = height;
os.println(" ");
os.println("
");
os.println(XMLWriter.normalizeText(getProperty(TITLE)));
os.println("");
String producer = getClass().getName();
if (!isDeviceIndependent()) {
producer += " "+version.substring(1,version.length()-1);
}
os.println("");
os.println(""+XMLWriter.normalizeText(getProperty(TITLE))+"");
os.println(""+XMLWriter.normalizeText(getCreator())+"");
os.println(""+XMLWriter.normalizeText(producer)+"");
os.println(""+XMLWriter.normalizeText(getProperty(FOR))+"");
if (!isDeviceIndependent()) {
os.println(""+
DateFormat.
getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).
format(new Date())+"");
}
os.println("");
writeDefs();
writeSetup();
}
private void writeDefs() throws IOException {
// The defs are kept in a file SVGDefs.txt in the same area
// as this class definition. It is simply copied into the
// output file.
os.println("");
copyResourceTo(this,"SVGDefs.txt", os);
if (isProperty(STYLABLE)) {
copyResourceTo(this, "SVGDefs-stylable.txt", os);
} else {
copyResourceTo(this, "SVGDefs-stylable.txt", os);
}
os.println("\n");
}
private void writeSetup() throws IOException {
os.println("");
setFont(getFont());
closeTags.push(" ");
}
public void writeBackground() throws IOException {
if (isProperty(TRANSPARENT)) {
setBackground(null);
} else if (isProperty(BACKGROUND)) {
setBackground(getPropertyColor(BACKGROUND_COLOR));
clearRect(0.0, 0.0, getSize().width, getSize().height);
} else {
setBackground(getComponent() != null ? getComponent().getBackground() : Color.WHITE);
clearRect(0.0, 0.0, getSize().width, getSize().height);
}
}
public void writeTrailer() throws IOException {
writeGraphicsRestore();
}
public void closeStream() throws IOException {
os.close();
}
/*================================================================================
| 4. Create
*================================================================================*/
public Graphics create() {
try {
writeGraphicsSave();
} catch (IOException e) {
handleException(e);
}
SVGGraphics2D tempGraphics = new SVGGraphics2D(this, true);
// os.println("");
return tempGraphics;
}
public Graphics create(double x, double y, double width, double height) {
try {
writeGraphicsSave();
} catch (IOException e) {
handleException(e);
}
SVGGraphics2D graphics = new SVGGraphics2D(this, true);
// FIXME: All other drivers have a translate(x,y), clip(0,0,w,h) here
os.println(" ");
return graphics;
}
protected void writeGraphicsSave() throws IOException {
// not applicable
}
protected void writeGraphicsRestore() throws IOException {
while(!closeTags.empty()) {
os.println(closeTags.pop());
}
}
/*================================================================================
| 5. Drawing Methods
*================================================================================*/
/* 5.1 shapes */
/* 5.1.1. lines, rectangles, round rectangles */
public void drawLine(double x1, double y1, double x2, double y2) {
os.println("");
}
public void drawRect(double x, double y, double width, double height) {
os.println("");
}
public void fillRect(double x, double y, double width, double height) {
os.println("");
}
public void drawRoundRect(double x, double y, double width, double height,
double arcWidth, double arcHeight) {
os.println("");
}
public void fillRoundRect(double x, double y, double width, double height,
double arcWidth, double arcHeight) {
os.println("");
}
/* 5.1.2. polylines, polygons */
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void drawPolyline(double[] xPoints, double[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void drawPolygon(double[] xPoints, double[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void drawPolygon(Polygon p) {
drawPolygon(p.xpoints,p.ypoints,p.npoints);
}
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void fillPolygon(double[] xPoints, double[] yPoints, int nPoints) {
if (nPoints>1) {
os.print("");
}
}
public void fillPolygon(Polygon p) {
fillPolygon(p.xpoints,p.ypoints,p.npoints);
}
/* 5.1.3. ovals, arcs */
public void drawArc(double x, double y,
double width, double height,
double startAngle, double arcAngle) {
double sa = startAngle * Math.PI / 180;
double aa = arcAngle * Math.PI / 180;
double rx = width/2;
double ry = height/2;
double x1 = x + bias + rx + rx*Math.cos(sa);
double y1 = y + bias + ry - ry*Math.sin(sa);
double x2 = x + bias + rx + rx*Math.cos(sa+aa);
double y2 = y + bias + ry - ry*Math.sin(sa+aa);
int large = (Math.abs(arcAngle) <= 180) ? 0 : 1;
int sweep = (arcAngle > 0) ? 0 : 1;
os.println("");
}
public void fillArc(double x, double y, double width, double height,
double startAngle, double arcAngle) {
double sa = startAngle * Math.PI / 180;
double aa = arcAngle * Math.PI / 180;
double rx = width/2;
double ry = height/2;
double x1 = x + rx + rx*Math.cos(sa);
double y1 = y + ry - ry*Math.sin(sa);
double x2 = x + rx + rx*Math.cos(sa+aa);
double y2 = y + ry - ry*Math.sin(sa+aa);
int large = (Math.abs(arcAngle) <= 180) ? 0 : 1;
int sweep = (arcAngle > 0) ? 0 : 1;
os.println("");
}
public void drawOval(double x, double y, double width, double height) {
os.println("");
}
public void fillOval(double x, double y, double width, double height) {
os.println("");
}
/*
public void drawSymbol(double x, double y, double size, int symbol) {
if (size>0) {
// FIXME: the current viewer (Adobe beta2 4/00) ignores the ViewBox in the symbols
// so we go for fixed size (100) symbols and do the scaling and translation ourselves...
// we also leave out any placement!
os.print("");
}
}
*/
/* 5.1.4. shapes */
public void draw(Shape shape) {
PathIterator path = shape.getPathIterator(null);
os.println("");
writePath(path);
os.println(" ");
}
public void fill(Shape shape) {
PathIterator path = shape.getPathIterator(null);
StringBuffer s = new StringBuffer();
s.append(color(null, getPaint()));
if (path.getWindingRule()==PathIterator.WIND_EVEN_ODD) {
s.append("fill-rule:evenodd;");
} else {
s.append("fill-rule:nonzero;");
}
os.println("");
writePath(path);
os.println(" ");
}
public void fillAndDraw(Shape shape, Color fillColor) {
PathIterator path = shape.getPathIterator(null);
StringBuffer s = new StringBuffer();
if (fillColor != null) {
s.append(color(getPaint(), fillColor));
if (path.getWindingRule()==PathIterator.WIND_EVEN_ODD) {
s.append("fill-rule:evenodd;");
} else {
s.append("fill-rule:nonzero;");
}
}
os.println("");
writePath(path);
os.println(" ");
}
/* 5.2. Images */
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
writeWarning(getClass()+": copyArea(int, int, int, int, int, int) not implemented.");
}
protected void writeImage(RenderedImage image, AffineTransform xform, Color bkg) throws IOException {
if ((xform != null) && !xform.isIdentity()) {
os.println("");
}
os.print("");
if ((xform != null) && !xform.isIdentity()) {
os.println(" ");
}
}
/* 5.3. Strings */
protected void writeString(String str, double x, double y) throws IOException {
str = FontUtilities.getEncodedString(str, getFont().getName());
os.println("");
os.println(XMLWriter.normalizeText(str));
os.println("");
}
public void drawString(String str, double x, double y, int horizontal, int vertical,
boolean framed, Color frameColor, double frameWidth,
boolean banner, Color bannerColor) {
str = FontUtilities.getEncodedString(str, getFont().getName());
LineMetrics metrics = getFont().getLineMetrics(str, getFontRenderContext());
double w = getFont().getStringBounds(str, getFontRenderContext()).getWidth();
double h = metrics.getHeight();
double d = metrics.getDescent();
double adjustment = (getFont().getSize2D()*2)/10;
double ny = getYalignment(y, h, d, vertical);
double nx = getXalignment(x, w, horizontal);
// Calculate the box size for the banner.
double rx = nx-adjustment;
double ry = ny-h+d-adjustment;
double rw = w+2*adjustment;
double rh = h+2*adjustment;
if (banner) {
Color color = getColor();
setColor(bannerColor);
fillRect(rx, ry, rw, rh);
setColor(color);
}
if (framed) {
Color color = getColor();
setColor(frameColor);
Stroke s = getStroke();
setLineWidth(frameWidth);
drawRect(rx, ry, rw, rh);
setColor(color);
setStroke(s);
}
os.println("");
os.println(XMLWriter.normalizeText(str));
os.println("");
}
public void drawString(TagString str, double x, double y, int horizontal, int vertical,
boolean framed, Color frameColor, double frameWidth,
boolean banner, Color bannerColor) {
SVGTagHandler tagHandler = new SVGTagHandler(isProperty(STYLABLE), getFont(), getFontRenderContext());
double nx = getXalignment(x, tagHandler.stringWidth(str), horizontal);
LineMetrics metrics = getFont().getLineMetrics(str.toString(), getFontRenderContext());
double w = tagHandler.stringWidth(str);
double h = metrics.getHeight();
double d = metrics.getDescent();
double adjustment = (getFont().getSize2D()*2)/10;
double ny = getYalignment(y, h, d, vertical);
// Calculate the box size for the banner.
double rx = nx-adjustment;
double ry = ny-h+d-adjustment;
double rw = w+2*adjustment;
double rh = h+2*adjustment;
if (banner) {
Color color = getColor();
setColor(bannerColor);
fillRect(rx, ry, rw, rh);
setColor(color);
}
if (framed) {
Color color = getColor();
setColor(frameColor);
Stroke s = getStroke();
setLineWidth(frameWidth);
drawRect(rx, ry, rw, rh);
setColor(color);
setStroke(s);
}
String string = tagHandler.parse(str);
string = FontUtilities.getEncodedString(string, getFont().getName());
os.println("");
os.println(string);
os.println("");
}
public void drawString(AttributedCharacterIterator iterator, float x, float y) {
writeWarning(getClass()+": drawString(AttributedCharacterIterator, float, float) not implemented.");
}
public void drawGlyphVector(GlyphVector g, float x, float y) {
writeWarning(getClass()+": drawGlyphVector(GlyphVector, float, float) not implemented.");
}
/*================================================================================
| 6. Transformations
*================================================================================*/
protected void writeTransform(AffineTransform transform) throws IOException {
os.println("");
closeTags.push(" ");
}
/*================================================================================
| 7. Clipping
*================================================================================*/
protected void writeClip(Rectangle2D r2d) throws IOException {
writeClip((Shape)r2d);
}
protected void writeClip(Shape s) throws IOException {
if (s == null) {
currentClipNumber = -1;
return;
}
PathIterator path = s.getPathIterator(null);
currentClipNumber = clipNumber.getInt();
clipNumber.set(currentClipNumber+1);
os.println("");
writePath(path);
os.println("");
}
/*================================================================================
| 8. Graphics State
*================================================================================*/
/* 8.1. stroke/linewidth */
protected void writeWidth(float width) throws IOException {
// width of 0 means thinnest line, which does not exist in SVG
if (width == 0) width = 0.000001f;
os.println("");
closeTags.push(" ");
}
protected void writeCap(int cap) throws IOException {
os.print("");
closeTags.push(" ");
}
protected void writeJoin(int join) throws IOException {
os.print("");
closeTags.push(" ");
}
protected void writeMiterLimit(float limit) throws IOException {
os.println("");
closeTags.push(" ");
}
protected void writeDash(double[] dash, double phase) throws IOException {
os.print(" 0) {
for (int i=0; i 0) s.append(",");
s.append(fixedPrecision(dash[i]));
}
s.append(";");
} else {
s.append("none;");
}
s.append("stroke-dashoffset:"+fixedPrecision(phase));
os.println(style(s.toString())+">");
closeTags.push(" ");
}
/* 8.2. paint/color */
public void setPaintMode() {
writeWarning(getClass()+": setPaintMode() not implemented.");
}
public void setXORMode(Color c1) {
writeWarning(getClass()+": setXORMode(Color) not implemented.");
}
protected void writePaint(Color c) throws IOException {
// written with every draw
}
protected void writePaint(GradientPaint paint) throws IOException {
if (gradients.get(paint) == null) {
String name = "gradient-"+gradients.size();
gradients.put(paint, name);
GradientPaint gp = (GradientPaint)paint;
Point2D p1 = gp.getPoint1();
Point2D p2 = gp.getPoint2();
os.println("");
os.print(" ");
os.println(" ");
os.println(" ");
os.println(" ");
os.println("");
}
os.println("");
closeTags.push(" ");
}
protected void writePaint(TexturePaint paint) throws IOException {
if (textures.get(paint) == null) {
String name = "texture-"+textures.size();
textures.put(paint, name);
TexturePaint tp = (TexturePaint)paint;
BufferedImage image = tp.getImage();
Rectangle2D rect = tp.getAnchorRect();
os.println("");
os.print(" ");
writeImage(image, null, null);
os.println(" ");
os.println("");
}
os.println("");
closeTags.push(" ");
}
protected void writePaint(Paint p) throws IOException {
writeWarning(getClass()+": writePaint(Paint) not implemented for "+p.getClass());
}
private static final Properties replaceFonts = new Properties();
static {
replaceFonts.setProperty("Dialog", "sans-serif");
replaceFonts.setProperty("DialogInput", "sans-serif");
replaceFonts.setProperty("Serif", "serif");
replaceFonts.setProperty("SansSerif", "sans-serif");
replaceFonts.setProperty("Monospaced", "monospace");
replaceFonts.setProperty("Symbol", "serif");
replaceFonts.setProperty("ZapfDingbats", "serif");
replaceFonts.setProperty("TimesRoman", "serif");
replaceFonts.setProperty("Helvetica", "sans-serif");
replaceFonts.setProperty("Courier", "monospace");
}
/* 8.3. font */
/**
* Method sets the current font. This method makes a reasonable
* guess for the desired SVG font since the names of the
* actual SVG fonts is implementation dependent.
* Currently, this tries to identify Helvetica, Times, Courier,
* Symbol, and ZapfDingbats fonts. If all else fails, Helvetica
* is used. */
public void setFont(Font font) {
super.setFont(font);
StringBuffer svgFont = new StringBuffer();
svgFont.append("font-family:");
String fontName = font.getName();
svgFont.append(replaceFonts.getProperty(fontName, fontName));
if (font.isBold()) {
svgFont.append(";font-weight:bold");
} else {
svgFont.append(";font-weight:normal");
}
if (font.isItalic()) {
svgFont.append(";font-style:italic");
} else {
svgFont.append(";font-style:normal");
}
int size = font.getSize();
svgFont.append(";font-size:"+size);
os.println("");
closeTags.push(" ");
}
/*================================================================================
| 9. Auxiliary
*================================================================================*/
public GraphicsConfiguration getDeviceConfiguration() {
writeWarning(getClass()+": getDeviceConfiguration() not implemented.");
return null;
}
public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
writeWarning(getClass()+": hit(Rectangle, Shape, boolean) not implemented.");
return false;
}
public void writeComment(String s) throws IOException {
os.println("");
}
public String toString() {
return "SVGGraphics2D";
}
/*================================================================================
| 10. Private/Utility Methos
*================================================================================*/
private String color(Paint stroke, Paint fill) {
StringBuffer s = new StringBuffer();
s.append("stroke:");
if (stroke != null) {
s.append(hexColor(stroke));
s.append(";stroke-opacity:");
s.append(alphaColor(stroke));
} else {
s.append("none");
}
s.append(";");
s.append("fill:");
if (fill != null) {
s.append(hexColor(fill));
s.append(";fill-opacity:");
s.append(alphaColor(fill));
} else {
s.append("none");
}
s.append(";");
return s.toString();
}
private float alphaColor(Paint p) {
if (p instanceof Color) {
return (float)(getPrintColor((Color)p).getAlpha()/255.0);
} else if (p instanceof GradientPaint) {
return 1.0f;
} else if (p instanceof TexturePaint) {
return 1.0f;
}
writeWarning(getClass()+": alphaColor() not implemented for "+p.getClass()+".");
return 1.0f;
}
private String hexColor(Paint p) {
if (p instanceof Color) {
return hexColor(getPrintColor((Color)p));
} else if (p instanceof GradientPaint) {
return hexColor((GradientPaint)p);
} else if (p instanceof TexturePaint) {
return hexColor((TexturePaint)p);
}
writeWarning(getClass()+": hexColor() not implemented for "+p.getClass()+".");
return "#000000";
}
private String hexColor(Color c) {
String s1 = Integer.toHexString(c.getRed());
s1 = (s1.length() != 2) ? "0"+s1 : s1;
String s2 = Integer.toHexString(c.getGreen());
s2 = (s2.length() != 2) ? "0"+s2 : s2;
String s3 = Integer.toHexString(c.getBlue());
s3 = (s3.length() != 2) ? "0"+s3 : s3;
return "#"+s1+s2+s3;
}
private String hexColor(GradientPaint p) {
return "url(#"+gradients.get(p)+")";
}
private String hexColor(TexturePaint p) {
return "url(#"+textures.get(p)+")";
}
/**
* Write the path to the output file.
*/
private void writePath(PathIterator path) {
double[] coords = new double[6];
double currentX = 0.;
double currentY = 0.;
os.print("");
}
private String clipPath() {
return (currentClipNumber < 0) ? "" : "clip-path=\"url(#clip"+currentClipNumber+")\" ";
}
private String defaultStyle() {
// FIXME: should be currentWidth...
return style(color(getPaint(), null)+
"stroke-width:1;"+
"stroke-linecap:square");
}
private String style(String stylableString) {
return style(isProperty(STYLABLE), stylableString);
}
static String style(boolean stylable, String stylableString) {
if ((stylableString == null) || (stylableString.equals(""))) return "";
if (stylable) return "style=\""+stylableString+"\"";
StringBuffer r = new StringBuffer();
StringTokenizer st1 = new StringTokenizer(stylableString, ";");
while (st1.hasMoreTokens()) {
String s = st1.nextToken();
int colon = s.indexOf(':');
if (colon >= 0) {
r.append(s.substring(0,colon));
r.append("=\"");
r.append(s.substring(colon+1));
r.append("\" ");
}
}
return r.toString();
}
private String getAlignmentString(int horizontal, int vertical, LineMetrics metrics) {
String textAnchor;
switch(horizontal) {
case TEXT_CENTER:
textAnchor = "middle";
break;
case TEXT_RIGHT:
textAnchor = "end";
break;
case TEXT_LEFT:
default:
textAnchor = "start";
break;
}
// FIXME not a very good job yet. For tagstrings this does not work very well.
double alignmentBaseline;
switch(vertical) {
case TEXT_TOP:
alignmentBaseline = -100*(metrics.getAscent()+metrics.getLeading())/metrics.getHeight();
break;
case TEXT_CENTER:
alignmentBaseline = -50*metrics.getAscent()/metrics.getHeight();
break;
case TEXT_BOTTOM:
alignmentBaseline = metrics.getDescent()/metrics.getHeight();
break;
case TEXT_BASELINE:
default:
alignmentBaseline = 0;
break;
}
return "text-anchor:"+textAnchor+";"+"baseline-shift:"+fixedPrecision(alignmentBaseline)+"%";
}
}