// Copyright 2004-2006, FreeHEP package org.freehep.graphicsio.latex; 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.TexturePaint; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import org.freehep.graphicsio.AbstractVectorGraphicsIO; import org.freehep.util.ScientificFormat; /** * @author Andre Bach * @author Mark Donszelmann * @version $Id: LatexGraphics2D.java 8584 2006-08-10 23:06:37Z duns $ */ public class LatexGraphics2D extends AbstractVectorGraphicsIO { /* * ================================================================================ * 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 * ================================================================================ */ private PrintStream ps; private AffineTransform pageTrans = new AffineTransform(); private double componentHeight; private static double pageHeight; private static double pageWidth; private int numberClips = 0; private float width = 1.f; private float[] dash; private Color savedColor; private float savedWidth; private float[] savedDash; /* * ================================================================================ * 1. Constructors & Factory Methods * ================================================================================ */ public LatexGraphics2D(File file, Dimension size) throws FileNotFoundException { this(new FileOutputStream(file), size, true); } public LatexGraphics2D(File file, Component component) throws FileNotFoundException { this(new FileOutputStream(file), component, true); } public LatexGraphics2D(OutputStream os, Component component, boolean doRestoreOnDispose) { super(component, doRestoreOnDispose); init(os); componentHeight = component.getSize().getHeight(); pageWidth = component.getSize().getWidth(); } public LatexGraphics2D(OutputStream os, Dimension size, boolean doRestoreOnDispose) { super(size, doRestoreOnDispose); init(os); } private void init(OutputStream os) { ps = new PrintStream(os); } protected LatexGraphics2D(LatexGraphics2D graphics, boolean doRestoreOnDispose) { super(graphics, doRestoreOnDispose); ps = graphics.ps; } /* * ================================================================================ | * 2. Document Settings * ================================================================================ */ /* * ================================================================================ | * 3. Header, Trailer, Multipage & Comments * ================================================================================ */ /* 3.1 Header & Trailer */ public void writeHeader() throws IOException { ps.println("\\documentclass{article}"); ps.println("\\usepackage[noxcolor]{pstricks}"); ps.println("\\usepackage{pst-grad}"); ps.println("\\pagestyle{empty}"); ps.println("\\begin{document}"); ps.println("\\begin{pspicture}(5,20)(15,15)"); ps.println("\\psset{unit=1pt}"); // One thing that should be changed // for scaling. ps.println("% componentHeight = " + componentHeight); ps.println("% pageWidth = " + pageWidth); ps.println("% pageHeight = " + pageHeight); pageHeight = componentHeight; ps.println("% pageHeight = " + pageHeight); pageTrans.scale(1, -1); pageTrans.translate(0, -pageHeight); transform(pageTrans); } public void writeBackground() throws IOException { // writeWarning(getClass()+": writeBackground() not implemented."); // Write out the background to the output stream. ps.println("% writeBackground() called"); } public void writeTrailer() throws IOException { ps.println("\\end{pspicture}"); ps.println("\\end{document}"); } public void closeStream() throws IOException { ps.close(); } /* 3.2 MultipageDocument methods */ /* * ================================================================================ * 4. Create & Dispose * ================================================================================ */ public Graphics create() { ps.println("% create() called"); LatexGraphics2D g = new LatexGraphics2D(this, true); try { g.writeGraphicsSave(getColor(), width, dash); } catch (IOException e) { handleException(e); } return g; } public Graphics create(double x, double y, double width, double height) { ps.println("% create(x,y,width,height) called"); ps.println("% x = " + x); ps.println("% y = " + y); ps.println("% width = " + width); ps.println("% height = " + height); LatexGraphics2D g = new LatexGraphics2D(this, true); try { g.writeGraphicsSave(getColor(), width, dash); } catch (IOException e) { handleException(e); } g.clipRect(x, y, width, height); return g; } protected void writeGraphicsSave(Color c, double w, float[] d) throws IOException { savedColor = c; savedWidth = (float) w; savedDash = d; } protected void writeGraphicsSave() throws IOException { } protected void writeGraphicsRestore() throws IOException { while (numberClips > 0) { ps.println("\\endpsclip"); numberClips--; } // If the current color was written inside a clip statement, it will not // be // remembered outside of it, so the restore must always be written. if (/* savedColor != getColor() && */savedColor != null) writePaint(savedColor); /* if (savedWidth != width) */writeWidth(savedWidth); /* if (savedDash != dash) */writeDash(savedDash, 0.0f); } /* * ================================================================================ | * 5. Drawing Methods * ================================================================================ */ /* 5.1.4. shapes */ public void draw(Shape shape) { LatexPathConstructor pc = new LatexPathConstructor(ps); try { if (getStroke() instanceof BasicStroke) { ps.println("\\pscustom[fillstyle=none]{"); pc.addPath(shape, getTransform()); ps.println("}"); } else { ps.println("\\pscustom[linestyle=none]{"); pc.addPath(getStroke().createStrokedShape(shape), getTransform()); ps.println("}"); } } catch (IOException e) { handleException(e); } } public void fill(Shape shape) { LatexPathConstructor pc = new LatexPathConstructor(ps); try { ps.println("\\pscustom[linestyle=none]{"); pc.addPath(shape, getTransform()); ps.println("}"); } catch (IOException e) { handleException(e); } } public void fillAndDraw(Shape shape, Color fillColor) { LatexPathConstructor pc = new LatexPathConstructor(ps); try { ps.println("\\newrgbcolor{colorfill}{" + fixedPrecision(fillColor.getRed() / 255.) + " " + fixedPrecision(fillColor.getGreen() / 255.) + " " + fixedPrecision(fillColor.getBlue() / 255.) + "}"); ps .println("\\pscustom[fillstyle=solid,fillcolor=colorfill,linecolor=current]{"); pc.addPath(shape, getTransform()); ps.println("}"); } catch (IOException e) { handleException(e); } } /* 5.2. Images */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { // Mostly unimplemented. ps.println("% copyArea(x,y,width,height,dx,dy) called"); } protected void writeImage(RenderedImage image, AffineTransform xform, Color bkg) throws IOException { // writeWarning(getClass()+": writeImage(RenderedImage, AffineTransform, // Color) not implemented."); // Write out the image. ps.println("% writeImage(image,xform,bkg) called"); } /* 5.3. Strings */ protected void writeString(String str, double x, double y) throws IOException { ps.println("% string componentHeight = " + componentHeight); ps.println("% string pageHeight = " + pageHeight); y = -y + pageHeight; ps.print("\\put(" + fixedPrecision(x) + "," + fixedPrecision(y) + "){"); ps.print("\\fontsize{" + getFont().getSize() + "}{" + fixedPrecision(1.2 * getFont().getSize()) + "}"); ps.print("\\textcolor{current}{"); if (getFont().getFamily().equals("Serif")) ps.print("\\rmfamily "); if (getFont().getFamily().equals("SansSerif")) ps.print("\\sffamily "); if (getFont().getFamily().equals("Monospaced")) ps.print("\\ttfamily "); switch (getFont().getStyle()) { case 0: ps.print("\\upshape "); break; case 1: ps.print("\\bfseries "); break; case 2: ps.print("\\itshape "); break; case 3: ps.print("\\bfseries \\itshape "); break; default: } ps.println(fixString(str) + "}}"); } // Certain chars can only be printed in the LaTeX math environment, or // are commands and must be preceded by a backslash. protected String fixString(String str) { StringBuffer buffer = new StringBuffer(str); for (int i = 0; i < buffer.length(); i++) { switch (buffer.charAt(i)) { default: break; case '{': case '}': case '#': case '%': case '&': case '$': case '_': buffer.insert(i, '\\'); i++; break; case '<': case '>': buffer.insert(i, '$'); buffer.insert((i + 2), '$'); i += 2; break; case '\\': buffer.delete(i, (i + 1)); buffer.insert(i, "$\\backslash$"); i += 11; break; } } return buffer.toString(); } /* * ================================================================================ | * 6. Transformations * ================================================================================ */ protected void writeTransform(AffineTransform t) throws IOException { // Ignored, transforms are pre-calculated before written to LaTeX. } protected void writeSetTransform(AffineTransform t) throws IOException { // Ignored, transforms are pre-calculated before written to LaTeX. } /* * ================================================================================ | * 7. Clipping * ================================================================================ */ protected void writeClip(Shape s) throws IOException { if (s == null || !isProperty(CLIP)) { return; } ps.println("\\psclip{"); LatexPathConstructor pc = new LatexPathConstructor(ps); try { ps.println("\\pscustom[linestyle=none,fillstyle=none]{"); pc.addPath(s, getTransform()); ps.println("}"); } catch (IOException e) { handleException(e); } ps.println("}"); numberClips++; } protected void writeSetClip(Shape s) throws IOException { // clear existing clips while (numberClips > 0) { ps.println("\\endpsclip"); numberClips--; } writeClip(s); } /* * ================================================================================ | * 8. Graphics State * ================================================================================ */ /* 8.1. stroke/linewidth */ protected void writeWidth(float width) throws IOException { ps.println("\\psset{linewidth=" + width + "}"); this.width = width; } protected void writeCap(int cap) throws IOException { // Not supported in LaTeX PSTricks when using lineto. ps.println("% writeCap(int cap) called, not supported"); /* * switch (cap) { case 0: ps.println("\\psset{arrows=-}"); break; case * 1: ps.println("\\psset{arrows=c-c}"); break; case 2: * ps.println("\\psset{arrows=C-C}"); break; default: } */ } protected void writeJoin(int join) throws IOException { // Not supported in LaTeX PSTricks. ps.println("% writeJoin(int join) called, not supported"); } protected void writeMiterLimit(float limit) throws IOException { // Not supported in LaTeX PSTricks. ps.println("% writeMiterLimit(float limit) called, not supported"); } protected void writeDash(float[] dash, float phase) throws IOException { this.dash = dash; if (dash == null || dash.length == 0) { ps.println("\\psset{linestyle=solid}"); return; } // Dotted lines must be dealt with separately. if (dash[0] == 0.0) { ps.println("\\psset{linestyle=dotted,dotsep=" + fixedPrecision(dash[1]) + "}"); return; } // In more complex patterns, dots will appear as very thin dashes. ps.print("\\psset{linestyle=dashed,dash="); for (int i = 0; i < dash.length - 1; i++) { ps.print(fixedPrecision(dash[i]) + " "); } ps.print(fixedPrecision(dash[dash.length - 1])); ps.println("}"); } /* 8.2. paint/color */ public void setPaintMode() { // Mostly unimplemented. ps.println("% setPaintMode() called"); } public void setXORMode(Color c1) { // Mostly unimplemented. ps.println("% setXORMode(Color c1) called"); } protected void writePaint(Color p) throws IOException { ps.println("\\newrgbcolor{current}{" + fixedPrecision(p.getRed() / 255.) + " " + fixedPrecision(p.getGreen() / 255.) + " " + fixedPrecision(p.getBlue() / 255.) + "}"); ps .println("\\psset{linecolor=current,fillcolor=current,fillstyle=solid}"); } protected void writePaint(GradientPaint p) throws IOException { double gradTheta = Math.atan((p.getPoint1().getY() - p.getPoint2() .getY()) / (p.getPoint1().getX() - p.getPoint2().getX())); ps.println("\\newrgbcolor{gradbegin}{" + fixedPrecision(p.getColor1().getRed() / 255.) + " " + fixedPrecision(p.getColor1().getGreen() / 255.) + " " + fixedPrecision(p.getColor1().getBlue() / 255.) + "}"); ps.println("\\newrgbcolor{gradend}{" + fixedPrecision(p.getColor2().getRed() / 255.) + " " + fixedPrecision(p.getColor2().getGreen() / 255.) + " " + fixedPrecision(p.getColor2().getBlue() / 255.) + "}"); ps.println("\\psset{gradmidpoint=1,gradangle=" + Math.toDegrees(gradTheta) + ",fillstyle=gradient}"); } protected void writePaint(TexturePaint p) throws IOException { // Not supported in LaTeX PSTricks. ps .println("% writePaint(TexturePaint p) called, not supported, crosshatch substituted"); ps.println("\\psset{fillstyle=crosshatch}"); } protected void writePaint(Paint p) throws IOException { writeWarning(getClass() + ": writePaint(Paint) not implemented for " + p.getClass()); // Write out the paint. ps.println("% writePaint(Paint p) called"); } /* 8.3. font */ protected void writeFont(Font font) throws IOException { // written when needed } /* 8.4. rendering hints */ /* * ================================================================================ | * 9. Auxiliary * ================================================================================ */ public GraphicsConfiguration getDeviceConfiguration() { // Mostly unimplemented return null; } public boolean hit(Rectangle rect, Shape s, boolean onStroke) { // Mostly unimplemented return false; } public void writeComment(String comment) throws IOException { ps.println("% " + comment); } public void writeWarning(String msg) { System.out.println(msg); } public String toString() { return "LatexGraphics"; } private static ScientificFormat scientific = new ScientificFormat(5, 100, false); public static String fixedPrecision(double d) { return scientific.format(d); } }