using System; using System.Collections; using System.IO; using System.Net; using System.util; using iTextSharp.text; /* * Copyright 2003-2008 by Paulo Soares. * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ * * This code was originally released in 2001 by SUN (see class * com.sun.media.imageioimpl.plugins.bmp.BMPImageReader.java) * using the BSD license in a specific wording. In a mail dating from * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission * to use the code under the following version of the BSD license: * * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for * use in the design, construction, operation or maintenance of any * nuclear facility. */ namespace iTextSharp.text.pdf.codec { /** Reads a BMP image. All types of BMP can be read. *
    * It is based in the JAI codec.
    *
    * @author  Paulo Soares (psoares@consiste.pt)
    */
    public class BmpImage {
        
        // BMP variables
        private Stream inputStream;
        private long bitmapFileSize;
        private long bitmapOffset;
        private long compression;
        private long imageSize;
        private byte[] palette;
        private int imageType;
        private int numBands;
        private bool isBottomUp;
        private int bitsPerPixel;
        private int redMask, greenMask, blueMask, alphaMask;
        public Hashtable properties = new Hashtable();    
        private long xPelsPerMeter;
        private long yPelsPerMeter;
        // BMP Image types
        private const int VERSION_2_1_BIT = 0;
        private const int VERSION_2_4_BIT = 1;
        private const int VERSION_2_8_BIT = 2;
        private const int VERSION_2_24_BIT = 3;
        
        private const int VERSION_3_1_BIT = 4;
        private const int VERSION_3_4_BIT = 5;
        private const int VERSION_3_8_BIT = 6;
        private const int VERSION_3_24_BIT = 7;
        
        private const int VERSION_3_NT_16_BIT = 8;
        private const int VERSION_3_NT_32_BIT = 9;
        
        private const int VERSION_4_1_BIT = 10;
        private const int VERSION_4_4_BIT = 11;
        private const int VERSION_4_8_BIT = 12;
        private const int VERSION_4_16_BIT = 13;
        private const int VERSION_4_24_BIT = 14;
        private const int VERSION_4_32_BIT = 15;
        
        // Color space types
        private const int LCS_CALIBRATED_RGB = 0;
        private const int LCS_sRGB = 1;
        private const int LCS_CMYK = 2;
        
        // Compression Types
        private const int BI_RGB = 0;
        private const int BI_RLE8 = 1;
        private const int BI_RLE4 = 2;
        private const int BI_BITFIELDS = 3;
        
        int width;
        int height;
        
        internal BmpImage(Stream isp, bool noHeader, int size) {
            bitmapFileSize = size;
            bitmapOffset = 0;
            Process(isp, noHeader);
        }
        
        /** Reads a BMP from an url.
        * @param url the url
        * @throws IOException on error
        * @return the image
        */    
        public static Image GetImage(Uri url) {
            Stream isp = null;
            try {
                isp = WebRequest.Create(url).GetResponse().GetResponseStream();
                Image img = GetImage(isp);
                img.Url = url;
                return img;
            }
            finally {
                if (isp != null) {
                    isp.Close();
                }
            }
        }
        
        /** Reads a BMP from a stream. The stream is not closed.
        * @param is the stream
        * @throws IOException on error
        * @return the image
        */    
        public static Image GetImage(Stream isp) {
            return GetImage(isp, false, 0);
        }
        
        /** Reads a BMP from a stream. The stream is not closed.
        * The BMP may not have a header and be considered as a plain DIB.
        * @param is the stream
        * @param noHeader true to process a plain DIB
        * @param size the size of the DIB. Not used for a BMP
        * @throws IOException on error
        * @return the image
        */    
        public static Image GetImage(Stream isp, bool noHeader, int size) {
            BmpImage bmp = new BmpImage(isp, noHeader, size);
            Image img = bmp.GetImage();
            img.SetDpi((int)((double)bmp.xPelsPerMeter * 0.0254 + 0.5), (int)((double)bmp.yPelsPerMeter * 0.0254 + 0.5));
            img.OriginalType = Image.ORIGINAL_BMP;
            return img;
        }
        
        /** Reads a BMP from a file.
        * @param file the file
        * @throws IOException on error
        * @return the image
        */    
        public static Image GetImage(String file) {
            return GetImage(Utilities.ToURL(file));
        }
        
        /** Reads a BMP from a byte array.
        * @param data the byte array
        * @throws IOException on error
        * @return the image
        */    
        public static Image GetImage(byte[] data) {
            Stream isp = new MemoryStream(data);
            Image img = GetImage(isp);
            img.OriginalData = data;
            return img;
        }
        
        protected void Process(Stream stream, bool noHeader) {
            if (noHeader || stream is BufferedStream) {
                inputStream = stream;
            } else {
                inputStream = new BufferedStream(stream);
            }
            if (!noHeader) {
                // Start File Header
                if (!(ReadUnsignedByte(inputStream) == 'B' &&
                ReadUnsignedByte(inputStream) == 'M')) {
                    throw new Exception("Invalid magic value for BMP file.");
                }
                // Read file size
                bitmapFileSize = ReadDWord(inputStream);
                // Read the two reserved fields
                ReadWord(inputStream);
                ReadWord(inputStream);
                // Offset to the bitmap from the beginning
                bitmapOffset = ReadDWord(inputStream);
                // End File Header
            }
            // Start BitmapCoreHeader
            long size = ReadDWord(inputStream);
            
            if (size == 12) {
                width = ReadWord(inputStream);
                height = ReadWord(inputStream);
            } else {
                width = ReadLong(inputStream);
                height = ReadLong(inputStream);
            }
            
            int planes = ReadWord(inputStream);
            bitsPerPixel = ReadWord(inputStream);
            
            properties["color_planes"] = planes;
            properties["bits_per_pixel"] = bitsPerPixel;
            
            // As BMP always has 3 rgb bands, except for Version 5,
            // which is bgra
            numBands = 3;
            if (bitmapOffset == 0)
                bitmapOffset = size;
            if (size == 12) {
                // Windows 2.x and OS/2 1.x
                properties["bmp_version"] = "BMP v. 2.x";
                
                // Classify the image type
                if (bitsPerPixel == 1) {
                    imageType = VERSION_2_1_BIT;
                } else if (bitsPerPixel == 4) {
                    imageType = VERSION_2_4_BIT;
                } else if (bitsPerPixel == 8) {
                    imageType = VERSION_2_8_BIT;
                } else if (bitsPerPixel == 24) {
                    imageType = VERSION_2_24_BIT;
                }
                
                // Read in the palette
                int numberOfEntries = (int)((bitmapOffset-14-size) / 3);
                int sizeOfPalette = numberOfEntries*3;
                if (bitmapOffset == size) {
                    switch (imageType) {
                        case VERSION_2_1_BIT:
                            sizeOfPalette = 2 * 3;
                            break;
                        case VERSION_2_4_BIT:
                            sizeOfPalette = 16 * 3;
                            break;
                        case VERSION_2_8_BIT:
                            sizeOfPalette = 256 * 3;
                            break;
                        case VERSION_2_24_BIT:
                            sizeOfPalette = 0;
                            break;
                    }
                    bitmapOffset = size + sizeOfPalette;
                }
                ReadPalette(sizeOfPalette);
            } else {
                
                compression = ReadDWord(inputStream);
                imageSize = ReadDWord(inputStream);
                xPelsPerMeter = ReadLong(inputStream);
                yPelsPerMeter = ReadLong(inputStream);
                long colorsUsed = ReadDWord(inputStream);
                long colorsImportant = ReadDWord(inputStream);
                
                switch ((int)compression) {
                    case BI_RGB:
                        properties["compression"] = "BI_RGB";
                        break;
                        
                    case BI_RLE8:
                        properties["compression"] = "BI_RLE8";
                        break;
                        
                    case BI_RLE4:
                        properties["compression"] = "BI_RLE4";
                        break;
                        
                    case BI_BITFIELDS:
                        properties["compression"] = "BI_BITFIELDS";
                        break;
                }
                
                properties["x_pixels_per_meter"] = xPelsPerMeter;
                properties["y_pixels_per_meter"] = yPelsPerMeter;
                properties["colors_used"] = colorsUsed;
                properties["colors_important"] = colorsImportant;
                
                if (size == 40) {
                    // Windows 3.x and Windows NT
                    switch ((int)compression) {
                        
                        case BI_RGB:  // No compression
                        case BI_RLE8:  // 8-bit RLE compression
                        case BI_RLE4:  // 4-bit RLE compression
                            
                            if (bitsPerPixel == 1) {
                                imageType = VERSION_3_1_BIT;
                            } else if (bitsPerPixel == 4) {
                                imageType = VERSION_3_4_BIT;
                            } else if (bitsPerPixel == 8) {
                                imageType = VERSION_3_8_BIT;
                            } else if (bitsPerPixel == 24) {
                                imageType = VERSION_3_24_BIT;
                            } else if (bitsPerPixel == 16) {
                                imageType = VERSION_3_NT_16_BIT;
                                redMask = 0x7C00;
                                greenMask = 0x3E0;
                                blueMask = 0x1F;
                                properties["red_mask"] = redMask;
                                properties["green_mask"] = greenMask;
                                properties["blue_mask"] = blueMask;
                            } else if (bitsPerPixel == 32) {
                                imageType = VERSION_3_NT_32_BIT;
                                redMask   = 0x00FF0000;
                                greenMask = 0x0000FF00;
                                blueMask  = 0x000000FF;
                                properties["red_mask"] = redMask;
                                properties["green_mask"] = greenMask;
                                properties["blue_mask"] = blueMask;
                            }
                            // Read in the palette
                            int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
                            int sizeOfPalette = numberOfEntries*4;
                            if (bitmapOffset == size) {
                                switch (imageType) {
                                    case VERSION_3_1_BIT:
                                        sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
                                        break;
                                    case VERSION_3_4_BIT:
                                        sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
                                        break;
                                    case VERSION_3_8_BIT:
                                        sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
                                        break;
                                    default:
                                        sizeOfPalette = 0;
                                        break;
                                }
                                bitmapOffset = size + sizeOfPalette;
                            }
                            ReadPalette(sizeOfPalette);
                            properties["bmp_version"] = "BMP v. 3.x";
                            break;
                            
                        case BI_BITFIELDS:
                            
                            if (bitsPerPixel == 16) {
                                imageType = VERSION_3_NT_16_BIT;
                            } else if (bitsPerPixel == 32) {
                                imageType = VERSION_3_NT_32_BIT;
                            }
                            
                            // BitsField encoding
                            redMask = (int)ReadDWord(inputStream);
                            greenMask = (int)ReadDWord(inputStream);
                            blueMask = (int)ReadDWord(inputStream);
                            
                            properties["red_mask"] = redMask;
                            properties["green_mask"] = greenMask;
                            properties["blue_mask"] = blueMask;
                            
                            if (colorsUsed != 0) {
                                // there is a palette
                                sizeOfPalette = (int)colorsUsed*4;
                                ReadPalette(sizeOfPalette);
                            }
                            
                            properties["bmp_version"] = "BMP v. 3.x NT";
                            break;
                            
                        default:
                            throw new
                            Exception("Invalid compression specified in BMP file.");
                    }
                } else if (size == 108) {
                    // Windows 4.x BMP
                    
                    properties["bmp_version"] = "BMP v. 4.x";
                    
                    // rgb masks, valid only if comp is BI_BITFIELDS
                    redMask = (int)ReadDWord(inputStream);
                    greenMask = (int)ReadDWord(inputStream);
                    blueMask = (int)ReadDWord(inputStream);
                    // Only supported for 32bpp BI_RGB argb
                    alphaMask = (int)ReadDWord(inputStream);
                    long csType = ReadDWord(inputStream);
                    int redX = ReadLong(inputStream);
                    int redY = ReadLong(inputStream);
                    int redZ = ReadLong(inputStream);
                    int greenX = ReadLong(inputStream);
                    int greenY = ReadLong(inputStream);
                    int greenZ = ReadLong(inputStream);
                    int blueX = ReadLong(inputStream);
                    int blueY = ReadLong(inputStream);
                    int blueZ = ReadLong(inputStream);
                    long gammaRed = ReadDWord(inputStream);
                    long gammaGreen = ReadDWord(inputStream);
                    long gammaBlue = ReadDWord(inputStream);
                    
                    if (bitsPerPixel == 1) {
                        imageType = VERSION_4_1_BIT;
                    } else if (bitsPerPixel == 4) {
                        imageType = VERSION_4_4_BIT;
                    } else if (bitsPerPixel == 8) {
                        imageType = VERSION_4_8_BIT;
                    } else if (bitsPerPixel == 16) {
                        imageType = VERSION_4_16_BIT;
                        if ((int)compression == BI_RGB) {
                            redMask = 0x7C00;
                            greenMask = 0x3E0;
                            blueMask = 0x1F;
                        }
                    } else if (bitsPerPixel == 24) {
                        imageType = VERSION_4_24_BIT;
                    } else if (bitsPerPixel == 32) {
                        imageType = VERSION_4_32_BIT;
                        if ((int)compression == BI_RGB) {
                            redMask   = 0x00FF0000;
                            greenMask = 0x0000FF00;
                            blueMask  = 0x000000FF;
                        }
                    }
                    
                    properties["red_mask"] = redMask;
                    properties["green_mask"] = greenMask;
                    properties["blue_mask"] = blueMask;
                    properties["alpha_mask"] = alphaMask;
                    // Read in the palette
                    int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
                    int sizeOfPalette = numberOfEntries*4;
                    if (bitmapOffset == size) {
                        switch (imageType) {
                            case VERSION_4_1_BIT:
                                sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
                                break;
                            case VERSION_4_4_BIT:
                                sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
                                break;
                            case VERSION_4_8_BIT:
                                sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
                                break;
                            default:
                                sizeOfPalette = 0;
                                break;
                        }
                        bitmapOffset = size + sizeOfPalette;
                    }
                    ReadPalette(sizeOfPalette);
                    
                    switch ((int)csType) {
                        case LCS_CALIBRATED_RGB:
                            // All the new fields are valid only for this case
                            properties["color_space"] = "LCS_CALIBRATED_RGB";
                            properties["redX"] = redX;
                            properties["redY"] = redY;
                            properties["redZ"] = redZ;
                            properties["greenX"] = greenX;
                            properties["greenY"] = greenY;
                            properties["greenZ"] = greenZ;
                            properties["blueX"] = blueX;
                            properties["blueY"] = blueY;
                            properties["blueZ"] = blueZ;
                            properties["gamma_red"] = gammaRed;
                            properties["gamma_green"] = gammaGreen;
                            properties["gamma_blue"] = gammaBlue;
                            
                            // break;
                            throw new
                            Exception("Not implemented yet.");
                            
                        case LCS_sRGB:
                            // Default Windows color space
                            properties["color_space"] = "LCS_sRGB";
                            break;
                            
                        case LCS_CMYK:
                            properties["color_space"] = "LCS_CMYK";
                            //		    break;
                            throw new
                            Exception("Not implemented yet.");
                    }
                    
                } else {
                    properties["bmp_version"] = "BMP v. 5.x";
                    throw new
                    Exception("BMP version 5 not implemented yet.");
                }
            }
            
            if (height > 0) {
                // bottom up image
                isBottomUp = true;
            } else {
                // top down image
                isBottomUp = false;
                height = Math.Abs(height);
            }
            // When number of bitsPerPixel is <= 8, we use IndexColorModel.
            if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
                
                numBands = 1;
                
                
                // Create IndexColorModel from the palette.
                byte[] r;
                byte[] g;
                byte[] b;
                int sizep;
                if (imageType == VERSION_2_1_BIT ||
                imageType == VERSION_2_4_BIT ||
                imageType == VERSION_2_8_BIT) {
                    
                    sizep = palette.Length/3;
                    
                    if (sizep > 256) {
                        sizep = 256;
                    }
                    
                    int off;
                    r = new byte[sizep];
                    g = new byte[sizep];
                    b = new byte[sizep];
                    for (int i=0; i