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