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