001/*
002 * $Id: PdfImage.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;
045
046import java.io.ByteArrayOutputStream;
047import java.io.IOException;
048import java.io.InputStream;
049import java.io.OutputStream;
050import com.itextpdf.text.error_messages.MessageLocalization;
051
052import com.itextpdf.text.Image;
053
054/**
055 * <CODE>PdfImage</CODE> is a <CODE>PdfStream</CODE> containing an image-<CODE>Dictionary</CODE> and -stream.
056 */
057
058public class PdfImage extends PdfStream {
059    
060    static final int TRANSFERSIZE = 4096;
061    // membervariables
062    
063    /** This is the <CODE>PdfName</CODE> of the image. */
064    protected PdfName name = null;
065    
066    // constructor
067    
068    /**
069     * Constructs a <CODE>PdfImage</CODE>-object.
070     *
071     * @param image the <CODE>Image</CODE>-object
072     * @param name the <CODE>PdfName</CODE> for this image
073     * @throws BadPdfFormatException on error
074     */
075    
076    public PdfImage(Image image, String name, PdfIndirectReference maskRef) throws BadPdfFormatException {
077        super();
078        if (name == null) 
079                generateImgResName( image );
080        else
081                this.name = new PdfName(name);
082        put(PdfName.TYPE, PdfName.XOBJECT);
083        put(PdfName.SUBTYPE, PdfName.IMAGE);
084        put(PdfName.WIDTH, new PdfNumber(image.getWidth()));
085        put(PdfName.HEIGHT, new PdfNumber(image.getHeight()));
086        if (image.getLayer() != null)
087            put(PdfName.OC, image.getLayer().getRef());
088        if (image.isMask() && (image.getBpc() == 1 || image.getBpc() > 0xff))
089            put(PdfName.IMAGEMASK, PdfBoolean.PDFTRUE);
090        if (maskRef != null) {
091            if (image.isSmask())
092                put(PdfName.SMASK, maskRef);
093            else
094                put(PdfName.MASK, maskRef);
095        }
096        if (image.isMask() && image.isInverted())
097            put(PdfName.DECODE, new PdfLiteral("[1 0]"));
098        if (image.isInterpolation())
099            put(PdfName.INTERPOLATE, PdfBoolean.PDFTRUE);
100        InputStream is = null;
101        try {
102            // Raw Image data
103            if (image.isImgRaw()) {
104                // will also have the CCITT parameters
105                int colorspace = image.getColorspace();
106                int transparency[] = image.getTransparency();
107                if (transparency != null && !image.isMask() && maskRef == null) {
108                    StringBuilder s = new StringBuilder("[");
109                    for (int k = 0; k < transparency.length; ++k)
110                        s.append(transparency[k]).append(" ");
111                    s.append("]");
112                    put(PdfName.MASK, new PdfLiteral(s.toString()));
113                }
114                bytes = image.getRawData();
115                put(PdfName.LENGTH, new PdfNumber(bytes.length));
116                int bpc = image.getBpc();
117                if (bpc > 0xff) {
118                    if (!image.isMask())
119                        put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
120                    put(PdfName.BITSPERCOMPONENT, new PdfNumber(1));
121                    put(PdfName.FILTER, PdfName.CCITTFAXDECODE);
122                    int k = bpc - Image.CCITTG3_1D;
123                    PdfDictionary decodeparms = new PdfDictionary();
124                    if (k != 0)
125                        decodeparms.put(PdfName.K, new PdfNumber(k));
126                    if ((colorspace & Image.CCITT_BLACKIS1) != 0)
127                        decodeparms.put(PdfName.BLACKIS1, PdfBoolean.PDFTRUE);
128                    if ((colorspace & Image.CCITT_ENCODEDBYTEALIGN) != 0)
129                        decodeparms.put(PdfName.ENCODEDBYTEALIGN, PdfBoolean.PDFTRUE);
130                    if ((colorspace & Image.CCITT_ENDOFLINE) != 0)
131                        decodeparms.put(PdfName.ENDOFLINE, PdfBoolean.PDFTRUE);
132                    if ((colorspace & Image.CCITT_ENDOFBLOCK) != 0)
133                        decodeparms.put(PdfName.ENDOFBLOCK, PdfBoolean.PDFFALSE);
134                    decodeparms.put(PdfName.COLUMNS, new PdfNumber(image.getWidth()));
135                    decodeparms.put(PdfName.ROWS, new PdfNumber(image.getHeight()));
136                    put(PdfName.DECODEPARMS, decodeparms);
137                }
138                else {
139                    switch(colorspace) {
140                        case 1:
141                            put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
142                            if (image.isInverted())
143                                put(PdfName.DECODE, new PdfLiteral("[1 0]"));
144                            break;
145                        case 3:
146                            put(PdfName.COLORSPACE, PdfName.DEVICERGB);
147                            if (image.isInverted())
148                                put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0]"));
149                            break;
150                        case 4:
151                        default:
152                            put(PdfName.COLORSPACE, PdfName.DEVICECMYK);
153                            if (image.isInverted())
154                                put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0 1 0]"));
155                    }
156                    PdfDictionary additional = image.getAdditional();
157                    if (additional != null)
158                        putAll(additional);
159                    if (image.isMask() && (image.getBpc() == 1 || image.getBpc() > 8))
160                        remove(PdfName.COLORSPACE);
161                    put(PdfName.BITSPERCOMPONENT, new PdfNumber(image.getBpc()));
162                    if (image.isDeflated())
163                        put(PdfName.FILTER, PdfName.FLATEDECODE);
164                    else {
165                        flateCompress(image.getCompressionLevel());
166                    }
167                }
168                return;
169            }
170            // GIF, JPEG or PNG
171            String errorID;
172            if (image.getRawData() == null){
173                is = image.getUrl().openStream();
174                errorID = image.getUrl().toString();
175            }
176            else{
177                is = new java.io.ByteArrayInputStream(image.getRawData());
178                errorID = "Byte array";
179            }
180            switch(image.type()) {
181                case Image.JPEG:
182                    put(PdfName.FILTER, PdfName.DCTDECODE);
183                    switch(image.getColorspace()) {
184                        case 1:
185                            put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
186                            break;
187                        case 3:
188                            put(PdfName.COLORSPACE, PdfName.DEVICERGB);
189                            break;
190                        default:
191                            put(PdfName.COLORSPACE, PdfName.DEVICECMYK);
192                            if (image.isInverted()) {
193                                put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0 1 0]"));
194                            }
195                    }
196                    put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
197                    if (image.getRawData() != null){
198                        bytes = image.getRawData();
199                        put(PdfName.LENGTH, new PdfNumber(bytes.length));
200                        return;
201                    }
202                    streamBytes = new ByteArrayOutputStream();
203                    transferBytes(is, streamBytes, -1);
204                    break;
205                case Image.JPEG2000:
206                    put(PdfName.FILTER, PdfName.JPXDECODE);
207                    if (image.getColorspace() > 0) {
208                        switch(image.getColorspace()) {
209                            case 1:
210                                put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
211                                break;
212                            case 3:
213                                put(PdfName.COLORSPACE, PdfName.DEVICERGB);
214                                break;
215                            default:
216                                put(PdfName.COLORSPACE, PdfName.DEVICECMYK);
217                        }
218                        put(PdfName.BITSPERCOMPONENT, new PdfNumber(image.getBpc()));
219                    }
220                    if (image.getRawData() != null){
221                        bytes = image.getRawData();
222                        put(PdfName.LENGTH, new PdfNumber(bytes.length));
223                        return;
224                    }
225                    streamBytes = new ByteArrayOutputStream();
226                    transferBytes(is, streamBytes, -1);
227                    break;
228                case Image.JBIG2:
229                    put(PdfName.FILTER, PdfName.JBIG2DECODE);
230                    put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
231                    put(PdfName.BITSPERCOMPONENT, new PdfNumber(1));
232                    if (image.getRawData() != null){
233                        bytes = image.getRawData();
234                        put(PdfName.LENGTH, new PdfNumber(bytes.length));
235                        return;
236                    }
237                    streamBytes = new ByteArrayOutputStream();
238                    transferBytes(is, streamBytes, -1);
239                        break;
240                default:
241                    throw new BadPdfFormatException(MessageLocalization.getComposedMessage("1.is.an.unknown.image.format", errorID));
242            }
243            put(PdfName.LENGTH, new PdfNumber(streamBytes.size()));
244        }
245        catch(IOException ioe) {
246            throw new BadPdfFormatException(ioe.getMessage());
247        }
248        finally {
249            if (is != null) {
250                try{
251                    is.close();
252                }
253                catch (Exception ee) {
254                    // empty on purpose
255                }
256            }
257        }
258    }
259    
260    /**
261     * Returns the <CODE>PdfName</CODE> of the image.
262     *
263     * @return          the name
264     */
265    
266    public PdfName name() {
267        return name;
268    }
269    
270    static void transferBytes(InputStream in, OutputStream out, int len) throws IOException {
271        byte buffer[] = new byte[TRANSFERSIZE];
272        if (len < 0)
273            len = 0x7fff0000;
274        int size;
275        while (len != 0) {
276            size = in.read(buffer, 0, Math.min(len, TRANSFERSIZE));
277            if (size < 0)
278                return;
279            out.write(buffer, 0, size);
280            len -= size;
281        }
282    }
283    
284    protected void importAll(PdfImage dup) {
285        name = dup.name;
286        compressed = dup.compressed;
287        compressionLevel = dup.compressionLevel;
288        streamBytes = dup.streamBytes;
289        bytes = dup.bytes;
290        hashMap = dup.hashMap;
291    }
292    
293    /**
294     * Called when no resource name is provided in our constructor.  This generates a 
295     * name that is required to be unique within a given resource dictionary.
296     * @since 5.0.1
297     */
298    private void generateImgResName( Image img ) {
299        name = new PdfName( "img" + Long.toHexString( img.getMySerialId() ) );
300    }
301}