| /* PostScriptGraphics2D.java -- AWT printer rendering class. |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.java.awt.print; |
| |
| import java.awt.BasicStroke; |
| import java.awt.Color; |
| import java.awt.Composite; |
| import java.awt.Paint; |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.GradientPaint; |
| import java.awt.Graphics; |
| import java.awt.GraphicsConfiguration; |
| import java.awt.Graphics2D; |
| import java.awt.Image; |
| import java.awt.Polygon; |
| import java.awt.Rectangle; |
| import java.awt.RenderingHints; |
| import java.awt.Shape; |
| import java.awt.Stroke; |
| import java.awt.geom.AffineTransform; |
| import java.awt.geom.Arc2D; |
| import java.awt.geom.Ellipse2D; |
| import java.awt.geom.RoundRectangle2D; |
| import java.awt.geom.PathIterator; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.font.FontRenderContext; |
| import java.awt.font.GlyphVector; |
| import java.awt.font.TextLayout; |
| import java.awt.image.BufferedImage; |
| import java.awt.image.BufferedImageOp; |
| import java.awt.image.renderable.RenderableImage; |
| import java.awt.image.RenderedImage; |
| import java.awt.image.ImageObserver; |
| import java.awt.image.PixelGrabber; |
| import java.awt.print.PageFormat; |
| import java.awt.print.Pageable; |
| import java.awt.print.Paper; |
| import java.awt.print.Printable; |
| import java.awt.print.PrinterException; |
| import java.awt.print.PrinterGraphics; |
| import java.awt.print.PrinterJob; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.text.AttributedCharacterIterator; |
| import java.util.Map; |
| |
| /** |
| * Class PostScriptGraphics2D - Class that implements the Graphics2D object, |
| * writing the output to a PostScript or EPS file |
| * |
| * @author Sven de Marothy |
| * |
| */ |
| class PostScriptGraphics2D extends Graphics2D |
| { |
| /** |
| * The associated printer job. |
| */ |
| private PrinterJob printerJob; |
| |
| /** |
| * Output file. |
| */ |
| private PrintWriter out; |
| |
| // Graphics data |
| private AffineTransform currentTransform = new AffineTransform(); |
| private AffineTransform pageTransform; |
| private RenderingHints renderingHints; |
| private Paint currentPaint = null; |
| private Shape clipShape = null; |
| private Font currentFont = null; |
| private Color currentColor = Color.black; |
| private Color backgroundColor = Color.white; |
| private Stroke currentStroke = null; |
| private static Stroke ordinaryStroke = new BasicStroke(0.0f, |
| BasicStroke.CAP_BUTT, |
| BasicStroke.JOIN_MITER); |
| private float cx; // current drawing position |
| private float cy; // current drawing position |
| private boolean currentFontIsPS; // set if currentFont is one of the above |
| |
| // settings |
| private double pageX = 595; |
| private double pageY = 842; |
| private double Y = pageY; |
| private boolean gradientOn = false; |
| |
| /** |
| * Constructor |
| * |
| */ |
| public PostScriptGraphics2D( PrinterJob pg ) |
| { |
| printerJob = pg; |
| // create transform objects |
| pageTransform = new AffineTransform(); |
| currentTransform = new AffineTransform(); |
| |
| /* |
| Create Rendering hints |
| No text aliasing |
| Quality color and rendering |
| Bicubic interpolation |
| Fractional metrics supported |
| */ |
| renderingHints = new RenderingHints(null); |
| renderingHints.put(RenderingHints.KEY_RENDERING, |
| RenderingHints.VALUE_RENDER_QUALITY); |
| renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, |
| RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); |
| renderingHints.put(RenderingHints.KEY_INTERPOLATION, |
| RenderingHints.VALUE_INTERPOLATION_BICUBIC); |
| renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, |
| RenderingHints.VALUE_FRACTIONALMETRICS_ON); |
| renderingHints.put(RenderingHints.KEY_COLOR_RENDERING, |
| RenderingHints.VALUE_COLOR_RENDER_QUALITY); |
| } |
| |
| /** |
| * Spool a document to PostScript. |
| * If Pageable is non-null, it will print that, otherwise it will use |
| * the supplied printable and pageFormat. |
| */ |
| public SpooledDocument spoolPostScript(Printable printable, |
| PageFormat pageFormat, |
| Pageable pageable) |
| throws PrinterException |
| { |
| try |
| { |
| // spool to a temporary file |
| File temp = File.createTempFile("cpspool", ".ps"); |
| temp.deleteOnExit(); |
| |
| out = new PrintWriter(new BufferedWriter |
| (new OutputStreamWriter |
| (new FileOutputStream(temp), |
| "ISO8859_1"), 1000000)); |
| |
| writePSHeader(); |
| |
| if(pageable != null) |
| { |
| for(int index = 0; index < pageable.getNumberOfPages(); index++) |
| spoolPage(out, pageable.getPrintable(index), |
| pageable.getPageFormat(index), index); |
| } |
| else |
| { |
| int index = 0; |
| while(spoolPage(out, printable, pageFormat, index++) == |
| Printable.PAGE_EXISTS); |
| } |
| out.println("%%Trailer"); |
| out.println("%%EOF"); |
| out.close(); |
| return new SpooledDocument( temp ); |
| } |
| catch (IOException e) |
| { |
| PrinterException pe = new PrinterException(); |
| pe.initCause(e); |
| throw pe; |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Write the postscript file header, |
| * setup the page format and transforms. |
| */ |
| private void writePSHeader() |
| { |
| out.println("%!PS-Adobe-3.0"); |
| out.println("%%Title: "+printerJob.getJobName()); |
| out.println("%%Creator: GNU Classpath "); |
| out.println("%%DocumentData: Clean8Bit"); |
| |
| out.println("%%DocumentNeededResources: font Times-Roman Helvetica Courier"); |
| out.println("%%EndComments"); |
| |
| out.println("%%BeginProlog"); |
| out.println("%%EndProlog"); |
| out.println("%%BeginSetup"); |
| |
| out.println("%%EndFeature"); |
| setupFonts(); |
| out.println("%%EndSetup"); |
| |
| // set default fonts and colors |
| setFont( new Font("Dialog", Font.PLAIN, 12) ); |
| currentColor = Color.white; |
| currentStroke = new BasicStroke(); |
| setPaint(currentColor); |
| setStroke(currentStroke); |
| } |
| |
| /** |
| * setupFonts - set up the font dictionaries for |
| * helvetica, times and courier |
| */ |
| private void setupFonts() |
| { |
| out.println("/helveticaISO"); |
| out.println("/Helvetica findfont dup length dict begin"); |
| out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); |
| out.println("/Encoding ISOLatin1Encoding def"); |
| out.println("currentdict end definefont pop"); |
| |
| out.println("/timesISO"); |
| out.println("/Times-Roman findfont dup length dict begin"); |
| out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); |
| out.println("/Encoding ISOLatin1Encoding def"); |
| out.println("currentdict end definefont pop"); |
| |
| out.println("/courierISO"); |
| out.println("/Courier findfont dup length dict begin"); |
| out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); |
| out.println("/Encoding ISOLatin1Encoding def"); |
| out.println("currentdict end definefont pop"); |
| } |
| |
| /** |
| * Spools a single page, returns NO_SUCH_PAGE unsuccessful, |
| * PAGE_EXISTS if it was. |
| */ |
| public int spoolPage(PrintWriter out, |
| Printable printable, |
| PageFormat pageFormat, |
| int index) throws IOException, PrinterException |
| { |
| out.println("%%BeginPageSetup"); |
| |
| Paper p = pageFormat.getPaper(); |
| pageX = p.getWidth(); |
| pageY = p.getHeight(); |
| |
| if( pageFormat.getOrientation() == PageFormat.PORTRAIT ) |
| out.println( "%%Orientation: Portrait" ); |
| else |
| { |
| out.println( "%%Orientation: Landscape" ); |
| double t = pageX; |
| pageX = pageY; |
| pageY = t; |
| } |
| |
| setClip(0, 0, (int)pageX, (int)pageY); |
| |
| out.println("gsave % first save"); |
| |
| // 595x842; 612x792 respectively |
| out.println("<< /PageSize [" +pageX + " "+pageY+ "] >> setpagedevice"); |
| |
| if( pageFormat.getOrientation() != PageFormat.LANDSCAPE ) |
| { |
| pageTransform.translate(pageX, 0); |
| pageTransform.scale(-1.0, 1.0); |
| } |
| |
| // save the original CTM |
| pushCTM(); |
| concatCTM(pageTransform); |
| setTransform(new AffineTransform()); |
| |
| out.println("%%EndPageSetup"); |
| |
| out.println("gsave"); |
| |
| if( printable.print(this, pageFormat, index) == Printable.NO_SUCH_PAGE ) |
| return Printable.NO_SUCH_PAGE; |
| |
| out.println("grestore"); |
| out.println("showpage"); |
| |
| return Printable.PAGE_EXISTS; |
| } |
| |
| /** push the Current Transformation Matrix onto the PS stack */ |
| private void pushCTM() |
| { |
| out.println("matrix currentmatrix % pushCTM()"); |
| } |
| |
| /** pop the Current Transformation Matrix from the PS stack */ |
| private void popCTM() |
| { |
| out.println("setmatrix % restore CTM"); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| public Graphics create() |
| { |
| return null; |
| } |
| |
| public void drawOval(int x, int y, int width, int height) |
| { |
| out.println("% drawOval()"); |
| setStroke(ordinaryStroke); |
| draw(new Ellipse2D.Double(x, y, width, height)); |
| setStroke(currentStroke); |
| } |
| |
| public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) |
| { |
| if (nPoints <= 0 || xPoints.length < nPoints || yPoints.length < nPoints) |
| return; |
| out.println("newpath % drawPolyLine()"); |
| out.println(xPoints[0] + " " + yPoints[0] + " moveto"); |
| for (int i = 1; i < nPoints; i++) |
| out.println(xPoints[i] + " " + yPoints[i] + " lineto"); |
| out.println("closepath"); |
| out.println("stroke"); |
| } |
| |
| public void drawRoundRect(int x, int y, int width, int height, int arcWidth, |
| int arcHeight) |
| { |
| out.println("% drawRoundRect()"); |
| RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width, |
| height, arcWidth, |
| arcHeight); |
| setStroke(ordinaryStroke); |
| draw(rr); |
| setStroke(currentStroke); |
| } |
| |
| public void fillRoundRect(int x, int y, int width, int height, int arcWidth, |
| int arcHeight) |
| { |
| out.println("% fillRoundRect()"); |
| RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width, |
| height, arcWidth, |
| arcHeight); |
| fill(rr); |
| } |
| |
| public void drawArc(int x, int y, int width, int height, int startAngle, |
| int arcAngle) |
| { |
| setStroke(ordinaryStroke); |
| draw(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); |
| setStroke(currentStroke); |
| } |
| |
| public void fillArc(int x, int y, int width, int height, int startAngle, |
| int arcAngle) |
| { |
| fill(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE)); |
| } |
| |
| public void fillOval(int x, int y, int width, int height) |
| { |
| out.println("% fillOval()"); |
| fill( new Ellipse2D.Double(x, y, width, height) ); |
| } |
| |
| public void fillPolygon(int[] x, int[] y, int nPoints) |
| { |
| out.println("% fillPolygon()"); |
| fill( new Polygon(x, y, nPoints) ); |
| } |
| |
| public void drawLine(int x1, int y1, int x2, int y2) |
| { |
| out.println("% drawLine()"); |
| setStroke(ordinaryStroke); |
| out.println("newpath"); |
| out.println(x1 + " " + (y1) + " moveto"); |
| out.println(x2 + " " + (y2) + " lineto"); |
| out.println("stroke"); |
| setStroke(currentStroke); |
| } |
| |
| //--------------- Image drawing ------------------------------------------ |
| public boolean drawImage(Image img, int x, int y, Color bgcolor, |
| ImageObserver observer) |
| { |
| int w = img.getWidth(null); |
| int h = img.getHeight(null); |
| |
| return drawImage(img, x, y, x + w, y + h, 0, 0, w - 1, h - 1, bgcolor, |
| observer); |
| } |
| |
| public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, |
| int sx1, int sy1, int sx2, int sy2, Color bgcolor, |
| ImageObserver observer) |
| { |
| int n = 0; |
| boolean flipx = false; |
| boolean flipy = false; |
| |
| // swap X and Y's |
| if (sx1 > sx2) |
| { |
| n = sx1; |
| sx1 = sx2; |
| sx2 = n; |
| flipx = ! flipx; |
| } |
| if (sy1 > sy2) |
| { |
| n = sy1; |
| sy1 = sy2; |
| sy2 = n; |
| flipy = ! flipy; |
| } |
| if (dx1 > dx2) |
| { |
| n = dx1; |
| dx1 = dx2; |
| dx2 = n; |
| flipx = ! flipx; |
| } |
| if (dy1 > dy2) |
| { |
| n = dy1; |
| dy1 = dy2; |
| dy2 = n; |
| flipy = ! flipy; |
| } |
| n = 0; |
| int sw = sx2 - sx1; // source width |
| int sh = sy2 - sy1; // source height |
| int[] pixels = new int[sw * sh]; // pixel buffer |
| int dw = dx2 - dx1; // destination width |
| int dh = dy2 - dy1; // destination height |
| double x_scale = ((double) dw) / ((double) sw); |
| double y_scale = ((double) dh) / ((double) sh); |
| |
| out.println("% drawImage() 2"); |
| out.println("gsave"); |
| out.println(dx1 + " " + dy1 + " translate"); |
| out.println(dw + " " + dh + " scale"); |
| out.println(sw + " " + sh + " 8 [" + (flipx ? -sw : sw) + " 0 0 " |
| + (flipy ? -sh : sh) + " " + (flipx ? sw : 0) + " " |
| + (flipy ? sh : 0) + " ]"); |
| out.println("{currentfile 3 string readhexstring pop} bind"); |
| out.println("false 3 colorimage"); |
| |
| PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sw, sh, pixels, 0, sw); |
| try |
| { |
| pg.grabPixels(); |
| } |
| catch (InterruptedException e) |
| { |
| System.err.println("interrupted waiting for pixels!"); |
| return (false); |
| } |
| |
| if ((pg.getStatus() & ImageObserver.ABORT) != 0) |
| { |
| System.err.println("image fetch aborted or errored"); |
| return (false); |
| } |
| |
| for (int j = 0; j < sh; j++) |
| { |
| for (int i = 0; i < sw; i++) |
| { |
| out.print(colorTripleHex(new Color(pixels[j * sw + i]))); |
| if (((++n) % 11) == 0) |
| out.println(); |
| } |
| } |
| |
| out.println(); |
| out.println("%%EOF"); |
| out.println("grestore"); |
| return true; |
| } |
| |
| public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, |
| int sx1, int sy1, int sx2, int sy2, |
| ImageObserver observer) |
| { |
| return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, |
| observer); |
| } |
| |
| public boolean drawImage(Image img, int x, int y, ImageObserver observer) |
| { |
| return drawImage(img, x, y, null, observer); |
| } |
| |
| public boolean drawImage(Image img, int x, int y, int width, int height, |
| Color bgcolor, ImageObserver observer) |
| { |
| int sw = img.getWidth(null); |
| int sh = img.getHeight(null); |
| return drawImage(img, x, y, x + width, y + height, /* destination */ |
| 0, 0, sw - 1, sh - 1, /* source */ |
| bgcolor, observer); |
| // correct? |
| } |
| |
| public boolean drawImage(Image img, int x, int y, int width, int height, |
| ImageObserver observer) |
| { |
| return drawImage(img, x, y, width, height, null, observer); |
| } |
| |
| /** Renders a BufferedImage that is filtered with a BufferedImageOp. */ |
| public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) |
| { |
| BufferedImage result = op.filter(img, null); |
| drawImage(result, x, y, null); |
| } |
| |
| /** Renders an image, applying a transform from image space |
| into user space before drawing. */ |
| public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) |
| { |
| AffineTransform oldTransform = new AffineTransform(currentTransform); |
| boolean ret; |
| |
| transform(xform); |
| ret = drawImage(img, 0, 0, null, obs); |
| setTransform(oldTransform); |
| |
| return ret; |
| } |
| |
| /** Renders a RenderableImage, applying a transform from image |
| space into user space before drawing. */ |
| public void drawRenderableImage(RenderableImage img, AffineTransform xform) |
| { |
| // FIXME |
| } |
| |
| /** Renders a RenderedImage, applying a transform from |
| image space into user space before drawing. */ |
| public void drawRenderedImage(RenderedImage img, AffineTransform xform) |
| { |
| // FIXME |
| } |
| |
| //------------------------------------------------------------------------- |
| public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) |
| { |
| setStroke(ordinaryStroke); |
| draw(new Polygon(xPoints, yPoints, nPoints)); |
| setStroke(currentStroke); |
| } |
| |
| public void drawString(String str, int x, int y) |
| { |
| drawString(str, (float) x, (float) y); |
| } |
| |
| public void drawString(String str, float x, float y) |
| { |
| if( str.trim().equals("") ) |
| return; // don't draw whitespace, silly! |
| |
| if( currentFontIsPS ) |
| { |
| drawStringPSFont(str, x, y); |
| return; |
| } |
| |
| TextLayout text = new TextLayout(str, currentFont, getFontRenderContext()); |
| Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y)); |
| drawStringShape(s); |
| } |
| |
| private void drawStringPSFont(String str, float x, float y) |
| { |
| out.println("% drawString PS font"); |
| out.println(x + " " + y + " moveto"); |
| saveAndInvertAxis(); |
| out.println("(" + str + ") show"); |
| restoreAxis(); |
| } |
| |
| private void saveAndInvertAxis() |
| { |
| // Invert the Y axis of the CTM. |
| popCTM(); |
| pushCTM(); |
| |
| double[] test = |
| { |
| pageTransform.getScaleX(), pageTransform.getShearY(), |
| pageTransform.getShearX(), pageTransform.getScaleY(), |
| pageTransform.getTranslateX(), |
| -pageTransform.getTranslateY() + pageY |
| }; |
| |
| double[] test2 = |
| { |
| currentTransform.getScaleX(), |
| currentTransform.getShearY(), |
| -currentTransform.getShearX(), |
| -currentTransform.getScaleY(), |
| currentTransform.getTranslateX(), |
| currentTransform.getTranslateY() |
| }; |
| |
| AffineTransform total = new AffineTransform(test); |
| total.concatenate(new AffineTransform(test2)); |
| concatCTM(total); |
| } |
| |
| private void restoreAxis() |
| { |
| // reset the CTM |
| popCTM(); |
| pushCTM(); |
| AffineTransform total = new AffineTransform(pageTransform); |
| total.concatenate(currentTransform); |
| concatCTM(total); |
| } |
| |
| /** |
| * special drawing routine for string shapes, |
| * which need to be drawn with the Y axis uninverted. |
| */ |
| private void drawStringShape(Shape s) |
| { |
| saveAndInvertAxis(); |
| |
| // draw the shape s with an inverted Y axis. |
| PathIterator pi = s.getPathIterator(null); |
| float[] coords = new float[6]; |
| |
| while (! pi.isDone()) |
| { |
| switch (pi.currentSegment(coords)) |
| { |
| case PathIterator.SEG_MOVETO: |
| out.println((coords[0]) + " " + (Y - coords[1]) + " moveto"); |
| cx = coords[0]; |
| cy = coords[1]; |
| break; |
| case PathIterator.SEG_LINETO: |
| out.println((coords[0]) + " " + (Y - coords[1]) + " lineto"); |
| cx = coords[0]; |
| cy = coords[1]; |
| break; |
| case PathIterator.SEG_QUADTO: |
| // convert to cubic bezier points |
| float x1 = (cx + 2 * coords[0]) / 3; |
| float y1 = (cy + 2 * coords[1]) / 3; |
| float x2 = (2 * coords[2] + coords[0]) / 3; |
| float y2 = (2 * coords[3] + coords[1]) / 3; |
| |
| out.print((x1) + " " + (Y - y1) + " "); |
| out.print((x2) + " " + (Y - y2) + " "); |
| out.println((coords[2]) + " " + (Y - coords[3]) + " curveto"); |
| cx = coords[2]; |
| cy = coords[3]; |
| break; |
| case PathIterator.SEG_CUBICTO: |
| out.print((coords[0]) + " " + (Y - coords[1]) + " "); |
| out.print((coords[2]) + " " + (Y - coords[3]) + " "); |
| out.println((coords[4]) + " " + (Y - coords[5]) + " curveto"); |
| cx = coords[4]; |
| cy = coords[5]; |
| break; |
| case PathIterator.SEG_CLOSE: |
| out.println("closepath"); |
| break; |
| } |
| pi.next(); |
| } |
| out.println("fill"); |
| |
| restoreAxis(); |
| } |
| |
| public void setColor(Color c) |
| { |
| /* don't set the color if it's already set */ |
| if (c.equals(currentColor)) |
| return; |
| gradientOn = false; |
| currentColor = c; |
| currentPaint = c; // Graphics2D extends colors to paint |
| |
| out.println(colorTriple(c) + " setrgbcolor"); |
| } |
| |
| public void clearRect(int x, int y, int width, int height) |
| { |
| out.println("% clearRect"); |
| Color c = currentColor; |
| setColor(backgroundColor); |
| fill(new Rectangle2D.Double(x, y, width, height)); |
| setColor(c); |
| } |
| |
| public void clipRect(int x, int y, int width, int height) |
| { |
| clip(new Rectangle2D.Double(x, y, width, height)); |
| } |
| |
| public void copyArea(int x, int y, int width, int height, int dx, int dy) |
| { |
| // FIXME |
| } |
| |
| public void fillRect(int x, int y, int width, int height) |
| { |
| fill(new Rectangle2D.Double(x, y, width, height)); |
| } |
| |
| public void dispose() |
| { |
| } |
| |
| public void setClip(int x, int y, int width, int height) |
| { |
| out.println("% setClip()"); |
| setClip(new Rectangle2D.Double(x, y, width, height)); |
| } |
| |
| public void setClip(Shape s) |
| { |
| clip(s); |
| } |
| |
| public Shape getClip() |
| { |
| return clipShape; |
| } |
| |
| public Rectangle getClipBounds() |
| { |
| return clipShape.getBounds(); |
| } |
| |
| public Color getColor() |
| { |
| return currentColor; |
| } |
| |
| public Font getFont() |
| { |
| return currentFont; |
| } |
| |
| public FontMetrics getFontMetrics() |
| { |
| return getFontMetrics(currentFont); |
| } |
| |
| public FontMetrics getFontMetrics(Font f) |
| { |
| // FIXME |
| return null; |
| } |
| |
| public void setFont(Font font) |
| { |
| out.println("% setfont()"); |
| if (font == null) |
| // use the default font |
| font = new Font("Dialog", Font.PLAIN, 12); |
| currentFont = font; |
| setPSFont(); // set up the PostScript fonts |
| } |
| |
| /** |
| * Setup the postscript font if the current font is one |
| */ |
| private void setPSFont() |
| { |
| currentFontIsPS = false; |
| |
| String s = currentFont.getName(); |
| out.println("% setPSFont: Fontname: " + s); |
| if (s.equalsIgnoreCase("Helvetica") || s.equalsIgnoreCase("SansSerif")) |
| out.print("/helveticaISO findfont "); |
| else if (s.equalsIgnoreCase("Times New Roman")) |
| out.print("/timesISO findfont "); |
| else if (s.equalsIgnoreCase("Courier")) |
| out.print("/courierISO findfont "); |
| else |
| return; |
| |
| currentFontIsPS = true; |
| |
| out.print(currentFont.getSize() + " scalefont "); |
| out.println("setfont"); |
| } |
| |
| /** XOR mode is not supported */ |
| public void setPaintMode() |
| { |
| } |
| |
| /** XOR mode is not supported */ |
| public void setXORMode(Color c1) |
| { |
| } |
| |
| public void close() |
| { |
| out.println("showpage"); |
| out.println("%%Trailer"); |
| out.println("grestore % restore original stuff"); |
| out.println("%%EOF"); |
| |
| try |
| { |
| out.close(); |
| } |
| catch (Exception e) |
| { |
| } |
| out = null; |
| } |
| |
| //---------------------------------------------------------------- |
| // Graphics2D stuff ---------------------------------------------- |
| |
| /** Sets the values of an arbitrary number of |
| preferences for the rendering algorithms. */ |
| public void addRenderingHints(Map hints) |
| { |
| /* rendering hint changes are disallowed */ |
| } |
| |
| /** write a shape to the file */ |
| private void writeShape(Shape s) |
| { |
| PathIterator pi = s.getPathIterator(null); |
| float[] coords = new float[6]; |
| |
| while (! pi.isDone()) |
| { |
| switch (pi.currentSegment(coords)) |
| { |
| case PathIterator.SEG_MOVETO: |
| out.println(coords[0] + " " + (coords[1]) + " moveto"); |
| cx = coords[0]; |
| cy = coords[1]; |
| break; |
| case PathIterator.SEG_LINETO: |
| out.println(coords[0] + " " + (coords[1]) + " lineto"); |
| cx = coords[0]; |
| cy = coords[1]; |
| break; |
| case PathIterator.SEG_QUADTO: |
| // convert to cubic bezier points |
| float x1 = (cx + 2 * coords[0]) / 3; |
| float y1 = (cy + 2 * coords[1]) / 3; |
| float x2 = (2 * coords[2] + coords[0]) / 3; |
| float y2 = (2 * coords[3] + coords[1]) / 3; |
| |
| out.print(x1 + " " + (Y - y1) + " "); |
| out.print(x2 + " " + (Y - y2) + " "); |
| out.println(coords[2] + " " + (Y - coords[3]) + " curveto"); |
| cx = coords[2]; |
| cy = coords[3]; |
| break; |
| case PathIterator.SEG_CUBICTO: |
| out.print(coords[0] + " " + coords[1] + " "); |
| out.print(coords[2] + " " + coords[3] + " "); |
| out.println(coords[4] + " " + coords[5] + " curveto"); |
| cx = coords[4]; |
| cy = coords[5]; |
| break; |
| case PathIterator.SEG_CLOSE: |
| out.println("closepath"); |
| break; |
| } |
| pi.next(); |
| } |
| } |
| |
| /** Intersects the current Clip with the interior of |
| the specified Shape and sets the Clip to the resulting intersection. */ |
| public void clip(Shape s) |
| { |
| clipShape = s; |
| out.println("% clip INACTIVE"); |
| // writeShape(s); |
| // out.println("clip"); |
| } |
| |
| /** Strokes the outline of a Shape using the |
| settings of the current Graphics2D context.*/ |
| public void draw(Shape s) |
| { |
| if(!(currentStroke instanceof BasicStroke)) |
| fill(currentStroke.createStrokedShape(s)); |
| |
| out.println("% draw"); |
| writeShape(s); |
| out.println("stroke"); |
| } |
| |
| /** Renders the text of the specified GlyphVector using the |
| Graphics2D context's rendering attributes. */ |
| public void drawGlyphVector(GlyphVector gv, float x, float y) |
| { |
| out.println("% drawGlyphVector"); |
| Shape s = gv.getOutline(); |
| drawStringShape(AffineTransform.getTranslateInstance(x, y) |
| .createTransformedShape(s)); |
| } |
| |
| /** Renders the text of the specified iterator, |
| using the Graphics2D context's current Paint.*/ |
| public void drawString(AttributedCharacterIterator iterator, float x, float y) |
| { |
| TextLayout text = new TextLayout(iterator, getFontRenderContext()); |
| Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y)); |
| drawStringShape(s); |
| } |
| |
| /** Renders the text of the specified iterator, |
| using the Graphics2D context's current Paint. */ |
| public void drawString(AttributedCharacterIterator iterator, int x, int y) |
| { |
| drawString(iterator, (float) x, (float) y); |
| } |
| |
| /** Fills the interior of a Shape using the settings of the Graphics2D context. */ |
| public void fill(Shape s) |
| { |
| out.println("% fill"); |
| if (! gradientOn) |
| { |
| writeShape(s); |
| out.println("fill"); |
| } |
| else |
| { |
| out.println("gsave"); |
| writeShape(s); |
| out.println("clip"); |
| writeGradient(); |
| out.println("shfill"); |
| out.println("grestore"); |
| } |
| } |
| |
| /** Returns the background color used for clearing a region. */ |
| public Color getBackground() |
| { |
| return backgroundColor; |
| } |
| |
| /** Returns the current Composite in the Graphics2D context. */ |
| public Composite getComposite() |
| { |
| // FIXME |
| return null; |
| } |
| |
| /** Returns the device configuration associated with this Graphics2D. */ |
| public GraphicsConfiguration getDeviceConfiguration() |
| { |
| // FIXME |
| out.println("% getDeviceConfiguration()"); |
| return null; |
| } |
| |
| /** Get the rendering context of the Font within this Graphics2D context. */ |
| public FontRenderContext getFontRenderContext() |
| { |
| out.println("% getFontRenderContext()"); |
| |
| double[] scaling = |
| { |
| pageTransform.getScaleX(), 0, 0, |
| -pageTransform.getScaleY(), 0, 0 |
| }; |
| |
| return (new FontRenderContext(new AffineTransform(scaling), false, true)); |
| } |
| |
| /** Returns the current Paint of the Graphics2D context. */ |
| public Paint getPaint() |
| { |
| return currentPaint; |
| } |
| |
| /** Returns the value of a single preference for the rendering algorithms. */ |
| public Object getRenderingHint(RenderingHints.Key hintKey) |
| { |
| return renderingHints.get(hintKey); |
| } |
| |
| /** Gets the preferences for the rendering algorithms. */ |
| public RenderingHints getRenderingHints() |
| { |
| return renderingHints; |
| } |
| |
| /** Returns the current Stroke in the Graphics2D context. */ |
| public Stroke getStroke() |
| { |
| return currentStroke; |
| } |
| |
| /** Returns a copy of the current Transform in the Graphics2D context. */ |
| public AffineTransform getTransform() |
| { |
| return currentTransform; |
| } |
| |
| /** |
| * Checks whether or not the specified Shape intersects |
| * the specified Rectangle, which is in device space. |
| */ |
| public boolean hit(Rectangle rect, Shape s, boolean onStroke) |
| { |
| Rectangle2D.Double r = new Rectangle2D.Double(rect.getX(), rect.getY(), |
| rect.getWidth(), |
| rect.getHeight()); |
| return s.intersects(r); |
| } |
| |
| /** Sets the background color for the Graphics2D context.*/ |
| public void setBackground(Color color) |
| { |
| out.println("% setBackground(" + color + ")"); |
| backgroundColor = color; |
| } |
| |
| /** Sets the Composite for the Graphics2D context. |
| Not supported. */ |
| public void setComposite(Composite comp) |
| { |
| } |
| |
| /** Sets the Paint attribute for the Graphics2D context.*/ |
| public void setPaint(Paint paint) |
| { |
| currentPaint = paint; |
| gradientOn = false; |
| if (paint instanceof Color) |
| { |
| setColor((Color) paint); |
| return; |
| } |
| if (paint instanceof GradientPaint) |
| { |
| gradientOn = true; |
| return; |
| } |
| } |
| |
| /* get a space seperated 0.0 - 1.0 color RGB triple */ |
| private String colorTriple(Color c) |
| { |
| return (((double) c.getRed() / 255.0) + " " |
| + ((double) c.getGreen() / 255.0) + " " |
| + ((double) c.getBlue() / 255.0)); |
| } |
| |
| /** |
| * Get a nonsperated hex RGB triple, eg FFFFFF = white |
| * used by writeGradient and drawImage |
| */ |
| private String colorTripleHex(Color c) |
| { |
| String r = "00" + Integer.toHexString(c.getRed()); |
| r = r.substring(r.length() - 2); |
| String g = "00" + Integer.toHexString(c.getGreen()); |
| g = g.substring(g.length() - 2); |
| String b = "00" + Integer.toHexString(c.getBlue()); |
| b = b.substring(b.length() - 2); |
| return r + g + b; |
| } |
| |
| /* write the current gradient fill */ |
| private void writeGradient() |
| { |
| GradientPaint paint = (GradientPaint) currentPaint; |
| out.println("% writeGradient()"); |
| |
| int n = 1; |
| double x; |
| double y; |
| double dx; |
| double dy; |
| Point2D p1 = currentTransform.transform(paint.getPoint1(), null); |
| Point2D p2 = currentTransform.transform(paint.getPoint2(), null); |
| x = p1.getX(); |
| y = p1.getY(); |
| dx = p2.getX() - x; |
| dy = p2.getY() - y; |
| |
| // get number of repetitions |
| while (x + n * dx < pageY && y + n * dy < pageX && x + n * dx > 0 |
| && y + n * dy > 0) |
| n++; |
| |
| out.println("<<"); // start |
| out.println("/ShadingType 2"); // gradient fill |
| out.println("/ColorSpace [ /DeviceRGB ]"); // RGB colors |
| out.print("/Coords ["); |
| out.print(x + " " + y + " " + (x + n * dx) + " " + (y + n * dy) + " "); |
| out.println("]"); // coordinates defining the axis |
| out.println("/Function <<"); |
| out.println("/FunctionType 0"); |
| out.println("/Order 1"); |
| out.println("/Domain [ 0 1 ]"); |
| out.println("/Range [ 0 1 0 1 0 1 ]"); |
| out.println("/BitsPerSample 8"); |
| out.println("/Size [ " + (1 + n) + " ]"); |
| out.print("/DataSource < " + colorTripleHex(paint.getColor1()) + " " |
| + colorTripleHex(paint.getColor2()) + " "); |
| for (; n > 1; n--) |
| if (paint.isCyclic()) |
| { |
| if ((n % 2) == 1) |
| out.print(colorTripleHex(paint.getColor1()) + " "); |
| else |
| out.print(colorTripleHex(paint.getColor2()) + " "); |
| } |
| else |
| out.print(colorTripleHex(paint.getColor2()) + " "); |
| out.println(">"); |
| out.println(">>"); |
| out.println(">>"); |
| } |
| |
| /** Sets the value of a single preference for the rendering algorithms. */ |
| public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) |
| { |
| /* we don't allow the changing of rendering hints. */ |
| } |
| |
| /** Replaces the values of all preferences for the rendering algorithms |
| with the specified hints. */ |
| public void setRenderingHints(Map hints) |
| { |
| /* we don't allow the changing of rendering hints. */ |
| } |
| |
| /** |
| * Sets the Stroke for the Graphics2D context. BasicStroke fully implemented. |
| */ |
| public void setStroke(Stroke s) |
| { |
| currentStroke = s; |
| |
| if (! (s instanceof BasicStroke)) |
| return; |
| |
| BasicStroke bs = (BasicStroke) s; |
| out.println("% setStroke()"); |
| try |
| { |
| // set the line width |
| out.println(bs.getLineWidth() + " setlinewidth"); |
| |
| // set the line dash |
| float[] dashArray = bs.getDashArray(); |
| if (dashArray != null) |
| { |
| out.print("[ "); |
| for (int i = 0; i < dashArray.length; i++) |
| out.print(dashArray[i] + " "); |
| out.println("] " + bs.getDashPhase() + " setdash"); |
| } |
| else |
| out.println("[] 0 setdash"); // set solid |
| |
| // set the line cap |
| switch (bs.getEndCap()) |
| { |
| case BasicStroke.CAP_BUTT: |
| out.println("0 setlinecap"); |
| break; |
| case BasicStroke.CAP_ROUND: |
| out.println("1 setlinecap"); |
| break; |
| case BasicStroke.CAP_SQUARE: |
| out.println("2 setlinecap"); |
| break; |
| } |
| |
| // set the line join |
| switch (bs.getLineJoin()) |
| { |
| case BasicStroke.JOIN_BEVEL: |
| out.println("2 setlinejoin"); |
| break; |
| case BasicStroke.JOIN_MITER: |
| out.println("0 setlinejoin"); |
| out.println(bs.getMiterLimit() + " setmiterlimit"); |
| break; |
| case BasicStroke.JOIN_ROUND: |
| out.println("1 setlinejoin"); |
| break; |
| } |
| } |
| catch (Exception e) |
| { |
| out.println("% Exception in setStroke()"); |
| } |
| } |
| |
| //////////////////// TRANSFORM SETTING ///////////////////////////////////// |
| private void concatCTM(AffineTransform Tx) |
| { |
| double[] matrixElements = new double[6]; |
| Tx.getMatrix(matrixElements); |
| |
| out.print("[ "); |
| for (int i = 0; i < 6; i++) |
| out.print(matrixElements[i] + " "); |
| out.println("] concat"); |
| } |
| |
| /** Sets the Transform in the Graphics2D context. */ |
| public void setTransform(AffineTransform Tx) |
| { |
| // set the transformation matrix; |
| currentTransform = Tx; |
| |
| // concatenate the current transform and the page transform |
| AffineTransform totalTransform = new AffineTransform(pageTransform); |
| totalTransform.concatenate(currentTransform); |
| out.println("% setTransform()"); |
| out.println("% pageTransform:" + pageTransform); |
| out.println("% currentTransform:" + currentTransform); |
| out.println("% totalTransform:" + totalTransform); |
| |
| popCTM(); |
| pushCTM(); // set the CTM to it's original state |
| concatCTM(totalTransform); // apply our transforms |
| } |
| |
| /** Composes an AffineTransform object with the Transform |
| in this Graphics2D according to the rule last-specified-first-applied. */ |
| public void transform(AffineTransform Tx) |
| { |
| // concatenate the current transform |
| currentTransform.concatenate(Tx); |
| // and the PS CTM |
| concatCTM(Tx); |
| } |
| |
| ////////////////////////// TRANSFORMS ////////////////////////////////////// |
| |
| /** shear transform */ |
| public void shear(double shx, double shy) |
| { |
| out.println("% shear()"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.shear(shx, shy); |
| transform(Tx); |
| } |
| |
| /** Translates the origin of the Graphics2D context |
| to the point (x, y) in the current coordinate system. */ |
| public void translate(int x, int y) |
| { |
| out.println("% translate()"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.translate(x, y); |
| transform(Tx); |
| } |
| |
| /** Translates the origin of the Graphics2D context |
| to the point (x, y) in the current coordinate system. */ |
| public void translate(double x, double y) |
| { |
| out.println("% translate(" + x + ", " + y + ")"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.translate(x, y); |
| transform(Tx); |
| } |
| |
| /** Concatenates the current Graphics2D Transform with a rotation transform.*/ |
| public void rotate(double theta) |
| { |
| out.println("% rotate(" + theta + ")"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.rotate(theta); |
| transform(Tx); |
| } |
| |
| /** Concatenates the current Graphics2D Transform with |
| a translated rotation transform.*/ |
| public void rotate(double theta, double x, double y) |
| { |
| out.println("% rotate()"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.rotate(theta, x, y); |
| transform(Tx); |
| } |
| |
| /** Concatenates the current Graphics2D Transform with a scaling |
| transformation Subsequent rendering is resized according to the |
| specified scaling factors relative to the previous scaling.*/ |
| public void scale(double sx, double sy) |
| { |
| out.println("% scale(" + sx + ", " + sy + ")"); |
| AffineTransform Tx = new AffineTransform(); |
| Tx.scale(sx, sy); |
| transform(Tx); |
| } |
| } |