SVG version implemented is REC-SVG-20010904, see www.w3.org
Adobe's SVGView plugin version 3.0 can be used as viewer
The following Limitations exist:
TestClipping, one pixel offset and only rectangular clipping
TestTaggedString last font not Courier
TestText2D vertical offset ignored
TestShapes no dingbats
TestShapes no symbols
TestPaint TexturePaint is not implemented
TestFonts real unicode problems
TestAll no Impact font.
TestAll no special characters for impact.
TestAll no sky.
FontMetrics is implemented using the JDK fonts rather than the SVG fonts.
The TestColorMap is not properly exported (linewidth or insets) to SVG. The surrounding boxes
are too thick.
The setTextPosition call is implemented, but viewers do not yet support "alignment-baseline"
The symbol font is not displayed
The zapfdingbats font is displayed as Helvetica
Compressed svg files (svgz) were never tested, since no viewers accepts them.
drawString(attributedCharIter) is unimplemented.
The tagstring <v> tag is ignored. This should be implemented using <glyphRun> which is not supported yet in
SVGView 3.0
@status Stable.
src/main/java/org/freehep/graphicsio/svg/SVGGlyph.java 0000644 0001750 0001750 00000005475 11323173777 022226 0 ustar user03 user03 // Copyright 2001-2006, FreeHEP.
package org.freehep.graphicsio.svg;
import java.awt.Shape;
import java.awt.font.GlyphMetrics;
import java.awt.geom.AffineTransform;
/**
* Class for embedding a Font in a SVG file.
*
* @author Steffen Greiffenberg
* @version $Id: SVGGlyph.java 8584 2006-08-10 23:06:37Z duns $
*/
public class SVGGlyph {
public static int FONT_SIZE = 100;
public static int UNITS_PER_EM = 2048;
/**
* glyp
*/
private Shape glyph;
/**
* Character index
*/
private int unicode;
/**
* Metrics of that character
*/
private GlyphMetrics glyphMetrics;
/**
* Flip the drawing upside down
*/
private static AffineTransform defaultTransform = new AffineTransform(1, 0,
0, -1, 0, 0);
/**
* stores the glyph data
*
* @param unicode
* @param glyph
*/
public SVGGlyph(Shape glyph, int unicode, GlyphMetrics glyphMetrics) {
this.unicode = unicode;
this.glyph = glyph;
this.glyphMetrics = glyphMetrics;
}
public String toString() {
StringBuffer result = new StringBuffer("");
return result.toString();
}
/**
* @return SVG path tag using SVGGraphics2D and defaultTransform
*/
protected String getPathString() {
return SVGGraphics2D.getPathContent(glyph
.getPathIterator(defaultTransform));
}
/**
* @return SVG tag horiz-adv-x
*/
public String getHorizontalAdvanceXString() {
StringBuffer result = new StringBuffer();
if (glyphMetrics.getAdvanceX() != 0) {
result.append("horiz-adv-x=\"");
result.append(SVGGraphics2D.fixedPrecision(glyphMetrics
.getAdvanceX()));
result.append("\" ");
}
return result.toString();
}
/**
* @return SVG tag horiz-adv-y
*/
public String getHorizontalAdvanceYString() {
StringBuffer result = new StringBuffer();
if (glyphMetrics.getAdvanceY() != 0) {
result.append("horiz-adv-y=\"");
result.append(SVGGraphics2D.fixedPrecision(glyphMetrics
.getAdvanceY()));
result.append("\" ");
}
return result.toString();
}
}
src/main/java/org/freehep/graphicsio/svg/SVGFontTable.java 0000644 0001750 0001750 00000024006 11323173777 023010 0 ustar user03 user03 // Copyright 2001-2006, FreeHEP.
package org.freehep.graphicsio.svg;
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.freehep.graphics2d.font.FontUtilities;
import org.freehep.graphicsio.font.FontTable;
/**
* A table to remember which glyphs were used while writing a svg file.
* Entries are added by calling {@link #addGlyphs(String, java.awt.Font)}.
* The final SVG tag for the section is generated using {@link #toString()}.
* Use {@link #normalize(java.util.Map)} for referencing embedded glyphs
* in tags.
*
* @author Steffen Greiffenberg
* @version $Id: SVGFontTable.java 12753 2007-06-12 22:32:31Z duns $
*/
public class SVGFontTable {
/**
* Stores fonts and a glyph-hashtable. The font key ist normalized using
* {@link #untransform(java.awt.Font)}
*/
private Hashtable/**/ glyphs =
new Hashtable/*>*/();
/**
* creates a glyph for the string character
*
* @param c
* @param font
* @return unique font name
*/
private SVGGlyph addGlyph(int c, Font font) {
// is the font stored?
Hashtable/**/ glyphs = getGlyphs(font);
// does a glyph allready exist?
SVGGlyph result = (SVGGlyph) glyphs.get(String.valueOf(c));
// create a new one?
if (result == null) {
// create and store the SVG Glyph
result = createGlyph(c, font);
glyphs.put(String.valueOf(c), result);
}
return result;
}
/**
* @param c
* @param font
* @return GlyphVector using a default rendering context
*/
private SVGGlyph createGlyph(int c, Font font) {
GlyphVector glyphVector = font.createGlyphVector(
// flipping is done by SVGGlyph
new FontRenderContext(null, true, true),
// unicode to char
String.valueOf((char) c));
// create and store the SVG Glyph
return new SVGGlyph(
glyphVector.getGlyphOutline(0),
c,
glyphVector.getGlyphMetrics(0));
}
/**
* creates the glyph for the string
*
* @param string
* @param font
*/
protected void addGlyphs(String string, Font font) {
font = untransform(font);
// add characters
for (int i = 0; i < string.length(); i ++) {
addGlyph(string.charAt(i), font);
}
}
/**
* @param font
* @return glyph vectors for font
*/
private Hashtable/**/ getGlyphs(Font font) {
// derive a default font for the font table
font = untransform(font);
Hashtable/**/ result =
(Hashtable/**/) glyphs.get(font);
if (result == null) {
result = new Hashtable/**/();
glyphs.put(font, result);
}
return result;
}
/**
* creates the font entry:
*
*
*
* ...
*
*
*
* @return string representing the entry
*/
public String toString() {
StringBuffer result = new StringBuffer();
Enumeration/**/ fonts = this.glyphs.keys();
while (fonts.hasMoreElements()) {
Font font = (Font) fonts.nextElement();
// replace font family for svg
Map /**/ attributes = FontUtilities.getAttributes(font);
// Dialog -> Helvetica
normalize(attributes);
// familiy
result.append("\n");
// font-face
result.append("\n");
// missing glyph
SVGGlyph glyph = createGlyph(font.getMissingGlyphCode(), font);
result.append("\n");
// regular glyphs
Iterator glyphs = getGlyphs(font).values().iterator();
while (glyphs.hasNext()) {
result.append(glyphs.next().toString());
result.append("\n");
}
// close ""
result.append("\n");
}
return result.toString();
}
/**
* creates a font based on the parameter. The size will be {@link SVGGlyph#FONT_SIZE}
* and transformation will be removed. Example:
* java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=30]
* will result to:
* java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=100]
*
* This method does not substitute font name or family.
*
* @param font
* @return font based on the parameter
*/
private Font untransform(Font font) {
// replace font family
Map /**/ attributes = FontUtilities.getAttributes(font);
// set default font size
attributes.put(TextAttribute.SIZE, new Float(SVGGlyph.FONT_SIZE));
// remove font transformation
attributes.remove(TextAttribute.TRANSFORM);
attributes.remove(TextAttribute.SUPERSCRIPT);
return new Font(attributes);
}
/**
* font replacements makes SVG in AdobeViewer look better, firefox replaces
* all font settings, even the family fame
*/
private static final Properties replaceFonts = new Properties();
static {
replaceFonts.setProperty("dialog", "Helvetica");
replaceFonts.setProperty("dialoginput", "Courier New");
// FIXME: works well on windows, others?
// "TimesRoman" is not valid under Firefox 1.5
replaceFonts.setProperty("serif", "Times");
replaceFonts.setProperty("timesroman", "Times");
replaceFonts.setProperty("sansserif", "Helvetica");
// FIXME: works well on windows, others?
// "Courier" is not valid under Firefox 1.5
replaceFonts.setProperty("monospaced", "Courier New");
// FIXME: replacement for zapfdingbats?
replaceFonts.setProperty("zapfdingbats", "Wingdings");
}
/**
* Replaces TextAttribute.FAMILY by values of replaceFonts. When a
* font created using the result of this method the transformation would be:
*
* java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=30]
* will result to:
* java.awt.Font[family=SansSerif,name=Helvetica,style=plain,size=30]
*
* Uses {@link FontTable#normalize(java.util.Map)} first.
*
* @param attributes with font name to change
*/
public static void normalize(Map /**/ attributes) {
// dialog.bold -> Dialog with TextAttribute.WEIGHT_BOLD
FontTable.normalize(attributes);
// get replaced font family name (Yes it's right, not the name!)
String family = replaceFonts.getProperty(
((String) attributes.get(TextAttribute.FAMILY)).toLowerCase());
if (family == null) {
family = (String) attributes.get(TextAttribute.FAMILY);
}
// replace the family (Yes it's right, not the name!) in the attributes
attributes.put(TextAttribute.FAMILY, family);
}
}
src/main/java/org/freehep/graphicsio/svg/SVGExportFileType.java 0000644 0001750 0001750 00000012152 11323173777 024054 0 ustar user03 user03 // Copyright 2002-2007, FreeHEP.
package org.freehep.graphicsio.svg;
import java.awt.Component;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.freehep.graphics2d.VectorGraphics;
import org.freehep.graphicsio.AbstractVectorGraphicsIO;
import org.freehep.graphicsio.ImageConstants;
import org.freehep.graphicsio.InfoConstants;
import org.freehep.graphicsio.exportchooser.AbstractExportFileType;
import org.freehep.graphicsio.exportchooser.BackgroundPanel;
import org.freehep.graphicsio.exportchooser.FontPanel;
import org.freehep.graphicsio.exportchooser.ImageSizePanel;
import org.freehep.graphicsio.exportchooser.ImageTypePanel;
import org.freehep.graphicsio.exportchooser.InfoPanel;
import org.freehep.graphicsio.exportchooser.OptionCheckBox;
import org.freehep.graphicsio.exportchooser.OptionComboBox;
import org.freehep.graphicsio.exportchooser.OptionPanel;
import org.freehep.graphicsio.exportchooser.OptionTextField;
import org.freehep.swing.layout.TableLayout;
import org.freehep.util.UserProperties;
/**
*
* @author Mark Donszelmann
* @version $Id: SVGExportFileType.java 12753 2007-06-12 22:32:31Z duns $
*/
public class SVGExportFileType extends AbstractExportFileType {
// Constants for the SVG options.
final private static String versionList[] = {
// SVGGraphics2D.VERSION_1_0,
SVGGraphics2D.VERSION_1_1,
// SVGGraphics2D.VERSION_1_2
};
private OptionCheckBox compress;
public String getDescription() {
return "Scalable Vector Graphics";
}
public String[] getExtensions() {
return new String[] { "svg", "svgz" };
}
public String[] getMIMETypes() {
return new String[] { "image/svg+xml" };
}
public boolean hasOptionPanel() {
return true;
}
public JPanel createOptionPanel(Properties user) {
UserProperties options = new UserProperties(user, SVGGraphics2D
.getDefaultProperties());
String rootKey = SVGGraphics2D.class.getName();
String abstractRootKey = AbstractVectorGraphicsIO.class.getName();
OptionPanel imageSize = new ImageSizePanel(options, rootKey);
OptionPanel format = new OptionPanel("Format");
format.add(TableLayout.LEFT, new JLabel("SVG Version"));
format.add(TableLayout.RIGHT, new OptionComboBox(options,
SVGGraphics2D.VERSION, versionList));
compress = new OptionCheckBox(options, SVGGraphics2D.COMPRESS,
"Compress");
format.add(TableLayout.FULL, compress);
format.add(TableLayout.FULL, new OptionCheckBox(options,
SVGGraphics2D.STYLABLE, "Stylable"));
OptionPanel imageExport = new OptionPanel("Embed / Export Images");
OptionCheckBox exportImages = new OptionCheckBox(options,
SVGGraphics2D.EXPORT_IMAGES, "Export");
imageExport.add(TableLayout.FULL, exportImages);
JLabel exportSuffixLabel = new JLabel("Image Suffix");
imageExport.add(TableLayout.LEFT, exportSuffixLabel);
exportImages.enables(exportSuffixLabel);
final OptionTextField exportSuffix = new OptionTextField(options,
SVGGraphics2D.EXPORT_SUFFIX, 20);
imageExport.add(TableLayout.RIGHT, exportSuffix);
exportImages.enables(exportSuffix);
InfoPanel infoPanel = new InfoPanel(options, rootKey, new String[] {
InfoConstants.CREATOR, InfoConstants.TITLE, });
// TableLayout.LEFT Panel
JPanel leftPanel = new OptionPanel();
leftPanel.add(TableLayout.COLUMN, imageSize);
leftPanel.add(TableLayout.COLUMN, format);
leftPanel.add(TableLayout.COLUMN_FILL, new JLabel());
// TableLayout.RIGHT Panel
JPanel rightPanel = new OptionPanel();
rightPanel.add(TableLayout.COLUMN, new BackgroundPanel(options,
rootKey, true));
rightPanel.add(TableLayout.COLUMN, imageExport);
rightPanel.add(TableLayout.COLUMN, new ImageTypePanel(options, rootKey,
new String[] { ImageConstants.SMALLEST, ImageConstants.PNG,
ImageConstants.JPG }));
rightPanel.add(TableLayout.COLUMN, new FontPanel(options, null,
abstractRootKey));
rightPanel.add(TableLayout.COLUMN_FILL, new JLabel());
// Make the full panel.
OptionPanel panel = new OptionPanel();
panel.add("0 0 [5 5 5 5] wt", leftPanel);
panel.add("1 0 [5 5 5 5] wt", rightPanel);
panel.add("0 1 2 1 [5 5 5 5] wt", infoPanel);
panel.add(TableLayout.COLUMN_FILL, new JLabel());
return panel;
}
public VectorGraphics getGraphics(OutputStream os, Component target)
throws IOException {
return new SVGGraphics2D(os, target);
}
public VectorGraphics getGraphics(OutputStream os, Dimension dimension)
throws IOException {
return new SVGGraphics2D(os, dimension);
}
public VectorGraphics getGraphics(File file, Component target)
throws IOException {
return new SVGGraphics2D(file, target);
}
public VectorGraphics getGraphics(File file, Dimension dimension)
throws IOException {
return new SVGGraphics2D(file, dimension);
}
public File adjustFilename(File file, Properties user) {
UserProperties options = new UserProperties(user, SVGGraphics2D
.getDefaultProperties());
if (options.isProperty(SVGGraphics2D.COMPRESS)) {
return adjustExtension(file, "svgz", null, "");
} else {
return adjustExtension(file, "svg", null, "");
}
}
}
src/main/java/org/freehep/graphicsio/svg/SVGGraphics2D.java 0000644 0001750 0001750 00000122621 11323173777 023062 0 ustar user03 user03 // Copyright 2000-2007 FreeHEP
package org.freehep.graphicsio.svg;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import java.util.zip.GZIPOutputStream;
import org.freehep.graphics2d.font.FontUtilities;
import org.freehep.graphicsio.AbstractVectorGraphicsIO;
import org.freehep.graphicsio.FontConstants;
import org.freehep.graphicsio.ImageConstants;
import org.freehep.graphicsio.ImageGraphics2D;
import org.freehep.graphicsio.InfoConstants;
import org.freehep.graphicsio.PageConstants;
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-SVG11-20030114
*
* @author Mark Donszelmann
* @version $Id: SVGGraphics2D.java 12753 2007-06-12 22:32:31Z duns $
*/
public class SVGGraphics2D extends AbstractVectorGraphicsIO {
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";
/**
* use style="font-size:20" instaed of font-size="20"
* see {@link #style(java.util.Properties)} for details
*/
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 BasicStroke defaultStroke = new BasicStroke();
public static final String EMBED_FONTS = rootKey + "."
+ FontConstants.EMBED_FONTS;
private SVGFontTable fontTable;
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_1);
defaultProperties.setProperty(COMPRESS, false);
defaultProperties.setProperty(STYLABLE, false);
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, "");
defaultProperties.setProperty(CLIP, true);
defaultProperties.setProperty(EMBED_FONTS, false);
defaultProperties.setProperty(TEXT_AS_SHAPES, true);
}
public static Properties getDefaultProperties() {
return defaultProperties;
}
public static void setDefaultProperties(Properties newProperties) {
defaultProperties.setProperties(newProperties);
}
public static final String version = "$Revision: 12753 $";
// 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 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) {
super(size, false);
init(os);
width = size.width;
height = size.height;
}
public SVGGraphics2D(OutputStream os, Component component) {
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);
}
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;
fontTable = graphics.fontTable;
}
/*
* ================================================================================ |
* 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);
fontTable = new SVGFontTable();
// Do the bounding box calculation.
setBoundingBox();
imageNumber = 0;
os.println("");
if (getProperty(VERSION).equals(VERSION_1_1)) {
// no DTD anymore
} 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.print("");
os.print(XMLWriter.normalizeText(getProperty(TITLE)));
os.println("");
String producer = getClass().getName();
if (!isDeviceIndependent()) {
producer += " " + version.substring(1, version.length() - 1);
}
os.print("");
os.print("Creator: " + XMLWriter.normalizeText(getCreator()));
os.print(" Producer: " + XMLWriter.normalizeText(producer));
os.print(" Source: " + XMLWriter.normalizeText(getProperty(FOR)));
if (!isDeviceIndependent()) {
os.print(" Date: "
+ DateFormat.getDateTimeInstance(DateFormat.FULL,
DateFormat.FULL).format(new Date()));
}
os.println("");
// write default stroke
os.print("");
// close default settings at the end
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);
}
}
/**
* Writes the font definitions and calls {@link #writeGraphicsRestore()} to
* close all open XML Tags
*
* @throws IOException
*/
public void writeTrailer() throws IOException {
// write font definition
if (isProperty(EMBED_FONTS)) {
os.println("");
os.println(fontTable.toString());
os.println(" ");
}
// restor graphic
writeGraphicsRestore();
}
public void closeStream() throws IOException {
os.close();
}
/*
* ================================================================================ |
* 4. Create
* ================================================================================
*/
public Graphics create() {
try {
writeGraphicsSave();
} catch (IOException e) {
handleException(e);
}
return new SVGGraphics2D(this, true);
}
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(" ");
// write default stroke
os.print("");
graphics.closeTags.push(" ");
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.4. shapes */
/**
* Draws the shape using the current paint as border
*
* @param shape Shape to draw
*/
public void draw(Shape shape) {
// others than BasicStrokes are written by its
// {@link Stroke#createStrokedShape()}
if (getStroke() instanceof BasicStroke) {
PathIterator path = shape.getPathIterator(null);
Properties style = new Properties();
if (getPaint() != null) {
style.put("stroke", hexColor(getPaint()));
style.put("stroke-opacity", fixedPrecision(alphaColor(getPaint())));
}
// no filling
style.put("fill", "none");
style.putAll(getStrokeProperties(getStroke(), false));
writePathIterator(path, style);
} else if (getStroke() != null) {
// fill the shape created by stroke
fill(getStroke().createStrokedShape(shape));
} else {
// FIXME: do nothing or draw using defaultStroke?
fill(defaultStroke.createStrokedShape(shape));
}
}
/**
* Fills the shape without a border using the current paint
*
* @param shape Shape to be filled with the current paint
*/
public void fill(Shape shape) {
// draw paint as image if needed
if (!(getPaint() instanceof Color || getPaint() instanceof GradientPaint)) {
// draw paint as image
fill(shape, getPaint());
} else {
PathIterator path = shape.getPathIterator(null);
Properties style = new Properties();
if (path.getWindingRule() == PathIterator.WIND_EVEN_ODD) {
style.put("fill-rule", "evenodd");
} else {
style.put("fill-rule", "nonzero");
}
// fill with paint
if (getPaint() != null) {
style.put("fill", hexColor(getPaint()));
style.put("fill-opacity", fixedPrecision(alphaColor(getPaint())));
}
// no border
style.put("stroke", "none");
writePathIterator(path, style);
}
}
/**
* writes a path using {@link #getPath(java.awt.geom.PathIterator)}
* and the given style
*
* @param pi PathIterator
* @param style Properties for tag
*/
private void writePathIterator(PathIterator pi, Properties style) {
StringBuffer result = new StringBuffer();
// write style
result.append("\n ");
// draw shape
result.append(getPath(pi));
// close style
result.append("\n ");
boolean drawClipped = false;
// test if clip intersects pi
if (getClip() != null) {
GeneralPath gp = new GeneralPath();
gp.append(pi, true);
// create the stroked shape
Stroke stroke = getStroke() == null? defaultStroke : getStroke();
Rectangle2D bounds = stroke.createStrokedShape(gp).getBounds();
// clip should intersect the path
// if clip contains the bounds completely, clipping is not needed
drawClipped = getClip().intersects(bounds) && !getClip().contains(bounds);
}
if (drawClipped) {
// write in a transformed and clipped context
os.println(
getTransformedString(
getTransform(),
getClippedString(result.toString())));
} else {
// write in a transformed context
os.println(
getTransformedString(
getTransform(),
result.toString()));
}
}
/* 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 {
StringBuffer result = new StringBuffer();
result.append("");
os.println(getTransformedString(getTransform(),
getClippedString(getTransformedString(xform, result
.toString()))));
}
/* 5.3. Strings */
protected void writeString(String str, double x, double y)
throws IOException {
// str = FontEncoder.getEncodedString(str, getFont().getName());
if (isProperty(EMBED_FONTS)) {
fontTable.addGlyphs(str, getFont());
}
// font transformation should _not_ transform string position
// so we draw at 0:0 and translate _before_ using getFont().getTransform()
// we could not just translate before and reverse translation after
// writing because the clipping area
// create font properties
Properties style = getFontProperties(getFont());
// add stroke properties
if (getPaint() != null) {
style.put("fill", hexColor(getPaint()));
style.put("fill-opacity", fixedPrecision(alphaColor(getPaint())));
} else {
style.put("fill", "none");
}
style.put("stroke", "none");
// convert tags to string values
str = XMLWriter.normalizeText(str);
// replace leading space by a0; otherwise firefox 1.5 fails
if (str.startsWith(" ")) {
str = " " + str.substring(1);
}
os.println(getTransformedString(
// general transformation
getTransform(),
// general clip
getClippedString(
getTransformedString(
// text offset
new AffineTransform(1, 0, 0, 1, x, y),
getTransformedString(
// font transformation and text
getFont().getTransform(),
""
// text
+ str
+ "")))));
}
/**
* Creates the properties list for the given font.
* Family, size, bold italic, underline and strikethrough are converted.
* {@link java.awt.font.TextAttribute#SUPERSCRIPT}
* is handled by {@link java.awt.Font#getTransform()}
*
* @return properties in svg style for the font
* @param font Font to
*/
private Properties getFontProperties(Font font) {
Properties result = new Properties();
// attribute for font properties
Map /**/ attributes = FontUtilities.getAttributes(font);
// dialog.bold -> Helvetica with TextAttribute.WEIGHT_BOLD
SVGFontTable.normalize(attributes);
// family
result.put("font-family", attributes.get(TextAttribute.FAMILY));
// weight
if (TextAttribute.WEIGHT_BOLD.equals(attributes.get(TextAttribute.WEIGHT))) {
result.put("font-weight", "bold");
} else {
result.put("font-weight", "normal");
}
// posture
if (TextAttribute.POSTURE_OBLIQUE.equals(attributes.get(TextAttribute.POSTURE))) {
result.put("font-style", "italic");
} else {
result.put("font-style", "normal");
}
Object ul = attributes.get(TextAttribute.UNDERLINE);
if (ul != null) {
// underline style, only supported by CSS 3
if (TextAttribute.UNDERLINE_LOW_DOTTED.equals(ul)) {
result.put("text-underline-style", "dotted");
} else if (TextAttribute.UNDERLINE_LOW_DASHED.equals(ul)) {
result.put("text-underline-style", "dashed");
} else if (TextAttribute.UNDERLINE_ON.equals(ul)) {
result.put("text-underline-style", "solid");
}
// the underline itself, supported by CSS 2
result.put("text-decoration", "underline");
}
if (attributes.get(TextAttribute.STRIKETHROUGH) != null) {
// is the property allready witten?
if (ul == null) {
result.put("text-decoration", "underline, line-through");
} else {
result.put("text-decoration", "line-through");
}
}
Float size = (Float) attributes.get(TextAttribute.SIZE);
result.put("font-size", fixedPrecision(size.floatValue()));
return result;
}
/*
* ================================================================================ |
* 6. Transformations
* ================================================================================
*/
protected void writeTransform(AffineTransform transform) throws IOException {
// written when needed
}
protected void writeSetTransform(AffineTransform transform)
throws IOException {
// written when needed
}
/*
* ================================================================================ |
* 7. Clipping
* ================================================================================
*/
protected void writeClip(Shape s) throws IOException {
// written when needed
}
protected void writeSetClip(Shape s) throws IOException {
// written when needed
}
/*
* ================================================================================ |
* 8. Graphics State
* ================================================================================
*/
/* 8.1. stroke/linewidth */
protected void writeWidth(float width) throws IOException {
// written when needed
}
protected void writeCap(int cap) throws IOException {
// Written when needed
}
protected void writeJoin(int join) throws IOException {
// written when needed
}
protected void writeMiterLimit(float limit) throws IOException {
// written when needed
}
protected void writeDash(float[] dash, float phase) throws IOException {
// written when needed
}
/**
* return the style tag for the stroke
*
* @param s
* Stroke to convert
* @param all
* all attributes (not only the differences to defaultStroke) are
* handled
* @return corresponding style string
*/
private Properties getStrokeProperties(Stroke s, boolean all) {
Properties result = new Properties();
// only BasisStrokes are written
if (!(s instanceof BasicStroke)) {
return result;
}
BasicStroke stroke = (BasicStroke) s;
// append linecap
if (all || (stroke.getEndCap() != defaultStroke.getEndCap())) {
// append cap
switch (stroke.getEndCap()) {
default:
case BasicStroke.CAP_BUTT:
result.put("stroke-linecap", "butt");
break;
case BasicStroke.CAP_ROUND:
result.put("stroke-linecap", "round");
break;
case BasicStroke.CAP_SQUARE:
result.put("stroke-linecap", "square");
break;
}
}
// append dasharray
if (all
|| !Arrays.equals(stroke.getDashArray(), defaultStroke
.getDashArray())) {
if (stroke.getDashArray() != null
&& stroke.getDashArray().length > 0) {
StringBuffer array = new StringBuffer();
for (int i = 0; i < stroke.getDashArray().length; i++) {
if (i > 0) {
array.append(",");
}
// SVG does not allow dash entry to be zero (Firefox 2.0).
float dash = stroke.getDashArray()[i];
array.append(fixedPrecision(dash > 0 ? dash : 0.1));
}
result.put("stroke-dasharray", array.toString());
} else {
result.put("stroke-dasharray", "none");
}
}
if (all || (stroke.getDashPhase() != defaultStroke.getDashPhase())) {
result.put("stroke-dashoffset", fixedPrecision(stroke.getDashPhase()));
}
// append meter limit
if (all || (stroke.getMiterLimit() != defaultStroke.getMiterLimit())) {
result.put("stroke-miterlimit", fixedPrecision(stroke.getMiterLimit()));
}
// append join
if (all || (stroke.getLineJoin() != defaultStroke.getLineJoin())) {
switch (stroke.getLineJoin()) {
default:
case BasicStroke.JOIN_MITER:
result.put("stroke-linejoin", "miter");
break;
case BasicStroke.JOIN_ROUND:
result.put("stroke-linejoin", "round");
break;
case BasicStroke.JOIN_BEVEL:
result.put("stroke-linejoin", "bevel");
break;
}
}
// append linewidth
if (all || (stroke.getLineWidth() != defaultStroke.getLineWidth())) {
// width of 0 means thinnest line, which does not exist in SVG
if (stroke.getLineWidth() == 0) {
result.put("stroke-width", fixedPrecision(0.000001f));
} else {
result.put("stroke-width", fixedPrecision(stroke.getLineWidth()));
}
}
return result;
}
/* 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);
Point2D p1 = paint.getPoint1();
Point2D p2 = paint.getPoint2();
os.println("");
os.print(" ");
os.println(" ");
os.println(" ");
os.println(" ");
os.println("");
}
// create style
Properties style = new Properties();
style.put("stroke", hexColor(getPaint()));
// write style
os.print("");
// close color later
closeTags.push(" ");
}
protected void writePaint(TexturePaint paint) throws IOException {
// written when needed
}
protected void writePaint(Paint p) throws IOException {
// written when needed
}
/* 8.3. font */
protected void writeFont(Font font) throws IOException {
// written when needed
}
/*
* ================================================================================ |
* 9. Auxiliary
* ================================================================================
*/
public GraphicsConfiguration getDeviceConfiguration() {
writeWarning(getClass() + ": getDeviceConfiguration() not implemented.");
return null;
}
public void writeComment(String s) throws IOException {
os.println("");
}
public String toString() {
return "SVGGraphics2D";
}
/*
* ================================================================================ |
* 10. Private/Utility Methos
* ================================================================================
*/
/**
* Encapsulates a SVG-Tag by the given transformation matrix
*
* @param t
* Transformation
* @param s
* SVG-Tag
*/
private String getTransformedString(AffineTransform t, String s) {
StringBuffer result = new StringBuffer();
if (t != null && !t.isIdentity()) {
result.append("\n");
}
result.append(s);
if (t != null && !t.isIdentity()) {
result.append("\n ");
}
return result.toString();
}
/**
* Encapsulates a SVG-Tag by the current clipping area matrix
*
* @param s SVG-Tag
* @return SVG Tag encapsulated by the current clip
*/
private String getClippedString(String s) {
StringBuffer result = new StringBuffer();
// clipping
if (isProperty(CLIP) && getClip() != null) {
// SVG uses unique lip numbers, don't reset allways increment them
clipNumber.set(clipNumber.getInt() + 1);
// define clip
result.append("\n ");
result.append(getPath(getClip().getPathIterator(null)));
result.append("\n\n");
// use clip
result.append("\n");
}
// append the string
result.append(s);
// close clipping
if (isProperty(CLIP) && getClip() != null) {
result.append("\n ");
}
return result.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) + ")";
}
protected static String getPathContent(PathIterator path) {
StringBuffer result = new StringBuffer();
double[] coords = new double[6];
result.append("d=\"");
while (!path.isDone()) {
int segType = path.currentSegment(coords);
switch (segType) {
case PathIterator.SEG_MOVETO:
result.append("M ");
result.append(fixedPrecision(coords[0]));
result.append(" ");
result.append(fixedPrecision(coords[1]));
break;
case PathIterator.SEG_LINETO:
result.append("L ");
result.append(fixedPrecision(coords[0]));
result.append(" ");
result.append(fixedPrecision(coords[1]));
break;
case PathIterator.SEG_CUBICTO:
result.append("C ");
result.append(fixedPrecision(coords[0]));
result.append(" ");
result.append(fixedPrecision(coords[1]));
result.append(" ");
result.append(fixedPrecision(coords[2]));
result.append(" ");
result.append(fixedPrecision(coords[3]));
result.append(" ");
result.append(fixedPrecision(coords[4]));
result.append(" ");
result.append(fixedPrecision(coords[5]));
break;
case PathIterator.SEG_QUADTO:
result.append("Q ");
result.append(fixedPrecision(coords[0]));
result.append(" ");
result.append(fixedPrecision(coords[1]));
result.append(" ");
result.append(fixedPrecision(coords[2]));
result.append(" ");
result.append(fixedPrecision(coords[3]));
break;
case PathIterator.SEG_CLOSE:
result.append("z");
break;
}
// Move to the next segment.
path.next();
// Not needed but makes the output readable
if (!path.isDone()) {
result.append(" ");
}
}
result.append("\"");
return result.toString();
}
protected String getPath(PathIterator path) {
StringBuffer result = new StringBuffer();
result.append("");
return result.toString();
}
/**
* For a given "key -> value" property set the
* method creates
* style="key1:value1;key2:value2;" or
* key2="value2" key2="value2" depending on
* {@link #STYLABLE}.
*
* @param style properties to convert
* @return String
*/
private String style(Properties style) {
if (style == null || style.isEmpty()) {
return "";
}
StringBuffer result = new StringBuffer();
boolean styleable = isProperty(STYLABLE);
// embed everything in a "style" attribute
if (styleable) {
result.append("style=\"");
}
Enumeration keys = style.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = style.getProperty(key);
result.append(key);
if (styleable) {
result.append(":");
result.append(value);
result.append(";");
} else {
result.append("=\"");
result.append(value);
result.append("\"");
if (keys.hasMoreElements()) {
result.append(" ");
}
}
}
// close the style attribute
if (styleable) {
result.append("\"");
}
return result.toString();
}
/**
* for fixedPrecision(double d), SVG does not understand "1E-7"
* we have to use ".0000007" instead
*/
private static DecimalFormat scientific = new DecimalFormat(
"#.####################",
new DecimalFormatSymbols(Locale.US));
/**
* converts the double value to a representing string
*
* @param d double value to convert
* @return same as string
*/
public static String fixedPrecision(double d) {
return scientific.format(d);
}
protected PrintWriter getOutputStream() {
return os;
}
}
src/site/ 0000755 0001750 0001750 00000000000 11323173777 011650 5 ustar user03 user03 src/site/apt/ 0000755 0001750 0001750 00000000000 11323173777 012434 5 ustar user03 user03 src/site/site.xml 0000644 0001750 0001750 00000001737 11323173777 013346 0 ustar user03 user03
FreeHEP GraphicsIO SVGhttp://java.freehep.org/mvn/freehep-graphicsio-svgFreeHEPhttp://java.freehep.org/images/sm-freehep.gifhttp://java.freehep.org/