001/*
002 * $Id: PngWriter.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
045package com.itextpdf.text.pdf.codec;
046
047import com.itextpdf.text.DocWriter;
048import java.io.ByteArrayOutputStream;
049import java.io.IOException;
050import java.io.OutputStream;
051import java.util.zip.DeflaterOutputStream;
052
053/**
054 * Writes a PNG image.
055 *
056 * @author  Paulo Soares
057 * @since 5.0.3
058 */
059public class PngWriter {
060    private static final byte[] PNG_SIGNTURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10};
061
062    private static final byte[] IHDR = DocWriter.getISOBytes("IHDR");
063    private static final byte[] PLTE = DocWriter.getISOBytes("PLTE");
064    private static final byte[] IDAT = DocWriter.getISOBytes("IDAT");
065    private static final byte[] IEND = DocWriter.getISOBytes("IEND");
066    private static final byte[] iCCP = DocWriter.getISOBytes("iCCP");
067
068    private static long[] crc_table;
069
070    private OutputStream outp;
071
072    public PngWriter(OutputStream outp) throws IOException {
073        this.outp = outp;
074        outp.write(PNG_SIGNTURE);
075    }
076
077    public void writeHeader(int width, int height, int bitDepth, int colorType) throws IOException {
078        ByteArrayOutputStream ms = new ByteArrayOutputStream();
079        outputInt(width, ms);
080        outputInt(height, ms);
081        ms.write(bitDepth);
082        ms.write(colorType);
083        ms.write(0);
084        ms.write(0);
085        ms.write(0);
086        writeChunk(IHDR, ms.toByteArray());
087    }
088
089    public void writeEnd() throws IOException {
090        writeChunk(IEND, new byte[0]);
091    }
092
093    public void writeData(byte[] data, int stride) throws IOException {
094        ByteArrayOutputStream stream = new ByteArrayOutputStream();
095        DeflaterOutputStream zip = new DeflaterOutputStream(stream);
096        for (int k = 0; k < data.length; k += stride) {
097            zip.write(0);
098            zip.write(data, k, stride);
099        }
100        zip.finish();
101        writeChunk(IDAT, stream.toByteArray());
102    }
103
104    public void writePalette(byte[] data) throws IOException {
105        writeChunk(PLTE, data);
106    }
107
108    public void writeIccProfile(byte[] data) throws IOException {
109        ByteArrayOutputStream stream = new ByteArrayOutputStream();
110        stream.write((byte)'I');
111        stream.write((byte)'C');
112        stream.write((byte)'C');
113        stream.write(0);
114        stream.write(0);
115        DeflaterOutputStream zip = new DeflaterOutputStream(stream);
116        zip.write(data);
117        zip.finish();
118        writeChunk(iCCP, stream.toByteArray());
119    }
120
121    private static void make_crc_table() {
122        if (crc_table != null)
123            return;
124        long[] crc2 = new long[256];
125        for (int n = 0; n < 256; n++) {
126            long c = n;
127            for (int k = 0; k < 8; k++) {
128                if ((c & 1) != 0)
129                    c = 0xedb88320L ^ (c >> 1);
130                else
131                    c = c >> 1;
132            }
133            crc2[n] = c;
134        }
135        crc_table = crc2;
136    }
137
138    private static long update_crc(long crc, byte[] buf, int offset, int len) {
139        long c = crc;
140
141        if (crc_table == null)
142            make_crc_table();
143        for (int n = 0; n < len; n++) {
144            c = crc_table[(int)((c ^ buf[n + offset]) & 0xff)] ^ (c >> 8);
145        }
146        return c;
147    }
148
149    private static long crc(byte[] buf, int offset, int len) {
150        return update_crc(0xffffffffL, buf, offset, len) ^ 0xffffffffL;
151    }
152
153    private static long crc(byte[] buf) {
154        return update_crc(0xffffffffL, buf, 0, buf.length) ^ 0xffffffffL;
155    }
156
157    public void outputInt(int n) throws IOException {
158        outputInt(n, outp);
159    }
160
161    public static void outputInt(int n, OutputStream s) throws IOException {
162        s.write((byte)(n >> 24));
163        s.write((byte)(n >> 16));
164        s.write((byte)(n >> 8));
165        s.write((byte)n);
166    }
167
168    public void writeChunk(byte[] chunkType, byte[] data) throws IOException {
169        outputInt(data.length);
170        outp.write(chunkType, 0, 4);
171        outp.write(data);
172        long c = update_crc(0xffffffffL, chunkType, 0, chunkType.length);
173        c = update_crc(c, data, 0, data.length) ^ 0xffffffffL;
174        outputInt((int)c);
175    }
176}