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