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