001/*
002 * $Id: TiffImage.java 4784 2011-03-15 08:33:00Z blowagie $
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.pdf.codec;
045import com.itextpdf.text.pdf.ICC_Profile;
046import java.io.ByteArrayOutputStream;
047import java.io.IOException;
048import java.util.zip.DataFormatException;
049import java.util.zip.DeflaterOutputStream;
050import java.util.zip.Inflater;
051import com.itextpdf.text.error_messages.MessageLocalization;
052
053import com.itextpdf.text.ExceptionConverter;
054import com.itextpdf.text.Image;
055import com.itextpdf.text.ImgRaw;
056import com.itextpdf.text.Jpeg;
057import com.itextpdf.text.pdf.PdfArray;
058import com.itextpdf.text.pdf.PdfDictionary;
059import com.itextpdf.text.pdf.PdfName;
060import com.itextpdf.text.pdf.PdfNumber;
061import com.itextpdf.text.pdf.PdfString;
062import com.itextpdf.text.pdf.RandomAccessFileOrArray;
063
064/** Reads TIFF images
065 * @author Paulo Soares
066 */
067public class TiffImage {
068    
069    /** Gets the number of pages the TIFF document has.
070     * @param s the file source
071     * @return the number of pages
072     */    
073    public static int getNumberOfPages(RandomAccessFileOrArray s) {
074        try {
075            return TIFFDirectory.getNumDirectories(s);
076        }
077        catch (Exception e) {
078            throw new ExceptionConverter(e);
079        }
080    }
081
082    static int getDpi(TIFFField fd, int resolutionUnit) {
083        if (fd == null)
084            return 0;
085        long res[] = fd.getAsRational(0);
086        float frac = (float)res[0] / (float)res[1];
087        int dpi = 0;
088        switch (resolutionUnit) {
089            case TIFFConstants.RESUNIT_INCH:
090            case TIFFConstants.RESUNIT_NONE:
091                dpi = (int)(frac + 0.5);
092                break;
093            case TIFFConstants.RESUNIT_CENTIMETER:
094                dpi = (int)(frac * 2.54 + 0.5);
095                break;
096        }
097        return dpi;
098    }
099    
100    /** Reads a page from a TIFF image. Direct mode is not used.
101     * @param s the file source
102     * @param page the page to get. The first page is 1
103     * @return the <CODE>Image</CODE>
104     */    
105    public static Image getTiffImage(RandomAccessFileOrArray s, int page) {
106        return getTiffImage(s, page, false);
107    }
108    
109    /** Reads a page from a TIFF image.
110     * @param s the file source
111     * @param page the page to get. The first page is 1
112     * @param direct for single strip, CCITT images, generate the image
113     * by direct byte copying. It's faster but may not work
114     * every time
115     * @return the <CODE>Image</CODE>
116     */    
117    public static Image getTiffImage(RandomAccessFileOrArray s, int page, boolean direct) {
118        if (page < 1)
119            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.page.number.must.be.gt.eq.1"));
120        try {
121            TIFFDirectory dir = new TIFFDirectory(s, page - 1);
122            if (dir.isTagPresent(TIFFConstants.TIFFTAG_TILEWIDTH))
123                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("tiles.are.not.supported"));
124            int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION);
125            switch (compression) {
126                case TIFFConstants.COMPRESSION_CCITTRLEW:
127                case TIFFConstants.COMPRESSION_CCITTRLE:
128                case TIFFConstants.COMPRESSION_CCITTFAX3:
129                case TIFFConstants.COMPRESSION_CCITTFAX4:
130                    break;
131                default:
132                    return getTiffImageColor(dir, s);
133            }
134            float rotation = 0;
135            if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) {
136                int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION);
137                if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT)
138                    rotation = (float)Math.PI;
139                else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT)
140                    rotation = (float)(Math.PI / 2.0);
141                else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT)
142                    rotation = -(float)(Math.PI / 2.0);
143            }
144
145            Image img = null;
146            long tiffT4Options = 0;
147            long tiffT6Options = 0;
148            int fillOrder = 1;
149            int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH);
150            int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH);
151            int dpiX = 0;
152            int dpiY = 0;
153            float XYRatio = 0;
154            int resolutionUnit = TIFFConstants.RESUNIT_INCH;
155            if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT))
156                resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT);
157            dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit);
158            dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit);
159            if (resolutionUnit == TIFFConstants.RESUNIT_NONE) {
160                if (dpiY != 0)
161                    XYRatio = (float)dpiX / (float)dpiY;
162                dpiX = 0;
163                dpiY = 0;
164            }
165            int rowsStrip = h;
166            if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP))
167                rowsStrip = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP);
168            if (rowsStrip <= 0 || rowsStrip > h)
169                rowsStrip = h;
170            long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS);
171            long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS);
172            if ((size == null || (size.length == 1 && (size[0] == 0 || size[0] + offset[0] > s.length()))) && h == rowsStrip) { // some TIFF producers are really lousy, so...
173                size = new long[]{s.length() - (int)offset[0]};
174            }
175            boolean reverse = false;
176            TIFFField fillOrderField =  dir.getField(TIFFConstants.TIFFTAG_FILLORDER);
177            if (fillOrderField != null)
178                fillOrder = fillOrderField.getAsInt(0);
179            reverse = (fillOrder == TIFFConstants.FILLORDER_LSB2MSB);
180            int params = 0;
181            if (dir.isTagPresent(TIFFConstants.TIFFTAG_PHOTOMETRIC)) {
182                long photo = dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC);
183                if (photo == TIFFConstants.PHOTOMETRIC_MINISBLACK)
184                    params |= Image.CCITT_BLACKIS1;
185            }
186            int imagecomp = 0;
187            switch (compression) {
188                case TIFFConstants.COMPRESSION_CCITTRLEW:
189                case TIFFConstants.COMPRESSION_CCITTRLE:
190                    imagecomp = Image.CCITTG3_1D;
191                    params |= Image.CCITT_ENCODEDBYTEALIGN | Image.CCITT_ENDOFBLOCK;
192                    break;
193                case TIFFConstants.COMPRESSION_CCITTFAX3:
194                    imagecomp = Image.CCITTG3_1D;
195                    params |= Image.CCITT_ENDOFLINE | Image.CCITT_ENDOFBLOCK;
196                    TIFFField t4OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP3OPTIONS);
197                    if (t4OptionsField != null) {
198                        tiffT4Options = t4OptionsField.getAsLong(0);
199                    if ((tiffT4Options & TIFFConstants.GROUP3OPT_2DENCODING) != 0)
200                        imagecomp = Image.CCITTG3_2D;
201                    if ((tiffT4Options & TIFFConstants.GROUP3OPT_FILLBITS) != 0)
202                        params |= Image.CCITT_ENCODEDBYTEALIGN;
203                    }
204                    break;
205                case TIFFConstants.COMPRESSION_CCITTFAX4:
206                    imagecomp = Image.CCITTG4;
207                    TIFFField t6OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP4OPTIONS);
208                    if (t6OptionsField != null)
209                        tiffT6Options = t6OptionsField.getAsLong(0);
210                    break;
211            }
212            if (direct && rowsStrip == h) { //single strip, direct
213                byte im[] = new byte[(int)size[0]];
214                s.seek(offset[0]);
215                s.readFully(im);
216                img = Image.getInstance(w, h, false, imagecomp, params, im);
217                img.setInverted(true);
218            }
219            else {
220                int rowsLeft = h;
221                CCITTG4Encoder g4 = new CCITTG4Encoder(w);
222                for (int k = 0; k < offset.length; ++k) {
223                    byte im[] = new byte[(int)size[k]];
224                    s.seek(offset[k]);
225                    s.readFully(im);
226                    int height = Math.min(rowsStrip, rowsLeft);
227                    TIFFFaxDecoder decoder = new TIFFFaxDecoder(fillOrder, w, height);
228                    byte outBuf[] = new byte[(w + 7) / 8 * height];
229                    switch (compression) {
230                        case TIFFConstants.COMPRESSION_CCITTRLEW:
231                        case TIFFConstants.COMPRESSION_CCITTRLE:
232                            decoder.decode1D(outBuf, im, 0, height);
233                            g4.fax4Encode(outBuf,height);
234                            break;
235                        case TIFFConstants.COMPRESSION_CCITTFAX3:
236                            try {
237                                decoder.decode2D(outBuf, im, 0, height, tiffT4Options);
238                            }
239                            catch (RuntimeException e) {
240                                // let's flip the fill bits and try again...
241                                tiffT4Options ^= TIFFConstants.GROUP3OPT_FILLBITS;
242                                try {
243                                    decoder.decode2D(outBuf, im, 0, height, tiffT4Options);
244                                }
245                                catch (RuntimeException e2) {
246                                    throw e;
247                                }
248                            }
249                            g4.fax4Encode(outBuf, height);
250                            break;
251                        case TIFFConstants.COMPRESSION_CCITTFAX4:
252                            decoder.decodeT6(outBuf, im, 0, height, tiffT6Options);
253                            g4.fax4Encode(outBuf, height);
254                            break;
255                    }
256                    rowsLeft -= rowsStrip;
257                }
258                byte g4pic[] = g4.close();
259                img = Image.getInstance(w, h, false, Image.CCITTG4, params & Image.CCITT_BLACKIS1, g4pic);
260            }
261            img.setDpi(dpiX, dpiY);
262            img.setXYRatio(XYRatio);
263            if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) {
264                try {
265                    TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE);
266                    ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes());
267                    if (icc_prof.getNumComponents() == 1)
268                        img.tagICC(icc_prof);
269                }
270                catch (RuntimeException e) {
271                    //empty
272                }
273            }
274            img.setOriginalType(Image.ORIGINAL_TIFF);
275            if (rotation != 0)
276                img.setInitialRotation(rotation);
277            return img;
278        }
279        catch (Exception e) {
280            throw new ExceptionConverter(e);
281        }
282    }
283    
284    protected static Image getTiffImageColor(TIFFDirectory dir, RandomAccessFileOrArray s) {
285        try {
286            int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION);
287            int predictor = 1;
288            TIFFLZWDecoder lzwDecoder = null;
289            switch (compression) {
290                case TIFFConstants.COMPRESSION_NONE:
291                case TIFFConstants.COMPRESSION_LZW:
292                case TIFFConstants.COMPRESSION_PACKBITS:
293                case TIFFConstants.COMPRESSION_DEFLATE:
294                case TIFFConstants.COMPRESSION_ADOBE_DEFLATE:
295                case TIFFConstants.COMPRESSION_OJPEG:
296                case TIFFConstants.COMPRESSION_JPEG:
297                    break;
298                default:
299                    throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.compression.1.is.not.supported", compression));
300            }
301            int photometric = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC);
302            switch (photometric) {
303                case TIFFConstants.PHOTOMETRIC_MINISWHITE:
304                case TIFFConstants.PHOTOMETRIC_MINISBLACK:
305                case TIFFConstants.PHOTOMETRIC_RGB:
306                case TIFFConstants.PHOTOMETRIC_SEPARATED:
307                case TIFFConstants.PHOTOMETRIC_PALETTE:
308                    break;
309                default:
310                    if (compression != TIFFConstants.COMPRESSION_OJPEG && compression != TIFFConstants.COMPRESSION_JPEG)
311                        throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.photometric.1.is.not.supported", photometric));
312            }
313            float rotation = 0;
314            if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) {
315                int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION);
316                if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT)
317                    rotation = (float)Math.PI;
318                else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT)
319                    rotation = (float)(Math.PI / 2.0);
320                else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT)
321                    rotation = -(float)(Math.PI / 2.0);
322            }
323            if (dir.isTagPresent(TIFFConstants.TIFFTAG_PLANARCONFIG)
324                && dir.getFieldAsLong(TIFFConstants.TIFFTAG_PLANARCONFIG) == TIFFConstants.PLANARCONFIG_SEPARATE)
325                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("planar.images.are.not.supported"));
326            int extraSamples = 0;
327            if (dir.isTagPresent(TIFFConstants.TIFFTAG_EXTRASAMPLES))
328                extraSamples = 1;
329            int samplePerPixel = 1;
330            if (dir.isTagPresent(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL)) // 1,3,4
331                samplePerPixel = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL);
332            int bitsPerSample = 1;
333            if (dir.isTagPresent(TIFFConstants.TIFFTAG_BITSPERSAMPLE))
334                bitsPerSample = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_BITSPERSAMPLE);
335            switch (bitsPerSample) {
336                case 1:
337                case 2:
338                case 4:
339                case 8:
340                    break;
341                default:
342                    throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bits.per.sample.1.is.not.supported", bitsPerSample));
343            }
344            Image img = null;
345
346            int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH);
347            int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH);
348            int dpiX = 0;
349            int dpiY = 0;
350            int resolutionUnit = TIFFConstants.RESUNIT_INCH;
351            if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT))
352                resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT);
353            dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit);
354            dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit);
355            int fillOrder = 1;
356            boolean reverse = false;
357            TIFFField fillOrderField =  dir.getField(TIFFConstants.TIFFTAG_FILLORDER);
358            if (fillOrderField != null)
359                fillOrder = fillOrderField.getAsInt(0);
360            reverse = (fillOrder == TIFFConstants.FILLORDER_LSB2MSB);
361            int rowsStrip = h;
362            if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP)) //another hack for broken tiffs
363                rowsStrip = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP);
364            if (rowsStrip <= 0 || rowsStrip > h)
365                rowsStrip = h;
366            long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS);
367            long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS);
368            if ((size == null || (size.length == 1 && (size[0] == 0 || size[0] + offset[0] > s.length()))) && h == rowsStrip) { // some TIFF producers are really lousy, so...
369                size = new long[]{s.length() - (int)offset[0]};
370            }
371            if (compression == TIFFConstants.COMPRESSION_LZW) {
372                TIFFField predictorField = dir.getField(TIFFConstants.TIFFTAG_PREDICTOR);
373                if (predictorField != null) {
374                    predictor = predictorField.getAsInt(0);
375                    if (predictor != 1 && predictor != 2) {
376                        throw new RuntimeException(MessageLocalization.getComposedMessage("illegal.value.for.predictor.in.tiff.file"));
377                    }
378                    if (predictor == 2 && bitsPerSample != 8) {
379                        throw new RuntimeException(MessageLocalization.getComposedMessage("1.bit.samples.are.not.supported.for.horizontal.differencing.predictor", bitsPerSample));
380                    }
381                }
382                lzwDecoder = new TIFFLZWDecoder(w, predictor, 
383                                                samplePerPixel);
384            }
385            int rowsLeft = h;
386            ByteArrayOutputStream stream = null;
387            ByteArrayOutputStream mstream = null;
388            DeflaterOutputStream zip = null;
389            DeflaterOutputStream mzip = null;
390            if (extraSamples > 0) {
391                mstream = new ByteArrayOutputStream();
392                mzip = new DeflaterOutputStream(mstream);
393            }
394
395            CCITTG4Encoder g4 = null;
396            if (bitsPerSample == 1 && samplePerPixel == 1 && photometric != TIFFConstants.PHOTOMETRIC_PALETTE) {
397                g4 = new CCITTG4Encoder(w);
398            }
399            else {
400                stream = new ByteArrayOutputStream();
401                if (compression != TIFFConstants.COMPRESSION_OJPEG && compression != TIFFConstants.COMPRESSION_JPEG)
402                    zip = new DeflaterOutputStream(stream);
403            }
404            if (compression == TIFFConstants.COMPRESSION_OJPEG) {
405                
406                // Assume that the TIFFTAG_JPEGIFBYTECOUNT tag is optional, since it's obsolete and 
407                // is often missing
408
409                if ((!dir.isTagPresent(TIFFConstants.TIFFTAG_JPEGIFOFFSET))) {
410                    throw new IOException(MessageLocalization.getComposedMessage("missing.tag.s.for.ojpeg.compression"));
411                }
412                int jpegOffset = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_JPEGIFOFFSET);
413                int jpegLength = s.length() - jpegOffset;
414
415                if (dir.isTagPresent(TIFFConstants.TIFFTAG_JPEGIFBYTECOUNT)) {
416                    jpegLength = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_JPEGIFBYTECOUNT) +
417                        (int)size[0];
418                }
419                
420                byte[] jpeg = new byte[Math.min(jpegLength, s.length() - jpegOffset)];
421
422                int posFilePointer = s.getFilePointer();
423                posFilePointer += jpegOffset;
424                s.seek(posFilePointer);
425                s.readFully(jpeg);
426                img = new Jpeg(jpeg);
427            } 
428            else if (compression == TIFFConstants.COMPRESSION_JPEG) {
429                if (size.length > 1)
430                    throw new IOException(MessageLocalization.getComposedMessage("compression.jpeg.is.only.supported.with.a.single.strip.this.image.has.1.strips", size.length));
431                byte[] jpeg = new byte[(int)size[0]];
432                s.seek(offset[0]);
433                s.readFully(jpeg);
434                img = new Jpeg(jpeg);
435            } 
436            else {
437                for (int k = 0; k < offset.length; ++k) {
438                    byte im[] = new byte[(int)size[k]];
439                    s.seek(offset[k]);
440                    s.readFully(im);
441                    int height = Math.min(rowsStrip, rowsLeft);
442                    byte outBuf[] = null;
443                    if (compression != TIFFConstants.COMPRESSION_NONE)
444                        outBuf = new byte[(w * bitsPerSample * samplePerPixel + 7) / 8 * height];
445                    if (reverse)
446                        TIFFFaxDecoder.reverseBits(im);
447                    switch (compression) {
448                        case TIFFConstants.COMPRESSION_DEFLATE:
449                        case TIFFConstants.COMPRESSION_ADOBE_DEFLATE:
450                            inflate(im, outBuf);
451                            break;
452                        case TIFFConstants.COMPRESSION_NONE:
453                            outBuf = im;
454                            break;
455                        case TIFFConstants.COMPRESSION_PACKBITS:
456                            decodePackbits(im,  outBuf);
457                            break;
458                        case TIFFConstants.COMPRESSION_LZW:
459                            lzwDecoder.decode(im, outBuf, height);
460                            break;
461                    }
462                    if (bitsPerSample == 1 && samplePerPixel == 1 && photometric != TIFFConstants.PHOTOMETRIC_PALETTE) {
463                        g4.fax4Encode(outBuf, height);
464                    }
465                    else {
466                        if (extraSamples > 0)
467                            ProcessExtraSamples(zip, mzip, outBuf, samplePerPixel, bitsPerSample, w, height);
468                        else
469                            zip.write(outBuf);
470                    }
471                    rowsLeft -= rowsStrip;
472                }
473                if (bitsPerSample == 1 && samplePerPixel == 1 && photometric != TIFFConstants.PHOTOMETRIC_PALETTE) {
474                    img = Image.getInstance(w, h, false, Image.CCITTG4, 
475                        photometric == TIFFConstants.PHOTOMETRIC_MINISBLACK ? Image.CCITT_BLACKIS1 : 0, g4.close());
476                }
477                else {
478                    zip.close();
479                    img = new ImgRaw(w, h, samplePerPixel - extraSamples, bitsPerSample, stream.toByteArray());
480                    img.setDeflated(true);
481                }
482            }
483            img.setDpi(dpiX, dpiY);
484            if (compression != TIFFConstants.COMPRESSION_OJPEG && compression != TIFFConstants.COMPRESSION_JPEG) {
485                if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) {
486                    try {
487                        TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE);
488                        ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes());
489                        if (samplePerPixel - extraSamples == icc_prof.getNumComponents())
490                            img.tagICC(icc_prof);
491                    }
492                    catch (RuntimeException e) {
493                        //empty
494                    }
495                }
496                if (dir.isTagPresent(TIFFConstants.TIFFTAG_COLORMAP)) {
497                    TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_COLORMAP);
498                    char rgb[] = fd.getAsChars();
499                    byte palette[] = new byte[rgb.length];
500                    int gColor = rgb.length / 3;
501                    int bColor = gColor * 2;
502                    for (int k = 0; k < gColor; ++k) {
503                        palette[k * 3] = (byte)(rgb[k] >>> 8);
504                        palette[k * 3 + 1] = (byte)(rgb[k + gColor] >>> 8);
505                        palette[k * 3 + 2] = (byte)(rgb[k + bColor] >>> 8);
506                    }
507                    PdfArray indexed = new PdfArray();
508                    indexed.add(PdfName.INDEXED);
509                    indexed.add(PdfName.DEVICERGB);
510                    indexed.add(new PdfNumber(gColor - 1));
511                    indexed.add(new PdfString(palette));
512                    PdfDictionary additional = new PdfDictionary();
513                    additional.put(PdfName.COLORSPACE, indexed);
514                    img.setAdditional(additional);
515                }
516                img.setOriginalType(Image.ORIGINAL_TIFF);
517            }
518            if (photometric == TIFFConstants.PHOTOMETRIC_MINISWHITE)
519                img.setInverted(true);
520            if (rotation != 0)
521                img.setInitialRotation(rotation);
522            if (extraSamples > 0) {
523                mzip.close();
524                Image mimg = Image.getInstance(w, h, 1, bitsPerSample, mstream.toByteArray());
525                mimg.makeMask();
526                mimg.setDeflated(true);
527                img.setImageMask(mimg);
528            }
529            return img;
530        }
531        catch (Exception e) {
532            throw new ExceptionConverter(e);
533        }
534    }
535
536    static Image ProcessExtraSamples(DeflaterOutputStream zip, DeflaterOutputStream mzip, byte[] outBuf, int samplePerPixel, int bitsPerSample, int width, int height) throws IOException {
537        if (bitsPerSample == 8) {
538            byte[] mask = new byte[width * height];
539            int mptr = 0;
540            int optr = 0;
541            int total = width * height * samplePerPixel;
542            for (int k = 0; k < total; k += samplePerPixel) {
543                for (int s = 0; s < samplePerPixel - 1; ++s) {
544                    outBuf[optr++] = outBuf[k + s];
545                }
546                mask[mptr++] = outBuf[k + samplePerPixel - 1];
547            }
548            zip.write(outBuf, 0, optr);
549            mzip.write(mask, 0, mptr);
550        }
551        else
552            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("extra.samples.are.not.supported"));
553        return null;
554    }
555
556    static long[] getArrayLongShort(TIFFDirectory dir, int tag) {
557        TIFFField field = dir.getField(tag);
558        if (field == null)
559            return null;
560        long offset[];
561        if (field.getType() == TIFFField.TIFF_LONG)
562            offset = field.getAsLongs();
563        else { // must be short
564            char temp[] = field.getAsChars();
565            offset = new long[temp.length];
566            for (int k = 0; k < temp.length; ++k)
567                offset[k] = temp[k];
568        }
569        return offset;
570    }
571    
572    // Uncompress packbits compressed image data.
573    public static void decodePackbits(byte data[], byte[] dst) {
574        int srcCount = 0, dstCount = 0;
575        byte repeat, b;
576        
577        try {
578            while (dstCount < dst.length) {
579                b = data[srcCount++];
580                if (b >= 0 && b <= 127) {
581                    // literal run packet
582                    for (int i=0; i<(b + 1); i++) {
583                        dst[dstCount++] = data[srcCount++];
584                    }
585
586                } else if (b <= -1 && b >= -127) {
587                    // 2 byte encoded run packet
588                    repeat = data[srcCount++];
589                    for (int i=0; i<(-b + 1); i++) {
590                        dst[dstCount++] = repeat;
591                    }
592                } else {
593                    // no-op packet. Do nothing
594                    srcCount++;
595                }
596            }
597        }
598        catch (Exception e) {
599            // do nothing
600        }
601    }
602
603    public static void inflate(byte[] deflated, byte[] inflated) {
604        Inflater inflater = new Inflater();
605        inflater.setInput(deflated);
606        try {
607            inflater.inflate(inflated);
608        }
609        catch(DataFormatException dfe) {
610            throw new ExceptionConverter(dfe);
611        }
612    }
613
614}