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