001/* 002 * $Id: Image.java 4862 2011-05-11 15:57:42Z redlab_b $ 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; 045 046import java.awt.Graphics2D; 047import java.awt.image.BufferedImage; 048import java.io.IOException; 049import java.io.InputStream; 050import java.lang.reflect.Constructor; 051import java.net.MalformedURLException; 052import java.net.URL; 053 054import com.itextpdf.text.api.Indentable; 055import com.itextpdf.text.api.Spaceable; 056import com.itextpdf.text.error_messages.MessageLocalization; 057import com.itextpdf.text.pdf.ICC_Profile; 058import com.itextpdf.text.pdf.PRIndirectReference; 059import com.itextpdf.text.pdf.PdfArray; 060import com.itextpdf.text.pdf.PdfContentByte; 061import com.itextpdf.text.pdf.PdfDictionary; 062import com.itextpdf.text.pdf.PdfIndirectReference; 063import com.itextpdf.text.pdf.PdfName; 064import com.itextpdf.text.pdf.PdfNumber; 065import com.itextpdf.text.pdf.PdfOCG; 066import com.itextpdf.text.pdf.PdfObject; 067import com.itextpdf.text.pdf.PdfReader; 068import com.itextpdf.text.pdf.PdfStream; 069import com.itextpdf.text.pdf.PdfTemplate; 070import com.itextpdf.text.pdf.PdfWriter; 071import com.itextpdf.text.pdf.RandomAccessFileOrArray; 072import com.itextpdf.text.pdf.codec.BmpImage; 073import com.itextpdf.text.pdf.codec.CCITTG4Encoder; 074import com.itextpdf.text.pdf.codec.GifImage; 075import com.itextpdf.text.pdf.codec.JBIG2Image; 076import com.itextpdf.text.pdf.codec.PngImage; 077import com.itextpdf.text.pdf.codec.TiffImage; 078 079/** 080 * An <CODE>Image</CODE> is the representation of a graphic element (JPEG, PNG 081 * or GIF) that has to be inserted into the document 082 * 083 * @see Element 084 * @see Rectangle 085 */ 086 087public abstract class Image extends Rectangle implements Indentable, Spaceable { 088 089 // static final membervariables 090 091 /** this is a kind of image alignment. */ 092 public static final int DEFAULT = 0; 093 094 /** this is a kind of image alignment. */ 095 public static final int RIGHT = 2; 096 097 /** this is a kind of image alignment. */ 098 public static final int LEFT = 0; 099 100 /** this is a kind of image alignment. */ 101 public static final int MIDDLE = 1; 102 103 /** this is a kind of image alignment. */ 104 public static final int TEXTWRAP = 4; 105 106 /** this is a kind of image alignment. */ 107 public static final int UNDERLYING = 8; 108 109 /** This represents a coordinate in the transformation matrix. */ 110 public static final int AX = 0; 111 112 /** This represents a coordinate in the transformation matrix. */ 113 public static final int AY = 1; 114 115 /** This represents a coordinate in the transformation matrix. */ 116 public static final int BX = 2; 117 118 /** This represents a coordinate in the transformation matrix. */ 119 public static final int BY = 3; 120 121 /** This represents a coordinate in the transformation matrix. */ 122 public static final int CX = 4; 123 124 /** This represents a coordinate in the transformation matrix. */ 125 public static final int CY = 5; 126 127 /** This represents a coordinate in the transformation matrix. */ 128 public static final int DX = 6; 129 130 /** This represents a coordinate in the transformation matrix. */ 131 public static final int DY = 7; 132 133 /** type of image */ 134 public static final int ORIGINAL_NONE = 0; 135 136 /** type of image */ 137 public static final int ORIGINAL_JPEG = 1; 138 139 /** type of image */ 140 public static final int ORIGINAL_PNG = 2; 141 142 /** type of image */ 143 public static final int ORIGINAL_GIF = 3; 144 145 /** type of image */ 146 public static final int ORIGINAL_BMP = 4; 147 148 /** type of image */ 149 public static final int ORIGINAL_TIFF = 5; 150 151 /** type of image */ 152 public static final int ORIGINAL_WMF = 6; 153 154 /** type of image */ 155 public static final int ORIGINAL_PS = 7; 156 157 /** type of image */ 158 public static final int ORIGINAL_JPEG2000 = 8; 159 160 /** 161 * type of image 162 * @since 2.1.5 163 */ 164 public static final int ORIGINAL_JBIG2 = 9; 165 166 // member variables 167 168 /** The image type. */ 169 protected int type; 170 171 /** The URL of the image. */ 172 protected URL url; 173 174 /** The raw data of the image. */ 175 protected byte rawData[]; 176 177 /** The bits per component of the raw image. It also flags a CCITT image. */ 178 protected int bpc = 1; 179 180 /** The template to be treated as an image. */ 181 protected PdfTemplate template[] = new PdfTemplate[1]; 182 183 /** The alignment of the Image. */ 184 protected int alignment; 185 186 /** Text that can be shown instead of the image. */ 187 protected String alt; 188 189 /** This is the absolute X-position of the image. */ 190 protected float absoluteX = Float.NaN; 191 192 /** This is the absolute Y-position of the image. */ 193 protected float absoluteY = Float.NaN; 194 195 /** This is the width of the image without rotation. */ 196 protected float plainWidth; 197 198 /** This is the width of the image without rotation. */ 199 protected float plainHeight; 200 201 /** This is the scaled width of the image taking rotation into account. */ 202 protected float scaledWidth; 203 204 /** This is the original height of the image taking rotation into account. */ 205 protected float scaledHeight; 206 207 /** 208 * The compression level of the content streams. 209 * @since 2.1.3 210 */ 211 protected int compressionLevel = PdfStream.DEFAULT_COMPRESSION; 212 213 /** an iText attributed unique id for this image. */ 214 protected Long mySerialId = getSerialId(); 215 216 // image from file or URL 217 218 /** 219 * Constructs an <CODE>Image</CODE> -object, using an <VAR>url </VAR>. 220 * 221 * @param url 222 * the <CODE>URL</CODE> where the image can be found. 223 */ 224 public Image(final URL url) { 225 super(0, 0); 226 this.url = url; 227 this.alignment = DEFAULT; 228 rotationRadians = 0; 229 } 230 231 /** 232 * Gets an instance of an Image. 233 * 234 * @param url 235 * an URL 236 * @return an Image 237 * @throws BadElementException 238 * @throws MalformedURLException 239 * @throws IOException 240 */ 241 public static Image getInstance(final URL url) throws BadElementException, 242 MalformedURLException, IOException { 243 InputStream is = null; 244 try { 245 is = url.openStream(); 246 int c1 = is.read(); 247 int c2 = is.read(); 248 int c3 = is.read(); 249 int c4 = is.read(); 250 // jbig2 251 int c5 = is.read(); 252 int c6 = is.read(); 253 int c7 = is.read(); 254 int c8 = is.read(); 255 is.close(); 256 257 is = null; 258 if (c1 == 'G' && c2 == 'I' && c3 == 'F') { 259 GifImage gif = new GifImage(url); 260 Image img = gif.getImage(1); 261 return img; 262 } 263 if (c1 == 0xFF && c2 == 0xD8) { 264 return new Jpeg(url); 265 } 266 if (c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x0c) { 267 return new Jpeg2000(url); 268 } 269 if (c1 == 0xff && c2 == 0x4f && c3 == 0xff && c4 == 0x51) { 270 return new Jpeg2000(url); 271 } 272 if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1] 273 && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) { 274 return PngImage.getImage(url); 275 } 276 if (c1 == 0xD7 && c2 == 0xCD) { 277 return new ImgWMF(url); 278 } 279 if (c1 == 'B' && c2 == 'M') { 280 return BmpImage.getImage(url); 281 } 282 if (c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42 283 || c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0) { 284 RandomAccessFileOrArray ra = null; 285 try { 286 if (url.getProtocol().equals("file")) { 287 String file = url.getFile(); 288 file = Utilities.unEscapeURL(file); 289 ra = new RandomAccessFileOrArray(file); 290 } else 291 ra = new RandomAccessFileOrArray(url); 292 Image img = TiffImage.getTiffImage(ra, 1); 293 img.url = url; 294 return img; 295 } finally { 296 if (ra != null) 297 ra.close(); 298 } 299 300 } 301 if ( c1 == 0x97 && c2 == 'J' && c3 == 'B' && c4 == '2' && 302 c5 == '\r' && c6 == '\n' && c7 == 0x1a && c8 == '\n' ) { 303 RandomAccessFileOrArray ra = null; 304 try { 305 if (url.getProtocol().equals("file")) { 306 String file = url.getFile(); 307 file = Utilities.unEscapeURL(file); 308 ra = new RandomAccessFileOrArray(file); 309 } else 310 ra = new RandomAccessFileOrArray(url); 311 Image img = JBIG2Image.getJbig2Image(ra, 1); 312 img.url = url; 313 return img; 314 } finally { 315 if (ra != null) 316 ra.close(); 317 } 318 } 319 throw new IOException(url.toString() 320 + " is not a recognized imageformat."); 321 } finally { 322 if (is != null) { 323 is.close(); 324 } 325 } 326 } 327 328 /** 329 * Gets an instance of an Image. 330 * 331 * @param filename 332 * a filename 333 * @return an object of type <CODE>Gif</CODE>,<CODE>Jpeg</CODE> or 334 * <CODE>Png</CODE> 335 * @throws BadElementException 336 * @throws MalformedURLException 337 * @throws IOException 338 */ 339 public static Image getInstance(final String filename) 340 throws BadElementException, MalformedURLException, IOException { 341 return getInstance(Utilities.toURL(filename)); 342 } 343 344 /** 345 * gets an instance of an Image 346 * 347 * @param imgb 348 * raw image date 349 * @return an Image object 350 * @throws BadElementException 351 * @throws MalformedURLException 352 * @throws IOException 353 */ 354 public static Image getInstance(final byte imgb[]) throws BadElementException, 355 MalformedURLException, IOException { 356 InputStream is = null; 357 try { 358 is = new java.io.ByteArrayInputStream(imgb); 359 int c1 = is.read(); 360 int c2 = is.read(); 361 int c3 = is.read(); 362 int c4 = is.read(); 363 is.close(); 364 365 is = null; 366 if (c1 == 'G' && c2 == 'I' && c3 == 'F') { 367 GifImage gif = new GifImage(imgb); 368 return gif.getImage(1); 369 } 370 if (c1 == 0xFF && c2 == 0xD8) { 371 return new Jpeg(imgb); 372 } 373 if (c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x0c) { 374 return new Jpeg2000(imgb); 375 } 376 if (c1 == 0xff && c2 == 0x4f && c3 == 0xff && c4 == 0x51) { 377 return new Jpeg2000(imgb); 378 } 379 if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1] 380 && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) { 381 return PngImage.getImage(imgb); 382 } 383 if (c1 == 0xD7 && c2 == 0xCD) { 384 return new ImgWMF(imgb); 385 } 386 if (c1 == 'B' && c2 == 'M') { 387 return BmpImage.getImage(imgb); 388 } 389 if (c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42 390 || c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0) { 391 RandomAccessFileOrArray ra = null; 392 try { 393 ra = new RandomAccessFileOrArray(imgb); 394 Image img = TiffImage.getTiffImage(ra, 1); 395 if (img.getOriginalData() == null) 396 img.setOriginalData(imgb); 397 return img; 398 } finally { 399 if (ra != null) 400 ra.close(); 401 } 402 403 } 404 if ( c1 == 0x97 && c2 == 'J' && c3 == 'B' && c4 == '2' ) { 405 is = new java.io.ByteArrayInputStream(imgb); 406 is.skip(4); 407 int c5 = is.read(); 408 int c6 = is.read(); 409 int c7 = is.read(); 410 int c8 = is.read(); 411 if ( c5 == '\r' && c6 == '\n' && c7 == 0x1a && c8 == '\n' ) { 412 int file_header_flags = is.read(); 413 // TODO number of pages never read, can be removed? 414 int number_of_pages = -1; 415 if ( (file_header_flags & 0x2) == 0x2 ) { 416 number_of_pages = is.read() << 24 | is.read() << 16 | is.read() << 8 | is.read(); 417 } 418 is.close(); 419 // a jbig2 file with a file header. the header is the only way we know here. 420 // embedded jbig2s don't have a header, have to create them by explicit use of Jbig2Image? 421 // nkerr, 2008-12-05 see also the getInstance(URL) 422 RandomAccessFileOrArray ra = null; 423 try { 424 ra = new RandomAccessFileOrArray(imgb); 425 Image img = JBIG2Image.getJbig2Image(ra, 1); 426 if (img.getOriginalData() == null) 427 img.setOriginalData(imgb); 428 return img; 429 } finally { 430 if (ra != null) 431 ra.close(); 432 } 433 } 434 } 435 throw new IOException(MessageLocalization.getComposedMessage("the.byte.array.is.not.a.recognized.imageformat")); 436 } finally { 437 if (is != null) { 438 is.close(); 439 } 440 } 441 } 442 443 /** 444 * Gets an instance of an Image in raw mode. 445 * 446 * @param width 447 * the width of the image in pixels 448 * @param height 449 * the height of the image in pixels 450 * @param components 451 * 1,3 or 4 for GrayScale, RGB and CMYK 452 * @param data 453 * the image data 454 * @param bpc 455 * bits per component 456 * @return an object of type <CODE>ImgRaw</CODE> 457 * @throws BadElementException 458 * on error 459 */ 460 public static Image getInstance(final int width, final int height, final int components, 461 final int bpc, final byte data[]) throws BadElementException { 462 return Image.getInstance(width, height, components, bpc, data, null); 463 } 464 465 /** 466 * Creates a JBIG2 Image. 467 * @param width the width of the image 468 * @param height the height of the image 469 * @param data the raw image data 470 * @param globals JBIG2 globals 471 * @return the Image 472 * @since 2.1.5 473 */ 474 public static Image getInstance(final int width, final int height, final byte[] data, final byte[] globals) { 475 Image img = new ImgJBIG2(width, height, data, globals); 476 return img; 477 } 478 479 /** 480 * Creates an Image with CCITT G3 or G4 compression. It assumes that the 481 * data bytes are already compressed. 482 * 483 * @param width 484 * the exact width of the image 485 * @param height 486 * the exact height of the image 487 * @param reverseBits 488 * reverses the bits in <code>data</code>. Bit 0 is swapped 489 * with bit 7 and so on 490 * @param typeCCITT 491 * the type of compression in <code>data</code>. It can be 492 * CCITTG4, CCITTG31D, CCITTG32D 493 * @param parameters 494 * parameters associated with this stream. Possible values are 495 * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and 496 * CCITT_ENDOFBLOCK or a combination of them 497 * @param data 498 * the image data 499 * @return an Image object 500 * @throws BadElementException 501 * on error 502 */ 503 public static Image getInstance(final int width, final int height, final boolean reverseBits, 504 final int typeCCITT, final int parameters, final byte[] data) 505 throws BadElementException { 506 return Image.getInstance(width, height, reverseBits, typeCCITT, 507 parameters, data, null); 508 } 509 510 /** 511 * Creates an Image with CCITT G3 or G4 compression. It assumes that the 512 * data bytes are already compressed. 513 * 514 * @param width 515 * the exact width of the image 516 * @param height 517 * the exact height of the image 518 * @param reverseBits 519 * reverses the bits in <code>data</code>. Bit 0 is swapped 520 * with bit 7 and so on 521 * @param typeCCITT 522 * the type of compression in <code>data</code>. It can be 523 * CCITTG4, CCITTG31D, CCITTG32D 524 * @param parameters 525 * parameters associated with this stream. Possible values are 526 * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and 527 * CCITT_ENDOFBLOCK or a combination of them 528 * @param data 529 * the image data 530 * @param transparency 531 * transparency information in the Mask format of the image 532 * dictionary 533 * @return an Image object 534 * @throws BadElementException 535 * on error 536 */ 537 public static Image getInstance(final int width, final int height, final boolean reverseBits, 538 final int typeCCITT, final int parameters, final byte[] data, final int transparency[]) 539 throws BadElementException { 540 if (transparency != null && transparency.length != 2) 541 throw new BadElementException(MessageLocalization.getComposedMessage("transparency.length.must.be.equal.to.2.with.ccitt.images")); 542 Image img = new ImgCCITT(width, height, reverseBits, typeCCITT, 543 parameters, data); 544 img.transparency = transparency; 545 return img; 546 } 547 548 /** 549 * Gets an instance of an Image in raw mode. 550 * 551 * @param width 552 * the width of the image in pixels 553 * @param height 554 * the height of the image in pixels 555 * @param components 556 * 1,3 or 4 for GrayScale, RGB and CMYK 557 * @param data 558 * the image data 559 * @param bpc 560 * bits per component 561 * @param transparency 562 * transparency information in the Mask format of the image 563 * dictionary 564 * @return an object of type <CODE>ImgRaw</CODE> 565 * @throws BadElementException 566 * on error 567 */ 568 public static Image getInstance(final int width, final int height, final int components, 569 final int bpc, final byte data[], final int transparency[]) 570 throws BadElementException { 571 if (transparency != null && transparency.length != components * 2) 572 throw new BadElementException(MessageLocalization.getComposedMessage("transparency.length.must.be.equal.to.componentes.2")); 573 if (components == 1 && bpc == 1) { 574 byte g4[] = CCITTG4Encoder.compress(data, width, height); 575 return Image.getInstance(width, height, false, Image.CCITTG4, 576 Image.CCITT_BLACKIS1, g4, transparency); 577 } 578 Image img = new ImgRaw(width, height, components, bpc, data); 579 img.transparency = transparency; 580 return img; 581 } 582 583 // images from a PdfTemplate 584 585 /** 586 * gets an instance of an Image 587 * 588 * @param template 589 * a PdfTemplate that has to be wrapped in an Image object 590 * @return an Image object 591 * @throws BadElementException 592 */ 593 public static Image getInstance(final PdfTemplate template) 594 throws BadElementException { 595 return new ImgTemplate(template); 596 } 597 598 // images from a java.awt.Image 599 600 /** 601 * Gets an instance of an Image from a java.awt.Image. 602 * 603 * @param image 604 * the <CODE>java.awt.Image</CODE> to convert 605 * @param color 606 * if different from <CODE>null</CODE> the transparency pixels 607 * are replaced by this color 608 * @param forceBW 609 * if <CODE>true</CODE> the image is treated as black and white 610 * @return an object of type <CODE>ImgRaw</CODE> 611 * @throws BadElementException 612 * on error 613 * @throws IOException 614 * on error 615 */ 616 public static Image getInstance(final java.awt.Image image, final java.awt.Color color, 617 boolean forceBW) throws BadElementException, IOException { 618 619 if(image instanceof BufferedImage){ 620 BufferedImage bi = (BufferedImage) image; 621 if(bi.getType()==BufferedImage.TYPE_BYTE_BINARY 622 && bi.getColorModel().getPixelSize() == 1) { 623 forceBW=true; 624 } 625 } 626 627 java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(image, 628 0, 0, -1, -1, true); 629 try { 630 pg.grabPixels(); 631 } catch (InterruptedException e) { 632 throw new IOException(MessageLocalization.getComposedMessage("java.awt.image.interrupted.waiting.for.pixels")); 633 } 634 if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) { 635 throw new IOException(MessageLocalization.getComposedMessage("java.awt.image.fetch.aborted.or.errored")); 636 } 637 int w = pg.getWidth(); 638 int h = pg.getHeight(); 639 int[] pixels = (int[]) pg.getPixels(); 640 if (forceBW) { 641 int byteWidth = w / 8 + ((w & 7) != 0 ? 1 : 0); 642 byte[] pixelsByte = new byte[byteWidth * h]; 643 644 int index = 0; 645 int size = h * w; 646 int transColor = 1; 647 if (color != null) { 648 transColor = color.getRed() + color.getGreen() 649 + color.getBlue() < 384 ? 0 : 1; 650 } 651 int transparency[] = null; 652 int cbyte = 0x80; 653 int wMarker = 0; 654 int currByte = 0; 655 if (color != null) { 656 for (int j = 0; j < size; j++) { 657 int alpha = pixels[j] >> 24 & 0xff; 658 if (alpha < 250) { 659 if (transColor == 1) 660 currByte |= cbyte; 661 } else { 662 if ((pixels[j] & 0x888) != 0) 663 currByte |= cbyte; 664 } 665 cbyte >>= 1; 666 if (cbyte == 0 || wMarker + 1 >= w) { 667 pixelsByte[index++] = (byte) currByte; 668 cbyte = 0x80; 669 currByte = 0; 670 } 671 ++wMarker; 672 if (wMarker >= w) 673 wMarker = 0; 674 } 675 } else { 676 for (int j = 0; j < size; j++) { 677 if (transparency == null) { 678 int alpha = pixels[j] >> 24 & 0xff; 679 if (alpha == 0) { 680 transparency = new int[2]; 681 /* bugfix by M.P. Liston, ASC, was: ... ? 1: 0; */ 682 transparency[0] = transparency[1] = (pixels[j] & 0x888) != 0 ? 0xff : 0; 683 } 684 } 685 if ((pixels[j] & 0x888) != 0) 686 currByte |= cbyte; 687 cbyte >>= 1; 688 if (cbyte == 0 || wMarker + 1 >= w) { 689 pixelsByte[index++] = (byte) currByte; 690 cbyte = 0x80; 691 currByte = 0; 692 } 693 ++wMarker; 694 if (wMarker >= w) 695 wMarker = 0; 696 } 697 } 698 return Image.getInstance(w, h, 1, 1, pixelsByte, transparency); 699 } else { 700 byte[] pixelsByte = new byte[w * h * 3]; 701 byte[] smask = null; 702 703 int index = 0; 704 int size = h * w; 705 int red = 255; 706 int green = 255; 707 int blue = 255; 708 if (color != null) { 709 red = color.getRed(); 710 green = color.getGreen(); 711 blue = color.getBlue(); 712 } 713 int transparency[] = null; 714 if (color != null) { 715 for (int j = 0; j < size; j++) { 716 int alpha = pixels[j] >> 24 & 0xff; 717 if (alpha < 250) { 718 pixelsByte[index++] = (byte) red; 719 pixelsByte[index++] = (byte) green; 720 pixelsByte[index++] = (byte) blue; 721 } else { 722 pixelsByte[index++] = (byte) (pixels[j] >> 16 & 0xff); 723 pixelsByte[index++] = (byte) (pixels[j] >> 8 & 0xff); 724 pixelsByte[index++] = (byte) (pixels[j] & 0xff); 725 } 726 } 727 } else { 728 int transparentPixel = 0; 729 smask = new byte[w * h]; 730 boolean shades = false; 731 for (int j = 0; j < size; j++) { 732 byte alpha = smask[j] = (byte) (pixels[j] >> 24 & 0xff); 733 /* bugfix by Chris Nokleberg */ 734 if (!shades) { 735 if (alpha != 0 && alpha != -1) { 736 shades = true; 737 } else if (transparency == null) { 738 if (alpha == 0) { 739 transparentPixel = pixels[j] & 0xffffff; 740 transparency = new int[6]; 741 transparency[0] = transparency[1] = transparentPixel >> 16 & 0xff; 742 transparency[2] = transparency[3] = transparentPixel >> 8 & 0xff; 743 transparency[4] = transparency[5] = transparentPixel & 0xff; 744 } 745 } else if ((pixels[j] & 0xffffff) != transparentPixel) { 746 shades = true; 747 } 748 } 749 pixelsByte[index++] = (byte) (pixels[j] >> 16 & 0xff); 750 pixelsByte[index++] = (byte) (pixels[j] >> 8 & 0xff); 751 pixelsByte[index++] = (byte) (pixels[j] & 0xff); 752 } 753 if (shades) 754 transparency = null; 755 else 756 smask = null; 757 } 758 Image img = Image.getInstance(w, h, 3, 8, pixelsByte, transparency); 759 if (smask != null) { 760 Image sm = Image.getInstance(w, h, 1, 8, smask); 761 try { 762 sm.makeMask(); 763 img.setImageMask(sm); 764 } catch (DocumentException de) { 765 throw new ExceptionConverter(de); 766 } 767 } 768 return img; 769 } 770 } 771 772 /** 773 * Gets an instance of an Image from a java.awt.Image. 774 * 775 * @param image 776 * the <CODE>java.awt.Image</CODE> to convert 777 * @param color 778 * if different from <CODE>null</CODE> the transparency pixels 779 * are replaced by this color 780 * @return an object of type <CODE>ImgRaw</CODE> 781 * @throws BadElementException 782 * on error 783 * @throws IOException 784 * on error 785 */ 786 public static Image getInstance(final java.awt.Image image, final java.awt.Color color) 787 throws BadElementException, IOException { 788 return Image.getInstance(image, color, false); 789 } 790 791 /** 792 * Gets an instance of a Image from a java.awt.Image. 793 * The image is added as a JPEG with a user defined quality. 794 * 795 * @param writer 796 * the <CODE>PdfWriter</CODE> object to which the image will be added 797 * @param awtImage 798 * the <CODE>java.awt.Image</CODE> to convert 799 * @param quality 800 * a float value between 0 and 1 801 * @return an object of type <CODE>PdfTemplate</CODE> 802 * @throws BadElementException 803 * on error 804 * @throws IOException 805 */ 806 public static Image getInstance(final PdfWriter writer, final java.awt.Image awtImage, final float quality) throws BadElementException, IOException { 807 return getInstance(new PdfContentByte(writer), awtImage, quality); 808 } 809 810 /** 811 * Gets an instance of a Image from a java.awt.Image. 812 * The image is added as a JPEG with a user defined quality. 813 * 814 * @param cb 815 * the <CODE>PdfContentByte</CODE> object to which the image will be added 816 * @param awtImage 817 * the <CODE>java.awt.Image</CODE> to convert 818 * @param quality 819 * a float value between 0 and 1 820 * @return an object of type <CODE>PdfTemplate</CODE> 821 * @throws BadElementException 822 * on error 823 * @throws IOException 824 */ 825 public static Image getInstance(final PdfContentByte cb, final java.awt.Image awtImage, final float quality) throws BadElementException, IOException { 826 java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(awtImage, 827 0, 0, -1, -1, true); 828 try { 829 pg.grabPixels(); 830 } catch (InterruptedException e) { 831 throw new IOException(MessageLocalization.getComposedMessage("java.awt.image.interrupted.waiting.for.pixels")); 832 } 833 if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) { 834 throw new IOException(MessageLocalization.getComposedMessage("java.awt.image.fetch.aborted.or.errored")); 835 } 836 int w = pg.getWidth(); 837 int h = pg.getHeight(); 838 PdfTemplate tp = cb.createTemplate(w, h); 839 Graphics2D g2d = tp.createGraphics(w, h, true, quality); 840 g2d.drawImage(awtImage, 0, 0, null); 841 g2d.dispose(); 842 return getInstance(tp); 843 } 844 845 // image from indirect reference 846 847 /** 848 * Holds value of property directReference. 849 * An image is embedded into a PDF as an Image XObject. 850 * This object is referenced by a PdfIndirectReference object. 851 */ 852 private PdfIndirectReference directReference; 853 854 /** 855 * Getter for property directReference. 856 * @return Value of property directReference. 857 */ 858 public PdfIndirectReference getDirectReference() { 859 return this.directReference; 860 } 861 862 /** 863 * Setter for property directReference. 864 * @param directReference New value of property directReference. 865 */ 866 public void setDirectReference(final PdfIndirectReference directReference) { 867 this.directReference = directReference; 868 } 869 870 /** 871 * Reuses an existing image. 872 * @param ref the reference to the image dictionary 873 * @throws BadElementException on error 874 * @return the image 875 */ 876 public static Image getInstance(final PRIndirectReference ref) throws BadElementException { 877 PdfDictionary dic = (PdfDictionary)PdfReader.getPdfObjectRelease(ref); 878 int width = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.WIDTH))).intValue(); 879 int height = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.HEIGHT))).intValue(); 880 Image imask = null; 881 PdfObject obj = dic.get(PdfName.SMASK); 882 if (obj != null && obj.isIndirect()) { 883 imask = getInstance((PRIndirectReference)obj); 884 } 885 else { 886 obj = dic.get(PdfName.MASK); 887 if (obj != null && obj.isIndirect()) { 888 PdfObject obj2 = PdfReader.getPdfObjectRelease(obj); 889 if (obj2 instanceof PdfDictionary) 890 imask = getInstance((PRIndirectReference)obj); 891 } 892 } 893 Image img = new ImgRaw(width, height, 1, 1, null); 894 img.imageMask = imask; 895 img.directReference = ref; 896 return img; 897 } 898 899 // copy constructor 900 901 /** 902 * Constructs an <CODE>Image</CODE> -object, using an <VAR>url </VAR>. 903 * 904 * @param image 905 * another Image object. 906 */ 907 protected Image(final Image image) { 908 super(image); 909 this.type = image.type; 910 this.url = image.url; 911 this.rawData = image.rawData; 912 this.bpc = image.bpc; 913 this.template = image.template; 914 this.alignment = image.alignment; 915 this.alt = image.alt; 916 this.absoluteX = image.absoluteX; 917 this.absoluteY = image.absoluteY; 918 this.plainWidth = image.plainWidth; 919 this.plainHeight = image.plainHeight; 920 this.scaledWidth = image.scaledWidth; 921 this.scaledHeight = image.scaledHeight; 922 this.mySerialId = image.mySerialId; 923 924 this.directReference = image.directReference; 925 926 this.rotationRadians = image.rotationRadians; 927 this.initialRotation = image.initialRotation; 928 this.indentationLeft = image.indentationLeft; 929 this.indentationRight = image.indentationRight; 930 this.spacingBefore = image.spacingBefore; 931 this.spacingAfter = image.spacingAfter; 932 933 this.widthPercentage = image.widthPercentage; 934 this.scaleToFitLineWhenOverflow = image.scaleToFitLineWhenOverflow; 935 this.annotation = image.annotation; 936 this.layer = image.layer; 937 this.interpolation = image.interpolation; 938 this.originalType = image.originalType; 939 this.originalData = image.originalData; 940 this.deflated = image.deflated; 941 this.dpiX = image.dpiX; 942 this.dpiY = image.dpiY; 943 this.XYRatio = image.XYRatio; 944 945 this.colorspace = image.colorspace; 946 this.invert = image.invert; 947 this.profile = image.profile; 948 this.additional = image.additional; 949 this.mask = image.mask; 950 this.imageMask = image.imageMask; 951 this.smask = image.smask; 952 this.transparency = image.transparency; 953 } 954 955 /** 956 * gets an instance of an Image 957 * 958 * @param image 959 * an Image object 960 * @return a new Image object 961 */ 962 public static Image getInstance(final Image image) { 963 if (image == null) 964 return null; 965 try { 966 Class<? extends Image> cs = image.getClass(); 967 Constructor<? extends Image> constructor = cs 968 .getDeclaredConstructor(new Class[] { Image.class }); 969 return constructor.newInstance(new Object[] { image }); 970 } catch (Exception e) { 971 throw new ExceptionConverter(e); 972 } 973 } 974 975 // implementation of the Element interface 976 977 /** 978 * Returns the type. 979 * 980 * @return a type 981 */ 982 983 @Override 984 public int type() { 985 return type; 986 } 987 988 /** 989 * @see com.itextpdf.text.Element#isNestable() 990 * @since iText 2.0.8 991 */ 992 @Override 993 public boolean isNestable() { 994 return true; 995 } 996 997 // checking the type of Image 998 999 /** 1000 * Returns <CODE>true</CODE> if the image is a <CODE>Jpeg</CODE> 1001 * -object. 1002 * 1003 * @return a <CODE>boolean</CODE> 1004 */ 1005 1006 public boolean isJpeg() { 1007 return type == JPEG; 1008 } 1009 1010 /** 1011 * Returns <CODE>true</CODE> if the image is a <CODE>ImgRaw</CODE> 1012 * -object. 1013 * 1014 * @return a <CODE>boolean</CODE> 1015 */ 1016 1017 public boolean isImgRaw() { 1018 return type == IMGRAW; 1019 } 1020 1021 /** 1022 * Returns <CODE>true</CODE> if the image is an <CODE>ImgTemplate</CODE> 1023 * -object. 1024 * 1025 * @return a <CODE>boolean</CODE> 1026 */ 1027 1028 public boolean isImgTemplate() { 1029 return type == IMGTEMPLATE; 1030 } 1031 1032 // getters and setters 1033 1034 /** 1035 * Gets the <CODE>String</CODE> -representation of the reference to the 1036 * image. 1037 * 1038 * @return a <CODE>String</CODE> 1039 */ 1040 1041 public URL getUrl() { 1042 return url; 1043 } 1044 1045 /** 1046 * Sets the url of the image 1047 * 1048 * @param url 1049 * the url of the image 1050 */ 1051 public void setUrl(final URL url) { 1052 this.url = url; 1053 } 1054 1055 /** 1056 * Gets the raw data for the image. 1057 * <P> 1058 * Remark: this only makes sense for Images of the type <CODE>RawImage 1059 * </CODE>. 1060 * 1061 * @return the raw data 1062 */ 1063 public byte[] getRawData() { 1064 return rawData; 1065 } 1066 1067 /** 1068 * Gets the bpc for the image. 1069 * <P> 1070 * Remark: this only makes sense for Images of the type <CODE>RawImage 1071 * </CODE>. 1072 * 1073 * @return a bpc value 1074 */ 1075 public int getBpc() { 1076 return bpc; 1077 } 1078 1079 /** 1080 * Gets the template to be used as an image. 1081 * <P> 1082 * Remark: this only makes sense for Images of the type <CODE>ImgTemplate 1083 * </CODE>. 1084 * 1085 * @return the template 1086 */ 1087 public PdfTemplate getTemplateData() { 1088 return template[0]; 1089 } 1090 1091 /** 1092 * Sets data from a PdfTemplate 1093 * 1094 * @param template 1095 * the template with the content 1096 */ 1097 public void setTemplateData(final PdfTemplate template) { 1098 this.template[0] = template; 1099 } 1100 1101 /** 1102 * Gets the alignment for the image. 1103 * 1104 * @return a value 1105 */ 1106 public int getAlignment() { 1107 return alignment; 1108 } 1109 1110 /** 1111 * Sets the alignment for the image. 1112 * 1113 * @param alignment 1114 * the alignment 1115 */ 1116 1117 public void setAlignment(final int alignment) { 1118 this.alignment = alignment; 1119 } 1120 1121 /** 1122 * Gets the alternative text for the image. 1123 * 1124 * @return a <CODE>String</CODE> 1125 */ 1126 1127 public String getAlt() { 1128 return alt; 1129 } 1130 1131 /** 1132 * Sets the alternative information for the image. 1133 * 1134 * @param alt 1135 * the alternative information 1136 */ 1137 1138 public void setAlt(final String alt) { 1139 this.alt = alt; 1140 } 1141 1142 /** 1143 * Sets the absolute position of the <CODE>Image</CODE>. 1144 * 1145 * @param absoluteX 1146 * @param absoluteY 1147 */ 1148 1149 public void setAbsolutePosition(final float absoluteX, final float absoluteY) { 1150 this.absoluteX = absoluteX; 1151 this.absoluteY = absoluteY; 1152 } 1153 1154 /** 1155 * Checks if the <CODE>Images</CODE> has to be added at an absolute X 1156 * position. 1157 * 1158 * @return a boolean 1159 */ 1160 public boolean hasAbsoluteX() { 1161 return !Float.isNaN(absoluteX); 1162 } 1163 1164 /** 1165 * Returns the absolute X position. 1166 * 1167 * @return a position 1168 */ 1169 public float getAbsoluteX() { 1170 return absoluteX; 1171 } 1172 1173 /** 1174 * Checks if the <CODE>Images</CODE> has to be added at an absolute 1175 * position. 1176 * 1177 * @return a boolean 1178 */ 1179 public boolean hasAbsoluteY() { 1180 return !Float.isNaN(absoluteY); 1181 } 1182 1183 /** 1184 * Returns the absolute Y position. 1185 * 1186 * @return a position 1187 */ 1188 public float getAbsoluteY() { 1189 return absoluteY; 1190 } 1191 1192 // width and height 1193 1194 /** 1195 * Gets the scaled width of the image. 1196 * 1197 * @return a value 1198 */ 1199 public float getScaledWidth() { 1200 return scaledWidth; 1201 } 1202 1203 /** 1204 * Gets the scaled height of the image. 1205 * 1206 * @return a value 1207 */ 1208 public float getScaledHeight() { 1209 return scaledHeight; 1210 } 1211 1212 /** 1213 * Gets the plain width of the image. 1214 * 1215 * @return a value 1216 */ 1217 public float getPlainWidth() { 1218 return plainWidth; 1219 } 1220 1221 /** 1222 * Gets the plain height of the image. 1223 * 1224 * @return a value 1225 */ 1226 public float getPlainHeight() { 1227 return plainHeight; 1228 } 1229 1230 /** 1231 * Scale the image to an absolute width and an absolute height. 1232 * 1233 * @param newWidth 1234 * the new width 1235 * @param newHeight 1236 * the new height 1237 */ 1238 public void scaleAbsolute(final float newWidth, final float newHeight) { 1239 plainWidth = newWidth; 1240 plainHeight = newHeight; 1241 float[] matrix = matrix(); 1242 scaledWidth = matrix[DX] - matrix[CX]; 1243 scaledHeight = matrix[DY] - matrix[CY]; 1244 setWidthPercentage(0); 1245 } 1246 1247 /** 1248 * Scale the image to an absolute width. 1249 * 1250 * @param newWidth 1251 * the new width 1252 */ 1253 public void scaleAbsoluteWidth(final float newWidth) { 1254 plainWidth = newWidth; 1255 float[] matrix = matrix(); 1256 scaledWidth = matrix[DX] - matrix[CX]; 1257 scaledHeight = matrix[DY] - matrix[CY]; 1258 setWidthPercentage(0); 1259 } 1260 1261 /** 1262 * Scale the image to an absolute height. 1263 * 1264 * @param newHeight 1265 * the new height 1266 */ 1267 public void scaleAbsoluteHeight(final float newHeight) { 1268 plainHeight = newHeight; 1269 float[] matrix = matrix(); 1270 scaledWidth = matrix[DX] - matrix[CX]; 1271 scaledHeight = matrix[DY] - matrix[CY]; 1272 setWidthPercentage(0); 1273 } 1274 1275 /** 1276 * Scale the image to a certain percentage. 1277 * 1278 * @param percent 1279 * the scaling percentage 1280 */ 1281 public void scalePercent(final float percent) { 1282 scalePercent(percent, percent); 1283 } 1284 1285 /** 1286 * Scale the width and height of an image to a certain percentage. 1287 * 1288 * @param percentX 1289 * the scaling percentage of the width 1290 * @param percentY 1291 * the scaling percentage of the height 1292 */ 1293 public void scalePercent(final float percentX, final float percentY) { 1294 plainWidth = getWidth() * percentX / 100f; 1295 plainHeight = getHeight() * percentY / 100f; 1296 float[] matrix = matrix(); 1297 scaledWidth = matrix[DX] - matrix[CX]; 1298 scaledHeight = matrix[DY] - matrix[CY]; 1299 setWidthPercentage(0); 1300 } 1301 1302 /** 1303 * Scales the image so that it fits a certain width and height. 1304 * 1305 * @param fitWidth 1306 * the width to fit 1307 * @param fitHeight 1308 * the height to fit 1309 */ 1310 public void scaleToFit(final float fitWidth, final float fitHeight) { 1311 scalePercent(100); 1312 float percentX = fitWidth * 100 / getScaledWidth(); 1313 float percentY = fitHeight * 100 / getScaledHeight(); 1314 scalePercent(percentX < percentY ? percentX : percentY); 1315 setWidthPercentage(0); 1316 } 1317 1318 /** 1319 * Returns the transformation matrix of the image. 1320 * 1321 * @return an array [AX, AY, BX, BY, CX, CY, DX, DY] 1322 */ 1323 public float[] matrix() { 1324 float[] matrix = new float[8]; 1325 float cosX = (float) Math.cos(rotationRadians); 1326 float sinX = (float) Math.sin(rotationRadians); 1327 matrix[AX] = plainWidth * cosX; 1328 matrix[AY] = plainWidth * sinX; 1329 matrix[BX] = -plainHeight * sinX; 1330 matrix[BY] = plainHeight * cosX; 1331 if (rotationRadians < Math.PI / 2f) { 1332 matrix[CX] = matrix[BX]; 1333 matrix[CY] = 0; 1334 matrix[DX] = matrix[AX]; 1335 matrix[DY] = matrix[AY] + matrix[BY]; 1336 } else if (rotationRadians < Math.PI) { 1337 matrix[CX] = matrix[AX] + matrix[BX]; 1338 matrix[CY] = matrix[BY]; 1339 matrix[DX] = 0; 1340 matrix[DY] = matrix[AY]; 1341 } else if (rotationRadians < Math.PI * 1.5f) { 1342 matrix[CX] = matrix[AX]; 1343 matrix[CY] = matrix[AY] + matrix[BY]; 1344 matrix[DX] = matrix[BX]; 1345 matrix[DY] = 0; 1346 } else { 1347 matrix[CX] = 0; 1348 matrix[CY] = matrix[AY]; 1349 matrix[DX] = matrix[AX] + matrix[BX]; 1350 matrix[DY] = matrix[BY]; 1351 } 1352 return matrix; 1353 } 1354 1355 // serial stamping 1356 1357 /** a static that is used for attributing a unique id to each image. */ 1358 static long serialId = 0; 1359 1360 /** Creates a new serial id. 1361 * @return the new serialId */ 1362 static protected synchronized Long getSerialId() { 1363 ++serialId; 1364 return Long.valueOf(serialId); 1365 } 1366 1367 /** 1368 * Returns a serial id for the Image (reuse the same image more than once) 1369 * 1370 * @return a serialId 1371 */ 1372 public Long getMySerialId() { 1373 return mySerialId; 1374 } 1375 1376 // rotation, note that the superclass also has a rotation value. 1377 1378 /** This is the rotation of the image in radians. */ 1379 protected float rotationRadians; 1380 1381 /** Holds value of property initialRotation. */ 1382 private float initialRotation; 1383 1384 /** 1385 * Gets the current image rotation in radians. 1386 * @return the current image rotation in radians 1387 */ 1388 public float getImageRotation() { 1389 double d = 2.0 * Math.PI; 1390 float rot = (float) ((rotationRadians - initialRotation) % d); 1391 if (rot < 0) { 1392 rot += d; 1393 } 1394 return rot; 1395 } 1396 1397 /** 1398 * Sets the rotation of the image in radians. 1399 * 1400 * @param r 1401 * rotation in radians 1402 */ 1403 public void setRotation(final float r) { 1404 double d = 2.0 * Math.PI; 1405 rotationRadians = (float) ((r + initialRotation) % d); 1406 if (rotationRadians < 0) { 1407 rotationRadians += d; 1408 } 1409 float[] matrix = matrix(); 1410 scaledWidth = matrix[DX] - matrix[CX]; 1411 scaledHeight = matrix[DY] - matrix[CY]; 1412 } 1413 1414 /** 1415 * Sets the rotation of the image in degrees. 1416 * 1417 * @param deg 1418 * rotation in degrees 1419 */ 1420 public void setRotationDegrees(final float deg) { 1421 double d = Math.PI; 1422 setRotation(deg / 180 * (float) d); 1423 } 1424 1425 /** 1426 * Getter for property initialRotation. 1427 * @return Value of property initialRotation. 1428 */ 1429 public float getInitialRotation() { 1430 return this.initialRotation; 1431 } 1432 1433 /** 1434 * Some image formats, like TIFF may present the images rotated that have 1435 * to be compensated. 1436 * @param initialRotation New value of property initialRotation. 1437 */ 1438 public void setInitialRotation(final float initialRotation) { 1439 float old_rot = rotationRadians - this.initialRotation; 1440 this.initialRotation = initialRotation; 1441 setRotation(old_rot); 1442 } 1443 1444 // indentations 1445 1446 /** the indentation to the left. */ 1447 protected float indentationLeft = 0; 1448 1449 /** the indentation to the right. */ 1450 protected float indentationRight = 0; 1451 1452 /** The spacing before the image. */ 1453 protected float spacingBefore; 1454 1455 /** The spacing after the image. */ 1456 protected float spacingAfter; 1457 1458 /** 1459 * Gets the left indentation. 1460 * 1461 * @return the left indentation 1462 */ 1463 public float getIndentationLeft() { 1464 return indentationLeft; 1465 } 1466 1467 /** 1468 * Sets the left indentation. 1469 * 1470 * @param f 1471 */ 1472 public void setIndentationLeft(final float f) { 1473 indentationLeft = f; 1474 } 1475 1476 /** 1477 * Gets the right indentation. 1478 * 1479 * @return the right indentation 1480 */ 1481 public float getIndentationRight() { 1482 return indentationRight; 1483 } 1484 1485 /** 1486 * Sets the right indentation. 1487 * 1488 * @param f 1489 */ 1490 public void setIndentationRight(final float f) { 1491 indentationRight = f; 1492 } 1493 1494 /** 1495 * Gets the spacing before this image. 1496 * 1497 * @return the spacing 1498 */ 1499 public float getSpacingBefore() { 1500 return spacingBefore; 1501 } 1502 1503 /** 1504 * Sets the spacing before this image. 1505 * 1506 * @param spacing 1507 * the new spacing 1508 */ 1509 1510 public void setSpacingBefore(final float spacing) { 1511 this.spacingBefore = spacing; 1512 } 1513 1514 /** 1515 * Gets the spacing before this image. 1516 * 1517 * @return the spacing 1518 */ 1519 public float getSpacingAfter() { 1520 return spacingAfter; 1521 } 1522 1523 /** 1524 * Sets the spacing after this image. 1525 * 1526 * @param spacing 1527 * the new spacing 1528 */ 1529 1530 public void setSpacingAfter(final float spacing) { 1531 this.spacingAfter = spacing; 1532 } 1533 1534 // widthpercentage (for the moment only used in ColumnText) 1535 1536 /** 1537 * Holds value of property widthPercentage. 1538 */ 1539 private float widthPercentage = 100; 1540 1541 /** 1542 * Getter for property widthPercentage. 1543 * 1544 * @return Value of property widthPercentage. 1545 */ 1546 public float getWidthPercentage() { 1547 return this.widthPercentage; 1548 } 1549 1550 /** 1551 * Setter for property widthPercentage. 1552 * 1553 * @param widthPercentage 1554 * New value of property widthPercentage. 1555 */ 1556 public void setWidthPercentage(final float widthPercentage) { 1557 this.widthPercentage = widthPercentage; 1558 } 1559 1560 // scaling the image to the available width (or not) 1561 1562 /** 1563 * Indicates if the image should be scaled to fit the line 1564 * when the image exceeds the available width. 1565 * @since iText 5.0.6 1566 */ 1567 protected boolean scaleToFitLineWhenOverflow; 1568 1569 /** 1570 * Gets the value of scaleToFitLineWhenOverflow. 1571 * @return true if the image size has to scale to the available width 1572 * @since iText 5.0.6 1573 */ 1574 public boolean isScaleToFitLineWhenOverflow() { 1575 return scaleToFitLineWhenOverflow; 1576 } 1577 1578 /** 1579 * Sets the value of scaleToFitLineWhenOverflow 1580 * @param scaleToFitLineWhenOverflow true if you want the image to scale to the available width 1581 * @since iText 5.0.6 1582 */ 1583 public void setScaleToFitLineWhenOverflow(final boolean scaleToFitLineWhenOverflow) { 1584 this.scaleToFitLineWhenOverflow = scaleToFitLineWhenOverflow; 1585 } 1586 1587 // annotation 1588 1589 /** if the annotation is not null the image will be clickable. */ 1590 protected Annotation annotation = null; 1591 1592 /** 1593 * Sets the annotation of this Image. 1594 * 1595 * @param annotation 1596 * the annotation 1597 */ 1598 public void setAnnotation(final Annotation annotation) { 1599 this.annotation = annotation; 1600 } 1601 1602 /** 1603 * Gets the annotation. 1604 * 1605 * @return the annotation that is linked to this image 1606 */ 1607 public Annotation getAnnotation() { 1608 return annotation; 1609 } 1610 1611 // Optional Content 1612 1613 /** Optional Content layer to which we want this Image to belong. */ 1614 protected PdfOCG layer; 1615 1616 /** 1617 * Gets the layer this image belongs to. 1618 * 1619 * @return the layer this image belongs to or <code>null</code> for no 1620 * layer defined 1621 */ 1622 public PdfOCG getLayer() { 1623 return layer; 1624 } 1625 1626 /** 1627 * Sets the layer this image belongs to. 1628 * 1629 * @param layer 1630 * the layer this image belongs to 1631 */ 1632 public void setLayer(final PdfOCG layer) { 1633 this.layer = layer; 1634 } 1635 1636 // interpolation 1637 1638 /** Holds value of property interpolation. */ 1639 protected boolean interpolation; 1640 1641 /** 1642 * Getter for property interpolation. 1643 * 1644 * @return Value of property interpolation. 1645 */ 1646 public boolean isInterpolation() { 1647 return interpolation; 1648 } 1649 1650 /** 1651 * Sets the image interpolation. Image interpolation attempts to produce a 1652 * smooth transition between adjacent sample values. 1653 * 1654 * @param interpolation 1655 * New value of property interpolation. 1656 */ 1657 public void setInterpolation(final boolean interpolation) { 1658 this.interpolation = interpolation; 1659 } 1660 1661 // original type and data 1662 1663 /** Holds value of property originalType. */ 1664 protected int originalType = ORIGINAL_NONE; 1665 1666 /** Holds value of property originalData. */ 1667 protected byte[] originalData; 1668 1669 /** 1670 * Getter for property originalType. 1671 * 1672 * @return Value of property originalType. 1673 * 1674 */ 1675 public int getOriginalType() { 1676 return this.originalType; 1677 } 1678 1679 /** 1680 * Setter for property originalType. 1681 * 1682 * @param originalType 1683 * New value of property originalType. 1684 * 1685 */ 1686 public void setOriginalType(final int originalType) { 1687 this.originalType = originalType; 1688 } 1689 1690 /** 1691 * Getter for property originalData. 1692 * 1693 * @return Value of property originalData. 1694 * 1695 */ 1696 public byte[] getOriginalData() { 1697 return this.originalData; 1698 } 1699 1700 /** 1701 * Setter for property originalData. 1702 * 1703 * @param originalData 1704 * New value of property originalData. 1705 * 1706 */ 1707 public void setOriginalData(final byte[] originalData) { 1708 this.originalData = originalData; 1709 } 1710 1711 // the following values are only set for specific types of images. 1712 1713 /** Holds value of property deflated. */ 1714 protected boolean deflated = false; 1715 1716 /** 1717 * Getter for property deflated. 1718 * 1719 * @return Value of property deflated. 1720 * 1721 */ 1722 public boolean isDeflated() { 1723 return this.deflated; 1724 } 1725 1726 /** 1727 * Setter for property deflated. 1728 * 1729 * @param deflated 1730 * New value of property deflated. 1731 */ 1732 public void setDeflated(final boolean deflated) { 1733 this.deflated = deflated; 1734 } 1735 1736 // DPI info 1737 1738 /** Holds value of property dpiX. */ 1739 protected int dpiX = 0; 1740 1741 /** Holds value of property dpiY. */ 1742 protected int dpiY = 0; 1743 1744 /** 1745 * Gets the dots-per-inch in the X direction. Returns 0 if not available. 1746 * 1747 * @return the dots-per-inch in the X direction 1748 */ 1749 public int getDpiX() { 1750 return dpiX; 1751 } 1752 1753 /** 1754 * Gets the dots-per-inch in the Y direction. Returns 0 if not available. 1755 * 1756 * @return the dots-per-inch in the Y direction 1757 */ 1758 public int getDpiY() { 1759 return dpiY; 1760 } 1761 1762 /** 1763 * Sets the dots per inch value 1764 * 1765 * @param dpiX 1766 * dpi for x coordinates 1767 * @param dpiY 1768 * dpi for y coordinates 1769 */ 1770 public void setDpi(final int dpiX, final int dpiY) { 1771 this.dpiX = dpiX; 1772 this.dpiY = dpiY; 1773 } 1774 1775 // XY Ratio 1776 1777 /** Holds value of property XYRatio. */ 1778 private float XYRatio = 0; 1779 1780 /** 1781 * Gets the X/Y pixel dimensionless aspect ratio. 1782 * 1783 * @return the X/Y pixel dimensionless aspect ratio 1784 */ 1785 public float getXYRatio() { 1786 return this.XYRatio; 1787 } 1788 1789 /** 1790 * Sets the X/Y pixel dimensionless aspect ratio. 1791 * 1792 * @param XYRatio 1793 * the X/Y pixel dimensionless aspect ratio 1794 */ 1795 public void setXYRatio(final float XYRatio) { 1796 this.XYRatio = XYRatio; 1797 } 1798 1799 // color, colorspaces and transparency 1800 1801 /** this is the colorspace of a jpeg-image. */ 1802 protected int colorspace = -1; 1803 1804 /** 1805 * Gets the colorspace for the image. 1806 * <P> 1807 * Remark: this only makes sense for Images of the type <CODE>Jpeg</CODE>. 1808 * 1809 * @return a colorspace value 1810 */ 1811 public int getColorspace() { 1812 return colorspace; 1813 } 1814 1815 /** Image color inversion */ 1816 protected boolean invert = false; 1817 1818 /** 1819 * Getter for the inverted value 1820 * 1821 * @return true if the image is inverted 1822 */ 1823 public boolean isInverted() { 1824 return invert; 1825 } 1826 1827 /** 1828 * Sets inverted true or false 1829 * 1830 * @param invert 1831 * true or false 1832 */ 1833 public void setInverted(final boolean invert) { 1834 this.invert = invert; 1835 } 1836 1837 /** ICC Profile attached */ 1838 protected ICC_Profile profile = null; 1839 1840 /** 1841 * Tags this image with an ICC profile. 1842 * 1843 * @param profile 1844 * the profile 1845 */ 1846 public void tagICC(final ICC_Profile profile) { 1847 this.profile = profile; 1848 } 1849 1850 /** 1851 * Checks is the image has an ICC profile. 1852 * 1853 * @return the ICC profile or <CODE>null</CODE> 1854 */ 1855 public boolean hasICCProfile() { 1856 return this.profile != null; 1857 } 1858 1859 /** 1860 * Gets the images ICC profile. 1861 * 1862 * @return the ICC profile 1863 */ 1864 public ICC_Profile getICCProfile() { 1865 return profile; 1866 } 1867 1868 /** a dictionary with additional information */ 1869 private PdfDictionary additional = null; 1870 1871 /** 1872 * Getter for the dictionary with additional information. 1873 * 1874 * @return a PdfDictionary with additional information. 1875 */ 1876 public PdfDictionary getAdditional() { 1877 return this.additional; 1878 } 1879 1880 /** 1881 * Sets the /Colorspace key. 1882 * 1883 * @param additional 1884 * a PdfDictionary with additional information. 1885 */ 1886 public void setAdditional(final PdfDictionary additional) { 1887 this.additional = additional; 1888 } 1889 1890 /** 1891 * Replaces CalRGB and CalGray colorspaces with DeviceRGB and DeviceGray. 1892 */ 1893 public void simplifyColorspace() { 1894 if (additional == null) 1895 return; 1896 PdfArray value = additional.getAsArray(PdfName.COLORSPACE); 1897 if (value == null) 1898 return; 1899 PdfObject cs = simplifyColorspace(value); 1900 PdfObject newValue; 1901 if (cs.isName()) 1902 newValue = cs; 1903 else { 1904 newValue = value; 1905 PdfName first = value.getAsName(0); 1906 if (PdfName.INDEXED.equals(first)) { 1907 if (value.size() >= 2) { 1908 PdfArray second = value.getAsArray(1); 1909 if (second != null) { 1910 value.set(1, simplifyColorspace(second)); 1911 } 1912 } 1913 } 1914 } 1915 additional.put(PdfName.COLORSPACE, newValue); 1916 } 1917 1918 /** 1919 * Gets a PDF Name from an array or returns the object that was passed. 1920 */ 1921 private PdfObject simplifyColorspace(final PdfArray obj) { 1922 if (obj == null) 1923 return obj; 1924 PdfName first = obj.getAsName(0); 1925 if (PdfName.CALGRAY.equals(first)) 1926 return PdfName.DEVICEGRAY; 1927 else if (PdfName.CALRGB.equals(first)) 1928 return PdfName.DEVICERGB; 1929 else 1930 return obj; 1931 } 1932 1933 /** Is this image a mask? */ 1934 protected boolean mask = false; 1935 1936 /** The image that serves as a mask for this image. */ 1937 protected Image imageMask; 1938 1939 /** Holds value of property smask. */ 1940 private boolean smask; 1941 1942 /** 1943 * Returns <CODE>true</CODE> if this <CODE>Image</CODE> is a mask. 1944 * 1945 * @return <CODE>true</CODE> if this <CODE>Image</CODE> is a mask 1946 */ 1947 public boolean isMask() { 1948 return mask; 1949 } 1950 1951 /** 1952 * Make this <CODE>Image</CODE> a mask. 1953 * 1954 * @throws DocumentException 1955 * if this <CODE>Image</CODE> can not be a mask 1956 */ 1957 public void makeMask() throws DocumentException { 1958 if (!isMaskCandidate()) 1959 throw new DocumentException(MessageLocalization.getComposedMessage("this.image.can.not.be.an.image.mask")); 1960 mask = true; 1961 } 1962 1963 /** 1964 * Returns <CODE>true</CODE> if this <CODE>Image</CODE> has the 1965 * requisites to be a mask. 1966 * 1967 * @return <CODE>true</CODE> if this <CODE>Image</CODE> can be a mask 1968 */ 1969 public boolean isMaskCandidate() { 1970 if (type == IMGRAW) { 1971 if (bpc > 0xff) 1972 return true; 1973 } 1974 return colorspace == 1; 1975 } 1976 1977 /** 1978 * Gets the explicit masking. 1979 * 1980 * @return the explicit masking 1981 */ 1982 public Image getImageMask() { 1983 return imageMask; 1984 } 1985 1986 /** 1987 * Sets the explicit masking. 1988 * 1989 * @param mask 1990 * the mask to be applied 1991 * @throws DocumentException 1992 * on error 1993 */ 1994 public void setImageMask(final Image mask) throws DocumentException { 1995 if (this.mask) 1996 throw new DocumentException(MessageLocalization.getComposedMessage("an.image.mask.cannot.contain.another.image.mask")); 1997 if (!mask.mask) 1998 throw new DocumentException(MessageLocalization.getComposedMessage("the.image.mask.is.not.a.mask.did.you.do.makemask")); 1999 imageMask = mask; 2000 smask = mask.bpc > 1 && mask.bpc <= 8; 2001 } 2002 2003 /** 2004 * Getter for property smask. 2005 * 2006 * @return Value of property smask. 2007 * 2008 */ 2009 public boolean isSmask() { 2010 return this.smask; 2011 } 2012 2013 /** 2014 * Setter for property smask. 2015 * 2016 * @param smask 2017 * New value of property smask. 2018 */ 2019 public void setSmask(final boolean smask) { 2020 this.smask = smask; 2021 } 2022 2023 /** this is the transparency information of the raw image */ 2024 protected int transparency[]; 2025 2026 /** 2027 * Returns the transparency. 2028 * 2029 * @return the transparency values 2030 */ 2031 2032 public int[] getTransparency() { 2033 return transparency; 2034 } 2035 2036 /** 2037 * Sets the transparency values 2038 * 2039 * @param transparency 2040 * the transparency values 2041 */ 2042 public void setTransparency(final int transparency[]) { 2043 this.transparency = transparency; 2044 } 2045 2046 2047 /** 2048 * Returns the compression level used for images written as a compressed stream. 2049 * @return the compression level (0 = best speed, 9 = best compression, -1 is default) 2050 * @since 2.1.3 2051 */ 2052 public int getCompressionLevel() { 2053 return compressionLevel; 2054 } 2055 2056 /** 2057 * Sets the compression level to be used if the image is written as a compressed stream. 2058 * @param compressionLevel a value between 0 (best speed) and 9 (best compression) 2059 * @since 2.1.3 2060 */ 2061 public void setCompressionLevel(final int compressionLevel) { 2062 if (compressionLevel < PdfStream.NO_COMPRESSION || compressionLevel > PdfStream.BEST_COMPRESSION) 2063 this.compressionLevel = PdfStream.DEFAULT_COMPRESSION; 2064 else 2065 this.compressionLevel = compressionLevel; 2066 } 2067}