// Copyright 2006, FreeHEP package org.freehep.graphicsio.emf; 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.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Area; 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 org.freehep.graphics2d.VectorGraphics; import org.freehep.graphicsio.AbstractVectorGraphicsIO; import org.freehep.graphicsio.PageConstants; import org.freehep.graphicsio.emf.gdi.EOF; import org.freehep.graphicsio.emf.gdiplus.Clear; import org.freehep.graphicsio.emf.gdiplus.DrawImage; import org.freehep.graphicsio.emf.gdiplus.DrawPath; import org.freehep.graphicsio.emf.gdiplus.EndOfFile; import org.freehep.graphicsio.emf.gdiplus.FillPath; import org.freehep.graphicsio.emf.gdiplus.GDIPlusObject; import org.freehep.graphicsio.emf.gdiplus.Header; import org.freehep.graphicsio.emf.gdiplus.MultiplyWorldTransform; import org.freehep.graphicsio.emf.gdiplus.ResetClip; import org.freehep.graphicsio.emf.gdiplus.Restore; import org.freehep.graphicsio.emf.gdiplus.Save; import org.freehep.graphicsio.emf.gdiplus.SetAntiAliasMode; import org.freehep.graphicsio.emf.gdiplus.SetClipPath; import org.freehep.graphicsio.emf.gdiplus.SetWorldTransform; import org.freehep.util.UserProperties; import org.freehep.util.Value; /** * Converts calls to Graphics2D to EMF+ Format. * * @author Mark Donszelmann * @version $Id: EMFPlusGraphics2D.java,v 1.1 2009-08-17 21:44:45 murkle Exp $ */ public class EMFPlusGraphics2D extends AbstractVectorGraphicsIO { public static final String version = "$Revision: 1.1 $"; private OutputStream ros; private EMFOutputStream os; private Rectangle imageBounds; // FIXME do we need this? private EMFHandleManager handleManager; private Value containerIndex; private Paint restorePaint; private static final String rootKey = EMFPlusGraphics2D.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; private static final UserProperties defaultProperties = new UserProperties(); static { defaultProperties.setProperty(TRANSPARENT, true); defaultProperties.setProperty(BACKGROUND, false); defaultProperties.setProperty(BACKGROUND_COLOR, Color.GRAY); defaultProperties.setProperty(CLIP, true); defaultProperties.setProperty(TEXT_AS_SHAPES, true); } public static Properties getDefaultProperties() { return defaultProperties; } // FIXME @Override public FontRenderContext getFontRenderContext() { // NOTE: not sure? return new FontRenderContext(new AffineTransform(-1, 0, 0, 1, 0, 0), true, true); } public static void setDefaultProperties(Properties newProperties) { defaultProperties.setProperties(newProperties); } public EMFPlusGraphics2D(File file, Dimension size) throws FileNotFoundException { this(new FileOutputStream(file), size); } public EMFPlusGraphics2D(File file, Component component) throws FileNotFoundException { this(new FileOutputStream(file), component); } public EMFPlusGraphics2D(OutputStream os, Dimension size) { super(size, false); this.imageBounds = new Rectangle(0, 0, size.width, size.height); init(os); } public EMFPlusGraphics2D(OutputStream os, Component component) { super(component, false); this.imageBounds = new Rectangle(0, 0, getSize().width, getSize().height); init(os); } private void init(OutputStream os) { handleManager = new EMFHandleManager(); ros = os; containerIndex = new Value(); containerIndex.set(0); initProperties(defaultProperties); } protected EMFPlusGraphics2D(EMFPlusGraphics2D 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; imageBounds = graphics.imageBounds; handleManager = graphics.handleManager; containerIndex = graphics.containerIndex; restorePaint = graphics.getPaint(); } @Override public void writeHeader() throws IOException { ros = new BufferedOutputStream(ros); // GeoGebra: disabled // Dimension device = isDeviceIndependent() ? new Dimension(1024, 768) // : Toolkit.getDefaultToolkit().getScreenSize(); Dimension device = new Dimension(1024, 768); String producer = getClass().getName(); if (!isDeviceIndependent()) { producer += " " + version.substring(1, version.length() - 1); } os = new EMFOutputStream(ros, imageBounds, handleManager, getCreator(), producer, device, 0x4001); os.writeTag(new Header()); // leave this on since we do text as shapes. os.writeTag(new SetAntiAliasMode(true)); // Point orig = new Point(imageBounds.x, imageBounds.y); // Dimension size = new Dimension(imageBounds.width, // imageBounds.height); // FIXME check what to write // os.writeTag(new SetMapMode(MM_ANISOTROPIC)); // os.writeTag(new SetWindowOrgEx(orig)); // os.writeTag(new SetWindowExtEx(size)); // os.writeTag(new SetViewportOrgEx(orig)); // os.writeTag(new SetViewportExtEx(size)); // os.writeTag(new SetTextAlign(TA_BASELINE)); // os.writeTag(new SetTextColor(getColor())); // os.writeTag(new SetPolyFillMode(EMFConstants.WINDING)); } @Override public void writeBackground() throws IOException { if (isProperty(TRANSPARENT)) { setBackground(null); // FIXME // os.writeTag(new Clear(new Color(0xFF, 0xFF, 0xFF, 0x00))); } else if (isProperty(BACKGROUND)) { setBackground(getPropertyColor(BACKGROUND_COLOR)); os.writeTag(new Clear(getBackground())); } else { setBackground(getComponent() != null ? getComponent().getBackground() : Color.WHITE); os.writeTag(new Clear(getBackground())); } } @Override public void writeTrailer() throws IOException { // delete any remaining objects for (;;) { int handle = handleManager.highestHandleInUse(); if (handle < 0) { break; } // os.writeTag(new DeleteObject(handle)); handleManager.freeHandle(handle); } os.writeTag(new EndOfFile()); os.writeTag(new EOF()); } @Override public void closeStream() throws IOException { os.close(); } @Override 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 EMFPlusGraphics2D(this, true); } @Override 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 EMFPlusGraphics2D(this, true); graphics.translate(x, y); graphics.clipRect(0, 0, width, height); return graphics; } @Override protected void writeGraphicsSave() throws IOException { os.writeTag(new Save(containerIndex.getInt())); containerIndex.set(containerIndex.getInt() + 1); } @Override protected void writeGraphicsRestore() throws IOException { containerIndex.set(containerIndex.getInt() - 1); os.writeTag(new Restore(containerIndex.getInt())); if (restorePaint != null) { writePaint(restorePaint); } } @Override public void draw(Shape shape) { try { Stroke stroke = getStroke(); if ((stroke instanceof BasicStroke) && (((BasicStroke) stroke).getLineWidth() == 0)) { os.writeTag(new GDIPlusObject(1, shape, false)); os.writeTag( new GDIPlusObject(2, new BasicStroke(0), getPaint())); os.writeTag(new DrawPath(1, 2)); } else { Shape strokedShape = getStroke().createStrokedShape(shape); fill(new Area(strokedShape)); } } catch (IOException e) { handleException(e); } } @Override public void fill(Shape shape) { try { os.writeTag(new GDIPlusObject(1, shape, false)); os.writeTag(new FillPath(1, 0)); } catch (IOException e) { handleException(e); } } @Override 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. } @Override protected void writeImage(RenderedImage image, AffineTransform xform, Color bkg) throws IOException { // FIXME use BKG and xform writeGraphicsSave(); os.writeTag(new GDIPlusObject(5, image)); os.writeTag(new MultiplyWorldTransform(xform, true)); os.writeTag(new DrawImage(5, image)); writeGraphicsRestore(); } @Override protected void writeString(String string, double x, double y) throws IOException { // text is drawn as shapes } @Override protected void writeTransform(AffineTransform t) throws IOException { os.writeTag(new MultiplyWorldTransform(t, true)); } @Override protected void writeSetTransform(AffineTransform t) throws IOException { os.writeTag(new SetWorldTransform(t)); } @Override protected void writeClip(Shape s) throws IOException { os.writeTag(new GDIPlusObject(4, s, false)); os.writeTag(new SetClipPath(4, SetClipPath.INTERSECT)); } @Override protected void writeSetClip(Shape s) throws IOException { if (s != null) { os.writeTag(new GDIPlusObject(4, s, false)); os.writeTag(new SetClipPath(4, SetClipPath.REPLACE)); } else { os.writeTag(new ResetClip()); } } @Override protected void writeWidth(float width) throws IOException { // settings convert to shape } @Override protected void writeCap(int cap) throws IOException { // settings convert to shape } @Override protected void writeJoin(int join) throws IOException { // settings convert to shape } @Override protected void writeMiterLimit(float limit) throws IOException { // settings convert to shape } @Override protected void writeDash(float[] dash, float phase) throws IOException { // settings convert to shape } @Override public void setPaintMode() { writeWarning(getClass() + ": setPaintMode() not implemented."); // Mostly unimplemented. } @Override public void setXORMode(Color c1) { writeWarning(getClass() + ": setXORMode(Color) not implemented."); // Mostly unimplemented. } @Override protected void writePaint(Color p) throws IOException { os.writeTag(new GDIPlusObject(0, p)); } @Override protected void writePaint(GradientPaint p) throws IOException { os.writeTag(new GDIPlusObject(0, p)); } @Override protected void writePaint(TexturePaint p) throws IOException { os.writeTag(new GDIPlusObject(0, p)); } @Override protected void writePaint(Paint p) throws IOException { os.writeTag(new GDIPlusObject(0, p)); } @Override protected void writeFont(Font font) throws IOException { // text converts to shapes } @Override public GraphicsConfiguration getDeviceConfiguration() { writeWarning( getClass() + ": getDeviceConfiguration() not implemented."); // Mostly unimplemented return null; } @Override public boolean hit(Rectangle rect, Shape s, boolean onStroke) { writeWarning(getClass() + ": hit(Rectangle, Shape, boolean) not implemented."); // Mostly unimplemented return false; } @Override public void writeComment(String comment) throws IOException { writeWarning(getClass() + ": writeComment(String) not implemented."); // Write out the comment. } @Override protected void writeWarning(String string) { System.err.println(string); } @Override public String toString() { return "EMFPlusGraphics2D"; } }