aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java')
-rw-r--r--src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java1450
1 files changed, 1450 insertions, 0 deletions
diff --git a/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java b/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java
new file mode 100644
index 0000000..962b502
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java
@@ -0,0 +1,1450 @@
+/*
+ * Copyright 2002 by Jim Moore <jim@scolamoore.com>.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.GradientPaint;
+import java.awt.TexturePaint;
+import java.awt.MediaTracker;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.Transparency;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+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.RenderingHints.Key;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.ArrayList;
+import java.io.ByteArrayOutputStream;
+//
+import java.util.Set;
+import java.util.Iterator;
+import java.awt.font.TextAttribute;
+
+public class PdfGraphics2D extends Graphics2D {
+
+ private static final int FILL = 1;
+ private static final int STROKE = 2;
+ private static final int CLIP = 3;
+ private BasicStroke strokeOne = new BasicStroke(1);
+
+ private static AffineTransform IDENTITY = new AffineTransform();
+
+ private Font font;
+ private BaseFont baseFont;
+ private float fontSize;
+ private AffineTransform transform;
+ private Paint paint;
+ private Color background;
+ private float width;
+ private float height;
+
+ private Area clip;
+
+ private RenderingHints rhints = new RenderingHints(null);
+
+ private Stroke stroke;
+ private Stroke originalStroke;
+
+ private PdfContentByte cb;
+
+ /** Storage for BaseFont objects created. */
+ private HashMap baseFonts;
+
+ private boolean disposeCalled = false;
+
+ private FontMapper fontMapper;
+
+ private ArrayList kids;
+
+ private boolean kid = false;
+
+ private Graphics2D dg2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB).createGraphics();
+
+ private boolean onlyShapes = false;
+
+ private Stroke oldStroke;
+ private Paint paintFill;
+ private Paint paintStroke;
+
+ private MediaTracker mediaTracker;
+
+ // Added by Jurij Bilas
+ protected boolean underline; // indicates if the font style is underlined
+
+ protected PdfGState fillGState[] = new PdfGState[256];
+ protected PdfGState strokeGState[] = new PdfGState[256];
+ protected int currentFillGState = 255;
+ protected int currentStrokeGState = 255;
+
+ public static int AFM_DIVISOR = 1000; // used to calculate coordinates
+
+ private boolean convertImagesToJPEG = false;
+ private float jpegQuality = .95f;
+
+ private PdfGraphics2D() {
+ dg2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ }
+
+ /**
+ * Constructor for PDFGraphics2D.
+ *
+ */
+ PdfGraphics2D(PdfContentByte cb, float width, float height, FontMapper fontMapper, boolean onlyShapes, boolean convertImagesToJPEG, float quality) {
+ super();
+ dg2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ try {
+ Class.forName("com.sun.image.codec.jpeg.JPEGCodec");
+ }
+ catch (Throwable t) {
+ convertImagesToJPEG = false;
+ }
+ this.convertImagesToJPEG = convertImagesToJPEG;
+ this.jpegQuality = quality;
+ this.onlyShapes = onlyShapes;
+ this.transform = new AffineTransform();
+ this.baseFonts = new HashMap();
+ if (!onlyShapes) {
+ this.fontMapper = fontMapper;
+ if (this.fontMapper == null)
+ this.fontMapper = new DefaultFontMapper();
+ }
+ this.kids = new ArrayList();
+ paint = Color.black;
+ background = Color.white;
+ setFont(new Font("sanserif", Font.PLAIN, 12));
+ this.cb = cb;
+ cb.saveState();
+ this.width = width;
+ this.height = height;
+ clip = new Area(new Rectangle2D.Float(0, 0, width, height));
+ clip(clip);
+ originalStroke = stroke = oldStroke = strokeOne;
+ setStrokeDiff(stroke, null);
+ cb.saveState();
+ }
+
+ /**
+ * @see Graphics2D#draw(Shape)
+ */
+ public void draw(Shape s) {
+ followPath(s, STROKE);
+ }
+
+ /**
+ * @see Graphics2D#drawImage(Image, AffineTransform, ImageObserver)
+ */
+ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
+ return drawImage(img, null, xform, null, obs);
+ }
+
+ /**
+ * @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#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);
+ }
+
+ /**
+ * @see Graphics2D#drawRenderableImage(RenderableImage, AffineTransform)
+ */
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ drawRenderedImage(img.createDefaultRendering(), xform);
+ }
+
+ /**
+ * @see Graphics#drawString(String, int, int)
+ */
+ public void drawString(String s, int x, int y) {
+ drawString(s, (float)x, (float)y);
+ }
+
+ /**
+ * Calculates position and/or stroke thickness depending on the font size
+ * @param d value to be converted
+ * @param i font size
+ * @return position and/or stroke thickness depending on the font size
+ */
+ public static double asPoints(double d, int i) {
+ return (d * (double)i) / (double)AFM_DIVISOR;
+ }
+ /**
+ * This routine goes through the attributes and sets the font
+ * before calling the actual string drawing routine
+ * @param iter
+ */
+ protected void doAttributes(AttributedCharacterIterator iter) {
+ underline = false;
+ Set set = iter.getAttributes().keySet();
+ for(Iterator iterator = set.iterator(); iterator.hasNext();) {
+ AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute)iterator.next();
+ if (!(attribute instanceof TextAttribute))
+ continue;
+ TextAttribute textattribute = (TextAttribute)attribute;
+ if(textattribute.equals(TextAttribute.FONT)) {
+ Font font = (Font)iter.getAttributes().get(textattribute);
+ setFont(font);
+ }
+ else if(textattribute.equals(TextAttribute.UNDERLINE)) {
+ if(iter.getAttributes().get(textattribute) == TextAttribute.UNDERLINE_ON)
+ underline = true;
+ }
+ else if(textattribute.equals(TextAttribute.SUPERSCRIPT)) {
+ throw new RuntimeException("TextAttribute.SUPERSCRIPT not supported");
+ /*
+ iter.getAttributes().get(textattribute);
+ Integer _tmp = TextAttribute.SUPERSCRIPT_SUPER;
+ subscript = true;
+ */
+ }
+ else if(textattribute.equals(TextAttribute.SIZE)) {
+ Object obj = iter.getAttributes().get(textattribute);
+ Font font1 = null;
+ if(obj instanceof Integer) {
+ int i = ((Integer)obj).intValue();
+ font1 = getFont().deriveFont(getFont().getStyle(), i);
+ }
+ else if(obj instanceof Float) {
+ float f = ((Float)obj).floatValue();
+ font1 = getFont().deriveFont(getFont().getStyle(), f);
+ }
+ else {
+ throw new RuntimeException("Unknown type " + obj.getClass() + " for attribute SIZE");
+ }
+ setFont(font1);
+ }
+ else if(textattribute.equals(TextAttribute.FOREGROUND)) {
+ setColor((Color) iter.getAttributes().get(textattribute));
+ }
+ else if(textattribute.equals(TextAttribute.BACKGROUND)) {
+ throw new RuntimeException("Background color not supported");
+ }
+ else {
+ throw new RuntimeException("Unknown TextAttribute: " + textattribute);
+ }
+ }
+ }
+
+ /**
+ * @see Graphics2D#drawString(String, float, float)
+ */
+ public void drawString(String s, float x, float y) {
+ if (s.length() == 0)
+ return;
+ setFillPaint();
+ if (onlyShapes) {
+// TextLayout tl = new TextLayout(s, this.font, new FontRenderContext(new AffineTransform(), false, true));
+// tl.draw(this, x, y);
+ drawGlyphVector(this.font.layoutGlyphVector(new FontRenderContext(new AffineTransform(), true, false), s.toCharArray(), 0, s.length(), java.awt.Font.LAYOUT_LEFT_TO_RIGHT), x, y);
+// Use the following line to compile in JDK 1.3
+// drawGlyphVector(this.font.createGlyphVector(new FontRenderContext(new AffineTransform(), true, false), s), x, y);
+ }
+ else {
+ AffineTransform at = getTransform();
+ AffineTransform at2 = getTransform();
+ at2.translate(x, y);
+ at2.concatenate(font.getTransform());
+ setTransform(at2);
+ AffineTransform inverse = this.normalizeMatrix();
+ AffineTransform flipper = AffineTransform.getScaleInstance(1,-1);
+ inverse.concatenate(flipper);
+ double[] mx = new double[6];
+ inverse.getMatrix(mx);
+ cb.beginText();
+ cb.setFontAndSize(baseFont, fontSize);
+ cb.setTextMatrix((float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]);
+ double width = 0;
+ if (font.getSize2D() > 0) {
+ float scale = 1000 / font.getSize2D();
+ width = font.deriveFont(AffineTransform.getScaleInstance(scale, scale)).getStringBounds(s, getFontRenderContext()).getWidth() / scale;
+ }
+ if (s.length() > 1) {
+ float adv = ((float)width - baseFont.getWidthPoint(s, fontSize)) / (s.length() - 1);
+ cb.setCharacterSpacing(adv);
+ }
+ cb.showText(s);
+ if (s.length() > 1) {
+ cb.setCharacterSpacing(0);
+ }
+ cb.endText();
+ setTransform(at);
+ if(underline)
+ {
+ // These two are supposed to be taken from the .AFM file
+ //int UnderlinePosition = -100;
+ int UnderlineThickness = 50;
+ //
+ double d = asPoints((double)UnderlineThickness, (int)fontSize);
+ setStroke(new BasicStroke((float)d));
+ y = (float)((double)(y) + asPoints((double)(UnderlineThickness), (int)fontSize));
+ Line2D line = new Line2D.Double((double)x, (double)y, (double)(width+x), (double)y);
+ draw(line);
+ }
+ }
+ }
+
+ /**
+ * @see Graphics#drawString(AttributedCharacterIterator, int, int)
+ */
+ public void drawString(AttributedCharacterIterator iterator, int x, int y) {
+ drawString(iterator, (float)x, (float)y);
+ }
+
+ /**
+ * @see Graphics2D#drawString(AttributedCharacterIterator, float, float)
+ */
+ public void drawString(AttributedCharacterIterator iter, float x, float y) {
+/*
+ StringBuffer sb = new StringBuffer();
+ for(char c = iter.first(); c != AttributedCharacterIterator.DONE; c = iter.next()) {
+ sb.append(c);
+ }
+ drawString(sb.toString(),x,y);
+*/
+ StringBuffer stringbuffer = new StringBuffer(iter.getEndIndex());
+ for(char c = iter.first(); c != '\uFFFF'; c = iter.next())
+ {
+ if(iter.getIndex() == iter.getRunStart())
+ {
+ if(stringbuffer.length() > 0)
+ {
+ drawString(stringbuffer.toString(), x, y);
+ FontMetrics fontmetrics = getFontMetrics();
+ x = (float)((double)x + fontmetrics.getStringBounds(stringbuffer.toString(), this).getWidth());
+ stringbuffer.delete(0, stringbuffer.length());
+ }
+ doAttributes(iter);
+ }
+ stringbuffer.append(c);
+ }
+
+ drawString(stringbuffer.toString(), x, y);
+ underline = false;
+ }
+
+ /**
+ * @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#fill(Shape)
+ */
+ public void fill(Shape s) {
+ followPath(s, FILL);
+ }
+
+ /**
+ * @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);
+ }
+
+ /**
+ * @see Graphics2D#getDeviceConfiguration()
+ */
+ public GraphicsConfiguration getDeviceConfiguration() {
+ return dg2.getDeviceConfiguration();
+ }
+
+ /**
+ * @see Graphics2D#setComposite(Composite)
+ */
+ public void setComposite(Composite comp) {
+
+ }
+
+ /**
+ * @see Graphics2D#setPaint(Paint)
+ */
+ public void setPaint(Paint paint) {
+ if (paint == null)
+ return;
+ this.paint = paint;
+// setPaint(paint, false, 0, 0);
+ }
+
+ 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);
+ }
+
+ private void setStrokeDiff(Stroke newStroke, Stroke oldStroke) {
+ if (newStroke == oldStroke)
+ return;
+ if (!(newStroke instanceof BasicStroke))
+ return;
+ BasicStroke nStroke = (BasicStroke)newStroke;
+ boolean oldOk = (oldStroke instanceof BasicStroke);
+ BasicStroke oStroke = null;
+ if (oldOk)
+ oStroke = (BasicStroke)oldStroke;
+ if (!oldOk || nStroke.getLineWidth() != oStroke.getLineWidth())
+ cb.setLineWidth(nStroke.getLineWidth());
+ if (!oldOk || nStroke.getEndCap() != oStroke.getEndCap()) {
+ switch (nStroke.getEndCap()) {
+ case BasicStroke.CAP_BUTT:
+ cb.setLineCap(0);
+ break;
+ case BasicStroke.CAP_SQUARE:
+ cb.setLineCap(2);
+ break;
+ default:
+ cb.setLineCap(1);
+ }
+ }
+ if (!oldOk || nStroke.getLineJoin() != oStroke.getLineJoin()) {
+ switch (nStroke.getLineJoin()) {
+ case BasicStroke.JOIN_MITER:
+ cb.setLineJoin(0);
+ break;
+ case BasicStroke.JOIN_BEVEL:
+ cb.setLineJoin(2);
+ break;
+ default:
+ cb.setLineJoin(1);
+ }
+ }
+ if (!oldOk || nStroke.getMiterLimit() != oStroke.getMiterLimit())
+ cb.setMiterLimit(nStroke.getMiterLimit());
+ boolean makeDash;
+ if (oldOk) {
+ if (nStroke.getDashArray() != null) {
+ if (nStroke.getDashPhase() != oStroke.getDashPhase()) {
+ makeDash = true;
+ }
+ else if (!java.util.Arrays.equals(nStroke.getDashArray(), oStroke.getDashArray())) {
+ makeDash = true;
+ }
+ else
+ makeDash = false;
+ }
+ else if (oStroke.getDashArray() != null) {
+ makeDash = true;
+ }
+ else
+ makeDash = false;
+ }
+ else {
+ makeDash = true;
+ }
+ if (makeDash) {
+ float dash[] = nStroke.getDashArray();
+ if (dash == null)
+ cb.setLiteral("[]0 d\n");
+ else {
+ cb.setLiteral('[');
+ int lim = dash.length;
+ for (int k = 0; k < lim; ++k) {
+ cb.setLiteral(dash[k]);
+ cb.setLiteral(' ');
+ }
+ cb.setLiteral(']');
+ cb.setLiteral(nStroke.getDashPhase());
+ cb.setLiteral(" d\n");
+ }
+ }
+ }
+
+ /**
+ * @see Graphics2D#setStroke(Stroke)
+ */
+ public void setStroke(Stroke s) {
+ originalStroke = s;
+ this.stroke = transformStroke(s);
+ }
+
+
+ /**
+ * Sets a rendering hint
+ * @param arg0
+ * @param arg1
+ */
+ public void setRenderingHint(Key arg0, Object arg1) {
+ rhints.put(arg0, arg1);
+ }
+
+ /**
+ * @param arg0 a key
+ * @return the rendering hint
+ */
+ public Object getRenderingHint(Key arg0) {
+ return rhints.get(arg0);
+ }
+
+ /**
+ * @see Graphics2D#setRenderingHints(Map)
+ */
+ public void setRenderingHints(Map hints) {
+ rhints.clear();
+ rhints.putAll(hints);
+ }
+
+ /**
+ * @see Graphics2D#addRenderingHints(Map)
+ */
+ public void addRenderingHints(Map hints) {
+ rhints.putAll(hints);
+ }
+
+ /**
+ * @see Graphics2D#getRenderingHints()
+ */
+ public RenderingHints getRenderingHints() {
+ return rhints;
+ }
+
+ /**
+ * @see Graphics#translate(int, int)
+ */
+ public void translate(int x, int y) {
+ translate((double)x, (double)y);
+ }
+
+ /**
+ * @see Graphics2D#translate(double, double)
+ */
+ public void translate(double tx, double ty) {
+ transform.translate(tx,ty);
+ }
+
+ /**
+ * @see Graphics2D#rotate(double)
+ */
+ public void rotate(double theta) {
+ transform.rotate(theta);
+ }
+
+ /**
+ * @see Graphics2D#rotate(double, double, double)
+ */
+ public void rotate(double theta, double x, double y) {
+ transform.rotate(theta, x, y);
+ }
+
+ /**
+ * @see Graphics2D#scale(double, double)
+ */
+ public void scale(double sx, double sy) {
+ transform.scale(sx, sy);
+ this.stroke = transformStroke(originalStroke);
+ }
+
+ /**
+ * @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);
+ }
+
+ /**
+ * @see Graphics2D#setTransform(AffineTransform)
+ */
+ public void setTransform(AffineTransform t) {
+ transform = new AffineTransform(t);
+ this.stroke = transformStroke(originalStroke);
+ }
+
+ /**
+ * @see Graphics2D#getTransform()
+ */
+ public AffineTransform getTransform() {
+ return new AffineTransform(transform);
+ }
+
+ /**
+ * @see Graphics2D#getPaint()
+ */
+ public Paint getPaint() {
+ return paint;
+ }
+
+ /**
+ * @see Graphics2D#getComposite()
+ */
+ public Composite getComposite() {
+ return null;
+ }
+
+ /**
+ * @see Graphics2D#setBackground(Color)
+ */
+ public void setBackground(Color color) {
+ background = color;
+ }
+
+ /**
+ * @see Graphics2D#getBackground()
+ */
+ public Color getBackground() {
+ return background;
+ }
+
+ /**
+ * @see Graphics2D#getStroke()
+ */
+ public Stroke getStroke() {
+ return originalStroke;
+ }
+
+
+ /**
+ * @see Graphics2D#getFontRenderContext()
+ */
+ public FontRenderContext getFontRenderContext() {
+ return new FontRenderContext(null, true, true);
+ }
+
+ /**
+ * @see Graphics#create()
+ */
+ public Graphics create() {
+ PdfGraphics2D g2 = new PdfGraphics2D();
+ g2.onlyShapes = this.onlyShapes;
+ g2.transform = new AffineTransform(this.transform);
+ g2.baseFonts = this.baseFonts;
+ g2.fontMapper = this.fontMapper;
+ g2.kids = this.kids;
+ g2.paint = this.paint;
+ g2.fillGState = this.fillGState;
+ g2.strokeGState = this.strokeGState;
+ g2.background = this.background;
+ g2.mediaTracker = this.mediaTracker;
+ g2.convertImagesToJPEG = this.convertImagesToJPEG;
+ g2.jpegQuality = this.jpegQuality;
+ g2.setFont(this.font);
+ g2.cb = this.cb.getDuplicate();
+ g2.cb.saveState();
+ g2.width = this.width;
+ g2.height = this.height;
+ g2.followPath(new Area(new Rectangle2D.Float(0, 0, width, height)), CLIP);
+ if (this.clip != null)
+ g2.clip = new Area(this.clip);
+ g2.stroke = stroke;
+ g2.originalStroke = originalStroke;
+ g2.strokeOne = (BasicStroke)g2.transformStroke(g2.strokeOne);
+ g2.oldStroke = g2.strokeOne;
+ g2.setStrokeDiff(g2.oldStroke, null);
+ g2.cb.saveState();
+ if (g2.clip != null)
+ g2.followPath(g2.clip, CLIP);
+ g2.kid = true;
+ synchronized (kids) {
+ kids.add(g2);
+ }
+ return g2;
+ }
+
+ public PdfContentByte getContent() {
+ return this.cb;
+ }
+ /**
+ * @see Graphics#getColor()
+ */
+ public Color getColor() {
+ if (paint instanceof Color) {
+ return (Color)paint;
+ } else {
+ return Color.black;
+ }
+ }
+
+ /**
+ * @see Graphics#setColor(Color)
+ */
+ public void setColor(Color color) {
+ setPaint(color);
+ }
+
+ /**
+ * @see Graphics#setPaintMode()
+ */
+ public void setPaintMode() {}
+
+ /**
+ * @see Graphics#setXORMode(Color)
+ */
+ public void setXORMode(Color c1) {
+
+ }
+
+ /**
+ * @see Graphics#getFont()
+ */
+ public Font getFont() {
+ return font;
+ }
+
+ /**
+ * @see Graphics#setFont(Font)
+ */
+ /**
+ * Sets the current font.
+ */
+ public void setFont(Font f) {
+ if (f == null)
+ return;
+ if (onlyShapes) {
+ font = f;
+ return;
+ }
+ if (f == font)
+ return;
+ font = f;
+ fontSize = f.getSize2D();
+ baseFont = getCachedBaseFont(f);
+ }
+
+ private BaseFont getCachedBaseFont(Font f) {
+ synchronized (baseFonts) {
+ BaseFont bf = (BaseFont)baseFonts.get(f.getFontName());
+ if (bf == null) {
+ bf = fontMapper.awtToPdf(f);
+ baseFonts.put(f.getFontName(), bf);
+ }
+ return bf;
+ }
+ }
+
+ /**
+ * @see Graphics#getFontMetrics(Font)
+ */
+ public FontMetrics getFontMetrics(Font f) {
+ return dg2.getFontMetrics(f);
+ }
+
+ /**
+ * @see Graphics#getClipBounds()
+ */
+ public Rectangle getClipBounds() {
+ if (clip == null)
+ return null;
+ return getClip().getBounds();
+ }
+
+ /**
+ * @see Graphics#clipRect(int, int, int, int)
+ */
+ public void clipRect(int x, int y, int width, int height) {
+ Rectangle2D rect = new Rectangle2D.Double(x,y,width,height);
+ clip(rect);
+ }
+
+ /**
+ * @see Graphics#setClip(int, int, int, int)
+ */
+ public void setClip(int x, int y, int width, int height) {
+ Rectangle2D rect = new Rectangle2D.Double(x,y,width,height);
+ setClip(rect);
+ }
+
+ /**
+ * @see Graphics2D#clip(Shape)
+ */
+ public void clip(Shape s) {
+ if (s != null)
+ s = transform.createTransformedShape(s);
+ if (clip == null)
+ clip = new Area(s);
+ else
+ clip.intersect(new Area(s));
+ followPath(s, CLIP);
+ }
+
+ /**
+ * @see Graphics#getClip()
+ */
+ public Shape getClip() {
+ try {
+ return transform.createInverse().createTransformedShape(clip);
+ }
+ catch (NoninvertibleTransformException e) {
+ return null;
+ }
+ }
+
+ /**
+ * @see Graphics#setClip(Shape)
+ */
+ public void setClip(Shape s) {
+ cb.restoreState();
+ cb.saveState();
+ if (s != null)
+ s = transform.createTransformedShape(s);
+ if (s == null) {
+ clip = null;
+ }
+ else {
+ clip = new Area(s);
+ followPath(s, CLIP);
+ }
+ paintFill = paintStroke = null;
+ currentFillGState = currentStrokeGState = 255;
+ oldStroke = strokeOne;
+ }
+
+ /**
+ * @see Graphics#copyArea(int, int, int, int, int, int)
+ */
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+
+ }
+
+ /**
+ * @see Graphics#drawLine(int, int, int, int)
+ */
+ public void drawLine(int x1, int y1, int x2, int y2) {
+ Line2D line = new Line2D.Double((double)x1, (double)y1, (double)x2, (double)y2);
+ draw(line);
+ }
+
+ /**
+ * @see Graphics#fillRect(int, int, int, int)
+ */
+ public void drawRect(int x, int y, int width, int height) {
+ draw(new Rectangle(x, y, width, height));
+ }
+
+ /**
+ * @see Graphics#fillRect(int, int, int, int)
+ */
+ public void fillRect(int x, int y, int width, int height) {
+ fill(new Rectangle(x,y,width,height));
+ }
+
+ /**
+ * @see Graphics#clearRect(int, int, int, int)
+ */
+ public void clearRect(int x, int y, int width, int height) {
+ Paint temp = paint;
+ setPaint(background);
+ fillRect(x,y,width,height);
+ setPaint(temp);
+ }
+
+ /**
+ * @see Graphics#drawRoundRect(int, int, int, int, int, int)
+ */
+ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x,y,width,height,arcWidth, arcHeight);
+ draw(rect);
+ }
+
+ /**
+ * @see Graphics#fillRoundRect(int, int, int, int, int, int)
+ */
+ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x,y,width,height,arcWidth, arcHeight);
+ fill(rect);
+ }
+
+ /**
+ * @see Graphics#drawOval(int, int, int, int)
+ */
+ public void drawOval(int x, int y, int width, int height) {
+ Ellipse2D oval = new Ellipse2D.Float((float)x, (float)y, (float)width, (float)height);
+ draw(oval);
+ }
+
+ /**
+ * @see Graphics#fillOval(int, int, int, int)
+ */
+ public void fillOval(int x, int y, int width, int height) {
+ Ellipse2D oval = new Ellipse2D.Float((float)x, (float)y, (float)width, (float)height);
+ fill(oval);
+ }
+
+ /**
+ * @see Graphics#drawArc(int, int, int, int, int, int)
+ */
+ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
+ Arc2D arc = new Arc2D.Double(x,y,width,height,startAngle, arcAngle, Arc2D.OPEN);
+ draw(arc);
+
+ }
+
+ /**
+ * @see Graphics#fillArc(int, int, int, int, int, int)
+ */
+ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
+ Arc2D arc = new Arc2D.Double(x,y,width,height,startAngle, arcAngle, Arc2D.PIE);
+ fill(arc);
+ }
+
+ /**
+ * @see Graphics#drawPolyline(int[], int[], int)
+ */
+ public void drawPolyline(int[] x, int[] y, int nPoints) {
+ Line2D line = new Line2D.Double(x[0],y[0],x[0],y[0]);
+ for (int i = 1; i < nPoints; i++) {
+ line.setLine(line.getX2(), line.getY2(), x[i], y[i]);
+ draw(line);
+ }
+ }
+
+ /**
+ * @see Graphics#drawPolygon(int[], int[], int)
+ */
+ public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+ Polygon poly = new Polygon();
+ for (int i = 0; i < nPoints; i++) {
+ poly.addPoint(xPoints[i], yPoints[i]);
+ }
+ draw(poly);
+ }
+
+ /**
+ * @see Graphics#fillPolygon(int[], int[], int)
+ */
+ public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+ Polygon poly = new Polygon();
+ for (int i = 0; i < nPoints; i++) {
+ poly.addPoint(xPoints[i], yPoints[i]);
+ }
+ fill(poly);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
+ return drawImage(img, x, y, null, observer);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, int, int, ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
+ return drawImage(img, x, y, width, height, null, observer);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, Color, ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
+ waitForImage(img);
+ return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), bgcolor, observer);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, int, int, Color, ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
+ waitForImage(img);
+ double scalex = width/(double)img.getWidth(observer);
+ double scaley = height/(double)img.getHeight(observer);
+ AffineTransform tx = AffineTransform.getTranslateInstance(x,y);
+ tx.scale(scalex,scaley);
+ return drawImage(img, null, tx, bgcolor, observer);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver)
+ */
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
+ return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer);
+ }
+
+ /**
+ * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver)
+ */
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
+ waitForImage(img);
+ double dwidth = (double)dx2-dx1;
+ double dheight = (double)dy2-dy1;
+ double swidth = (double)sx2-sx1;
+ double sheight = (double)sy2-sy1;
+
+ //if either width or height is 0, then there is nothing to draw
+ if (dwidth == 0 || dheight == 0 || swidth == 0 || sheight == 0) return true;
+
+ double scalex = dwidth/swidth;
+ double scaley = dheight/sheight;
+
+ double transx = sx1*scalex;
+ double transy = sy1*scaley;
+ AffineTransform tx = AffineTransform.getTranslateInstance(dx1-transx,dy1-transy);
+ tx.scale(scalex,scaley);
+
+ BufferedImage mask = new BufferedImage(img.getWidth(observer), img.getHeight(observer), BufferedImage.TYPE_BYTE_BINARY);
+ Graphics g = mask.getGraphics();
+ g.fillRect(sx1,sy1, (int)swidth, (int)sheight);
+ drawImage(img, mask, tx, null, observer);
+ g.dispose();
+ return true;
+ }
+
+ /**
+ * @see Graphics#dispose()
+ */
+ public void dispose() {
+ if (kid)
+ return;
+ if (!disposeCalled) {
+ disposeCalled = true;
+ cb.restoreState();
+ cb.restoreState();
+ for (int k = 0; k < kids.size(); ++k) {
+ PdfGraphics2D g2 = (PdfGraphics2D)kids.get(k);
+ g2.cb.restoreState();
+ g2.cb.restoreState();
+ cb.add(g2.cb);
+ g2.dg2.dispose();
+ g2.dg2 = null;
+ }
+ dg2.dispose();
+ dg2 = null;
+ }
+ }
+
+ ///////////////////////////////////////////////
+ //
+ //
+ // implementation specific methods
+ //
+ //
+
+
+ private void followPath(Shape s, int drawType) {
+ 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();
+ PathIterator points;
+ int traces = 0;
+ if (drawType == CLIP)
+ points = s.getPathIterator(IDENTITY);
+ else
+ points = s.getPathIterator(transform);
+ float[] coords = new float[6];
+ while(!points.isDone()) {
+ ++traces;
+ int segtype = points.currentSegment(coords);
+ normalizeY(coords);
+ switch(segtype) {
+ case PathIterator.SEG_CLOSE:
+ cb.closePath();
+ break;
+
+ case PathIterator.SEG_CUBICTO:
+ cb.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+ break;
+
+ case PathIterator.SEG_LINETO:
+ cb.lineTo(coords[0], coords[1]);
+ break;
+
+ case PathIterator.SEG_MOVETO:
+ cb.moveTo(coords[0], coords[1]);
+ break;
+
+ case PathIterator.SEG_QUADTO:
+ cb.curveTo(coords[0], coords[1], coords[2], coords[3]);
+ break;
+ }
+ points.next();
+ }
+ switch (drawType) {
+ case FILL:
+ if (traces > 0) {
+ if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD)
+ cb.eoFill();
+ else
+ cb.fill();
+ }
+ break;
+ case STROKE:
+ if (traces > 0)
+ cb.stroke();
+ break;
+ default: //drawType==CLIP
+ if (traces == 0)
+ cb.rectangle(0, 0, 0, 0);
+ if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD)
+ cb.eoClip();
+ else
+ cb.clip();
+ cb.newPath();
+ }
+ }
+
+ private float normalizeY(float y) {
+ return this.height - y;
+ }
+
+ private void normalizeY(float[] coords) {
+ coords[1] = normalizeY(coords[1]);
+ coords[3] = normalizeY(coords[3]);
+ coords[5] = normalizeY(coords[5]);
+ }
+
+ private AffineTransform normalizeMatrix() {
+ double[] mx = new double[6];
+ AffineTransform result = AffineTransform.getTranslateInstance(0,0);
+ result.getMatrix(mx);
+ mx[3]=-1;
+ mx[5]=height;
+ result = new AffineTransform(mx);
+ result.concatenate(transform);
+ return result;
+ }
+
+ private boolean drawImage(Image img, Image mask, AffineTransform xform, Color bgColor, ImageObserver obs) {
+ if (xform==null) return true;
+
+ xform.translate(0, img.getHeight(obs));
+ xform.scale(img.getWidth(obs), img.getHeight(obs));
+
+ AffineTransform inverse = this.normalizeMatrix();
+ AffineTransform flipper = AffineTransform.getScaleInstance(1,-1);
+ inverse.concatenate(xform);
+ inverse.concatenate(flipper);
+
+ double[] mx = new double[6];
+ inverse.getMatrix(mx);
+ if (currentFillGState != 255) {
+ PdfGState gs = fillGState[255];
+ if (gs == null) {
+ gs = new PdfGState();
+ gs.setFillOpacity(1);
+ fillGState[255] = gs;
+ }
+ cb.setGState(gs);
+ }
+
+ try {
+ com.lowagie.text.Image image = null;
+ if(!convertImagesToJPEG){
+ image = com.lowagie.text.Image.getInstance(img, bgColor);
+ }
+ else{
+ BufferedImage scaled = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
+ Graphics2D g3 = scaled.createGraphics();
+ g3.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null);
+ g3.dispose();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ com.sun.image.codec.jpeg.JPEGImageEncoder encoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(baos);
+ com.sun.image.codec.jpeg.JPEGEncodeParam param = com.sun.image.codec.jpeg.JPEGCodec.getDefaultJPEGEncodeParam(scaled);
+ param.setQuality(jpegQuality, true);
+ encoder.encode(scaled, param);
+ scaled.flush();
+ scaled = null;
+ image = com.lowagie.text.Image.getInstance(baos.toByteArray());
+
+ }
+ if (mask!=null) {
+ com.lowagie.text.Image msk = com.lowagie.text.Image.getInstance(mask, null, true);
+ msk.makeMask();
+ msk.setInvertMask(true);
+ image.setImageMask(msk);
+ }
+ cb.addImage(image, (float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]);
+ } catch (Exception ex) {
+ throw new IllegalArgumentException();
+ }
+ if (currentFillGState != 255) {
+ PdfGState gs = fillGState[currentFillGState];
+ cb.setGState(gs);
+ }
+ return true;
+ }
+
+ private boolean checkNewPaint(Paint oldPaint) {
+ if (paint == oldPaint)
+ return false;
+ return !((paint instanceof Color) && paint.equals(oldPaint));
+ }
+
+ private void setFillPaint() {
+ if (checkNewPaint(paintFill)) {
+ paintFill = paint;
+ setPaint(false, 0, 0, true);
+ }
+ }
+
+ private void setStrokePaint() {
+ if (checkNewPaint(paintStroke)) {
+ paintStroke = paint;
+ setPaint(false, 0, 0, false);
+ }
+ }
+
+ private void setPaint(boolean invert, double xoffset, double yoffset, boolean fill) {
+ if (paint instanceof Color) {
+ Color color = (Color)paint;
+ int alpha = color.getAlpha();
+ if (fill) {
+ if (alpha != currentFillGState) {
+ currentFillGState = alpha;
+ PdfGState gs = fillGState[alpha];
+ if (gs == null) {
+ gs = new PdfGState();
+ gs.setFillOpacity((float)alpha / 255f);
+ fillGState[alpha] = gs;
+ }
+ cb.setGState(gs);
+ }
+ cb.setColorFill(color);
+ }
+ else {
+ if (alpha != currentStrokeGState) {
+ currentStrokeGState = alpha;
+ PdfGState gs = strokeGState[alpha];
+ if (gs == null) {
+ gs = new PdfGState();
+ gs.setStrokeOpacity((float)alpha / 255f);
+ strokeGState[alpha] = gs;
+ }
+ cb.setGState(gs);
+ }
+ cb.setColorStroke(color);
+ }
+ }
+ else if (paint instanceof GradientPaint) {
+ GradientPaint gp = (GradientPaint)paint;
+ Point2D p1 = gp.getPoint1();
+ transform.transform(p1, p1);
+ Point2D p2 = gp.getPoint2();
+ transform.transform(p2, p2);
+ Color c1 = gp.getColor1();
+ Color c2 = gp.getColor2();
+ PdfShading shading = PdfShading.simpleAxial(cb.getPdfWriter(), (float)p1.getX(), normalizeY((float)p1.getY()), (float)p2.getX(), normalizeY((float)p2.getY()), c1, c2);
+ PdfShadingPattern pat = new PdfShadingPattern(shading);
+ if (fill)
+ cb.setShadingFill(pat);
+ else
+ cb.setShadingStroke(pat);
+ }
+ else if (paint instanceof TexturePaint) {
+ try {
+ TexturePaint tp = (TexturePaint)paint;
+ BufferedImage img = tp.getImage();
+ Rectangle2D rect = tp.getAnchorRect();
+ com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(img, null);
+ PdfPatternPainter pattern = cb.createPattern(image.width(), image.height());
+ AffineTransform inverse = this.normalizeMatrix();
+ inverse.translate(rect.getX(), rect.getY());
+ inverse.scale(rect.getWidth() / image.width(), -rect.getHeight() / image.height());
+ double[] mx = new double[6];
+ inverse.getMatrix(mx);
+ pattern.setPatternMatrix((float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]) ;
+ image.setAbsolutePosition(0,0);
+ pattern.addImage(image);
+ if (fill)
+ cb.setPatternFill(pattern);
+ else
+ cb.setPatternStroke(pattern);
+ } catch (Exception ex) {
+ if (fill)
+ cb.setColorFill(Color.gray);
+ else
+ cb.setColorStroke(Color.gray);
+ }
+ }
+ else {
+ try {
+ BufferedImage img = null;
+ int type = BufferedImage.TYPE_4BYTE_ABGR;
+ if (paint.getTransparency() == Transparency.OPAQUE) {
+ type = BufferedImage.TYPE_3BYTE_BGR;
+ }
+ img = new BufferedImage((int)width, (int)height, type);
+ Graphics2D g = (Graphics2D)img.getGraphics();
+ g.transform(transform);
+ AffineTransform inv = transform.createInverse();
+ Shape fillRect = new Rectangle2D.Double(0,0,img.getWidth(),img.getHeight());
+ fillRect = inv.createTransformedShape(fillRect);
+ g.setPaint(paint);
+ g.fill(fillRect);
+ if (invert) {
+ AffineTransform tx = new AffineTransform();
+ tx.scale(1,-1);
+ tx.translate(-xoffset,-yoffset);
+ g.drawImage(img,tx,null);
+ }
+ g.dispose();
+ g = null;
+ com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(img, null);
+ PdfPatternPainter pattern = cb.createPattern(width, height);
+ image.setAbsolutePosition(0,0);
+ pattern.addImage(image);
+ if (fill)
+ cb.setPatternFill(pattern);
+ else
+ cb.setPatternStroke(pattern);
+ } catch (Exception ex) {
+ if (fill)
+ cb.setColorFill(Color.gray);
+ else
+ cb.setColorStroke(Color.gray);
+ }
+ }
+ }
+
+ private synchronized void waitForImage(java.awt.Image image) {
+ if (mediaTracker == null)
+ mediaTracker = new MediaTracker(new PdfGraphics2D.fakeComponent());
+ mediaTracker.addImage(image, 0);
+ try {
+ mediaTracker.waitForID(0);
+ }
+ catch (InterruptedException e) {
+ // empty on purpose
+ }
+ mediaTracker.removeImage(image);
+ }
+
+ static private class fakeComponent extends Component {
+ }
+}