001/*
002 * $Id: BmpImage.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 */
044
045/*
046 * This code was originally released in 2001 by SUN (see class
047 * com.sun.media.imageioimpl.plugins.bmp.BMPImageReader.java)
048 * using the BSD license in a specific wording. In a mail dating from
049 * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission
050 * to use the code under the following version of the BSD license:
051 *
052 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
053 *
054 * Redistribution and use in source and binary forms, with or without
055 * modification, are permitted provided that the following conditions
056 * are met:
057 *
058 * - Redistribution of source code must retain the above copyright
059 *   notice, this  list of conditions and the following disclaimer.
060 *
061 * - Redistribution in binary form must reproduce the above copyright
062 *   notice, this list of conditions and the following disclaimer in
063 *   the documentation and/or other materials provided with the
064 *   distribution.
065 *
066 * Neither the name of Sun Microsystems, Inc. or the names of
067 * contributors may be used to endorse or promote products derived
068 * from this software without specific prior written permission.
069 *
070 * This software is provided "AS IS," without a warranty of any
071 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
072 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
073 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
074 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
075 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
076 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
077 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
078 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
079 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
080 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
081 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
082 * POSSIBILITY OF SUCH DAMAGES.
083 *
084 * You acknowledge that this software is not designed or intended for
085 * use in the design, construction, operation or maintenance of any
086 * nuclear facility.
087 */
088package com.itextpdf.text.pdf.codec;
089
090import java.io.BufferedInputStream;
091import java.io.ByteArrayInputStream;
092import java.io.IOException;
093import java.io.InputStream;
094import java.net.URL;
095import java.util.HashMap;
096
097import com.itextpdf.text.BadElementException;
098import com.itextpdf.text.ExceptionConverter;
099import com.itextpdf.text.Image;
100import com.itextpdf.text.ImgRaw;
101import com.itextpdf.text.Utilities;
102import com.itextpdf.text.error_messages.MessageLocalization;
103import com.itextpdf.text.pdf.PdfArray;
104import com.itextpdf.text.pdf.PdfDictionary;
105import com.itextpdf.text.pdf.PdfName;
106import com.itextpdf.text.pdf.PdfNumber;
107import com.itextpdf.text.pdf.PdfString;
108
109/** Reads a BMP image. All types of BMP can be read.
110 * <p>
111 * It is based in the JAI codec.
112 *
113 * @author  Paulo Soares
114 */
115public class BmpImage {
116
117    // BMP variables
118    private InputStream inputStream;
119    private long bitmapFileSize;
120    private long bitmapOffset;
121    private long compression;
122    private long imageSize;
123    private byte palette[];
124    private int imageType;
125    private int numBands;
126    private boolean isBottomUp;
127    private int bitsPerPixel;
128    private int redMask, greenMask, blueMask, alphaMask;
129    public HashMap<String, Object> properties = new HashMap<String, Object>();
130    private long xPelsPerMeter;
131    private long yPelsPerMeter;
132    // BMP Image types
133    private static final int VERSION_2_1_BIT = 0;
134    private static final int VERSION_2_4_BIT = 1;
135    private static final int VERSION_2_8_BIT = 2;
136    private static final int VERSION_2_24_BIT = 3;
137
138    private static final int VERSION_3_1_BIT = 4;
139    private static final int VERSION_3_4_BIT = 5;
140    private static final int VERSION_3_8_BIT = 6;
141    private static final int VERSION_3_24_BIT = 7;
142
143    private static final int VERSION_3_NT_16_BIT = 8;
144    private static final int VERSION_3_NT_32_BIT = 9;
145
146    private static final int VERSION_4_1_BIT = 10;
147    private static final int VERSION_4_4_BIT = 11;
148    private static final int VERSION_4_8_BIT = 12;
149    private static final int VERSION_4_16_BIT = 13;
150    private static final int VERSION_4_24_BIT = 14;
151    private static final int VERSION_4_32_BIT = 15;
152
153    // Color space types
154    private static final int LCS_CALIBRATED_RGB = 0;
155    private static final int LCS_sRGB = 1;
156    private static final int LCS_CMYK = 2;
157
158    // Compression Types
159    private static final int BI_RGB = 0;
160    private static final int BI_RLE8 = 1;
161    private static final int BI_RLE4 = 2;
162    private static final int BI_BITFIELDS = 3;
163
164    int width;
165    int height;
166
167    BmpImage(InputStream is, boolean noHeader, int size) throws IOException {
168        bitmapFileSize = size;
169        bitmapOffset = 0;
170        process(is, noHeader);
171    }
172
173    /** Reads a BMP from an url.
174     * @param url the url
175     * @throws IOException on error
176     * @return the image
177     */
178    public static Image getImage(URL url) throws IOException {
179        InputStream is = null;
180        try {
181            is = url.openStream();
182            Image img = getImage(is);
183            img.setUrl(url);
184            return img;
185        }
186        finally {
187            if (is != null) {
188                is.close();
189            }
190        }
191    }
192
193    /** Reads a BMP from a stream. The stream is not closed.
194     * @param is the stream
195     * @throws IOException on error
196     * @return the image
197     */
198    public static Image getImage(InputStream is) throws IOException {
199        return getImage(is, false, 0);
200    }
201
202    /** Reads a BMP from a stream. The stream is not closed.
203     * The BMP may not have a header and be considered as a plain DIB.
204     * @param is the stream
205     * @param noHeader true to process a plain DIB
206     * @param size the size of the DIB. Not used for a BMP
207     * @throws IOException on error
208     * @return the image
209     */
210    public static Image getImage(InputStream is, boolean noHeader, int size) throws IOException {
211        BmpImage bmp = new BmpImage(is, noHeader, size);
212        try {
213            Image img = bmp.getImage();
214            img.setDpi((int)(bmp.xPelsPerMeter * 0.0254d + 0.5d), (int)(bmp.yPelsPerMeter * 0.0254d + 0.5d));
215            img.setOriginalType(Image.ORIGINAL_BMP);
216            return img;
217        }
218        catch (BadElementException be) {
219            throw new ExceptionConverter(be);
220        }
221    }
222
223    /** Reads a BMP from a file.
224     * @param file the file
225     * @throws IOException on error
226     * @return the image
227     */
228    public static Image getImage(String file) throws IOException {
229        return getImage(Utilities.toURL(file));
230    }
231
232    /** Reads a BMP from a byte array.
233     * @param data the byte array
234     * @throws IOException on error
235     * @return the image
236     */
237    public static Image getImage(byte data[]) throws IOException {
238        ByteArrayInputStream is = new ByteArrayInputStream(data);
239        Image img = getImage(is);
240        img.setOriginalData(data);
241        return img;
242    }
243
244
245    protected void process(InputStream stream, boolean noHeader) throws IOException {
246        if (noHeader || stream instanceof BufferedInputStream) {
247            inputStream = stream;
248        } else {
249            inputStream = new BufferedInputStream(stream);
250        }
251        if (!noHeader) {
252            // Start File Header
253            if (!(readUnsignedByte(inputStream) == 'B' &&
254            readUnsignedByte(inputStream) == 'M')) {
255                throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.magic.value.for.bmp.file"));
256            }
257
258            // Read file size
259            bitmapFileSize = readDWord(inputStream);
260
261            // Read the two reserved fields
262            readWord(inputStream);
263            readWord(inputStream);
264
265            // Offset to the bitmap from the beginning
266            bitmapOffset = readDWord(inputStream);
267
268            // End File Header
269        }
270        // Start BitmapCoreHeader
271        long size = readDWord(inputStream);
272
273        if (size == 12) {
274            width = readWord(inputStream);
275            height = readWord(inputStream);
276        } else {
277            width = readLong(inputStream);
278            height = readLong(inputStream);
279        }
280
281        int planes = readWord(inputStream);
282        bitsPerPixel = readWord(inputStream);
283
284        properties.put("color_planes", Integer.valueOf(planes));
285        properties.put("bits_per_pixel", Integer.valueOf(bitsPerPixel));
286
287        // As BMP always has 3 rgb bands, except for Version 5,
288        // which is bgra
289        numBands = 3;
290        if (bitmapOffset == 0)
291            bitmapOffset = size;
292        if (size == 12) {
293            // Windows 2.x and OS/2 1.x
294            properties.put("bmp_version", "BMP v. 2.x");
295
296            // Classify the image type
297            if (bitsPerPixel == 1) {
298                imageType = VERSION_2_1_BIT;
299            } else if (bitsPerPixel == 4) {
300                imageType = VERSION_2_4_BIT;
301            } else if (bitsPerPixel == 8) {
302                imageType = VERSION_2_8_BIT;
303            } else if (bitsPerPixel == 24) {
304                imageType = VERSION_2_24_BIT;
305            }
306
307            // Read in the palette
308            int numberOfEntries = (int)((bitmapOffset-14-size) / 3);
309            int sizeOfPalette = numberOfEntries*3;
310            if (bitmapOffset == size) {
311                switch (imageType) {
312                    case VERSION_2_1_BIT:
313                        sizeOfPalette = 2 * 3;
314                        break;
315                    case VERSION_2_4_BIT:
316                        sizeOfPalette = 16 * 3;
317                        break;
318                    case VERSION_2_8_BIT:
319                        sizeOfPalette = 256 * 3;
320                        break;
321                    case VERSION_2_24_BIT:
322                        sizeOfPalette = 0;
323                        break;
324                }
325                bitmapOffset = size + sizeOfPalette;
326            }
327            readPalette(sizeOfPalette);
328        } else {
329
330            compression = readDWord(inputStream);
331            imageSize = readDWord(inputStream);
332            xPelsPerMeter = readLong(inputStream);
333            yPelsPerMeter = readLong(inputStream);
334            long colorsUsed = readDWord(inputStream);
335            long colorsImportant = readDWord(inputStream);
336
337            switch((int)compression) {
338                case BI_RGB:
339                    properties.put("compression", "BI_RGB");
340                    break;
341
342                case BI_RLE8:
343                    properties.put("compression", "BI_RLE8");
344                    break;
345
346                case BI_RLE4:
347                    properties.put("compression", "BI_RLE4");
348                    break;
349
350                case BI_BITFIELDS:
351                    properties.put("compression", "BI_BITFIELDS");
352                    break;
353            }
354
355            properties.put("x_pixels_per_meter", Long.valueOf(xPelsPerMeter));
356            properties.put("y_pixels_per_meter", Long.valueOf(yPelsPerMeter));
357            properties.put("colors_used", Long.valueOf(colorsUsed));
358            properties.put("colors_important", Long.valueOf(colorsImportant));
359
360            if (size == 40) {
361                // Windows 3.x and Windows NT
362                switch((int)compression) {
363
364                    case BI_RGB:  // No compression
365                    case BI_RLE8:  // 8-bit RLE compression
366                    case BI_RLE4:  // 4-bit RLE compression
367
368                        if (bitsPerPixel == 1) {
369                            imageType = VERSION_3_1_BIT;
370                        } else if (bitsPerPixel == 4) {
371                            imageType = VERSION_3_4_BIT;
372                        } else if (bitsPerPixel == 8) {
373                            imageType = VERSION_3_8_BIT;
374                        } else if (bitsPerPixel == 24) {
375                            imageType = VERSION_3_24_BIT;
376                        } else if (bitsPerPixel == 16) {
377                            imageType = VERSION_3_NT_16_BIT;
378                            redMask = 0x7C00;
379                            greenMask = 0x3E0;
380                            blueMask = 0x1F;
381                            properties.put("red_mask", Integer.valueOf(redMask));
382                            properties.put("green_mask", Integer.valueOf(greenMask));
383                            properties.put("blue_mask", Integer.valueOf(blueMask));
384                        } else if (bitsPerPixel == 32) {
385                            imageType = VERSION_3_NT_32_BIT;
386                            redMask   = 0x00FF0000;
387                            greenMask = 0x0000FF00;
388                            blueMask  = 0x000000FF;
389                            properties.put("red_mask", Integer.valueOf(redMask));
390                            properties.put("green_mask", Integer.valueOf(greenMask));
391                            properties.put("blue_mask", Integer.valueOf(blueMask));
392                        }
393
394                        // Read in the palette
395                        int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
396                        int sizeOfPalette = numberOfEntries*4;
397                        if (bitmapOffset == size) {
398                            switch (imageType) {
399                                case VERSION_3_1_BIT:
400                                    sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
401                                    break;
402                                case VERSION_3_4_BIT:
403                                    sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
404                                    break;
405                                case VERSION_3_8_BIT:
406                                    sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
407                                    break;
408                                default:
409                                    sizeOfPalette = 0;
410                                    break;
411                            }
412                            bitmapOffset = size + sizeOfPalette;
413                        }
414                        readPalette(sizeOfPalette);
415
416                        properties.put("bmp_version", "BMP v. 3.x");
417                        break;
418
419                    case BI_BITFIELDS:
420
421                        if (bitsPerPixel == 16) {
422                            imageType = VERSION_3_NT_16_BIT;
423                        } else if (bitsPerPixel == 32) {
424                            imageType = VERSION_3_NT_32_BIT;
425                        }
426
427                        // BitsField encoding
428                        redMask = (int)readDWord(inputStream);
429                        greenMask = (int)readDWord(inputStream);
430                        blueMask = (int)readDWord(inputStream);
431
432                        properties.put("red_mask", Integer.valueOf(redMask));
433                        properties.put("green_mask", Integer.valueOf(greenMask));
434                        properties.put("blue_mask", Integer.valueOf(blueMask));
435
436                        if (colorsUsed != 0) {
437                            // there is a palette
438                            sizeOfPalette = (int)colorsUsed*4;
439                            readPalette(sizeOfPalette);
440                        }
441
442                        properties.put("bmp_version", "BMP v. 3.x NT");
443                        break;
444
445                    default:
446                        throw new
447                        RuntimeException("Invalid compression specified in BMP file.");
448                }
449            } else if (size == 108) {
450                // Windows 4.x BMP
451
452                properties.put("bmp_version", "BMP v. 4.x");
453
454                // rgb masks, valid only if comp is BI_BITFIELDS
455                redMask = (int)readDWord(inputStream);
456                greenMask = (int)readDWord(inputStream);
457                blueMask = (int)readDWord(inputStream);
458                // Only supported for 32bpp BI_RGB argb
459                alphaMask = (int)readDWord(inputStream);
460                long csType = readDWord(inputStream);
461                int redX = readLong(inputStream);
462                int redY = readLong(inputStream);
463                int redZ = readLong(inputStream);
464                int greenX = readLong(inputStream);
465                int greenY = readLong(inputStream);
466                int greenZ = readLong(inputStream);
467                int blueX = readLong(inputStream);
468                int blueY = readLong(inputStream);
469                int blueZ = readLong(inputStream);
470                long gammaRed = readDWord(inputStream);
471                long gammaGreen = readDWord(inputStream);
472                long gammaBlue = readDWord(inputStream);
473
474                if (bitsPerPixel == 1) {
475                    imageType = VERSION_4_1_BIT;
476                } else if (bitsPerPixel == 4) {
477                    imageType = VERSION_4_4_BIT;
478                } else if (bitsPerPixel == 8) {
479                    imageType = VERSION_4_8_BIT;
480                } else if (bitsPerPixel == 16) {
481                    imageType = VERSION_4_16_BIT;
482                    if ((int)compression == BI_RGB) {
483                        redMask = 0x7C00;
484                        greenMask = 0x3E0;
485                        blueMask = 0x1F;
486                    }
487                } else if (bitsPerPixel == 24) {
488                    imageType = VERSION_4_24_BIT;
489                } else if (bitsPerPixel == 32) {
490                    imageType = VERSION_4_32_BIT;
491                    if ((int)compression == BI_RGB) {
492                        redMask   = 0x00FF0000;
493                        greenMask = 0x0000FF00;
494                        blueMask  = 0x000000FF;
495                    }
496                }
497
498                properties.put("red_mask", Integer.valueOf(redMask));
499                properties.put("green_mask", Integer.valueOf(greenMask));
500                properties.put("blue_mask", Integer.valueOf(blueMask));
501                properties.put("alpha_mask", Integer.valueOf(alphaMask));
502
503                // Read in the palette
504                int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
505                int sizeOfPalette = numberOfEntries*4;
506                if (bitmapOffset == size) {
507                    switch (imageType) {
508                        case VERSION_4_1_BIT:
509                            sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
510                            break;
511                        case VERSION_4_4_BIT:
512                            sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
513                            break;
514                        case VERSION_4_8_BIT:
515                            sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
516                            break;
517                        default:
518                            sizeOfPalette = 0;
519                            break;
520                    }
521                    bitmapOffset = size + sizeOfPalette;
522                }
523                readPalette(sizeOfPalette);
524
525                switch((int)csType) {
526                    case LCS_CALIBRATED_RGB:
527                        // All the new fields are valid only for this case
528                        properties.put("color_space", "LCS_CALIBRATED_RGB");
529                        properties.put("redX", Integer.valueOf(redX));
530                        properties.put("redY", Integer.valueOf(redY));
531                        properties.put("redZ", Integer.valueOf(redZ));
532                        properties.put("greenX", Integer.valueOf(greenX));
533                        properties.put("greenY", Integer.valueOf(greenY));
534                        properties.put("greenZ", Integer.valueOf(greenZ));
535                        properties.put("blueX", Integer.valueOf(blueX));
536                        properties.put("blueY", Integer.valueOf(blueY));
537                        properties.put("blueZ", Integer.valueOf(blueZ));
538                        properties.put("gamma_red", Long.valueOf(gammaRed));
539                        properties.put("gamma_green", Long.valueOf(gammaGreen));
540                        properties.put("gamma_blue", Long.valueOf(gammaBlue));
541
542                        // break;
543                        throw new
544                        RuntimeException("Not implemented yet.");
545
546                    case LCS_sRGB:
547                        // Default Windows color space
548                        properties.put("color_space", "LCS_sRGB");
549                        break;
550
551                    case LCS_CMYK:
552                        properties.put("color_space", "LCS_CMYK");
553                        //                  break;
554                        throw new
555                        RuntimeException("Not implemented yet.");
556                }
557
558            } else {
559                properties.put("bmp_version", "BMP v. 5.x");
560                throw new
561                RuntimeException("BMP version 5 not implemented yet.");
562            }
563        }
564
565        if (height > 0) {
566            // bottom up image
567            isBottomUp = true;
568        } else {
569            // top down image
570            isBottomUp = false;
571            height = Math.abs(height);
572        }
573        // When number of bitsPerPixel is <= 8, we use IndexColorModel.
574        if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
575
576            numBands = 1;
577
578
579            // Create IndexColorModel from the palette.
580            byte r[], g[], b[];
581            int sizep;
582            if (imageType == VERSION_2_1_BIT ||
583            imageType == VERSION_2_4_BIT ||
584            imageType == VERSION_2_8_BIT) {
585
586                sizep = palette.length/3;
587
588                if (sizep > 256) {
589                    sizep = 256;
590                }
591
592                int off;
593                r = new byte[sizep];
594                g = new byte[sizep];
595                b = new byte[sizep];
596                for (int i=0; i<sizep; i++) {
597                    off = 3 * i;
598                    b[i] = palette[off];
599                    g[i] = palette[off+1];
600                    r[i] = palette[off+2];
601                }
602            } else {
603                sizep = palette.length/4;
604
605                if (sizep > 256) {
606                    sizep = 256;
607                }
608
609                int off;
610                r = new byte[sizep];
611                g = new byte[sizep];
612                b = new byte[sizep];
613                for (int i=0; i<sizep; i++) {
614                    off = 4 * i;
615                    b[i] = palette[off];
616                    g[i] = palette[off+1];
617                    r[i] = palette[off+2];
618                }
619            }
620
621        } else if (bitsPerPixel == 16) {
622            numBands = 3;
623        } else if (bitsPerPixel == 32) {
624            numBands = alphaMask == 0 ? 3 : 4;
625
626            // The number of bands in the SampleModel is determined by
627            // the length of the mask array passed in.
628        } else {
629            numBands = 3;
630        }
631    }
632
633    private byte[] getPalette(int group) {
634        if (palette == null)
635            return null;
636        byte np[] = new byte[palette.length / group * 3];
637        int e = palette.length / group;
638        for (int k = 0; k < e; ++k) {
639            int src = k * group;
640            int dest = k * 3;
641            np[dest + 2] = palette[src++];
642            np[dest + 1] = palette[src++];
643            np[dest] = palette[src];
644        }
645        return np;
646    }
647
648    private Image getImage() throws IOException, BadElementException {
649        byte bdata[] = null; // buffer for byte data
650
651        //      if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
652        //          bdata = (byte[])((DataBufferByte)tile.getDataBuffer()).getData();
653        //      else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
654        //          sdata = (short[])((DataBufferUShort)tile.getDataBuffer()).getData();
655        //      else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
656        //          idata = (int[])((DataBufferInt)tile.getDataBuffer()).getData();
657
658        // There should only be one tile.
659        switch(imageType) {
660
661            case VERSION_2_1_BIT:
662                // no compression
663                return read1Bit(3);
664
665            case VERSION_2_4_BIT:
666                // no compression
667                return read4Bit(3);
668
669            case VERSION_2_8_BIT:
670                // no compression
671                return read8Bit(3);
672
673            case VERSION_2_24_BIT:
674                // no compression
675                bdata = new byte[width * height * 3];
676                read24Bit(bdata);
677                return new ImgRaw(width, height, 3, 8, bdata);
678
679            case VERSION_3_1_BIT:
680                // 1-bit images cannot be compressed.
681                return read1Bit(4);
682
683            case VERSION_3_4_BIT:
684                switch((int)compression) {
685                    case BI_RGB:
686                        return read4Bit(4);
687
688                    case BI_RLE4:
689                        return readRLE4();
690
691                    default:
692                        throw new
693                        RuntimeException("Invalid compression specified for BMP file.");
694                }
695
696            case VERSION_3_8_BIT:
697                switch((int)compression) {
698                    case BI_RGB:
699                        return read8Bit(4);
700
701                    case BI_RLE8:
702                        return readRLE8();
703
704                    default:
705                        throw new
706                        RuntimeException("Invalid compression specified for BMP file.");
707                }
708
709            case VERSION_3_24_BIT:
710                // 24-bit images are not compressed
711                bdata = new byte[width * height * 3];
712                read24Bit(bdata);
713                return new ImgRaw(width, height, 3, 8, bdata);
714
715            case VERSION_3_NT_16_BIT:
716                return read1632Bit(false);
717
718            case VERSION_3_NT_32_BIT:
719                return read1632Bit(true);
720
721            case VERSION_4_1_BIT:
722                return read1Bit(4);
723
724            case VERSION_4_4_BIT:
725                switch((int)compression) {
726
727                    case BI_RGB:
728                        return read4Bit(4);
729
730                    case BI_RLE4:
731                        return readRLE4();
732
733                    default:
734                        throw new
735                        RuntimeException("Invalid compression specified for BMP file.");
736                }
737
738            case VERSION_4_8_BIT:
739                switch((int)compression) {
740
741                    case BI_RGB:
742                        return read8Bit(4);
743
744                    case BI_RLE8:
745                        return readRLE8();
746
747                    default:
748                        throw new
749                        RuntimeException("Invalid compression specified for BMP file.");
750                }
751
752            case VERSION_4_16_BIT:
753                return read1632Bit(false);
754
755            case VERSION_4_24_BIT:
756                bdata = new byte[width * height * 3];
757                read24Bit(bdata);
758                return new ImgRaw(width, height, 3, 8, bdata);
759
760            case VERSION_4_32_BIT:
761                return read1632Bit(true);
762        }
763        return null;
764    }
765
766    private Image indexedModel(byte bdata[], int bpc, int paletteEntries) throws BadElementException {
767        Image img = new ImgRaw(width, height, 1, bpc, bdata);
768        PdfArray colorspace = new PdfArray();
769        colorspace.add(PdfName.INDEXED);
770        colorspace.add(PdfName.DEVICERGB);
771        byte np[] = getPalette(paletteEntries);
772        int len = np.length;
773        colorspace.add(new PdfNumber(len / 3 - 1));
774        colorspace.add(new PdfString(np));
775        PdfDictionary ad = new PdfDictionary();
776        ad.put(PdfName.COLORSPACE, colorspace);
777        img.setAdditional(ad);
778        return img;
779    }
780
781    private void readPalette(int sizeOfPalette) throws IOException {
782        if (sizeOfPalette == 0) {
783            return;
784        }
785
786        palette = new byte[sizeOfPalette];
787        int bytesRead = 0;
788        while (bytesRead < sizeOfPalette) {
789            int r = inputStream.read(palette, bytesRead, sizeOfPalette - bytesRead);
790            if (r < 0) {
791                throw new RuntimeException(MessageLocalization.getComposedMessage("incomplete.palette"));
792            }
793            bytesRead += r;
794        }
795        properties.put("palette", palette);
796    }
797
798    // Deal with 1 Bit images using IndexColorModels
799    private Image read1Bit(int paletteEntries) throws IOException, BadElementException {
800        byte bdata[] = new byte[(width + 7) / 8 * height];
801        int padding = 0;
802        int bytesPerScanline = (int)Math.ceil(width/8.0d);
803
804        int remainder = bytesPerScanline % 4;
805        if (remainder != 0) {
806            padding = 4 - remainder;
807        }
808
809        int imSize = (bytesPerScanline + padding) * height;
810
811        // Read till we have the whole image
812        byte values[] = new byte[imSize];
813        int bytesRead = 0;
814        while (bytesRead < imSize) {
815            bytesRead += inputStream.read(values, bytesRead,
816            imSize - bytesRead);
817        }
818
819        if (isBottomUp) {
820
821            // Convert the bottom up image to a top down format by copying
822            // one scanline from the bottom to the top at a time.
823
824            for (int i=0; i<height; i++) {
825                System.arraycopy(values,
826                imSize - (i+1)*(bytesPerScanline + padding),
827                bdata,
828                i*bytesPerScanline, bytesPerScanline);
829            }
830        } else {
831
832            for (int i=0; i<height; i++) {
833                System.arraycopy(values,
834                i * (bytesPerScanline + padding),
835                bdata,
836                i * bytesPerScanline,
837                bytesPerScanline);
838            }
839        }
840        return indexedModel(bdata, 1, paletteEntries);
841    }
842
843    // Method to read a 4 bit BMP image data
844    private Image read4Bit(int paletteEntries) throws IOException, BadElementException {
845        byte bdata[] = new byte[(width + 1) / 2 * height];
846
847        // Padding bytes at the end of each scanline
848        int padding = 0;
849
850        int bytesPerScanline = (int)Math.ceil(width/2.0d);
851        int remainder = bytesPerScanline % 4;
852        if (remainder != 0) {
853            padding = 4 - remainder;
854        }
855
856        int imSize = (bytesPerScanline + padding) * height;
857
858        // Read till we have the whole image
859        byte values[] = new byte[imSize];
860        int bytesRead = 0;
861        while (bytesRead < imSize) {
862            bytesRead += inputStream.read(values, bytesRead,
863            imSize - bytesRead);
864        }
865
866        if (isBottomUp) {
867
868            // Convert the bottom up image to a top down format by copying
869            // one scanline from the bottom to the top at a time.
870            for (int i=0; i<height; i++) {
871                System.arraycopy(values,
872                imSize - (i+1)*(bytesPerScanline + padding),
873                bdata,
874                i*bytesPerScanline,
875                bytesPerScanline);
876            }
877        } else {
878            for (int i=0; i<height; i++) {
879                System.arraycopy(values,
880                i * (bytesPerScanline + padding),
881                bdata,
882                i * bytesPerScanline,
883                bytesPerScanline);
884            }
885        }
886        return indexedModel(bdata, 4, paletteEntries);
887    }
888
889    // Method to read 8 bit BMP image data
890    private Image read8Bit(int paletteEntries) throws IOException, BadElementException {
891        byte bdata[] = new byte[width * height];
892        // Padding bytes at the end of each scanline
893        int padding = 0;
894
895        // width * bitsPerPixel should be divisible by 32
896        int bitsPerScanline = width * 8;
897        if ( bitsPerScanline%32 != 0) {
898            padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
899            padding = (int)Math.ceil(padding/8.0);
900        }
901
902        int imSize = (width + padding) * height;
903
904        // Read till we have the whole image
905        byte values[] = new byte[imSize];
906        int bytesRead = 0;
907        while (bytesRead < imSize) {
908            bytesRead += inputStream.read(values, bytesRead, imSize - bytesRead);
909        }
910
911        if (isBottomUp) {
912
913            // Convert the bottom up image to a top down format by copying
914            // one scanline from the bottom to the top at a time.
915            for (int i=0; i<height; i++) {
916                System.arraycopy(values,
917                imSize - (i+1) * (width + padding),
918                bdata,
919                i * width,
920                width);
921            }
922        } else {
923            for (int i=0; i<height; i++) {
924                System.arraycopy(values,
925                i * (width + padding),
926                bdata,
927                i * width,
928                width);
929            }
930        }
931        return indexedModel(bdata, 8, paletteEntries);
932    }
933
934    // Method to read 24 bit BMP image data
935    private void read24Bit(byte[] bdata) {
936        // Padding bytes at the end of each scanline
937        int padding = 0;
938
939        // width * bitsPerPixel should be divisible by 32
940        int bitsPerScanline = width * 24;
941        if ( bitsPerScanline%32 != 0) {
942            padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
943            padding = (int)Math.ceil(padding/8.0);
944        }
945
946
947        int imSize = (width * 3 + 3) / 4 * 4 * height;
948        // Read till we have the whole image
949        byte values[] = new byte[imSize];
950        try {
951            int bytesRead = 0;
952            while (bytesRead < imSize) {
953                int r = inputStream.read(values, bytesRead,
954                imSize - bytesRead);
955                if (r < 0)
956                    break;
957                bytesRead += r;
958            }
959        } catch (IOException ioe) {
960            throw new ExceptionConverter(ioe);
961        }
962
963        int l=0, count;
964
965        if (isBottomUp) {
966            int max = width*height*3-1;
967
968            count = -padding;
969            for (int i=0; i<height; i++) {
970                l = max - (i+1)*width*3 + 1;
971                count += padding;
972                for (int j=0; j<width; j++) {
973                    bdata[l + 2] = values[count++];
974                    bdata[l + 1] = values[count++];
975                    bdata[l] = values[count++];
976                    l += 3;
977                }
978            }
979        } else {
980            count = -padding;
981            for (int i=0; i<height; i++) {
982                count += padding;
983                for (int j=0; j<width; j++) {
984                    bdata[l + 2] = values[count++];
985                    bdata[l + 1] = values[count++];
986                    bdata[l] = values[count++];
987                    l += 3;
988                }
989            }
990        }
991    }
992
993    private int findMask(int mask) {
994        int k = 0;
995        for (; k < 32; ++k) {
996            if ((mask & 1) == 1)
997                break;
998            mask >>>= 1;
999        }
1000        return mask;
1001    }
1002
1003    private int findShift(int mask) {
1004        int k = 0;
1005        for (; k < 32; ++k) {
1006            if ((mask & 1) == 1)
1007                break;
1008            mask >>>= 1;
1009        }
1010        return k;
1011    }
1012
1013    private Image read1632Bit(boolean is32) throws IOException, BadElementException {
1014
1015        int red_mask = findMask(redMask);
1016        int red_shift = findShift(redMask);
1017        int red_factor = red_mask + 1;
1018        int green_mask = findMask(greenMask);
1019        int green_shift = findShift(greenMask);
1020        int green_factor = green_mask + 1;
1021        int blue_mask = findMask(blueMask);
1022        int blue_shift = findShift(blueMask);
1023        int blue_factor = blue_mask + 1;
1024        byte bdata[] = new byte[width * height * 3];
1025        // Padding bytes at the end of each scanline
1026        int padding = 0;
1027
1028        if (!is32) {
1029        // width * bitsPerPixel should be divisible by 32
1030            int bitsPerScanline = width * 16;
1031            if ( bitsPerScanline%32 != 0) {
1032                padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
1033                padding = (int)Math.ceil(padding/8.0);
1034            }
1035        }
1036
1037        int imSize = (int)imageSize;
1038        if (imSize == 0) {
1039            imSize = (int)(bitmapFileSize - bitmapOffset);
1040        }
1041
1042        int l=0;
1043        int v;
1044        if (isBottomUp) {
1045            for (int i=height - 1; i >= 0; --i) {
1046                l = width * 3 * i;
1047                for (int j=0; j<width; j++) {
1048                    if (is32)
1049                        v = (int)readDWord(inputStream);
1050                    else
1051                        v = readWord(inputStream);
1052                    bdata[l++] = (byte)((v >>> red_shift & red_mask) * 256 / red_factor);
1053                    bdata[l++] = (byte)((v >>> green_shift & green_mask) * 256 / green_factor);
1054                    bdata[l++] = (byte)((v >>> blue_shift & blue_mask) * 256 / blue_factor);
1055                }
1056                for (int m=0; m<padding; m++) {
1057                    inputStream.read();
1058                }
1059            }
1060        } else {
1061            for (int i=0; i<height; i++) {
1062                for (int j=0; j<width; j++) {
1063                    if (is32)
1064                        v = (int)readDWord(inputStream);
1065                    else
1066                        v = readWord(inputStream);
1067                    bdata[l++] = (byte)((v >>> red_shift & red_mask) * 256 / red_factor);
1068                    bdata[l++] = (byte)((v >>> green_shift & green_mask) * 256 / green_factor);
1069                    bdata[l++] = (byte)((v >>> blue_shift & blue_mask) * 256 / blue_factor);
1070                }
1071                for (int m=0; m<padding; m++) {
1072                    inputStream.read();
1073                }
1074            }
1075        }
1076        return new ImgRaw(width, height, 3, 8, bdata);
1077    }
1078
1079    private Image readRLE8() throws IOException, BadElementException {
1080
1081        // If imageSize field is not provided, calculate it.
1082        int imSize = (int)imageSize;
1083        if (imSize == 0) {
1084            imSize = (int)(bitmapFileSize - bitmapOffset);
1085        }
1086
1087        // Read till we have the whole image
1088        byte values[] = new byte[imSize];
1089        int bytesRead = 0;
1090        while (bytesRead < imSize) {
1091            bytesRead += inputStream.read(values, bytesRead,
1092            imSize - bytesRead);
1093        }
1094
1095        // Since data is compressed, decompress it
1096        byte val[] = decodeRLE(true, values);
1097
1098        // Uncompressed data does not have any padding
1099        imSize = width * height;
1100
1101        if (isBottomUp) {
1102
1103            // Convert the bottom up image to a top down format by copying
1104            // one scanline from the bottom to the top at a time.
1105            // int bytesPerScanline = (int)Math.ceil((double)width/8.0);
1106            byte temp[] = new byte[val.length];
1107            int bytesPerScanline = width;
1108            for (int i=0; i<height; i++) {
1109                System.arraycopy(val,
1110                imSize - (i+1)*bytesPerScanline,
1111                temp,
1112                i*bytesPerScanline, bytesPerScanline);
1113            }
1114            val = temp;
1115        }
1116        return indexedModel(val, 8, 4);
1117    }
1118
1119    private Image readRLE4() throws IOException, BadElementException {
1120
1121        // If imageSize field is not specified, calculate it.
1122        int imSize = (int)imageSize;
1123        if (imSize == 0) {
1124            imSize = (int)(bitmapFileSize - bitmapOffset);
1125        }
1126
1127        // Read till we have the whole image
1128        byte values[] = new byte[imSize];
1129        int bytesRead = 0;
1130        while (bytesRead < imSize) {
1131            bytesRead += inputStream.read(values, bytesRead,
1132            imSize - bytesRead);
1133        }
1134
1135        // Decompress the RLE4 compressed data.
1136        byte val[] = decodeRLE(false, values);
1137
1138        // Invert it as it is bottom up format.
1139        if (isBottomUp) {
1140
1141            byte inverted[] = val;
1142            val = new byte[width * height];
1143            int l = 0, index, lineEnd;
1144
1145            for (int i = height-1; i >= 0; i--) {
1146                index = i * width;
1147                lineEnd = l + width;
1148                while(l != lineEnd) {
1149                    val[l++] = inverted[index++];
1150                }
1151            }
1152        }
1153        int stride = (width + 1) / 2;
1154        byte bdata[] = new byte[stride * height];
1155        int ptr = 0;
1156        int sh = 0;
1157        for (int h = 0; h < height; ++h) {
1158            for (int w = 0; w < width; ++w) {
1159                if ((w & 1) == 0)
1160                    bdata[sh + w / 2] = (byte)(val[ptr++] << 4);
1161                else
1162                    bdata[sh + w / 2] |= (byte)(val[ptr++] & 0x0f);
1163            }
1164            sh += stride;
1165        }
1166        return indexedModel(bdata, 4, 4);
1167    }
1168
1169    private byte[] decodeRLE(boolean is8, byte values[]) {
1170        byte val[] = new byte[width * height];
1171        try {
1172            int ptr = 0;
1173            int x = 0;
1174            int q = 0;
1175            for (int y = 0; y < height && ptr < values.length;) {
1176                int count = values[ptr++] & 0xff;
1177                if (count != 0) {
1178                    // encoded mode
1179                    int bt = values[ptr++] & 0xff;
1180                    if (is8) {
1181                        for (int i = count; i != 0; --i) {
1182                            val[q++] = (byte)bt;
1183                        }
1184                    }
1185                    else {
1186                        for (int i = 0; i < count; ++i) {
1187                            val[q++] = (byte)((i & 1) == 1 ? bt & 0x0f : bt >>> 4 & 0x0f);
1188                        }
1189                    }
1190                    x += count;
1191                }
1192                else {
1193                    // escape mode
1194                    count = values[ptr++] & 0xff;
1195                    if (count == 1)
1196                        break;
1197                    switch (count) {
1198                        case 0:
1199                            x = 0;
1200                            ++y;
1201                            q = y * width;
1202                            break;
1203                        case 2:
1204                            // delta mode
1205                            x += values[ptr++] & 0xff;
1206                            y += values[ptr++] & 0xff;
1207                            q = y * width + x;
1208                            break;
1209                        default:
1210                            // absolute mode
1211                            if (is8) {
1212                                for (int i = count; i != 0; --i)
1213                                    val[q++] = (byte)(values[ptr++] & 0xff);
1214                            }
1215                            else {
1216                                int bt = 0;
1217                                for (int i = 0; i < count; ++i) {
1218                                    if ((i & 1) == 0)
1219                                        bt = values[ptr++] & 0xff;
1220                                    val[q++] = (byte)((i & 1) == 1 ? bt & 0x0f : bt >>> 4 & 0x0f);
1221                                }
1222                            }
1223                            x += count;
1224                            // read pad byte
1225                            if (is8) {
1226                                if ((count & 1) == 1)
1227                                    ++ptr;
1228                            }
1229                            else {
1230                                if ((count & 3) == 1 || (count & 3) == 2)
1231                                    ++ptr;
1232                            }
1233                            break;
1234                    }
1235                }
1236            }
1237        }
1238        catch (RuntimeException e) {
1239            //empty on purpose
1240        }
1241
1242        return val;
1243    }
1244
1245    // Windows defined data type reading methods - everything is little endian
1246
1247    // Unsigned 8 bits
1248    private int readUnsignedByte(InputStream stream) throws IOException {
1249        return stream.read() & 0xff;
1250    }
1251
1252    // Unsigned 2 bytes
1253    private int readUnsignedShort(InputStream stream) throws IOException {
1254        int b1 = readUnsignedByte(stream);
1255        int b2 = readUnsignedByte(stream);
1256        return (b2 << 8 | b1) & 0xffff;
1257    }
1258
1259    // Signed 16 bits
1260    private int readShort(InputStream stream) throws IOException {
1261        int b1 = readUnsignedByte(stream);
1262        int b2 = readUnsignedByte(stream);
1263        return b2 << 8 | b1;
1264    }
1265
1266    // Unsigned 16 bits
1267    private int readWord(InputStream stream) throws IOException {
1268        return readUnsignedShort(stream);
1269    }
1270
1271    // Unsigned 4 bytes
1272    private long readUnsignedInt(InputStream stream) throws IOException {
1273        int b1 = readUnsignedByte(stream);
1274        int b2 = readUnsignedByte(stream);
1275        int b3 = readUnsignedByte(stream);
1276        int b4 = readUnsignedByte(stream);
1277        long l = b4 << 24 | b3 << 16 | b2 << 8 | b1;
1278        return l & 0xffffffff;
1279    }
1280
1281    // Signed 4 bytes
1282    private int readInt(InputStream stream) throws IOException {
1283        int b1 = readUnsignedByte(stream);
1284        int b2 = readUnsignedByte(stream);
1285        int b3 = readUnsignedByte(stream);
1286        int b4 = readUnsignedByte(stream);
1287        return b4 << 24 | b3 << 16 | b2 << 8 | b1;
1288    }
1289
1290    // Unsigned 4 bytes
1291    private long readDWord(InputStream stream) throws IOException {
1292        return readUnsignedInt(stream);
1293    }
1294
1295    // 32 bit signed value
1296    private int readLong(InputStream stream) throws IOException {
1297        return readInt(stream);
1298    }
1299}