package com.tom_roush.pdfbox.filter; import android.util.Log; import com.tom_roush.pdfbox.cos.COSDictionary; import com.tom_roush.pdfbox.cos.COSName; import com.tom_roush.pdfbox.filter.ccitt.CCITTFaxG31DDecodeInputStream; import com.tom_roush.pdfbox.filter.ccitt.FillOrderChangeInputStream; import com.tom_roush.pdfbox.filter.ccitt.TIFFFaxDecoder; import com.tom_roush.pdfbox.io.IOUtils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Decodes image data that has been encoded using either Group 3 or Group 4 * CCITT facsimile (fax) encoding. * * @author Ben Litchfield * @author Marcel Kammer * @author Paul King */ final class CCITTFaxFilter extends Filter { @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { DecodeResult result = new DecodeResult(new COSDictionary()); result.getParameters().addAll(parameters); // get decode parameters COSDictionary decodeParms = getDecodeParams(parameters, index); // parse dimensions int cols = decodeParms.getInt(COSName.COLUMNS, 1728); int rows = decodeParms.getInt(COSName.ROWS, 0); int height = parameters.getInt(COSName.HEIGHT, COSName.H, 0); if (rows > 0 && height > 0) { // ensure that rows doesn't contain implausible data, see PDFBOX-771 rows = Math.min(rows, height); } else { // at least one of the values has to have a valid value rows = Math.max(rows, height); } // decompress data int k = decodeParms.getInt(COSName.K, 0); boolean encodedByteAlign = decodeParms.getBoolean(COSName.ENCODED_BYTE_ALIGN, false); int arraySize = (cols + 7) / 8 * rows; // TODO possible options?? long tiffOptions = 0; byte[] decompressed; if (k == 0) { InputStream in = new CCITTFaxG31DDecodeInputStream(encoded, cols, rows, encodedByteAlign); in = new FillOrderChangeInputStream(in); decompressed = IOUtils.toByteArray(in); in.close(); } else { TIFFFaxDecoder faxDecoder = new TIFFFaxDecoder(1, cols, rows); byte[] compressed = IOUtils.toByteArray(encoded); decompressed = new byte[arraySize]; if (k > 0) { faxDecoder.decode2D(decompressed, compressed, 0, rows, tiffOptions); } else { // k < 0 faxDecoder.decodeT6(decompressed, compressed, 0, rows, tiffOptions, encodedByteAlign); } } // invert bitmap boolean blackIsOne = decodeParms.getBoolean(COSName.BLACK_IS_1, false); if (!blackIsOne) { // Inverting the bitmap // Note the previous approach with starting from an IndexColorModel didn't work // reliably. In some cases the image wouldn't be painted for some reason. // So a safe but slower approach was taken. invertBitmap(decompressed); } // repair missing color space if (!parameters.containsKey(COSName.COLORSPACE)) { result.getParameters().setName(COSName.COLORSPACE, COSName.DEVICEGRAY.getName()); } decoded.write(decompressed); return new DecodeResult(parameters); } private void invertBitmap(byte[] bufferData) { for (int i = 0, c = bufferData.length; i < c; i++) { bufferData[i] = (byte) (~bufferData[i] & 0xFF); } } @Override protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { Log.w("PdfBox-Android", "CCITTFaxDecode.encode is not implemented yet, skipping this stream."); } }