From 7eb151055b2acb1c60d0cfc8b78ce898bf9e271d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 20 Feb 2021 10:00:00 +0100 Subject: [PATCH] gnujpdf setTranform uses cm operator --- lib/gnujpdf.jar | Bin 175274 -> 174993 bytes .../flash/exporters/FrameExporter.java | 3 ++- libsrc/gnujpdf/src/gnu/jpdf/PDFGraphics.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gnujpdf.jar b/lib/gnujpdf.jar index 840de62d5211484dd04a6bad8c58ca9b0e979777..0844f483bd97db7b65ba67c607677b17d21a8350 100644 GIT binary patch delta 22145 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 QRU50gU5103u4l3Qe`+}))Bpeg diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index 9e1c2bbf2..f7f16614b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -622,7 +622,8 @@ public class FrameExporter { } } - g2.drawTransparentStringWithTransform(text.toString(), (float) x, (float) y, trans.toTransform()); + g2.setTransform(trans.toTransform()); + g2.drawTransparentString(text.toString(), (float) x, (float) y); } } diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFGraphics.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFGraphics.java index ebdd768ae..ff3580d38 100644 --- a/libsrc/gnujpdf/src/gnu/jpdf/PDFGraphics.java +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFGraphics.java @@ -1 +1 @@ -/* * $Id: PDFGraphics.java,v 1.6 2007/09/22 12:58:40 gil1 Exp $ * * $Date: 2007/09/22 12:58:40 $ * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package gnu.jpdf; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.RenderingHints.Key; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.awt.print.PageFormat; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Serializable; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Hashtable; import java.util.Locale; import java.util.Map; import java.util.WeakHashMap; /** * This class is our implementation of AWT's Graphics class. It provides a * Java standard way of rendering into a PDF Document's Page. * * @author Peter T Mount, http://www.retep.org.uk/pdf/ * @author Eric Z. Beard, ericzbeard@hotmail.com * @author Gilbert DeLeeuw, gil1@users.sourceforge.net * @version $Revision: 1.6 $, $Date: 2007/09/22 12:58:40 $ * @see gnu.jpdf.PDFGraphics */ public class PDFGraphics extends Graphics2D implements Serializable { /** * One degree in radians */ private static final double degrees_to_radians = Math.PI/180.0; private static final int FILL = 1; private static final int STROKE = 2; private static final int CLIP = 3; private static final AffineTransform IDENTITY = new AffineTransform(); private static final Stroke DEF_STROKE = new BasicStroke(); /* * NOTE: The original class is the work of Peter T. Mount, who released it * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as * follows: * The package name was changed to gnu.pdf. * The formatting was changed a little bit. * This used to subclass an abstract class in a different package with * the same name (confusing). Now it's one concrete class. * drawImage() was implemented * It is still licensed under the LGPL. */ // Implementation notes: // // Pages 333-335 of the PDF Reference Manual // // Unless absolutely required, use the moveto, lineto and rectangle // operators to perform those actions. // They contain some extra optimizations // which will reduce the output size by up to half in some cases. // // About fill operators: For correct operation, any fill operation should // start with closeBlock(), which will ensure any previous path is completed, // otherwise you may find the fill will include previous items private static final DecimalFormat df = new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.ENGLISH)); //JPEXS: cache for already used images private static Map usedImages = new WeakHashMap(); private Color background; /** * This is true for any Graphics instance that didn't create the stream. * @see #create */ private boolean child; private Area clip; /** * This holds the current clipRectangle */ protected Rectangle clipRectangle; private Composite composite; private Graphics2D dg2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB).createGraphics(); /** * This is the current font (in Java format) */ private Font font; /** * Part of the optimizer: * When true, we are drawing a path. */ private boolean inStroke; /** * Part of the optimizer: * When true, we are within a Text Block. */ private boolean inText; // true if within a Text Block - see newTextBlock() /** * The stroke line cap code; */ private int lineCap = 0; /** * The stroke line join code */ private int lineJoin = 0; /** * The stroke line width */ private float lineWidth = 1.0f; /** * Part of the optimizer: * The last known moveto/lineto x coordinate * @see #moveto * @see #lineto */ private float lx; // last known moveto/lineto coordinates /** * Part of the optimizer: * The last known moveto/lineto y coordinate * @see #moveto * @see #lineto */ private float ly; // last known moveto/lineto coordinates private float miterLimit = 10.0f; /** * Part of the optimizer: * When true, the font has changed. */ private boolean newFont; // true if the font changes - see newTextBlock() private Stroke originalStroke; // Original transform private AffineTransform oTransform; /** * This is a reference to the PDFPage we are rendering to. */ private PDFPage page; /** * This is the current pen/fill color */ private Paint paint; /** * This is the current font (in PDF format) */ private PDFFont pdffont; /** * Part of the optimizer: * This is written to the stream when the newPath() is called. np then clears * this value. */ private String pre_np; // PDF space transform private AffineTransform pTransform; /** * This is the PrintWriter used to write PDF drawing commands to the Stream */ private RawPrintWriter pw; /** * RenderingHints */ private RenderingHints rhints = new RenderingHints(null); private Stroke stroke; // Start of Graphics2D properties // Java space transform private AffineTransform transform; private AffineTransform realTransform; /** * This is used to translate coordinates */ protected float trax; /** * This is used to translate coordinates */ protected float tray; /** * Part of the optimizer: * The last x coordinate when rendering text */ private float tx; // the last coordinate for text rendering /** * Part of the optimizer: * The last y coordinate when rendering text */ private float ty; // the last coordinate for text rendering /** * @see Graphics2D#addRenderingHints(Map) */ public void addRenderingHints(Map hints) { rhints.putAll(hints); } /** * This produces an arc by breaking it down into one or more Bezier curves. * It is used internally to implement the drawArc and fillArc methods. * * @param axc X coordinate of arc centre * @param ayc Y coordinate of arc centre * @param width of bounding rectangle * @param height of bounding rectangle * @param ang1 Start angle * @param ang2 End angle * @param clockwise true to draw clockwise, false anti-clockwise */ public void arc(double axc,double ayc, double width,double height, double ang1,double ang2, boolean clockwise) { double adiff; double x0, y0; double x3r, y3r; boolean first = true; // may not need this //if( ar < 0 ) { //ang1 += fixed_180; //ang2 += fixed_180; //ar = - ar; //} double ang1r = (ang1%360.0)*degrees_to_radians; double sin0 = Math.sin(ang1r); double cos0 = Math.cos(ang1r); x0 = axc + width*cos0; y0 = ayc + height*sin0; // NB: !clockwise here as Java Space is inverted to User Space if( !clockwise ) { // Quadrant reduction while ( ang1 < ang2 ) ang2 -= 360.0; while ( (adiff = ang2 - ang1) < -90.0 ) { double w = sin0; sin0 = -cos0; cos0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 -= 90.0; first = false; } } else { // Quadrant reduction while ( ang2 < ang1 ) ang2 += 360.0; while ( (adiff = ang2 - ang1) > 90.0 ) { double w = cos0; cos0 = -sin0; sin0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 += 90.0; first = false; } } // Compute the intersection of the tangents. // We know that -fixed_90 <= adiff <= fixed_90. double trad = Math.tan(adiff * (degrees_to_radians / 2)); double ang2r = ang2 * degrees_to_radians; double xt = x0 - trad * width*sin0; double yt = y0 + trad * height*cos0; arc_add(first, width, height, x0, y0, (axc + width * Math.cos(ang2r)), (ayc + height * Math.sin(ang2r)), xt, yt); } /** * Used by the arc method to actually add an arc to the path * Important: We write directly to the stream here, because this method * operates in User space, rather than Java space. * @param first true if the first arc * @param w width * @param h height * @param x0 coordinate * @param y0 coordinate * @param x3 coordinate * @param y3 coordinate * @param xt coordinate * @param yt coordinate */ private void arc_add(boolean first, double w,double h, double x0,double y0, double x3,double y3, double xt,double yt) { double dx = xt - x0, dy = yt - y0; double dist = dx*dx + dy*dy; double w2 = w*w, h2=h*h; double r2 = w2+h2; double fw = 0.0, fh = 0.0; if(dist < (r2*1.0e8)) { // JM fw = (w2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/w2))) : 0.0; fh = (h2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/h2))) : 0.0; } // The path must have a starting point if(first) moveto(x0,y0); double x = x0+((xt-x0)*fw); double y = y0+((yt-y0)*fh); x0 = x3+((xt-x3)*fw); y0 = y3+((yt-y3)*fh); // Finally the actual curve. curveto(x,y,x0,y0,x3,y3); } /** * This simply draws a White Rectangle to clear the area * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clearRect(int x,int y,int w,int h) { closeBlock(); pw.print("q 1 1 1 RG ");// save state, set colour to White drawRect(x,y,w,h); closeBlock("B Q"); // close fill & stroke, then restore state } /** * @see Graphics2D#clip(Shape) */ public void clip(Shape s) { if (s == null) { setClip(null); return; } s = transform.createTransformedShape(s); if (clip == null) clip = new Area(s); else clip.intersect(new Area(s)); // followPath(s, CLIP); } /** * This extra method allows PDF users to clip to a Polygon. * *

In theory you could use setClip(), except that java.awt.Graphics * only supports Rectangle with that method, so we will have an extra * method. * @param p Polygon to clip to */ public void clipPolygon(Polygon p) { closeBlock(); // finish off any existing path polygon(p.xpoints,p.ypoints,p.npoints); closeBlock("W"); // clip to current path clipRectangle = p.getBounds(); } /** * Clips to a set of coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clipRect(int x,int y,int w,int h) { setClip(x,y,w,h); } /** * All functions should call this to close any existing optimized blocks. */ void closeBlock() { closeBlock("S"); } /** *

This is used by code that use the path in any way other than Stroke * (like Fill, close path & Stroke etc). Usually this is used internally.

* * @param code PDF operators that will close the path */ void closeBlock(String code) { if(inText) { pw.println("ET Q"); // setOrientation(); // fixes Orientation matrix } if(inStroke) { pw.println(code); } inStroke=inText=false; } /** * This is unsupported - how do you do this with Vector graphics? * @param x coordinate * @param y coordinate * @param w width * @param h height * @param dx coordinate * @param dy coordinate */ public void copyArea(int x,int y,int w,int h,int dx,int dy) { // Hmm... Probably need to keep track of everything // that has been drawn so far to get the contents of an area } //============ Line operations ======================= /** *

This returns a child instance of this Graphics object. As with AWT, the * affects of using the parent instance while the child exists, is not * determined.

* *

Once complete, the child should be released with it's dispose() * method which will restore the graphics state to it's parent.

* * @return Graphics object to render onto the page */ public Graphics create() { closeBlock(); PDFGraphics g = createGraphic(page,pw); // The new instance inherits a few items // g.media = new Rectangle(media); g.trax = trax; g.tray = tray; g.clipRectangle = new Rectangle(clipRectangle); return (Graphics) g; } // end create() /** * This method creates a new instance of the class based on the page * and a print writer. * * @param page the page to attach to * @param pw the PrintWriter to attach to. */ protected PDFGraphics createGraphic(PDFPage page, RawPrintWriter pw) { PDFGraphics g = new PDFGraphics(); g.init(page,pw); return g; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=(float)x2; ly=(float)y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(double x1,double y1,double x2,double y2,double x3,double y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=(float)x3; ly=(float)y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=x2; ly=y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(int x1,int y1,int x2,int y2,int x3,int y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=x3; ly=y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=(float)x2; ly=(float)y2; } // Arcs are horrible and complex. They are at the end of the // file, because they are the largest. This is because, unlike // Postscript, PDF doesn't have any arc operators, so we must // implement them by converting into one or more Bezier curves // (which is how Postscript does them internally). /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=x2; ly=y2; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(double w,double h) { double nw=w,nh=h; // scratch // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait //nw = w; nh = -h; // break; // // case PageFormat.LANDSCAPE: // // Landscape // nw = h; // nh = w; // break; // //// case 180: //// // Inverse Portrait //// nw = -w; //// //nh = h; //// break; // // case PageFormat.REVERSE_LANDSCAPE: // // Seascape // nw = -h; // nh = -w; // break; // } return ""+df.format(nw)+" "+df.format(nh)+" "; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(int w,int h) { return cwh((double)w,(double)h); } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(double x, double y) { // double nx = x, ny = y; // scratch // double mh = page.getPageFormat().getHeight(); Point2D ptSrc = new Point2D.Double(x, y); Point2D ptDst = new Point2D.Double(); transform.transform(ptSrc, ptDst); // x += trax; // y += tray; // // nx = x; // ny = mh - y; // // System.out.println("\ncxy(" + ptSrc.getX() + ", " + ptSrc.getY() + ")"); // System.out.println("Old [" + nx + "," + ny + "]"); // System.out.println("Trn [" + ptDst.getX() + ", " + ptDst.getY() + "]"); // // return "" + df.format(nx) + " " + df.format(ny) + " "; return ""+df.format(ptDst.getX())+" "+df.format(ptDst.getY())+" "; } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(int x,int y) { return cxy((double)x,(double)y); } /** *

This releases any resources used by this Graphics object. You must use * this method once finished with it. Leaving it open will leave the PDF * stream in an inconsistent state, and will produce errors.

* *

If this was created with Graphics.create() then the parent instance * can be used again. If not, then this closes the graphics operations for * this page when used with PDFJob.

* *

When using PDFPage, you can create another fresh Graphics instance, * which will draw over this one.

* */ public void dispose() { closeBlock(); if(child) { pw.println("Q"); // restore graphics context } else { pw.close(); // close the stream if were the parent } } // ********************************************* // **** Implementation of java.awt.Graphics **** // ********************************************* //============ Rectangle operations ======================= /** * @see Graphics2D#draw(Shape) */ public void draw(Shape s) { followPath(s, STROKE); } /** *

Not implemented

* *

Draws a 3-D highlighted outline of the specified rectangle. * The edges of the rectangle are highlighted so that they appear * to be beveled and lit from the upper left corner. * The colors used for the highlighting effect are determined based on * the current color. The resulting rectangle covers an area that * is width + 1 pixels wide by height + 1 pixels tall. *

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void draw3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Draws an arc * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void drawArc(int x,int y,int w,int h,int sa,int aa) { w=w>>1; h=h>>1; x+=w; y+=h; arc((double)x,(double)y, (double)w,(double)h, (double)-sa,(double)(-sa-aa), false); } /** *

Not implemented

* * @param data a byte[] value * @param offset an int value * @param length an int value * @param x an int value * @param y an int value */ public void drawBytes(byte[] data, int offset, int length, int x, int y) { } //============ Optimizers ======================= /** * @see Graphics2D#drawGlyphVector(GlyphVector, float, float) */ public void drawGlyphVector(GlyphVector g, float x, float y) { Shape s = g.getOutline(x, y); fill(s); } /** * @see Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int) */ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { BufferedImage result = img; if (op != null) { result = op.createCompatibleDestImage(img, img.getColorModel()); result = op.filter(img, result); } drawImage(result, x, y, null); } /** * @see Graphics2D#drawImage(Image, AffineTransform, ImageObserver) */ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { // return drawImage(img, null, xform, null, obs); return true; } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,Color bgcolor, ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs), bgcolor,obs); } /** * Draw's an image onto the page * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, Color bgcolor,ImageObserver obs) { closeBlock(); pw.print("q "); // save state Color c = getColor(); // save current colour setColor(bgcolor); // change the colour drawRect(x,y,w,h); closeBlock("B Q"); // fill stroke, restore state paint = c; // restore original colour return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draws an image onto the page.

* *

This method is implemented with ASCIIbase85 encoding and the * zip stream deflater. It results in a stream that is anywhere * from 3 to 10 times as big as the image. This obviously needs some * improvement, but it works well for small images

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, ImageObserver obs) { closeBlock(); PDFImage image; if (usedImages.containsKey(img)) { image = usedImages.get(img); } else { image = new PDFImage(img, x, y, w, h, obs); // The image needs to be registered in several places page.getPDFDocument().setImageName(image); page.getPDFDocument().add(image); usedImages.put(img, image); } page.addToProcset("/ImageC"); page.addImageResource(image.getName() + " " + image.getSerialID() + " 0 R"); // JM /*page.addResource("/XObject << " + image.getName() + " " + image.getSerialID() + " 0 R >>");*/ // q w 0 0 h x y cm % the coordinate matrix pw.print("q " + w + " 0 0 " + h + " " + x + " " + ((int) page.getDimension().getHeight() - y - h) + " cm \n" + image.getName() + " Do\nQ\n"); return false; } /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, Color bgcolor,ImageObserver obs) { return false; } //============ Clipping operations ======================= /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, ImageObserver obs) { // This shouldn't be too bad, just change the coordinate matrix return false; } /** * Draws a line between two coordinates. * * If the first coordinate is the same as the last one drawn * (i.e. a previous drawLine, moveto, etc) it is ignored. * @param x1 coordinate * @param y1 coordinate * @param x2 coordinate * @param y2 coordinate */ public void drawLine(int x1,int y1,int x2,int y2) { moveto(x1,y1); lineto(x2,y2); } //============ Arcs operations ============================== // These are the standard Graphics operators. They use the // arc extension operators to achieve the affect. /** *

Draws an oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawOval(int x,int y,int w,int h) { drawArc(x, y, w, h, 0, 360); } /** * Draws a polygon, linking the first and last coordinates. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void drawPolygon(int[] xp,int[] yp,int np) { polygon(xp,yp,np); closeBlock("s"); // close path and stroke } /** * Draws a polyline. The first and last coordinates are not linked. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polyline */ public void drawPolyline(int[] xp,int[] yp,int np) { polygon(xp,yp,np); // no stroke, as we keep the optimizer in stroke state } /** * We override Graphics.drawRect as it doesn't join the 4 lines. * Also, PDF provides us with a Rectangle operator, so we will use that. * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawRect(int x,int y,int w,int h) { newPath(); pw.print(cxy(x,y)+cwh(w,h)+"re "); // rectangle lx=x; // I don't know if this is correct, but lets see if PDF ends ly=y; // the rectangle at it's start. // stroke (optimized) } /** * @see Graphics2D#drawRenderableImage(RenderableImage, AffineTransform) */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { drawRenderedImage(img.createDefaultRendering(), xform); } /** * @see Graphics2D#drawRenderedImage(RenderedImage, AffineTransform) */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { BufferedImage image = null; if (img instanceof BufferedImage) { image = (BufferedImage)img; } else { ColorModel cm = img.getColorModel(); int width = img.getWidth(); int height = img.getHeight(); WritableRaster raster = cm.createCompatibleWritableRaster(width, height); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); Hashtable properties = new Hashtable(); String[] keys = img.getPropertyNames(); if (keys!=null) { for (int i = 0; i < keys.length; i++) { properties.put(keys[i], img.getProperty(keys[i])); } } BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties); img.copyData(raster); image=result; } drawImage(image, xform, null); } /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void drawRoundRect(int x,int y,int w,int h,int aw,int ah) { } //============ Oval operations ======================= /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, float x,float y) { } /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, int x,int y) { } public void drawString(String s,float x,float y) { newTextBlock(x, y); pw.print("[("); pw.printRaw(PDFStringHelper.makeRawPDFString(s)); pw.println(")] TJ"); closeBlock(); } /** * This draws a string. * * @oaran s String to draw * @param x coordinate * @param y coordinate */ public void drawString(String s,int x,int y) { drawString(s, (float) x, (float) y); } public void drawTransparentString(String s, float x, float y) { newTextBlock(x, y); pw.println("3 Tr"); pw.print("[("); pw.printRaw(PDFStringHelper.makeRawPDFString(s)); pw.println(")] TJ"); closeBlock(); } /** * This draws a transparent string. * * @oaran s String to draw * @param x coordinate * @param y coordinate */ public void drawTransparentString(String s, int x, int y) { drawTransparentString(s, (float) x, (float) y); } /** * @see Graphics2D#fill(Shape) */ public void fill(Shape s) { followPath(s, FILL); } /** *

Not implemented

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void fill3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Fills an arc, joining the start and end coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void fillArc(int x,int y,int w,int h,int sa,int aa) { // here we fool the optimizer. We force any open path to be closed, // then draw the arc. Finally, as the optimizer hasn't stroke'd the // path, we close and fill it, and mark the Stroke as closed. // // Note: The lineto to the centre of the object is required, because // the fill only fills the arc. Skipping this includes an extra // chord, which isn't correct. Peter May 31 2000 closeBlock(); drawArc(x,y,w,h,sa,aa); lineto(x+(w>>1),y+(h>>1)); closeBlock("b"); // closepath and fill } //============ Extension operations ============================== // These are extensions, and provide access to PDF Specific // operators. /** *

Draws a filled oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillOval(int x,int y,int w,int h) { fillArc(x, y, w, h, 0, 360); } //============ Polygon operations ======================= /** * Fills a polygon. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void fillPolygon(int[] xp,int[] yp,int np) { closeBlock(); // finish off any previous paths polygon(xp,yp,np); closeBlock("b"); // closepath, fill and stroke } //============ Image operations ======================= /** * Fills a rectangle with the current colour * * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillRect(int x,int y,int w,int h) { // end any path & stroke. This ensures the fill is on this // rectangle, and not on any previous graphics closeBlock(); drawRect(x,y,w,h); closeBlock("B"); // rectangle, fill stroke } //============ Round Rectangle operations ======================= /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void fillRoundRect(int x,int y,int w,int h,int aw,int ah) { } /////////////////////////////////////////////// // // // implementation specific methods // // private void followPath(Shape s, int drawType) { PathIterator points; if (s==null) return; if (drawType==STROKE) { if (!(stroke instanceof BasicStroke)) { s = stroke.createStrokedShape(s); followPath(s, FILL); return; } } // if (drawType==STROKE) { // setStrokeDiff(stroke, oldStroke); // oldStroke = stroke; // setStrokePaint(); // } // else if (drawType==FILL) // setFillPaint(); points = s.getPathIterator(IDENTITY); int segments = 0; float[] coords = new float[6]; while(!points.isDone()) { segments++; int segtype = points.currentSegment(coords); switch(segtype) { case PathIterator.SEG_CLOSE: pw.print("h "); break; case PathIterator.SEG_CUBICTO: curveto(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case PathIterator.SEG_LINETO: lineto(coords[0], coords[1]); break; case PathIterator.SEG_MOVETO: moveto(coords[0], coords[1]); break; case PathIterator.SEG_QUADTO: curveto(coords[0], coords[1], coords[2], coords[3]); break; } points.next(); } switch (drawType) { case FILL: if (segments > 0) { if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("B*"); else closeBlock("B"); } break; case STROKE: if (segments > 0) closeBlock("S"); break; case CLIP: default: //drawType==CLIP if (segments == 0) drawRect(0, 0, 0, 0); if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("W*"); else closeBlock("W"); } } /** * @see Graphics2D#getBackground() */ public Color getBackground() { return background; } /** * Returns the Shape of the clipping region * As my JDK docs say, this may break with Java 2D. * @return Shape of the clipping region */ public Shape getClip() { return null; } /** * Returns the Rectangle that fits the current clipping region * @return the Rectangle that fits the current clipping region */ public Rectangle getClipBounds() { return clipRectangle; } //============ Color operations ======================= /** * Returns the current pen Colour * @return the current pen Colour */ public Color getColor() { return (paint instanceof Color) ? (Color) paint : Color.black; } /** * @see Graphics2D#getComposite() */ public Composite getComposite() { return composite; } /** * @see Graphics2D#getDeviceConfiguration() */ public GraphicsConfiguration getDeviceConfiguration() { return dg2.getDeviceConfiguration(); } /** * Return's the current font. * @return the current font. */ public Font getFont() { if(font==null) setFont(new Font("SansSerif",Font.PLAIN,12)); return font; } /** * Returns the FontMetrics for a font. *

This doesn't work correctly. Perhaps having some way of mapping * the base 14 fonts to our own FontMetrics implementation? * @param font The java.awt.Font to return the metrics for * @return FontMetrics for a font */ public FontMetrics getFontMetrics(Font font) { Frame dummy = new Frame(); dummy.addNotify(); Image image = dummy.createImage(100, 100); if (image == null) { System.err.println("getFontMetrics: image is null"); } Graphics graphics = image.getGraphics(); return graphics.getFontMetrics(font); } /** * @see Graphics2D#getFontRenderContext() */ public FontRenderContext getFontRenderContext() { boolean antialias = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING)); boolean fractions = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS)); return new FontRenderContext(new AffineTransform(), antialias, fractions); } /** * Returns the associated PDFPage for this graphic * @return the associated PDFPage for this graphic */ public PDFPage getPage() { return page; } /** * Returns the current pen Colour * @return the current pen Colour */ public Paint getPaint() { return paint; } /** * @param arg0 a key * @return the rendering hint */ public Object getRenderingHint(Key arg0) { return rhints.get(arg0); } /** * @see Graphics2D#getRenderingHints() */ public RenderingHints getRenderingHints() { return rhints; } /** * @see Graphics2D#getStroke() */ public Stroke getStroke() { return originalStroke; } /** * @see Graphics2D#getTransform() */ public AffineTransform getTransform() { return new AffineTransform(oTransform); } /** * Returns the PrintWriter handling the underlying stream * @return the PrintWriter handling the underlying stream */ public RawPrintWriter getWriter() { return pw; } /** * @see Graphics2D#hit(Rectangle, Shape, boolean) */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = stroke.createStrokedShape(s); } s = transform.createTransformedShape(s); Area area = new Area(s); if (clip != null) area.intersect(clip); return area.intersects(rect.x, rect.y, rect.width, rect.height); } /** * This initialises the stream by saving the current graphics state, and * setting up the default line width (for us). * * It also sets up the instance ready for graphic operations and any * optimisations. * *

For child instances, the stream is already open, so this should keep * things happy. */ private void init() { PageFormat pf = page.getPageFormat(); // save graphics state (restored by dispose) if(child) { pw.print("q "); } // now initialise the instance //setColor(Color.black); paint = Color.black; // possible: if parent.color is not black, then force black? // must check to see what AWT does? // Original User Space Transform (identity) oTransform = new AffineTransform(); // Transform from Java Space to PDF Space pTransform = new AffineTransform(); pTransform.translate(0, pf.getHeight()); pTransform.scale(1d, -1d); // Combined Transform User->Java->PDF transform = new AffineTransform(oTransform); transform.preConcatenate(pTransform); // Set the line width setStroke(DEF_STROKE); } /** * This is called by PDFPage when creating a Graphcis instance. * @param page The PDFPage to draw onto. */ protected void init(PDFPage page) { this.page = page; // We are the parent instance child = false; // Now create a stream to store the graphics in PDFStream stream = new PDFStream(); // To view detail in uncompressed format comment out the next line stream.setDeflate(true); page.getPDFDocument().add(stream); page.add(stream); pw = new RawPrintWriter(stream.getOutputStream()); // initially, we are limited to the page size clipRectangle = page.getImageableArea(); // finally initialise the stream init(); } /** * This method is used internally by create() and by the PDFJob class * @param page PDFPage to draw into * @param pw PrintWriter to use */ protected void init(PDFPage page, RawPrintWriter pw) { this.page = page; this.pw = pw; // In this case, we didn't create the stream (our parent did) // so child is true (see dispose) child = true; // finally initialise the stream init(); } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"l "); lx=(float)x; ly=(float)y; } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(int x,int y) { newPath(); if(lx!=x && ly!=y) pw.print(cxy(x,y)+"l "); lx=x; ly=y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"m "); lx=(float)x; ly=(float)y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(int x,int y) { newPath(); if(lx!=x || ly!=y) pw.print(cxy(x,y)+"m "); lx=x; ly=y; } /** * Functions that draw lines should start by calling this. It starts a * new path unless inStroke is set, in that case it uses the existing path */ void newPath() { if(inText) { closeBlock(); } if(!inStroke) { if(pre_np!=null) { pw.print(pre_np); // this is the prefix set by setOrientation() pre_np = null; } pw.print("n "); } inText=false; inStroke=true; // an unlikely coordinate to fool the moveto() optimizer lx = ly = -9999; } /** *

Functions that draw text should start by calling this. It starts a text * block (accounting for media orientation) unless we are already in a Text * block.

* *

It also handles if the font has been changed since the current text * block was started, so your function will be current.

* * @param x x coordinate in java space * @param y y coordinate in java space */ void newTextBlock(float x,float y) { // close the current path if there is one if(inStroke) { closeBlock(); } // create the text block if one is not current. If we are, the newFont // condition at the end catches font changes if(!inText) { // This ensures that there is a font available getFont(); pw.print("q BT "); tx=ty=0; if (realTransform != null) { pw.println("" + df.format(pTransform.getScaleX()) + " " + "" + df.format(pTransform.getShearY()) + " " + "" + df.format(pTransform.getShearX()) + " " + "" + df.format(pTransform.getScaleY()) + " " + "" + df.format(pTransform.getTranslateX()) + " " + "" + df.format(pTransform.getTranslateY()) + " Tm" ); } // produce the text matrix for the media // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait // //pw.println("1 0 0 1 0 0 Tm"); // break; // // case PageFormat.LANDSCAPE: // Landscape // pw.println("0 1 -1 0 0 0 Tm"); // rotate // break; // // case 180: // Inverted Portrait // pw.println("1 0 0 -1 0 0 Tm"); // break; // // case PageFormat.REVERSE_LANDSCAPE: // Seascape // pw.println("0 -1 1 0 0 0 Tm"); // rotate // break; // } // move the text cursor by an absolute amount pw.print(txy(x,y)+"Td "); } else { // move the text cursor by a relative amount pw.print(twh(x, y, tx, ty) + "Td "); //pw.print(txy(x,y)+"Td "); } // preserve the coordinates for the next time tx = x; ty = y; if(newFont || !inText) pw.print(pdffont.getName()+" "+font.getSize()+" Tf "); // later add colour changes here (if required) inStroke = newFont = false; inText = true; } /** * This is used to add a polygon to the current path. * Used by drawPolygon(), drawPolyline() and fillPolygon() etal * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon * @see #drawPolygon * @see #drawPolyline * @see #fillPolygon */ public void polygon(int[] xp,int[] yp,int np) { // newPath() not needed here as moveto does it ;-) moveto(xp[0],yp[0]); for(int i=1;iSets the clipping region to that of a Shape. * @param s Shape to clip to. */ public void setClip(Shape s) { Rectangle r = s.getBounds(); setClip(r.x,r.y,r.width,r.height); } /** * Sets the color for drawing * @param c Color to use */ public void setColor(Color c) { setPaint(c); } /** * @see Graphics2D#setComposite(Composite) */ public void setComposite(Composite comp) { this.composite = comp; } /** * This extension sets the line width to the default of 1mm which is what * Java uses when drawing to a PrintJob. */ public void setDefaultLineWidth() { closeBlock(); // draw any path before we change the line width pw.println("1 w"); } /** * This sets the font. * @param f java.awt.Font to set to. */ public void setFont(Font f) { // optimize: Save some space if the font is already the current one. if(font!=f) { font = f; pdffont = page.getFont("/Type1",f.getName(),f.getStyle()); // mark the font as changed newFont = true; } } public void setExistingTtfFont(Font f) { if (font != f) { font = f; pdffont = page.getFont("/TrueType", f.getName(), f.getStyle()); // mark the font as changed newFont = true; } } public void setTtfFont(Font f, File file) throws IOException { if (font != f) { font = f; pdffont = page.getEmbeddedFont(f.getName(), f.getStyle(), file); // mark the font as changed newFont = true; } } private void setLineCap(int cap) { int lineCap = 0; switch (cap) { case BasicStroke.JOIN_MITER: lineCap = 0; break; case BasicStroke.JOIN_ROUND: lineCap = 1; break; case BasicStroke.JOIN_BEVEL: lineCap = 2; break; } if (this.lineCap != lineCap) { closeBlock(); // draw any path before we change the line width this.lineCap = lineCap; pw.println(""+lineCap+" J"); } } private void setLineJoin(int join) { int lineJoin = 0; switch (join) { case BasicStroke.JOIN_MITER: lineJoin = 0; break; case BasicStroke.JOIN_ROUND: lineJoin = 1; break; case BasicStroke.JOIN_BEVEL: lineJoin = 2; break; } if (this.lineJoin != lineJoin) { closeBlock(); // draw any path before we change the line width this.lineJoin = lineJoin; pw.println(""+lineJoin+" j"); } } /** * This extension allows the width of the drawn line to be set * @param width Line width in pdf graphic units (points) */ public void setLineWidth(float width) { if (width != this.lineWidth) { closeBlock(); // draw any path before we change the line width this.lineWidth = width; pw.println(""+width+" w"); } } private void setMiterLimit(float limit) { if (limit != this.miterLimit) { closeBlock(); // draw any path before we change the line width this.miterLimit = limit; pw.println(""+limit+" M"); } } /** * Sets the paint for drawing * @param paint Paint to use */ public void setPaint(Paint paint) { this.paint = paint; if (paint instanceof Color) { Color c = (Color) paint; double r = ((double) c.getRed()) / 255.0; double g = ((double) c.getGreen()) / 255.0; double b = ((double) c.getBlue()) / 255.0; closeBlock(); // This ensures any paths are drawn in the previous // colours pw.println("" + r + " " + g + " " + b + " rg " + r + " " + g + " " + b + " RG"); } } /** * Not implemented, as this is not supported in the PDF specification. */ public void setPaintMode() { } /** * Sets a rendering hint * @param arg0 * @param arg1 */ public void setRenderingHint(Key arg0, Object arg1) { if (arg1 != null) { rhints.put(arg0, arg1); } else { rhints.remove(arg0); } } // Add Graphics2D methods. /** * @see Graphics2D#setRenderingHints(Map) */ public void setRenderingHints(Map hints) { rhints.clear(); rhints.putAll(hints); } /** * @see Graphics2D#setStroke(Stroke) */ public void setStroke(Stroke s) { originalStroke = s; this.stroke = transformStroke(s); if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) stroke; setLineCap(bs.getEndCap()); setLineJoin(bs.getLineJoin()); setLineWidth(bs.getLineWidth()); setMiterLimit(bs.getMiterLimit()); // TODO: Line dash pattern } } /** * @see Graphics2D#setTransform(AffineTransform) */ public void setTransform(AffineTransform t) { // Save copy of original transform. oTransform = t; // Working copy of transform transform = new AffineTransform(t); // Concatenate Java Space to PDF Space transform transform.preConcatenate(pTransform); this.stroke = transformStroke(originalStroke); inText = false; } /** * Not implemented, as this is not supported in the PDF specification. * @param c1 Color to xor with */ public void setXORMode(Color c1) { } //============ Text operations ======================= /** * @see Graphics2D#shear(double, double) */ public void shear(double shx, double shy) { transform.shear(shx, shy); } /** * @see Graphics2D#transform(AffineTransform) */ public void transform(AffineTransform tx) { transform.concatenate(tx); this.stroke = transformStroke(originalStroke); } private Stroke transformStroke(Stroke stroke) { if (!(stroke instanceof BasicStroke)) return stroke; BasicStroke st = (BasicStroke)stroke; float scale = (float)Math.sqrt(Math.abs(transform.getDeterminant())); float dash[] = st.getDashArray(); if (dash != null) { for (int k = 0; k < dash.length; ++k) dash[k] *= scale; } return new BasicStroke(st.getLineWidth() * scale, st.getEndCap(), st.getLineJoin(), st.getMiterLimit(), dash, st.getDashPhase() * scale); } /** * @see Graphics2D#translate(double, double) */ public void translate(double tx, double ty) { transform.translate(tx, ty); trax = (float) tx; tray = (float) ty; } /** * @see Graphics#translate(int, int) */ public void translate(int x, int y) { translate((double)x, (double)y); } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @param tx coordinate * @param ty coordinate * @return String containing the coordinates in PDF text space */ private String twh(float x,float y,float tx,float ty) { float nx=x, ny=y; float ntx=tx, nty=ty; /*int mh = (int) page.getPageFormat().getHeight(); int sx=1,sy=1;*/ Point2D ptSrc = new Point2D.Float(x, y); Point2D ptDst = new Point2D.Float(); transform.transform(ptSrc, ptDst); Point2D pt2Src = new Point2D.Float(tx, ty); Point2D pt2Dst = new Point2D.Float(); transform.transform(pt2Src, pt2Dst); /*ny = mh - y; nty = mh - ty; nx = sx*(nx-ntx); ny = sy*(ny-nty);*/ nx = (float) (ptDst.getX() - pt2Dst.getX()); ny = (float) (ptDst.getY() - pt2Dst.getY()); return ""+df.format(nx)+" "+df.format(ny)+" "; } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF text space */ private String txy(float x,float y) { // float nx=x, ny=y; // int mh = (int) page.getPageFormat().getHeight(); Point2D ptSrc = new Point2D.Float(x, y); Point2D ptDst = new Point2D.Float(); transform.transform(ptSrc, ptDst); // // handle any translations // x+=trax; // y+=tray; // // nx = x; // ny = mh - y; // // System.out.println("\ntxy(" + ptSrc.getX() + ", " + ptSrc.getY() + ")"); // System.out.println("Old [" + nx + "," + ny + "]"); // System.out.println("Trn [" + ptDst.getX() + ", " + ptDst.getY() + "]"); // // return ""+df.format(nx)+" "+df.format(ny)+" "; return ""+df.format(ptDst.getX())+" "+df.format(ptDst.getY())+" "; } private void realTransform(AffineTransform t) { inText = false; AffineTransform newTransform = new AffineTransform(t); newTransform.preConcatenate(pTransform); realTransform = newTransform; transform = new AffineTransform(pTransform); pw.println("" + df.format(newTransform.getScaleX()) + " " + "" + df.format(newTransform.getShearY()) + " " + "" + df.format(newTransform.getShearX()) + " " + "" + df.format(newTransform.getScaleY()) + " " + "" + df.format(newTransform.getTranslateX()) + " " + "" + df.format(newTransform.getTranslateY()) + " cm" ); } private void saveState() { pw.println("q"); } private void restoreState() { pw.println("Q"); } public void drawStringWithTransform(String s, float x, float y, AffineTransform trans) { saveState(); realTransform(trans); drawString(s, x, y); restoreState(); realTransform = null; } public void drawTransparentStringWithTransform(String s, float x, float y, AffineTransform trans) { saveState(); realTransform(trans); drawTransparentString(s, x, y); restoreState(); realTransform = null; } } // end class PDFGraphics \ No newline at end of file +/* * $Id: PDFGraphics.java,v 1.6 2007/09/22 12:58:40 gil1 Exp $ * * $Date: 2007/09/22 12:58:40 $ * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package gnu.jpdf; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.RenderingHints.Key; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.awt.print.PageFormat; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Serializable; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Hashtable; import java.util.Locale; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * This class is our implementation of AWT's Graphics class. It provides a * Java standard way of rendering into a PDF Document's Page. * * @author Peter T Mount, http://www.retep.org.uk/pdf/ * @author Eric Z. Beard, ericzbeard@hotmail.com * @author Gilbert DeLeeuw, gil1@users.sourceforge.net * @version $Revision: 1.6 $, $Date: 2007/09/22 12:58:40 $ * @see gnu.jpdf.PDFGraphics */ public class PDFGraphics extends Graphics2D implements Serializable { /** * One degree in radians */ private static final double degrees_to_radians = Math.PI/180.0; private static final int FILL = 1; private static final int STROKE = 2; private static final int CLIP = 3; private static final AffineTransform IDENTITY = new AffineTransform(); private static final Stroke DEF_STROKE = new BasicStroke(); /* * NOTE: The original class is the work of Peter T. Mount, who released it * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as * follows: * The package name was changed to gnu.pdf. * The formatting was changed a little bit. * This used to subclass an abstract class in a different package with * the same name (confusing). Now it's one concrete class. * drawImage() was implemented * It is still licensed under the LGPL. */ // Implementation notes: // // Pages 333-335 of the PDF Reference Manual // // Unless absolutely required, use the moveto, lineto and rectangle // operators to perform those actions. // They contain some extra optimizations // which will reduce the output size by up to half in some cases. // // About fill operators: For correct operation, any fill operation should // start with closeBlock(), which will ensure any previous path is completed, // otherwise you may find the fill will include previous items private static final DecimalFormat df = new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.ENGLISH)); //JPEXS: cache for already used images private static Map usedImages = new WeakHashMap(); private Color background; /** * This is true for any Graphics instance that didn't create the stream. * @see #create */ private boolean child; private Area clip; /** * This holds the current clipRectangle */ protected Rectangle clipRectangle; private Composite composite; private Graphics2D dg2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB).createGraphics(); /** * This is the current font (in Java format) */ private Font font; /** * Part of the optimizer: * When true, we are drawing a path. */ private boolean inStroke; /** * Part of the optimizer: * When true, we are within a Text Block. */ private boolean inText; // true if within a Text Block - see newTextBlock() /** * The stroke line cap code; */ private int lineCap = 0; /** * The stroke line join code */ private int lineJoin = 0; /** * The stroke line width */ private float lineWidth = 1.0f; /** * Part of the optimizer: * The last known moveto/lineto x coordinate * @see #moveto * @see #lineto */ private float lx; // last known moveto/lineto coordinates /** * Part of the optimizer: * The last known moveto/lineto y coordinate * @see #moveto * @see #lineto */ private float ly; // last known moveto/lineto coordinates private float miterLimit = 10.0f; /** * Part of the optimizer: * When true, the font has changed. */ private boolean newFont; // true if the font changes - see newTextBlock() /** * This is a reference to the PDFPage we are rendering to. */ private PDFPage page; /** * This is the current pen/fill color */ private Paint paint; /** * This is the current font (in PDF format) */ private PDFFont pdffont; /** * Part of the optimizer: * This is written to the stream when the newPath() is called. np then clears * this value. */ private String pre_np; // PDF space transform private AffineTransform pTransform; /** * This is the PrintWriter used to write PDF drawing commands to the Stream */ private RawPrintWriter pw; /** * RenderingHints */ private RenderingHints rhints = new RenderingHints(null); private Stroke stroke; // Start of Graphics2D properties private AffineTransform transform; /** * Part of the optimizer: * The last x coordinate when rendering text */ private float tx; // the last coordinate for text rendering /** * Part of the optimizer: * The last y coordinate when rendering text */ private float ty; // the last coordinate for text rendering /** * @see Graphics2D#addRenderingHints(Map) */ public void addRenderingHints(Map hints) { rhints.putAll(hints); } /** * This produces an arc by breaking it down into one or more Bezier curves. * It is used internally to implement the drawArc and fillArc methods. * * @param axc X coordinate of arc centre * @param ayc Y coordinate of arc centre * @param width of bounding rectangle * @param height of bounding rectangle * @param ang1 Start angle * @param ang2 End angle * @param clockwise true to draw clockwise, false anti-clockwise */ public void arc(double axc,double ayc, double width,double height, double ang1,double ang2, boolean clockwise) { double adiff; double x0, y0; double x3r, y3r; boolean first = true; // may not need this //if( ar < 0 ) { //ang1 += fixed_180; //ang2 += fixed_180; //ar = - ar; //} double ang1r = (ang1%360.0)*degrees_to_radians; double sin0 = Math.sin(ang1r); double cos0 = Math.cos(ang1r); x0 = axc + width*cos0; y0 = ayc + height*sin0; // NB: !clockwise here as Java Space is inverted to User Space if( !clockwise ) { // Quadrant reduction while ( ang1 < ang2 ) ang2 -= 360.0; while ( (adiff = ang2 - ang1) < -90.0 ) { double w = sin0; sin0 = -cos0; cos0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 -= 90.0; first = false; } } else { // Quadrant reduction while ( ang2 < ang1 ) ang2 += 360.0; while ( (adiff = ang2 - ang1) > 90.0 ) { double w = cos0; cos0 = -sin0; sin0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 += 90.0; first = false; } } // Compute the intersection of the tangents. // We know that -fixed_90 <= adiff <= fixed_90. double trad = Math.tan(adiff * (degrees_to_radians / 2)); double ang2r = ang2 * degrees_to_radians; double xt = x0 - trad * width*sin0; double yt = y0 + trad * height*cos0; arc_add(first, width, height, x0, y0, (axc + width * Math.cos(ang2r)), (ayc + height * Math.sin(ang2r)), xt, yt); } /** * Used by the arc method to actually add an arc to the path * Important: We write directly to the stream here, because this method * operates in User space, rather than Java space. * @param first true if the first arc * @param w width * @param h height * @param x0 coordinate * @param y0 coordinate * @param x3 coordinate * @param y3 coordinate * @param xt coordinate * @param yt coordinate */ private void arc_add(boolean first, double w,double h, double x0,double y0, double x3,double y3, double xt,double yt) { double dx = xt - x0, dy = yt - y0; double dist = dx*dx + dy*dy; double w2 = w*w, h2=h*h; double r2 = w2+h2; double fw = 0.0, fh = 0.0; if(dist < (r2*1.0e8)) { // JM fw = (w2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/w2))) : 0.0; fh = (h2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/h2))) : 0.0; } // The path must have a starting point if(first) moveto(x0,y0); double x = x0+((xt-x0)*fw); double y = y0+((yt-y0)*fh); x0 = x3+((xt-x3)*fw); y0 = y3+((yt-y3)*fh); // Finally the actual curve. curveto(x,y,x0,y0,x3,y3); } /** * This simply draws a White Rectangle to clear the area * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clearRect(int x,int y,int w,int h) { closeBlock(); pw.print("q 1 1 1 RG ");// save state, set colour to White drawRect(x,y,w,h); closeBlock("B Q"); // close fill & stroke, then restore state } /** * @see Graphics2D#clip(Shape) */ public void clip(Shape s) { if (s == null) { setClip(null); return; } if (clip == null) clip = new Area(s); else clip.intersect(new Area(s)); // followPath(s, CLIP); } /** * This extra method allows PDF users to clip to a Polygon. * *

In theory you could use setClip(), except that java.awt.Graphics * only supports Rectangle with that method, so we will have an extra * method. * @param p Polygon to clip to */ public void clipPolygon(Polygon p) { closeBlock(); // finish off any existing path polygon(p.xpoints,p.ypoints,p.npoints); closeBlock("W"); // clip to current path clipRectangle = p.getBounds(); } /** * Clips to a set of coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clipRect(int x,int y,int w,int h) { setClip(x,y,w,h); } /** * All functions should call this to close any existing optimized blocks. */ void closeBlock() { closeBlock("S"); } /** *

This is used by code that use the path in any way other than Stroke * (like Fill, close path & Stroke etc). Usually this is used internally.

* * @param code PDF operators that will close the path */ void closeBlock(String code) { if(inText) { pw.println("ET Q"); // setOrientation(); // fixes Orientation matrix } if(inStroke) { pw.println(code); } inStroke=inText=false; } /** * This is unsupported - how do you do this with Vector graphics? * @param x coordinate * @param y coordinate * @param w width * @param h height * @param dx coordinate * @param dy coordinate */ public void copyArea(int x,int y,int w,int h,int dx,int dy) { // Hmm... Probably need to keep track of everything // that has been drawn so far to get the contents of an area } //============ Line operations ======================= /** *

This returns a child instance of this Graphics object. As with AWT, the * affects of using the parent instance while the child exists, is not * determined.

* *

Once complete, the child should be released with it's dispose() * method which will restore the graphics state to it's parent.

* * @return Graphics object to render onto the page */ @Override public Graphics create() { closeBlock(); PDFGraphics g = createGraphic(page,pw); // The new instance inherits a few items g.clipRectangle = new Rectangle(clipRectangle); return (Graphics) g; } // end create() /** * This method creates a new instance of the class based on the page * and a print writer. * * @param page the page to attach to * @param pw the PrintWriter to attach to. */ protected PDFGraphics createGraphic(PDFPage page, RawPrintWriter pw) { PDFGraphics g = new PDFGraphics(); g.init(page,pw); return g; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=(float)x2; ly=(float)y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(double x1,double y1,double x2,double y2,double x3,double y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=(float)x3; ly=(float)y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=x2; ly=y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(int x1,int y1,int x2,int y2,int x3,int y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=x3; ly=y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=(float)x2; ly=(float)y2; } // Arcs are horrible and complex. They are at the end of the // file, because they are the largest. This is because, unlike // Postscript, PDF doesn't have any arc operators, so we must // implement them by converting into one or more Bezier curves // (which is how Postscript does them internally). /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=x2; ly=y2; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(double w,double h) { double nw=w,nh=h; // scratch // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait //nw = w; nh = -h; // break; // // case PageFormat.LANDSCAPE: // // Landscape // nw = h; // nh = w; // break; // //// case 180: //// // Inverse Portrait //// nw = -w; //// //nh = h; //// break; // // case PageFormat.REVERSE_LANDSCAPE: // // Seascape // nw = -h; // nh = -w; // break; // } return ""+df.format(nw)+" "+df.format(nh)+" "; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(int w,int h) { return cwh((double)w,(double)h); } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(double x, double y) { return "" + df.format(x) + " " + df.format(y) + " "; } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(int x,int y) { return cxy((double)x,(double)y); } /** *

This releases any resources used by this Graphics object. You must use * this method once finished with it. Leaving it open will leave the PDF * stream in an inconsistent state, and will produce errors.

* *

If this was created with Graphics.create() then the parent instance * can be used again. If not, then this closes the graphics operations for * this page when used with PDFJob.

* *

When using PDFPage, you can create another fresh Graphics instance, * which will draw over this one.

* */ public void dispose() { closeBlock(); if(child) { pw.println("Q"); // restore graphics context } else { pw.close(); // close the stream if were the parent } } // ********************************************* // **** Implementation of java.awt.Graphics **** // ********************************************* //============ Rectangle operations ======================= /** * @see Graphics2D#draw(Shape) */ public void draw(Shape s) { followPath(s, STROKE); } /** *

Not implemented

* *

Draws a 3-D highlighted outline of the specified rectangle. * The edges of the rectangle are highlighted so that they appear * to be beveled and lit from the upper left corner. * The colors used for the highlighting effect are determined based on * the current color. The resulting rectangle covers an area that * is width + 1 pixels wide by height + 1 pixels tall. *

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void draw3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Draws an arc * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void drawArc(int x,int y,int w,int h,int sa,int aa) { w=w>>1; h=h>>1; x+=w; y+=h; arc((double)x,(double)y, (double)w,(double)h, (double)-sa,(double)(-sa-aa), false); } /** *

Not implemented

* * @param data a byte[] value * @param offset an int value * @param length an int value * @param x an int value * @param y an int value */ public void drawBytes(byte[] data, int offset, int length, int x, int y) { } //============ Optimizers ======================= /** * @see Graphics2D#drawGlyphVector(GlyphVector, float, float) */ public void drawGlyphVector(GlyphVector g, float x, float y) { Shape s = g.getOutline(x, y); fill(s); } /** * @see Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int) */ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { BufferedImage result = img; if (op != null) { result = op.createCompatibleDestImage(img, img.getColorModel()); result = op.filter(img, result); } drawImage(result, x, y, null); } /** * @see Graphics2D#drawImage(Image, AffineTransform, ImageObserver) */ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { // return drawImage(img, null, xform, null, obs); return true; } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,Color bgcolor, ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs), bgcolor,obs); } /** * Draw's an image onto the page * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, Color bgcolor,ImageObserver obs) { closeBlock(); pw.print("q "); // save state Color c = getColor(); // save current colour setColor(bgcolor); // change the colour drawRect(x,y,w,h); closeBlock("B Q"); // fill stroke, restore state paint = c; // restore original colour return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draws an image onto the page.

* *

This method is implemented with ASCIIbase85 encoding and the * zip stream deflater. It results in a stream that is anywhere * from 3 to 10 times as big as the image. This obviously needs some * improvement, but it works well for small images

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, ImageObserver obs) { closeBlock(); PDFImage image; if (usedImages.containsKey(img)) { image = usedImages.get(img); } else { image = new PDFImage(img, x, y, w, h, obs); // The image needs to be registered in several places page.getPDFDocument().setImageName(image); page.getPDFDocument().add(image); usedImages.put(img, image); } page.addToProcset("/ImageC"); page.addImageResource(image.getName() + " " + image.getSerialID() + " 0 R"); // JM /*page.addResource("/XObject << " + image.getName() + " " + image.getSerialID() + " 0 R >>");*/ // q w 0 0 h x y cm % the coordinate matrix AffineTransform newTransform = new AffineTransform(w, 0, 0, h, x, y); newTransform.preConcatenate(pTransform); AffineTransform transformToSet = newTransform; pw.print("q " + transformToSet.getScaleX() + " " + transformToSet.getShearY() + " " + transformToSet.getShearX() + " " + transformToSet.getScaleY() + " " + transformToSet.getTranslateX() + " " + transformToSet.getTranslateY() + " cm \n" + image.getName() + " Do\nQ\n"); return false; } /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, Color bgcolor,ImageObserver obs) { return false; } //============ Clipping operations ======================= /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, ImageObserver obs) { // This shouldn't be too bad, just change the coordinate matrix return false; } /** * Draws a line between two coordinates. * * If the first coordinate is the same as the last one drawn * (i.e. a previous drawLine, moveto, etc) it is ignored. * @param x1 coordinate * @param y1 coordinate * @param x2 coordinate * @param y2 coordinate */ public void drawLine(int x1,int y1,int x2,int y2) { moveto(x1,y1); lineto(x2,y2); } //============ Arcs operations ============================== // These are the standard Graphics operators. They use the // arc extension operators to achieve the affect. /** *

Draws an oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawOval(int x,int y,int w,int h) { drawArc(x, y, w, h, 0, 360); } /** * Draws a polygon, linking the first and last coordinates. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void drawPolygon(int[] xp,int[] yp,int np) { polygon(xp,yp,np); closeBlock("s"); // close path and stroke } /** * Draws a polyline. The first and last coordinates are not linked. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polyline */ public void drawPolyline(int[] xp,int[] yp,int np) { polygon(xp,yp,np); // no stroke, as we keep the optimizer in stroke state } /** * We override Graphics.drawRect as it doesn't join the 4 lines. * Also, PDF provides us with a Rectangle operator, so we will use that. * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawRect(int x,int y,int w,int h) { newPath(); pw.print(cxy(x,y)+cwh(w,h)+"re "); // rectangle lx=x; // I don't know if this is correct, but lets see if PDF ends ly=y; // the rectangle at it's start. // stroke (optimized) } /** * @see Graphics2D#drawRenderableImage(RenderableImage, AffineTransform) */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { drawRenderedImage(img.createDefaultRendering(), xform); } /** * @see Graphics2D#drawRenderedImage(RenderedImage, AffineTransform) */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { BufferedImage image = null; if (img instanceof BufferedImage) { image = (BufferedImage)img; } else { ColorModel cm = img.getColorModel(); int width = img.getWidth(); int height = img.getHeight(); WritableRaster raster = cm.createCompatibleWritableRaster(width, height); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); Hashtable properties = new Hashtable(); String[] keys = img.getPropertyNames(); if (keys!=null) { for (int i = 0; i < keys.length; i++) { properties.put(keys[i], img.getProperty(keys[i])); } } BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties); img.copyData(raster); image=result; } drawImage(image, xform, null); } /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void drawRoundRect(int x,int y,int w,int h,int aw,int ah) { } //============ Oval operations ======================= /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, float x,float y) { } /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, int x,int y) { } public void drawStringWithMode(String s, float x, float y, int mode) { newTextBlock(x, y); if (mode > -1) { pw.println("" + mode + " Tr"); } if (pdffont instanceof PDFEmbeddedFont) { pw.print("[("); pw.printRaw(PDFStringHelper.makeRawPDFString(s)); pw.println(")] TJ"); } else { pw.print(PDFStringHelper.makePDFString(s)); pw.println(" Tj"); } closeBlock(); } @Override public void drawString(String s, float x, float y) { drawStringWithMode(s, x, y, -1); } /** * This draws a string. * * @param s * @oaran s String to draw * @param x coordinate * @param y coordinate */ @Override public void drawString(String s,int x,int y) { drawString(s, (float) x, (float) y); } public void drawTransparentString(String s, float x, float y) { drawStringWithMode(s, x, y, 3); } /** * This draws a transparent string. * * @oaran s String to draw * @param x coordinate * @param y coordinate */ public void drawTransparentString(String s, int x, int y) { drawTransparentString(s, (float) x, (float) y); } /** * @see Graphics2D#fill(Shape) */ public void fill(Shape s) { followPath(s, FILL); } /** *

Not implemented

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void fill3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Fills an arc, joining the start and end coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void fillArc(int x,int y,int w,int h,int sa,int aa) { // here we fool the optimizer. We force any open path to be closed, // then draw the arc. Finally, as the optimizer hasn't stroke'd the // path, we close and fill it, and mark the Stroke as closed. // // Note: The lineto to the centre of the object is required, because // the fill only fills the arc. Skipping this includes an extra // chord, which isn't correct. Peter May 31 2000 closeBlock(); drawArc(x,y,w,h,sa,aa); lineto(x+(w>>1),y+(h>>1)); closeBlock("b"); // closepath and fill } //============ Extension operations ============================== // These are extensions, and provide access to PDF Specific // operators. /** *

Draws a filled oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillOval(int x,int y,int w,int h) { fillArc(x, y, w, h, 0, 360); } //============ Polygon operations ======================= /** * Fills a polygon. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void fillPolygon(int[] xp,int[] yp,int np) { closeBlock(); // finish off any previous paths polygon(xp,yp,np); closeBlock("b"); // closepath, fill and stroke } //============ Image operations ======================= /** * Fills a rectangle with the current colour * * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillRect(int x,int y,int w,int h) { // end any path & stroke. This ensures the fill is on this // rectangle, and not on any previous graphics closeBlock(); drawRect(x,y,w,h); closeBlock("B"); // rectangle, fill stroke } //============ Round Rectangle operations ======================= /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void fillRoundRect(int x,int y,int w,int h,int aw,int ah) { } /////////////////////////////////////////////// // // // implementation specific methods // // private void followPath(Shape s, int drawType) { PathIterator points; if (s==null) return; if (drawType==STROKE) { if (!(stroke instanceof BasicStroke)) { s = stroke.createStrokedShape(s); followPath(s, FILL); return; } } // if (drawType==STROKE) { // setStrokeDiff(stroke, oldStroke); // oldStroke = stroke; // setStrokePaint(); // } // else if (drawType==FILL) // setFillPaint(); points = s.getPathIterator(IDENTITY); int segments = 0; float[] coords = new float[6]; while(!points.isDone()) { segments++; int segtype = points.currentSegment(coords); switch(segtype) { case PathIterator.SEG_CLOSE: pw.print("h "); break; case PathIterator.SEG_CUBICTO: curveto(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case PathIterator.SEG_LINETO: lineto(coords[0], coords[1]); break; case PathIterator.SEG_MOVETO: moveto(coords[0], coords[1]); break; case PathIterator.SEG_QUADTO: curveto(coords[0], coords[1], coords[2], coords[3]); break; } points.next(); } switch (drawType) { case FILL: if (segments > 0) { if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("B*"); else closeBlock("B"); } break; case STROKE: if (segments > 0) closeBlock("S"); break; case CLIP: default: //drawType==CLIP if (segments == 0) drawRect(0, 0, 0, 0); if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("W*"); else closeBlock("W"); } } /** * @see Graphics2D#getBackground() */ public Color getBackground() { return background; } /** * Returns the Shape of the clipping region * As my JDK docs say, this may break with Java 2D. * @return Shape of the clipping region */ public Shape getClip() { return null; } /** * Returns the Rectangle that fits the current clipping region * @return the Rectangle that fits the current clipping region */ public Rectangle getClipBounds() { return clipRectangle; } //============ Color operations ======================= /** * Returns the current pen Colour * @return the current pen Colour */ public Color getColor() { return (paint instanceof Color) ? (Color) paint : Color.black; } /** * @see Graphics2D#getComposite() */ public Composite getComposite() { return composite; } /** * @see Graphics2D#getDeviceConfiguration() */ public GraphicsConfiguration getDeviceConfiguration() { return dg2.getDeviceConfiguration(); } /** * Return's the current font. * @return the current font. */ public Font getFont() { if(font==null) setFont(new Font("SansSerif",Font.PLAIN,12)); return font; } /** * Returns the FontMetrics for a font. *

This doesn't work correctly. Perhaps having some way of mapping * the base 14 fonts to our own FontMetrics implementation? * @param font The java.awt.Font to return the metrics for * @return FontMetrics for a font */ public FontMetrics getFontMetrics(Font font) { Frame dummy = new Frame(); dummy.addNotify(); Image image = dummy.createImage(100, 100); if (image == null) { System.err.println("getFontMetrics: image is null"); } Graphics graphics = image.getGraphics(); return graphics.getFontMetrics(font); } /** * @see Graphics2D#getFontRenderContext() */ public FontRenderContext getFontRenderContext() { boolean antialias = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING)); boolean fractions = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS)); return new FontRenderContext(new AffineTransform(), antialias, fractions); } /** * Returns the associated PDFPage for this graphic * @return the associated PDFPage for this graphic */ public PDFPage getPage() { return page; } /** * Returns the current pen Colour * @return the current pen Colour */ public Paint getPaint() { return paint; } /** * @param arg0 a key * @return the rendering hint */ public Object getRenderingHint(Key arg0) { return rhints.get(arg0); } /** * @see Graphics2D#getRenderingHints() */ public RenderingHints getRenderingHints() { return rhints; } /** * @see Graphics2D#getStroke() */ public Stroke getStroke() { return stroke; } /** * @see Graphics2D#getTransform() */ public AffineTransform getTransform() { return new AffineTransform(transform); } /** * Returns the PrintWriter handling the underlying stream * @return the PrintWriter handling the underlying stream */ public RawPrintWriter getWriter() { return pw; } /** * @see Graphics2D#hit(Rectangle, Shape, boolean) */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = stroke.createStrokedShape(s); } Area area = new Area(s); if (clip != null) area.intersect(clip); return area.intersects(rect.x, rect.y, rect.width, rect.height); } /** * This initialises the stream by saving the current graphics state, and * setting up the default line width (for us). * * It also sets up the instance ready for graphic operations and any * optimisations. * *

For child instances, the stream is already open, so this should keep * things happy. */ private void init() { PageFormat pf = page.getPageFormat(); // save graphics state (restored by dispose) if(child) { pw.print("q "); } // now initialise the instance //setColor(Color.black); paint = Color.black; // possible: if parent.color is not black, then force black? // must check to see what AWT does? // Original User Space Transform (identity) // Transform from Java Space to PDF Space pTransform = new AffineTransform(); pTransform.translate(0, pf.getHeight()); pTransform.scale(1d, -1d); // Combined Transform User->Java->PDF setNewTranform(new AffineTransform()); // Set the line width setStroke(DEF_STROKE); } /** * This is called by PDFPage when creating a Graphcis instance. * @param page The PDFPage to draw onto. */ protected void init(PDFPage page) { this.page = page; // We are the parent instance child = false; // Now create a stream to store the graphics in PDFStream stream = new PDFStream(); // To view detail in uncompressed format comment out the next line stream.setDeflate(true); page.getPDFDocument().add(stream); page.add(stream); pw = new RawPrintWriter(stream.getOutputStream()); // initially, we are limited to the page size clipRectangle = page.getImageableArea(); // finally initialise the stream init(); } /** * This method is used internally by create() and by the PDFJob class * @param page PDFPage to draw into * @param pw PrintWriter to use */ protected void init(PDFPage page, RawPrintWriter pw) { this.page = page; this.pw = pw; // In this case, we didn't create the stream (our parent did) // so child is true (see dispose) child = true; // finally initialise the stream init(); } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"l "); lx=(float)x; ly=(float)y; } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(int x,int y) { newPath(); if(lx!=x && ly!=y) pw.print(cxy(x,y)+"l "); lx=x; ly=y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"m "); lx=(float)x; ly=(float)y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(int x,int y) { newPath(); if(lx!=x || ly!=y) pw.print(cxy(x,y)+"m "); lx=x; ly=y; } /** * Functions that draw lines should start by calling this. It starts a * new path unless inStroke is set, in that case it uses the existing path */ void newPath() { if(inText) { closeBlock(); } if(!inStroke) { if(pre_np!=null) { pw.print(pre_np); // this is the prefix set by setOrientation() pre_np = null; } pw.print("n "); } inText=false; inStroke=true; // an unlikely coordinate to fool the moveto() optimizer lx = ly = -9999; } /** *

Functions that draw text should start by calling this. It starts a text * block (accounting for media orientation) unless we are already in a Text * block.

* *

It also handles if the font has been changed since the current text * block was started, so your function will be current.

* * @param x x coordinate in java space * @param y y coordinate in java space */ void newTextBlock(float x,float y) { // close the current path if there is one if(inStroke) { closeBlock(); } // create the text block if one is not current. If we are, the newFont // condition at the end catches font changes if(!inText) { // This ensures that there is a font available getFont(); pw.print("q BT "); tx=ty=0; AffineTransform tm = new AffineTransform(pTransform); pw.println("" + df.format(tm.getScaleX()) + " " + "" + df.format(tm.getShearY()) + " " + "" + df.format(tm.getShearX()) + " " + "" + df.format(tm.getScaleY()) + " " + "" + df.format(tm.getTranslateX()) + " " + "" + df.format(tm.getTranslateY()) + " Tm" ); // produce the text matrix for the media // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait // //pw.println("1 0 0 1 0 0 Tm"); // break; // // case PageFormat.LANDSCAPE: // Landscape // pw.println("0 1 -1 0 0 0 Tm"); // rotate // break; // // case 180: // Inverted Portrait // pw.println("1 0 0 -1 0 0 Tm"); // break; // // case PageFormat.REVERSE_LANDSCAPE: // Seascape // pw.println("0 -1 1 0 0 0 Tm"); // rotate // break; // } // move the text cursor by an absolute amount pw.print(txy(x,y)+"Td "); } else { // move the text cursor by a relative amount pw.print(twh(x, y, tx, ty) + "Td "); //pw.print(txy(x,y)+"Td "); } // preserve the coordinates for the next time tx = x; ty = y; if(newFont || !inText) pw.print(pdffont.getName()+" "+font.getSize()+" Tf "); // later add colour changes here (if required) inStroke = newFont = false; inText = true; } /** * This is used to add a polygon to the current path. * Used by drawPolygon(), drawPolyline() and fillPolygon() etal * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon * @see #drawPolygon * @see #drawPolyline * @see #fillPolygon */ public void polygon(int[] xp,int[] yp,int np) { // newPath() not needed here as moveto does it ;-) moveto(xp[0],yp[0]); for(int i=1;iSets the clipping region to that of a Shape. * @param s Shape to clip to. */ public void setClip(Shape s) { Rectangle r = s.getBounds(); setClip(r.x,r.y,r.width,r.height); } /** * Sets the color for drawing * @param c Color to use */ public void setColor(Color c) { setPaint(c); } /** * @see Graphics2D#setComposite(Composite) */ public void setComposite(Composite comp) { this.composite = comp; } /** * This extension sets the line width to the default of 1mm which is what * Java uses when drawing to a PrintJob. */ public void setDefaultLineWidth() { closeBlock(); // draw any path before we change the line width pw.println("1 w"); } /** * This sets the font. * @param f java.awt.Font to set to. */ public void setFont(Font f) { // optimize: Save some space if the font is already the current one. if(font!=f) { font = f; pdffont = page.getFont("/Type1",f.getName(),f.getStyle()); // mark the font as changed newFont = true; } } public void setExistingTtfFont(Font f) { if (font != f) { font = f; pdffont = page.getFont("/TrueType", f.getName(), f.getStyle()); // mark the font as changed newFont = true; } } public void setTtfFont(Font f, File file) throws IOException { if (font != f) { font = f; pdffont = page.getEmbeddedFont(f.getName(), f.getStyle(), file); // mark the font as changed newFont = true; } } private void setLineCap(int cap) { int lineCap = 0; switch (cap) { case BasicStroke.JOIN_MITER: lineCap = 0; break; case BasicStroke.JOIN_ROUND: lineCap = 1; break; case BasicStroke.JOIN_BEVEL: lineCap = 2; break; } if (this.lineCap != lineCap) { closeBlock(); // draw any path before we change the line width this.lineCap = lineCap; pw.println(""+lineCap+" J"); } } private void setLineJoin(int join) { int lineJoin = 0; switch (join) { case BasicStroke.JOIN_MITER: lineJoin = 0; break; case BasicStroke.JOIN_ROUND: lineJoin = 1; break; case BasicStroke.JOIN_BEVEL: lineJoin = 2; break; } if (this.lineJoin != lineJoin) { closeBlock(); // draw any path before we change the line width this.lineJoin = lineJoin; pw.println(""+lineJoin+" j"); } } /** * This extension allows the width of the drawn line to be set * @param width Line width in pdf graphic units (points) */ public void setLineWidth(float width) { if (width != this.lineWidth) { closeBlock(); // draw any path before we change the line width this.lineWidth = width; pw.println(""+width+" w"); } } private void setMiterLimit(float limit) { if (limit != this.miterLimit) { closeBlock(); // draw any path before we change the line width this.miterLimit = limit; pw.println(""+limit+" M"); } } /** * Sets the paint for drawing * @param paint Paint to use */ public void setPaint(Paint paint) { this.paint = paint; if (paint instanceof Color) { Color c = (Color) paint; double r = ((double) c.getRed()) / 255.0; double g = ((double) c.getGreen()) / 255.0; double b = ((double) c.getBlue()) / 255.0; closeBlock(); // This ensures any paths are drawn in the previous // colours pw.println("" + r + " " + g + " " + b + " rg " + r + " " + g + " " + b + " RG"); } } /** * Not implemented, as this is not supported in the PDF specification. */ public void setPaintMode() { } /** * Sets a rendering hint * @param arg0 * @param arg1 */ public void setRenderingHint(Key arg0, Object arg1) { if (arg1 != null) { rhints.put(arg0, arg1); } else { rhints.remove(arg0); } } // Add Graphics2D methods. /** * @see Graphics2D#setRenderingHints(Map) */ public void setRenderingHints(Map hints) { rhints.clear(); rhints.putAll(hints); } /** * @see Graphics2D#setStroke(Stroke) */ public void setStroke(Stroke s) { this.stroke = s; if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) stroke; setLineCap(bs.getEndCap()); setLineJoin(bs.getLineJoin()); setLineWidth(bs.getLineWidth()); setMiterLimit(bs.getMiterLimit()); // TODO: Line dash pattern } } /** * @see Graphics2D#setTransform(AffineTransform) */ public void setTransform(AffineTransform t) { setNewTranform(new AffineTransform(t)); } /** * Not implemented, as this is not supported in the PDF specification. * @param c1 Color to xor with */ public void setXORMode(Color c1) { } //============ Text operations ======================= /** * @see Graphics2D#shear(double, double) */ public void shear(double shx, double shy) { AffineTransform newTransform = new AffineTransform(transform); newTransform.shear(shx, shy); setNewTranform(newTransform); } /** * @see Graphics2D#transform(AffineTransform) */ public void transform(AffineTransform tx) { AffineTransform newTransform = new AffineTransform(transform); newTransform.concatenate(tx); setNewTranform(newTransform); } /** * @see Graphics2D#translate(double, double) */ public void translate(double tx, double ty) { AffineTransform newTransform = new AffineTransform(transform); newTransform.translate(tx, ty); setNewTranform(newTransform); } /** * @see Graphics#translate(int, int) */ public void translate(int x, int y) { translate((double)x, (double)y); } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @param tx coordinate * @param ty coordinate * @return String containing the coordinates in PDF text space */ private String twh(float x,float y,float tx,float ty) { float nx=x, ny=y; float ntx=tx, nty=ty; nx = (float) (x - tx); ny = (float) (y - ty); return ""+df.format(nx)+" "+df.format(ny)+" "; } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF text space */ private String txy(float x, float y) { Point2D ptSrc = new Point2D.Float(x, y); Point2D ptDst = new Point2D.Float(); pTransform.transform(ptSrc, ptDst); return "" + df.format(ptDst.getX()) + " " + df.format(ptDst.getY()) + " "; } private void setNewTranform(AffineTransform t) { closeBlock(); AffineTransform newTransform = new AffineTransform(t); AffineTransform transformToSet = new AffineTransform(newTransform); if (transform != null) { AffineTransform realTransformWithPTransform = new AffineTransform(transform); realTransformWithPTransform.preConcatenate(pTransform); AffineTransform inverted = new AffineTransform(realTransformWithPTransform); try { inverted.invert(); } catch (NoninvertibleTransformException ex) { Logger.getLogger(PDFGraphics.class.getName()).log(Level.SEVERE, null, ex); } AffineTransform newTransformWithPTransform = new AffineTransform(newTransform); newTransformWithPTransform.preConcatenate(pTransform); transformToSet = newTransformWithPTransform; transformToSet.preConcatenate(inverted); } else { transformToSet.preConcatenate(pTransform); } transform = newTransform; pw.println("" + df.format(transformToSet.getScaleX()) + " " + "" + df.format(transformToSet.getShearY()) + " " + "" + df.format(transformToSet.getShearX()) + " " + "" + df.format(transformToSet.getScaleY()) + " " + "" + df.format(transformToSet.getTranslateX()) + " " + "" + df.format(transformToSet.getTranslateY()) + " cm" ); } private void saveState() { pw.println("q"); } private void restoreState() { pw.println("Q"); } } // end class PDFGraphics \ No newline at end of file