mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-23 21:35:45 +00:00
1 line
64 KiB
Java
1 line
64 KiB
Java
/*
|
|
* $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<Image, PDFImage> usedImages = new WeakHashMap<Image, PDFImage>();
|
|
|
|
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)
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
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.
|
|
*
|
|
* <p>
|
|
* 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
|
|
*/
|
|
@Override
|
|
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");
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* 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.</p>
|
|
*
|
|
* @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
|
|
*/
|
|
@Override
|
|
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 =======================
|
|
/**
|
|
* <p>
|
|
* 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.</p>
|
|
*
|
|
* <p>
|
|
* Once complete, the child should be released with it's dispose() method
|
|
* which will restore the graphics state to it's parent.</p>
|
|
*
|
|
* @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 <code>PrintWriter</code> 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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* 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.</p>
|
|
*
|
|
* <p>
|
|
* 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.</p>
|
|
*
|
|
* <p>
|
|
* When using PDFPage, you can create another fresh Graphics instance, which
|
|
* will draw over this one.</p>
|
|
*
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
public void draw(Shape s) {
|
|
followPath(s, STROKE);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Not implemented</p>
|
|
*
|
|
* <p>
|
|
* 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.
|
|
* </p>
|
|
*
|
|
* @param x an <code>int</code> value
|
|
* @param y an <code>int</code> value
|
|
* @param width an <code>int</code> value
|
|
* @param height an <code>int</code> value
|
|
* @param raised a <code>boolean</code> value
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Not implemented</p>
|
|
*
|
|
* @param data a <code>byte[]</code> value
|
|
* @param offset an <code>int</code> value
|
|
* @param length an <code>int</code> value
|
|
* @param x an <code>int</code> value
|
|
* @param y an <code>int</code> value
|
|
*/
|
|
@Override
|
|
public void drawBytes(byte[] data, int offset, int length, int x, int y) {
|
|
|
|
}
|
|
|
|
//============ Optimizers =======================
|
|
/**
|
|
* @see Graphics2D#drawGlyphVector(GlyphVector, float, float)
|
|
*/
|
|
@Override
|
|
public void drawGlyphVector(GlyphVector g, float x, float y) {
|
|
Shape s = g.getOutline(x, y);
|
|
fill(s);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int)
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
|
|
// return drawImage(img, null, xform, null, obs);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Draw's an image onto the page, with a backing colour.</p>
|
|
*
|
|
* @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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
public boolean drawImage(Image img, int x, int y, ImageObserver obs) {
|
|
return drawImage(img, x, y, img.getWidth(obs), img.getHeight(obs), obs);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Draw's an image onto the page, with a backing colour.</p>
|
|
*
|
|
* @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
|
|
*/
|
|
@Override
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Draws an image onto the page.</p>
|
|
*
|
|
* <p>
|
|
* 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</p>
|
|
*
|
|
* @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
|
|
*/
|
|
@Override
|
|
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
|
|
* <p>
|
|
* 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
|
|
*/
|
|
@Override
|
|
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
|
|
* <p>
|
|
* 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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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.
|
|
/**
|
|
* <p>
|
|
* Draws an oval</p>
|
|
*
|
|
* @param x coordinate
|
|
* @param y coordinate
|
|
* @param w width
|
|
* @param h height
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
|
|
drawRenderedImage(img.createDefaultRendering(), xform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#drawRenderedImage(RenderedImage, AffineTransform)
|
|
*/
|
|
@Override
|
|
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<String, Object> properties = new Hashtable<String, Object>();
|
|
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
|
|
*/
|
|
@Override
|
|
public void drawRoundRect(int x, int y, int w, int h, int aw, int ah) {
|
|
}
|
|
|
|
//============ Oval operations =======================
|
|
/**
|
|
* Draws a string using a AttributedCharacterIterator.
|
|
* <p>
|
|
* This is not supported yet, as I have no idea what an
|
|
* AttributedCharacterIterator is.
|
|
* <p>
|
|
* This method is new to the Java2 API.
|
|
*/
|
|
@Override
|
|
public void drawString(java.text.AttributedCharacterIterator aci,
|
|
float x, float y) {
|
|
}
|
|
|
|
/**
|
|
* Draws a string using a AttributedCharacterIterator.
|
|
* <p>
|
|
* This is not supported yet, as I have no idea what an
|
|
* AttributedCharacterIterator is.
|
|
* <p>
|
|
* This method is new to the Java2 API.
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
public void fill(Shape s) {
|
|
followPath(s, FILL);
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Not implemented</p>
|
|
*
|
|
* @param x an <code>int</code> value
|
|
* @param y an <code>int</code> value
|
|
* @param width an <code>int</code> value
|
|
* @param height an <code>int</code> value
|
|
* @param raised a <code>boolean</code> value
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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.
|
|
/**
|
|
* <p>
|
|
* Draws a filled oval</p>
|
|
*
|
|
* @param x coordinate
|
|
* @param y coordinate
|
|
* @param w width
|
|
* @param h height
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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()
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
public Shape getClip() {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the Rectangle that fits the current clipping region
|
|
*
|
|
* @return the Rectangle that fits the current clipping region
|
|
*/
|
|
@Override
|
|
public Rectangle getClipBounds() {
|
|
return clipRectangle;
|
|
}
|
|
|
|
//============ Color operations =======================
|
|
/**
|
|
* Returns the current pen Colour
|
|
*
|
|
* @return the current pen Colour
|
|
*/
|
|
@Override
|
|
public Color getColor() {
|
|
return (paint instanceof Color) ? (Color) paint : Color.black;
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#getComposite()
|
|
*/
|
|
@Override
|
|
public Composite getComposite() {
|
|
return composite;
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#getDeviceConfiguration()
|
|
*/
|
|
@Override
|
|
public GraphicsConfiguration getDeviceConfiguration() {
|
|
return dg2.getDeviceConfiguration();
|
|
}
|
|
|
|
/**
|
|
* Return's the current font.
|
|
*
|
|
* @return the current font.
|
|
*/
|
|
@Override
|
|
public Font getFont() {
|
|
if (font == null) {
|
|
setFont(new Font("SansSerif", Font.PLAIN, 12));
|
|
}
|
|
return font;
|
|
}
|
|
|
|
/**
|
|
* Returns the FontMetrics for a font.
|
|
* <p>
|
|
* 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
|
|
*/
|
|
@Override
|
|
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()
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
public Paint getPaint() {
|
|
return paint;
|
|
}
|
|
|
|
/**
|
|
* @param arg0 a key
|
|
* @return the rendering hint
|
|
*/
|
|
@Override
|
|
public Object getRenderingHint(Key arg0) {
|
|
return rhints.get(arg0);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#getRenderingHints()
|
|
*/
|
|
@Override
|
|
public RenderingHints getRenderingHints() {
|
|
return rhints;
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#getStroke()
|
|
*/
|
|
@Override
|
|
public Stroke getStroke() {
|
|
return stroke;
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#getTransform()
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
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.
|
|
*
|
|
* <p>
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* 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.</p>
|
|
*
|
|
* <p>
|
|
* It also handles if the font has been changed since the current text block
|
|
* was started, so your function will be current.</p>
|
|
*
|
|
* @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; i < np; i++) {
|
|
lineto(xp[i], yp[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#rotate(double)
|
|
*/
|
|
@Override
|
|
public void rotate(double theta) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.rotate(theta);
|
|
setNewTranform(newTransform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#rotate(double, double, double)
|
|
*/
|
|
@Override
|
|
public void rotate(double theta, double x, double y) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.rotate(theta, x, y);
|
|
setNewTranform(newTransform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#scale(double, double)
|
|
*/
|
|
@Override
|
|
public void scale(double sx, double sy) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.scale(sx, sy);
|
|
setNewTranform(newTransform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#setBackground(Color)
|
|
*/
|
|
@Override
|
|
public void setBackground(Color color) {
|
|
background = color;
|
|
}
|
|
|
|
/**
|
|
* Clips to a set of coordinates
|
|
*
|
|
* @param x coordinate
|
|
* @param y coordinate
|
|
* @param w width
|
|
* @param h height
|
|
*/
|
|
@Override
|
|
public void setClip(int x, int y, int w, int h) {
|
|
clipRectangle = new Rectangle(x, y, w, h);
|
|
closeBlock(); // finish off any existing paths
|
|
drawRect(x, y, w, h);
|
|
closeBlock("W n"); // clip to current path
|
|
}
|
|
|
|
/**
|
|
* As my JDK docs say, this may break with Java 2D.
|
|
* <p>
|
|
* Sets the clipping region to that of a Shape.
|
|
*
|
|
* @param s Shape to clip to.
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
public void setColor(Color c) {
|
|
setPaint(c);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#setComposite(Composite)
|
|
*/
|
|
@Override
|
|
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.
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
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.
|
|
*/
|
|
@Override
|
|
public void setPaintMode() {
|
|
}
|
|
|
|
/**
|
|
* Sets a rendering hint
|
|
*
|
|
* @param arg0
|
|
* @param arg1
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
public void setRenderingHints(Map<?, ?> hints) {
|
|
rhints.clear();
|
|
rhints.putAll(hints);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#setStroke(Stroke)
|
|
*/
|
|
@Override
|
|
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)
|
|
*/
|
|
@Override
|
|
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
|
|
*/
|
|
@Override
|
|
public void setXORMode(Color c1) {
|
|
}
|
|
|
|
//============ Text operations =======================
|
|
/**
|
|
* @see Graphics2D#shear(double, double)
|
|
*/
|
|
@Override
|
|
public void shear(double shx, double shy) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.shear(shx, shy);
|
|
setNewTranform(newTransform);
|
|
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#transform(AffineTransform)
|
|
*/
|
|
@Override
|
|
public void transform(AffineTransform tx) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.concatenate(tx);
|
|
setNewTranform(newTransform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics2D#translate(double, double)
|
|
*/
|
|
@Override
|
|
public void translate(double tx, double ty) {
|
|
AffineTransform newTransform = new AffineTransform(transform);
|
|
newTransform.translate(tx, ty);
|
|
setNewTranform(newTransform);
|
|
}
|
|
|
|
/**
|
|
* @see Graphics#translate(int, int)
|
|
*/
|
|
@Override
|
|
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
|
|
|