/** * Copyright (c) 2003-2005, www.pdfbox.org * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions 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. * 3. Neither the name of pdfbox; nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * http://www.pdfbox.org * */ package org.pdfbox.filter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.log4j.Logger; import org.pdfbox.cos.COSDictionary; import org.pdfbox.cos.COSName; /** * This is a filter for the CCITTFax Decoder. * * @author Ben Litchfield, Marcel Kammer, Paul King * @version $Revision: 1.10 $ */ public class CCITTFaxDecodeFilter implements Filter { private static Logger log = Logger.getLogger(CCITTFaxDecodeFilter.class); // Filter will write 15 TAG's // If you add or remove TAG's you will have to modify this value private static final int TAG_COUNT = 15; // HEADERLENGTH(fix 8 Bytes) plus ImageLength(variable) private int offset = 8; // Bytecounter for Bytes that will be written after the TAG-DICTIONARY private int tailingBytesCount = 0; // Bytes to write after TAG-DICTIONARY private final ByteArrayOutputStream tailer = new ByteArrayOutputStream(); /** * Constructor. */ public CCITTFaxDecodeFilter() { } /** * This will decode some compressed data. * * @param compressedData * The compressed byte stream. * @param result * The place to write the uncompressed byte stream. * @param options * The options to use to encode the data. * * @throws IOException * If there is an error decompressing the stream. */ public void decode(InputStream compressedData, OutputStream result, COSDictionary options) throws IOException { // log.warn( "Warning: CCITTFaxDecode.decode is not implemented yet, // skipping this stream." ); // Get ImageParams from PDF COSDictionary dict = (COSDictionary) options.getDictionaryObject("DecodeParms"); int width = options.getInt("Width"); int height = options.getInt("Height"); int length = options.getInt(COSName.LENGTH); int compressionType = dict.getInt("K"); boolean blackIs1 = dict.getBoolean("BlackIs1", false); // HEADER-INFO and starting point of TAG-DICTIONARY writeTagHeader(result, length); // IMAGE-DATA int i = 0; //int sum = 0; byte[] buffer = new byte[32768]; int lentoread = length; while ((lentoread > 0) && ((i = compressedData.read(buffer, 0, Math.min(lentoread, 32768))) != -1)) { //sum += i; result.write(buffer, 0, i); lentoread = lentoread - i; } // If lentoread is > 0 then we need to write out some padding to equal the header // We'll use what we have in the buffer it's just padding after all while (lentoread > 0) { result.write(buffer, 0, Math.min(lentoread, 32768)); lentoread = lentoread - Math.min(lentoread, 32738); } //System.out.println("Gelesen: " + sum); // TAG-COUNT writeTagCount(result); // WIDTH 0x0100 writeTagWidth(result, width); // HEIGHT 0x0101 writeTagHeight(result, height); // BITSPERSAMPLE 0x0102 // Always 1 for CCITTFax writeTagBitsPerSample(result, 1); // COMPRESSION 0x0103 writeTagCompression(result, compressionType); // PHOTOMETRIC 0x0106 writeTagPhotometric(result, blackIs1); // STRIPOFFSET 0x0111 // HERE ALWAYS 8, because ImageData comes before TAG-DICTIONARY writeTagStripOffset(result, 8); // ORIENTATION 0x0112 writeTagOrientation(result, 1); // SamplesPerPixel 0x0115 writeTagSamplesPerPixel(result, 1); // RowsPerStrip 0x0116 writeTagRowsPerStrip(result, height); // Stripcount 0x0117 writeTagStripByteCount(result, length); // XRESOLUTION 0x011A // HERE: 200 DPI writeTagXRes(result, 200, 1); // YRESOLITION 0x011B // HERE: 200 DPI writeTagYRes(result, 200, 1); // ResolutionUnit 0x0128 // HERE: DPI writeTagResolutionUnit(result, 2); // SOFTWARE 0x0131 // minimum 4 chars writeTagSoftware(result, "pdfbox".getBytes()); // DATE AND TIME 0x0132 writeTagDateTime(result, new Date()); // END OF TAG-DICT writeTagTailer(result); } private void writeTagHeader(OutputStream result, int length) throws IOException { byte[] header = { 'M', 'M', 0, '*' };// Big-endian result.write(header); // Add imagelength to offset offset += length; // OFFSET TAG-DICTIONARY int i1 = offset/16777216;//=value/(256*256*256) int i2 = (offset-i1*16777216)/65536; int i3 = (offset-i1*16777216-i2*65536)/256; int i4 = offset % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); } private void writeTagCount(OutputStream result) throws IOException { result.write(TAG_COUNT / 256); result.write(TAG_COUNT % 256);// tagCount } private void writeTagWidth(OutputStream result, int width) throws IOException { // @todo width berechnen // TAG-ID 100 result.write(1); result.write(0); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE = width result.write(width/256); result.write(width%256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagHeight(OutputStream result, int height) throws IOException { //@todo height berechnen // TAG-ID 101 result.write(1); result.write(1); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(height/256); result.write(height%256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagBitsPerSample(OutputStream result, int value) throws IOException { // TAG-ID 102 result.write(1); result.write(2); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(value/256); result.write(value%256); result.write(0);//SHORT=0 result.write(0);//SHORT=0 } /** * Write the tag compression. * * @param result The stream to write to. * @param type The type to write. * @throws IOException If there is an error writing to the stream. */ public void writeTagCompression(OutputStream result, int type) throws IOException { // TAG-ID 103 result.write(1); result.write(3); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LEGNTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE //@todo typ eintragen; hier immer 4 result.write(0); if (type < 0) { result.write(4);// G4 } else if (type == 0) { result.write(3);// G3-1D } else { result.write(2);// G3-2D } result.write(0); result.write(0); } private void writeTagPhotometric(OutputStream result, boolean blackIs1) throws IOException { // TAG-ID 106 result.write(1); result.write(6); // TAG-TYPE SHORT result.write(0); result.write(3); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(0); if (blackIs1) { result.write(1); } else { result.write(0); } result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagStripOffset(OutputStream result, int value) throws IOException { // TAG-ID 111 result.write(1); result.write(17); // TAG-TYPE LONG=4 result.write(0); result.write(4); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE = 8 //VOR TAG-DICTIONARY int i1 = value/16777216;//=value/(256*256*256) int i2 = (value-i1*16777216)/65536; int i3 = (value-i1*16777216-i2*65536)/256; int i4 = value % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); } private void writeTagSamplesPerPixel(OutputStream result, int value) throws IOException { // TAG-ID 115 result.write(1); result.write(21); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(value / 256); result.write(value % 256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagRowsPerStrip(OutputStream result, int value) throws IOException { // TAG-ID 116 result.write(1); result.write(22); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(value / 256); result.write(value % 256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagStripByteCount(OutputStream result, int value) throws IOException { //@todo value auswerten // TAG-ID 117 result.write(1); result.write(23); // TAG-TYPE LONG=4 result.write(0); result.write(4); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE int i1 = value/16777216;//=value/(256*256*256) int i2 = (value-i1*16777216)/65536; int i3 = (value-i1*16777216-i2*65536)/256; int i4 = value % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); } private void writeTagXRes(OutputStream result, int value1, int value2) throws IOException { // TAG-ID 11A result.write(1); result.write(26); // TAG-TYPE RATIONAL=5 result.write(0); result.write(5); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE=OFFSET TO RATIONAL int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size(); int i1 = valueOffset/16777216;//=value/(256*256*256) int i2 = (valueOffset-i1*16777216)/65536; int i3 = (valueOffset-i1*16777216-i2*65536)/256; int i4 = valueOffset % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); i1 = value1 /16777216; i2 = (value1-i1*16777216)/65536; i3 = (value1-i1*16777216 - i2*65536)/256; i4 = value1 % 256; tailer.write(i1); tailer.write(i2); tailer.write(i3); tailer.write(i4); i1 = value2 /16777216; i2 = (value2-i1*16777216)/65536; i3 = (value2-i1*16777216 - i2*65536)/256; i4 = value2 % 256; tailer.write(i1); tailer.write(i2); tailer.write(i3); tailer.write(i4); tailingBytesCount += 8; } private void writeTagYRes(OutputStream result, int value1, int value2) throws IOException { // TAG-ID 11B result.write(1); result.write(27); // TAG-TYPE RATIONAL=5 result.write(0); result.write(5); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE=OFFSET TO RATIONAL int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size(); int i1 = valueOffset/16777216;//=value/(256*256*256) int i2 = (valueOffset-i1*16777216)/65536; int i3 = (valueOffset-i1*16777216-i2*65536)/256; int i4 = valueOffset % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); i1 = value1 /16777216; i2 = (value1-i1*16777216)/65536; i3 = (value1-i1*16777216 - i2*65536)/256; i4 = value1 % 256; tailer.write(i1); tailer.write(i2); tailer.write(i3); tailer.write(i4); i1 = value2 /16777216; i2 = (value2-i1*16777216)/65536; i3 = (value2-i1*16777216 - i2*65536)/256; i4 = value2 % 256; tailer.write(i1); tailer.write(i2); tailer.write(i3); tailer.write(i4); tailingBytesCount += 8; } private void writeTagResolutionUnit(OutputStream result, int value) throws IOException { // TAG-ID 128 result.write(1); result.write(40); // TAG-TYPE SHORT=3 result.write(0); result.write(3); // TAG-LENGTH = 1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(value/256); result.write(value%256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagOrientation(OutputStream result, int value) throws IOException { // TAG-ID 112 result.write(1); result.write(18); // TAG-TYPE SHORT = 3 result.write(0); result.write(3); // TAG-LENGTH=1 result.write(0); result.write(0); result.write(0); result.write(1); // TAG-VALUE result.write(value / 256); result.write(value % 256); result.write(0);// SHORT=0 result.write(0);// SHORT=0 } private void writeTagTailer(OutputStream result) throws IOException { // END OF TAG-DICTIONARY result.write(0); result.write(0); result.write(0); result.write(0); // TAILER WITH VALUES OF RATIONALFIELD's result.write(tailer.toByteArray()); } private void writeTagSoftware(OutputStream result, byte[] text) throws IOException { // TAG-ID 131 result.write(1); result.write(49); // TAG-TYPE ASCII=2 result.write(0); result.write(2); // TAG-LENGTH=id.length+1 result.write(0); result.write(0); result.write((text.length + 1) / 256); result.write((text.length + 1) % 256); // TAG-VALUE int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size(); int i1 = valueOffset/16777216;//=value/(256*256*256) int i2 = (valueOffset-i1*16777216)/65536; int i3 = (valueOffset-i1*16777216-i2*65536)/256; int i4 = valueOffset % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); tailer.write(text); tailer.write(0); tailingBytesCount += text.length + 1; } private void writeTagDateTime(OutputStream result, Date date) throws IOException { // TAG-ID 132 result.write(1); result.write(50); // TAG-TYPE ASCII=2 result.write(0); result.write(2); // TAG-LENGTH=20 result.write(0); result.write(0); result.write(0); result.write(20); // TAG-VALUE int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size(); int i1 = valueOffset/16777216;//=value/(256*256*256) int i2 = (valueOffset-i1*16777216)/65536; int i3 = (valueOffset-i1*16777216-i2*65536)/256; int i4 = valueOffset % 256; result.write(i1); result.write(i2); result.write(i3); result.write(i4); SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); String datetime = sdf.format(date); tailer.write(datetime.getBytes()); tailer.write(0); tailingBytesCount += 20; } /** * This will encode some data. * * @param rawData * The raw data to encode. * @param result * The place to write to encoded results to. * @param options * The options to use to encode the data. * * @throws IOException * If there is an error compressing the stream. */ public void encode(InputStream rawData, OutputStream result, COSDictionary options) throws IOException { log.warn("Warning: CCITTFaxDecode.encode is not implemented yet, skipping this stream."); } }