1 /*
2  * $Id$
3  *
4  * Copyright 2002 by Jim Moore <jim@scolamoore.com>.
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above.  If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */
49 
50 package com.lowagie.text.pdf;
51 
52 import java.awt.AlphaComposite;
53 import java.awt.BasicStroke;
54 import java.awt.Color;
55 import java.awt.Component;
56 import java.awt.Composite;
57 import java.awt.Font;
58 import java.awt.FontMetrics;
59 import java.awt.GradientPaint;
60 import java.awt.Graphics;
61 import java.awt.Graphics2D;
62 import java.awt.GraphicsConfiguration;
63 import java.awt.Image;
64 import java.awt.MediaTracker;
65 import java.awt.Paint;
66 import java.awt.Polygon;
67 import java.awt.Rectangle;
68 import java.awt.RenderingHints;
69 import java.awt.Shape;
70 import java.awt.Stroke;
71 import java.awt.TexturePaint;
72 import java.awt.Transparency;
73 import java.awt.RenderingHints.Key;
74 import java.awt.font.FontRenderContext;
75 import java.awt.font.GlyphVector;
76 import java.awt.font.TextAttribute;
77 import java.awt.geom.AffineTransform;
78 import java.awt.geom.Arc2D;
79 import java.awt.geom.Area;
80 import java.awt.geom.Ellipse2D;
81 import java.awt.geom.Line2D;
82 import java.awt.geom.NoninvertibleTransformException;
83 import java.awt.geom.PathIterator;
84 import java.awt.geom.Point2D;
85 import java.awt.geom.Rectangle2D;
86 import java.awt.geom.RoundRectangle2D;
87 import java.awt.image.BufferedImage;
88 import java.awt.image.BufferedImageOp;
89 import java.awt.image.ColorModel;
90 import java.awt.image.ImageObserver;
91 import java.awt.image.RenderedImage;
92 import java.awt.image.WritableRaster;
93 import java.awt.image.renderable.RenderableImage;
94 import java.io.ByteArrayOutputStream;
95 import java.text.AttributedCharacterIterator;
96 import java.util.ArrayList;
97 import java.util.HashMap;
98 import java.util.Hashtable;
99 import java.util.Iterator;
100 import java.util.Map;
101 import java.util.Set;
102 
103 import com.lowagie.text.pdf.internal.PolylineShape;
104 import java.util.Locale;
105 import javax.imageio.IIOImage;
106 import javax.imageio.ImageIO;
107 import javax.imageio.ImageWriteParam;
108 import javax.imageio.ImageWriter;
109 import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
110 import javax.imageio.stream.ImageOutputStream;
111 
112 public class PdfGraphics2D extends Graphics2D {
113 
114     private static final int FILL = 1;
115     private static final int STROKE = 2;
116     private static final int CLIP = 3;
117     private BasicStroke strokeOne = new BasicStroke(1);
118 
119     private static final AffineTransform IDENTITY = new AffineTransform();
120 
121     private Font font;
122     private BaseFont baseFont;
123     private float fontSize;
124     private AffineTransform transform;
125     private Paint paint;
126     private Color background;
127     private float width;
128     private float height;
129 
130     private Area clip;
131 
132     private RenderingHints rhints = new RenderingHints(null);
133 
134     private Stroke stroke;
135     private Stroke originalStroke;
136 
137     private PdfContentByte cb;
138 
139     /** Storage for BaseFont objects created. */
140     private HashMap baseFonts;
141 
142     private boolean disposeCalled = false;
143 
144     private FontMapper fontMapper;
145 
146     private ArrayList kids;
147 
148     private boolean kid = false;
149 
150     private Graphics2D dg2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB).createGraphics();
151 
152     private boolean onlyShapes = false;
153 
154     private Stroke oldStroke;
155     private Paint paintFill;
156     private Paint paintStroke;
157 
158     private MediaTracker mediaTracker;
159 
160     // Added by Jurij Bilas
161     protected boolean underline;          // indicates if the font style is underlined
162 
163     protected PdfGState fillGState[] = new PdfGState[256];
164     protected PdfGState strokeGState[] = new PdfGState[256];
165     protected int currentFillGState = 255;
166     protected int currentStrokeGState = 255;
167 
168     public static final int AFM_DIVISOR = 1000; // used to calculate coordinates
169 
170     private boolean convertImagesToJPEG = false;
171     private float jpegQuality = .95f;
172 
173 	// Added by Alexej Suchov
174 	private float alpha;
175 
176 	// Added by Alexej Suchov
177 	private Composite composite;
178 
179 	// Added by Alexej Suchov
180 	private Paint realPaint;
181 
PdfGraphics2D()182     private PdfGraphics2D() {
183         dg2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
184         setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
185         setRenderingHint(HyperLinkKey.KEY_INSTANCE, HyperLinkKey.VALUE_HYPERLINKKEY_OFF);
186     }
187 
188     /**
189      * Constructor for PDFGraphics2D.
190      *
191      */
PdfGraphics2D(PdfContentByte cb, float width, float height, FontMapper fontMapper, boolean onlyShapes, boolean convertImagesToJPEG, float quality)192     PdfGraphics2D(PdfContentByte cb, float width, float height, FontMapper fontMapper, boolean onlyShapes, boolean convertImagesToJPEG, float quality) {
193         super();
194         dg2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
195         setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
196         setRenderingHint(HyperLinkKey.KEY_INSTANCE, HyperLinkKey.VALUE_HYPERLINKKEY_OFF);
197         this.convertImagesToJPEG = convertImagesToJPEG;
198         this.jpegQuality = quality;
199         this.onlyShapes = onlyShapes;
200         this.transform = new AffineTransform();
201         this.baseFonts = new HashMap();
202         if (!onlyShapes) {
203             this.fontMapper = fontMapper;
204             if (this.fontMapper == null)
205                 this.fontMapper = new DefaultFontMapper();
206         }
207         paint = Color.black;
208         background = Color.white;
209         setFont(new Font("sanserif", Font.PLAIN, 12));
210         this.cb = cb;
211         cb.saveState();
212         this.width = width;
213         this.height = height;
214         clip = new Area(new Rectangle2D.Float(0, 0, width, height));
215         clip(clip);
216         originalStroke = stroke = oldStroke = strokeOne;
217         setStrokeDiff(stroke, null);
218         cb.saveState();
219     }
220 
221     /**
222      * @see Graphics2D#draw(Shape)
223      */
draw(Shape s)224     public void draw(Shape s) {
225         followPath(s, STROKE);
226     }
227 
228     /**
229      * @see Graphics2D#drawImage(Image, AffineTransform, ImageObserver)
230      */
drawImage(Image img, AffineTransform xform, ImageObserver obs)231     public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
232         return drawImage(img, null, xform, null, obs);
233     }
234 
235     /**
236      * @see Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int)
237      */
drawImage(BufferedImage img, BufferedImageOp op, int x, int y)238     public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
239         BufferedImage result = img;
240         if (op != null) {
241             result = op.createCompatibleDestImage(img, img.getColorModel());
242             result = op.filter(img, result);
243         }
244         drawImage(result, x, y, null);
245     }
246 
247     /**
248      * @see Graphics2D#drawRenderedImage(RenderedImage, AffineTransform)
249      */
drawRenderedImage(RenderedImage img, AffineTransform xform)250     public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
251         BufferedImage image = null;
252         if (img instanceof BufferedImage) {
253             image = (BufferedImage)img;
254         } else {
255             ColorModel cm = img.getColorModel();
256             int width = img.getWidth();
257             int height = img.getHeight();
258             WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
259             boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
260             Hashtable properties = new Hashtable();
261             String[] keys = img.getPropertyNames();
262             if (keys!=null) {
263                 for (int i = 0; i < keys.length; i++) {
264                     properties.put(keys[i], img.getProperty(keys[i]));
265                 }
266             }
267             BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
268             img.copyData(raster);
269             image=result;
270         }
271         drawImage(image, xform, null);
272     }
273 
274     /**
275      * @see Graphics2D#drawRenderableImage(RenderableImage, AffineTransform)
276      */
drawRenderableImage(RenderableImage img, AffineTransform xform)277     public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
278         drawRenderedImage(img.createDefaultRendering(), xform);
279     }
280 
281     /**
282      * @see Graphics#drawString(String, int, int)
283      */
drawString(String s, int x, int y)284     public void drawString(String s, int x, int y) {
285         drawString(s, (float)x, (float)y);
286     }
287 
288     /**
289      * Calculates position and/or stroke thickness depending on the font size
290      * @param d value to be converted
291      * @param i font size
292      * @return position and/or stroke thickness depending on the font size
293      */
asPoints(double d, int i)294     public static double asPoints(double d, int i) {
295         return d * i / AFM_DIVISOR;
296     }
297     /**
298      * This routine goes through the attributes and sets the font
299      * before calling the actual string drawing routine
300      * @param iter
301      */
doAttributes(AttributedCharacterIterator iter)302     protected void doAttributes(AttributedCharacterIterator iter) {
303         underline = false;
304         Set set = iter.getAttributes().keySet();
305         for(Iterator iterator = set.iterator(); iterator.hasNext();) {
306             AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute)iterator.next();
307             if (!(attribute instanceof TextAttribute))
308                 continue;
309             TextAttribute textattribute = (TextAttribute)attribute;
310             if(textattribute.equals(TextAttribute.FONT)) {
311                 Font font = (Font)iter.getAttributes().get(textattribute);
312                 setFont(font);
313             }
314             else if(textattribute.equals(TextAttribute.UNDERLINE)) {
315                 if(iter.getAttributes().get(textattribute) == TextAttribute.UNDERLINE_ON)
316                     underline = true;
317             }
318             else if(textattribute.equals(TextAttribute.SIZE)) {
319                 Object obj = iter.getAttributes().get(textattribute);
320                 if(obj instanceof Integer) {
321                     int i = ((Integer)obj).intValue();
322                     setFont(getFont().deriveFont(getFont().getStyle(), i));
323                 }
324                 else if(obj instanceof Float) {
325                     float f = ((Float)obj).floatValue();
326                     setFont(getFont().deriveFont(getFont().getStyle(), f));
327                 }
328             }
329             else if(textattribute.equals(TextAttribute.FOREGROUND)) {
330                 setColor((Color) iter.getAttributes().get(textattribute));
331             }
332             else if(textattribute.equals(TextAttribute.FAMILY)) {
333               Font font = getFont();
334               Map fontAttributes = font.getAttributes();
335               fontAttributes.put(TextAttribute.FAMILY, iter.getAttributes().get(textattribute));
336               setFont(font.deriveFont(fontAttributes));
337             }
338             else if(textattribute.equals(TextAttribute.POSTURE)) {
339               Font font = getFont();
340               Map fontAttributes = font.getAttributes();
341               fontAttributes.put(TextAttribute.POSTURE, iter.getAttributes().get(textattribute));
342               setFont(font.deriveFont(fontAttributes));
343             }
344             else if(textattribute.equals(TextAttribute.WEIGHT)) {
345               Font font = getFont();
346               Map fontAttributes = font.getAttributes();
347               fontAttributes.put(TextAttribute.WEIGHT, iter.getAttributes().get(textattribute));
348               setFont(font.deriveFont(fontAttributes));
349             }
350         }
351     }
352 
353     /**
354      * @see Graphics2D#drawString(String, float, float)
355      */
drawString(String s, float x, float y)356     public void drawString(String s, float x, float y) {
357         if (s.length() == 0)
358             return;
359         setFillPaint();
360         if (onlyShapes) {
361             drawGlyphVector(this.font.layoutGlyphVector(getFontRenderContext(), s.toCharArray(), 0, s.length(), java.awt.Font.LAYOUT_LEFT_TO_RIGHT), x, y);
362 //            Use the following line to compile in JDK 1.3
363 //            drawGlyphVector(this.font.createGlyphVector(getFontRenderContext(), s), x, y);
364         }
365         else {
366         	boolean restoreTextRenderingMode = false;
367             AffineTransform at = getTransform();
368             AffineTransform at2 = getTransform();
369             at2.translate(x, y);
370             at2.concatenate(font.getTransform());
371             setTransform(at2);
372             AffineTransform inverse = this.normalizeMatrix();
373             AffineTransform flipper = AffineTransform.getScaleInstance(1,-1);
374             inverse.concatenate(flipper);
375             double[] mx = new double[6];
376             inverse.getMatrix(mx);
377             cb.beginText();
378             cb.setFontAndSize(baseFont, fontSize);
379             // Check if we need to simulate an italic font.
380             // When there are different fonts for italic, bold, italic bold
381             // the font.getName() will be different from the font.getFontName()
382             // value. When they are the same value then we are normally dealing
383             // with a single font that has been made into an italic or bold
384             // font.
385             if (font.isItalic() && font.getFontName().equals(font.getName())) {
386                 float angle = baseFont.getFontDescriptor(BaseFont.ITALICANGLE, 1000);
387                 float angle2 = font.getItalicAngle();
388                 // We don't have an italic version of this font so we need
389                 // to set the font angle ourselves to produce an italic font.
390                 if (angle2 == 0) {
391                     // The JavaVM didn't have an angle setting for making
392                     // the font an italic font so use a default of
393                     // italic angle of 15 degrees.
394                     angle2 = 15.0f;
395                 } else {
396                     // This sign of the angle for Java and PDF seams
397                     // seams to be reversed.
398                     angle2 = -angle2;
399                 }
400                 if (angle == 0) {
401                     mx[2] = angle2 / 100.0f;
402                 }
403             }
404             cb.setTextMatrix((float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]);
405             Float fontTextAttributeWidth = (Float)font.getAttributes().get(TextAttribute.WIDTH);
406             fontTextAttributeWidth = (fontTextAttributeWidth == null)
407                                      ? TextAttribute.WIDTH_REGULAR
408                                      : fontTextAttributeWidth;
409             if (!TextAttribute.WIDTH_REGULAR.equals(fontTextAttributeWidth))
410                 cb.setHorizontalScaling(100.0f / fontTextAttributeWidth.floatValue());
411 
412             // Check if we need to simulate a bold font.
413             // Do nothing if the BaseFont is already bold. This test is not foolproof but it will work most of the times.
414             if (baseFont.getPostscriptFontName().toLowerCase().indexOf("bold") < 0) {
415                 // Get the weight of the font so we can detect fonts with a weight
416                 // that makes them bold, but the Font.isBold() value is false.
417                 Float weight = (Float) font.getAttributes().get(TextAttribute.WEIGHT);
418                 if (weight == null) {
419                     weight = (font.isBold()) ? TextAttribute.WEIGHT_BOLD
420                                              : TextAttribute.WEIGHT_REGULAR;
421                 }
422                 if ((font.isBold() || (weight.floatValue() >= TextAttribute.WEIGHT_SEMIBOLD.floatValue()))
423                     && (font.getFontName().equals(font.getName()))) {
424                     // Simulate a bold font.
425                     float strokeWidth = font.getSize2D() * (weight.floatValue() - TextAttribute.WEIGHT_REGULAR.floatValue()) / 30f;
426                     if (strokeWidth != 1) {
427                         if(realPaint instanceof Color){
428                             cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
429                             cb.setLineWidth(strokeWidth);
430                             Color color = (Color)realPaint;
431                             int alpha = color.getAlpha();
432                             if (alpha != currentStrokeGState) {
433                                 currentStrokeGState = alpha;
434                                 PdfGState gs = strokeGState[alpha];
435                                 if (gs == null) {
436                                     gs = new PdfGState();
437                                     gs.setStrokeOpacity(alpha / 255f);
438                                     strokeGState[alpha] = gs;
439                                 }
440                                 cb.setGState(gs);
441                             }
442                             cb.setColorStroke(color);
443                             restoreTextRenderingMode = true;
444                         }
445                     }
446                 }
447             }
448 
449             double width = 0;
450             if (font.getSize2D() > 0) {
451                 float scale = 1000 / font.getSize2D();
452                 Font derivedFont = font.deriveFont(AffineTransform.getScaleInstance(scale, scale));
453                 width = derivedFont.getStringBounds(s, getFontRenderContext()).getWidth();
454                 if (derivedFont.isTransformed())
455                     width /= scale;
456             }
457             // if the hyperlink flag is set add an action to the text
458             Object url = getRenderingHint(HyperLinkKey.KEY_INSTANCE);
459             if (url != null && !url.equals(HyperLinkKey.VALUE_HYPERLINKKEY_OFF))
460             {
461                 float scale = 1000 / font.getSize2D();
462                 Font derivedFont = font.deriveFont(AffineTransform.getScaleInstance(scale, scale));
463                 double height = derivedFont.getStringBounds(s, getFontRenderContext()).getHeight();
464                 if (derivedFont.isTransformed())
465                     height /= scale;
466                 double leftX = cb.getXTLM();
467                 double leftY = cb.getYTLM();
468                 PdfAction action = new  PdfAction(url.toString());
469                 cb.setAction(action, (float)leftX, (float)leftY, (float)(leftX+width), (float)(leftY+height));
470             }
471             if (s.length() > 1) {
472                 float adv = ((float)width - baseFont.getWidthPoint(s, fontSize)) / (s.length() - 1);
473                 cb.setCharacterSpacing(adv);
474             }
475             cb.showText(s);
476             if (s.length() > 1) {
477                 cb.setCharacterSpacing(0);
478             }
479             if (!TextAttribute.WIDTH_REGULAR.equals(fontTextAttributeWidth))
480                 cb.setHorizontalScaling(100);
481 
482             // Restore the original TextRenderingMode if needed.
483             if (restoreTextRenderingMode) {
484                 cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
485             }
486 
487             cb.endText();
488             setTransform(at);
489             if(underline)
490             {
491                 // These two are supposed to be taken from the .AFM file
492                 //int UnderlinePosition = -100;
493                 int UnderlineThickness = 50;
494                 //
495                 double d = asPoints(UnderlineThickness, (int)fontSize);
496                 Stroke savedStroke = originalStroke;
497                 setStroke(new BasicStroke((float)d));
498                 y = (float)(y + asPoints(UnderlineThickness, (int)fontSize));
499                 Line2D line = new Line2D.Double(x, y, width+x, y);
500                 draw(line);
501                 setStroke(savedStroke);
502             }
503         }
504     }
505 
506     /**
507      * @see Graphics#drawString(AttributedCharacterIterator, int, int)
508      */
drawString(AttributedCharacterIterator iterator, int x, int y)509     public void drawString(AttributedCharacterIterator iterator, int x, int y) {
510         drawString(iterator, (float)x, (float)y);
511     }
512 
513     /**
514      * @see Graphics2D#drawString(AttributedCharacterIterator, float, float)
515      */
drawString(AttributedCharacterIterator iter, float x, float y)516     public void drawString(AttributedCharacterIterator iter, float x, float y) {
517 /*
518         StringBuffer sb = new StringBuffer();
519         for(char c = iter.first(); c != AttributedCharacterIterator.DONE; c = iter.next()) {
520             sb.append(c);
521         }
522         drawString(sb.toString(),x,y);
523 */
524         StringBuffer stringbuffer = new StringBuffer(iter.getEndIndex());
525         for(char c = iter.first(); c != '\uFFFF'; c = iter.next())
526         {
527             if(iter.getIndex() == iter.getRunStart())
528             {
529                 if(stringbuffer.length() > 0)
530                 {
531                     drawString(stringbuffer.toString(), x, y);
532                     FontMetrics fontmetrics = getFontMetrics();
533                     x = (float)(x + fontmetrics.getStringBounds(stringbuffer.toString(), this).getWidth());
534                     stringbuffer.delete(0, stringbuffer.length());
535                 }
536                 doAttributes(iter);
537             }
538             stringbuffer.append(c);
539         }
540 
541         drawString(stringbuffer.toString(), x, y);
542         underline = false;
543     }
544 
545     /**
546      * @see Graphics2D#drawGlyphVector(GlyphVector, float, float)
547      */
drawGlyphVector(GlyphVector g, float x, float y)548     public void drawGlyphVector(GlyphVector g, float x, float y) {
549         Shape s = g.getOutline(x, y);
550         fill(s);
551     }
552 
553     /**
554      * @see Graphics2D#fill(Shape)
555      */
fill(Shape s)556     public void fill(Shape s) {
557         followPath(s, FILL);
558     }
559 
560     /**
561      * @see Graphics2D#hit(Rectangle, Shape, boolean)
562      */
hit(Rectangle rect, Shape s, boolean onStroke)563     public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
564         if (onStroke) {
565             s = stroke.createStrokedShape(s);
566         }
567         s = transform.createTransformedShape(s);
568         Area area = new Area(s);
569         if (clip != null)
570             area.intersect(clip);
571         return area.intersects(rect.x, rect.y, rect.width, rect.height);
572     }
573 
574     /**
575      * @see Graphics2D#getDeviceConfiguration()
576      */
getDeviceConfiguration()577     public GraphicsConfiguration getDeviceConfiguration() {
578         return dg2.getDeviceConfiguration();
579     }
580 
581     /**
582 	 * Method contributed by Alexej Suchov
583      * @see Graphics2D#setComposite(Composite)
584      */
setComposite(Composite comp)585     public void setComposite(Composite comp) {
586 
587 		if (comp instanceof AlphaComposite) {
588 
589 			AlphaComposite composite = (AlphaComposite) comp;
590 
591 			if (composite.getRule() == 3) {
592 
593 				alpha = composite.getAlpha();
594 				this.composite = composite;
595 
596 				if (realPaint != null && (realPaint instanceof Color)) {
597 
598 					Color c = (Color) realPaint;
599 					paint = new Color(c.getRed(), c.getGreen(), c.getBlue(),
600 							(int) (c.getAlpha() * alpha));
601 				}
602 				return;
603 			}
604 		}
605 
606 		this.composite = comp;
607 		alpha = 1.0F;
608 
609     }
610 
611     /**
612 	 * Method contributed by Alexej Suchov
613      * @see Graphics2D#setPaint(Paint)
614      */
setPaint(Paint paint)615     public void setPaint(Paint paint) {
616         if (paint == null)
617             return;
618         this.paint = paint;
619 		realPaint = paint;
620 
621 		if ((composite instanceof AlphaComposite) && (paint instanceof Color)) {
622 
623 			AlphaComposite co = (AlphaComposite) composite;
624 
625 			if (co.getRule() == 3) {
626 				Color c = (Color) paint;
627 				this.paint = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int) (c.getAlpha() * alpha));
628 				realPaint = paint;
629 			}
630 		}
631 
632     }
633 
transformStroke(Stroke stroke)634     private Stroke transformStroke(Stroke stroke) {
635         if (!(stroke instanceof BasicStroke))
636             return stroke;
637         BasicStroke st = (BasicStroke)stroke;
638         float scale = (float)Math.sqrt(Math.abs(transform.getDeterminant()));
639         float dash[] = st.getDashArray();
640         if (dash != null) {
641             for (int k = 0; k < dash.length; ++k)
642                 dash[k] *= scale;
643         }
644         return new BasicStroke(st.getLineWidth() * scale, st.getEndCap(), st.getLineJoin(), st.getMiterLimit(), dash, st.getDashPhase() * scale);
645     }
646 
setStrokeDiff(Stroke newStroke, Stroke oldStroke)647     private void setStrokeDiff(Stroke newStroke, Stroke oldStroke) {
648         if (newStroke == oldStroke)
649             return;
650         if (!(newStroke instanceof BasicStroke))
651             return;
652         BasicStroke nStroke = (BasicStroke)newStroke;
653         boolean oldOk = (oldStroke instanceof BasicStroke);
654         BasicStroke oStroke = null;
655         if (oldOk)
656             oStroke = (BasicStroke)oldStroke;
657         if (!oldOk || nStroke.getLineWidth() != oStroke.getLineWidth())
658             cb.setLineWidth(nStroke.getLineWidth());
659         if (!oldOk || nStroke.getEndCap() != oStroke.getEndCap()) {
660             switch (nStroke.getEndCap()) {
661             case BasicStroke.CAP_BUTT:
662                 cb.setLineCap(0);
663                 break;
664             case BasicStroke.CAP_SQUARE:
665                 cb.setLineCap(2);
666                 break;
667             default:
668                 cb.setLineCap(1);
669             }
670         }
671         if (!oldOk || nStroke.getLineJoin() != oStroke.getLineJoin()) {
672             switch (nStroke.getLineJoin()) {
673             case BasicStroke.JOIN_MITER:
674                 cb.setLineJoin(0);
675                 break;
676             case BasicStroke.JOIN_BEVEL:
677                 cb.setLineJoin(2);
678                 break;
679             default:
680                 cb.setLineJoin(1);
681             }
682         }
683         if (!oldOk || nStroke.getMiterLimit() != oStroke.getMiterLimit())
684             cb.setMiterLimit(nStroke.getMiterLimit());
685         boolean makeDash;
686         if (oldOk) {
687             if (nStroke.getDashArray() != null) {
688                 if (nStroke.getDashPhase() != oStroke.getDashPhase()) {
689                     makeDash = true;
690                 }
691                 else if (!java.util.Arrays.equals(nStroke.getDashArray(), oStroke.getDashArray())) {
692                     makeDash = true;
693                 }
694                 else
695                     makeDash = false;
696             }
697             else if (oStroke.getDashArray() != null) {
698                 makeDash = true;
699             }
700             else
701                 makeDash = false;
702         }
703         else {
704             makeDash = true;
705         }
706         if (makeDash) {
707             float dash[] = nStroke.getDashArray();
708             if (dash == null)
709                 cb.setLiteral("[]0 d\n");
710             else {
711                 cb.setLiteral('[');
712                 int lim = dash.length;
713                 for (int k = 0; k < lim; ++k) {
714                     cb.setLiteral(dash[k]);
715                     cb.setLiteral(' ');
716                 }
717                 cb.setLiteral(']');
718                 cb.setLiteral(nStroke.getDashPhase());
719                 cb.setLiteral(" d\n");
720             }
721         }
722     }
723 
724     /**
725      * @see Graphics2D#setStroke(Stroke)
726      */
setStroke(Stroke s)727     public void setStroke(Stroke s) {
728         originalStroke = s;
729         this.stroke = transformStroke(s);
730     }
731 
732 
733     /**
734      * Sets a rendering hint
735      * @param arg0
736      * @param arg1
737      */
setRenderingHint(Key arg0, Object arg1)738     public void setRenderingHint(Key arg0, Object arg1) {
739     	 if (arg1 != null) {
740          	rhints.put(arg0, arg1);
741          } else {
742         	 if (arg0 instanceof HyperLinkKey)
743         	 {
744         		 rhints.put(arg0, HyperLinkKey.VALUE_HYPERLINKKEY_OFF);
745         	 }
746         	 else
747         	 {
748         		 rhints.remove(arg0);
749         	 }
750          }
751     }
752 
753     /**
754      * @param arg0 a key
755      * @return the rendering hint
756      */
getRenderingHint(Key arg0)757     public Object getRenderingHint(Key arg0) {
758         return rhints.get(arg0);
759     }
760 
761     /**
762      * @see Graphics2D#setRenderingHints(Map)
763      */
setRenderingHints(Map hints)764     public void setRenderingHints(Map hints) {
765         rhints.clear();
766         rhints.putAll(hints);
767     }
768 
769     /**
770      * @see Graphics2D#addRenderingHints(Map)
771      */
addRenderingHints(Map hints)772     public void addRenderingHints(Map hints) {
773         rhints.putAll(hints);
774     }
775 
776     /**
777      * @see Graphics2D#getRenderingHints()
778      */
getRenderingHints()779     public RenderingHints getRenderingHints() {
780         return rhints;
781     }
782 
783     /**
784      * @see Graphics#translate(int, int)
785      */
translate(int x, int y)786     public void translate(int x, int y) {
787         translate((double)x, (double)y);
788     }
789 
790     /**
791      * @see Graphics2D#translate(double, double)
792      */
translate(double tx, double ty)793     public void translate(double tx, double ty) {
794         transform.translate(tx,ty);
795     }
796 
797     /**
798      * @see Graphics2D#rotate(double)
799      */
rotate(double theta)800     public void rotate(double theta) {
801         transform.rotate(theta);
802     }
803 
804     /**
805      * @see Graphics2D#rotate(double, double, double)
806      */
rotate(double theta, double x, double y)807     public void rotate(double theta, double x, double y) {
808         transform.rotate(theta, x, y);
809     }
810 
811     /**
812      * @see Graphics2D#scale(double, double)
813      */
scale(double sx, double sy)814     public void scale(double sx, double sy) {
815         transform.scale(sx, sy);
816         this.stroke = transformStroke(originalStroke);
817     }
818 
819     /**
820      * @see Graphics2D#shear(double, double)
821      */
shear(double shx, double shy)822     public void shear(double shx, double shy) {
823         transform.shear(shx, shy);
824     }
825 
826     /**
827      * @see Graphics2D#transform(AffineTransform)
828      */
transform(AffineTransform tx)829     public void transform(AffineTransform tx) {
830         transform.concatenate(tx);
831         this.stroke = transformStroke(originalStroke);
832     }
833 
834     /**
835      * @see Graphics2D#setTransform(AffineTransform)
836      */
setTransform(AffineTransform t)837     public void setTransform(AffineTransform t) {
838         transform = new AffineTransform(t);
839         this.stroke = transformStroke(originalStroke);
840     }
841 
842     /**
843      * @see Graphics2D#getTransform()
844      */
getTransform()845     public AffineTransform getTransform() {
846         return new AffineTransform(transform);
847     }
848 
849     /**
850 	 * Method contributed by Alexej Suchov
851      * @see Graphics2D#getPaint()
852      */
getPaint()853     public Paint getPaint() {
854         if (realPaint != null) {
855             return realPaint;
856         } else {
857             return paint;
858         }
859 	}
860 
861     /**
862      * @see Graphics2D#getComposite()
863      */
getComposite()864     public Composite getComposite() {
865         return composite;
866     }
867 
868     /**
869      * @see Graphics2D#setBackground(Color)
870      */
setBackground(Color color)871     public void setBackground(Color color) {
872         background = color;
873     }
874 
875     /**
876      * @see Graphics2D#getBackground()
877      */
getBackground()878     public Color getBackground() {
879         return background;
880     }
881 
882     /**
883      * @see Graphics2D#getStroke()
884      */
getStroke()885     public Stroke getStroke() {
886         return originalStroke;
887     }
888 
889 
890     /**
891      * @see Graphics2D#getFontRenderContext()
892      */
getFontRenderContext()893     public FontRenderContext getFontRenderContext() {
894         boolean antialias = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
895         boolean fractions = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS));
896         return new FontRenderContext(new AffineTransform(), antialias, fractions);
897     }
898 
899     /**
900      * @see Graphics#create()
901      */
create()902     public Graphics create() {
903         PdfGraphics2D g2 = new PdfGraphics2D();
904         g2.rhints.putAll( this.rhints );
905         g2.onlyShapes = this.onlyShapes;
906         g2.transform = new AffineTransform(this.transform);
907         g2.baseFonts = this.baseFonts;
908         g2.fontMapper = this.fontMapper;
909         g2.paint = this.paint;
910         g2.fillGState = this.fillGState;
911         g2.currentFillGState = this.currentFillGState;
912         g2.strokeGState = this.strokeGState;
913         g2.background = this.background;
914         g2.mediaTracker = this.mediaTracker;
915         g2.convertImagesToJPEG = this.convertImagesToJPEG;
916         g2.jpegQuality = this.jpegQuality;
917         g2.setFont(this.font);
918         g2.cb = this.cb.getDuplicate();
919         g2.cb.saveState();
920         g2.width = this.width;
921         g2.height = this.height;
922         g2.followPath(new Area(new Rectangle2D.Float(0, 0, width, height)), CLIP);
923         if (this.clip != null)
924             g2.clip = new Area(this.clip);
925         g2.composite = composite;
926         g2.stroke = stroke;
927         g2.originalStroke = originalStroke;
928         g2.strokeOne = (BasicStroke)g2.transformStroke(g2.strokeOne);
929         g2.oldStroke = g2.strokeOne;
930         g2.setStrokeDiff(g2.oldStroke, null);
931         g2.cb.saveState();
932         if (g2.clip != null)
933             g2.followPath(g2.clip, CLIP);
934         g2.kid = true;
935         if (this.kids == null)
936             this.kids = new ArrayList();
937         this.kids.add(new Integer(cb.getInternalBuffer().size()));
938         this.kids.add(g2);
939         return g2;
940     }
941 
getContent()942     public PdfContentByte getContent() {
943         return this.cb;
944     }
945     /**
946      * @see Graphics#getColor()
947      */
getColor()948     public Color getColor() {
949         if (paint instanceof Color) {
950             return (Color)paint;
951         } else {
952             return Color.black;
953         }
954     }
955 
956     /**
957      * @see Graphics#setColor(Color)
958      */
setColor(Color color)959     public void setColor(Color color) {
960         setPaint(color);
961     }
962 
963     /**
964      * @see Graphics#setPaintMode()
965      */
setPaintMode()966     public void setPaintMode() {}
967 
968     /**
969      * @see Graphics#setXORMode(Color)
970      */
setXORMode(Color c1)971     public void setXORMode(Color c1) {
972 
973     }
974 
975     /**
976      * @see Graphics#getFont()
977      */
getFont()978     public Font getFont() {
979         return font;
980     }
981 
982     /**
983      * @see Graphics#setFont(Font)
984      */
985     /**
986      * Sets the current font.
987      */
setFont(Font f)988     public void setFont(Font f) {
989         if (f == null)
990             return;
991         if (onlyShapes) {
992             font = f;
993             return;
994         }
995         if (f == font)
996             return;
997         font = f;
998         fontSize = f.getSize2D();
999         baseFont = getCachedBaseFont(f);
1000     }
1001 
getCachedBaseFont(Font f)1002     private BaseFont getCachedBaseFont(Font f) {
1003         synchronized (baseFonts) {
1004             BaseFont bf = (BaseFont)baseFonts.get(f.getFontName());
1005             if (bf == null) {
1006                 bf = fontMapper.awtToPdf(f);
1007                 baseFonts.put(f.getFontName(), bf);
1008             }
1009             return bf;
1010         }
1011     }
1012 
1013     /**
1014      * @see Graphics#getFontMetrics(Font)
1015      */
getFontMetrics(Font f)1016     public FontMetrics getFontMetrics(Font f) {
1017         return dg2.getFontMetrics(f);
1018     }
1019 
1020     /**
1021      * @see Graphics#getClipBounds()
1022      */
getClipBounds()1023     public Rectangle getClipBounds() {
1024         if (clip == null)
1025             return null;
1026         return getClip().getBounds();
1027     }
1028 
1029     /**
1030      * @see Graphics#clipRect(int, int, int, int)
1031      */
clipRect(int x, int y, int width, int height)1032     public void clipRect(int x, int y, int width, int height) {
1033         Rectangle2D rect = new Rectangle2D.Double(x,y,width,height);
1034         clip(rect);
1035     }
1036 
1037     /**
1038      * @see Graphics#setClip(int, int, int, int)
1039      */
setClip(int x, int y, int width, int height)1040     public void setClip(int x, int y, int width, int height) {
1041         Rectangle2D rect = new Rectangle2D.Double(x,y,width,height);
1042         setClip(rect);
1043     }
1044 
1045     /**
1046      * @see Graphics2D#clip(Shape)
1047      */
clip(Shape s)1048     public void clip(Shape s) {
1049         if (s == null) {
1050             setClip(null);
1051             return;
1052         }
1053         s = transform.createTransformedShape(s);
1054         if (clip == null)
1055             clip = new Area(s);
1056         else
1057             clip.intersect(new Area(s));
1058         followPath(s, CLIP);
1059     }
1060 
1061     /**
1062      * @see Graphics#getClip()
1063      */
getClip()1064     public Shape getClip() {
1065         try {
1066             return transform.createInverse().createTransformedShape(clip);
1067         }
1068         catch (NoninvertibleTransformException e) {
1069             return null;
1070         }
1071     }
1072 
1073     /**
1074      * @see Graphics#setClip(Shape)
1075      */
setClip(Shape s)1076     public void setClip(Shape s) {
1077         cb.restoreState();
1078         cb.saveState();
1079         if (s != null)
1080             s = transform.createTransformedShape(s);
1081         if (s == null) {
1082             clip = null;
1083         }
1084         else {
1085             clip = new Area(s);
1086             followPath(s, CLIP);
1087         }
1088         paintFill = paintStroke = null;
1089         currentFillGState = currentStrokeGState = 255;
1090         oldStroke = strokeOne;
1091     }
1092 
1093     /**
1094      * @see Graphics#copyArea(int, int, int, int, int, int)
1095      */
copyArea(int x, int y, int width, int height, int dx, int dy)1096     public void copyArea(int x, int y, int width, int height, int dx, int dy) {
1097 
1098     }
1099 
1100     /**
1101      * @see Graphics#drawLine(int, int, int, int)
1102      */
drawLine(int x1, int y1, int x2, int y2)1103     public void drawLine(int x1, int y1, int x2, int y2) {
1104         Line2D line = new Line2D.Double(x1, y1, x2, y2);
1105         draw(line);
1106     }
1107 
1108     /**
1109      * @see Graphics#fillRect(int, int, int, int)
1110      */
drawRect(int x, int y, int width, int height)1111     public void drawRect(int x, int y, int width, int height) {
1112         draw(new Rectangle(x, y, width, height));
1113     }
1114 
1115     /**
1116      * @see Graphics#fillRect(int, int, int, int)
1117      */
fillRect(int x, int y, int width, int height)1118     public void fillRect(int x, int y, int width, int height) {
1119         fill(new Rectangle(x,y,width,height));
1120     }
1121 
1122     /**
1123      * @see Graphics#clearRect(int, int, int, int)
1124      */
clearRect(int x, int y, int width, int height)1125     public void clearRect(int x, int y, int width, int height) {
1126         Paint temp = paint;
1127         setPaint(background);
1128         fillRect(x,y,width,height);
1129         setPaint(temp);
1130     }
1131 
1132     /**
1133      * @see Graphics#drawRoundRect(int, int, int, int, int, int)
1134      */
drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)1135     public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
1136         RoundRectangle2D rect = new RoundRectangle2D.Double(x,y,width,height,arcWidth, arcHeight);
1137         draw(rect);
1138     }
1139 
1140     /**
1141      * @see Graphics#fillRoundRect(int, int, int, int, int, int)
1142      */
fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)1143     public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
1144         RoundRectangle2D rect = new RoundRectangle2D.Double(x,y,width,height,arcWidth, arcHeight);
1145         fill(rect);
1146     }
1147 
1148     /**
1149      * @see Graphics#drawOval(int, int, int, int)
1150      */
drawOval(int x, int y, int width, int height)1151     public void drawOval(int x, int y, int width, int height) {
1152         Ellipse2D oval = new Ellipse2D.Float(x, y, width, height);
1153         draw(oval);
1154     }
1155 
1156     /**
1157      * @see Graphics#fillOval(int, int, int, int)
1158      */
fillOval(int x, int y, int width, int height)1159     public void fillOval(int x, int y, int width, int height) {
1160         Ellipse2D oval = new Ellipse2D.Float(x, y, width, height);
1161         fill(oval);
1162     }
1163 
1164     /**
1165      * @see Graphics#drawArc(int, int, int, int, int, int)
1166      */
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)1167     public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
1168         Arc2D arc = new Arc2D.Double(x,y,width,height,startAngle, arcAngle, Arc2D.OPEN);
1169         draw(arc);
1170 
1171     }
1172 
1173     /**
1174      * @see Graphics#fillArc(int, int, int, int, int, int)
1175      */
fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)1176     public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
1177         Arc2D arc = new Arc2D.Double(x,y,width,height,startAngle, arcAngle, Arc2D.PIE);
1178         fill(arc);
1179     }
1180 
1181     /**
1182      * @see Graphics#drawPolyline(int[], int[], int)
1183      */
drawPolyline(int[] x, int[] y, int nPoints)1184     public void drawPolyline(int[] x, int[] y, int nPoints) {
1185         PolylineShape polyline = new PolylineShape(x, y, nPoints);
1186         draw(polyline);
1187     }
1188 
1189     /**
1190      * @see Graphics#drawPolygon(int[], int[], int)
1191      */
drawPolygon(int[] xPoints, int[] yPoints, int nPoints)1192     public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
1193         Polygon poly = new Polygon(xPoints, yPoints, nPoints);
1194         draw(poly);
1195     }
1196 
1197     /**
1198      * @see Graphics#fillPolygon(int[], int[], int)
1199      */
fillPolygon(int[] xPoints, int[] yPoints, int nPoints)1200     public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
1201         Polygon poly = new Polygon();
1202         for (int i = 0; i < nPoints; i++) {
1203             poly.addPoint(xPoints[i], yPoints[i]);
1204         }
1205         fill(poly);
1206     }
1207 
1208     /**
1209      * @see Graphics#drawImage(Image, int, int, ImageObserver)
1210      */
drawImage(Image img, int x, int y, ImageObserver observer)1211     public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
1212         return drawImage(img, x, y, null, observer);
1213     }
1214 
1215     /**
1216      * @see Graphics#drawImage(Image, int, int, int, int, ImageObserver)
1217      */
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)1218     public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
1219         return drawImage(img, x, y, width, height, null, observer);
1220     }
1221 
1222     /**
1223      * @see Graphics#drawImage(Image, int, int, Color, ImageObserver)
1224      */
drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)1225     public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
1226         waitForImage(img);
1227         return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), bgcolor, observer);
1228     }
1229 
1230     /**
1231      * @see Graphics#drawImage(Image, int, int, int, int, Color, ImageObserver)
1232      */
drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)1233     public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
1234         waitForImage(img);
1235         double scalex = width/(double)img.getWidth(observer);
1236         double scaley = height/(double)img.getHeight(observer);
1237         AffineTransform tx = AffineTransform.getTranslateInstance(x,y);
1238         tx.scale(scalex,scaley);
1239         return drawImage(img, null, tx, bgcolor, observer);
1240     }
1241 
1242     /**
1243      * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver)
1244      */
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)1245     public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
1246         return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer);
1247     }
1248 
1249     /**
1250      * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver)
1251      */
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)1252     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) {
1253         waitForImage(img);
1254         double dwidth = (double)dx2-dx1;
1255         double dheight = (double)dy2-dy1;
1256         double swidth = (double)sx2-sx1;
1257         double sheight = (double)sy2-sy1;
1258 
1259         //if either width or height is 0, then there is nothing to draw
1260         if (dwidth == 0 || dheight == 0 || swidth == 0 || sheight == 0) return true;
1261 
1262         double scalex = dwidth/swidth;
1263         double scaley = dheight/sheight;
1264 
1265         double transx = sx1*scalex;
1266         double transy = sy1*scaley;
1267         AffineTransform tx = AffineTransform.getTranslateInstance(dx1-transx,dy1-transy);
1268         tx.scale(scalex,scaley);
1269 
1270         BufferedImage mask = new BufferedImage(img.getWidth(observer), img.getHeight(observer), BufferedImage.TYPE_BYTE_BINARY);
1271         Graphics g = mask.getGraphics();
1272         g.fillRect(sx1,sy1, (int)swidth, (int)sheight);
1273         drawImage(img, mask, tx, null, observer);
1274         g.dispose();
1275         return true;
1276     }
1277 
1278     /**
1279      * @see Graphics#dispose()
1280      */
dispose()1281     public void dispose() {
1282         if (kid)
1283             return;
1284         if (!disposeCalled) {
1285             disposeCalled = true;
1286             cb.restoreState();
1287             cb.restoreState();
1288             dg2.dispose();
1289             dg2 = null;
1290             if (kids != null) {
1291                 ByteBuffer buf = new ByteBuffer();
1292                 internalDispose(buf);
1293                 ByteBuffer buf2 = cb.getInternalBuffer();
1294                 buf2.reset();
1295                 buf2.append(buf);
1296             }
1297         }
1298     }
1299 
internalDispose(ByteBuffer buf)1300     private void internalDispose(ByteBuffer buf) {
1301         int last = 0;
1302         int pos = 0;
1303         ByteBuffer buf2 = cb.getInternalBuffer();
1304         if (kids != null) {
1305             for (int k = 0; k < kids.size(); k += 2) {
1306                 pos = ((Integer)kids.get(k)).intValue();
1307                 PdfGraphics2D g2 = (PdfGraphics2D)kids.get(k + 1);
1308                 g2.cb.restoreState();
1309                 g2.cb.restoreState();
1310                 buf.append(buf2.getBuffer(), last, pos - last);
1311                 g2.dg2.dispose();
1312                 g2.dg2 = null;
1313                 g2.internalDispose(buf);
1314                 last = pos;
1315             }
1316         }
1317         buf.append(buf2.getBuffer(), last, buf2.size() - last);
1318     }
1319 
1320     ///////////////////////////////////////////////
1321     //
1322     //
1323     //		implementation specific methods
1324     //
1325     //
1326 
1327 
followPath(Shape s, int drawType)1328     private void followPath(Shape s, int drawType) {
1329         if (s==null) return;
1330         if (drawType==STROKE) {
1331             if (!(stroke instanceof BasicStroke)) {
1332                 s = stroke.createStrokedShape(s);
1333                 followPath(s, FILL);
1334                 return;
1335             }
1336         }
1337         if (drawType==STROKE) {
1338             setStrokeDiff(stroke, oldStroke);
1339             oldStroke = stroke;
1340             setStrokePaint();
1341         }
1342         else if (drawType==FILL)
1343             setFillPaint();
1344         PathIterator points;
1345         int traces = 0;
1346         if (drawType == CLIP)
1347             points = s.getPathIterator(IDENTITY);
1348         else
1349             points = s.getPathIterator(transform);
1350         float[] coords = new float[6];
1351         while(!points.isDone()) {
1352             ++traces;
1353             int segtype = points.currentSegment(coords);
1354             normalizeY(coords);
1355             switch(segtype) {
1356                 case PathIterator.SEG_CLOSE:
1357                     cb.closePath();
1358                     break;
1359 
1360                 case PathIterator.SEG_CUBICTO:
1361                     cb.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
1362                     break;
1363 
1364                 case PathIterator.SEG_LINETO:
1365                     cb.lineTo(coords[0], coords[1]);
1366                     break;
1367 
1368                 case PathIterator.SEG_MOVETO:
1369                     cb.moveTo(coords[0], coords[1]);
1370                     break;
1371 
1372                 case PathIterator.SEG_QUADTO:
1373                     cb.curveTo(coords[0], coords[1], coords[2], coords[3]);
1374                     break;
1375             }
1376             points.next();
1377         }
1378         switch (drawType) {
1379         case FILL:
1380             if (traces > 0) {
1381                 if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD)
1382                     cb.eoFill();
1383                 else
1384                     cb.fill();
1385             }
1386             break;
1387         case STROKE:
1388             if (traces > 0)
1389                 cb.stroke();
1390             break;
1391         default: //drawType==CLIP
1392             if (traces == 0)
1393                 cb.rectangle(0, 0, 0, 0);
1394             if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD)
1395                 cb.eoClip();
1396             else
1397                 cb.clip();
1398             cb.newPath();
1399         }
1400     }
1401 
normalizeY(float y)1402     private float normalizeY(float y) {
1403         return this.height - y;
1404     }
1405 
normalizeY(float[] coords)1406     private void normalizeY(float[] coords) {
1407         coords[1] = normalizeY(coords[1]);
1408         coords[3] = normalizeY(coords[3]);
1409         coords[5] = normalizeY(coords[5]);
1410     }
1411 
normalizeMatrix()1412     private AffineTransform normalizeMatrix() {
1413         double[] mx = new double[6];
1414         AffineTransform result = AffineTransform.getTranslateInstance(0,0);
1415         result.getMatrix(mx);
1416         mx[3]=-1;
1417         mx[5]=height;
1418         result = new AffineTransform(mx);
1419         result.concatenate(transform);
1420         return result;
1421     }
1422 
drawImage(Image img, Image mask, AffineTransform xform, Color bgColor, ImageObserver obs)1423     private boolean drawImage(Image img, Image mask, AffineTransform xform, Color bgColor, ImageObserver obs) {
1424         if (xform==null)
1425             xform = new AffineTransform();
1426         else
1427             xform = new AffineTransform(xform);
1428         xform.translate(0, img.getHeight(obs));
1429         xform.scale(img.getWidth(obs), img.getHeight(obs));
1430 
1431         AffineTransform inverse = this.normalizeMatrix();
1432         AffineTransform flipper = AffineTransform.getScaleInstance(1,-1);
1433         inverse.concatenate(xform);
1434         inverse.concatenate(flipper);
1435 
1436         double[] mx = new double[6];
1437         inverse.getMatrix(mx);
1438         if (currentFillGState != 255) {
1439             PdfGState gs = fillGState[255];
1440             if (gs == null) {
1441                 gs = new PdfGState();
1442                 gs.setFillOpacity(1);
1443                 fillGState[255] = gs;
1444             }
1445             cb.setGState(gs);
1446         }
1447 
1448         try {
1449             com.lowagie.text.Image image = null;
1450             if(!convertImagesToJPEG){
1451                 image = com.lowagie.text.Image.getInstance(img, bgColor);
1452             }
1453             else{
1454                 BufferedImage scaled = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
1455                 Graphics2D g3 = scaled.createGraphics();
1456                 g3.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null);
1457                 g3.dispose();
1458 
1459                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1460                 ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
1461                 iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
1462                 iwparam.setCompressionQuality(jpegQuality);//Set here your compression rate
1463                 ImageWriter iw = (ImageWriter)ImageIO.getImageWritersByFormatName("jpg").next();
1464                 ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
1465                 iw.setOutput(ios);
1466                 iw.write(null, new IIOImage(scaled, null, null), iwparam);
1467                 iw.dispose();
1468                 ios.close();
1469 
1470                 scaled.flush();
1471                 scaled = null;
1472                 image = com.lowagie.text.Image.getInstance(baos.toByteArray());
1473 
1474             }
1475             if (mask!=null) {
1476                 com.lowagie.text.Image msk = com.lowagie.text.Image.getInstance(mask, null, true);
1477                 msk.makeMask();
1478                 msk.setInverted(true);
1479                 image.setImageMask(msk);
1480             }
1481             cb.addImage(image, (float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]);
1482             Object url = getRenderingHint(HyperLinkKey.KEY_INSTANCE);
1483             if (url != null && !url.equals(HyperLinkKey.VALUE_HYPERLINKKEY_OFF)) {
1484             	PdfAction action = new  PdfAction(url.toString());
1485                 cb.setAction(action, (float)mx[4], (float)mx[5], (float)(mx[0]+mx[4]), (float)(mx[3]+mx[5]));
1486             }
1487         } catch (Exception ex) {
1488             throw new IllegalArgumentException();
1489         }
1490         if (currentFillGState != 255) {
1491             PdfGState gs = fillGState[currentFillGState];
1492             cb.setGState(gs);
1493         }
1494         return true;
1495     }
1496 
checkNewPaint(Paint oldPaint)1497     private boolean checkNewPaint(Paint oldPaint) {
1498         if (paint == oldPaint)
1499             return false;
1500         return !((paint instanceof Color) && paint.equals(oldPaint));
1501     }
1502 
setFillPaint()1503     private void setFillPaint() {
1504         if (checkNewPaint(paintFill)) {
1505             paintFill = paint;
1506             setPaint(false, 0, 0, true);
1507         }
1508     }
1509 
setStrokePaint()1510     private void setStrokePaint() {
1511         if (checkNewPaint(paintStroke)) {
1512             paintStroke = paint;
1513             setPaint(false, 0, 0, false);
1514         }
1515     }
1516 
setPaint(boolean invert, double xoffset, double yoffset, boolean fill)1517     private void setPaint(boolean invert, double xoffset, double yoffset, boolean fill) {
1518         if (paint instanceof Color) {
1519             Color color = (Color)paint;
1520             int alpha = color.getAlpha();
1521             if (fill) {
1522                 if (alpha != currentFillGState) {
1523                     currentFillGState = alpha;
1524                     PdfGState gs = fillGState[alpha];
1525                     if (gs == null) {
1526                         gs = new PdfGState();
1527                         gs.setFillOpacity(alpha / 255f);
1528                         fillGState[alpha] = gs;
1529                     }
1530                     cb.setGState(gs);
1531                 }
1532                 cb.setColorFill(color);
1533             }
1534             else {
1535                 if (alpha != currentStrokeGState) {
1536                     currentStrokeGState = alpha;
1537                     PdfGState gs = strokeGState[alpha];
1538                     if (gs == null) {
1539                         gs = new PdfGState();
1540                         gs.setStrokeOpacity(alpha / 255f);
1541                         strokeGState[alpha] = gs;
1542                     }
1543                     cb.setGState(gs);
1544                 }
1545                 cb.setColorStroke(color);
1546             }
1547         }
1548         else if (paint instanceof GradientPaint) {
1549             GradientPaint gp = (GradientPaint)paint;
1550             Point2D p1 = gp.getPoint1();
1551             transform.transform(p1, p1);
1552             Point2D p2 = gp.getPoint2();
1553             transform.transform(p2, p2);
1554             Color c1 = gp.getColor1();
1555             Color c2 = gp.getColor2();
1556             PdfShading shading = PdfShading.simpleAxial(cb.getPdfWriter(), (float)p1.getX(), normalizeY((float)p1.getY()), (float)p2.getX(), normalizeY((float)p2.getY()), c1, c2);
1557             PdfShadingPattern pat = new PdfShadingPattern(shading);
1558             if (fill)
1559                 cb.setShadingFill(pat);
1560             else
1561                 cb.setShadingStroke(pat);
1562         }
1563         else if (paint instanceof TexturePaint) {
1564             try {
1565                 TexturePaint tp = (TexturePaint)paint;
1566                 BufferedImage img = tp.getImage();
1567                 Rectangle2D rect = tp.getAnchorRect();
1568                 com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(img, null);
1569                 PdfPatternPainter pattern = cb.createPattern(image.getWidth(), image.getHeight());
1570                 AffineTransform inverse = this.normalizeMatrix();
1571                 inverse.translate(rect.getX(), rect.getY());
1572                 inverse.scale(rect.getWidth() / image.getWidth(), -rect.getHeight() / image.getHeight());
1573                 double[] mx = new double[6];
1574                 inverse.getMatrix(mx);
1575                 pattern.setPatternMatrix((float)mx[0], (float)mx[1], (float)mx[2], (float)mx[3], (float)mx[4], (float)mx[5]) ;
1576                 image.setAbsolutePosition(0,0);
1577                 pattern.addImage(image);
1578                 if (fill)
1579                     cb.setPatternFill(pattern);
1580                 else
1581                     cb.setPatternStroke(pattern);
1582             } catch (Exception ex) {
1583                 if (fill)
1584                     cb.setColorFill(Color.gray);
1585                 else
1586                     cb.setColorStroke(Color.gray);
1587             }
1588         }
1589         else {
1590             try {
1591                 BufferedImage img = null;
1592                 int type = BufferedImage.TYPE_4BYTE_ABGR;
1593                 if (paint.getTransparency() == Transparency.OPAQUE) {
1594                     type = BufferedImage.TYPE_3BYTE_BGR;
1595                 }
1596                 img = new BufferedImage((int)width, (int)height, type);
1597                 Graphics2D g = (Graphics2D)img.getGraphics();
1598                 g.transform(transform);
1599                 AffineTransform inv = transform.createInverse();
1600                 Shape fillRect = new Rectangle2D.Double(0,0,img.getWidth(),img.getHeight());
1601                 fillRect = inv.createTransformedShape(fillRect);
1602                 g.setPaint(paint);
1603                 g.fill(fillRect);
1604                 if (invert) {
1605                     AffineTransform tx = new AffineTransform();
1606                     tx.scale(1,-1);
1607                     tx.translate(-xoffset,-yoffset);
1608                     g.drawImage(img,tx,null);
1609                 }
1610                 g.dispose();
1611                 g = null;
1612                 com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(img, null);
1613                 PdfPatternPainter pattern = cb.createPattern(width, height);
1614                 image.setAbsolutePosition(0,0);
1615                 pattern.addImage(image);
1616                 if (fill)
1617                     cb.setPatternFill(pattern);
1618                 else
1619                     cb.setPatternStroke(pattern);
1620             } catch (Exception ex) {
1621                 if (fill)
1622                     cb.setColorFill(Color.gray);
1623                 else
1624                     cb.setColorStroke(Color.gray);
1625             }
1626         }
1627     }
1628 
waitForImage(java.awt.Image image)1629     private synchronized void waitForImage(java.awt.Image image) {
1630         if (mediaTracker == null)
1631             mediaTracker = new MediaTracker(new PdfGraphics2D.FakeComponent());
1632         mediaTracker.addImage(image, 0);
1633         try {
1634             mediaTracker.waitForID(0);
1635         }
1636         catch (InterruptedException e) {
1637             // empty on purpose
1638         }
1639         mediaTracker.removeImage(image);
1640     }
1641 
1642     static private class FakeComponent extends Component {
1643 
1644 		private static final long serialVersionUID = 6450197945596086638L;
1645     }
1646 
1647     /**
1648      * @since 2.0.8
1649      */
1650     public static class HyperLinkKey extends RenderingHints.Key
1651 	{
1652 	 	public static final HyperLinkKey KEY_INSTANCE = new HyperLinkKey(9999);
1653 	 	public static final Object VALUE_HYPERLINKKEY_OFF = "0";
1654 
HyperLinkKey(int arg0)1655 		protected HyperLinkKey(int arg0) {
1656 			super(arg0);
1657 		}
1658 
isCompatibleValue(Object val)1659 		public boolean isCompatibleValue(Object val)
1660 		{
1661 			return true;
1662 		}
toString()1663 		public String toString()
1664 		{
1665 			return "HyperLinkKey";
1666 		}
1667 	}
1668 
1669 }
1670