
org.freehep.graphicsio.swf.SWFGraphics2D Maven / Gradle / Ivy
The newest version!
// Copyright 2000-2006 FreeHEP
package org.freehep.graphicsio.swf;
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.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Vector;
import org.freehep.graphics2d.PrintColor;
import org.freehep.graphics2d.VectorGraphics;
import org.freehep.graphics2d.font.FontEncoder;
import org.freehep.graphicsbase.util.UserProperties;
import org.freehep.graphicsbase.util.Value;
import org.freehep.graphicsio.AbstractVectorGraphicsIO;
import org.freehep.graphicsio.ImageConstants;
import org.freehep.graphicsio.PageConstants;
import org.freehep.graphicsio.swf.DefineText.Record;
/**
* SWF Graphics 2D driver.
*
* @author Mark Donszelmann
* @version $Id: freehep-graphicsio-swf/src/main/java/org/freehep/graphicsio/swf/SWFGraphics2D.java 9fd14b4f13ca 2006/11/28 00:22:04 duns $
*/
public class SWFGraphics2D extends AbstractVectorGraphicsIO implements
SWFConstants {
private static final int SWF_VERSION = 8; // for cyclic gradients
private static final String rootKey = SWFGraphics2D.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 WRITE_IMAGES_AS = rootKey + "."
+ ImageConstants.WRITE_IMAGES_AS;
private static final UserProperties defaultProperties = new UserProperties();
static {
defaultProperties.setProperty(TRANSPARENT, true);
defaultProperties.setProperty(BACKGROUND, false);
defaultProperties.setProperty(BACKGROUND_COLOR, Color.GRAY);
defaultProperties.setProperty(WRITE_IMAGES_AS, ImageConstants.SMALLEST);
// Seems not to work yet...
defaultProperties.setProperty(CLIP, false);
defaultProperties.setProperty(TEXT_AS_SHAPES, true);
}
public static Properties getDefaultProperties() {
return defaultProperties;
}
public static void setDefaultProperties(Properties newProperties) {
defaultProperties.setProperties(newProperties);
}
public final static String version = "$Revision$";
private OutputStream ros;
private SWFOutputStream os;
private Value id;
private Value depth;
private static final float frameRate = 20.0f;
private boolean compress = true;
private LineStyleArray lineStyles;
private FillStyleArray fillStyles;
// keeps the color for text, which cannot be a paint
private Color textColor;
// keeps the last clip which has not been written yet, its ID and depth
private Shape unwrittenClip = null;
private AffineTransform clipTransform = null;
private int clipID, clipDepthID, showClipID, showClipDepthID;
// for rendering shapes
boolean fillStroke;
// for debugging
private static final boolean showBounds = false;
/*
* ================================================================================
* Table of Contents: ------------------ 1. Constructors & Factory Methods
* 2. Document Settings 3. Header, Trailer, Multipage & Comments 3.1 Header &
* Trailer 3.2 MultipageDocument methods 4. Create & Dispose 5. Drawing
* Methods 5.1. shapes (draw/fill) 5.1.1. lines, rectangles, round
* rectangles 5.1.2. polylines, polygons 5.1.3. ovals, arcs 5.1.4. shapes
* 5.2. Images 5.3. Strings 6. Transformations 7. Clipping 8. Graphics State /
* Settings 8.1. stroke/linewidth 8.2. paint/color 8.3. font 8.4. rendering
* hints 9. Auxiliary 10. Private/Utility Methos
* ================================================================================
*/
/*
* ================================================================================
* 1. Constructors & Factory Methods
* ================================================================================
*/
public SWFGraphics2D(File file, Dimension size)
throws FileNotFoundException {
this(new FileOutputStream(file), size);
}
public SWFGraphics2D(File file, Component component)
throws FileNotFoundException {
this(new FileOutputStream(file), component);
}
public SWFGraphics2D(OutputStream os, Dimension size) {
super(size, true);
init(os);
}
public SWFGraphics2D(OutputStream os, Component component) {
super(component, true);
init(os);
}
private void init(OutputStream os) {
initProperties(getDefaultProperties());
ros = os;
id = new Value().set(1);
depth = new Value().set(1);
textColor = getColor();
fillStroke = true;
}
protected SWFGraphics2D(SWFGraphics2D graphics, boolean doRestoreOnDispose) {
super(graphics, doRestoreOnDispose);
// Create a graphics context from a given graphics context.
// This constructor is used by the system to clone a given graphics
// context.
// doRestoreOnDispose is used to call writeGraphicsRestore(),
// when the graphics context is being disposed off.
os = graphics.os;
id = graphics.id;
depth = graphics.depth;
lineStyles = new LineStyleArray();
lineStyles.add(graphics.lineStyles.get(0));
if (showBounds || isProperty(CLIP)) {
lineStyles.add(graphics.lineStyles.get(1));
lineStyles.add(graphics.lineStyles.get(2));
}
fillStyles = new FillStyleArray();
fillStyles.add(graphics.fillStyles.get(0));
textColor = graphics.textColor;
fillStroke = graphics.fillStroke;
}
/*
* ================================================================================ |
* 2. Document Settings
* ================================================================================
*/
/*
* ================================================================================ |
* 3. Header, Trailer, Multipage & Comments
* ================================================================================
*/
/* 3.1 Header & Trailer */
public void writeHeader() throws IOException {
os = new SWFOutputStream(new BufferedOutputStream(ros), SWF_VERSION, getSize(),
frameRate, compress);
String description = getCreator() + ":" + getClass().getName();
if (!isDeviceIndependent()) {
description += " " + version.substring(1, version.length() - 1);
}
// FIXME no way to write out a comment...
}
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 {
os.writeTag(new ShowFrame());
os.writeTag(new End());
}
public void closeStream() throws IOException {
os.close();
}
/* 3.2 MultipageDocument methods */
/*
* ================================================================================
* 4. Create & Dispose
* ================================================================================
*/
public Graphics create() {
// Create a new graphics context from the current one.
try {
// Save the current context for restore later.
writeGraphicsSave();
} catch (IOException e) {
handleException(e);
}
// The correct graphics context should be created.
return new SWFGraphics2D(this, true);
}
public Graphics create(double x, double y, double width, double height) {
// Create a new graphics context from the current one.
try {
// Save the current context for restore later.
writeGraphicsSave();
} catch (IOException e) {
handleException(e);
}
// The correct graphics context should be created.
VectorGraphics graphics = new SWFGraphics2D(this, true);
graphics.translate(x, y);
graphics.clipRect(0, 0, width, height);
return graphics;
}
protected void writeGraphicsSave() throws IOException {
}
protected void writeGraphicsRestore() throws IOException {
popStreamAndWriteClip();
}
/*
* ================================================================================ |
* 5. Drawing Methods
* ================================================================================
*/
/* 5.1.4. shapes */
public void draw(Shape shape) {
Shape strokedShape = getStroke().createStrokedShape(shape);
if (fillStroke) {
// do this for dashed lines and non-round linejoins and linecaps
fill(new Area(strokedShape));
return;
}
try {
Rectangle2D bounds = strokedShape.getBounds2D();
SWFShape swfShape = createShape(shape, 1, 0, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfShape));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
if (showBounds) {
SWFShape swfBounds = createShape(bounds, 2, 0, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfBounds));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
}
} catch (IOException e) {
handleException(e);
}
}
public void fill(Shape shape) {
try {
Rectangle2D bounds = new BasicStroke().createStrokedShape(shape)
.getBounds2D();
SWFShape swfShape = createShape(shape, 0, 1, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfShape));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
if (showBounds) {
SWFShape swfBounds = createShape(bounds, 2, 0, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfBounds));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
}
boolean eo = SWFPathConstructor.isEvenOdd(shape);
if (!eo) {
writeWarning(getClass()
+ ": cannot fill using non-zero winding rule, used even-odd instead.");
}
} catch (IOException e) {
handleException(e);
}
}
public void fillAndDraw(Shape shape, Color fillColor) {
try {
setFillColor(fillColor);
fill(shape);
setFillColor(getColor());
draw(shape);
} catch (IOException e) {
handleException(e);
}
}
/* 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.");
// Mostly unimplemented.
}
protected void writeImage(RenderedImage image, AffineTransform xform,
Color bkg) throws IOException {
// define image
int imageID = id.getInt();
os.writeTag(getImageTag(imageID, image, bkg));
id.set(id.getInt() + 1);
// define shape for image
Shape shape = xform.createTransformedShape(new Rectangle(0, 0, image
.getWidth(), image.getHeight()));
AffineTransform imageTransform = new AffineTransform(TWIPS, 0, 0,
TWIPS, 0, 0);
xform.concatenate(imageTransform);
// create fill from shape
SWFShape imageShape = createShape(shape, 0, 0, 1);
FillStyleArray imageFill = new FillStyleArray();
imageFill.add(new FillStyle(imageID, false, xform));
LineStyleArray imageLine = new LineStyleArray();
Rectangle bounds = shape.getBounds();
os.writeTag(new DefineShape4(id.getInt(), bounds, imageFill, imageLine,
imageShape));
// place image
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
if (showBounds) {
SWFShape swfBounds = createShape(bounds, 2, 0, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfBounds));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
}
}
private final static Properties replaceFonts = new Properties();
static {
replaceFonts.setProperty("Symbol", "Serif");
replaceFonts.setProperty("ZapfDingbats", "Serif");
}
/* 5.3. Strings */
protected void writeString(String string, double x, double y)
throws IOException {
// for special fonts (Symbol, ZapfDingbats) we choose a standard font
// and
// encode using unicode.
String fontName = getFont().getName();
string = FontEncoder.getEncodedString(string, fontName);
fontName = replaceFonts.getProperty(fontName, null);
Font font = (fontName == null) ? getFont() : new Font(fontName,
getFont().getStyle(), getFont().getSize());
Font font1024 = font.deriveFont((float) (1024.0 / TWIPS));
GlyphVector glyphs = font1024.createGlyphVector(getFontRenderContext(),
string);
Rectangle2D bounds = font.createGlyphVector(getFontRenderContext(),
string).getVisualBounds();
bounds.setRect(bounds.getX() + x, bounds.getY() + y, bounds.getWidth(),
bounds.getHeight());
// define the Glyphs
int fontID = id.getInt();
DefineFont swfFont = new DefineFont(fontID);
id.set(id.getInt() + 1);
// define Text
Vector text = new Vector();
DefineText2.RecordType1 record1 = new DefineText2.RecordType1(fontID,
textColor, (int) (x * TWIPS), (int) (y * TWIPS), (int) (font
.getSize2D() * TWIPS));
DefineText2.RecordType0 record0 = new DefineText2.RecordType0();
text.add(record1);
text.add(record0);
int textID = id.getInt();
DefineText2 swfText = new DefineText2(textID, bounds,
new AffineTransform(), text);
id.set(id.getInt() + 1);
// hook font and text
for (int i = 0; i < glyphs.getNumGlyphs(); i++) {
// add filled shapes to font
swfFont.add(createShape(glyphs.getGlyphOutline(i), -1, 1, -1));
// add glyphs to text
// float advance = glyphs.getGlyphMetrics(i).getAdvance();
record0.add(new DefineText2.GlyphEntry(i, 0)); // (int)(advance*TWIPS)));
}
// write font and text
os.writeTag(swfFont);
os.writeTag(swfText);
// place String
os.writeTag(new PlaceObject2(textID, depth.getInt(), getTransform()));
depth.set(depth.getInt() + 1);
if (showBounds) {
SWFShape swfBounds = createShape(bounds, 2, 0, -1);
os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
lineStyles, swfBounds));
os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
getTransform()));
id.set(id.getInt() + 1);
depth.set(depth.getInt() + 1);
}
}
/*
* ================================================================================ |
* 6. Transformations
* ================================================================================
*/
protected void writeTransform(AffineTransform t) throws IOException {
// Transforms written when needed
}
protected void writeSetTransform(AffineTransform t) throws IOException {
// Transforms written when needed
}
/*
* ================================================================================ |
* 7. Clipping
* ================================================================================
*/
protected void writeSetClip(Shape s) throws IOException {
writeClip(s);
}
protected void writeClip(Shape s) throws IOException {
// we assume we can write nested clips
popStreamAndWriteClip();
unwrittenClip = s;
clipTransform = (unwrittenClip != null) ? new AffineTransform(
getTransform()) : null;
if (unwrittenClip != null) {
// reserve IDs
clipID = id.getInt();
id.set(id.getInt() + 1);
clipDepthID = depth.getInt();
depth.set(depth.getInt() + 1);
if (isProperty(CLIP)) {
showClipID = id.getInt();
id.set(id.getInt() + 1);
showClipDepthID = depth.getInt();
depth.set(depth.getInt() + 1);
}
os.pushBuffer();
} else {
clipID = 0;
clipDepthID = 0;
if (isProperty(CLIP)) {
showClipID = 0;
showClipDepthID = 0;
}
}
}
/*
* ================================================================================ |
* 8. Graphics State
* ================================================================================
*/
protected void writeStroke(Stroke stroke) throws IOException {
fillStroke = true;
if (stroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) stroke;
setPen(bs, getColor());
if (bs.getLineWidth() == 0) {
fillStroke = false;
}
}
}
/* 8.2. paint/color */
public void setPaintMode() {
writeWarning(getClass() + ": setPaintMode() not implemented.");
// Mostly unimplemented.
}
public void setXORMode(Color c1) {
writeWarning(getClass() + ": setXORMode(Color) not implemented.");
// Mostly unimplemented.
}
protected void writePaint(Color p) throws IOException {
setPen((BasicStroke) getStroke(), p);
setFillColor(p);
textColor = PrintColor.createPrintColor(p);
}
protected void writePaint(GradientPaint p) throws IOException {
Gradient[] gradient = new Gradient[2];
gradient[0] = new Gradient(0, p.getColor1());
gradient[1] = new Gradient(255, p.getColor2());
double x0 = p.getPoint1().getX();
double y0 = p.getPoint1().getY();
double dx = p.getPoint2().getX() - x0;
double dy = p.getPoint2().getY() - y0;
double scale = p.getPoint1().distance(p.getPoint2()) * TWIPS / 32768;
double angle = Math.atan2(dy, dx);
AffineTransform transform = new AffineTransform(scale, 0, 0, scale, dx
/ 2 + x0, dy / 2 + y0);
transform.rotate(angle);
int spreadMode = p.isCyclic() ? FillStyle.SPREAD_MODE_REFLECT : FillStyle.SPREAD_MODE_PAD;
fillStyles = new FillStyleArray();
fillStyles.add(new FillStyle(gradient, FillStyle.LINEAR_GRADIENT, spreadMode, FillStyle.INTERPOLATION_MODE_NORMAL_RGB, 0.0f, transform));
textColor = PrintColor.mixColor(p.getColor1(), p.getColor2());
}
protected void writePaint(TexturePaint p) throws IOException {
// define image
BufferedImage image = p.getImage();
int imageID = id.getInt();
os.writeTag(getImageTag(imageID, image, null));
id.set(id.getInt() + 1);
// setup image as fill
Rectangle2D a = p.getAnchorRect();
double sx = a.getWidth() / image.getWidth();
double sy = a.getHeight() / image.getHeight();
fillStyles = new FillStyleArray();
fillStyles.add(new FillStyle(imageID, true, new AffineTransform(sx
* TWIPS, 0, 0, sy * TWIPS, a.getX(), a.getY())));
textColor = PrintColor.black;
}
protected void writePaint(Paint p) throws IOException {
writeWarning(getClass() + ": writePaint(Paint) not implemented for "
+ p.getClass());
// Write out the paint.
writePaint(Color.BLACK);
}
/* 8.3. font */
protected void writeFont(Font font) throws IOException {
// written when needed
}
/* 8.4. rendering hints */
/*
* ================================================================================ |
* 9. Auxiliary
* ================================================================================
*/
public GraphicsConfiguration getDeviceConfiguration() {
writeWarning(getClass() + ": getDeviceConfiguration() not implemented.");
// Mostly unimplemented
return null;
}
public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
writeWarning(getClass()
+ ": hit(Rectangle, Shape, boolean) not implemented.");
// Mostly unimplemented
return false;
}
public void writeComment(String comment) throws IOException {
writeWarning(getClass() + ": writeComment(String) not implemented.");
// Write out the comment.
}
public String toString() {
return "SWFGraphics2D";
}
/*
* Private methods
*/
SWFShape createShape(Shape shape, int stroke, int fill0, int fill1)
throws IOException {
// use a resolution compatible with the current transform
AffineTransform t = getTransform();
double resolution = 0.5 / (TWIPS * Math.min(t.getScaleX(), t
.getScaleY()));
Vector path = new Vector();
SWFPathConstructor pc = new SWFPathConstructor(path, stroke, fill0,
fill1, resolution);
pc.addPath(shape);
return new SWFShape(path);
}
private void setPen(BasicStroke stroke, Color color) throws IOException {
lineStyles = new LineStyleArray();
lineStyles.add(new LineStyle((int) (stroke.getLineWidth() * TWIPS), getPrintColor(color)));
if (showBounds || isProperty(CLIP)) {
lineStyles.add(new LineStyle(TWIPS, Color.cyan));
lineStyles.add(new LineStyle(TWIPS, Color.orange));
}
}
private void setFillColor(Color color) throws IOException {
fillStyles = new FillStyleArray();
fillStyles.add(new FillStyle(getPrintColor(color)));
}
private void popStreamAndWriteClip() throws IOException {
if (unwrittenClip == null)
return;
if ((clipID == 0) || (clipDepthID == 0)) {
System.err
.println("SWFGraphics2D: internal error, invalid clipID or clipDepthID");
return;
}
// pop buffer
os.popBuffer();
Rectangle2D bounds = unwrittenClip.getBounds2D();
SWFShape clipShape = createShape(unwrittenClip, 0, 1, -1);
os.writeTag(new DefineShape4(clipID, bounds, fillStyles, lineStyles,
clipShape));
int clipDepth = depth.getInt() - 1;
os.writeTag(new PlaceObject2(clipID, clipDepthID, clipTransform,
clipDepth));
// System.out.println("Clip "+clipID+" at depth "+clipDepthID+" for
// clipDepth "+clipDepth+": "+unwrittenClip);
if (isProperty(CLIP)) {
if ((showClipID == 0) || (showClipDepthID == 0)) {
System.err
.println("SWFGraphics2D: internal error, invalid showClipID or showClipDepthID");
return;
}
SWFShape swfBounds = createShape(bounds, 3, 0, -1);
os.writeTag(new DefineShape4(showClipID, bounds, fillStyles,
lineStyles, swfBounds));
os.writeTag(new PlaceObject(showClipID, showClipDepthID,
clipTransform));
}
// append popped buffer;
os.append();
}
private SWFTag getImageTag(int imageID, RenderedImage image, Color bkg)
throws IOException {
String writeAs = getProperty(WRITE_IMAGES_AS);
DefineBitsLossless2 flateTag = null;
if (writeAs.equals(ImageConstants.ZLIB)
|| writeAs.equals(ImageConstants.SMALLEST)) {
flateTag = new DefineBitsLossless2(imageID, image, bkg);
}
DefineBitsJPEG3 jpgTag = null;
if (writeAs.equals(ImageConstants.JPG)
|| writeAs.equals(ImageConstants.SMALLEST)) {
jpgTag = new DefineBitsJPEG3(imageID, image, bkg, new Properties());
}
SWFTag imageTag;
if (writeAs.equals(ImageConstants.ZLIB)) {
imageTag = flateTag;
} else if (writeAs.equals(ImageConstants.JPG)) {
imageTag = jpgTag;
} else {
imageTag = (jpgTag.getLength() < 0.5 * flateTag.getLength()) ? (SWFTag) jpgTag
: (SWFTag) flateTag;
}
return imageTag;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy