From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../java/com/lowagie/text/pdf/codec/BmpImage.java | 1282 +++++++++ .../com/lowagie/text/pdf/codec/CCITTG4Encoder.java | 600 +++++ .../java/com/lowagie/text/pdf/codec/GifImage.java | 593 +++++ .../java/com/lowagie/text/pdf/codec/PngImage.java | 987 +++++++ .../com/lowagie/text/pdf/codec/TIFFConstants.java | 296 +++ .../com/lowagie/text/pdf/codec/TIFFDirectory.java | 656 +++++ .../com/lowagie/text/pdf/codec/TIFFFaxDecoder.java | 1477 +++++++++++ .../java/com/lowagie/text/pdf/codec/TIFFField.java | 472 ++++ .../com/lowagie/text/pdf/codec/TIFFLZWDecoder.java | 255 ++ .../java/com/lowagie/text/pdf/codec/TiffImage.java | 522 ++++ .../text/pdf/codec/postscript/JavaCharStream.java | 547 ++++ .../text/pdf/codec/postscript/MetaDoPS.java | 98 + .../text/pdf/codec/postscript/PACommand.java | 19 + .../text/pdf/codec/postscript/PAContext.java | 2772 ++++++++++++++++++++ .../text/pdf/codec/postscript/PAEngine.java | 155 ++ .../text/pdf/codec/postscript/PAParser.java | 351 +++ .../pdf/codec/postscript/PAParserConstants.java | 60 + .../pdf/codec/postscript/PAParserTokenManager.java | 1011 +++++++ .../text/pdf/codec/postscript/PAPencil.java | 431 +++ .../lowagie/text/pdf/codec/postscript/PAToken.java | 66 + .../pdf/codec/postscript/PainterException.java | 20 + .../text/pdf/codec/postscript/ParseException.java | 192 ++ .../lowagie/text/pdf/codec/postscript/Token.java | 81 + .../text/pdf/codec/postscript/TokenMgrError.java | 133 + .../com/lowagie/text/pdf/codec/wmf/InputMeta.java | 112 + .../com/lowagie/text/pdf/codec/wmf/MetaBrush.java | 94 + .../com/lowagie/text/pdf/codec/wmf/MetaDo.java | 760 ++++++ .../com/lowagie/text/pdf/codec/wmf/MetaFont.java | 211 ++ .../com/lowagie/text/pdf/codec/wmf/MetaObject.java | 71 + .../com/lowagie/text/pdf/codec/wmf/MetaPen.java | 91 + .../com/lowagie/text/pdf/codec/wmf/MetaState.java | 372 +++ 31 files changed, 14787 insertions(+) create mode 100644 src/main/java/com/lowagie/text/pdf/codec/BmpImage.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/CCITTG4Encoder.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/GifImage.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/PngImage.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TIFFDirectory.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TIFFField.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/TiffImage.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAEngine.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAParser.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserConstants.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserTokenManager.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAPencil.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PAToken.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/PainterException.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/ParseException.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/Token.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/postscript/TokenMgrError.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/InputMeta.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaBrush.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaDo.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaFont.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaObject.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaPen.java create mode 100644 src/main/java/com/lowagie/text/pdf/codec/wmf/MetaState.java (limited to 'src/main/java/com/lowagie/text/pdf/codec') diff --git a/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java new file mode 100644 index 0000000..f9f80be --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java @@ -0,0 +1,1282 @@ +/* + * Copyright 2003 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/ + * + * + * The original JAI codecs have the following license + * + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; + +import com.lowagie.text.pdf.*; +import com.lowagie.text.*; +import java.io.*; +import java.util.HashMap; +import java.net.URL; + +/** 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 InputStream inputStream; + private long bitmapFileSize; + private long bitmapOffset; + private long compression; + private long imageSize; + private byte palette[]; + private int imageType; + private int numBands; + private boolean isBottomUp; + private int bitsPerPixel; + private int redMask, greenMask, blueMask, alphaMask; + public HashMap properties = new HashMap(); + private long xPelsPerMeter; + private long yPelsPerMeter; + // BMP Image types + private static final int VERSION_2_1_BIT = 0; + private static final int VERSION_2_4_BIT = 1; + private static final int VERSION_2_8_BIT = 2; + private static final int VERSION_2_24_BIT = 3; + + private static final int VERSION_3_1_BIT = 4; + private static final int VERSION_3_4_BIT = 5; + private static final int VERSION_3_8_BIT = 6; + private static final int VERSION_3_24_BIT = 7; + + private static final int VERSION_3_NT_16_BIT = 8; + private static final int VERSION_3_NT_32_BIT = 9; + + private static final int VERSION_4_1_BIT = 10; + private static final int VERSION_4_4_BIT = 11; + private static final int VERSION_4_8_BIT = 12; + private static final int VERSION_4_16_BIT = 13; + private static final int VERSION_4_24_BIT = 14; + private static final int VERSION_4_32_BIT = 15; + + // Color space types + private static final int LCS_CALIBRATED_RGB = 0; + private static final int LCS_sRGB = 1; + private static final int LCS_CMYK = 2; + + // Compression Types + private static final int BI_RGB = 0; + private static final int BI_RLE8 = 1; + private static final int BI_RLE4 = 2; + private static final int BI_BITFIELDS = 3; + + int width; + int height; + + BmpImage(InputStream is, boolean noHeader, int size) throws IOException { + bitmapFileSize = size; + bitmapOffset = 0; + process(is, noHeader); + } + + /** Reads a BMP from an url. + * @param url the url + * @throws IOException on error + * @return the image + */ + public static Image getImage(URL url) throws IOException { + InputStream is = null; + try { + is = url.openStream(); + Image img = getImage(is); + img.setUrl(url); + return img; + } + finally { + if (is != null) { + is.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(InputStream is) throws IOException { + return getImage(is, 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(InputStream is, boolean noHeader, int size) throws IOException { + BmpImage bmp = new BmpImage(is, noHeader, size); + try { + Image img = bmp.getImage(); + img.setDpi((int)((double)bmp.xPelsPerMeter * 0.0254), (int)((double)bmp.yPelsPerMeter * 0.0254)); + img.setOriginalType(Image.ORIGINAL_BMP); + return img; + } + catch (BadElementException be) { + throw new ExceptionConverter(be); + } + } + + /** Reads a BMP from a file. + * @param file the file + * @throws IOException on error + * @return the image + */ + public static Image getImage(String file) throws IOException { + return getImage(Image.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[]) throws IOException { + InputStream is = null; + try { + is = new ByteArrayInputStream(data); + Image img = getImage(is); + img.setOriginalData(data); + return img; + } + finally { + if (is != null) { + is.close(); + } + } + } + + + protected void process(InputStream stream, boolean noHeader) throws IOException { + if (noHeader || stream instanceof BufferedInputStream) { + inputStream = stream; + } else { + inputStream = new BufferedInputStream(stream); + } + if (!noHeader) { + // Start File Header + if (!(readUnsignedByte(inputStream) == 'B' && + readUnsignedByte(inputStream) == 'M')) { + throw new + RuntimeException("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.put("color_planes", new Integer(planes)); + properties.put("bits_per_pixel", new Integer(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.put("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; + } + palette = new byte[sizeOfPalette]; + inputStream.read(palette, 0, sizeOfPalette); + properties.put("palette", palette); + } 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.put("compression", "BI_RGB"); + break; + + case BI_RLE8: + properties.put("compression", "BI_RLE8"); + break; + + case BI_RLE4: + properties.put("compression", "BI_RLE4"); + break; + + case BI_BITFIELDS: + properties.put("compression", "BI_BITFIELDS"); + break; + } + + properties.put("x_pixels_per_meter", new Long(xPelsPerMeter)); + properties.put("y_pixels_per_meter", new Long(yPelsPerMeter)); + properties.put("colors_used", new Long(colorsUsed)); + properties.put("colors_important", new Long(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.put("red_mask", new Integer(redMask)); + properties.put("green_mask", new Integer(greenMask)); + properties.put("blue_mask", new Integer(blueMask)); + } else if (bitsPerPixel == 32) { + imageType = VERSION_3_NT_32_BIT; + redMask = 0x00FF0000; + greenMask = 0x0000FF00; + blueMask = 0x000000FF; + properties.put("red_mask", new Integer(redMask)); + properties.put("green_mask", new Integer(greenMask)); + properties.put("blue_mask", new Integer(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; + } + palette = new byte[sizeOfPalette]; + inputStream.read(palette, 0, sizeOfPalette); + properties.put("palette", palette); + + properties.put("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.put("red_mask", new Integer(redMask)); + properties.put("green_mask", new Integer(greenMask)); + properties.put("blue_mask", new Integer(blueMask)); + + if (colorsUsed != 0) { + // there is a palette + sizeOfPalette = (int)colorsUsed*4; + palette = new byte[sizeOfPalette]; + inputStream.read(palette, 0, sizeOfPalette); + properties.put("palette", palette); + } + + properties.put("bmp_version", "BMP v. 3.x NT"); + break; + + default: + throw new + RuntimeException("Invalid compression specified in BMP file."); + } + } else if (size == 108) { + // Windows 4.x BMP + + properties.put("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.put("red_mask", new Integer(redMask)); + properties.put("green_mask", new Integer(greenMask)); + properties.put("blue_mask", new Integer(blueMask)); + properties.put("alpha_mask", new Integer(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; + } + palette = new byte[sizeOfPalette]; + inputStream.read(palette, 0, sizeOfPalette); + + if (palette != null || palette.length != 0) { + properties.put("palette", palette); + } + + switch((int)csType) { + case LCS_CALIBRATED_RGB: + // All the new fields are valid only for this case + properties.put("color_space", "LCS_CALIBRATED_RGB"); + properties.put("redX", new Integer(redX)); + properties.put("redY", new Integer(redY)); + properties.put("redZ", new Integer(redZ)); + properties.put("greenX", new Integer(greenX)); + properties.put("greenY", new Integer(greenY)); + properties.put("greenZ", new Integer(greenZ)); + properties.put("blueX", new Integer(blueX)); + properties.put("blueY", new Integer(blueY)); + properties.put("blueZ", new Integer(blueZ)); + properties.put("gamma_red", new Long(gammaRed)); + properties.put("gamma_green", new Long(gammaGreen)); + properties.put("gamma_blue", new Long(gammaBlue)); + + // break; + throw new + RuntimeException("Not implemented yet."); + + case LCS_sRGB: + // Default Windows color space + properties.put("color_space", "LCS_sRGB"); + break; + + case LCS_CMYK: + properties.put("color_space", "LCS_CMYK"); + // break; + throw new + RuntimeException("Not implemented yet."); + } + + } else { + properties.put("bmp_version", "BMP v. 5.x"); + throw new + RuntimeException("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[], g[], 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 256) { + sizep = 256; + } + + int off; + r = new byte[sizep]; + g = new byte[sizep]; + b = new byte[sizep]; + for (int i=0; i>>= 1; + } + return mask; + } + + private int findShift(int mask) { + int k = 0; + for (; k < 32; ++k) { + if ((mask & 1) == 1) + break; + mask >>>= 1; + } + return k; + } + + private Image read1632Bit(boolean is32) throws IOException, BadElementException { + + int red_mask = findMask(redMask); + int red_shift = findShift(redMask); + int red_factor = red_mask + 1; + int green_mask = findMask(greenMask); + int green_shift = findShift(greenMask); + int green_factor = green_mask + 1; + int blue_mask = findMask(blueMask); + int blue_shift = findShift(blueMask); + int blue_factor = blue_mask + 1; + byte bdata[] = new byte[width * height * 3]; + // Padding bytes at the end of each scanline + int padding = 0; + + if (!is32) { + // width * bitsPerPixel should be divisible by 32 + int bitsPerScanline = width * 16; + if ( bitsPerScanline%32 != 0) { + padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline; + padding = (int)Math.ceil(padding/8.0); + } + } + + int imSize = (int)imageSize; + if (imSize == 0) { + imSize = (int)(bitmapFileSize - bitmapOffset); + } + + int l=0; + int v; + if (isBottomUp) { + for (int i=height - 1; i >= 0; --i) { + l = width * 3 * i; + for (int j=0; j>> red_shift) & red_mask) * 256 / red_factor); + bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor); + bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor); + } + for (int m=0; m>> red_shift) & red_mask) * 256 / red_factor); + bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor); + bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor); + } + for (int m=0; m= 0; i--) { + index = i * width; + lineEnd = l + width; + while(l != lineEnd) { + val[l++] = inverted[index++]; + } + } + } + int stride = ((width + 1) / 2); + byte bdata[] = new byte[stride * height]; + int ptr = 0; + int sh = 0; + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + if ((w & 1) == 0) + bdata[sh + w / 2] = (byte)(val[ptr++] << 4); + else + bdata[sh + w / 2] |= (byte)(val[ptr++] & 0x0f); + } + sh += stride; + } + return indexedModel(bdata, 4, 4); + } + + private byte[] decodeRLE(boolean is8, byte values[]) { + byte val[] = new byte[width * height]; + try { + int ptr = 0; + int x = 0; + int q = 0; + for (int y = 0; y < height && ptr < values.length;) { + int count = values[ptr++] & 0xff; + if (count != 0) { + // encoded mode + int bt = values[ptr++] & 0xff; + if (is8) { + for (int i = count; i != 0; --i) { + val[q++] = (byte)bt; + } + } + else { + for (int i = 0; i < count; ++i) { + val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f)); + } + } + x += count; + } + else { + // escape mode + count = values[ptr++] & 0xff; + if (count == 1) + break; + switch (count) { + case 0: + x = 0; + ++y; + q = y * width; + break; + case 2: + // delta mode + x += values[ptr++] & 0xff; + y += values[ptr++] & 0xff; + q = y * width + x; + break; + default: + // absolute mode + if (is8) { + for (int i = count; i != 0; --i) + val[q++] = (byte)(values[ptr++] & 0xff); + } + else { + int bt = 0; + for (int i = 0; i < count; ++i) { + if ((i & 1) == 0) + bt = values[ptr++] & 0xff; + val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f)); + } + } + x += count; + // read pad byte + if (is8) { + if ((count & 1) == 1) + ++ptr; + } + else { + if ((count & 3) == 1 || (count & 3) == 2) + ++ptr; + } + break; + } + } + } + } + catch (Exception e) { + //empty on purpose + } + + return val; + } + + // Windows defined data type reading methods - everything is little endian + + // Unsigned 8 bits + private int readUnsignedByte(InputStream stream) throws IOException { + return (stream.read() & 0xff); + } + + // Unsigned 2 bytes + private int readUnsignedShort(InputStream stream) throws IOException { + int b1 = readUnsignedByte(stream); + int b2 = readUnsignedByte(stream); + return ((b2 << 8) | b1) & 0xffff; + } + + // Signed 16 bits + private int readShort(InputStream stream) throws IOException { + int b1 = readUnsignedByte(stream); + int b2 = readUnsignedByte(stream); + return (b2 << 8) | b1; + } + + // Unsigned 16 bits + private int readWord(InputStream stream) throws IOException { + return readUnsignedShort(stream); + } + + // Unsigned 4 bytes + private long readUnsignedInt(InputStream stream) throws IOException { + int b1 = readUnsignedByte(stream); + int b2 = readUnsignedByte(stream); + int b3 = readUnsignedByte(stream); + int b4 = readUnsignedByte(stream); + long l = (long)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); + return l & 0xffffffff; + } + + // Signed 4 bytes + private int readInt(InputStream stream) throws IOException { + int b1 = readUnsignedByte(stream); + int b2 = readUnsignedByte(stream); + int b3 = readUnsignedByte(stream); + int b4 = readUnsignedByte(stream); + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } + + // Unsigned 4 bytes + private long readDWord(InputStream stream) throws IOException { + return readUnsignedInt(stream); + } + + // 32 bit signed value + private int readLong(InputStream stream) throws IOException { + return readInt(stream); + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/CCITTG4Encoder.java b/src/main/java/com/lowagie/text/pdf/codec/CCITTG4Encoder.java new file mode 100644 index 0000000..ae67be1 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/CCITTG4Encoder.java @@ -0,0 +1,600 @@ +/* + * Copyright 2005 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 is base in the libtiff encoder + */ +package com.lowagie.text.pdf.codec; + +import com.lowagie.text.pdf.ByteBuffer; + +/** + * Encodes data in the CCITT G4 FAX format. + */ +public class CCITTG4Encoder { + private int rowbytes; + private int rowpixels; + private int bit = 8; + private int data; + private byte[] refline; + private ByteBuffer outBuf = new ByteBuffer(1024); + private byte[] dataBp; + private int offsetData; + private int sizeData; + + /** + * Creates a new encoder. + * @param width the line width + */ + public CCITTG4Encoder(int width) { + rowpixels = width; + rowbytes = (rowpixels + 7) / 8; + refline = new byte[rowbytes]; + } + + /** + * Encodes a number of lines. + * @param data the data to be encoded + * @param offset the offset into the data + * @param size the size of the data to be encoded + */ + public void fax4Encode(byte[] data, int offset, int size) { + dataBp = data; + offsetData = offset; + sizeData = size; + while (sizeData > 0) { + Fax3Encode2DRow(); + System.arraycopy(dataBp, offsetData, refline, 0, rowbytes); + offsetData += rowbytes; + sizeData -= rowbytes; + } + } + + + /** + * Encodes a full image. + * @param data the data to encode + * @param width the image width + * @param height the image height + * @return the encoded image + */ + public static byte[] compress(byte[] data, int width, int height) { + CCITTG4Encoder g4 = new CCITTG4Encoder(width); + g4.fax4Encode(data, 0, g4.rowbytes * height); + return g4.close(); + } + + /** + * Encodes a number of lines. + * @param data the data to be encoded + * @param height the number of lines to encode + */ + public void fax4Encode(byte[] data, int height) { + fax4Encode(data, 0, rowbytes * height); + } + + private void putcode(int[] table) { + putBits(table[CODE], table[LENGTH]); + } + + private void putspan(int span, int[][] tab) { + int code, length; + + while (span >= 2624) { + int[] te = tab[63 + (2560>>6)]; + code = te[CODE]; + length = te[LENGTH]; + putBits(code, length); + span -= te[RUNLEN]; + } + if (span >= 64) { + int[] te = tab[63 + (span>>6)]; + code = te[CODE]; + length = te[LENGTH]; + putBits(code, length); + span -= te[RUNLEN]; + } + code = tab[span][CODE]; + length = tab[span][LENGTH]; + putBits(code, length); + } + + private void putBits(int bits, int length) { + while (length > bit) { + data |= bits >> (length - bit); + length -= bit; + outBuf.append((byte)data); + data = 0; + bit = 8; + } + data |= (bits & msbmask[length]) << (bit - length); + bit -= length; + if (bit == 0) { + outBuf.append((byte)data); + data = 0; + bit = 8; + } + } + + private void Fax3Encode2DRow() { + int a0 = 0; + int a1 = (pixel(dataBp, offsetData, 0) != 0 ? 0 : finddiff(dataBp, offsetData, 0, rowpixels, 0)); + int b1 = (pixel(refline, 0, 0) != 0 ? 0 : finddiff(refline, 0, 0, rowpixels, 0)); + int a2, b2; + + for (;;) { + b2 = finddiff2(refline, 0, b1, rowpixels, pixel(refline, 0,b1)); + if (b2 >= a1) { + int d = b1 - a1; + if (!(-3 <= d && d <= 3)) { /* horizontal mode */ + a2 = finddiff2(dataBp, offsetData, a1, rowpixels, pixel(dataBp, offsetData,a1)); + putcode(horizcode); + if (a0+a1 == 0 || pixel(dataBp, offsetData, a0) == 0) { + putspan(a1-a0, TIFFFaxWhiteCodes); + putspan(a2-a1, TIFFFaxBlackCodes); + } else { + putspan(a1-a0, TIFFFaxBlackCodes); + putspan(a2-a1, TIFFFaxWhiteCodes); + } + a0 = a2; + } else { /* vertical mode */ + putcode(vcodes[d+3]); + a0 = a1; + } + } else { /* pass mode */ + putcode(passcode); + a0 = b2; + } + if (a0 >= rowpixels) + break; + a1 = finddiff(dataBp, offsetData, a0, rowpixels, pixel(dataBp, offsetData,a0)); + b1 = finddiff(refline, 0, a0, rowpixels, pixel(dataBp, offsetData,a0) ^ 1); + b1 = finddiff(refline, 0, b1, rowpixels, pixel(dataBp, offsetData,a0)); + } + } + + private void Fax4PostEncode() { + putBits(EOL, 12); + putBits(EOL, 12); + if (bit != 8) { + outBuf.append((byte)data); + data = 0; + bit = 8; + } + } + + /** + * Closes the encoder and returns the encoded data. + * @return the encoded data + */ + public byte[] close() { + Fax4PostEncode(); + return outBuf.toByteArray(); + } + + private int pixel(byte[] data, int offset, int bit) { + if (bit >= rowpixels) + return 0; + return ((data[offset + (bit >> 3)] & 0xff) >> (7-((bit)&7))) & 1; + } + + private static int find1span(byte[] bp, int offset, int bs, int be) { + int bits = be - bs; + int n, span; + + int pos = offset + (bs >> 3); + /* + * Check partial byte on lhs. + */ + if (bits > 0 && (n = (bs & 7)) != 0) { + span = oneruns[((int)bp[pos] << n) & 0xff]; + if (span > 8-n) /* table value too generous */ + span = 8-n; + if (span > bits) /* constrain span to bit range */ + span = bits; + if (n+span < 8) /* doesn't extend to edge of byte */ + return (span); + bits -= span; + pos++; + } else + span = 0; + /* + * Scan full bytes for all 1's. + */ + while (bits >= 8) { + if (bp[pos] != -1) /* end of run */ + return (span + oneruns[bp[pos] & 0xff]); + span += 8; + bits -= 8; + pos++; + } + /* + * Check partial byte on rhs. + */ + if (bits > 0) { + n = oneruns[bp[pos] & 0xff]; + span += (n > bits ? bits : n); + } + return (span); + } + + private static int find0span(byte[] bp, int offset, int bs, int be) { + int bits = be - bs; + int n, span; + + int pos = offset + (bs >> 3); + /* + * Check partial byte on lhs. + */ + if (bits > 0 && (n = (bs & 7)) != 0) { + span = zeroruns[((int)bp[pos] << n) & 0xff]; + if (span > 8-n) /* table value too generous */ + span = 8-n; + if (span > bits) /* constrain span to bit range */ + span = bits; + if (n+span < 8) /* doesn't extend to edge of byte */ + return (span); + bits -= span; + pos++; + } else + span = 0; + /* + * Scan full bytes for all 1's. + */ + while (bits >= 8) { + if (bp[pos] != 0) /* end of run */ + return (span + zeroruns[bp[pos] & 0xff]); + span += 8; + bits -= 8; + pos++; + } + /* + * Check partial byte on rhs. + */ + if (bits > 0) { + n = zeroruns[bp[pos] & 0xff]; + span += (n > bits ? bits : n); + } + return (span); + } + + private static int finddiff(byte[] bp, int offset, int bs, int be, int color) { + return bs + (color != 0 ? find1span(bp, offset, bs, be) : find0span(bp, offset, bs, be)); + } + + private static int finddiff2(byte[] bp, int offset, int bs, int be, int color) { + return bs < be ? finddiff(bp, offset, bs, be, color) : be; + } + + private static byte zeroruns[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */ + }; + + private static byte oneruns[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8 /* 0xf0 - 0xff */ + }; + + private static final int LENGTH = 0; /* bit length of g3 code */ + private static final int CODE = 1; /* g3 code */ + private static final int RUNLEN = 2; /* run length in bits */ + + private static final int EOL = 0x001; /* EOL code value - 0000 0000 0000 1 */ + + /* status values returned instead of a run length */ + private static final int G3CODE_EOL = -1; /* NB: ACT_EOL - ACT_WRUNT */ + private static final int G3CODE_INVALID = -2; /* NB: ACT_INVALID - ACT_WRUNT */ + private static final int G3CODE_EOF = -3; /* end of input data */ + private static final int G3CODE_INCOMP = -4; /* incomplete run code */ + + private int[][] TIFFFaxWhiteCodes = { + { 8, 0x35, 0 }, /* 0011 0101 */ + { 6, 0x7, 1 }, /* 0001 11 */ + { 4, 0x7, 2 }, /* 0111 */ + { 4, 0x8, 3 }, /* 1000 */ + { 4, 0xB, 4 }, /* 1011 */ + { 4, 0xC, 5 }, /* 1100 */ + { 4, 0xE, 6 }, /* 1110 */ + { 4, 0xF, 7 }, /* 1111 */ + { 5, 0x13, 8 }, /* 1001 1 */ + { 5, 0x14, 9 }, /* 1010 0 */ + { 5, 0x7, 10 }, /* 0011 1 */ + { 5, 0x8, 11 }, /* 0100 0 */ + { 6, 0x8, 12 }, /* 0010 00 */ + { 6, 0x3, 13 }, /* 0000 11 */ + { 6, 0x34, 14 }, /* 1101 00 */ + { 6, 0x35, 15 }, /* 1101 01 */ + { 6, 0x2A, 16 }, /* 1010 10 */ + { 6, 0x2B, 17 }, /* 1010 11 */ + { 7, 0x27, 18 }, /* 0100 111 */ + { 7, 0xC, 19 }, /* 0001 100 */ + { 7, 0x8, 20 }, /* 0001 000 */ + { 7, 0x17, 21 }, /* 0010 111 */ + { 7, 0x3, 22 }, /* 0000 011 */ + { 7, 0x4, 23 }, /* 0000 100 */ + { 7, 0x28, 24 }, /* 0101 000 */ + { 7, 0x2B, 25 }, /* 0101 011 */ + { 7, 0x13, 26 }, /* 0010 011 */ + { 7, 0x24, 27 }, /* 0100 100 */ + { 7, 0x18, 28 }, /* 0011 000 */ + { 8, 0x2, 29 }, /* 0000 0010 */ + { 8, 0x3, 30 }, /* 0000 0011 */ + { 8, 0x1A, 31 }, /* 0001 1010 */ + { 8, 0x1B, 32 }, /* 0001 1011 */ + { 8, 0x12, 33 }, /* 0001 0010 */ + { 8, 0x13, 34 }, /* 0001 0011 */ + { 8, 0x14, 35 }, /* 0001 0100 */ + { 8, 0x15, 36 }, /* 0001 0101 */ + { 8, 0x16, 37 }, /* 0001 0110 */ + { 8, 0x17, 38 }, /* 0001 0111 */ + { 8, 0x28, 39 }, /* 0010 1000 */ + { 8, 0x29, 40 }, /* 0010 1001 */ + { 8, 0x2A, 41 }, /* 0010 1010 */ + { 8, 0x2B, 42 }, /* 0010 1011 */ + { 8, 0x2C, 43 }, /* 0010 1100 */ + { 8, 0x2D, 44 }, /* 0010 1101 */ + { 8, 0x4, 45 }, /* 0000 0100 */ + { 8, 0x5, 46 }, /* 0000 0101 */ + { 8, 0xA, 47 }, /* 0000 1010 */ + { 8, 0xB, 48 }, /* 0000 1011 */ + { 8, 0x52, 49 }, /* 0101 0010 */ + { 8, 0x53, 50 }, /* 0101 0011 */ + { 8, 0x54, 51 }, /* 0101 0100 */ + { 8, 0x55, 52 }, /* 0101 0101 */ + { 8, 0x24, 53 }, /* 0010 0100 */ + { 8, 0x25, 54 }, /* 0010 0101 */ + { 8, 0x58, 55 }, /* 0101 1000 */ + { 8, 0x59, 56 }, /* 0101 1001 */ + { 8, 0x5A, 57 }, /* 0101 1010 */ + { 8, 0x5B, 58 }, /* 0101 1011 */ + { 8, 0x4A, 59 }, /* 0100 1010 */ + { 8, 0x4B, 60 }, /* 0100 1011 */ + { 8, 0x32, 61 }, /* 0011 0010 */ + { 8, 0x33, 62 }, /* 0011 0011 */ + { 8, 0x34, 63 }, /* 0011 0100 */ + { 5, 0x1B, 64 }, /* 1101 1 */ + { 5, 0x12, 128 }, /* 1001 0 */ + { 6, 0x17, 192 }, /* 0101 11 */ + { 7, 0x37, 256 }, /* 0110 111 */ + { 8, 0x36, 320 }, /* 0011 0110 */ + { 8, 0x37, 384 }, /* 0011 0111 */ + { 8, 0x64, 448 }, /* 0110 0100 */ + { 8, 0x65, 512 }, /* 0110 0101 */ + { 8, 0x68, 576 }, /* 0110 1000 */ + { 8, 0x67, 640 }, /* 0110 0111 */ + { 9, 0xCC, 704 }, /* 0110 0110 0 */ + { 9, 0xCD, 768 }, /* 0110 0110 1 */ + { 9, 0xD2, 832 }, /* 0110 1001 0 */ + { 9, 0xD3, 896 }, /* 0110 1001 1 */ + { 9, 0xD4, 960 }, /* 0110 1010 0 */ + { 9, 0xD5, 1024 }, /* 0110 1010 1 */ + { 9, 0xD6, 1088 }, /* 0110 1011 0 */ + { 9, 0xD7, 1152 }, /* 0110 1011 1 */ + { 9, 0xD8, 1216 }, /* 0110 1100 0 */ + { 9, 0xD9, 1280 }, /* 0110 1100 1 */ + { 9, 0xDA, 1344 }, /* 0110 1101 0 */ + { 9, 0xDB, 1408 }, /* 0110 1101 1 */ + { 9, 0x98, 1472 }, /* 0100 1100 0 */ + { 9, 0x99, 1536 }, /* 0100 1100 1 */ + { 9, 0x9A, 1600 }, /* 0100 1101 0 */ + { 6, 0x18, 1664 }, /* 0110 00 */ + { 9, 0x9B, 1728 }, /* 0100 1101 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID } /* 0000 0000 0000 */ + }; + + private int[][] TIFFFaxBlackCodes = { + { 10, 0x37, 0 }, /* 0000 1101 11 */ + { 3, 0x2, 1 }, /* 010 */ + { 2, 0x3, 2 }, /* 11 */ + { 2, 0x2, 3 }, /* 10 */ + { 3, 0x3, 4 }, /* 011 */ + { 4, 0x3, 5 }, /* 0011 */ + { 4, 0x2, 6 }, /* 0010 */ + { 5, 0x3, 7 }, /* 0001 1 */ + { 6, 0x5, 8 }, /* 0001 01 */ + { 6, 0x4, 9 }, /* 0001 00 */ + { 7, 0x4, 10 }, /* 0000 100 */ + { 7, 0x5, 11 }, /* 0000 101 */ + { 7, 0x7, 12 }, /* 0000 111 */ + { 8, 0x4, 13 }, /* 0000 0100 */ + { 8, 0x7, 14 }, /* 0000 0111 */ + { 9, 0x18, 15 }, /* 0000 1100 0 */ + { 10, 0x17, 16 }, /* 0000 0101 11 */ + { 10, 0x18, 17 }, /* 0000 0110 00 */ + { 10, 0x8, 18 }, /* 0000 0010 00 */ + { 11, 0x67, 19 }, /* 0000 1100 111 */ + { 11, 0x68, 20 }, /* 0000 1101 000 */ + { 11, 0x6C, 21 }, /* 0000 1101 100 */ + { 11, 0x37, 22 }, /* 0000 0110 111 */ + { 11, 0x28, 23 }, /* 0000 0101 000 */ + { 11, 0x17, 24 }, /* 0000 0010 111 */ + { 11, 0x18, 25 }, /* 0000 0011 000 */ + { 12, 0xCA, 26 }, /* 0000 1100 1010 */ + { 12, 0xCB, 27 }, /* 0000 1100 1011 */ + { 12, 0xCC, 28 }, /* 0000 1100 1100 */ + { 12, 0xCD, 29 }, /* 0000 1100 1101 */ + { 12, 0x68, 30 }, /* 0000 0110 1000 */ + { 12, 0x69, 31 }, /* 0000 0110 1001 */ + { 12, 0x6A, 32 }, /* 0000 0110 1010 */ + { 12, 0x6B, 33 }, /* 0000 0110 1011 */ + { 12, 0xD2, 34 }, /* 0000 1101 0010 */ + { 12, 0xD3, 35 }, /* 0000 1101 0011 */ + { 12, 0xD4, 36 }, /* 0000 1101 0100 */ + { 12, 0xD5, 37 }, /* 0000 1101 0101 */ + { 12, 0xD6, 38 }, /* 0000 1101 0110 */ + { 12, 0xD7, 39 }, /* 0000 1101 0111 */ + { 12, 0x6C, 40 }, /* 0000 0110 1100 */ + { 12, 0x6D, 41 }, /* 0000 0110 1101 */ + { 12, 0xDA, 42 }, /* 0000 1101 1010 */ + { 12, 0xDB, 43 }, /* 0000 1101 1011 */ + { 12, 0x54, 44 }, /* 0000 0101 0100 */ + { 12, 0x55, 45 }, /* 0000 0101 0101 */ + { 12, 0x56, 46 }, /* 0000 0101 0110 */ + { 12, 0x57, 47 }, /* 0000 0101 0111 */ + { 12, 0x64, 48 }, /* 0000 0110 0100 */ + { 12, 0x65, 49 }, /* 0000 0110 0101 */ + { 12, 0x52, 50 }, /* 0000 0101 0010 */ + { 12, 0x53, 51 }, /* 0000 0101 0011 */ + { 12, 0x24, 52 }, /* 0000 0010 0100 */ + { 12, 0x37, 53 }, /* 0000 0011 0111 */ + { 12, 0x38, 54 }, /* 0000 0011 1000 */ + { 12, 0x27, 55 }, /* 0000 0010 0111 */ + { 12, 0x28, 56 }, /* 0000 0010 1000 */ + { 12, 0x58, 57 }, /* 0000 0101 1000 */ + { 12, 0x59, 58 }, /* 0000 0101 1001 */ + { 12, 0x2B, 59 }, /* 0000 0010 1011 */ + { 12, 0x2C, 60 }, /* 0000 0010 1100 */ + { 12, 0x5A, 61 }, /* 0000 0101 1010 */ + { 12, 0x66, 62 }, /* 0000 0110 0110 */ + { 12, 0x67, 63 }, /* 0000 0110 0111 */ + { 10, 0xF, 64 }, /* 0000 0011 11 */ + { 12, 0xC8, 128 }, /* 0000 1100 1000 */ + { 12, 0xC9, 192 }, /* 0000 1100 1001 */ + { 12, 0x5B, 256 }, /* 0000 0101 1011 */ + { 12, 0x33, 320 }, /* 0000 0011 0011 */ + { 12, 0x34, 384 }, /* 0000 0011 0100 */ + { 12, 0x35, 448 }, /* 0000 0011 0101 */ + { 13, 0x6C, 512 }, /* 0000 0011 0110 0 */ + { 13, 0x6D, 576 }, /* 0000 0011 0110 1 */ + { 13, 0x4A, 640 }, /* 0000 0010 0101 0 */ + { 13, 0x4B, 704 }, /* 0000 0010 0101 1 */ + { 13, 0x4C, 768 }, /* 0000 0010 0110 0 */ + { 13, 0x4D, 832 }, /* 0000 0010 0110 1 */ + { 13, 0x72, 896 }, /* 0000 0011 1001 0 */ + { 13, 0x73, 960 }, /* 0000 0011 1001 1 */ + { 13, 0x74, 1024 }, /* 0000 0011 1010 0 */ + { 13, 0x75, 1088 }, /* 0000 0011 1010 1 */ + { 13, 0x76, 1152 }, /* 0000 0011 1011 0 */ + { 13, 0x77, 1216 }, /* 0000 0011 1011 1 */ + { 13, 0x52, 1280 }, /* 0000 0010 1001 0 */ + { 13, 0x53, 1344 }, /* 0000 0010 1001 1 */ + { 13, 0x54, 1408 }, /* 0000 0010 1010 0 */ + { 13, 0x55, 1472 }, /* 0000 0010 1010 1 */ + { 13, 0x5A, 1536 }, /* 0000 0010 1101 0 */ + { 13, 0x5B, 1600 }, /* 0000 0010 1101 1 */ + { 13, 0x64, 1664 }, /* 0000 0011 0010 0 */ + { 13, 0x65, 1728 }, /* 0000 0011 0010 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID } /* 0000 0000 0000 */ + }; + + private int[] horizcode = + { 3, 0x1, 0 }; /* 001 */ + private int[] passcode = + { 4, 0x1, 0 }; /* 0001 */ + private int[][] vcodes = { + { 7, 0x03, 0 }, /* 0000 011 */ + { 6, 0x03, 0 }, /* 0000 11 */ + { 3, 0x03, 0 }, /* 011 */ + { 1, 0x1, 0 }, /* 1 */ + { 3, 0x2, 0 }, /* 010 */ + { 6, 0x02, 0 }, /* 0000 10 */ + { 7, 0x02, 0 } /* 0000 010 */ + }; + private int[] msbmask = + { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/GifImage.java b/src/main/java/com/lowagie/text/pdf/codec/GifImage.java new file mode 100644 index 0000000..f6858ac --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/GifImage.java @@ -0,0 +1,593 @@ +/* + * Copyright 2003 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/ + */ +package com.lowagie.text.pdf.codec; + +import com.lowagie.text.pdf.*; +import com.lowagie.text.*; +import java.io.*; +import java.net.URL; +import java.util.ArrayList; + +/** Reads gif images of all types. All the images in a gif are read in the constructors + * and can be retrieved with other methods. + * @author Paulo Soares (psoares@consiste.pt) + */ +public class GifImage { + + protected DataInputStream in; + protected int width; // full image width + protected int height; // full image height + protected boolean gctFlag; // global color table used + + protected int bgIndex; // background color index + protected int bgColor; // background color + protected int pixelAspect; // pixel aspect ratio + + protected boolean lctFlag; // local color table flag + protected boolean interlace; // interlace flag + protected int lctSize; // local color table size + + protected int ix, iy, iw, ih; // current image rectangle + + protected byte[] block = new byte[256]; // current data block + protected int blockSize = 0; // block size + + // last graphic control extension info + protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev + protected boolean transparency = false; // use transparent color + protected int delay = 0; // delay in milliseconds + protected int transIndex; // transparent color index + + protected static final int MaxStackSize = 4096; // max decoder pixel stack size + + // LZW decoder working arrays + protected short[] prefix; + protected byte[] suffix; + protected byte[] pixelStack; + protected byte[] pixels; + + protected byte m_out[]; + protected int m_bpc; + protected int m_gbpc; + protected byte m_global_table[]; + protected byte m_local_table[]; + protected byte m_curr_table[]; + protected int m_line_stride; + protected byte fromData[]; + protected URL fromUrl; + + + protected ArrayList frames = new ArrayList(); // frames read from current file + + /** Reads gif images from an URL. + * @param url the URL + * @throws IOException on error + */ + public GifImage(URL url) throws IOException { + fromUrl = url; + InputStream is = null; + try { + is = url.openStream(); + process(is); + } + finally { + if (is != null) { + is.close(); + } + } + } + + /** Reads gif images from a file. + * @param file the file + * @throws IOException on error + */ + public GifImage(String file) throws IOException { + this(Image.toURL(file)); + } + + /** Reads gif images from a byte array. + * @param data the byte array + * @throws IOException on error + */ + public GifImage(byte data[]) throws IOException { + fromData = data; + InputStream is = null; + try { + is = new ByteArrayInputStream(data); + process(is); + } + finally { + if (is != null) { + is.close(); + } + } + } + + /** Reads gif images from a stream. The stream is not closed. + * @param is the stream + * @throws IOException on error + */ + public GifImage(InputStream is) throws IOException { + process(is); + } + + /** Gets the number of frames the gif has. + * @return the number of frames the gif has + */ + public int getFrameCount() { + return frames.size(); + } + + /** Gets the image from a frame. The first frame is 1. + * @param frame the frame to get the image from + * @return the image + */ + public Image getImage(int frame) { + GifFrame gf = (GifFrame)frames.get(frame - 1); + return gf.image; + } + + /** Gets the [x,y] position of the frame in reference to the + * logical screen. + * @param frame the frame + * @return the [x,y] position of the frame + */ + public int[] getFramePosition(int frame) { + GifFrame gf = (GifFrame)frames.get(frame - 1); + return new int[]{gf.ix, gf.iy}; + + } + + /** Gets the logical screen. The images may be smaller and placed + * in some position in this screen to playback some animation. + * No image will be be bigger that this. + * @return the logical screen dimensions as [x,y] + */ + public int[] getLogicalScreen() { + return new int[]{width, height}; + } + + void process(InputStream is) throws IOException { + in = new DataInputStream(new BufferedInputStream(is)); + readHeader(); + readContents(); + if (frames.size() == 0) + throw new IOException("The file does not contain any valid image."); + } + + /** + * Reads GIF file header information. + */ + protected void readHeader() throws IOException { + String id = ""; + for (int i = 0; i < 6; i++) + id += (char)in.read(); + if (!id.startsWith("GIF8")) { + throw new IOException("Gif signature nor found."); + } + + readLSD(); + if (gctFlag) { + m_global_table = readColorTable(m_gbpc); + } + } + + /** + * Reads Logical Screen Descriptor + */ + protected void readLSD() throws IOException { + + // logical screen size + width = readShort(); + height = readShort(); + + // packed fields + int packed = in.read(); + gctFlag = (packed & 0x80) != 0; // 1 : global color table flag + m_gbpc = (packed & 7) + 1; + bgIndex = in.read(); // background color index + pixelAspect = in.read(); // pixel aspect ratio + } + + /** + * Reads next 16-bit value, LSB first + */ + protected int readShort() throws IOException { + // read 16-bit value, LSB first + return in.read() | (in.read() << 8); + } + + /** + * Reads next variable length block from input. + * + * @return number of bytes stored in "buffer" + */ + protected int readBlock() throws IOException { + blockSize = in.read(); + if (blockSize <= 0) + return blockSize = 0; + for (int k = 0; k < blockSize; ++k) { + int v = in.read(); + if (v < 0) { + return blockSize = k; + } + block[k] = (byte)v; + } + return blockSize; + } + + protected byte[] readColorTable(int bpc) throws IOException { + int ncolors = 1 << bpc; + int nbytes = 3*ncolors; + bpc = newBpc(bpc); + byte table[] = new byte[(1 << bpc) * 3]; + in.readFully(table, 0, nbytes); + return table; + } + + + static protected int newBpc(int bpc) { + switch (bpc) { + case 1: + case 2: + case 4: + break; + case 3: + return 4; + default: + return 8; + } + return bpc; + } + + protected void readContents() throws IOException { + // read GIF file content blocks + boolean done = false; + while (!done) { + int code = in.read(); + switch (code) { + + case 0x2C: // image separator + readImage(); + break; + + case 0x21: // extension + code = in.read(); + switch (code) { + + case 0xf9: // graphics control extension + readGraphicControlExt(); + break; + + case 0xff: // application extension + readBlock(); + skip(); // don't care + break; + + default: // uninteresting extension + skip(); + } + break; + + default: + done = true; + break; + } + } + } + + /** + * Reads next frame image + */ + protected void readImage() throws IOException { + ix = readShort(); // (sub)image position & size + iy = readShort(); + iw = readShort(); + ih = readShort(); + + int packed = in.read(); + lctFlag = (packed & 0x80) != 0; // 1 - local color table flag + interlace = (packed & 0x40) != 0; // 2 - interlace flag + // 3 - sort flag + // 4-5 - reserved + lctSize = 2 << (packed & 7); // 6-8 - local color table size + m_bpc = newBpc(m_gbpc); + if (lctFlag) { + m_curr_table = readColorTable((packed & 7) + 1); // read table + m_bpc = newBpc((packed & 7) + 1); + } + else { + m_curr_table = m_global_table; + } + if (transparency && transIndex >= m_curr_table.length / 3) + transparency = false; + if (transparency && m_bpc == 1) { // Acrobat 5.05 doesn't like this combination + byte tp[] = new byte[12]; + System.arraycopy(m_curr_table, 0, tp, 0, 6); + m_curr_table = tp; + m_bpc = 2; + } + boolean skipZero = decodeImageData(); // decode pixel data + if (!skipZero) + skip(); + + Image img = null; + try { + img = new ImgRaw(iw, ih, 1, m_bpc, m_out); + PdfArray colorspace = new PdfArray(); + colorspace.add(PdfName.INDEXED); + colorspace.add(PdfName.DEVICERGB); + int len = m_curr_table.length; + colorspace.add(new PdfNumber(len / 3 - 1)); + colorspace.add(new PdfString(m_curr_table)); + PdfDictionary ad = new PdfDictionary(); + ad.put(PdfName.COLORSPACE, colorspace); + img.setAdditional(ad); + if (transparency) { + img.setTransparency(new int[]{transIndex, transIndex}); + } + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + img.setOriginalType(Image.ORIGINAL_GIF); + img.setOriginalData(fromData); + img.setUrl(fromUrl); + GifFrame gf = new GifFrame(); + gf.image = img; + gf.ix = ix; + gf.iy = iy; + frames.add(gf); // add image to frame list + + //resetFrame(); + + } + + protected boolean decodeImageData() throws IOException { + int NullCode = -1; + int npix = iw * ih; + int available, clear, code_mask, code_size, end_of_information, in_code, old_code, + bits, code, count, i, datum, data_size, first, top, bi; + boolean skipZero = false; + + if (prefix == null) + prefix = new short[MaxStackSize]; + if (suffix == null) + suffix = new byte[MaxStackSize]; + if (pixelStack == null) + pixelStack = new byte[MaxStackSize+1]; + + m_line_stride = (iw * m_bpc + 7) / 8; + m_out = new byte[m_line_stride * ih]; + int pass = 1; + int inc = interlace ? 8 : 1; + int line = 0; + int xpos = 0; + + // Initialize GIF data stream decoder. + + data_size = in.read(); + clear = 1 << data_size; + end_of_information = clear + 1; + available = clear + 2; + old_code = NullCode; + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + for (code = 0; code < clear; code++) { + prefix[code] = 0; + suffix[code] = (byte) code; + } + + // Decode GIF pixel stream. + + datum = bits = count = first = top = bi = 0; + + for (i = 0; i < npix; ) { + if (top == 0) { + if (bits < code_size) { + // Load bytes until there are enough bits for a code. + if (count == 0) { + // Read a new data block. + count = readBlock(); + if (count <= 0) { + skipZero = true; + break; + } + bi = 0; + } + datum += (((int) block[bi]) & 0xff) << bits; + bits += 8; + bi++; + count--; + continue; + } + + // Get the next code. + + code = datum & code_mask; + datum >>= code_size; + bits -= code_size; + + // Interpret the code + + if ((code > available) || (code == end_of_information)) + break; + if (code == clear) { + // Reset decoder. + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + available = clear + 2; + old_code = NullCode; + continue; + } + if (old_code == NullCode) { + pixelStack[top++] = suffix[code]; + old_code = code; + first = code; + continue; + } + in_code = code; + if (code == available) { + pixelStack[top++] = (byte) first; + code = old_code; + } + while (code > clear) { + pixelStack[top++] = suffix[code]; + code = prefix[code]; + } + first = ((int) suffix[code]) & 0xff; + + // Add a new string to the string table, + + if (available >= MaxStackSize) + break; + pixelStack[top++] = (byte) first; + prefix[available] = (short) old_code; + suffix[available] = (byte) first; + available++; + if (((available & code_mask) == 0) && (available < MaxStackSize)) { + code_size++; + code_mask += available; + } + old_code = in_code; + } + + // Pop a pixel off the pixel stack. + + top--; + i++; + + setPixel(xpos, line, pixelStack[top]); + ++xpos; + if (xpos >= iw) { + xpos = 0; + line += inc; + if (line >= ih) { + if (interlace) { + do { + pass++; + switch (pass) { + case 2: + line = 4; + break; + case 3: + line = 2; + inc = 4; + break; + case 4: + line = 1; + inc = 2; + break; + default: // this shouldn't happen + line = ih - 1; + inc = 0; + } + } while (line >= ih); + } + else { + line = ih - 1; // this shouldn't happen + inc = 0; + } + } + } + } + return skipZero; + } + + + protected void setPixel(int x, int y, int v) { + if (m_bpc == 8) { + int pos = x + iw * y; + m_out[pos] = (byte)v; + } + else { + int pos = m_line_stride * y + x / (8 / m_bpc); + int vout = v << (8 - m_bpc * (x % (8 / m_bpc))- m_bpc); + m_out[pos] |= vout; + } + } + + /** + * Resets frame state for reading next image. + */ + protected void resetFrame() { + // it does nothing in the pdf context + //boolean transparency = false; + //int delay = 0; + } + + /** + * Reads Graphics Control Extension values + */ + protected void readGraphicControlExt() throws IOException { + in.read(); // block size + int packed = in.read(); // packed fields + dispose = (packed & 0x1c) >> 2; // disposal method + if (dispose == 0) + dispose = 1; // elect to keep old image if discretionary + transparency = (packed & 1) != 0; + delay = readShort() * 10; // delay in milliseconds + transIndex = in.read(); // transparent color index + in.read(); // block terminator + } + + /** + * Skips variable length blocks up to and including + * next zero length block. + */ + protected void skip() throws IOException { + do { + readBlock(); + } while (blockSize > 0); + } + + static class GifFrame { + Image image; + int ix; + int iy; + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/PngImage.java b/src/main/java/com/lowagie/text/pdf/codec/PngImage.java new file mode 100644 index 0000000..b82e17b --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/PngImage.java @@ -0,0 +1,987 @@ +/* + * Copyright 2003 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/ + * + * + * The original JAI codecs have the following license + * + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ + +package com.lowagie.text.pdf.codec; + +import java.awt.color.ICC_Profile; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +import com.lowagie.text.ExceptionConverter; +import com.lowagie.text.Image; +import com.lowagie.text.ImgRaw; +import com.lowagie.text.pdf.ByteBuffer; +import com.lowagie.text.pdf.PdfArray; +import com.lowagie.text.pdf.PdfDictionary; +import com.lowagie.text.pdf.PdfLiteral; +import com.lowagie.text.pdf.PdfName; +import com.lowagie.text.pdf.PdfNumber; +import com.lowagie.text.pdf.PdfObject; +import com.lowagie.text.pdf.PdfReader; +import com.lowagie.text.pdf.PdfString; + +/** Reads a PNG image. All types of PNG can be read. + *

+ * It is based in part in the JAI codec. + * + * @author Paulo Soares (psoares@consiste.pt) + */ +public class PngImage { +/** Some PNG specific values. */ + public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10}; + +/** A PNG marker. */ + public static final String IHDR = "IHDR"; + +/** A PNG marker. */ + public static final String PLTE = "PLTE"; + +/** A PNG marker. */ + public static final String IDAT = "IDAT"; + +/** A PNG marker. */ + public static final String IEND = "IEND"; + +/** A PNG marker. */ + public static final String tRNS = "tRNS"; + +/** A PNG marker. */ + public static final String pHYs = "pHYs"; + +/** A PNG marker. */ + public static final String gAMA = "gAMA"; + +/** A PNG marker. */ + public static final String cHRM = "cHRM"; + +/** A PNG marker. */ + public static final String sRGB = "sRGB"; + +/** A PNG marker. */ + public static final String iCCP = "iCCP"; + + private static final int TRANSFERSIZE = 4096; + private static final int PNG_FILTER_NONE = 0; + private static final int PNG_FILTER_SUB = 1; + private static final int PNG_FILTER_UP = 2; + private static final int PNG_FILTER_AVERAGE = 3; + private static final int PNG_FILTER_PAETH = 4; + private static final PdfName intents[] = {PdfName.PERCEPTUAL, + PdfName.RELATIVECALORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECALORIMETRIC}; + + InputStream is; + DataInputStream dataStream; + int width; + int height; + int bitDepth; + int colorType; + int compressionMethod; + int filterMethod; + int interlaceMethod; + PdfDictionary additional = new PdfDictionary(); + byte image[]; + byte smask[]; + byte trans[]; + NewByteArrayOutputStream idat = new NewByteArrayOutputStream(); + int dpiX; + int dpiY; + float XYRatio; + boolean genBWMask; + boolean palShades; + int transRedGray = -1; + int transGreen = -1; + int transBlue = -1; + int inputBands; + int bytesPerPixel; // number of bytes per input pixel + byte colorTable[]; + float gamma = 1f; + boolean hasCHRM = false; + float xW, yW, xR, yR, xG, yG, xB, yB; + PdfName intent; + ICC_Profile icc_profile; + + + + /** Creates a new instance of PngImage */ + PngImage(InputStream is) { + this.is = is; + } + + /** Reads a PNG from an url. + * @param url the url + * @throws IOException on error + * @return the image + */ + public static Image getImage(URL url) throws IOException { + InputStream is = null; + try { + is = url.openStream(); + Image img = getImage(is); + img.setUrl(url); + return img; + } + finally { + if (is != null) { + is.close(); + } + } + } + + /** Reads a PNG from a stream. + * @param is the stream + * @throws IOException on error + * @return the image + */ + public static Image getImage(InputStream is) throws IOException { + PngImage png = new PngImage(is); + return png.getImage(); + } + + /** Reads a PNG from a file. + * @param file the file + * @throws IOException on error + * @return the image + */ + public static Image getImage(String file) throws IOException { + return getImage(Image.toURL(file)); + } + + /** Reads a PNG from a byte array. + * @param data the byte array + * @throws IOException on error + * @return the image + */ + public static Image getImage(byte data[]) throws IOException { + InputStream is = null; + try { + is = new ByteArrayInputStream(data); + Image img = getImage(is); + img.setOriginalData(data); + return img; + } + finally { + if (is != null) { + is.close(); + } + } + } + + boolean checkMarker(String s) { + if (s.length() != 4) + return false; + for (int k = 0; k < 4; ++k) { + char c = s.charAt(k); + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) + return false; + } + return true; + } + + void readPng() throws IOException { + for (int i = 0; i < PNGID.length; i++) { + if (PNGID[i] != is.read()) { + throw new IOException("File is not a valid PNG."); + } + } + byte buffer[] = new byte[TRANSFERSIZE]; + while (true) { + int len = getInt(is); + String marker = getString(is); + if (len < 0 || !checkMarker(marker)) + throw new IOException("Corrupted PNG file."); + if (IDAT.equals(marker)) { + int size; + while (len != 0) { + size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE)); + if (size < 0) + return; + idat.write(buffer, 0, size); + len -= size; + } + } + else if (tRNS.equals(marker)) { + switch (colorType) { + case 0: + if (len >= 2) { + len -= 2; + int gray = getWord(is); + if (bitDepth == 16) + transRedGray = gray; + else + additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]")); + } + break; + case 2: + if (len >= 6) { + len -= 6; + int red = getWord(is); + int green = getWord(is); + int blue = getWord(is); + if (bitDepth == 16) { + transRedGray = red; + transGreen = green; + transBlue = blue; + } + else + additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]")); + } + break; + case 3: + if (len > 0) { + trans = new byte[len]; + for (int k = 0; k < len; ++k) + trans[k] = (byte)is.read(); + len = 0; + } + break; + } + Image.skip(is, len); + } + else if (IHDR.equals(marker)) { + width = getInt(is); + height = getInt(is); + + bitDepth = is.read(); + colorType = is.read(); + compressionMethod = is.read(); + filterMethod = is.read(); + interlaceMethod = is.read(); + } + else if (PLTE.equals(marker)) { + if (colorType == 3) { + PdfArray colorspace = new PdfArray(); + colorspace.add(PdfName.INDEXED); + colorspace.add(getColorspace()); + colorspace.add(new PdfNumber(len / 3 - 1)); + ByteBuffer colortable = new ByteBuffer(); + while ((len--) > 0) { + colortable.append_i(is.read()); + } + colorspace.add(new PdfString(colorTable = colortable.toByteArray())); + additional.put(PdfName.COLORSPACE, colorspace); + } + else { + Image.skip(is, len); + } + } + else if (pHYs.equals(marker)) { + int dx = getInt(is); + int dy = getInt(is); + int unit = is.read(); + if (unit == 1) { + dpiX = (int)((float)dx * 0.0254f); + dpiY = (int)((float)dy * 0.0254f); + } + else { + if (dy != 0) + XYRatio = (float)dx / (float)dy; + } + } + else if (cHRM.equals(marker)) { + xW = (float)getInt(is) / 100000f; + yW = (float)getInt(is) / 100000f; + xR = (float)getInt(is) / 100000f; + yR = (float)getInt(is) / 100000f; + xG = (float)getInt(is) / 100000f; + yG = (float)getInt(is) / 100000f; + xB = (float)getInt(is) / 100000f; + yB = (float)getInt(is) / 100000f; + hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f); + } + else if (sRGB.equals(marker)) { + int ri = is.read(); + intent = intents[ri]; + gamma = 2.2f; + xW = 0.3127f; + yW = 0.329f; + xR = 0.64f; + yR = 0.33f; + xG = 0.3f; + yG = 0.6f; + xB = 0.15f; + yB = 0.06f; + hasCHRM = true; + } + else if (gAMA.equals(marker)) { + int gm = getInt(is); + if (gm != 0) { + gamma = 100000f / (float)gm; + if (!hasCHRM) { + xW = 0.3127f; + yW = 0.329f; + xR = 0.64f; + yR = 0.33f; + xG = 0.3f; + yG = 0.6f; + xB = 0.15f; + yB = 0.06f; + hasCHRM = true; + } + } + } + else if (iCCP.equals(marker)) { + do { + --len; + } while (is.read() != 0); + is.read(); + --len; + byte icccom[] = new byte[len]; + int p = 0; + while (len > 0) { + int r = is.read(icccom, p, len); + if (r < 0) + throw new IOException("Premature end of file."); + p += r; + len -= r; + } + byte iccp[] = PdfReader.FlateDecode(icccom, true); + icccom = null; + try { + icc_profile = ICC_Profile.getInstance(iccp); + } + catch (Exception e) { + icc_profile = null; + } + } + else if (IEND.equals(marker)) { + break; + } + else { + Image.skip(is, len); + } + Image.skip(is, 4); + } + } + + PdfObject getColorspace() { + if (icc_profile != null) { + if ((colorType & 2) == 0) + return PdfName.DEVICEGRAY; + else + return PdfName.DEVICERGB; + } + if (gamma == 1f && !hasCHRM) { + if ((colorType & 2) == 0) + return PdfName.DEVICEGRAY; + else + return PdfName.DEVICERGB; + } + else { + PdfArray array = new PdfArray(); + PdfDictionary dic = new PdfDictionary(); + if ((colorType & 2) == 0) { + if (gamma == 1f) + return PdfName.DEVICEGRAY; + array.add(PdfName.CALGRAY); + dic.put(PdfName.GAMMA, new PdfNumber(gamma)); + dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]")); + array.add(dic); + } + else { + PdfObject wp = new PdfLiteral("[1 1 1]"); + array.add(PdfName.CALRGB); + if (gamma != 1f) { + PdfArray gm = new PdfArray(); + PdfNumber n = new PdfNumber(gamma); + gm.add(n); + gm.add(n); + gm.add(n); + dic.put(PdfName.GAMMA, gm); + } + if (hasCHRM) { + float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB); + float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z; + float XA = YA*xR/yR; + float ZA = YA*((1-xR)/yR-1); + float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z; + float XB = YB*xG/yG; + float ZB = YB*((1-xG)/yG-1); + float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z; + float XC = YC*xB/yB; + float ZC = YC*((1-xB)/yB-1); + float XW = XA+XB+XC; + float YW = 1;//YA+YB+YC; + float ZW = ZA+ZB+ZC; + PdfArray wpa = new PdfArray(); + wpa.add(new PdfNumber(XW)); + wpa.add(new PdfNumber(YW)); + wpa.add(new PdfNumber(ZW)); + wp = wpa; + PdfArray matrix = new PdfArray(); + matrix.add(new PdfNumber(XA)); + matrix.add(new PdfNumber(YA)); + matrix.add(new PdfNumber(ZA)); + matrix.add(new PdfNumber(XB)); + matrix.add(new PdfNumber(YB)); + matrix.add(new PdfNumber(ZB)); + matrix.add(new PdfNumber(XC)); + matrix.add(new PdfNumber(YC)); + matrix.add(new PdfNumber(ZC)); + dic.put(PdfName.MATRIX, matrix); + } + dic.put(PdfName.WHITEPOINT, wp); + array.add(dic); + } + return array; + } + } + + Image getImage() throws IOException { + readPng(); + try { + int pal0 = 0; + int palIdx = 0; + palShades = false; + if (trans != null) { + for (int k = 0; k < trans.length; ++k) { + int n = trans[k] & 0xff; + if (n == 0) { + ++pal0; + palIdx = k; + } + if (n != 0 && n != 255) { + palShades = true; + break; + } + } + } + if ((colorType & 4) != 0) + palShades = true; + genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0)); + if (!palShades && !genBWMask && pal0 == 1) { + additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]")); + } + boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask; + switch (colorType) { + case 0: + inputBands = 1; + break; + case 2: + inputBands = 3; + break; + case 3: + inputBands = 1; + break; + case 4: + inputBands = 2; + break; + case 6: + inputBands = 4; + break; + } + if (needDecode) + decodeIdat(); + int components = inputBands; + if ((colorType & 4) != 0) + --components; + int bpc = bitDepth; + if (bpc == 16) + bpc = 8; + Image img; + if (image != null) + img = Image.getInstance(width, height, components, bpc, image); + else { + img = new ImgRaw(width, height, components, bpc, idat.toByteArray()); + img.setDeflated(true); + PdfDictionary decodeparms = new PdfDictionary(); + decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth)); + decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15)); + decodeparms.put(PdfName.COLUMNS, new PdfNumber(width)); + decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3)); + additional.put(PdfName.DECODEPARMS, decodeparms); + } + if (additional.get(PdfName.COLORSPACE) == null) + additional.put(PdfName.COLORSPACE, getColorspace()); + if (intent != null) + additional.put(PdfName.INTENT, intent); + if (additional.size() > 0) + img.setAdditional(additional); + if (icc_profile != null) + img.tagICC(icc_profile); + if (palShades) { + Image im2 = Image.getInstance(width, height, 1, 8, smask); + im2.makeMask(); + img.setImageMask(im2); + } + if (genBWMask) { + Image im2 = Image.getInstance(width, height, 1, 1, smask); + im2.makeMask(); + img.setImageMask(im2); + } + img.setDpi(dpiX, dpiY); + img.setXYRatio(XYRatio); + img.setOriginalType(Image.ORIGINAL_PNG); + return img; + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + } + + void decodeIdat() { + int nbitDepth = bitDepth; + if (nbitDepth == 16) + nbitDepth = 8; + int size = -1; + bytesPerPixel = (bitDepth == 16) ? 2 : 1; + switch (colorType) { + case 0: + size = (nbitDepth * width + 7) / 8 * height; + break; + case 2: + size = width * 3 * height; + bytesPerPixel *= 3; + break; + case 3: + if (interlaceMethod == 1) + size = (nbitDepth * width + 7) / 8 * height; + bytesPerPixel = 1; + break; + case 4: + size = width * height; + bytesPerPixel *= 2; + break; + case 6: + size = width * 3 * height; + bytesPerPixel *= 4; + break; + } + if (size >= 0) + image = new byte[size]; + if (palShades) + smask = new byte[width * height]; + else if (genBWMask) + smask = new byte[(width + 7) / 8 * height]; + ByteArrayInputStream bai = new ByteArrayInputStream(idat.getBuf(), 0, idat.size()); + InputStream infStream = new InflaterInputStream(bai, new Inflater()); + dataStream = new DataInputStream(infStream); + + if (interlaceMethod != 1) { + decodePass(0, 0, 1, 1, width, height); + } + else { + decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8); + decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8); + decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8); + decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4); + decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4); + decodePass(1, 0, 2, 2, width/2, (height + 1)/2); + decodePass(0, 1, 1, 2, width, height/2); + } + + } + + void decodePass( int xOffset, int yOffset, + int xStep, int yStep, + int passWidth, int passHeight) { + if ((passWidth == 0) || (passHeight == 0)) { + return; + } + + int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8; + byte[] curr = new byte[bytesPerRow]; + byte[] prior = new byte[bytesPerRow]; + + // Decode the (sub)image row-by-row + int srcY, dstY; + for (srcY = 0, dstY = yOffset; + srcY < passHeight; + srcY++, dstY += yStep) { + // Read the filter type byte and a row of data + int filter = 0; + try { + filter = dataStream.read(); + dataStream.readFully(curr, 0, bytesPerRow); + } catch (Exception e) { + // empty on purpose + } + + switch (filter) { + case PNG_FILTER_NONE: + break; + case PNG_FILTER_SUB: + decodeSubFilter(curr, bytesPerRow, bytesPerPixel); + break; + case PNG_FILTER_UP: + decodeUpFilter(curr, prior, bytesPerRow); + break; + case PNG_FILTER_AVERAGE: + decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel); + break; + case PNG_FILTER_PAETH: + decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel); + break; + default: + // Error -- uknown filter type + throw new RuntimeException("PNG filter unknown."); + } + + processPixels(curr, xOffset, xStep, dstY, passWidth); + + // Swap curr and prior + byte[] tmp = prior; + prior = curr; + curr = tmp; + } + } + + void processPixels(byte curr[], int xOffset, int step, int y, int width) { + int srcX, dstX; + + int out[] = getPixel(curr); + int sizes = 0; + switch (colorType) { + case 0: + case 3: + case 4: + sizes = 1; + break; + case 2: + case 6: + sizes = 3; + break; + } + if (image != null) { + dstX = xOffset; + int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8; + for (srcX = 0; srcX < width; srcX++) { + setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride); + dstX += step; + } + } + if (palShades) { + if ((colorType & 4) != 0) { + if (bitDepth == 16) { + for (int k = 0; k < width; ++k) + out[k * inputBands + sizes] >>>= 8; + } + int yStride = this.width; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride); + dstX += step; + } + } + else { //colorType 3 + int yStride = this.width; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int idx = out[srcX]; + if (idx < trans.length) + v[0] = trans[idx]; + setPixel(smask, v, 0, 1, dstX, y, 8, yStride); + dstX += step; + } + } + } + else if (genBWMask) { + switch (colorType) { + case 3: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int idx = out[srcX]; + if (idx < trans.length) + v[0] = (trans[idx] == 0 ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + case 0: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int g = out[srcX]; + v[0] = (g == transRedGray ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + case 2: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int markRed = inputBands * srcX; + v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen + && out[markRed + 2] == transBlue ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + } + } + } + + static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) { + if (bitDepth == 8) { + int pos = bytesPerRow * y + x; + return image[pos] & 0xff; + } + else { + int pos = bytesPerRow * y + x / (8 / bitDepth); + int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth); + return v & ((1 << bitDepth) - 1); + } + } + + static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) { + if (bitDepth == 8) { + int pos = bytesPerRow * y + size * x; + for (int k = 0; k < size; ++k) + image[pos + k] = (byte)data[k + offset]; + } + else if (bitDepth == 16) { + int pos = bytesPerRow * y + size * x; + for (int k = 0; k < size; ++k) + image[pos + k] = (byte)(data[k + offset] >>> 8); + } + else { + int pos = bytesPerRow * y + x / (8 / bitDepth); + int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth); + image[pos] |= v; + } + } + + int[] getPixel(byte curr[]) { + switch (bitDepth) { + case 8: { + int out[] = new int[curr.length]; + for (int k = 0; k < out.length; ++k) + out[k] = curr[k] & 0xff; + return out; + } + case 16: { + int out[] = new int[curr.length / 2]; + for (int k = 0; k < out.length; ++k) + out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff); + return out; + } + default: { + int out[] = new int[curr.length * 8 / bitDepth]; + int idx = 0; + int passes = 8 / bitDepth; + int mask = (1 << bitDepth) - 1; + for (int k = 0; k < curr.length; ++k) { + for (int j = passes - 1; j >= 0; --j) { + out[idx++] = (curr[k] >>> (bitDepth * j)) & mask; + } + } + return out; + } + } + } + + private static void decodeSubFilter(byte[] curr, int count, int bpp) { + for (int i = bpp; i < count; i++) { + int val; + + val = curr[i] & 0xff; + val += curr[i - bpp] & 0xff; + + curr[i] = (byte)val; + } + } + + private static void decodeUpFilter(byte[] curr, byte[] prev, + int count) { + for (int i = 0; i < count; i++) { + int raw = curr[i] & 0xff; + int prior = prev[i] & 0xff; + + curr[i] = (byte)(raw + prior); + } + } + + private static void decodeAverageFilter(byte[] curr, byte[] prev, + int count, int bpp) { + int raw, priorPixel, priorRow; + + for (int i = 0; i < bpp; i++) { + raw = curr[i] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + priorRow/2); + } + + for (int i = bpp; i < count; i++) { + raw = curr[i] & 0xff; + priorPixel = curr[i - bpp] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + (priorPixel + priorRow)/2); + } + } + + private static int paethPredictor(int a, int b, int c) { + int p = a + b - c; + int pa = Math.abs(p - a); + int pb = Math.abs(p - b); + int pc = Math.abs(p - c); + + if ((pa <= pb) && (pa <= pc)) { + return a; + } else if (pb <= pc) { + return b; + } else { + return c; + } + } + + private static void decodePaethFilter(byte[] curr, byte[] prev, + int count, int bpp) { + int raw, priorPixel, priorRow, priorRowPixel; + + for (int i = 0; i < bpp; i++) { + raw = curr[i] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + priorRow); + } + + for (int i = bpp; i < count; i++) { + raw = curr[i] & 0xff; + priorPixel = curr[i - bpp] & 0xff; + priorRow = prev[i] & 0xff; + priorRowPixel = prev[i - bpp] & 0xff; + + curr[i] = (byte)(raw + paethPredictor(priorPixel, + priorRow, + priorRowPixel)); + } + } + + static class NewByteArrayOutputStream extends ByteArrayOutputStream { + public byte[] getBuf() { + return buf; + } + } + +/** + * Gets an int from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final int getInt(InputStream is) throws IOException { + return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read(); + } + +/** + * Gets a word from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final int getWord(InputStream is) throws IOException { + return (is.read() << 8) + is.read(); + } + +/** + * Gets a String from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final String getString(InputStream is) throws IOException { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < 4; i++) { + buf.append((char)is.read()); + } + return buf.toString(); + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java new file mode 100644 index 0000000..a4ba5d5 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; + +/** + * A baseline TIFF reader. The reader has some functionality in addition to + * the baseline specifications for Bilevel images, for which the group 3 and + * group 4 decompression schemes have been implemented. Support for LZW + * decompression has also been added. Support for Horizontal differencing + * predictor decoding is also included, when used with LZW compression. + * However, this support is limited to data with bitsPerSample value of 8. + * When reading in RGB images, support for alpha and extraSamples being + * present has been added. Support for reading in images with 16 bit samples + * has been added. Support for the SampleFormat tag (signed samples as well + * as floating-point samples) has also been added. In all other cases, support + * is limited to Baseline specifications. + * + * + */ +public class TIFFConstants { + +/* + * TIFF Tag Definitions (from tifflib). + */ + public static final int TIFFTAG_SUBFILETYPE = 254; /* subfile data descriptor */ + public static final int FILETYPE_REDUCEDIMAGE = 0x1; /* reduced resolution version */ + public static final int FILETYPE_PAGE = 0x2; /* one page of many */ + public static final int FILETYPE_MASK = 0x4; /* transparency mask */ + public static final int TIFFTAG_OSUBFILETYPE = 255; /* +kind of data in subfile */ + public static final int OFILETYPE_IMAGE = 1; /* full resolution image data */ + public static final int OFILETYPE_REDUCEDIMAGE = 2; /* reduced size image data */ + public static final int OFILETYPE_PAGE = 3; /* one page of many */ + public static final int TIFFTAG_IMAGEWIDTH = 256; /* image width in pixels */ + public static final int TIFFTAG_IMAGELENGTH = 257; /* image height in pixels */ + public static final int TIFFTAG_BITSPERSAMPLE = 258; /* bits per channel (sample) */ + public static final int TIFFTAG_COMPRESSION = 259; /* data compression technique */ + public static final int COMPRESSION_NONE = 1; /* dump mode */ + public static final int COMPRESSION_CCITTRLE = 2; /* CCITT modified Huffman RLE */ + public static final int COMPRESSION_CCITTFAX3 = 3; /* CCITT Group 3 fax encoding */ + public static final int COMPRESSION_CCITTFAX4 = 4; /* CCITT Group 4 fax encoding */ + public static final int COMPRESSION_LZW = 5; /* Lempel-Ziv & Welch */ + public static final int COMPRESSION_OJPEG = 6; /* !6.0 JPEG */ + public static final int COMPRESSION_JPEG = 7; /* %JPEG DCT compression */ + public static final int COMPRESSION_NEXT = 32766; /* NeXT 2-bit RLE */ + public static final int COMPRESSION_CCITTRLEW = 32771; /* #1 w/ word alignment */ + public static final int COMPRESSION_PACKBITS = 32773; /* Macintosh RLE */ + public static final int COMPRESSION_THUNDERSCAN = 32809; /* ThunderScan RLE */ + /* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT */ + public static final int COMPRESSION_DCS = 32947; /* Kodak DCS encoding */ + public static final int COMPRESSION_JBIG = 34661; /* ISO JBIG */ + public static final int COMPRESSION_SGILOG = 34676; /* SGI Log Luminance RLE */ + public static final int COMPRESSION_SGILOG24 = 34677; /* SGI Log 24-bit packed */ + public static final int TIFFTAG_PHOTOMETRIC = 262; /* photometric interpretation */ + public static final int PHOTOMETRIC_MINISWHITE = 0; /* min value is white */ + public static final int PHOTOMETRIC_MINISBLACK = 1; /* min value is black */ + public static final int PHOTOMETRIC_RGB = 2; /* RGB color model */ + public static final int PHOTOMETRIC_PALETTE = 3; /* color map indexed */ + public static final int PHOTOMETRIC_MASK = 4; /* $holdout mask */ + public static final int PHOTOMETRIC_SEPARATED = 5; /* !color separations */ + public static final int PHOTOMETRIC_YCBCR = 6; /* !CCIR 601 */ + public static final int PHOTOMETRIC_CIELAB = 8; /* !1976 CIE L*a*b* */ + public static final int PHOTOMETRIC_LOGL = 32844; /* CIE Log2(L) */ + public static final int PHOTOMETRIC_LOGLUV = 32845; /* CIE Log2(L) (u',v') */ + public static final int TIFFTAG_THRESHHOLDING = 263; /* +thresholding used on data */ + public static final int THRESHHOLD_BILEVEL = 1; /* b&w art scan */ + public static final int THRESHHOLD_HALFTONE = 2; /* or dithered scan */ + public static final int THRESHHOLD_ERRORDIFFUSE = 3; /* usually floyd-steinberg */ + public static final int TIFFTAG_CELLWIDTH = 264; /* +dithering matrix width */ + public static final int TIFFTAG_CELLLENGTH = 265; /* +dithering matrix height */ + public static final int TIFFTAG_FILLORDER = 266; /* data order within a byte */ + public static final int FILLORDER_MSB2LSB = 1; /* most significant -> least */ + public static final int FILLORDER_LSB2MSB = 2; /* least significant -> most */ + public static final int TIFFTAG_DOCUMENTNAME = 269; /* name of doc. image is from */ + public static final int TIFFTAG_IMAGEDESCRIPTION = 270; /* info about image */ + public static final int TIFFTAG_MAKE = 271; /* scanner manufacturer name */ + public static final int TIFFTAG_MODEL = 272; /* scanner model name/number */ + public static final int TIFFTAG_STRIPOFFSETS = 273; /* offsets to data strips */ + public static final int TIFFTAG_ORIENTATION = 274; /* +image orientation */ + public static final int ORIENTATION_TOPLEFT = 1; /* row 0 top, col 0 lhs */ + public static final int ORIENTATION_TOPRIGHT = 2; /* row 0 top, col 0 rhs */ + public static final int ORIENTATION_BOTRIGHT = 3; /* row 0 bottom, col 0 rhs */ + public static final int ORIENTATION_BOTLEFT = 4; /* row 0 bottom, col 0 lhs */ + public static final int ORIENTATION_LEFTTOP = 5; /* row 0 lhs, col 0 top */ + public static final int ORIENTATION_RIGHTTOP = 6; /* row 0 rhs, col 0 top */ + public static final int ORIENTATION_RIGHTBOT = 7; /* row 0 rhs, col 0 bottom */ + public static final int ORIENTATION_LEFTBOT = 8; /* row 0 lhs, col 0 bottom */ + public static final int TIFFTAG_SAMPLESPERPIXEL = 277; /* samples per pixel */ + public static final int TIFFTAG_ROWSPERSTRIP = 278; /* rows per strip of data */ + public static final int TIFFTAG_STRIPBYTECOUNTS = 279; /* bytes counts for strips */ + public static final int TIFFTAG_MINSAMPLEVALUE = 280; /* +minimum sample value */ + public static final int TIFFTAG_MAXSAMPLEVALUE = 281; /* +maximum sample value */ + public static final int TIFFTAG_XRESOLUTION = 282; /* pixels/resolution in x */ + public static final int TIFFTAG_YRESOLUTION = 283; /* pixels/resolution in y */ + public static final int TIFFTAG_PLANARCONFIG = 284; /* storage organization */ + public static final int PLANARCONFIG_CONTIG = 1; /* single image plane */ + public static final int PLANARCONFIG_SEPARATE = 2; /* separate planes of data */ + public static final int TIFFTAG_PAGENAME = 285; /* page name image is from */ + public static final int TIFFTAG_XPOSITION = 286; /* x page offset of image lhs */ + public static final int TIFFTAG_YPOSITION = 287; /* y page offset of image lhs */ + public static final int TIFFTAG_FREEOFFSETS = 288; /* +byte offset to free block */ + public static final int TIFFTAG_FREEBYTECOUNTS = 289; /* +sizes of free blocks */ + public static final int TIFFTAG_GRAYRESPONSEUNIT = 290; /* $gray scale curve accuracy */ + public static final int GRAYRESPONSEUNIT_10S = 1; /* tenths of a unit */ + public static final int GRAYRESPONSEUNIT_100S = 2; /* hundredths of a unit */ + public static final int GRAYRESPONSEUNIT_1000S = 3; /* thousandths of a unit */ + public static final int GRAYRESPONSEUNIT_10000S = 4; /* ten-thousandths of a unit */ + public static final int GRAYRESPONSEUNIT_100000S = 5; /* hundred-thousandths */ + public static final int TIFFTAG_GRAYRESPONSECURVE = 291; /* $gray scale response curve */ + public static final int TIFFTAG_GROUP3OPTIONS = 292; /* 32 flag bits */ + public static final int GROUP3OPT_2DENCODING = 0x1; /* 2-dimensional coding */ + public static final int GROUP3OPT_UNCOMPRESSED = 0x2; /* data not compressed */ + public static final int GROUP3OPT_FILLBITS = 0x4; /* fill to byte boundary */ + public static final int TIFFTAG_GROUP4OPTIONS = 293; /* 32 flag bits */ + public static final int GROUP4OPT_UNCOMPRESSED = 0x2; /* data not compressed */ + public static final int TIFFTAG_RESOLUTIONUNIT = 296; /* units of resolutions */ + public static final int RESUNIT_NONE = 1; /* no meaningful units */ + public static final int RESUNIT_INCH = 2; /* english */ + public static final int RESUNIT_CENTIMETER = 3; /* metric */ + public static final int TIFFTAG_PAGENUMBER = 297; /* page numbers of multi-page */ + public static final int TIFFTAG_COLORRESPONSEUNIT = 300; /* $color curve accuracy */ + public static final int COLORRESPONSEUNIT_10S = 1; /* tenths of a unit */ + public static final int COLORRESPONSEUNIT_100S = 2; /* hundredths of a unit */ + public static final int COLORRESPONSEUNIT_1000S = 3; /* thousandths of a unit */ + public static final int COLORRESPONSEUNIT_10000S = 4; /* ten-thousandths of a unit */ + public static final int COLORRESPONSEUNIT_100000S = 5; /* hundred-thousandths */ + public static final int TIFFTAG_TRANSFERFUNCTION = 301; /* !colorimetry info */ + public static final int TIFFTAG_SOFTWARE = 305; /* name & release */ + public static final int TIFFTAG_DATETIME = 306; /* creation date and time */ + public static final int TIFFTAG_ARTIST = 315; /* creator of image */ + public static final int TIFFTAG_HOSTCOMPUTER = 316; /* machine where created */ + public static final int TIFFTAG_PREDICTOR = 317; /* prediction scheme w/ LZW */ + public static final int TIFFTAG_WHITEPOINT = 318; /* image white point */ + public static final int TIFFTAG_PRIMARYCHROMATICITIES = 319; /* !primary chromaticities */ + public static final int TIFFTAG_COLORMAP = 320; /* RGB map for pallette image */ + public static final int TIFFTAG_HALFTONEHINTS = 321; /* !highlight+shadow info */ + public static final int TIFFTAG_TILEWIDTH = 322; /* !rows/data tile */ + public static final int TIFFTAG_TILELENGTH = 323; /* !cols/data tile */ + public static final int TIFFTAG_TILEOFFSETS = 324; /* !offsets to data tiles */ + public static final int TIFFTAG_TILEBYTECOUNTS = 325; /* !byte counts for tiles */ + public static final int TIFFTAG_BADFAXLINES = 326; /* lines w/ wrong pixel count */ + public static final int TIFFTAG_CLEANFAXDATA = 327; /* regenerated line info */ + public static final int CLEANFAXDATA_CLEAN = 0; /* no errors detected */ + public static final int CLEANFAXDATA_REGENERATED = 1; /* receiver regenerated lines */ + public static final int CLEANFAXDATA_UNCLEAN = 2; /* uncorrected errors exist */ + public static final int TIFFTAG_CONSECUTIVEBADFAXLINES = 328; /* max consecutive bad lines */ + public static final int TIFFTAG_SUBIFD = 330; /* subimage descriptors */ + public static final int TIFFTAG_INKSET = 332; /* !inks in separated image */ + public static final int INKSET_CMYK = 1; /* !cyan-magenta-yellow-black */ + public static final int TIFFTAG_INKNAMES = 333; /* !ascii names of inks */ + public static final int TIFFTAG_NUMBEROFINKS = 334; /* !number of inks */ + public static final int TIFFTAG_DOTRANGE = 336; /* !0% and 100% dot codes */ + public static final int TIFFTAG_TARGETPRINTER = 337; /* !separation target */ + public static final int TIFFTAG_EXTRASAMPLES = 338; /* !info about extra samples */ + public static final int EXTRASAMPLE_UNSPECIFIED = 0; /* !unspecified data */ + public static final int EXTRASAMPLE_ASSOCALPHA = 1; /* !associated alpha data */ + public static final int EXTRASAMPLE_UNASSALPHA = 2; /* !unassociated alpha data */ + public static final int TIFFTAG_SAMPLEFORMAT = 339; /* !data sample format */ + public static final int SAMPLEFORMAT_UINT = 1; /* !unsigned integer data */ + public static final int SAMPLEFORMAT_INT = 2; /* !signed integer data */ + public static final int SAMPLEFORMAT_IEEEFP = 3; /* !IEEE floating point data */ + public static final int SAMPLEFORMAT_VOID = 4; /* !untyped data */ + public static final int SAMPLEFORMAT_COMPLEXINT = 5; /* !complex signed int */ + public static final int SAMPLEFORMAT_COMPLEXIEEEFP = 6; /* !complex ieee floating */ + public static final int TIFFTAG_SMINSAMPLEVALUE = 340; /* !variable MinSampleValue */ + public static final int TIFFTAG_SMAXSAMPLEVALUE = 341; /* !variable MaxSampleValue */ + public static final int TIFFTAG_JPEGTABLES = 347; /* %JPEG table stream */ +/* + * Tags 512-521 are obsoleted by Technical Note #2 + * which specifies a revised JPEG-in-TIFF scheme. + */ + public static final int TIFFTAG_JPEGPROC = 512; /* !JPEG processing algorithm */ + public static final int JPEGPROC_BASELINE = 1; /* !baseline sequential */ + public static final int JPEGPROC_LOSSLESS = 14; /* !Huffman coded lossless */ + public static final int TIFFTAG_JPEGIFOFFSET = 513; /* !pointer to SOI marker */ + public static final int TIFFTAG_JPEGIFBYTECOUNT = 514; /* !JFIF stream length */ + public static final int TIFFTAG_JPEGRESTARTINTERVAL = 515; /* !restart interval length */ + public static final int TIFFTAG_JPEGLOSSLESSPREDICTORS = 517; /* !lossless proc predictor */ + public static final int TIFFTAG_JPEGPOINTTRANSFORM = 518; /* !lossless point transform */ + public static final int TIFFTAG_JPEGQTABLES = 519; /* !Q matrice offsets */ + public static final int TIFFTAG_JPEGDCTABLES = 520; /* !DCT table offsets */ + public static final int TIFFTAG_JPEGACTABLES = 521; /* !AC coefficient offsets */ + public static final int TIFFTAG_YCBCRCOEFFICIENTS = 529; /* !RGB -> YCbCr transform */ + public static final int TIFFTAG_YCBCRSUBSAMPLING = 530; /* !YCbCr subsampling factors */ + public static final int TIFFTAG_YCBCRPOSITIONING = 531; /* !subsample positioning */ + public static final int YCBCRPOSITION_CENTERED = 1; /* !as in PostScript Level 2 */ + public static final int YCBCRPOSITION_COSITED = 2; /* !as in CCIR 601-1 */ + public static final int TIFFTAG_REFERENCEBLACKWHITE = 532; /* !colorimetry info */ + /* tags 32952-32956 are private tags registered to Island Graphics */ + public static final int TIFFTAG_REFPTS = 32953; /* image reference points */ + public static final int TIFFTAG_REGIONTACKPOINT = 32954; /* region-xform tack point */ + public static final int TIFFTAG_REGIONWARPCORNERS = 32955; /* warp quadrilateral */ + public static final int TIFFTAG_REGIONAFFINE = 32956; /* affine transformation mat */ + /* tags 32995-32999 are private tags registered to SGI */ + public static final int TIFFTAG_MATTEING = 32995; /* $use ExtraSamples */ + public static final int TIFFTAG_DATATYPE = 32996; /* $use SampleFormat */ + public static final int TIFFTAG_IMAGEDEPTH = 32997; /* z depth of image */ + public static final int TIFFTAG_TILEDEPTH = 32998; /* z depth/data tile */ + /* tags 33300-33309 are private tags registered to Pixar */ +/* + * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH + * are set when an image has been cropped out of a larger image. + * They reflect the size of the original uncropped image. + * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used + * to determine the position of the smaller image in the larger one. + */ + public static final int TIFFTAG_PIXAR_IMAGEFULLWIDTH = 33300; /* full image size in x */ + public static final int TIFFTAG_PIXAR_IMAGEFULLLENGTH = 33301; /* full image size in y */ + /* Tags 33302-33306 are used to identify special image modes and data + * used by Pixar's texture formats. + */ + public static final int TIFFTAG_PIXAR_TEXTUREFORMAT = 33302; /* texture map format */ + public static final int TIFFTAG_PIXAR_WRAPMODES = 33303; /* s & t wrap modes */ + public static final int TIFFTAG_PIXAR_FOVCOT = 33304; /* cotan(fov) for env. maps */ + public static final int TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN = 33305; + public static final int TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA = 33306; + /* tag 33405 is a private tag registered to Eastman Kodak */ + public static final int TIFFTAG_WRITERSERIALNUMBER = 33405; /* device serial number */ + /* tag 33432 is listed in the 6.0 spec w/ unknown ownership */ + public static final int TIFFTAG_COPYRIGHT = 33432; /* copyright string */ + /* IPTC TAG from RichTIFF specifications */ + public static final int TIFFTAG_RICHTIFFIPTC = 33723; + /* 34016-34029 are reserved for ANSI IT8 TIFF/IT */ + public static final int TIFFTAG_STONITS = 37439; /* Sample value to Nits */ + /* tag 34929 is a private tag registered to FedEx */ + public static final int TIFFTAG_FEDEX_EDR = 34929; /* unknown use */ + /* tag 65535 is an undefined tag used by Eastman Kodak */ + public static final int TIFFTAG_DCSHUESHIFTVALUES = 65535; /* hue shift correction data */ + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFDirectory.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFDirectory.java new file mode 100644 index 0000000..abcc0dd --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFDirectory.java @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; +import java.io.IOException; +import java.io.EOFException; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.ArrayList; +import com.lowagie.text.pdf.RandomAccessFileOrArray; + +/** + * A class representing an Image File Directory (IFD) from a TIFF 6.0 + * stream. The TIFF file format is described in more detail in the + * comments for the TIFFDescriptor class. + * + *

A TIFF IFD consists of a set of TIFFField tags. Methods are + * provided to query the set of tags and to obtain the raw field + * array. In addition, convenience methods are provided for acquiring + * the values of tags that contain a single value that fits into a + * byte, int, long, float, or double. + * + *

Every TIFF file is made up of one or more public IFDs that are + * joined in a linked list, rooted in the file header. A file may + * also contain so-called private IFDs that are referenced from + * tag data and do not appear in the main list. + * + *

This class is not a committed part of the JAI API. It may + * be removed or changed in future releases of JAI. + * + * @see TIFFField + */ +public class TIFFDirectory extends Object implements Serializable { + + /** A boolean storing the endianness of the stream. */ + boolean isBigEndian; + + /** The number of entries in the IFD. */ + int numEntries; + + /** An array of TIFFFields. */ + TIFFField[] fields; + + /** A Hashtable indexing the fields by tag number. */ + Hashtable fieldIndex = new Hashtable(); + + /** The offset of this IFD. */ + long IFDOffset = 8; + + /** The offset of the next IFD. */ + long nextIFDOffset = 0; + + /** The default constructor. */ + TIFFDirectory() {} + + private static boolean isValidEndianTag(int endian) { + return ((endian == 0x4949) || (endian == 0x4d4d)); + } + + /** + * Constructs a TIFFDirectory from a SeekableStream. + * The directory parameter specifies which directory to read from + * the linked list present in the stream; directory 0 is normally + * read but it is possible to store multiple images in a single + * TIFF file by maintaing multiple directories. + * + * @param stream a SeekableStream to read from. + * @param directory the index of the directory to read. + */ + public TIFFDirectory(RandomAccessFileOrArray stream, int directory) + throws IOException { + + long global_save_offset = stream.getFilePointer(); + long ifd_offset; + + // Read the TIFF header + stream.seek(0L); + int endian = stream.readUnsignedShort(); + if (!isValidEndianTag(endian)) { + throw new + IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); + } + isBigEndian = (endian == 0x4d4d); + + int magic = readUnsignedShort(stream); + if (magic != 42) { + throw new + IllegalArgumentException("Bad magic number, should be 42."); + } + + // Get the initial ifd offset as an unsigned int (using a long) + ifd_offset = readUnsignedInt(stream); + + for (int i = 0; i < directory; i++) { + if (ifd_offset == 0L) { + throw new + IllegalArgumentException("Directory number too large."); + } + + stream.seek(ifd_offset); + int entries = readUnsignedShort(stream); + stream.skip(12*entries); + + ifd_offset = readUnsignedInt(stream); + } + + stream.seek(ifd_offset); + initialize(stream); + stream.seek(global_save_offset); + } + + /** + * Constructs a TIFFDirectory by reading a SeekableStream. + * The ifd_offset parameter specifies the stream offset from which + * to begin reading; this mechanism is sometimes used to store + * private IFDs within a TIFF file that are not part of the normal + * sequence of IFDs. + * + * @param stream a SeekableStream to read from. + * @param ifd_offset the long byte offset of the directory. + * @param directory the index of the directory to read beyond the + * one at the current stream offset; zero indicates the IFD + * at the current offset. + */ + public TIFFDirectory(RandomAccessFileOrArray stream, long ifd_offset, int directory) + throws IOException { + + long global_save_offset = stream.getFilePointer(); + stream.seek(0L); + int endian = stream.readUnsignedShort(); + if (!isValidEndianTag(endian)) { + throw new + IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); + } + isBigEndian = (endian == 0x4d4d); + + // Seek to the first IFD. + stream.seek(ifd_offset); + + // Seek to desired IFD if necessary. + int dirNum = 0; + while(dirNum < directory) { + // Get the number of fields in the current IFD. + int numEntries = readUnsignedShort(stream); + + // Skip to the next IFD offset value field. + stream.seek(ifd_offset + 12*numEntries); + + // Read the offset to the next IFD beyond this one. + ifd_offset = readUnsignedInt(stream); + + // Seek to the next IFD. + stream.seek(ifd_offset); + + // Increment the directory. + dirNum++; + } + + initialize(stream); + stream.seek(global_save_offset); + } + + private static final int[] sizeOfType = { + 0, // 0 = n/a + 1, // 1 = byte + 1, // 2 = ascii + 2, // 3 = short + 4, // 4 = long + 8, // 5 = rational + 1, // 6 = sbyte + 1, // 7 = undefined + 2, // 8 = sshort + 4, // 9 = slong + 8, // 10 = srational + 4, // 11 = float + 8 // 12 = double + }; + + private void initialize(RandomAccessFileOrArray stream) throws IOException { + long nextTagOffset = 0L; + long maxOffset = stream.length(); + int i, j; + + IFDOffset = stream.getFilePointer(); + + numEntries = readUnsignedShort(stream); + fields = new TIFFField[numEntries]; + + for (i = 0; (i < numEntries) && (nextTagOffset < maxOffset); i++) { + int tag = readUnsignedShort(stream); + int type = readUnsignedShort(stream); + int count = (int)(readUnsignedInt(stream)); + boolean processTag = true; + + // The place to return to to read the next tag + nextTagOffset = stream.getFilePointer() + 4; + + try { + // If the tag data can't fit in 4 bytes, the next 4 bytes + // contain the starting offset of the data + if (count*sizeOfType[type] > 4) { + long valueOffset = readUnsignedInt(stream); + + // bounds check offset for EOF + if (valueOffset < maxOffset) { + stream.seek(valueOffset); + } + else { + // bad offset pointer .. skip tag + processTag = false; + } + } + } catch (ArrayIndexOutOfBoundsException ae) { + // if the data type is unknown we should skip this TIFF Field + processTag = false; + } + + if (processTag) { + fieldIndex.put(new Integer(tag), new Integer(i)); + Object obj = null; + + switch (type) { + case TIFFField.TIFF_BYTE: + case TIFFField.TIFF_SBYTE: + case TIFFField.TIFF_UNDEFINED: + case TIFFField.TIFF_ASCII: + byte[] bvalues = new byte[count]; + stream.readFully(bvalues, 0, count); + + if (type == TIFFField.TIFF_ASCII) { + + // Can be multiple strings + int index = 0, prevIndex = 0; + ArrayList v = new ArrayList(); + + while (index < count) { + + while ((index < count) && (bvalues[index++] != 0)); + + // When we encountered zero, means one string has ended + v.add(new String(bvalues, prevIndex, + (index - prevIndex)) ); + prevIndex = index; + } + + count = v.size(); + String strings[] = new String[count]; + for (int c = 0 ; c < count; c++) { + strings[c] = (String)v.get(c); + } + + obj = strings; + } else { + obj = bvalues; + } + + break; + + case TIFFField.TIFF_SHORT: + char[] cvalues = new char[count]; + for (j = 0; j < count; j++) { + cvalues[j] = (char)(readUnsignedShort(stream)); + } + obj = cvalues; + break; + + case TIFFField.TIFF_LONG: + long[] lvalues = new long[count]; + for (j = 0; j < count; j++) { + lvalues[j] = readUnsignedInt(stream); + } + obj = lvalues; + break; + + case TIFFField.TIFF_RATIONAL: + long[][] llvalues = new long[count][2]; + for (j = 0; j < count; j++) { + llvalues[j][0] = readUnsignedInt(stream); + llvalues[j][1] = readUnsignedInt(stream); + } + obj = llvalues; + break; + + case TIFFField.TIFF_SSHORT: + short[] svalues = new short[count]; + for (j = 0; j < count; j++) { + svalues[j] = readShort(stream); + } + obj = svalues; + break; + + case TIFFField.TIFF_SLONG: + int[] ivalues = new int[count]; + for (j = 0; j < count; j++) { + ivalues[j] = readInt(stream); + } + obj = ivalues; + break; + + case TIFFField.TIFF_SRATIONAL: + int[][] iivalues = new int[count][2]; + for (j = 0; j < count; j++) { + iivalues[j][0] = readInt(stream); + iivalues[j][1] = readInt(stream); + } + obj = iivalues; + break; + + case TIFFField.TIFF_FLOAT: + float[] fvalues = new float[count]; + for (j = 0; j < count; j++) { + fvalues[j] = readFloat(stream); + } + obj = fvalues; + break; + + case TIFFField.TIFF_DOUBLE: + double[] dvalues = new double[count]; + for (j = 0; j < count; j++) { + dvalues[j] = readDouble(stream); + } + obj = dvalues; + break; + + default: + break; + } + + fields[i] = new TIFFField(tag, type, count, obj); + } + + stream.seek(nextTagOffset); + } + + // Read the offset of the next IFD. + nextIFDOffset = readUnsignedInt(stream); + } + + /** Returns the number of directory entries. */ + public int getNumEntries() { + return numEntries; + } + + /** + * Returns the value of a given tag as a TIFFField, + * or null if the tag is not present. + */ + public TIFFField getField(int tag) { + Integer i = (Integer)fieldIndex.get(new Integer(tag)); + if (i == null) { + return null; + } else { + return fields[i.intValue()]; + } + } + + /** + * Returns true if a tag appears in the directory. + */ + public boolean isTagPresent(int tag) { + return fieldIndex.containsKey(new Integer(tag)); + } + + /** + * Returns an ordered array of ints indicating the tag + * values. + */ + public int[] getTags() { + int[] tags = new int[fieldIndex.size()]; + Enumeration e = fieldIndex.keys(); + int i = 0; + + while (e.hasMoreElements()) { + tags[i++] = ((Integer)e.nextElement()).intValue(); + } + + return tags; + } + + /** + * Returns an array of TIFFFields containing all the fields + * in this directory. + */ + public TIFFField[] getFields() { + return fields; + } + + /** + * Returns the value of a particular index of a given tag as a + * byte. The caller is responsible for ensuring that the tag is + * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or + * TIFF_UNDEFINED. + */ + public byte getFieldAsByte(int tag, int index) { + Integer i = (Integer)fieldIndex.get(new Integer(tag)); + byte [] b = (fields[i.intValue()]).getAsBytes(); + return b[index]; + } + + /** + * Returns the value of index 0 of a given tag as a + * byte. The caller is responsible for ensuring that the tag is + * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or + * TIFF_UNDEFINED. + */ + public byte getFieldAsByte(int tag) { + return getFieldAsByte(tag, 0); + } + + /** + * Returns the value of a particular index of a given tag as a + * long. The caller is responsible for ensuring that the tag is + * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, + * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. + */ + public long getFieldAsLong(int tag, int index) { + Integer i = (Integer)fieldIndex.get(new Integer(tag)); + return (fields[i.intValue()]).getAsLong(index); + } + + /** + * Returns the value of index 0 of a given tag as a + * long. The caller is responsible for ensuring that the tag is + * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, + * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. + */ + public long getFieldAsLong(int tag) { + return getFieldAsLong(tag, 0); + } + + /** + * Returns the value of a particular index of a given tag as a + * float. The caller is responsible for ensuring that the tag is + * present and has numeric type (all but TIFF_UNDEFINED and + * TIFF_ASCII). + */ + public float getFieldAsFloat(int tag, int index) { + Integer i = (Integer)fieldIndex.get(new Integer(tag)); + return fields[i.intValue()].getAsFloat(index); + } + + /** + * Returns the value of index 0 of a given tag as a float. The + * caller is responsible for ensuring that the tag is present and + * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). + */ + public float getFieldAsFloat(int tag) { + return getFieldAsFloat(tag, 0); + } + + /** + * Returns the value of a particular index of a given tag as a + * double. The caller is responsible for ensuring that the tag is + * present and has numeric type (all but TIFF_UNDEFINED and + * TIFF_ASCII). + */ + public double getFieldAsDouble(int tag, int index) { + Integer i = (Integer)fieldIndex.get(new Integer(tag)); + return fields[i.intValue()].getAsDouble(index); + } + + /** + * Returns the value of index 0 of a given tag as a double. The + * caller is responsible for ensuring that the tag is present and + * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). + */ + public double getFieldAsDouble(int tag) { + return getFieldAsDouble(tag, 0); + } + + // Methods to read primitive data types from the stream + + private short readShort(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readShort(); + } else { + return stream.readShortLE(); + } + } + + private int readUnsignedShort(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readUnsignedShort(); + } else { + return stream.readUnsignedShortLE(); + } + } + + private int readInt(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readInt(); + } else { + return stream.readIntLE(); + } + } + + private long readUnsignedInt(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readUnsignedInt(); + } else { + return stream.readUnsignedIntLE(); + } + } + + private long readLong(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readLong(); + } else { + return stream.readLongLE(); + } + } + + private float readFloat(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readFloat(); + } else { + return stream.readFloatLE(); + } + } + + private double readDouble(RandomAccessFileOrArray stream) + throws IOException { + if (isBigEndian) { + return stream.readDouble(); + } else { + return stream.readDoubleLE(); + } + } + + private static int readUnsignedShort(RandomAccessFileOrArray stream, + boolean isBigEndian) + throws IOException { + if (isBigEndian) { + return stream.readUnsignedShort(); + } else { + return stream.readUnsignedShortLE(); + } + } + + private static long readUnsignedInt(RandomAccessFileOrArray stream, + boolean isBigEndian) + throws IOException { + if (isBigEndian) { + return stream.readUnsignedInt(); + } else { + return stream.readUnsignedIntLE(); + } + } + + // Utilities + + /** + * Returns the number of image directories (subimages) stored in a + * given TIFF file, represented by a SeekableStream. + */ + public static int getNumDirectories(RandomAccessFileOrArray stream) + throws IOException{ + long pointer = stream.getFilePointer(); // Save stream pointer + + stream.seek(0L); + int endian = stream.readUnsignedShort(); + if (!isValidEndianTag(endian)) { + throw new + IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); + } + boolean isBigEndian = (endian == 0x4d4d); + int magic = readUnsignedShort(stream, isBigEndian); + if (magic != 42) { + throw new + IllegalArgumentException("Bad magic number, should be 42."); + } + + stream.seek(4L); + long offset = readUnsignedInt(stream, isBigEndian); + + int numDirectories = 0; + while (offset != 0L) { + ++numDirectories; + + // EOFException means IFD was probably not properly terminated. + try { + stream.seek(offset); + int entries = readUnsignedShort(stream, isBigEndian); + stream.skip(12*entries); + offset = readUnsignedInt(stream, isBigEndian); + } catch(EOFException eof) { + numDirectories--; + break; + } + } + + stream.seek(pointer); // Reset stream pointer + return numDirectories; + } + + /** + * Returns a boolean indicating whether the byte order used in the + * the TIFF file is big-endian (i.e. whether the byte order is from + * the most significant to the least significant) + */ + public boolean isBigEndian() { + return isBigEndian; + } + + /** + * Returns the offset of the IFD corresponding to this + * TIFFDirectory. + */ + public long getIFDOffset() { + return IFDOffset; + } + + /** + * Returns the offset of the next IFD after the IFD corresponding to this + * TIFFDirectory. + */ + public long getNextIFDOffset() { + return nextIFDOffset; + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java new file mode 100644 index 0000000..7a7b61c --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java @@ -0,0 +1,1477 @@ +/* + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; + +public class TIFFFaxDecoder { + + private int bitPointer, bytePointer; + private byte[] data; + private int w, h; + private int fillOrder; + + // Data structures needed to store changing elements for the previous + // and the current scanline + private int changingElemSize = 0; + private int prevChangingElems[]; + private int currChangingElems[]; + + // Element at which to start search in getNextChangingElement + private int lastChangingElement = 0; + + private int compression = 2; + + // Variables set by T4Options + private int uncompressedMode = 0; + private int fillBits = 0; + private int oneD; + + static int table1[] = { + 0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN + 0x01, // 1 bits are left in first byte + 0x03, // 2 bits are left in first byte + 0x07, // 3 bits are left in first byte + 0x0f, // 4 bits are left in first byte + 0x1f, // 5 bits are left in first byte + 0x3f, // 6 bits are left in first byte + 0x7f, // 7 bits are left in first byte + 0xff // 8 bits are left in first byte + }; + + static int table2[] = { + 0x00, // 0 + 0x80, // 1 + 0xc0, // 2 + 0xe0, // 3 + 0xf0, // 4 + 0xf8, // 5 + 0xfc, // 6 + 0xfe, // 7 + 0xff // 8 + }; + + // Table to be used when fillOrder = 2, for flipping bytes. + static byte flipTable[] = { + 0, -128, 64, -64, 32, -96, 96, -32, + 16, -112, 80, -48, 48, -80, 112, -16, + 8, -120, 72, -56, 40, -88, 104, -24, + 24, -104, 88, -40, 56, -72, 120, -8, + 4, -124, 68, -60, 36, -92, 100, -28, + 20, -108, 84, -44, 52, -76, 116, -12, + 12, -116, 76, -52, 44, -84, 108, -20, + 28, -100, 92, -36, 60, -68, 124, -4, + 2, -126, 66, -62, 34, -94, 98, -30, + 18, -110, 82, -46, 50, -78, 114, -14, + 10, -118, 74, -54, 42, -86, 106, -22, + 26, -102, 90, -38, 58, -70, 122, -6, + 6, -122, 70, -58, 38, -90, 102, -26, + 22, -106, 86, -42, 54, -74, 118, -10, + 14, -114, 78, -50, 46, -82, 110, -18, + 30, -98, 94, -34, 62, -66, 126, -2, + 1, -127, 65, -63, 33, -95, 97, -31, + 17, -111, 81, -47, 49, -79, 113, -15, + 9, -119, 73, -55, 41, -87, 105, -23, + 25, -103, 89, -39, 57, -71, 121, -7, + 5, -123, 69, -59, 37, -91, 101, -27, + 21, -107, 85, -43, 53, -75, 117, -11, + 13, -115, 77, -51, 45, -83, 109, -19, + 29, -99, 93, -35, 61, -67, 125, -3, + 3, -125, 67, -61, 35, -93, 99, -29, + 19, -109, 83, -45, 51, -77, 115, -13, + 11, -117, 75, -53, 43, -85, 107, -21, + 27, -101, 91, -37, 59, -69, 123, -5, + 7, -121, 71, -57, 39, -89, 103, -25, + 23, -105, 87, -41, 55, -73, 119, -9, + 15, -113, 79, -49, 47, -81, 111, -17, + 31, -97, 95, -33, 63, -65, 127, -1, + }; + + // The main 10 bit white runs lookup table + static short white[] = { + // 0 - 7 + 6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225, + // 8 - 15 + 944, 944, 944, 944, 976, 976, 976, 976, + // 16 - 23 + 1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488, + // 24 - 31 + 718, 718, 718, 718, 718, 718, 718, 718, + // 32 - 39 + 750, 750, 750, 750, 750, 750, 750, 750, + // 40 - 47 + 1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552, + // 48 - 55 + 428, 428, 428, 428, 428, 428, 428, 428, + // 56 - 63 + 428, 428, 428, 428, 428, 428, 428, 428, + // 64 - 71 + 654, 654, 654, 654, 654, 654, 654, 654, + // 72 - 79 + 1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104, + // 80 - 87 + 1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168, + // 88 - 95 + 1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232, + // 96 - 103 + 622, 622, 622, 622, 622, 622, 622, 622, + // 104 - 111 + 1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040, + // 112 - 119 + 44, 44, 44, 44, 44, 44, 44, 44, + // 120 - 127 + 44, 44, 44, 44, 44, 44, 44, 44, + // 128 - 135 + 396, 396, 396, 396, 396, 396, 396, 396, + // 136 - 143 + 396, 396, 396, 396, 396, 396, 396, 396, + // 144 - 151 + 1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744, + // 152 - 159 + 846, 846, 846, 846, 846, 846, 846, 846, + // 160 - 167 + 1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296, + // 168 - 175 + 1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360, + // 176 - 183 + 1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424, + // 184 - 191 + 686, 686, 686, 686, 686, 686, 686, 686, + // 192 - 199 + 910, 910, 910, 910, 910, 910, 910, 910, + // 200 - 207 + 1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000, + // 208 - 215 + 2032, 2032, 2032, 2032, 16, 16, 16, 16, + // 216 - 223 + 10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305, + // 224 - 231 + 330, 330, 330, 330, 330, 330, 330, 330, + // 232 - 239 + 330, 330, 330, 330, 330, 330, 330, 330, + // 240 - 247 + 330, 330, 330, 330, 330, 330, 330, 330, + // 248 - 255 + 330, 330, 330, 330, 330, 330, 330, 330, + // 256 - 263 + 362, 362, 362, 362, 362, 362, 362, 362, + // 264 - 271 + 362, 362, 362, 362, 362, 362, 362, 362, + // 272 - 279 + 362, 362, 362, 362, 362, 362, 362, 362, + // 280 - 287 + 362, 362, 362, 362, 362, 362, 362, 362, + // 288 - 295 + 878, 878, 878, 878, 878, 878, 878, 878, + // 296 - 303 + 1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936, + // 304 - 311 + -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221, + // 312 - 319 + 590, 590, 590, 590, 590, 590, 590, 590, + // 320 - 327 + 782, 782, 782, 782, 782, 782, 782, 782, + // 328 - 335 + 1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616, + // 336 - 343 + 1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680, + // 344 - 351 + 814, 814, 814, 814, 814, 814, 814, 814, + // 352 - 359 + 1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808, + // 360 - 367 + 1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872, + // 368 - 375 + 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, + // 376 - 383 + 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, + // 384 - 391 + -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, + // 392 - 399 + -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, + // 400 - 407 + 14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401, + // 408 - 415 + 22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497, + // 416 - 423 + 18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691, + // 424 - 431 + 30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653, + // 432 - 439 + -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461, + // 440 - 447 + 8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207, + // 448 - 455 + 72, 72, 72, 72, 72, 72, 72, 72, + // 456 - 463 + 72, 72, 72, 72, 72, 72, 72, 72, + // 464 - 471 + 72, 72, 72, 72, 72, 72, 72, 72, + // 472 - 479 + 72, 72, 72, 72, 72, 72, 72, 72, + // 480 - 487 + 72, 72, 72, 72, 72, 72, 72, 72, + // 488 - 495 + 72, 72, 72, 72, 72, 72, 72, 72, + // 496 - 503 + 72, 72, 72, 72, 72, 72, 72, 72, + // 504 - 511 + 72, 72, 72, 72, 72, 72, 72, 72, + // 512 - 519 + 104, 104, 104, 104, 104, 104, 104, 104, + // 520 - 527 + 104, 104, 104, 104, 104, 104, 104, 104, + // 528 - 535 + 104, 104, 104, 104, 104, 104, 104, 104, + // 536 - 543 + 104, 104, 104, 104, 104, 104, 104, 104, + // 544 - 551 + 104, 104, 104, 104, 104, 104, 104, 104, + // 552 - 559 + 104, 104, 104, 104, 104, 104, 104, 104, + // 560 - 567 + 104, 104, 104, 104, 104, 104, 104, 104, + // 568 - 575 + 104, 104, 104, 104, 104, 104, 104, 104, + // 576 - 583 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 584 - 591 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 592 - 599 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 600 - 607 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 608 - 615 + 266, 266, 266, 266, 266, 266, 266, 266, + // 616 - 623 + 266, 266, 266, 266, 266, 266, 266, 266, + // 624 - 631 + 266, 266, 266, 266, 266, 266, 266, 266, + // 632 - 639 + 266, 266, 266, 266, 266, 266, 266, 266, + // 640 - 647 + 298, 298, 298, 298, 298, 298, 298, 298, + // 648 - 655 + 298, 298, 298, 298, 298, 298, 298, 298, + // 656 - 663 + 298, 298, 298, 298, 298, 298, 298, 298, + // 664 - 671 + 298, 298, 298, 298, 298, 298, 298, 298, + // 672 - 679 + 524, 524, 524, 524, 524, 524, 524, 524, + // 680 - 687 + 524, 524, 524, 524, 524, 524, 524, 524, + // 688 - 695 + 556, 556, 556, 556, 556, 556, 556, 556, + // 696 - 703 + 556, 556, 556, 556, 556, 556, 556, 556, + // 704 - 711 + 136, 136, 136, 136, 136, 136, 136, 136, + // 712 - 719 + 136, 136, 136, 136, 136, 136, 136, 136, + // 720 - 727 + 136, 136, 136, 136, 136, 136, 136, 136, + // 728 - 735 + 136, 136, 136, 136, 136, 136, 136, 136, + // 736 - 743 + 136, 136, 136, 136, 136, 136, 136, 136, + // 744 - 751 + 136, 136, 136, 136, 136, 136, 136, 136, + // 752 - 759 + 136, 136, 136, 136, 136, 136, 136, 136, + // 760 - 767 + 136, 136, 136, 136, 136, 136, 136, 136, + // 768 - 775 + 168, 168, 168, 168, 168, 168, 168, 168, + // 776 - 783 + 168, 168, 168, 168, 168, 168, 168, 168, + // 784 - 791 + 168, 168, 168, 168, 168, 168, 168, 168, + // 792 - 799 + 168, 168, 168, 168, 168, 168, 168, 168, + // 800 - 807 + 168, 168, 168, 168, 168, 168, 168, 168, + // 808 - 815 + 168, 168, 168, 168, 168, 168, 168, 168, + // 816 - 823 + 168, 168, 168, 168, 168, 168, 168, 168, + // 824 - 831 + 168, 168, 168, 168, 168, 168, 168, 168, + // 832 - 839 + 460, 460, 460, 460, 460, 460, 460, 460, + // 840 - 847 + 460, 460, 460, 460, 460, 460, 460, 460, + // 848 - 855 + 492, 492, 492, 492, 492, 492, 492, 492, + // 856 - 863 + 492, 492, 492, 492, 492, 492, 492, 492, + // 864 - 871 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 872 - 879 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 880 - 887 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 888 - 895 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 896 - 903 + 200, 200, 200, 200, 200, 200, 200, 200, + // 904 - 911 + 200, 200, 200, 200, 200, 200, 200, 200, + // 912 - 919 + 200, 200, 200, 200, 200, 200, 200, 200, + // 920 - 927 + 200, 200, 200, 200, 200, 200, 200, 200, + // 928 - 935 + 200, 200, 200, 200, 200, 200, 200, 200, + // 936 - 943 + 200, 200, 200, 200, 200, 200, 200, 200, + // 944 - 951 + 200, 200, 200, 200, 200, 200, 200, 200, + // 952 - 959 + 200, 200, 200, 200, 200, 200, 200, 200, + // 960 - 967 + 232, 232, 232, 232, 232, 232, 232, 232, + // 968 - 975 + 232, 232, 232, 232, 232, 232, 232, 232, + // 976 - 983 + 232, 232, 232, 232, 232, 232, 232, 232, + // 984 - 991 + 232, 232, 232, 232, 232, 232, 232, 232, + // 992 - 999 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1000 - 1007 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1008 - 1015 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1016 - 1023 + 232, 232, 232, 232, 232, 232, 232, 232, + }; + + // Additional make up codes for both White and Black runs + static short additionalMakeup[] = { + 28679, 28679, 31752, (short)32777, + (short)33801, (short)34825, (short)35849, (short)36873, + (short)29703, (short)29703, (short)30727, (short)30727, + (short)37897, (short)38921, (short)39945, (short)40969 + }; + + // Initial black run look up table, uses the first 4 bits of a code + static short initBlack[] = { + // 0 - 7 + 3226, 6412, 200, 168, 38, 38, 134, 134, + // 8 - 15 + 100, 100, 100, 100, 68, 68, 68, 68 + }; + + // + static short twoBitBlack[] = {292, 260, 226, 226}; // 0 - 3 + + // Main black run table, using the last 9 bits of possible 13 bit code + static short black[] = { + // 0 - 7 + 62, 62, 30, 30, 0, 0, 0, 0, + // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, + // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, + // 24 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, + // 32 - 39 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 40 - 47 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 48 - 55 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 56 - 63 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 64 - 71 + 588, 588, 588, 588, 588, 588, 588, 588, + // 72 - 79 + 1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776, + // 80 - 87 + 1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904, + // 88 - 95 + 1936, 1936, -16365, -14317, 782, 782, 782, 782, + // 96 - 103 + 814, 814, 814, 814, -12269, -10221, 10257, 10257, + // 104 - 111 + 12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712, + // 112 - 119 + 1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605, + // 120 - 127 + 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, + // 128 - 135 + 424, 424, 424, 424, 424, 424, 424, 424, + // 136 - 143 + 424, 424, 424, 424, 424, 424, 424, 424, + // 144 - 151 + 424, 424, 424, 424, 424, 424, 424, 424, + // 152 - 159 + 424, 424, 424, 424, 424, 424, 424, 424, + // 160 - 167 + 750, 750, 750, 750, 1616, 1616, 1648, 1648, + // 168 - 175 + 1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520, + // 176 - 183 + 1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209, + // 184 - 191 + 524, 524, 524, 524, 524, 524, 524, 524, + // 192 - 199 + 556, 556, 556, 556, 556, 556, 556, 556, + // 200 - 207 + 1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032, + // 208 - 215 + 976, 976, 1008, 1008, 1040, 1040, 1072, 1072, + // 216 - 223 + 1296, 1296, 1328, 1328, 718, 718, 718, 718, + // 224 - 231 + 456, 456, 456, 456, 456, 456, 456, 456, + // 232 - 239 + 456, 456, 456, 456, 456, 456, 456, 456, + // 240 - 247 + 456, 456, 456, 456, 456, 456, 456, 456, + // 248 - 255 + 456, 456, 456, 456, 456, 456, 456, 456, + // 256 - 263 + 326, 326, 326, 326, 326, 326, 326, 326, + // 264 - 271 + 326, 326, 326, 326, 326, 326, 326, 326, + // 272 - 279 + 326, 326, 326, 326, 326, 326, 326, 326, + // 280 - 287 + 326, 326, 326, 326, 326, 326, 326, 326, + // 288 - 295 + 326, 326, 326, 326, 326, 326, 326, 326, + // 296 - 303 + 326, 326, 326, 326, 326, 326, 326, 326, + // 304 - 311 + 326, 326, 326, 326, 326, 326, 326, 326, + // 312 - 319 + 326, 326, 326, 326, 326, 326, 326, 326, + // 320 - 327 + 358, 358, 358, 358, 358, 358, 358, 358, + // 328 - 335 + 358, 358, 358, 358, 358, 358, 358, 358, + // 336 - 343 + 358, 358, 358, 358, 358, 358, 358, 358, + // 344 - 351 + 358, 358, 358, 358, 358, 358, 358, 358, + // 352 - 359 + 358, 358, 358, 358, 358, 358, 358, 358, + // 360 - 367 + 358, 358, 358, 358, 358, 358, 358, 358, + // 368 - 375 + 358, 358, 358, 358, 358, 358, 358, 358, + // 376 - 383 + 358, 358, 358, 358, 358, 358, 358, 358, + // 384 - 391 + 490, 490, 490, 490, 490, 490, 490, 490, + // 392 - 399 + 490, 490, 490, 490, 490, 490, 490, 490, + // 400 - 407 + 4113, 4113, 6161, 6161, 848, 848, 880, 880, + // 408 - 415 + 912, 912, 944, 944, 622, 622, 622, 622, + // 416 - 423 + 654, 654, 654, 654, 1104, 1104, 1136, 1136, + // 424 - 431 + 1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264, + // 432 - 439 + 686, 686, 686, 686, 1360, 1360, 1392, 1392, + // 440 - 447 + 12, 12, 12, 12, 12, 12, 12, 12, + // 448 - 455 + 390, 390, 390, 390, 390, 390, 390, 390, + // 456 - 463 + 390, 390, 390, 390, 390, 390, 390, 390, + // 464 - 471 + 390, 390, 390, 390, 390, 390, 390, 390, + // 472 - 479 + 390, 390, 390, 390, 390, 390, 390, 390, + // 480 - 487 + 390, 390, 390, 390, 390, 390, 390, 390, + // 488 - 495 + 390, 390, 390, 390, 390, 390, 390, 390, + // 496 - 503 + 390, 390, 390, 390, 390, 390, 390, 390, + // 504 - 511 + 390, 390, 390, 390, 390, 390, 390, 390, + }; + + static byte twoDCodes[] = { + // 0 - 7 + 80, 88, 23, 71, 30, 30, 62, 62, + // 8 - 15 + 4, 4, 4, 4, 4, 4, 4, 4, + // 16 - 23 + 11, 11, 11, 11, 11, 11, 11, 11, + // 24 - 31 + 11, 11, 11, 11, 11, 11, 11, 11, + // 32 - 39 + 35, 35, 35, 35, 35, 35, 35, 35, + // 40 - 47 + 35, 35, 35, 35, 35, 35, 35, 35, + // 48 - 55 + 51, 51, 51, 51, 51, 51, 51, 51, + // 56 - 63 + 51, 51, 51, 51, 51, 51, 51, 51, + // 64 - 71 + 41, 41, 41, 41, 41, 41, 41, 41, + // 72 - 79 + 41, 41, 41, 41, 41, 41, 41, 41, + // 80 - 87 + 41, 41, 41, 41, 41, 41, 41, 41, + // 88 - 95 + 41, 41, 41, 41, 41, 41, 41, 41, + // 96 - 103 + 41, 41, 41, 41, 41, 41, 41, 41, + // 104 - 111 + 41, 41, 41, 41, 41, 41, 41, 41, + // 112 - 119 + 41, 41, 41, 41, 41, 41, 41, 41, + // 120 - 127 + 41, 41, 41, 41, 41, 41, 41, 41, + }; + + /** + * @param fillOrder The fill order of the compressed data bytes. + * @param w + * @param h + */ + public TIFFFaxDecoder(int fillOrder, int w, int h) { + this.fillOrder = fillOrder; + this.w = w; + this.h = h; + + this.bitPointer = 0; + this.bytePointer = 0; + this.prevChangingElems = new int[w]; + this.currChangingElems = new int[w]; + } + + // One-dimensional decoding methods + + public void decode1D(byte[] buffer, byte[] compData, + int startX, int height) { + this.data = compData; + + int lineOffset = 0; + int scanlineStride = (w + 7)/8; + + bitPointer = 0; + bytePointer = 0; + + for (int i = 0; i < height; i++) { + decodeNextScanline(buffer, lineOffset, startX); + lineOffset += scanlineStride; + } + } + + public void decodeNextScanline(byte[] buffer, + int lineOffset, int bitOffset) { + int bits = 0, code = 0, isT = 0; + int current, entry, twoBits; + boolean isWhite = true; + + // Initialize starting of the changing elements array + changingElemSize = 0; + + // While scanline not complete + while (bitOffset < w) { + while (isWhite) { + // White run + current = nextNBits(10); + entry = white[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x0f; + + if (bits == 12) { // Additional Make up code + // Get the next 2 bits + twoBits = nextLesserThan8Bits(2); + // Consolidate the 2 new bits and last 2 bits into 4 bits + current = ((current << 2) & 0x000c) | twoBits; + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + bitOffset += code; // Skip white run + + updatePointer(4 - bits); + } else if (bits == 0) { // ERROR + throw new RuntimeException("Invalid code encountered."); + } else if (bits == 15) { // EOL + throw new RuntimeException("EOL code word encountered in White run."); + } else { + // 11 bits - 0000 0111 1111 1111 = 0x07ff + code = (entry >>> 5) & 0x07ff; + bitOffset += code; + + updatePointer(10 - bits); + if (isT == 0) { + isWhite = false; + currChangingElems[changingElemSize++] = bitOffset; + } + } + } + + // Check whether this run completed one width, if so + // advance to next byte boundary for compression = 2. + if (bitOffset == w) { + if (compression == 2) { + advancePointer(); + } + break; + } + + while (isWhite == false) { + // Black run + current = nextLesserThan8Bits(4); + entry = initBlack[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (code == 100) { + current = nextNBits(9); + entry = black[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (bits == 12) { + // Additional makeup codes + updatePointer(5); + current = nextLesserThan8Bits(4); + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(4 - bits); + } else if (bits == 15) { + // EOL code + throw new RuntimeException("EOL code word encountered in Black run."); + } else { + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(9 - bits); + if (isT == 0) { + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } + } + } else if (code == 200) { + // Is a Terminating code + current = nextLesserThan8Bits(2); + entry = twoBitBlack[current]; + code = (entry >>> 5) & 0x07ff; + bits = (entry >>> 1) & 0x0f; + + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(2 - bits); + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } else { + // Is a Terminating code + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(4 - bits); + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } + } + + // Check whether this run completed one width + if (bitOffset == w) { + if (compression == 2) { + advancePointer(); + } + break; + } + } + + currChangingElems[changingElemSize++] = bitOffset; + } + + // Two-dimensional decoding methods + + public void decode2D(byte[] buffer, + byte compData[], + int startX, + int height, + long tiffT4Options) { + this.data = compData; + compression = 3; + + bitPointer = 0; + bytePointer = 0; + + int scanlineStride = (w + 7)/8; + + int a0, a1, b1, b2; + int[] b = new int[2]; + int entry, code, bits; + boolean isWhite; + int currIndex = 0; + int temp[]; + + // fillBits - dealt with this in readEOL + // 1D/2D encoding - dealt with this in readEOL + + // uncompressedMode - haven't dealt with this yet. + + + oneD = (int)(tiffT4Options & 0x01); + uncompressedMode = (int)((tiffT4Options & 0x02) >> 1); + fillBits = (int)((tiffT4Options & 0x04) >> 2); + + // The data must start with an EOL code + if (readEOL(true) != 1) { + throw new RuntimeException("First scanline must be 1D encoded."); + } + + int lineOffset = 0; + int bitOffset; + + // Then the 1D encoded scanline data will occur, changing elements + // array gets set. + decodeNextScanline(buffer, lineOffset, startX); + lineOffset += scanlineStride; + + for (int lines = 1; lines < height; lines++) { + + // Every line must begin with an EOL followed by a bit which + // indicates whether the following scanline is 1D or 2D encoded. + if (readEOL(false) == 0) { + // 2D encoded scanline follows + + // Initialize previous scanlines changing elements, and + // initialize current scanline's changing elements array + temp = prevChangingElems; + prevChangingElems = currChangingElems; + currChangingElems = temp; + currIndex = 0; + + // a0 has to be set just before the start of this scanline. + a0 = -1; + isWhite = true; + bitOffset = startX; + + lastChangingElement = 0; + + while (bitOffset < w) { + // Get the next changing element + getNextChangingElement(a0, isWhite, b); + + b1 = b[0]; + b2 = b[1]; + + // Get the next seven bits + entry = nextLesserThan8Bits(7); + + // Run these through the 2DCodes table + entry = (int)(twoDCodes[entry] & 0xff); + + // Get the code and the number of bits used up + code = (entry & 0x78) >>> 3; + bits = entry & 0x07; + + if (code == 0) { + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + b2 - bitOffset); + } + bitOffset = a0 = b2; + + // Set pointer to consume the correct number of bits. + updatePointer(7 - bits); + } else if (code == 1) { + // Horizontal + updatePointer(7 - bits); + + // identify the next 2 codes. + int number; + if (isWhite) { + number = decodeWhiteCodeWord(); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + } else { + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + + number = decodeWhiteCodeWord(); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + } + + a0 = bitOffset; + } else if (code <= 8) { + // Vertical + a1 = b1 + (code - 5); + + currChangingElems[currIndex++] = a1; + + // We write the current color till a1 - 1 pos, + // since a1 is where the next color starts + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + a1 - bitOffset); + } + bitOffset = a0 = a1; + isWhite = !isWhite; + + updatePointer(7 - bits); + } else { + throw new RuntimeException("Invalid code encountered while decoding 2D group 3 compressed data."); + } + } + + // Add the changing element beyond the current scanline for the + // other color too + currChangingElems[currIndex++] = bitOffset; + changingElemSize = currIndex; + } else { + // 1D encoded scanline follows + decodeNextScanline(buffer, lineOffset, startX); + } + + lineOffset += scanlineStride; + } + } + + public synchronized void decodeT6(byte[] buffer, + byte[] compData, + int startX, + int height, + long tiffT6Options) { + this.data = compData; + compression = 4; + + bitPointer = 0; + bytePointer = 0; + + int scanlineStride = (w + 7)/8; + + int a0, a1, b1, b2; + int entry, code, bits; + boolean isWhite; + int currIndex; + int temp[]; + + // Return values from getNextChangingElement + int[] b = new int[2]; + + // uncompressedMode - have written some code for this, but this + // has not been tested due to lack of test images using this optional + + uncompressedMode = (int)((tiffT6Options & 0x02) >> 1); + + // Local cached reference + int[] cce = currChangingElems; + + // Assume invisible preceding row of all white pixels and insert + // both black and white changing elements beyond the end of this + // imaginary scanline. + changingElemSize = 0; + cce[changingElemSize++] = w; + cce[changingElemSize++] = w; + + int lineOffset = 0; + int bitOffset; + + for (int lines = 0; lines < height; lines++) { + // a0 has to be set just before the start of the scanline. + a0 = -1; + isWhite = true; + + // Assign the changing elements of the previous scanline to + // prevChangingElems and start putting this new scanline's + // changing elements into the currChangingElems. + temp = prevChangingElems; + prevChangingElems = currChangingElems; + cce = currChangingElems = temp; + currIndex = 0; + + // Start decoding the scanline at startX in the raster + bitOffset = startX; + + // Reset search start position for getNextChangingElement + lastChangingElement = 0; + + // Till one whole scanline is decoded + while (bitOffset < w) { + // Get the next changing element + getNextChangingElement(a0, isWhite, b); + b1 = b[0]; + b2 = b[1]; + + // Get the next seven bits + entry = nextLesserThan8Bits(7); + // Run these through the 2DCodes table + entry = (int)(twoDCodes[entry] & 0xff); + + // Get the code and the number of bits used up + code = (entry & 0x78) >>> 3; + bits = entry & 0x07; + + if (code == 0) { // Pass + // We always assume WhiteIsZero format for fax. + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + b2 - bitOffset); + } + bitOffset = a0 = b2; + + // Set pointer to only consume the correct number of bits. + updatePointer(7 - bits); + } else if (code == 1) { // Horizontal + // Set pointer to only consume the correct number of bits. + updatePointer(7 - bits); + + // identify the next 2 alternating color codes. + int number; + if (isWhite) { + // Following are white and black runs + number = decodeWhiteCodeWord(); + bitOffset += number; + cce[currIndex++] = bitOffset; + + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + cce[currIndex++] = bitOffset; + } else { + // First a black run and then a white run follows + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + cce[currIndex++] = bitOffset; + + number = decodeWhiteCodeWord(); + bitOffset += number; + cce[currIndex++] = bitOffset; + } + + a0 = bitOffset; + } else if (code <= 8) { // Vertical + a1 = b1 + (code - 5); + cce[currIndex++] = a1; + + // We write the current color till a1 - 1 pos, + // since a1 is where the next color starts + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + a1 - bitOffset); + } + bitOffset = a0 = a1; + isWhite = !isWhite; + + updatePointer(7 - bits); + } else if (code == 11) { + if (nextLesserThan8Bits(3) != 7) { + throw new RuntimeException("Invalid code encountered while decoding 2D group 4 compressed data."); + } + + int zeros = 0; + boolean exit = false; + + while (!exit) { + while (nextLesserThan8Bits(1) != 1) { + zeros++; + } + + if (zeros > 5) { + // Exit code + + // Zeros before exit code + zeros = zeros - 6; + + if (!isWhite && (zeros > 0)) { + cce[currIndex++] = bitOffset; + } + + // Zeros before the exit code + bitOffset += zeros; + if (zeros > 0) { + // Some zeros have been written + isWhite = true; + } + + // Read in the bit which specifies the color of + // the following run + if (nextLesserThan8Bits(1) == 0) { + if (!isWhite) { + cce[currIndex++] = bitOffset; + } + isWhite = true; + } else { + if (isWhite) { + cce[currIndex++] = bitOffset; + } + isWhite = false; + } + + exit = true; + } + + if (zeros == 5) { + if (!isWhite) { + cce[currIndex++] = bitOffset; + } + bitOffset += zeros; + + // Last thing written was white + isWhite = true; + } else { + bitOffset += zeros; + + cce[currIndex++] = bitOffset; + setToBlack(buffer, lineOffset, bitOffset, 1); + ++bitOffset; + + // Last thing written was black + isWhite = false; + } + + } + } else { + throw new RuntimeException("Invalid code encountered while decoding 2D group 4 compressed data."); + } + } + + // Add the changing element beyond the current scanline for the + // other color too + //make sure that the index does not exceed the bounds of the array + if(currIndex < cce.length) + cce[currIndex++] = bitOffset; + + // Number of changing elements in this scanline. + changingElemSize = currIndex; + + lineOffset += scanlineStride; + } + } + + private void setToBlack(byte[] buffer, + int lineOffset, int bitOffset, + int numBits) { + int bitNum = 8*lineOffset + bitOffset; + int lastBit = bitNum + numBits; + + int byteNum = bitNum >> 3; + + // Handle bits in first byte + int shift = bitNum & 0x7; + if (shift > 0) { + int maskVal = 1 << (7 - shift); + byte val = buffer[byteNum]; + while (maskVal > 0 && bitNum < lastBit) { + val |= maskVal; + maskVal >>= 1; + ++bitNum; + } + buffer[byteNum] = val; + } + + // Fill in 8 bits at a time + byteNum = bitNum >> 3; + while (bitNum < lastBit - 7) { + buffer[byteNum++] = (byte)255; + bitNum += 8; + } + + // Fill in remaining bits + while (bitNum < lastBit) { + byteNum = bitNum >> 3; + buffer[byteNum] |= 1 << (7 - (bitNum & 0x7)); + ++bitNum; + } + } + + // Returns run length + private int decodeWhiteCodeWord() { + int current, entry, bits, isT, twoBits, code = -1; + int runLength = 0; + boolean isWhite = true; + + while (isWhite) { + current = nextNBits(10); + entry = white[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x0f; + + if (bits == 12) { // Additional Make up code + // Get the next 2 bits + twoBits = nextLesserThan8Bits(2); + // Consolidate the 2 new bits and last 2 bits into 4 bits + current = ((current << 2) & 0x000c) | twoBits; + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + runLength += code; + updatePointer(4 - bits); + } else if (bits == 0) { // ERROR + throw new RuntimeException("Invalid code encountered."); + } else if (bits == 15) { // EOL + throw new RuntimeException("EOL code word encountered in White run."); + } else { + // 11 bits - 0000 0111 1111 1111 = 0x07ff + code = (entry >>> 5) & 0x07ff; + runLength += code; + updatePointer(10 - bits); + if (isT == 0) { + isWhite = false; + } + } + } + + return runLength; + } + + // Returns run length + private int decodeBlackCodeWord() { + int current, entry, bits, isT, code = -1; + int runLength = 0; + boolean isWhite = false; + + while (!isWhite) { + current = nextLesserThan8Bits(4); + entry = initBlack[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (code == 100) { + current = nextNBits(9); + entry = black[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (bits == 12) { + // Additional makeup codes + updatePointer(5); + current = nextLesserThan8Bits(4); + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + runLength += code; + + updatePointer(4 - bits); + } else if (bits == 15) { + // EOL code + throw new RuntimeException("EOL code word encountered in Black run."); + } else { + runLength += code; + updatePointer(9 - bits); + if (isT == 0) { + isWhite = true; + } + } + } else if (code == 200) { + // Is a Terminating code + current = nextLesserThan8Bits(2); + entry = twoBitBlack[current]; + code = (entry >>> 5) & 0x07ff; + runLength += code; + bits = (entry >>> 1) & 0x0f; + updatePointer(2 - bits); + isWhite = true; + } else { + // Is a Terminating code + runLength += code; + updatePointer(4 - bits); + isWhite = true; + } + } + + return runLength; + } + + private int readEOL(boolean isFirstEOL) { + if (fillBits == 0) { + int next12Bits = nextNBits(12); + if (isFirstEOL && next12Bits == 0) { + + // Might have the case of EOL padding being used even + // though it was not flagged in the T4Options field. + // This was observed to be the case in TIFFs produced + // by a well known vendor who shall remain nameless. + + if(nextNBits(4) == 1) { + + // EOL must be padded: reset the fillBits flag. + + fillBits = 1; + return 1; + } + } + if(next12Bits != 1) { + throw new RuntimeException("Scanline must begin with EOL code word."); + } + } else if (fillBits == 1) { + + // First EOL code word xxxx 0000 0000 0001 will occur + // As many fill bits will be present as required to make + // the EOL code of 12 bits end on a byte boundary. + + int bitsLeft = 8 - bitPointer; + + if (nextNBits(bitsLeft) != 0) { + throw new RuntimeException("All fill bits preceding EOL code must be 0."); + } + + // If the number of bitsLeft is less than 8, then to have a 12 + // bit EOL sequence, two more bytes are certainly going to be + // required. The first of them has to be all zeros, so ensure + // that. + if (bitsLeft < 4) { + if (nextNBits(8) != 0) { + throw new RuntimeException("All fill bits preceding EOL code must be 0."); + } + } + + // There might be a random number of fill bytes with 0s, so + // loop till the EOL of 0000 0001 is found, as long as all + // the bytes preceding it are 0's. + int n; + while ((n = nextNBits(8)) != 1) { + + // If not all zeros + if (n != 0) { + throw new RuntimeException("All fill bits preceding EOL code must be 0."); + } + } + } + + // If one dimensional encoding mode, then always return 1 + if (oneD == 0) { + return 1; + } else { + // Otherwise for 2D encoding mode, + // The next one bit signifies 1D/2D encoding of next line. + return nextLesserThan8Bits(1); + } + } + + private void getNextChangingElement(int a0, boolean isWhite, int[] ret) { + // Local copies of instance variables + int[] pce = this.prevChangingElems; + int ces = this.changingElemSize; + + // If the previous match was at an odd element, we still + // have to search the preceeding element. + // int start = lastChangingElement & ~0x1; + int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0; + if (isWhite) { + start &= ~0x1; // Search even numbered elements + } else { + start |= 0x1; // Search odd numbered elements + } + + int i = start; + for (; i < ces; i += 2) { + int temp = pce[i]; + if (temp > a0) { + lastChangingElement = i; + ret[0] = temp; + break; + } + } + + if (i + 1 < ces) { + ret[1] = pce[i + 1]; + } + } + + private int nextNBits(int bitsToGet) { + byte b, next, next2next; + int l = data.length - 1; + int bp = this.bytePointer; + + if (fillOrder == 1) { + b = data[bp]; + + if (bp == l) { + next = 0x00; + next2next = 0x00; + } else if ((bp + 1) == l) { + next = data[bp + 1]; + next2next = 0x00; + } else { + next = data[bp + 1]; + next2next = data[bp + 2]; + } + } else if (fillOrder == 2) { + b = flipTable[data[bp] & 0xff]; + + if (bp == l) { + next = 0x00; + next2next = 0x00; + } else if ((bp + 1) == l) { + next = flipTable[data[bp + 1] & 0xff]; + next2next = 0x00; + } else { + next = flipTable[data[bp + 1] & 0xff]; + next2next = flipTable[data[bp + 2] & 0xff]; + } + } else { + throw new RuntimeException("TIFF_FILL_ORDER tag must be either 1 or 2."); + } + + int bitsLeft = 8 - bitPointer; + int bitsFromNextByte = bitsToGet - bitsLeft; + int bitsFromNext2NextByte = 0; + if (bitsFromNextByte > 8) { + bitsFromNext2NextByte = bitsFromNextByte - 8; + bitsFromNextByte = 8; + } + + bytePointer++; + + int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft); + int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); + + int i3 = 0; + if (bitsFromNext2NextByte != 0) { + i2 <<= bitsFromNext2NextByte; + i3 = (next2next & table2[bitsFromNext2NextByte]) >>> + (8 - bitsFromNext2NextByte); + i2 |= i3; + bytePointer++; + bitPointer = bitsFromNext2NextByte; + } else { + if (bitsFromNextByte == 8) { + bitPointer = 0; + bytePointer++; + } else { + bitPointer = bitsFromNextByte; + } + } + + int i = i1 | i2; + return i; + } + + private int nextLesserThan8Bits(int bitsToGet) { + byte b, next; + int l = data.length - 1; + int bp = this.bytePointer; + + if (fillOrder == 1) { + b = data[bp]; + if (bp == l) { + next = 0x00; + } else { + next = data[bp + 1]; + } + } else if (fillOrder == 2) { + b = flipTable[data[bp] & 0xff]; + if (bp == l) { + next = 0x00; + } else { + next = flipTable[data[bp + 1] & 0xff]; + } + } else { + throw new RuntimeException("TIFF_FILL_ORDER tag must be either 1 or 2."); + } + + int bitsLeft = 8 - bitPointer; + int bitsFromNextByte = bitsToGet - bitsLeft; + + int shift = bitsLeft - bitsToGet; + int i1, i2; + if (shift >= 0) { + i1 = (b & table1[bitsLeft]) >>> shift; + bitPointer += bitsToGet; + if (bitPointer == 8) { + bitPointer = 0; + bytePointer++; + } + } else { + i1 = (b & table1[bitsLeft]) << (-shift); + i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); + + i1 |= i2; + bytePointer++; + bitPointer = bitsFromNextByte; + } + + return i1; + } + + // Move pointer backwards by given amount of bits + private void updatePointer(int bitsToMoveBack) { + int i = bitPointer - bitsToMoveBack; + + if (i < 0) { + bytePointer--; + bitPointer = 8 + i; + } else { + bitPointer = i; + } + } + + // Move to the next byte boundary + private boolean advancePointer() { + if (bitPointer != 0) { + bytePointer++; + bitPointer = 0; + } + + return true; + } +} + diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java new file mode 100644 index 0000000..ac3b068 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; + +import java.io.Serializable; + +/** + * A class representing a field in a TIFF 6.0 Image File Directory. + * + *

The TIFF file format is described in more detail in the + * comments for the TIFFDescriptor class. + * + *

A field in a TIFF Image File Directory (IFD). A field is defined + * as a sequence of values of identical data type. TIFF 6.0 defines + * 12 data types, which are mapped internally onto the Java datatypes + * byte, int, long, float, and double. + * + *

This class is not a committed part of the JAI API. It may + * be removed or changed in future releases of JAI. + * + * @see TIFFDirectory + */ +public class TIFFField extends Object implements Comparable, Serializable { + + /** Flag for 8 bit unsigned integers. */ + public static final int TIFF_BYTE = 1; + + /** Flag for null-terminated ASCII strings. */ + public static final int TIFF_ASCII = 2; + + /** Flag for 16 bit unsigned integers. */ + public static final int TIFF_SHORT = 3; + + /** Flag for 32 bit unsigned integers. */ + public static final int TIFF_LONG = 4; + + /** Flag for pairs of 32 bit unsigned integers. */ + public static final int TIFF_RATIONAL = 5; + + /** Flag for 8 bit signed integers. */ + public static final int TIFF_SBYTE = 6; + + /** Flag for 8 bit uninterpreted bytes. */ + public static final int TIFF_UNDEFINED = 7; + + /** Flag for 16 bit signed integers. */ + public static final int TIFF_SSHORT = 8; + + /** Flag for 32 bit signed integers. */ + public static final int TIFF_SLONG = 9; + + /** Flag for pairs of 32 bit signed integers. */ + public static final int TIFF_SRATIONAL = 10; + + /** Flag for 32 bit IEEE floats. */ + public static final int TIFF_FLOAT = 11; + + /** Flag for 64 bit IEEE doubles. */ + public static final int TIFF_DOUBLE = 12; + + /** The tag number. */ + int tag; + + /** The tag type. */ + int type; + + /** The number of data items present in the field. */ + int count; + + /** The field data. */ + Object data; + + /** The default constructor. */ + TIFFField() {} + + /** + * Constructs a TIFFField with arbitrary data. The data + * parameter must be an array of a Java type appropriate for the + * type of the TIFF field. Since there is no available 32-bit + * unsigned datatype, long is used. The mapping between types is + * as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TIFF type Java type
TIFF_BYTE byte
TIFF_ASCII String
TIFF_SHORT char
TIFF_LONG long
TIFF_RATIONAL long[2]
TIFF_SBYTE byte
TIFF_UNDEFINED byte
TIFF_SSHORT short
TIFF_SLONG int
TIFF_SRATIONAL int[2]
TIFF_FLOAT float
TIFF_DOUBLE double
+ */ + public TIFFField(int tag, int type, int count, Object data) { + this.tag = tag; + this.type = type; + this.count = count; + this.data = data; + } + + /** + * Returns the tag number, between 0 and 65535. + */ + public int getTag() { + return tag; + } + + /** + * Returns the type of the data stored in the IFD. + * For a TIFF6.0 file, the value will equal one of the + * TIFF_ constants defined in this class. For future + * revisions of TIFF, higher values are possible. + * + */ + public int getType() { + return type; + } + + /** + * Returns the number of elements in the IFD. + */ + public int getCount() { + return count; + } + + /** + * Returns the data as an uninterpreted array of bytes. + * The type of the field must be one of TIFF_BYTE, TIFF_SBYTE, + * or TIFF_UNDEFINED; + * + *

For data in TIFF_BYTE format, the application must take + * care when promoting the data to longer integral types + * to avoid sign extension. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_BYTE, TIFF_SBYTE, or TIFF_UNDEFINED. + */ + public byte[] getAsBytes() { + return (byte[])data; + } + + /** + * Returns TIFF_SHORT data as an array of chars (unsigned 16-bit + * integers). + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_SHORT. + */ + public char[] getAsChars() { + return (char[])data; + } + + /** + * Returns TIFF_SSHORT data as an array of shorts (signed 16-bit + * integers). + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_SSHORT. + */ + public short[] getAsShorts() { + return (short[])data; + } + + /** + * Returns TIFF_SLONG data as an array of ints (signed 32-bit + * integers). + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_SLONG. + */ + public int[] getAsInts() { + return (int[])data; + } + + /** + * Returns TIFF_LONG data as an array of longs (signed 64-bit + * integers). + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_LONG. + */ + public long[] getAsLongs() { + return (long[])data; + } + + /** + * Returns TIFF_FLOAT data as an array of floats. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_FLOAT. + */ + public float[] getAsFloats() { + return (float[])data; + } + + /** + * Returns TIFF_DOUBLE data as an array of doubles. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_DOUBLE. + */ + public double[] getAsDoubles() { + return (double[])data; + } + + /** + * Returns TIFF_SRATIONAL data as an array of 2-element arrays of ints. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_SRATIONAL. + */ + public int[][] getAsSRationals() { + return (int[][])data; + } + + /** + * Returns TIFF_RATIONAL data as an array of 2-element arrays of longs. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_RATTIONAL. + */ + public long[][] getAsRationals() { + return (long[][])data; + } + + /** + * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, + * TIFF_SSHORT, or TIFF_SLONG format as an int. + * + *

TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned; + * that is, no sign extension will take place and the returned + * value will be in the range [0, 255]. TIFF_SBYTE data will + * be returned in the range [-128, 127]. + * + *

A ClassCastException will be thrown if the field is not of + * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, + * TIFF_SSHORT, or TIFF_SLONG. + */ + public int getAsInt(int index) { + switch (type) { + case TIFF_BYTE: case TIFF_UNDEFINED: + return ((byte[])data)[index] & 0xff; + case TIFF_SBYTE: + return ((byte[])data)[index]; + case TIFF_SHORT: + return ((char[])data)[index] & 0xffff; + case TIFF_SSHORT: + return ((short[])data)[index]; + case TIFF_SLONG: + return ((int[])data)[index]; + default: + throw new ClassCastException(); + } + } + + /** + * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, + * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG format as a long. + * + *

TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned; + * that is, no sign extension will take place and the returned + * value will be in the range [0, 255]. TIFF_SBYTE data will + * be returned in the range [-128, 127]. + * + *

A ClassCastException will be thrown if the field is not of + * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, + * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG. + */ + public long getAsLong(int index) { + switch (type) { + case TIFF_BYTE: case TIFF_UNDEFINED: + return ((byte[])data)[index] & 0xff; + case TIFF_SBYTE: + return ((byte[])data)[index]; + case TIFF_SHORT: + return ((char[])data)[index] & 0xffff; + case TIFF_SSHORT: + return ((short[])data)[index]; + case TIFF_SLONG: + return ((int[])data)[index]; + case TIFF_LONG: + return ((long[])data)[index]; + default: + throw new ClassCastException(); + } + } + + /** + * Returns data in any numerical format as a float. Data in + * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by + * dividing the numerator into the denominator using + * double-precision arithmetic and then truncating to single + * precision. Data in TIFF_SLONG, TIFF_LONG, or TIFF_DOUBLE + * format may suffer from truncation. + * + *

A ClassCastException will be thrown if the field is + * of type TIFF_UNDEFINED or TIFF_ASCII. + */ + public float getAsFloat(int index) { + switch (type) { + case TIFF_BYTE: + return ((byte[])data)[index] & 0xff; + case TIFF_SBYTE: + return ((byte[])data)[index]; + case TIFF_SHORT: + return ((char[])data)[index] & 0xffff; + case TIFF_SSHORT: + return ((short[])data)[index]; + case TIFF_SLONG: + return ((int[])data)[index]; + case TIFF_LONG: + return ((long[])data)[index]; + case TIFF_FLOAT: + return ((float[])data)[index]; + case TIFF_DOUBLE: + return (float)((double[])data)[index]; + case TIFF_SRATIONAL: + int[] ivalue = getAsSRational(index); + return (float)((double)ivalue[0]/ivalue[1]); + case TIFF_RATIONAL: + long[] lvalue = getAsRational(index); + return (float)((double)lvalue[0]/lvalue[1]); + default: + throw new ClassCastException(); + } + } + + /** + * Returns data in any numerical format as a float. Data in + * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by + * dividing the numerator into the denominator using + * double-precision arithmetic. + * + *

A ClassCastException will be thrown if the field is of + * type TIFF_UNDEFINED or TIFF_ASCII. + */ + public double getAsDouble(int index) { + switch (type) { + case TIFF_BYTE: + return ((byte[])data)[index] & 0xff; + case TIFF_SBYTE: + return ((byte[])data)[index]; + case TIFF_SHORT: + return ((char[])data)[index] & 0xffff; + case TIFF_SSHORT: + return ((short[])data)[index]; + case TIFF_SLONG: + return ((int[])data)[index]; + case TIFF_LONG: + return ((long[])data)[index]; + case TIFF_FLOAT: + return ((float[])data)[index]; + case TIFF_DOUBLE: + return ((double[])data)[index]; + case TIFF_SRATIONAL: + int[] ivalue = getAsSRational(index); + return (double)ivalue[0]/ivalue[1]; + case TIFF_RATIONAL: + long[] lvalue = getAsRational(index); + return (double)lvalue[0]/lvalue[1]; + default: + throw new ClassCastException(); + } + } + + /** + * Returns a TIFF_ASCII data item as a String. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_ASCII. + */ + public String getAsString(int index) { + return ((String[])data)[index]; + } + + /** + * Returns a TIFF_SRATIONAL data item as a two-element array + * of ints. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_SRATIONAL. + */ + public int[] getAsSRational(int index) { + return ((int[][])data)[index]; + } + + /** + * Returns a TIFF_RATIONAL data item as a two-element array + * of ints. + * + *

A ClassCastException will be thrown if the field is not + * of type TIFF_RATIONAL. + */ + public long[] getAsRational(int index) { + return ((long[][])data)[index]; + } + + /** + * Compares this TIFFField with another + * TIFFField by comparing the tags. + * + *

Note: this class has a natural ordering that is inconsistent + * with equals(). + * + * @throws IllegalArgumentException if the parameter is null. + * @throws ClassCastException if the parameter is not a + * TIFFField. + */ + public int compareTo(Object o) { + if(o == null) { + throw new IllegalArgumentException(); + } + + int oTag = ((TIFFField)o).getTag(); + + if(tag < oTag) { + return -1; + } else if(tag > oTag) { + return 1; + } else { + return 0; + } + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java new file mode 100644 index 0000000..ca0f91a --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2001 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: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct 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 AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ +package com.lowagie.text.pdf.codec; + +/** + * A class for performing LZW decoding. + * + * + */ +public class TIFFLZWDecoder { + + byte stringTable[][]; + byte data[] = null, uncompData[]; + int tableIndex, bitsToGet = 9; + int bytePointer, bitPointer; + int dstIndex; + int w, h; + int predictor, samplesPerPixel; + int nextData = 0; + int nextBits = 0; + + int andTable[] = { + 511, + 1023, + 2047, + 4095 + }; + + public TIFFLZWDecoder(int w, int predictor, int samplesPerPixel) { + this.w = w; + this.predictor = predictor; + this.samplesPerPixel = samplesPerPixel; + } + + /** + * Method to decode LZW compressed data. + * + * @param data The compressed data. + * @param uncompData Array to return the uncompressed data in. + * @param h The number of rows the compressed data contains. + */ + public byte[] decode(byte data[], byte uncompData[], int h) { + + if(data[0] == (byte)0x00 && data[1] == (byte)0x01) { + throw new UnsupportedOperationException("TIFF 5.0-style LZW codes are not supported."); + } + + initializeStringTable(); + + this.data = data; + this.h = h; + this.uncompData = uncompData; + + // Initialize pointers + bytePointer = 0; + bitPointer = 0; + dstIndex = 0; + + + nextData = 0; + nextBits = 0; + + int code, oldCode = 0; + byte string[]; + + while ( ((code = getNextCode()) != 257) && + dstIndex < uncompData.length) { + + if (code == 256) { + + initializeStringTable(); + code = getNextCode(); + + if (code == 257) { + break; + } + + writeString(stringTable[code]); + oldCode = code; + + } else { + + if (code < tableIndex) { + + string = stringTable[code]; + + writeString(string); + addStringToTable(stringTable[oldCode], string[0]); + oldCode = code; + + } else { + + string = stringTable[oldCode]; + string = composeString(string, string[0]); + writeString(string); + addStringToTable(string); + oldCode = code; + } + + } + + } + + // Horizontal Differencing Predictor + if (predictor == 2) { + + int count; + for (int j = 0; j < h; j++) { + + count = samplesPerPixel * (j * w + 1); + + for (int i = samplesPerPixel; i < w * samplesPerPixel; i++) { + + uncompData[count] += uncompData[count - samplesPerPixel]; + count++; + } + } + } + + return uncompData; + } + + + /** + * Initialize the string table. + */ + public void initializeStringTable() { + + stringTable = new byte[4096][]; + + for (int i=0; i<256; i++) { + stringTable[i] = new byte[1]; + stringTable[i][0] = (byte)i; + } + + tableIndex = 258; + bitsToGet = 9; + } + + /** + * Write out the string just uncompressed. + */ + public void writeString(byte string[]) { + + for (int i=0; inewString to the end of oldString. + */ + public byte[] composeString(byte oldString[], byte newString) { + int length = oldString.length; + byte string[] = new byte[length + 1]; + System.arraycopy(oldString, 0, string, 0, length); + string[length] = newString; + + return string; + } + + // Returns the next 9, 10, 11 or 12 bits + public int getNextCode() { + // Attempt to get the next code. The exception is caught to make + // this robust to cases wherein the EndOfInformation code has been + // omitted from a strip. Examples of such cases have been observed + // in practice. + try { + nextData = (nextData << 8) | (data[bytePointer++] & 0xff); + nextBits += 8; + + if (nextBits < bitsToGet) { + nextData = (nextData << 8) | (data[bytePointer++] & 0xff); + nextBits += 8; + } + + int code = + (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9]; + nextBits -= bitsToGet; + + return code; + } catch(ArrayIndexOutOfBoundsException e) { + // Strip not terminated as expected: return EndOfInformation code. + return 257; + } + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java b/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java new file mode 100644 index 0000000..cc812d7 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java @@ -0,0 +1,522 @@ +/* + * Copyright 2003-2005 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/ + */ +package com.lowagie.text.pdf.codec; +import com.lowagie.text.pdf.*; +import com.lowagie.text.Image; +import com.lowagie.text.Jpeg; +import com.lowagie.text.ExceptionConverter; +import java.io.*; +import java.util.zip.*; +import java.awt.color.ICC_Profile; + +/** Reads TIFF images + * @author Paulo Soares (psoares@consiste.pt) + */ +public class TiffImage { + + /** Gets the number of pages the TIFF document has. + * @param s the file source + * @return the number of pages + */ + public static int getNumberOfPages(RandomAccessFileOrArray s) { + try { + return TIFFDirectory.getNumDirectories(s); + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + } + + static int getDpi(TIFFField fd, int resolutionUnit) { + if (fd == null) + return 0; + long res[] = fd.getAsRational(0); + float frac = (float)res[0] / (float)res[1]; + int dpi = 0; + switch (resolutionUnit) { + case TIFFConstants.RESUNIT_INCH: + case TIFFConstants.RESUNIT_NONE: + dpi = (int)frac; + break; + case TIFFConstants.RESUNIT_CENTIMETER: + dpi = (int)(frac * 2.54); + break; + } + return dpi; + } + + /** Reads a page from a TIFF image. Direct mode is not used. + * @param s the file source + * @param page the page to get. The first page is 1 + * @return the Image + */ + public static Image getTiffImage(RandomAccessFileOrArray s, int page) { + return getTiffImage(s, page, false); + } + + /** Reads a page from a TIFF image. + * @param s the file source + * @param page the page to get. The first page is 1 + * @param direct for single strip, CCITT images, generate the image + * by direct byte copying. It's faster but may not work + * every time + * @return the Image + */ + public static Image getTiffImage(RandomAccessFileOrArray s, int page, boolean direct) { + if (page < 1) + throw new IllegalArgumentException("The page number must be >= 1."); + try { + TIFFDirectory dir = new TIFFDirectory(s, page - 1); + if (dir.isTagPresent(TIFFConstants.TIFFTAG_TILEWIDTH)) + throw new IllegalArgumentException("Tiles are not supported."); + int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION); + switch (compression) { + case TIFFConstants.COMPRESSION_CCITTRLEW: + case TIFFConstants.COMPRESSION_CCITTRLE: + case TIFFConstants.COMPRESSION_CCITTFAX3: + case TIFFConstants.COMPRESSION_CCITTFAX4: + break; + default: + return getTiffImageColor(dir, s); + } + float rotation = 0; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) { + int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION); + if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT) + rotation = (float)Math.PI; + else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT) + rotation = (float)(Math.PI / 2.0); + else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT) + rotation = -(float)(Math.PI / 2.0); + } + + Image img = null; + long tiffT4Options = 0; + long tiffT6Options = 0; + int fillOrder = 1; + int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH); + int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH); + int dpiX = 0; + int dpiY = 0; + float XYRatio = 0; + int resolutionUnit = TIFFConstants.RESUNIT_INCH; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT)) + resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT); + dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit); + dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit); + if (resolutionUnit == TIFFConstants.RESUNIT_NONE) { + if (dpiY != 0) + XYRatio = (float)dpiX / (float)dpiY; + dpiX = 0; + dpiY = 0; + } + long tstrip = 0xFFFFFFFFL; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP)) + tstrip = dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP); + int rowsStrip = (int)Math.min(h, tstrip); + long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS); + long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS); + if (size == null && h == rowsStrip) { // some TIFF producers are really lousy, so... + size = new long[]{s.length() - (int)offset[0]}; + } + boolean reverse = false; + TIFFField fillOrderField = dir.getField(TIFFConstants.TIFFTAG_FILLORDER); + if (fillOrderField != null) + fillOrder = fillOrderField.getAsInt(0); + reverse = (fillOrder == TIFFConstants.FILLORDER_LSB2MSB); + int params = 0; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_PHOTOMETRIC)) { + long photo = dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC); + if (photo == TIFFConstants.PHOTOMETRIC_MINISBLACK) + params |= Image.CCITT_BLACKIS1; + } + int imagecomp = 0; + switch (compression) { + case TIFFConstants.COMPRESSION_CCITTRLEW: + case TIFFConstants.COMPRESSION_CCITTRLE: + imagecomp = Image.CCITTG3_1D; + params |= Image.CCITT_ENCODEDBYTEALIGN | Image.CCITT_ENDOFBLOCK; + break; + case TIFFConstants.COMPRESSION_CCITTFAX3: + imagecomp = Image.CCITTG3_1D; + params |= Image.CCITT_ENDOFLINE | Image.CCITT_ENDOFBLOCK; + TIFFField t4OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP3OPTIONS); + if (t4OptionsField != null) { + tiffT4Options = t4OptionsField.getAsLong(0); + if ((tiffT4Options & TIFFConstants.GROUP3OPT_2DENCODING) != 0) + imagecomp = Image.CCITTG3_2D; + if ((tiffT4Options & TIFFConstants.GROUP3OPT_FILLBITS) != 0) + params |= Image.CCITT_ENCODEDBYTEALIGN; + } + break; + case TIFFConstants.COMPRESSION_CCITTFAX4: + imagecomp = Image.CCITTG4; + TIFFField t6OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP4OPTIONS); + if (t6OptionsField != null) + tiffT6Options = t6OptionsField.getAsLong(0); + break; + } + if (direct && rowsStrip == h) { //single strip, direct + byte im[] = new byte[(int)size[0]]; + s.seek(offset[0]); + s.readFully(im); + img = Image.getInstance(w, h, reverse, imagecomp, params, im); + img.setInverted(true); + } + else { + int rowsLeft = h; + CCITTG4Encoder g4 = new CCITTG4Encoder(w); + for (int k = 0; k < offset.length; ++k) { + byte im[] = new byte[(int)size[k]]; + s.seek(offset[k]); + s.readFully(im); + int height = Math.min(rowsStrip, rowsLeft); + TIFFFaxDecoder decoder = new TIFFFaxDecoder(fillOrder, w, height); + byte outBuf[] = new byte[(w + 7) / 8 * height]; + switch (compression) { + case TIFFConstants.COMPRESSION_CCITTRLEW: + case TIFFConstants.COMPRESSION_CCITTRLE: + decoder.decode1D(outBuf, im, 0, height); + g4.fax4Encode(outBuf,height); + break; + case TIFFConstants.COMPRESSION_CCITTFAX3: + try { + decoder.decode2D(outBuf, im, 0, height, tiffT4Options); + } + catch (Exception e) { + // let's flip the fill bits and try again... + tiffT4Options ^= TIFFConstants.GROUP3OPT_FILLBITS; + try { + decoder.decode2D(outBuf, im, 0, height, tiffT4Options); + } + catch (Exception e2) { + throw e; + } + } + g4.fax4Encode(outBuf, height); + break; + case TIFFConstants.COMPRESSION_CCITTFAX4: + decoder.decodeT6(outBuf, im, 0, height, tiffT6Options); + g4.fax4Encode(outBuf, height); + break; + } + rowsLeft -= rowsStrip; + } + byte g4pic[] = g4.close(); + img = Image.getInstance(w, h, false, Image.CCITTG4, params & Image.CCITT_BLACKIS1, g4pic); + } + img.setDpi(dpiX, dpiY); + img.setXYRatio(XYRatio); + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) { + try { + TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE); + ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes()); + if (icc_prof.getNumComponents() == 1) + img.tagICC(icc_prof); + } + catch (Exception e) { + //empty + } + } + img.setOriginalType(Image.ORIGINAL_TIFF); + if (rotation != 0) + img.setInitialRotation(rotation); + return img; + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + } + + protected static Image getTiffImageColor(TIFFDirectory dir, RandomAccessFileOrArray s) { + try { + int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION); + int predictor = 1; + TIFFLZWDecoder lzwDecoder = null; + switch (compression) { + case TIFFConstants.COMPRESSION_NONE: + case TIFFConstants.COMPRESSION_LZW: + case TIFFConstants.COMPRESSION_PACKBITS: + case TIFFConstants.COMPRESSION_DEFLATE: + case TIFFConstants.COMPRESSION_OJPEG: + break; + default: + throw new IllegalArgumentException("The compression " + compression + " is not supported."); + } + int photometric = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC); + switch (photometric) { + case TIFFConstants.PHOTOMETRIC_MINISWHITE: + case TIFFConstants.PHOTOMETRIC_MINISBLACK: + case TIFFConstants.PHOTOMETRIC_RGB: + case TIFFConstants.PHOTOMETRIC_SEPARATED: + case TIFFConstants.PHOTOMETRIC_PALETTE: + break; + default: + throw new IllegalArgumentException("The photometric " + photometric + " is not supported."); + } + float rotation = 0; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) { + int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION); + if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT) + rotation = (float)Math.PI; + else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT) + rotation = (float)(Math.PI / 2.0); + else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT) + rotation = -(float)(Math.PI / 2.0); + } + if (dir.isTagPresent(TIFFConstants.TIFFTAG_PLANARCONFIG) + && dir.getFieldAsLong(TIFFConstants.TIFFTAG_PLANARCONFIG) == TIFFConstants.PLANARCONFIG_SEPARATE) + throw new IllegalArgumentException("Planar images are not supported."); + if (dir.isTagPresent(TIFFConstants.TIFFTAG_EXTRASAMPLES)) + throw new IllegalArgumentException("Extra samples are not supported."); + int samplePerPixel = 1; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL)) // 1,3,4 + samplePerPixel = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL); + int bitsPerSample = 1; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_BITSPERSAMPLE)) + bitsPerSample = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_BITSPERSAMPLE); + switch (bitsPerSample) { + case 1: + case 2: + case 4: + case 8: + break; + default: + throw new IllegalArgumentException("Bits per sample " + bitsPerSample + " is not supported."); + } + Image img = null; + + int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH); + int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH); + int dpiX = 0; + int dpiY = 0; + int resolutionUnit = TIFFConstants.RESUNIT_INCH; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT)) + resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT); + dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit); + dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit); + int rowsStrip = h; + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP)) //another hack for broken tiffs + rowsStrip = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP); + long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS); + long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS); + if (size == null && h == rowsStrip) { // some TIFF producers are really lousy, so... + size = new long[]{s.length() - (int)offset[0]}; + } + if (compression == TIFFConstants.COMPRESSION_LZW) { + TIFFField predictorField = dir.getField(TIFFConstants.TIFFTAG_PREDICTOR); + if (predictorField != null) { + predictor = predictorField.getAsInt(0); + if (predictor != 1 && predictor != 2) { + throw new RuntimeException("Illegal value for Predictor in TIFF file."); + } + if (predictor == 2 && bitsPerSample != 8) { + throw new RuntimeException(bitsPerSample + "-bit samples are not supported for Horizontal differencing Predictor."); + } + } + lzwDecoder = new TIFFLZWDecoder(w, predictor, + samplePerPixel); + } + int rowsLeft = h; + ByteArrayOutputStream stream = null; + DeflaterOutputStream zip = null; + CCITTG4Encoder g4 = null; + if (bitsPerSample == 1 && samplePerPixel == 1) { + g4 = new CCITTG4Encoder(w); + } + else { + stream = new ByteArrayOutputStream(); + if (compression != TIFFConstants.COMPRESSION_OJPEG) + zip = new DeflaterOutputStream(stream); + } + for (int k = 0; k < offset.length; ++k) { + byte im[] = new byte[(int)size[k]]; + s.seek(offset[k]); + s.readFully(im); + int height = Math.min(rowsStrip, rowsLeft); + byte outBuf[] = null; + if (compression != TIFFConstants.COMPRESSION_NONE) + outBuf = new byte[(w * bitsPerSample * samplePerPixel + 7) / 8 * height]; + switch (compression) { + case TIFFConstants.COMPRESSION_DEFLATE: + inflate(im, outBuf); + break; + case TIFFConstants.COMPRESSION_NONE: + outBuf = im; + break; + case TIFFConstants.COMPRESSION_PACKBITS: + decodePackbits(im, outBuf); + break; + case TIFFConstants.COMPRESSION_LZW: + lzwDecoder.decode(im, outBuf, height); + break; + case TIFFConstants.COMPRESSION_OJPEG: + stream.write(im); + break; + } + if (bitsPerSample == 1 && samplePerPixel == 1) { + g4.fax4Encode(outBuf, height); + } + else if (compression != TIFFConstants.COMPRESSION_OJPEG) { + zip.write(outBuf); + } + rowsLeft -= rowsStrip; + } + if (bitsPerSample == 1 && samplePerPixel == 1) { + img = Image.getInstance(w, h, false, Image.CCITTG4, + photometric == TIFFConstants.PHOTOMETRIC_MINISBLACK ? Image.CCITT_BLACKIS1 : 0, g4.close()); + } + else { + if (compression == TIFFConstants.COMPRESSION_OJPEG) { + img = new Jpeg(stream.toByteArray()); + } + else { + zip.close(); + img = Image.getInstance(w, h, samplePerPixel, bitsPerSample, stream.toByteArray()); + img.setDeflated(true); + } + } + img.setDpi(dpiX, dpiY); + if (compression != TIFFConstants.COMPRESSION_OJPEG) { + if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) { + try { + TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE); + ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes()); + if (samplePerPixel == icc_prof.getNumComponents()) + img.tagICC(icc_prof); + } + catch (Exception e) { + //empty + } + } + if (dir.isTagPresent(TIFFConstants.TIFFTAG_COLORMAP)) { + TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_COLORMAP); + char rgb[] = fd.getAsChars(); + byte palette[] = new byte[rgb.length]; + int gColor = rgb.length / 3; + int bColor = gColor * 2; + for (int k = 0; k < gColor; ++k) { + palette[k * 3] = (byte)(rgb[k] >>> 8); + palette[k * 3 + 1] = (byte)(rgb[k + gColor] >>> 8); + palette[k * 3 + 2] = (byte)(rgb[k + bColor] >>> 8); + } + PdfArray indexed = new PdfArray(); + indexed.add(PdfName.INDEXED); + indexed.add(PdfName.DEVICERGB); + indexed.add(new PdfNumber(gColor - 1)); + indexed.add(new PdfString(palette)); + PdfDictionary additional = new PdfDictionary(); + additional.put(PdfName.COLORSPACE, indexed); + img.setAdditional(additional); + } + img.setOriginalType(Image.ORIGINAL_TIFF); + } + if (photometric == TIFFConstants.PHOTOMETRIC_MINISWHITE) + img.setInverted(true); + if (rotation != 0) + img.setInitialRotation(rotation); + return img; + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + } + + static long[] getArrayLongShort(TIFFDirectory dir, int tag) { + TIFFField field = dir.getField(tag); + if (field == null) + return null; + long offset[]; + if (field.getType() == TIFFField.TIFF_LONG) + offset = field.getAsLongs(); + else { // must be short + char temp[] = field.getAsChars(); + offset = new long[temp.length]; + for (int k = 0; k < temp.length; ++k) + offset[k] = temp[k]; + } + return offset; + } + + // Uncompress packbits compressed image data. + public static void decodePackbits(byte data[], byte[] dst) { + int srcCount = 0, dstCount = 0; + byte repeat, b; + + while (dstCount < dst.length) { + b = data[srcCount++]; + if (b >= 0 && b <= 127) { + // literal run packet + for (int i=0; i<(b + 1); i++) { + dst[dstCount++] = data[srcCount++]; + } + + } else if (b <= -1 && b >= -127) { + // 2 byte encoded run packet + repeat = data[srcCount++]; + for (int i=0; i<(-b + 1); i++) { + dst[dstCount++] = repeat; + } + } else { + // no-op packet. Do nothing + srcCount++; + } + } + } + + public static void inflate(byte[] deflated, byte[] inflated) { + Inflater inflater = new Inflater(); + inflater.setInput(deflated); + try { + inflater.inflate(inflated); + } + catch(DataFormatException dfe) { + throw new ExceptionConverter(dfe); + } + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java new file mode 100644 index 0000000..da52458 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java @@ -0,0 +1,547 @@ +/* Generated By:JavaCC: Do not edit this line. JavaCharStream.java Version 2.1 */ +package com.lowagie.text.pdf.codec.postscript; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (with java-like unicode escape processing). + */ + +public final class JavaCharStream +{ + public static final boolean staticFlag = false; + static final int hexval(char c) throws java.io.IOException { + switch(c) + { + case '0' : + return 0; + case '1' : + return 1; + case '2' : + return 2; + case '3' : + return 3; + case '4' : + return 4; + case '5' : + return 5; + case '6' : + return 6; + case '7' : + return 7; + case '8' : + return 8; + case '9' : + return 9; + + case 'a' : + case 'A' : + return 10; + case 'b' : + case 'B' : + return 11; + case 'c' : + case 'C' : + return 12; + case 'd' : + case 'D' : + return 13; + case 'e' : + case 'E' : + return 14; + case 'f' : + case 'F' : + return 15; + } + + throw new java.io.IOException(); // Should never come here + } + + public int bufpos = -1; + int bufsize; + int available; + int tokenBegin; + private int bufline[]; + private int bufcolumn[]; + + private int column = 0; + private int line = 1; + + private boolean prevCharIsCR = false; + private boolean prevCharIsLF = false; + + private java.io.Reader inputStream; + + private char[] nextCharBuf; + private char[] buffer; + private int maxNextCharInd = 0; + private int nextCharInd = -1; + private int inBuf = 0; + + private final void ExpandBuff(boolean wrapAround) + { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try + { + if (wrapAround) + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, + bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + bufpos += (bufsize - tokenBegin); + } + else + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + bufpos -= tokenBegin; + } + } + catch (Throwable t) + { + throw new Error(t.getMessage()); + } + + available = (bufsize += 2048); + tokenBegin = 0; + } + + private final void FillBuff() throws java.io.IOException + { + int i; + if (maxNextCharInd == 4096) + maxNextCharInd = nextCharInd = 0; + + try { + if ((i = inputStream.read(nextCharBuf, maxNextCharInd, + 4096 - maxNextCharInd)) == -1) + { + inputStream.close(); + throw new java.io.IOException(); + } + else + maxNextCharInd += i; + return; + } + catch(java.io.IOException e) { + if (bufpos != 0) + { + --bufpos; + backup(0); + } + else + { + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + throw e; + } + } + + private final char ReadByte() throws java.io.IOException + { + if (++nextCharInd >= maxNextCharInd) + FillBuff(); + + return nextCharBuf[nextCharInd]; + } + + public final char BeginToken() throws java.io.IOException + { + if (inBuf > 0) + { + --inBuf; + + if (++bufpos == bufsize) + bufpos = 0; + + tokenBegin = bufpos; + return buffer[bufpos]; + } + + tokenBegin = 0; + bufpos = -1; + + return readChar(); + } + + private final void AdjustBuffSize() + { + if (available == bufsize) + { + if (tokenBegin > 2048) + { + bufpos = 0; + available = tokenBegin; + } + else + ExpandBuff(false); + } + else if (available > tokenBegin) + available = bufsize; + else if ((tokenBegin - available) < 2048) + ExpandBuff(true); + else + available = tokenBegin; + } + + private final void UpdateLineColumn(char c) + { + column++; + + if (prevCharIsLF) + { + prevCharIsLF = false; + line += (column = 1); + } + else if (prevCharIsCR) + { + prevCharIsCR = false; + if (c == '\n') + { + prevCharIsLF = true; + } + else + line += (column = 1); + } + + switch (c) + { + case '\r' : + prevCharIsCR = true; + break; + case '\n' : + prevCharIsLF = true; + break; + case '\t' : + column--; + column += (8 - (column & 07)); + break; + default : + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + + public final char readChar() throws java.io.IOException + { + if (inBuf > 0) + { + --inBuf; + + if (++bufpos == bufsize) + bufpos = 0; + + return buffer[bufpos]; + } + + char c; + + if (++bufpos == available) + AdjustBuffSize(); + + if ((buffer[bufpos] = c = ReadByte()) == '\\') + { + UpdateLineColumn(c); + + int backSlashCnt = 1; + + for (;;) // Read all the backslashes + { + if (++bufpos == available) + AdjustBuffSize(); + + try + { + if ((buffer[bufpos] = c = ReadByte()) != '\\') + { + UpdateLineColumn(c); + // found a non-backslash char. + if ((c == 'u') && ((backSlashCnt & 1) == 1)) + { + if (--bufpos < 0) + bufpos = bufsize - 1; + + break; + } + + backup(backSlashCnt); + return '\\'; + } + } + catch(java.io.IOException e) + { + if (backSlashCnt > 1) + backup(backSlashCnt); + + return '\\'; + } + + UpdateLineColumn(c); + backSlashCnt++; + } + + // Here, we have seen an odd number of backslash's followed by a 'u' + try + { + while ((c = ReadByte()) == 'u') + ++column; + + buffer[bufpos] = c = (char)(hexval(c) << 12 | + hexval(ReadByte()) << 8 | + hexval(ReadByte()) << 4 | + hexval(ReadByte())); + + column += 4; + } + catch(java.io.IOException e) + { + throw new Error("Invalid escape character at line " + line + + " column " + column + "."); + } + + if (backSlashCnt == 1) + return c; + else + { + backup(backSlashCnt - 1); + return '\\'; + } + } + else + { + UpdateLineColumn(c); + return (c); + } + } + + /** + * @deprecated + * @see #getEndColumn + */ + + public final int getColumn() { + return bufcolumn[bufpos]; + } + + /** + * @deprecated + * @see #getEndLine + */ + + public final int getLine() { + return bufline[bufpos]; + } + + public final int getEndColumn() { + return bufcolumn[bufpos]; + } + + public final int getEndLine() { + return bufline[bufpos]; + } + + public final int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + public final int getBeginLine() { + return bufline[tokenBegin]; + } + + public final void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) + bufpos += bufsize; + } + + public JavaCharStream(java.io.Reader dstream, + int startline, int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + nextCharBuf = new char[4096]; + } + + public JavaCharStream(java.io.Reader dstream, + int startline, int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public JavaCharStream(java.io.Reader dstream) + { + this(dstream, 1, 1, 4096); + } + public void ReInit(java.io.Reader dstream, + int startline, int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) + { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + nextCharBuf = new char[4096]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + nextCharInd = bufpos = -1; + } + + public void ReInit(java.io.Reader dstream, + int startline, int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + + public void ReInit(java.io.Reader dstream) + { + ReInit(dstream, 1, 1, 4096); + } + public JavaCharStream(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + } + + public JavaCharStream(java.io.InputStream dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public JavaCharStream(java.io.InputStream dstream) + { + this(dstream, 1, 1, 4096); + } + + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + } + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + public void ReInit(java.io.InputStream dstream) + { + ReInit(dstream, 1, 1, 4096); + } + + public final String GetImage() + { + if (bufpos >= tokenBegin) + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + else + return new String(buffer, tokenBegin, bufsize - tokenBegin) + + new String(buffer, 0, bufpos + 1); + } + + public final char[] GetSuffix(int len) + { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else + { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, + len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + public void Done() + { + nextCharBuf = null; + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token.
+ */ + public void adjustBeginLineColumn(int newLine, int newCol) + { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) + { + len = bufpos - tokenBegin + inBuf + 1; + } + else + { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && + bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) + { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) + { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) + { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) + bufline[j] = newLine++; + else + bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java new file mode 100644 index 0000000..a160e6b --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java @@ -0,0 +1,98 @@ +/* + * $Id: MetaDoPS.java,v 1.4 2006/04/22 16:56:39 psoares33 Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.postscript; + +import java.io.*; +import java.awt.*; +import com.lowagie.text.*; +import com.lowagie.text.pdf.*; + +public class MetaDoPS { + + public PdfContentByte cb; + InputStream in; + int left; + int top; + int right; + int bottom; + int inch; + + public MetaDoPS(InputStream in, PdfContentByte cb) { + this.cb = cb; + this.in = in; + } + + public void readAll() throws IOException, DocumentException { + + cb.saveState(); + java.awt.Graphics2D g2 = cb.createGraphicsShapes(PageSize.A4. + width(), PageSize.A4.height()); + try { + PAContext context = new PAContext( (Graphics2D) g2, + new Dimension( (int) PageSize.A4.width(), + (int) PageSize.A4.height())); + context.draw(in); +// context.draw(new BufferedInputStream(in)); + // ( (Graphics2D) backBuffer.getGraphics()).dispose(); + in.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + catch (PainterException ex) { + ex.printStackTrace(); + } + g2.dispose(); + cb.restoreState(); + + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java new file mode 100644 index 0000000..91b2e18 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java @@ -0,0 +1,19 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +public interface PACommand { + + public void execute(PAContext context) throws PainterException; + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java new file mode 100644 index 0000000..863d82a --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java @@ -0,0 +1,2772 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +import java.util.*; +import java.io.*; +import java.awt.*; +import java.awt.geom.*; +import com.lowagie.text.pdf.PdfGraphics2D; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfName; +import com.lowagie.text.*; +import com.lowagie.text.pdf.RandomAccessFileOrArray; + +public class PAContext { + + public PAPencil pencil; + public Stack dictionaries; + public Stack operands; + public PAEngine engine; + PAParser poorscript = null; + protected Random randomNumberGenerator; + InputStream is=null; + + protected Object lastUnknownIdentifier; + public static boolean IgnoreUnknownCommands = false; + public static boolean DebugExecution = false; + + public PAContext(Component component) { + this(new PAPencil(component)); + } + + public PAContext(Graphics2D g, Dimension size) { + this(new PAPencil(g, size)); + } + + public PAContext(PAPencil pencil) { + super(); + this.pencil = pencil; + this.dictionaries = new Stack(); + this.operands = new Stack(); + this.engine = new PAEngine(this); + HashMap systemDict = this.constructSystemDict(); + this.dictionaries.push(systemDict); + HashMap globalDict = this.constructGlobalDict(); + this.dictionaries.push(globalDict); + HashMap userDict = this.constructUserDict(); + systemDict.put("userdict", userDict); + this.dictionaries.push(userDict); + this.randomNumberGenerator = new Random(); + this.lastUnknownIdentifier = null; + } + + /** + * draw + * + * @param inputStream InputStream + * @throws PainterException + */ + public void draw(InputStream inputStream) throws PainterException { + try { + String filename="init.ps"; +// poorscript = new PAParser(new NamedInputStream(PAContext.class.getResourceAsStream(filename),filename)); + InputStream inpstr=PAContext.class.getResourceAsStream(filename); + poorscript = new PAParser(inpstr); + poorscript.parse(this); + try { + inpstr.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } +// poorscript.enable_tracing(); +// poorscript.token_source.setDebugStream(System.err); +// byte[] b=null; +// try { +// b = RandomAccessFileOrArray.InputStreamToArray(inputStream); +// } +// catch (IOException ex) { +// ex.printStackTrace(); +// } +// ByteArrayInputStream bar=new ByteArrayInputStream(b); +// is = bar; + poorscript.ReInit(inputStream); + poorscript.parse(this); + // pencil.graphics.dispose(); + } + catch (ParseException e) { + e.printStackTrace(); + throw new PainterException(e.toString()); + } + } + + + + public Object getLastUnknownIdentifier() { + return this.lastUnknownIdentifier; + } + + public double[] popNumberOperands(int n) throws PainterException { + double[] result = new double[n]; + Object objectValue; + double doubleValue; + + for (int i = n - 1; i >= 0; i--) { + try { + objectValue = this.operands.pop(); + } + catch (EmptyStackException e) { + throw new PainterException("Operand stack is empty poping " + n + + " number operands"); + } + if (objectValue instanceof Number) { + doubleValue = ( (Number) objectValue).doubleValue(); + } + else { + throw new PainterException("Number expected on operand stack poping " + + n + " number operands, found " + + objectValue.getClass().getName()); + } + result[i] = doubleValue; + } + return result; + } + + public Object[] popOperands(int n) throws PainterException { + Object[] result = new Object[n]; + Object objectValue; + + for (int i = n - 1; i >= 0; i--) { + try { + objectValue = this.operands.pop(); + } + catch (EmptyStackException e) { + throw new PainterException("Operand stack is empty poping " + n + + " operands"); + } + result[i] = objectValue; + } + return result; + } + + public Object peekOperand() throws PainterException { + Object objectValue; + + try { + objectValue = this.operands.peek(); + } + catch (EmptyStackException e) { + throw new PainterException("Operand stack is empty peeking operand"); + } + return objectValue; + } + + public Object findIdentifier(Object identifier) { + Object result = null; + int i, n; + + n = this.dictionaries.size(); + i = n - 1; + while (i >= 0 && result == null) { + HashMap dictionary = (HashMap)this.dictionaries.elementAt(i); + result = dictionary.get(identifier); + i--; + } + if (result == null) { + this.lastUnknownIdentifier = identifier; + } + return result; + } + + public Object findDictionary(Object identifier) { + Object result = null; + HashMap dictionary = null; + int i, n; + + n = this.dictionaries.size(); + i = n - 1; + while (i >= 0 && result == null) { + dictionary = (HashMap)this.dictionaries.elementAt(i); + result = dictionary.get(identifier); + i--; + } + if (result == null) { + return result; + } + else { + return dictionary; + } + } + + public void collectArray() throws PainterException { + ArrayList result; + Object objectValue; + int i, n; + boolean found = false; + + n = this.operands.size(); + for (i = n - 1; i >= 0; i--) { + objectValue = this.operands.elementAt(i); + if (objectValue instanceof PAToken && + ( (PAToken) objectValue).type == PAToken.START_ARRAY) { + found = true; + break; + } + } + if (!found) { + throw new PainterException("No array was started"); + } + result = new ArrayList(n - i - 1); + for (int j = 0; j < n - i - 1; j++) { + result.add(null); + } + for (int j = n - 1; j > i; j--) { + try { + objectValue = this.operands.pop(); + } + catch (EmptyStackException e) { + throw new PainterException( + "Operand stack is empty collecting array elements"); + } + result.set(j - i - 1, objectValue); + } + try { + this.operands.pop(); // the start array mark itself + } + catch (EmptyStackException e) { + throw new PainterException( + "Operand stack is empty removing begin array mark"); + } + this.operands.push(result); + } + + public void collectDict() throws PainterException { + HashMap result; // = new HashMap(); + Object objectValue; + int i, n; + boolean found = false; + + n = this.operands.size(); + for (i = n - 1; i >= 0; i--) { + objectValue = this.operands.elementAt(i); + if (objectValue instanceof PAToken && + ( (PAToken) objectValue).type == PAToken.START_DICT) { + found = true; + break; + } + } + if (!found) { + throw new PainterException("No dict was started"); + } +// result = new ArrayList(n - i - 1); + result = new HashMap(); +// for (int j = 0; j < n - i - 1; j++) { +// result.add(null); +// } + for (int j = n - 1; j > i; j -= 2) { + Object targetValue; + try { + targetValue = this.operands.pop(); + objectValue = this.operands.pop(); + } + catch (EmptyStackException e) { + throw new PainterException( + "Operand stack is empty collecting hashmap elements"); + } + result.put(objectValue, targetValue); + } + try { + this.operands.pop(); // the start array mark itself + } + catch (EmptyStackException e) { + throw new PainterException( + "Operand stack is empty removing begin array mark"); + } + this.operands.push(result); + } + + protected HashMap constructGlobalDict() { + HashMap globalDict = new HashMap(); + return globalDict; + } + + protected HashMap constructUserDict() { + HashMap userDict = new HashMap(); + + return userDict; + } + + public static void main(String[] args) { + javax.swing.JFrame jf = new javax.swing.JFrame(); + jf.setVisible(true); + jf.setDefaultCloseOperation(jf.DISPOSE_ON_CLOSE); + PAContext pac = new PAContext(new PAPencil(jf)); + HashMap hm = (HashMap) pac.findDictionary("systemdict"); + Iterator it = new TreeSet(hm.keySet()).iterator(); + while (it.hasNext()) { + + String obname = it.next().toString(); + Object ob = hm.get(obname); + String typname = ob.getClass().getName(); + System.out.println(obname + ":" + typname); + } + System.exit(0); + } + + protected HashMap constructSystemDict() { + HashMap systemDict = new HashMap(); + + // newpath + systemDict.put("newpath", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.newpath(); + } + }); + + // moveto + systemDict.put("moveto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(2); + context.pencil.moveto(data[0], data[1]); + } + }); + + // rmoveto + systemDict.put("rmoveto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.pencil.rmoveto(data[0], data[1]); + } + }); + + // lineto + systemDict.put("lineto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.pencil.lineto(data[0], data[1]); + } + }); + + // rlineto + systemDict.put("rlineto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.pencil.rlineto(data[0], data[1]); + } + }); + + // arc + systemDict.put("arc", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(5); + context.pencil.arc(data[0], data[1], data[2], data[3], data[4]); + } + }); + + // arcn + systemDict.put("arcn", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(5); + context.pencil.arcn(data[0], data[1], data[2], data[3], data[4]); + } + }); + + // curveto + systemDict.put("curveto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(6); + context.pencil.curveto(data[0], data[1], data[2], data[3], data[4], + data[5]); + } + }); + + // rcurveto + systemDict.put("rcurveto", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(6); + context.pencil.rcurveto(data[0], data[1], data[2], data[3], data[4], + data[5]); + } + }); + + // closepath + systemDict.put("closepath", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.closepath(); + } + }); + + // gsave + systemDict.put("gsave", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.gsave(); + } + }); + + // grestore + systemDict.put("grestore", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.grestore(); + } + }); + + // translate + systemDict.put("translate", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (context.peekOperand() instanceof Number) { + double data[]; + AffineTransform at = new AffineTransform(); + AffineTransform ctm = context.pencil.graphics.getTransform(); + + data = context.popNumberOperands(2); + at.translate(data[0], data[1]); + ctm.concatenate(at); + context.pencil.graphics.setTransform(ctm); + } + else { + Object data[]; + + data = context.popOperands(3); + if (! (data[0] instanceof Number)) { + throw new PainterException("translate: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("translate: wrong arguments"); + } + if (! (data[2] instanceof ArrayList)) { + throw new PainterException("translate: wrong arguments"); + } + + ArrayList array = (ArrayList) data[2]; + + if (! (array.size() == 6)) { + throw new PainterException("translate: wrong arguments"); + } + + AffineTransform at = new AffineTransform(); + + at.translate( ( (Number) data[0]).doubleValue(), + ( (Number) data[1]).doubleValue()); + + double[] entries = new double[6]; + + at.getMatrix(entries); + + for (int i = 0; i < 6; i++) { + array.set(i, new Double(entries[i])); + } + context.operands.push(array); + } + } + }); + + // rotate + systemDict.put("rotate", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (context.peekOperand() instanceof Number) { + double data[]; + AffineTransform at = new AffineTransform(); + AffineTransform ctm = context.pencil.graphics.getTransform(); + + data = context.popNumberOperands(1); + at.rotate(data[0] * Math.PI / 180.0d); + ctm.concatenate(at); + context.pencil.graphics.setTransform(ctm); + } + else { + Object data[]; + AffineTransform at = new AffineTransform(); + + data = context.popOperands(2); + if (! (data[0] instanceof Number)) { + throw new PainterException("rotate: wrong arguments"); + } + if (! (data[1] instanceof ArrayList)) { + throw new PainterException("rotate: wrong arguments"); + } + + ArrayList array = (ArrayList) data[1]; + + if (! (array.size() == 6)) { + throw new PainterException("rotate: wrong arguments"); + } + + at.rotate( ( (Number) data[0]).doubleValue()); + + double[] entries = new double[6]; + + at.getMatrix(entries); + + for (int i = 0; i < 6; i++) { + array.set(i, new Double(entries[i])); + } + context.operands.push(array); + } + } + }); + + // scale + systemDict.put("scale", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (context.peekOperand() instanceof Number) { + double data[]; + AffineTransform at = new AffineTransform(); + AffineTransform ctm = context.pencil.graphics.getTransform(); + + data = context.popNumberOperands(2); + at.scale(data[0], data[1]); + ctm.concatenate(at); + context.pencil.graphics.setTransform(ctm); + } + else { + Object data[]; + + data = context.popOperands(3); + if (! (data[0] instanceof Number)) { + throw new PainterException("scale: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("scale: wrong arguments"); + } + if (! (data[2] instanceof ArrayList)) { + throw new PainterException("scale: wrong arguments"); + } + + ArrayList array = (ArrayList) data[2]; + + double[] entries = new double[6]; + + if (! (array.size() == 6)) { + throw new PainterException("scale: wrong arguments"); + } + + entries[0] = ( (Number) data[0]).doubleValue(); + entries[1] = 0.0d; + entries[2] = 0.0d; + entries[3] = ( (Number) data[1]).doubleValue(); + entries[4] = 0.0d; + entries[5] = 0.0d; + + for (int i = 0; i < 6; i++) { + array.set(i, new Double(entries[i])); + } + context.operands.push(array); + } + } + }); + // currentmatrix + systemDict.put("currentmatrix", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof ArrayList)) { + throw new PainterException("currentmatrix: wrong argument"); + } + ArrayList array = (ArrayList) data[0]; + + double[] entries = new double[6]; + + if (! (array.size() == 6)) { + throw new PainterException("currentmatrix: wrong arguments"); + } + + + AffineTransform ctm = context.pencil.graphics.getTransform(); + ctm.getMatrix(entries); + + for (int i = 0; i < 6; i++) { + array.set(i, new Double(entries[i])); + } + context.operands.push(array); + } + }); + + // setmatrix + systemDict.put("setmatrix", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof ArrayList)) { + throw new PainterException("setmatrix: wrong argument"); + } + ArrayList array = (ArrayList) data[0]; + + double[] entries = new double[6]; + + if (! (array.size() == 6)) { + throw new PainterException("setmatrix: wrong arguments"); + } + entries[0] = ((Number)array.get(0)).doubleValue(); + entries[1] = ((Number)array.get(1)).doubleValue(); + entries[2] = ((Number)array.get(2)).doubleValue(); + entries[3] = ((Number)array.get(3)).doubleValue(); + entries[4] = ((Number)array.get(4)).doubleValue(); + entries[5] = ((Number)array.get(5)).doubleValue(); + + AffineTransform at = new AffineTransform(entries); + context.pencil.graphics.setTransform(at); + } + }); + + // stroke + systemDict.put("stroke", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.stroke(); + } + }); + + // fill + systemDict.put("fill", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.fill(); + } + }); + + // eofill + systemDict.put("eofill", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.eofill(); + } + }); + + // show + systemDict.put("show", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(1); + if (! (data[0] instanceof String)) { + throw new PainterException("show: wrong arguments"); + } + context.pencil.show( (String) data[0]); + } + }); + + // stringwidth + systemDict.put("stringwidth", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + float[] result; + java.awt.Font font; + + data = context.popOperands(1); + if (! (data[0] instanceof String)) { + throw new PainterException("stringwidth: wrong arguments"); + } + font = context.pencil.graphics.getFont(); + Rectangle2D rect = font.getStringBounds( (String) data[0], + context.pencil.graphics. + getFontRenderContext()); + context.operands.push(new Float(rect.getWidth())); + context.operands.push(new Float(rect.getHeight())); + } + }); + + // showpage + systemDict.put("showpage", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.showpage(); + } + }); + + // findfont + systemDict.put("findfont", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("findfont: wrong arguments"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("findfont: wrong arguments"); + } + context.operands.push(context.pencil.findFont( (String) patoken.value)); + } + }); + + // makefont + systemDict.put("makefont", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(2); + if (! (data[0] instanceof java.awt.Font)) { + throw new PainterException("makefont: wrong arguments"); + } + if (! (data[1] instanceof ArrayList)) { + throw new PainterException("makefont: wrong arguments"); + } + // @TODO implement!!! + context.operands.push(data[0]); + } + }); + + // scalefont + systemDict.put("scalefont", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(2); + if (! (data[0] instanceof java.awt.Font)) { + throw new PainterException("scalefont: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("scalefont: wrong arguments"); + } + java.awt.Font fn=( (java.awt.Font) data[0]).deriveFont( ( (Number) + data[1]). + floatValue()); + System.out.println("Fonthoehe:"+fn.getSize2D()); + context.operands.push(fn ); + } + }); + + // setfont + systemDict.put("setfont", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof java.awt.Font)) { + throw new PainterException("setfont: wrong arguments"); + } + java.awt.Font fn=(java.awt.Font)data[0]; + System.out.println("Fonthoehe:"+fn.getSize2D()); + /** + * @todo two times the same? + */ + context.pencil.graphics.setFont( fn); + context.pencil.state.font=fn; + } + }); + + // def + systemDict.put("def", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(2); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("def: wrong arguments"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("def: wrong arguments"); + } + try { + ( (HashMap) context.dictionaries.peek()).put(patoken.value, data[1]); + } + catch (EmptyStackException e) { + throw new PainterException(e.toString()); + } + } + }); + + // bind + systemDict.put("bind", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("bind: wrong arguments, not PAToken"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("bind: wrong arguments, not Procedure " + + patoken.value); + } + context.engine.bindProcedure(patoken); + context.operands.push(patoken); + } + }); + + // mul + systemDict.put("mul", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.operands.push(new Double(data[0] * data[1])); + } + }); + + // div + systemDict.put("div", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.operands.push(new Double(data[0] / data[1])); + } + }); + + // mod + systemDict.put("mod", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + int a, b, m; + a = (int) data[0]; + b = (int) data[1]; + m = a % b; + context.operands.push(new Integer(m)); + } + }); + + // add + systemDict.put("add", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.operands.push(new Double(data[0] + data[1])); + } + }); + + // neg + systemDict.put("neg", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Double( -data[0])); + } + }); + // ceiling + systemDict.put("ceiling", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.ceil(data[0]))); + } + }); + // sub + systemDict.put("sub", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(2); + context.operands.push(new Double(data[0] - data[1])); + } + }); + + // atan + systemDict.put("atan", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(2); + context.operands.push(new Double(Math.atan2(data[0], data[1]))); + } + }); + + // sin + systemDict.put("sin", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.sin(data[0] * Math.PI / 180.0))); + } + }); + + // cos + systemDict.put("cos", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.cos(data[0] * Math.PI / 180.0))); + } + }); + + // sqrt + systemDict.put("sqrt", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.sqrt(data[0]))); + } + }); + // ln + systemDict.put("log", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.log(data[0]))); + } + }); + // exp + systemDict.put("exp", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(2); + context.operands.push(new Double(Math.pow(data[0], data[1]))); + } + }); + + // exch + systemDict.put("exch", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + context.operands.push(data[1]); + context.operands.push(data[0]); + } + }); + + // dup + systemDict.put("dup", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(1); + context.operands.push(data[0]); + context.operands.push(data[0]); + } + }); + + // roll + systemDict.put("roll", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + Object rollData[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number)) { + throw new PainterException("roll: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("roll: wrong arguments"); + } + int numberOfElements, numberOfPositions, i; + + numberOfElements = ( (Number) data[0]).intValue(); + numberOfPositions = ( (Number) data[1]).intValue(); + + if (numberOfPositions == 0 || numberOfElements <= 0) { + return; + } + + rollData = context.popOperands(numberOfElements); + + if (numberOfPositions < 0) { + numberOfPositions = -numberOfPositions; + numberOfPositions = numberOfPositions % numberOfElements; + + // downward roll + for (i = numberOfPositions; i < numberOfElements; i++) { + context.operands.push(rollData[i]); + } + for (i = 0; i < numberOfPositions; i++) { + context.operands.push(rollData[i]); + } + } + else { + numberOfPositions = numberOfPositions % numberOfElements; + + // upward roll + for (i = numberOfElements - numberOfPositions; i < numberOfElements; + i++) { + context.operands.push(rollData[i]); + } + for (i = 0; i < numberOfElements - numberOfPositions; i++) { + context.operands.push(rollData[i]); + } + } + } + }); + + // pop + systemDict.put("pop", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.popOperands(1); + } + }); + + // index + systemDict.put("index", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof Number)) { + throw new PainterException("index: wrong arguments"); + } + int index = ( (Number) data[0]).intValue(); + try { + context.operands.push(context.operands.elementAt(index)); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new PainterException(e.toString()); + } + } + }); + + // mark + systemDict.put("mark", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(new PAToken(null, PAToken.MARK)); + } + }); + + // cvx + systemDict.put("cvx", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data; + data = context.operands.pop(); + ArrayList ar = (ArrayList) data; + Stack stack = new Stack(); + for (int i = ar.size() - 1; i >= 0; i--) { + stack.add(ar.get(i)); + } + PAToken patoken = new PAToken(stack, PAToken.PROCEDURE); +// patoken.type=PAToken.PROCEDURE; + context.operands.push(patoken); + } + }); + // cleartomark + systemDict.put("cleartomark", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data; + boolean finished = false; + + while (!finished) { + try { + data = context.operands.pop(); + if (data instanceof PAToken) { + if ( ( (PAToken) data).type == PAToken.MARK) { + finished = true; + } + } + } + catch (EmptyStackException e) { + throw new PainterException(e.toString()); + } + } + } + }); + + // copy + systemDict.put("copy", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(2); + + // decide if it's a simple copy or a composite object copy + if ( (data[0] instanceof PAToken) && (data[1] instanceof PAToken)) { + // composite object copy + if ( ( (PAToken) data[0]).type == ( (PAToken) data[1]).type) { + // our tokens are immutable so a copy is easy + context.operands.push(data[0]); + context.operands.push(data[0]); + } + else { + throw new PainterException( + "copy operation failed because composite objects on stack are not of same type"); + } + } + else { + // restore first arg, we're not interested in it in this simple case + context.operands.push(data[0]); + + if (data[1] instanceof Number) { + int index = ( (Number) data[1]).intValue(); + int i, n; + n = context.operands.size(); + Object[] copyData = new Object[index]; + for (i = n - index; i < n; i++) { + try { + copyData[i - n + index] = context.operands.elementAt(i); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new PainterException(e.toString()); + } + } + for (i = 0; i < index; i++) { + context.operands.push(copyData[i]); + } + } + else { + throw new PainterException("I expect a number on stack, dude"); + } + } + } + }); + + // setgray + systemDict.put("setgray", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(1); + context.pencil.graphics.setPaint(new Color( (float) data[0], + (float) data[0], (float) data[0])); + } + }); + + // setrgbcolor + systemDict.put("setrgbcolor", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(3); + float[] fv = new float[3]; + fv[0] = (float) Math.max(Math.min(data[0], 1.0d), 0.0d); + fv[1] = (float) Math.max(Math.min(data[1], 1.0d), 0.0d); + fv[2] = (float) Math.max(Math.min(data[2], 1.0d), 0.0d); + context.pencil.graphics.setPaint(new Color(fv[0], fv[1], fv[2])); + } + }); + + // currentrgbcolor +systemDict.put("currentrgbcolor", new PACommand() { + public void execute(PAContext context) throws PainterException { + Color cl=context.pencil.graphics.getColor(); + float[] fv = cl.getRGBComponents(null); + context.operands.push(new Float(fv[0])); + context.operands.push(new Float(fv[1])); + context.operands.push(new Float(fv[2])); + } +}); + + + // PENDING(uweh): color stuff still shaky + // sethsbcolor + systemDict.put("sethsbcolor", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(3); + float[] fv = new float[3]; + fv[0] = (float) Math.max(Math.min(data[0], 1.0d), 0.0d); + fv[1] = (float) Math.max(Math.min(data[1], 1.0d), 0.0d); + fv[2] = (float) Math.max(Math.min(data[2], 1.0d), 0.0d); + context.pencil.graphics.setPaint(new Color(fv[0], fv[1], fv[2])); + } + }); + + // PENDING(uweh): I have to convert these puppies myself to rgb ? + // setcmykcolor + systemDict.put("setcmykcolor", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + int rd, gr, bl; + + data = context.popNumberOperands(4); + float[] fv = new float[4]; + fv[0] = (float) data[0]; + fv[1] = (float) data[1]; + fv[2] = (float) data[2]; + fv[3] = (float) data[3]; + rd = (int) (255 * Math.max(0, 1 - fv[0] - fv[3])); + gr = (int) (255 * Math.max(0, 1 - fv[1] - fv[3])); + bl = (int) (255 * Math.max(0, 1 - fv[2] - fv[3])); + context.pencil.graphics.setPaint(new Color(rd, gr, bl)); + } + }); + + // setlinewidth + systemDict.put("setlinewidth", new PACommand() { + private double minLineWidth(double w, AffineTransform at) { + double matrix[] = new double[4]; + at.getMatrix(matrix); + double scale = matrix[0] * matrix[3] - matrix[1] * matrix[2]; + double minlw = .25 / Math.sqrt(Math.abs(scale)); + if (w < minlw) { + w = minlw; + } + return w; + } + + public void execute(PAContext context) throws PainterException { + double data[]; + BasicStroke newStroke; + Stroke oldStroke = context.pencil.graphics.getStroke(); + data = context.popNumberOperands(1); + data[0] = this.minLineWidth(data[0], + context.pencil.graphics.getTransform()); + if (oldStroke instanceof BasicStroke) { + newStroke = new BasicStroke( (float) data[0], + ( (BasicStroke) oldStroke).getEndCap(), + ( (BasicStroke) oldStroke).getLineJoin(), + ( (BasicStroke) oldStroke).getMiterLimit(), + ( (BasicStroke) oldStroke).getDashArray(), + ( (BasicStroke) oldStroke).getDashPhase()); + } + else { + newStroke = new BasicStroke( (float) data[0], BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); + } + /** + * @todo two times the same? + */ + context.pencil.graphics.setStroke(newStroke); +// context.pencil.state.stroke=newStroke; + } + }); + + // setlinecap + systemDict.put("setlinecap", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + BasicStroke newStroke; + Stroke oldStroke = context.pencil.graphics.getStroke(); + data = context.popNumberOperands(1); + if (oldStroke instanceof BasicStroke) { + newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(), + (int) data[0], + ( (BasicStroke) oldStroke).getLineJoin(), + ( (BasicStroke) oldStroke).getMiterLimit(), + ( (BasicStroke) oldStroke).getDashArray(), + ( (BasicStroke) oldStroke).getDashPhase()); + } + else { + newStroke = new BasicStroke(1.0f, (int) data[0], + BasicStroke.JOIN_ROUND); + } + context.pencil.graphics.setStroke(newStroke); + } + }); + + // setmiterlimit + systemDict.put("setmiterlimit", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + BasicStroke newStroke; + Stroke oldStroke = context.pencil.graphics.getStroke(); + data = context.popNumberOperands(1); + if (oldStroke instanceof BasicStroke) { + newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(), + ( (BasicStroke) oldStroke).getEndCap(), + ( (BasicStroke) oldStroke).getLineJoin(), + (float) data[0], + ( (BasicStroke) oldStroke).getDashArray(), + ( (BasicStroke) oldStroke).getDashPhase()); + } + else { + newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, (float) data[0]); + } + context.pencil.graphics.setStroke(newStroke); + } + }); + + // setdash + systemDict.put("setdash", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + BasicStroke newStroke; + Stroke oldStroke = context.pencil.graphics.getStroke(); + data = context.popOperands(2); + if (! (data[0] instanceof ArrayList)) { + throw new PainterException("setdash: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("setdash: wrong arguments"); + } + + ArrayList list = (ArrayList) data[0]; + + if (list.size() == 0) { + return; + } + float[] dashpattern = new float[list.size()]; + for (int i = 0; i < dashpattern.length; i++) { + dashpattern[i] = ( (Number) list.get(i)).floatValue(); + } + float dashoffset = ( (Number) data[1]).floatValue(); + if (oldStroke instanceof BasicStroke) { + newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(), + ( (BasicStroke) oldStroke).getEndCap(), + ( (BasicStroke) oldStroke).getLineJoin(), + ( (BasicStroke) oldStroke).getMiterLimit(), + dashpattern, + dashoffset); + } + else { + newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 1.0f, dashpattern, + dashoffset); + } + context.pencil.graphics.setStroke(newStroke); + } + }); + + // setlinejoin + systemDict.put("setlinejoin", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + BasicStroke newStroke; + Stroke oldStroke = context.pencil.graphics.getStroke(); + data = context.popNumberOperands(1); + if (oldStroke instanceof BasicStroke) { + newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(), + ( (BasicStroke) oldStroke).getEndCap(), + (int) data[0], + ( (BasicStroke) oldStroke).getMiterLimit(), + ( (BasicStroke) oldStroke).getDashArray(), + ( (BasicStroke) oldStroke).getDashPhase()); + } + else { + newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, (int) data[0]); + } + context.pencil.graphics.setStroke(newStroke); + } + }); + + // dumpstack + systemDict.put("dumpstack", new PACommand() { + public void execute(PAContext context) throws PainterException { + Enumeration enumx = context.operands.elements(); + System.out.println("-------------Stack--------------"); + while (enumx.hasMoreElements()) { + System.out.println(enumx.nextElement()); + } + System.out.println("--------------------------------"); + } + }); + + // for + systemDict.put("for", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + + data = context.popOperands(4); + if (! (data[3] instanceof PAToken)) { + throw new PainterException("for: wrong arguments"); + } + if (! (data[0] instanceof Number)) { + throw new PainterException("for: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("for: wrong arguments"); + } + if (! (data[2] instanceof Number)) { + throw new PainterException("for: wrong arguments"); + } + patoken = (PAToken) data[3]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("for: wrong arguments"); + } + int i0, i1, i2; + i0 = ( (Number) data[0]).intValue(); + i1 = ( (Number) data[1]).intValue(); + i2 = ( (Number) data[2]).intValue(); + + if (i1 > 0) { + for (int i = i0; i <= i2; i += i1) { + context.operands.push(new Integer(i)); + context.engine.process(patoken); + } + } + else { + for (int i = i0; i >= i2; i -= i1) { + context.operands.push(new Integer(i)); + context.engine.process(patoken); + } + } + } + }); + + // repeat + systemDict.put("repeat", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(2); + if (! (data[1] instanceof PAToken)) { + throw new PainterException("repeat: wrong arguments"); + } + if (! (data[0] instanceof Number)) { + throw new PainterException("repeat: wrong arguments"); + } + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("repeat: wrong arguments"); + } + int n = ( (Number) data[0]).intValue(); + for (int i = 0; i < n; i++) { + context.engine.process(patoken); + } + } + }); + + // true + systemDict.put("true", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(new Boolean(true)); + } + }); + + // false + systemDict.put("false", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(new Boolean(false)); + } + }); + + // lt + systemDict.put("lt", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("lt: wrong arguments"); + } + if (data[0] instanceof Number) { + if (! (data[1] instanceof Number)) { + throw new PainterException("lt: wrong arguments"); + } + double d0, d1; + d0 = ( (Number) data[0]).doubleValue(); + d1 = ( (Number) data[1]).doubleValue(); + if (d0 < d1) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + else { + if (! (data[1] instanceof String)) { + throw new PainterException("lt: wrong arguments"); + } + String s0, s1; + s0 = (String) data[0]; + s1 = (String) data[1]; + if (s0.compareTo(s1) < 0) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + } + }); + + // gt + systemDict.put("gt", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("gt: wrong arguments"); + } + if (data[0] instanceof Number) { + if (! (data[1] instanceof Number)) { + throw new PainterException("gt: wrong arguments"); + } + double d0, d1; + d0 = ( (Number) data[0]).doubleValue(); + d1 = ( (Number) data[1]).doubleValue(); + if (d0 > d1) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + else { + if (! (data[1] instanceof String)) { + throw new PainterException("gt: wrong arguments"); + } + String s0, s1; + s0 = (String) data[0]; + s1 = (String) data[1]; + if (s0.compareTo(s1) > 0) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + } + }); + // ge + systemDict.put("ge", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("ge: wrong arguments"); + } + if (data[0] instanceof Number) { + if (! (data[1] instanceof Number)) { + throw new PainterException("ge: wrong arguments"); + } + double d0, d1; + d0 = ( (Number) data[0]).doubleValue(); + d1 = ( (Number) data[1]).doubleValue(); + if (d0 >= d1) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + else { + if (! (data[1] instanceof String)) { + throw new PainterException("ge: wrong arguments"); + } + String s0, s1; + s0 = (String) data[0]; + s1 = (String) data[1]; + if (s0.compareTo(s1) >= 0) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + } + }); + // ne + systemDict.put("ne", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("ne: wrong arguments"); + } + if (data[0] instanceof Number) { + if (! (data[1] instanceof Number)) { + throw new PainterException("ne: wrong arguments"); + } + double d0, d1; + d0 = ( (Number) data[0]).doubleValue(); + d1 = ( (Number) data[1]).doubleValue(); + if (d0 != d1) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + else { + if (! (data[1] instanceof String)) { + throw new PainterException("ne: wrong arguments"); + } + String s0, s1; + s0 = (String) data[0]; + s1 = (String) data[1]; + if (s0.equals(s1)) { + context.operands.push(new Boolean(false)); + } + else { + context.operands.push(new Boolean(true)); + } + } + } + }); + + // eq + systemDict.put("eq", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("eq: wrong arguments"); + } + if (data[0] instanceof Number) { + if (! (data[1] instanceof Number)) { + throw new PainterException("eq: wrong arguments"); + } + double d0, d1; + d0 = ( (Number) data[0]).doubleValue(); + d1 = ( (Number) data[1]).doubleValue(); + if (d0 == d1) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + else { + if (! (data[1] instanceof String)) { + throw new PainterException("eq: wrong arguments"); + } + String s0, s1; + s0 = (String) data[0]; + s1 = (String) data[1]; + if (s0.compareTo(s1) == 0) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + } + }); + + // if + systemDict.put("if", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(2); + if (! (data[0] instanceof Boolean)) { + throw new PainterException("if: wrong arguments"); + } + if (! (data[1] instanceof PAToken)) { + throw new PainterException("if: wrong arguments"); + } + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("if: wrong arguments"); + } + if ( ( (Boolean) data[0]).booleanValue()) { + context.engine.process(patoken); + } + } + }); + + // ifelse + systemDict.put("ifelse", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken1, patoken2; + data = context.popOperands(3); + if (! (data[0] instanceof Boolean)) { + throw new PainterException("ifelse: wrong arguments"); + } + if (! (data[1] instanceof PAToken)) { + throw new PainterException("ifelse: wrong arguments"); + } + if (! (data[2] instanceof PAToken)) { + throw new PainterException("ifelse: wrong arguments"); + } + patoken1 = (PAToken) data[1]; + patoken2 = (PAToken) data[2]; + if (! (patoken1.type == PAToken.PROCEDURE)) { + throw new PainterException("ifelse: wrong arguments"); + } + if (! (patoken2.type == PAToken.PROCEDURE)) { + throw new PainterException("ifelse: wrong arguments"); + } + if ( ( (Boolean) data[0]).booleanValue()) { + context.engine.process(patoken1); + } + else { + context.engine.process(patoken2); + } + } + }); + + // dict + systemDict.put("dict", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new HashMap( (int) data[0])); + } + }); + + // put + systemDict.put("put", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(3); + if ( (data[0] instanceof HashMap) && (data[1] instanceof PAToken)) { + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("put: wrong arguments"); + } + ( (HashMap) data[0]).put(patoken.value, data[2]); + } + else + if ( (data[0] instanceof ArrayList) && (data[1] instanceof Number)) { + ArrayList ar = (ArrayList) data[0]; + Number nr = (Number) data[1]; + ar.set(nr.intValue(), data[2]); + } + else + if ( (data[0] instanceof StringBuffer) && (data[1] instanceof Number) && + (data[2] instanceof Number)) { + StringBuffer text = (StringBuffer) data[0]; + Number nr = (Number) data[1]; + Number ch = (Number) data[2]; + text.setCharAt(nr.intValue(), (char) (ch.intValue())); + } + else { + throw new PainterException("put: wrong arguments"); + } + } + }); + + // get + systemDict.put("get", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(2); + if (! (data[0] instanceof HashMap) && ! (data[0] instanceof ArrayList)) { + throw new PainterException("get: wrong arguments"); + } + if (data[0] instanceof HashMap) { + if (! (data[1] instanceof PAToken)) { + throw new PainterException("get: wrong arguments"); + } + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("get: wrong arguments"); + } + context.operands.push( ( (HashMap) data[0]).get(patoken.value)); + } + else if (data[0] instanceof ArrayList) { + if (! (data[1] instanceof Number)) { + throw new PainterException("get: wrong arguments"); + } + context.operands.push( ( (ArrayList) data[0]).get( ( (Number) data[1]). + intValue())); + } + } + }); + // getinterval + systemDict.put("getinterval", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(3); + if (! (data[0] instanceof HashMap) && ! (data[0] instanceof ArrayList)) { + throw new PainterException("getinterval: wrong arguments"); + } + if (data[0] instanceof HashMap) { + if (! (data[1] instanceof PAToken)) { + throw new PainterException("getinterval: wrong arguments"); + } + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("getinterval: wrong arguments"); + } + if (! (data[2] instanceof Number)) { + throw new PainterException("getinterval: wrong arguments"); + } + HashMap target = new HashMap(); + context.operands.push( ( (HashMap) data[0]).get(patoken.value)); + } + else if (data[0] instanceof ArrayList) { + if (! (data[1] instanceof Number)) { + throw new PainterException("getinterval: wrong arguments"); + } + if (! (data[2] instanceof Number)) { + throw new PainterException("getinterval: wrong arguments"); + } + ArrayList source = ( (ArrayList) data[0]); + int from = ( (Number) data[1]).intValue(); + int to = from + ( (Number) data[2]).intValue(); + ArrayList target = new ArrayList(source.subList(from, to)); + context.operands.push(target); + } + } + }); + // load + systemDict.put("load", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("load: wrong arguments"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("load: wrong arguments"); + } + context.operands.push(context.findIdentifier(patoken.value)); + } + }); + + // length + systemDict.put("length", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + int size = 0; + data = context.popOperands(1); + if (data[0] instanceof PAToken) { + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("length: wrong arguments"); + } + size = ( (String) patoken.value).length(); + } + else if (data[0] instanceof HashMap) { + size = ( (HashMap) data[0]).size(); + } + else if (data[0] instanceof ArrayList) { + size = ( (ArrayList) data[0]).size(); + } + else if (data[0] instanceof String) { + size = ( (String) data[0]).length(); + } + else { + throw new PainterException("length: wrong arguments"); + } + + context.operands.push(new Integer(size)); + } + }); + + // begin + systemDict.put("begin", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof HashMap)) { + throw new PainterException("begin: wrong arguments"); + } + context.dictionaries.push(data[0]); + } + }); + + // end + systemDict.put("end", new PACommand() { + public void execute(PAContext context) throws PainterException { + try { + context.dictionaries.pop(); + } + catch (EmptyStackException e) { + throw new PainterException("Dictionary stack is empty"); + } + } + }); + + // undef + systemDict.put("undef", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(2); + if (! (data[0] instanceof HashMap)) { + throw new PainterException("undef: wrong arguments"); + } + if (! (data[1] instanceof PAToken)) { + throw new PainterException("undef: wrong arguments"); + } + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("undef: wrong arguments"); + } + // we don't do an actual undef because we don't care + } + }); + + // known + systemDict.put("known", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[], foundObject; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("known: wrong arguments"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("known: wrong arguments"); + } + foundObject = context.findIdentifier(patoken.value); + if (foundObject != null) { + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + }); + + // where + systemDict.put("where", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[], foundObject; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("where: wrong arguments"); + } + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.KEY)) { + throw new PainterException("where: wrong arguments"); + } + foundObject = context.findDictionary(patoken.value); + if (foundObject != null) { + context.operands.push(foundObject); + context.operands.push(new Boolean(true)); + } + else { + context.operands.push(new Boolean(false)); + } + } + }); + + // aload + systemDict.put("aload", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object[] data; + java.util.AbstractList list; + data = context.popOperands(1); + if (data[0] instanceof PAToken) { + data[0] = ( (PAToken) data[0]).value; + } + if (! (data[0] instanceof java.util.AbstractList)) { + throw new PainterException("aload: wrong arguments"); + } + + list = (java.util.AbstractList) data[0]; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + context.operands.push(iterator.next()); + } + context.operands.push(data[0]); + } + }); + + // forall + systemDict.put("forall", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + ArrayList list; + PAToken patoken; + + data = context.popOperands(2); + if (! (data[0] instanceof ArrayList)) { + throw new PainterException("forall: wrong arguments"); + } + if (! (data[1] instanceof PAToken)) { + throw new PainterException("forall: wrong arguments"); + } + + patoken = (PAToken) data[1]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("forall: wrong arguments"); + } + + list = (ArrayList) data[0]; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + context.operands.push(iterator.next()); + context.engine.process(patoken); + } + + } + }); + + // currentflat PENDING(uweh):placeholder for now + systemDict.put("currentflat", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + context.operands.push(new Double(1.0f)); + } + }); + + // setflat PENDING(uweh):placeholder for now + systemDict.put("setflat", new PACommand() { + public void execute(PAContext context) throws PainterException { + double[] data; + data = context.popNumberOperands(1); + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + cb.setFlatness( ( (float) data[0])); + } + }); + + // round + systemDict.put("round", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Long(Math.round(data[0]))); + } + }); + + // abs + systemDict.put("abs", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + data = context.popNumberOperands(1); + context.operands.push(new Double(Math.abs(data[0]))); + } + }); + + // transform + systemDict.put("transform", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (context.peekOperand() instanceof Number) { + double data[]; + double[] transformedData = new double[2]; + data = context.popNumberOperands(2); + AffineTransform at = context.pencil.graphics.getTransform(); + at.transform(data, 0, transformedData, 0, 1); + context.operands.push(new Double(transformedData[0])); + context.operands.push(new Double(transformedData[1])); + } + else { + Object data[]; + + data = context.popOperands(3); + if (! (data[0] instanceof Number)) { + throw new PainterException("transform: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("transform: wrong arguments"); + } + if (! (data[2] instanceof ArrayList)) { + throw new PainterException("transform: wrong arguments"); + } + + ArrayList array = (ArrayList) data[2]; + + double[] entries = new double[6]; + + if (! (array.size() == 6)) { + throw new PainterException("transform: wrong arguments"); + } + + for (int i = 0; i < 6; i++) { + entries[i] = ( (Number) array.get(i)).doubleValue(); + } + + AffineTransform at = new AffineTransform(entries); + + double numberdata[] = new double[2]; + numberdata[0] = ( (Number) data[0]).doubleValue(); + numberdata[1] = ( (Number) data[1]).doubleValue(); + + double[] transformedData = new double[2]; + + at.transform(numberdata, 0, transformedData, 0, 1); + context.operands.push(new Double(transformedData[0])); + context.operands.push(new Double(transformedData[1])); + } + } + }); + + // itransform + systemDict.put("itransform", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (context.peekOperand() instanceof Number) { + double data[]; + double[] transformedData = new double[2]; + data = context.popNumberOperands(2); + AffineTransform at = context.pencil.graphics.getTransform(); + try { + at.inverseTransform(data, 0, transformedData, 0, 1); + } + catch (NoninvertibleTransformException e) { + throw new PainterException(e.toString()); + } + context.operands.push(new Double(transformedData[0])); + context.operands.push(new Double(transformedData[1])); + } + else { + Object data[]; + + data = context.popOperands(3); + if (! (data[0] instanceof Number)) { + throw new PainterException("itransform: wrong arguments"); + } + if (! (data[1] instanceof Number)) { + throw new PainterException("itransform: wrong arguments"); + } + if (! (data[2] instanceof ArrayList)) { + throw new PainterException("itransform: wrong arguments"); + } + + ArrayList array = (ArrayList) data[2]; + + double[] entries = new double[6]; + + if (! (array.size() == 6)) { + throw new PainterException("itransform: wrong arguments"); + } + + for (int i = 0; i < 6; i++) { + entries[i] = ( (Number) array.get(i)).doubleValue(); + } + + AffineTransform at = new AffineTransform(entries); + + double numberdata[] = new double[2]; + numberdata[0] = ( (Number) data[0]).doubleValue(); + numberdata[1] = ( (Number) data[0]).doubleValue(); + + double[] transformedData = new double[2]; + + try { + at.inverseTransform(numberdata, 0, transformedData, 0, 1); + } + catch (NoninvertibleTransformException e) { + throw new PainterException(e.toString()); + } + context.operands.push(new Double(transformedData[0])); + context.operands.push(new Double(transformedData[1])); + } + } + }); + + // currentpoint + // PENDING(uweh): what about CTM, same thing when you construct path + // this is different than ps, might not work in a few instances + systemDict.put("currentpoint", new PACommand() { + public void execute(PAContext context) throws PainterException { + Point2D currentPoint = context.pencil.state.path.getCurrentPoint(); + context.operands.push(new Double(currentPoint.getX())); + context.operands.push(new Double(currentPoint.getY())); + } + }); + + // clippath + systemDict.put("clippath", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.clippath(); + } + }); + + // matrix + systemDict.put("matrix", new PACommand() { + public void execute(PAContext context) throws PainterException { + ArrayList identityMatrix = new ArrayList(6); + + identityMatrix.add(new Double(1.0d)); + identityMatrix.add(new Double(0.0d)); + identityMatrix.add(new Double(0.0d)); + identityMatrix.add(new Double(1.0d)); + identityMatrix.add(new Double(0.0d)); + identityMatrix.add(new Double(0.0d)); + context.operands.push(identityMatrix); + } + }); + + // concatmatrix + systemDict.put("concatmatrix", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(3); + if (! (data[0] instanceof ArrayList)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + if (! (data[1] instanceof ArrayList)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + if (! (data[2] instanceof ArrayList)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + ArrayList arrayOne, arrayTwo, arrayThree; + AffineTransform atOne, atTwo; + + arrayOne = (ArrayList) data[0]; + arrayTwo = (ArrayList) data[1]; + arrayThree = (ArrayList) data[2]; + + double[] entries = new double[6]; + + if (! (arrayOne.size() == 6)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + if (! (arrayTwo.size() == 6)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + if (! (arrayThree.size() == 6)) { + throw new PainterException("concatmatrix: wrong arguments"); + } + + for (int i = 0; i < 6; i++) { + entries[i] = ( (Number) arrayOne.get(i)).doubleValue(); + } + atOne = new AffineTransform(entries); + for (int i = 0; i < 6; i++) { + entries[i] = ( (Number) arrayTwo.get(i)).doubleValue(); + } + atTwo = new AffineTransform(entries); + + atOne.concatenate(atTwo); + + atOne.getMatrix(entries); + for (int i = 0; i < 6; i++) { + arrayThree.set(i, new Double(entries[i])); + } + context.operands.push(arrayThree); + } + }); + + // pathbbox + systemDict.put("pathbbox", new PACommand() { + public void execute(PAContext context) throws PainterException { + Rectangle2D pathBounds = context.pencil.state.path.getBounds2D(); + + context.operands.push(new Double(pathBounds.getMinX())); + context.operands.push(new Double(pathBounds.getMinY())); + context.operands.push(new Double(pathBounds.getMaxX())); + context.operands.push(new Double(pathBounds.getMaxY())); + } + }); + + // initmatrix + systemDict.put("initmatrix", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); +// cb.transform(Affine); + } + }); + // initclip + systemDict.put("initclip", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + context.pencil.clippath(); +// pdfg2d.setClip(context.); +// if(!PAContext.IgnoreUnknownCommands) +// throw new UnsupportedOperationException("initclip"); + } + }); + + // truncate + systemDict.put("truncate", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + double truncated; + + data = context.popNumberOperands(1); + if (data[0] < 0) { + truncated = Math.ceil(data[0]); + } + else { + truncated = Math.floor(data[0]); + } + context.operands.push(new Double(truncated)); + } + }); + + // rand + systemDict.put("rand", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(new Integer(Math.abs(randomNumberGenerator. + nextInt( (1 << 31) - 1)))); + } + }); + + // srand + systemDict.put("srand", new PACommand() { + public void execute(PAContext context) throws PainterException { + double data[]; + + data = context.popNumberOperands(1); + randomNumberGenerator = new Random(Math.round(data[0])); + } + }); + // version + systemDict.put("version", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push("2016"); + } + }); + // cvi + systemDict.put("cvi", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(1); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("cvi: wrong arguments"); + } + if (data[0] instanceof Number) { + int d; + + d = ( (Number) data[0]).intValue(); + context.operands.push(new Integer(d)); + } + else { + String s; + s = (String) data[0]; + + context.operands.push(new Integer(s)); + } + } + }); + // cvr + systemDict.put("cvr", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(1); + if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) { + throw new PainterException("cvr: wrong arguments"); + } + if (data[0] instanceof Number) { + int d; + + d = ( (Number) data[0]).intValue(); + context.operands.push(new Double(d)); + } + else { + String s; + s = (String) data[0]; + + context.operands.push(new Double(s)); + } + } + }); + // usertime + systemDict.put("usertime", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(new Long(System.currentTimeMillis())); + } + }); +// save + systemDict.put("save", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + cb.saveState(); + context.operands.push(new Long(0)); + } + }); +// restore + systemDict.put("restore", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + cb.restoreState(); + Object data[]; + data = context.popOperands(1); + } + }); +// clear + systemDict.put("clear", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.clear(); + } + }); + // readonly + systemDict.put("readonly", new PACommand() { + public void execute(PAContext context) throws PainterException { + } + }); + +// currentfile + systemDict.put("currentfile", new PACommand() { + public void execute(PAContext context) throws PainterException { + final JavaCharStream jcs=context.poorscript.jj_input_stream; + InputStream ins=new InputStream(){ + /** + * Reads the next byte of data from the input stream. + * + * @return the next byte of data, or -1 if the end of the stream is reached. + * @throws IOException if an I/O error occurs. + * @todo Implement this java.io.InputStream method + */ + public int read() throws IOException { + return jcs.readChar(); + } + + }; + context.operands.push(ins); + } + }); + // flushfile + systemDict.put("flushfile", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof InputStream)) { + throw new PainterException("flushfile: wrong arguments"); + } + + InputStream is = (InputStream) data[0]; + try { + while (is.read() != -1) { + } + } + catch (IOException ex) { + } + } + }); + + // closefile + systemDict.put("closefile", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof InputStream)) { + throw new PainterException("closefile: wrong arguments"); + } + + InputStream is = (InputStream) data[0]; + try { + is.close(); + } + catch (IOException ex) { + } + } + }); + + // string + systemDict.put("string", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (! (data[0] instanceof Number)) { + throw new PainterException("string: wrong arguments"); + } + int d; + d = ( (Number) data[0]).intValue(); + StringBuffer sb = new StringBuffer(d); + sb.setLength(d); + context.operands.push(sb); + } + }); + // null + systemDict.put("null", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(null); + } + }); + // currentscreen + systemDict.put("currentscreen", new PACommand() { + public void execute(PAContext context) throws PainterException { + if (!PAContext.IgnoreUnknownCommands) { + throw new UnsupportedOperationException("currentscreen"); + } + else { + context.operands.push(new Double(60)); + context.operands.push(new Double(0)); + context.operands.push(new Double(0)); + } + } + }); + // setscreen + systemDict.put("setscreen", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(3); + +// if (!PAContext.IgnoreUnknownCommands) +// throw new UnsupportedOperationException("setscreen"); +// else { +// +// } + } + }); + + // flattenpath + systemDict.put("flattenpath", new PACommand() { + public void execute(PAContext context) throws PainterException { + + } + }); + // filter + systemDict.put("filter", new PACommand() { + public void execute(PAContext context) throws PainterException { + String filtername; + filtername = (String) ( (PAToken) context.popOperands(1)[0]).value; + Object obj; + while (! ( (obj = context.peekOperand()) instanceof InputStream)) { + Object param = context.popOperands(1); + } + + InputStream datasrc; + datasrc = (InputStream) (context.popOperands(1)[0]); + + InputStream dis; + if (filtername.equals("ASCIIHexDecode")) { + // dis = new ASCIIHexInputStream(datasrc); + final InputStream is=datasrc; + dis=new InputStream(){ + + /** + * Reads the next byte of data from the input stream. + * + * @return the next byte of data, or -1 if the end of the stream is reached. + * @throws IOException if an I/O error occurs. + * @todo Implement this java.io.InputStream method + */ + public int read() throws IOException { + int firstchar,secondchar; + for(;;){ + firstchar=is.read(); + if(firstchar==-1)return -1; + if(firstchar=='>')return -1; + if(firstchar=='\n')continue; + if(firstchar=='\r')continue; + break; + } + for(;;){ + secondchar=is.read(); + if(secondchar=='>')return -1; + if(secondchar==-1)return -1; + if(secondchar=='\n')continue; + if(secondchar=='\r')continue; + break; + } + int highbyte=0; + if(firstchar>=48&&firstchar<=57)highbyte=firstchar-48; + if(firstchar>=65&&firstchar<=70)highbyte=firstchar-55; + int lowbyte=0; + if(secondchar>=48&&secondchar<=57)lowbyte=secondchar-48; + if(secondchar>=65&&secondchar<=70)lowbyte=secondchar-55; + + return(highbyte*16+lowbyte); + } + }; + } +// else +// if (filtername.equals("DCTDecode")) { +// dis = new DCTInputStream(datasrc); +// } + else { + dis = datasrc; + } + + context.operands.push(dis); + } + }); + // clip + systemDict.put("clip", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.pencil.clip(); + } + }); + // setcolorspace + systemDict.put("setcolorspace", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + Object data[]; + data = context.popOperands(1); + if (data[0] instanceof PAToken) { + String colorspace = ( (String) ( (PAToken) data[0]).value); + cb.setDefaultColorspace(PdfName.COLORSPACE, PdfName.DEVICERGB); + + } + } + }); + // image + systemDict.put("image", new PACommand() { + public void execute(PAContext context) throws PainterException { + PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics; + PdfContentByte cb = pdfg2d.getContent(); + Object data[]; + data = context.popOperands(1); + if (data[0] instanceof Number) { + /** + * Level1 image + */ + int width = ( (Number) data[0]).intValue(); + data = context.popOperands(4); + int height = ( (Number) data[0]).intValue(); + int bits = ( (Number) data[1]).intValue(); + + }else if (data[0] instanceof PAToken) { + PAToken proc = (PAToken) data[0]; + + data = context.popOperands(4); + int width = ( (Number) data[0]).intValue(); + int height = ( (Number) data[1]).intValue(); + int bitspercomponent = ( (Number) data[2]).intValue(); + ArrayList ar = (ArrayList) data[3]; + System.out.println("I " + width + "*" + height + " " + + bitspercomponent + " " + ar); + +// context.engine.process(proc); + } + else if (data[0] instanceof HashMap){ + HashMap hsm = (HashMap) data[0]; + Iterator it = hsm.keySet().iterator(); + int width = 0, height = 0, bitspercomponent = 0; + int imagetype = 0; + InputStream datasrc = null; + Object decode = null; + Object imagematrix = null; + while (it.hasNext()) { + PAToken token = (PAToken) it.next(); + if (token.value.toString().equals("ImageType")) { + imagetype = ( (Number) hsm.get(token)).intValue(); + } + if (token.value.toString().equals("DataSource")) { + datasrc = (InputStream) hsm.get(token); + } + + if (token.value.toString().equals("BitsPerComponent")) { + bitspercomponent = ( (Number) hsm.get(token)).intValue(); + } + if (token.value.toString().equals("Width")) { + width = ( (Number) hsm.get(token)).intValue(); + } + if (token.value.toString().equals("Height")) { + height = ( (Number) hsm.get(token)).intValue(); + } + if (token.value.toString().equals("Decode")) { + decode = ( (Object) hsm.get(token)); + } + if (token.value.toString().equals("ImageMatrix")) { + imagematrix = ( (Object) hsm.get(token)); + } + } + + try { + byte[] barr = {}; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int aByte; + while ( (aByte = datasrc.read()) >= 0) { + bout.write(aByte); +// System.out.print((char)aByte); + } + System.out.println("I " + width + "*" + height + " " + + bitspercomponent + " " + imagetype + " " + + decode + " " + imagematrix + " " + datasrc); + barr = bout.toByteArray(); +// com.lowagie.text.Image img = new ImgRaw(width, height, 1, +// bitspercomponent, barr); + com.lowagie.text.Image img = new Jpeg(barr); + try { + cb.addImage(img,width,0,0,height,0,0); + } + catch (DocumentException ex1) { + ex1.printStackTrace(); + } + } + catch (IOException ex) { + ex.printStackTrace(); + } + catch (BadElementException ex) { + ex.printStackTrace(); + } + + } + } + }); + + // imagemask + systemDict.put("imagemask", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(5); +// if (data[0] instanceof PAToken) { +// PAToken token = (PAToken) data[0]; +// context.engine.process(token); +// } + } + }); + + + // exec + systemDict.put("exec", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + data = context.popOperands(1); + if (data[0] instanceof PAToken) { + PAToken token = (PAToken) data[0]; + context.engine.process(token); + } + } + }); + // currentdict + systemDict.put("currentdict", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.operands.push(context.dictionaries.peek()); + } + }); + +// cleardictstack + systemDict.put("cleardictstack", new PACommand() { + public void execute(PAContext context) throws PainterException { + context.dictionaries.clear(); + HashMap systemDict = context.constructSystemDict(); + context.dictionaries.push(systemDict); + HashMap globalDict = context.constructGlobalDict(); + context.dictionaries.push(globalDict); + HashMap userDict = context.constructUserDict(); + systemDict.put("userdict", userDict); + systemDict.put("globaldict", globalDict); + context.dictionaries.push(userDict); + } + }); + + // charpath + systemDict.put("charpath", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + + data = context.popOperands(2); + if (! (data[0] instanceof String)) { + throw new PainterException("charpath: wrong arguments"); + } + if (! (data[1] instanceof Boolean)) { + throw new PainterException("charpath: wrong arguments"); + } + + context.pencil.charpath( (String) data[0], + ( (Boolean) data[1]).booleanValue()); + } + }); + + // PENDING(uweh): we only support procedure right now and always push false on the stack + // stopped + systemDict.put("stopped", new PACommand() { + public void execute(PAContext context) throws PainterException { + Object data[]; + PAToken patoken; + data = context.popOperands(1); + if (! (data[0] instanceof PAToken)) { + throw new PainterException("stopped: wrong arguments"); + } + + patoken = (PAToken) data[0]; + if (! (patoken.type == PAToken.PROCEDURE)) { + throw new PainterException("stopped: wrong arguments"); + } + context.engine.process(patoken); + context.operands.push(new Boolean(false)); + } + }); + systemDict.put("systemdict", systemDict); + return systemDict; + } + + /** + *

Title:

+ * + *

Description:

+ * + *

Copyright: Copyright (c) 2006

+ * + *

Company:

+ * @author not attributable + * @version 1.0 + */ + private class NamedInputStream extends InputStream{ + public String filename; + InputStream in; + NamedInputStream(InputStream in,String filename) { + super(); + this.filename=filename; + this.in=in; + } + public int read() throws IOException { + return in.read(); + } + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAEngine.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAEngine.java new file mode 100644 index 0000000..e905402 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAEngine.java @@ -0,0 +1,155 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +import java.util.*; + + +public class PAEngine extends Object { + + static public final int MODE_STACK = 0; + static public final int MODE_PROCEDURE = 1; + static public final int MODE_ARRAY = 2; + + protected PAContext context; + protected int mode; + protected Stack procedure; + protected int innerProcedures; + + public PAEngine(PAContext context){ + super(); + this.context = context; + this.mode = PAEngine.MODE_STACK; + } + + public void startProcedure() throws PainterException { + this.procedure = new Stack(); + this.mode = PAEngine.MODE_PROCEDURE; + this.innerProcedures = 0; + } + + public void endProcedure() throws PainterException { + this.context.operands.push(new PAToken(this.procedure, PAToken.PROCEDURE)); + this.mode = PAEngine.MODE_STACK; + } + + public void bindProcedure(PAToken patoken){ + Stack oldStack = (Stack) patoken.value; + Stack newStack = new Stack(); + int i,n; + n = oldStack.size(); + for(i = 0; i < n; i++){ + Object token = oldStack.elementAt(i); + if((token instanceof PAToken) && ((PAToken) token).type == PAToken.IDENTIFIER){ + Object foundToken = this.context.findIdentifier(((PAToken) token).value); + if(foundToken == null){ + newStack.push(token); + } else { + newStack.push(foundToken); + } + } else { + newStack.push(token); + } + } + patoken.value = newStack; + } + + public void process(Object token) throws PainterException { + if(token == null){ + throw new IllegalStateException("Null token encountered; last unknown identifier was " + this.context.getLastUnknownIdentifier()+" at line "+this.context.poorscript.token.beginLine); + } + if(PAContext.DebugExecution){ + System.out.print("==>" + token.toString()); +// System.out.flush(); + } + if(token instanceof PAToken && ((PAToken) token).type == PAToken.IMMEDIATE){ + Object foundValue = this.context.findIdentifier(((PAToken) token).value); + this.process(foundValue); + return; + } + if(this.mode == MODE_STACK){ + if(token instanceof PACommand){ + ((PACommand) token).execute(this.context); + } else if(token instanceof PAToken){ + PAToken patoken = (PAToken) token; + + switch(patoken.type){ + case PAToken.IDENTIFIER: + this.process(this.context.findIdentifier(patoken.value)); + break; + case PAToken.KEY: + case PAToken.MARK: + case PAToken.START_ARRAY: + this.context.operands.push(token); + break; + case PAToken.PROCEDURE: + Enumeration enumx = ((Vector) patoken.value).elements(); + while(enumx.hasMoreElements()){ + this.process(enumx.nextElement()); + } + break; + case PAToken.START_PROCEDURE: + this.startProcedure(); + break; + case PAToken.END_ARRAY: + this.context.collectArray(); + break; + case PAToken.START_DICT: + this.context.operands.push(token); + break; + case PAToken.END_DICT: + this.context.collectDict(); + break; + default: + throw new IllegalStateException("Unknown token encountered" + token); + } + } else { + this.context.operands.push(token); + } + } else if(this.mode == MODE_PROCEDURE){ + if(token instanceof PAToken){ + PAToken patoken = (PAToken) token; + + switch(patoken.type){ + case PAToken.START_PROCEDURE: + this.innerProcedures++; + this.procedure.push(token); + break; + case PAToken.END_PROCEDURE: + if(this.innerProcedures > 0){ + this.innerProcedures--; + this.procedure.push(token); + } else { + this.endProcedure(); + } + break; + default: + this.procedure.push(token); + } + } else { + this.procedure.push(token); + } + } + } + + public String litMode(){ + switch(this.mode){ + case MODE_ARRAY: return "Array"; + case MODE_PROCEDURE: return "Proc"+innerProcedures; + case MODE_STACK: return "Stack"; + default: return "Unknown"; + } + } + +} + diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParser.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParser.java new file mode 100644 index 0000000..3c2c64a --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParser.java @@ -0,0 +1,351 @@ +/* Generated By:JavaCC: Do not edit this line. PAParser.java */ +package com.lowagie.text.pdf.codec.postscript; + +import java.lang.*; +import java.lang.reflect.*; +import java.util.*; +import java.awt.*; +import java.awt.geom.*; +import java.awt.color.*; +import java.awt.font.*; +import java.io.*; +import java.net.URL; + +public class PAParser extends Object implements PAParserConstants { + + void error_skipto(int kind) throws ParseException { +ParseException e=generateParseException(); +Token t; +String dump=""; +do{ +if(getToken(1).kind==kind)break; +t=getNextToken(); +dump+=t.image; +}while(t.kind!=kind); +System.out.println("Ignoriere >"+dump+"<"); + } + + String ExceptionString(String hint,JavaCharStream jj_input_stream,PAContext context,Token t,Exception e) throws ParseException { + return "\nparser "+hint+" ["+jj_input_stream.bufpos+"]"+context.engine.litMode()+":\""+t.image+"\" in line "+t.beginLine+" column "+t.beginColumn+"\n"+e.toString(); + } + + final public void parse(PAContext context) throws ParseException { + Token x = null; + try { + label_1: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case IDENTIFIER: + case KEY_IDENTIFIER: + case IMMEDIATE_IDENTIFIER: + case LBRACE: + case RBRACE: + case LBRACKET: + case RBRACKET: + case LDICT: + case RDICT: + case Instring: + ; + break; + default: + jj_la1[0] = jj_gen; + break label_1; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case IDENTIFIER: + case KEY_IDENTIFIER: + case IMMEDIATE_IDENTIFIER: + case Instring: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTEGER_LITERAL: + x = jj_consume_token(INTEGER_LITERAL); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new Integer(x.image)); + } catch(NumberFormatException e) { + {if (true) throw new ParseException(ExceptionString("int_literal",jj_input_stream,context,token,e));} + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("int_literal",jj_input_stream,context,token,e));} + } + break; + case FLOATING_POINT_LITERAL: + x = jj_consume_token(FLOATING_POINT_LITERAL); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new Double(x.image)); + } catch(NumberFormatException e) { + {if (true) throw new ParseException(ExceptionString("float_literal",jj_input_stream,context,token,e));} + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("float_literal",jj_input_stream,context,token,e));} + } + break; + case Instring: + jj_consume_token(Instring); + x = jj_consume_token(HEX_STRING_LITERAL); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process((x.image.substring(1, x.image.length() -1))); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("hex_string_literal",jj_input_stream,context,token,e));} + } + break; + case STRING_LITERAL: + x = jj_consume_token(STRING_LITERAL); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(x.image.substring(1, x.image.length() -1)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("string_literal",jj_input_stream,context,token,e));} + } + break; + case IDENTIFIER: + x = jj_consume_token(IDENTIFIER); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(x.image, PAToken.IDENTIFIER)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("identifier",jj_input_stream,context,token,e));} + } + break; + case KEY_IDENTIFIER: + x = jj_consume_token(KEY_IDENTIFIER); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(x.image.substring(1, x.image.length()), PAToken.KEY)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("key_identifier",jj_input_stream,context,token,e));} + } + break; + case IMMEDIATE_IDENTIFIER: + x = jj_consume_token(IMMEDIATE_IDENTIFIER); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+x.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(x.image.substring(2, x.image.length()), PAToken.IMMEDIATE)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("immediate_identifier",jj_input_stream,context,token,e));} + } + break; + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + break; + case LBRACE: + jj_consume_token(LBRACE); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.START_PROCEDURE)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("lbrace",jj_input_stream,context,token,e));} + } + break; + case RBRACE: + jj_consume_token(RBRACE); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.END_PROCEDURE)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("rbrace",jj_input_stream,context,token,e));} + } + break; + case LBRACKET: + jj_consume_token(LBRACKET); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.START_ARRAY)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("lbracket",jj_input_stream,context,token,e));} + } + break; + case RBRACKET: + jj_consume_token(RBRACKET); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.END_ARRAY)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("rbracket",jj_input_stream,context,token,e));} + } + break; + case LDICT: + jj_consume_token(LDICT); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.START_DICT)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("ldict",jj_input_stream,context,token,e));} + } + break; + case RDICT: + jj_consume_token(RDICT); + try { + {if(PAContext.DebugExecution){System.out.print("\nparser ["+jj_input_stream.getBeginLine()+","+jj_input_stream.getBeginColumn()+"]"+context.engine.litMode()+":\""+token.image+"\"");System.out.flush();System.err.flush();}} + context.engine.process(new PAToken(null, PAToken.END_DICT)); + } catch(PainterException e) { + {if (true) throw new ParseException(ExceptionString("rdict",jj_input_stream,context,token,e));} + } + break; + default: + jj_la1[2] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } catch (ParseException e) { + System.out.flush();System.err.flush(); + //System.out.println("Fehlerhaftes Element in Spalte "+e.currentToken.beginColumn+" in Eingabedatei in Zeile="+e.currentToken.next.beginLine+" in Zeichen Nr. "+e.currentToken.next.beginColumn+". >"+e.currentToken.next.image+"< wurde hier nicht erwartet."); + //System.err.println("Fehler:"+e); + e.printStackTrace(); + error_skipto(WHITESPACE); + System.exit(0); + } + } + + public PAParserTokenManager token_source; + JavaCharStream jj_input_stream; + public Token token, jj_nt; + private int jj_ntk; + private int jj_gen; + final private int[] jj_la1 = new int[3]; + final private int[] jj_la1_0 = {0x13f3d20,0x1003d20,0x13f3d20,}; + + public PAParser(java.io.InputStream stream) { + jj_input_stream = new JavaCharStream(stream, 1, 1); + token_source = new PAParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + public void ReInit(java.io.InputStream stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + public PAParser(java.io.Reader stream) { + jj_input_stream = new JavaCharStream(stream, 1, 1); + token_source = new PAParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + public void ReInit(java.io.Reader stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + public PAParser(PAParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + public void ReInit(PAParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; + } + + final private Token jj_consume_token(int kind) throws ParseException { + Token oldToken; + if ((oldToken = token).next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + if (token.kind == kind) { + jj_gen++; + return token; + } + token = oldToken; + jj_kind = kind; + throw generateParseException(); + } + + final public Token getNextToken() { + if (token.next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + jj_gen++; + return token; + } + + final public Token getToken(int index) { + Token t = token; + for (int i = 0; i < index; i++) { + if (t.next != null) t = t.next; + else t = t.next = token_source.getNextToken(); + } + return t; + } + + final private int jj_ntk() { + if ((jj_nt=token.next) == null) + return (jj_ntk = (token.next=token_source.getNextToken()).kind); + else + return (jj_ntk = jj_nt.kind); + } + + private java.util.Vector jj_expentries = new java.util.Vector(); + private int[] jj_expentry; + private int jj_kind = -1; + + final public ParseException generateParseException() { + jj_expentries.removeAllElements(); + boolean[] la1tokens = new boolean[25]; + for (int i = 0; i < 25; i++) { + la1tokens[i] = false; + } + if (jj_kind >= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 3; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1<", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "\"{\"", + "\"}\"", + "\"[\"", + "\"]\"", + "\"<<\"", + "\">>\"", + "", + "\">\"", + "\"<\"", + }; + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserTokenManager.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserTokenManager.java new file mode 100644 index 0000000..9b908ba --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserTokenManager.java @@ -0,0 +1,1011 @@ +/* Generated By:JavaCC: Do not edit this line. PAParserTokenManager.java */ +package com.lowagie.text.pdf.codec.postscript; +import java.lang.*; +import java.lang.reflect.*; +import java.util.*; +import java.awt.*; +import java.awt.geom.*; +import java.awt.color.*; +import java.awt.font.*; +import java.io.*; +import java.net.URL; + +public class PAParserTokenManager implements PAParserConstants +{ + public java.io.PrintStream debugStream = System.out; + public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } +private final int jjStopStringLiteralDfa_0(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_0(int pos, long active0) +{ + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); +} +private final int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private final int jjStartNfaWithStates_0(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_0(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_0() +{ + switch(curChar) + { + case 60: + jjmatchedKind = 24; + return jjMoveStringLiteralDfa1_0(0x100000L); + case 62: + return jjMoveStringLiteralDfa1_0(0x200000L); + case 91: + return jjStopAtPos(0, 18); + case 93: + return jjStopAtPos(0, 19); + case 123: + return jjStopAtPos(0, 16); + case 125: + return jjStopAtPos(0, 17); + default : + return jjMoveNfa_0(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_0(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0); + return 1; + } + switch(curChar) + { + case 60: + if ((active0 & 0x100000L) != 0L) + return jjStopAtPos(1, 20); + break; + case 62: + if ((active0 & 0x200000L) != 0L) + return jjStopAtPos(1, 21); + break; + default : + break; + } + return jjStartNfa_0(0, active0); +} +private final void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private final void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private final void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} +private final void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} +private final void jjCheckNAddStates(int start) +{ + jjCheckNAdd(jjnextStates[start]); + jjCheckNAdd(jjnextStates[start + 1]); +} +static final long[] jjbitVec0 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec2 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec3 = { + 0x1ff00000fffffffeL, 0xffffffffffffc000L, 0xffffffffL, 0x600000000000000L +}; +static final long[] jjbitVec4 = { + 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL +}; +static final long[] jjbitVec5 = { + 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec6 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L +}; +static final long[] jjbitVec7 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L, 0x0L +}; +static final long[] jjbitVec8 = { + 0x3fffffffffffL, 0x0L, 0x0L, 0x0L +}; +private final int jjMoveNfa_0(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 74; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 5) + kind = 5; + jjCheckNAddStates(0, 8); + } + else if ((0x100003610L & l) != 0L) + { + if (kind > 1) + kind = 1; + } + else if ((0x8000281000000000L & l) != 0L) + { + if (kind > 11) + kind = 11; + jjCheckNAdd(21); + } + else if (curChar == 37) + jjCheckNAddStates(9, 14); + else if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 26; + else if (curChar == 40) + jjCheckNAddStates(15, 17); + else if (curChar == 46) + jjCheckNAdd(6); + if (curChar == 45) + jjCheckNAddStates(18, 23); + else if (curChar == 47) + jjCheckNAddTwoStates(23, 24); + else if (curChar == 48) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 1: + if (curChar == 48) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 3: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 5) + kind = 5; + jjCheckNAddTwoStates(3, 4); + break; + case 5: + if (curChar == 46) + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(24, 26); + break; + case 8: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(9); + break; + case 9: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(9, 10); + break; + case 11: + if (curChar == 40) + jjCheckNAddStates(15, 17); + break; + case 12: + if ((0xfffffdffffffffffL & l) != 0L) + jjCheckNAddStates(15, 17); + break; + case 14: + if ((0x38000000400L & l) != 0L) + jjCheckNAddStates(15, 17); + break; + case 15: + if (curChar == 41 && kind > 10) + kind = 10; + break; + case 16: + if ((0xff000000000000L & l) != 0L) + jjCheckNAddStates(27, 30); + break; + case 17: + if ((0xff000000000000L & l) != 0L) + jjCheckNAddStates(15, 17); + break; + case 18: + if ((0xf000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 19; + break; + case 19: + if ((0xff000000000000L & l) != 0L) + jjCheckNAdd(17); + break; + case 20: + if ((0x8000281000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(21); + break; + case 21: + if ((0x83ff681000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(21); + break; + case 22: + if (curChar == 47) + jjCheckNAddTwoStates(23, 24); + break; + case 23: + if ((0x400800000000L & l) != 0L) + jjCheckNAdd(24); + break; + case 24: + if ((0x8000281000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(25); + break; + case 25: + if ((0x83ff681000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(25); + break; + case 26: + if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 27; + break; + case 27: + if ((0x8000281000000000L & l) == 0L) + break; + if (kind > 13) + kind = 13; + jjCheckNAdd(28); + break; + case 28: + if ((0x83ff681000000000L & l) == 0L) + break; + if (kind > 13) + kind = 13; + jjCheckNAdd(28); + break; + case 29: + if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 26; + break; + case 30: + if (curChar == 37) + jjCheckNAddStates(9, 14); + break; + case 31: + if ((0xfffffffffffffbffL & l) != 0L) + jjCheckNAddTwoStates(31, 32); + break; + case 32: + if (curChar == 10 && kind > 2) + kind = 2; + break; + case 33: + if ((0xffffffffffffdfffL & l) != 0L) + jjCheckNAddTwoStates(33, 34); + break; + case 34: + if (curChar == 13 && kind > 3) + kind = 3; + break; + case 35: + if ((0xffffffffffffdfffL & l) != 0L) + jjCheckNAddTwoStates(35, 37); + break; + case 36: + if (curChar == 10 && kind > 4) + kind = 4; + break; + case 37: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 36; + break; + case 38: + if (curChar == 45) + jjCheckNAddStates(18, 23); + break; + case 39: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 5) + kind = 5; + jjCheckNAddTwoStates(39, 4); + break; + case 40: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(40, 41); + break; + case 41: + if (curChar != 46) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(31, 33); + break; + case 42: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(31, 33); + break; + case 44: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(45); + break; + case 45: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(45, 10); + break; + case 46: + if (curChar == 46) + jjCheckNAdd(47); + break; + case 47: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(34, 36); + break; + case 49: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(50); + break; + case 50: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(50, 10); + break; + case 51: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(51, 52); + break; + case 53: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(54); + break; + case 54: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(54, 10); + break; + case 55: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(37, 39); + break; + case 57: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(58); + break; + case 58: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(58, 10); + break; + case 59: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 5) + kind = 5; + jjCheckNAddStates(0, 8); + break; + case 60: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(60, 61); + break; + case 61: + if (curChar != 46) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(40, 42); + break; + case 62: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddStates(40, 42); + break; + case 64: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(65); + break; + case 65: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(65, 10); + break; + case 66: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(66, 67); + break; + case 68: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(69); + break; + case 69: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 8) + kind = 8; + jjCheckNAddTwoStates(69, 10); + break; + case 70: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(43, 45); + break; + case 72: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(73); + break; + case 73: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(73, 10); + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + case 21: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(21); + break; + case 2: + if ((0x100000001000000L & l) != 0L) + jjCheckNAdd(3); + break; + case 3: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 5) + kind = 5; + jjCheckNAddTwoStates(3, 4); + break; + case 4: + if ((0x100000001000L & l) != 0L && kind > 5) + kind = 5; + break; + case 7: + if ((0x2000000020L & l) != 0L) + jjAddStates(46, 47); + break; + case 10: + if ((0x5000000050L & l) != 0L && kind > 8) + kind = 8; + break; + case 12: + if ((0xffffffffefffffffL & l) != 0L) + jjCheckNAddStates(15, 17); + break; + case 13: + if (curChar == 92) + jjAddStates(48, 50); + break; + case 14: + if ((0x14404410000000L & l) != 0L) + jjCheckNAddStates(15, 17); + break; + case 24: + case 25: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(25); + break; + case 27: + case 28: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 13) + kind = 13; + jjCheckNAdd(28); + break; + case 31: + jjAddStates(51, 52); + break; + case 33: + jjAddStates(53, 54); + break; + case 35: + jjAddStates(55, 56); + break; + case 43: + if ((0x2000000020L & l) != 0L) + jjAddStates(57, 58); + break; + case 48: + if ((0x2000000020L & l) != 0L) + jjAddStates(59, 60); + break; + case 52: + if ((0x2000000020L & l) != 0L) + jjAddStates(61, 62); + break; + case 56: + if ((0x2000000020L & l) != 0L) + jjAddStates(63, 64); + break; + case 63: + if ((0x2000000020L & l) != 0L) + jjAddStates(65, 66); + break; + case 67: + if ((0x2000000020L & l) != 0L) + jjAddStates(67, 68); + break; + case 71: + if ((0x2000000020L & l) != 0L) + jjAddStates(69, 70); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + case 21: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(21); + break; + case 12: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(15, 17); + break; + case 24: + case 25: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(25); + break; + case 27: + case 28: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 13) + kind = 13; + jjCheckNAdd(28); + break; + case 31: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(51, 52); + break; + case 33: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(53, 54); + break; + case 35: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(55, 56); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 74 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_1(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_1(int pos, long active0) +{ + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_1(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_1(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_1() +{ + switch(curChar) + { + case 62: + return jjStopAtPos(0, 23); + default : + return jjMoveNfa_1(0, 0); + } +} +private final int jjMoveNfa_1(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xbfffffffffffffffL & l) != 0L) + kind = 22; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 22; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 22) + kind = 22; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +static final int[] jjnextStates = { + 39, 4, 60, 61, 66, 67, 70, 71, 10, 31, 32, 33, 34, 35, 37, 12, + 13, 15, 39, 1, 40, 46, 51, 55, 6, 7, 10, 12, 13, 17, 15, 42, + 43, 10, 47, 48, 10, 55, 56, 10, 62, 63, 10, 70, 71, 10, 8, 9, + 14, 16, 18, 31, 32, 33, 34, 35, 37, 44, 45, 49, 50, 53, 54, 57, + 58, 64, 65, 68, 69, 72, 73, +}; +private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default : + if ((jjbitVec0[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec4[i2] & l2) != 0L); + case 48: + return ((jjbitVec5[i2] & l2) != 0L); + case 49: + return ((jjbitVec6[i2] & l2) != 0L); + case 51: + return ((jjbitVec7[i2] & l2) != 0L); + case 61: + return ((jjbitVec8[i2] & l2) != 0L); + default : + if ((jjbitVec3[i1] & l1) != 0L) + return true; + return false; + } +} +public static final String[] jjstrLiteralImages = { +"", null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, "\173", "\175", "\133", "\135", "\74\74", "\76\76", null, null, +"\74", }; +public static final String[] lexStateNames = { + "DEFAULT", + "WITHINSTRING", +}; +public static final int[] jjnewLexState = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, +}; +static final long[] jjtoToken = { + 0x1bf3d21L, +}; +static final long[] jjtoSkip = { + 0x1eL, +}; +static final long[] jjtoMore = { + 0x400000L, +}; +private JavaCharStream input_stream; +private final int[] jjrounds = new int[74]; +private final int[] jjstateSet = new int[148]; +StringBuffer image; +int jjimageLen; +int lengthOfMatch; +protected char curChar; +public PAParserTokenManager(JavaCharStream stream) +{ + if (JavaCharStream.staticFlag) + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + input_stream = stream; +} +public PAParserTokenManager(JavaCharStream stream, int lexState) +{ + this(stream); + SwitchTo(lexState); +} +public void ReInit(JavaCharStream stream) +{ + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); +} +private final void ReInitRounds() +{ + int i; + jjround = 0x80000001; + for (i = 74; i-- > 0;) + jjrounds[i] = 0x80000000; +} +public void ReInit(JavaCharStream stream, int lexState) +{ + ReInit(stream); + SwitchTo(lexState); +} +public void SwitchTo(int lexState) +{ + if (lexState >= 2 || lexState < 0) + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + else + curLexState = lexState; +} + +private final Token jjFillToken() +{ + Token t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + String im = jjstrLiteralImages[jjmatchedKind]; + t.image = (im == null) ? input_stream.GetImage() : im; + t.beginLine = input_stream.getBeginLine(); + t.beginColumn = input_stream.getBeginColumn(); + t.endLine = input_stream.getEndLine(); + t.endColumn = input_stream.getEndColumn(); + return t; +} + +int curLexState = 0; +int defaultLexState = 0; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; + +public final Token getNextToken() +{ + int kind; + Token specialToken = null; + Token matchedToken; + int curPos = 0; + + EOFLoop : + for (;;) + { + try + { + curChar = input_stream.BeginToken(); + } + catch(java.io.IOException e) + { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + return matchedToken; + } + image = null; + jjimageLen = 0; + + for (;;) + { + switch(curLexState) + { + case 0: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + break; + } + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; + } + else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; + } + jjimageLen += jjmatchedPos + 1; + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + curPos = 0; + jjmatchedKind = 0x7fffffff; + try { + curChar = input_stream.readChar(); + continue; + } + catch (java.io.IOException e1) { } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { input_stream.readChar(); input_stream.backup(1); } + catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } + } +} + +final void TokenLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + case 23 : + if (image == null) + image = new StringBuffer(new String(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)))); + else + image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + matchedToken.image=image.toString().substring(0,image.toString().length()-1); + break; + default : + break; + } +} +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAPencil.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAPencil.java new file mode 100644 index 0000000..5612603 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAPencil.java @@ -0,0 +1,431 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +import java.util.*; +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfGraphics2D; + + +public class PAPencil { + + static protected class State implements Cloneable { + public Stroke stroke; + public Paint paint; + public AffineTransform at; + public Shape clipShape; + public Font font; + public Composite composite; + public GeneralPath path; + + public State(){ + this(null); + } + + public State(Graphics2D g){ + if(g == null){ + this.stroke = new BasicStroke(); + this.paint = Color.black; + this.at = new AffineTransform(); + this.font = new Font("SansSerif", Font.PLAIN, 12); + this.composite = AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f); + this.clipShape = null; + } else { + this.recordState(g); + } + this.path = new GeneralPath(); + } + + public void recordState(Graphics2D g){ + this.stroke = g.getStroke(); + this.paint = g.getPaint(); + this.at = g.getTransform(); + this.font = g.getFont(); + this.composite = g.getComposite(); + this.clipShape = g.getClip(); + } + + public void stampState(Graphics2D g, Dimension size){ + g.setTransform(new AffineTransform()); + g.setClip(new Rectangle(0, 0, size.width, size.height)); + g.setStroke(this.stroke); + g.setPaint(this.paint); + g.setTransform(this.at); + g.setFont(this.font); + g.setComposite(this.composite); + if(this.clipShape != null){ + g.clip(this.clipShape); + } + } + + public Object clone(){ + try { + State n = (State)super.clone(); + + n.at = (AffineTransform) this.at.clone(); + n.path = new GeneralPath(); + n.path.append(this.path, false); + return n; + } catch(CloneNotSupportedException e){ + throw new InternalError(); + } + } + + } + + // + // Class Variables + // + + // + // Instance Variables + // + + /** + * The canvas size. + */ + protected Dimension size; + + /** + * The current graphics state. + */ + protected State state; + + /** + * The stack of graphic states. + */ + protected Stack gStack; + + /** + * The font hashtable with postscript names as keys + */ + protected HashMap fonts; + + /** + * The current graphics device + */ + public Graphics2D graphics; + + // + // Constructors + // + + public PAPencil(Component component){ + this.graphics = (Graphics2D) component.getGraphics(); + this.size = component.getSize(); + this.initgraphics(); + } + + public PAPencil(Graphics graphics, Dimension size){ + this.graphics = (Graphics2D) graphics; + this.size = size; + this.initgraphics(); + } + + // + // Graphics state management + // + + public void gsave(){ + this.state.recordState(this.graphics); + State next = (State) this.state.clone(); + + this.gStack.push(this.state); + this.state = next; + } + + public void grestore(){ + if(this.gStack.empty()){ + this.initgraphics(); + } else { + this.state = (State) this.gStack.pop(); + this.state.stampState(this.graphics, this.size); + } + } + + public void grestoreall(){ + this.initgraphics(); + } + + public void initgraphics(){ + AffineTransform at = new AffineTransform(); + // turn anti-aliasing and high-quality rendering on + this.graphics.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); + this.graphics.setRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); + + // initialize to a postscript coordinate system + at.translate(0, this.size.getHeight()); + at.scale(1, -1); + this.graphics.setTransform(at); + // this.graphics.translate(0, this.size.getHeight()); + // this.graphics.scale(1, -1); + + // state, stack and page + this.state = new State(this.graphics); + this.gStack = new Stack(); + this.erasepage(); + } + + // + // Path definition + // + + public void newpath(){ + this.state.path.reset(); + } + + public void moveto(double x, double y){ + this.state.path.moveTo((float) x, (float) y); + } + + public void moveto(Point2D p) { + this.moveto(p.getX(), p.getY()); + } + + public void rmoveto(double dx, double dy) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + this.state.path.moveTo((float) (currentPoint.getX() + dx) , (float) (currentPoint.getY() + dy)); + } + + public void lineto(double x, double y) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + this.state.path.lineTo((float) x, (float) y); + } + + public void lineto(Point2D p) throws PainterException { + this.lineto(p.getX(), p.getY()); + } + + public void rlineto(double dx, double dy) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + this.state.path.lineTo((float) (currentPoint.getX() + dx) , (float) (currentPoint.getY() + dy)); + } + + /** + * + * @param cx double Centerpoint x + * @param cy double Centerpoint y + * @param r double Radius r + * @param ang1 double first angle + * @param ang2 double second angle + */ + public void arc(double cx, double cy, double r, double ang1, double ang2){ + Arc2D.Float arc = new Arc2D.Float((float) ( cx - r ), (float) ( cy + - r ), (float) r * 2f, (float) r * 2f,- (float) ang1,- (float) ( ang2 - + ang1 ), Arc2D.OPEN ); + Point2D currentPoint = this.state.path.getCurrentPoint(); + if(currentPoint == null){ + this.state.path.append(arc, false); + } else { + this.state.path.append(arc, true); + } + } + + public void arcn(double cx, double cy, double r, double ang1, double ang2) { + Arc2D.Float arc = new Arc2D.Float((float) ( cx - r ), (float) + ( cy - r ), (float) r * 2f, (float) r * 2f,- (float) ang1, -(float) ( + ang2 - ang1 ), Arc2D.OPEN ); + Point2D currentPoint = this.state.path.getCurrentPoint(); + if(currentPoint == null){ + this.state.path.append(arc, false); + } else { + this.state.path.append(arc, true); + } + } + + public void curveto(double x1, double y1, double x2, double y2, + double x3, double y3) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + this.state.path.curveTo((float) x1, (float) y1, (float) x2, (float) y2, + (float) x3, (float) y3); + } + + public void rcurveto(double dx1, double dy1, double dx2, double dy2, + double dx3, double dy3) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + double x0 = currentPoint.getX(); + double y0 = currentPoint.getY(); + this.curveto(x0 + dx1, y0 + dy1, x0 + dx2, y0 + dy2, x0 + dx3,y0 + dy3); + } + + public void closepath(){ + this.state.path.closePath(); + } + + // PENDING(uweh): just a placeholder for now + public void clippath(){ + this.rectpath(0.0d, 0.0d, size.width, size.height); + } + public void clip(){ + PdfGraphics2D pdfg2d = (PdfGraphics2D) this.graphics; + pdfg2d.clip(this.state.path); + this.newpath(); +// Area currentclip=new Area(this.state.clipShape); +// Area addclip=new Area(this.state.path.createTransformedShape(AffineTransform.getTranslateInstance(0,0))); +// currentclip.intersect(addclip); +// this.graphics.clip(currentclip ); + } + public void erasepage(){ + this.graphics.clearRect(0, 0, size.width, size.height); + } + + public void charpath(String aString, boolean adjustForStroking)throws PainterException{ + FontRenderContext frc=this.graphics.getFontRenderContext(); + Font fn=this.state.font; +// System.out.println("Fonthoehe:"+fn.getSize2D()); + GlyphVector glyphVector = fn.createGlyphVector(frc, aString); + Point2D currentPoint = this.state.path.getCurrentPoint(); + Shape glyphShape = glyphVector.getOutline(); + AffineTransform currentTransform = AffineTransform.getScaleInstance(1,-1); + glyphShape=currentTransform.createTransformedShape(glyphShape); + AffineTransform currentTransform2 = AffineTransform.getTranslateInstance((float)currentPoint.getX(),(float)currentPoint.getY()); + glyphShape=currentTransform2.createTransformedShape(glyphShape); + this.state.path.append(glyphShape, false); + } + + public void showpage(){ + PdfGraphics2D pdfg2d = (PdfGraphics2D) this.graphics; + PdfContentByte cb = pdfg2d.getContent(); + try { + cb.getPdfWriter().newPage(); + } + catch (com.lowagie.text.DocumentException ex) { + ex.printStackTrace(); + } + } + + public void show(String string) throws PainterException { + Point2D currentPoint = this.state.path.getCurrentPoint(); + AffineTransform currentTransform = this.graphics.getTransform(); + Point2D tranformedPoint = currentTransform.transform(currentPoint, null); + + if(currentPoint == null){ + throw new PainterException("no current point"); + } + this.graphics.setTransform(new AffineTransform()); + this.graphics.drawString(string, (float) tranformedPoint.getX(), (float) tranformedPoint.getY()); + this.graphics.setTransform(currentTransform); + } + + public void fill(){ + this.graphics.fill(this.state.path); + this.newpath(); + } + + public void eofill(){ + this.state.path.setWindingRule(GeneralPath.WIND_EVEN_ODD); + this.graphics.fill(this.state.path); + this.state.path.setWindingRule(GeneralPath.WIND_NON_ZERO); + this.newpath(); + } + + public void stroke()throws PainterException{ + this.graphics.draw(this.state.path); + this.newpath(); + + } + + public void rectfill(double x, double y, double width, double height){ + this.gsave(); + this.rectpath(x, y, width, height); + this.fill(); + this.grestore(); + } + + public void rectfill(Rectangle2D rect){ + this.rectfill(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + public void rectstroke(double x, double y, double width, double height)throws PainterException{ + this.gsave(); + this.rectpath(x, y, width, height); + this.stroke(); + this.grestore(); + } + + public void rectstroke(Rectangle2D rect)throws PainterException{ + this.rectstroke(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + public void rectpath(double x, double y, double width, double height){ + this.newpath(); + this.moveto(x, y); + try { + this.rlineto(width, 0); + this.rlineto(0, height); + this.rlineto(-width, 0); + } catch(PainterException e){ + } + this.closepath(); + } + + // convenience + + // this guy tries to find an appropiate font + // if he fails returns whatever font he wants + public Font findFont(String fontname){ + Font result; + StringBuffer buffer = new StringBuffer(fontname); + int i, n; + n = buffer.length(); + + for(i = 0; i < n; i++){ + if(buffer.charAt(i) == '-'){ + buffer.setCharAt(i,' '); + } + } + + fontname = buffer.toString(); + + if(this.fonts == null){ + // construct the fonts dictionary + GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Font[] fontArray = genv.getAllFonts(); + this.fonts = new HashMap(); + for(i = 0; i < fontArray.length; i++){ + String postscriptName = fontArray[i].getPSName(); + this.fonts.put(postscriptName, fontArray[i]); + } + } + result = (Font) this.fonts.get(fontname); + if(result == null){ +// result = new Font("SansSerif", Font.PLAIN, 12); + result = new Font("Arial", Font.PLAIN, 12); + } + return result; + } +} + diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAToken.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAToken.java new file mode 100644 index 0000000..52f347e --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAToken.java @@ -0,0 +1,66 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +public class PAToken { + + static public final int IDENTIFIER = 0; + static public final int KEY = 1; + static public final int PROCEDURE = 2; + static public final int MARK = 3; + static public final int START_PROCEDURE = 4; + static public final int END_PROCEDURE = 5; + static public final int IMMEDIATE = 6; + static public final int START_ARRAY = 7; + static public final int END_ARRAY = 8; + static public final int START_DICT = 9; + static public final int END_DICT = 10; + + public Object value; + public int type; + + public PAToken(Object value, int type) { + super(); + this.value = value; + this.type = type; + } + + public String toString() { + switch (this.type) { + case IDENTIFIER: + return "IDENTIFIER " + this.value.toString(); + case KEY: + return "KEY " + this.value.toString(); + case PROCEDURE: + return "PROCEDURE " + this.value.toString(); + case MARK: + return "MARK"; + case START_PROCEDURE: + return "START_PROCEDURE"; + case END_PROCEDURE: + return "END_PROCEDURE"; + case IMMEDIATE: + return "IMMEDIATE " + this.value.toString(); + case START_ARRAY: + return "START_ARRAY"; + case END_ARRAY: + return "END_ARRAY"; + case START_DICT: + return "START_DICT"; + case END_DICT: + return "END_DICT"; + } + return this.value.toString(); + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PainterException.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PainterException.java new file mode 100644 index 0000000..236eece --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PainterException.java @@ -0,0 +1,20 @@ +/* + * Copyright 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +package com.lowagie.text.pdf.codec.postscript; + +public class PainterException extends Exception { + + public PainterException(String msg){ + super(msg); + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/ParseException.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/ParseException.java new file mode 100644 index 0000000..4bf572e --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/ParseException.java @@ -0,0 +1,192 @@ +/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 2.1 */ +package com.lowagie.text.pdf.codec.postscript; + +/** + * This exception is thrown when parse errors are encountered. + * You can explicitly create objects of this exception type by + * calling the method generateParseException in the generated + * parser. + * + * You can modify this class to customize your error reporting + * mechanisms so long as you retain the public fields. + */ +public class ParseException extends Exception { + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. The boolean + * flag "specialConstructor" is also set to true to indicate that + * this constructor was used to create this object. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) + { + super(""); + specialConstructor = true; + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } + + /** + * The following constructors are for use by you for whatever + * purpose you can think of. Constructing the exception in this + * manner makes the exception behave in the normal way - i.e., as + * documented in the class "Throwable". The fields "errorToken", + * "expectedTokenSequences", and "tokenImage" do not contain + * relevant information. The JavaCC generated code does not use + * these constructors. + */ + + public ParseException() { + super(); + specialConstructor = false; + } + + public ParseException(String message) { + super(message); + specialConstructor = false; + } + + /** + * This variable determines which constructor was used to create + * this object and thereby affects the semantics of the + * "getMessage" method (see below). + */ + protected boolean specialConstructor; + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * This method has the standard behavior when this object has been + * created using the standard constructors. Otherwise, it uses + * "currentToken" and "expectedTokenSequences" to generate a parse + * error message and returns it. If this object has been created + * due to a parse error, and you do not catch it (it gets thrown + * from the parser), then this method is called during the printing + * of the final stack trace, and hence the correct error message + * gets displayed. + */ + public String getMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + String expected = ""; + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected += tokenImage[expectedTokenSequences[i][j]] + " "; + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected += "..."; + } + expected += eol + " "; + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; + } + retval += expected; + return retval; + } + + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/Token.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/Token.java new file mode 100644 index 0000000..530e4de --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/Token.java @@ -0,0 +1,81 @@ +/* Generated By:JavaCC: Do not edit this line. Token.java Version 2.1 */ +package com.lowagie.text.pdf.codec.postscript; + +/** + * Describes the input token stream. + */ + +public class Token { + + /** + * An integer that describes the kind of this token. This numbering + * system is determined by JavaCCParser, and a table of these numbers is + * stored in the file ...Constants.java. + */ + public int kind; + + /** + * beginLine and beginColumn describe the position of the first character + * of this token; endLine and endColumn describe the position of the + * last character of this token. + */ + public int beginLine, beginColumn, endLine, endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input + * stream. If this is the last token from the input stream, or if the + * token manager has not read tokens beyond this one, this field is + * set to null. This is true only if this token is also a regular + * token. Otherwise, see below for a description of the contents of + * this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this + * token, but after the immediately preceding regular (non-special) token. + * If there are no such special tokens, this field is set to null. + * When there are more than one such special token, this field refers + * to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on + * until the first special token (whose specialToken field is null). + * The next fields of special tokens refer to other special tokens that + * immediately follow it (without an intervening regular token). If there + * is no such token, this field is null. + */ + public Token specialToken; + + /** + * Returns the image. + */ + public final String toString() + { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you + * can create and return subclass objects based on the value of ofKind. + * Simply add the cases to the switch for all those special cases. + * For example, if you have a subclass of Token called IDToken that + * you want to create if ofKind is ID, simlpy add something like : + * + * case MyParserConstants.ID : return new IDToken(); + * + * to the following switch statement. Then you can cast matchedToken + * variable to the appropriate type and use it in your lexical actions. + */ + public static final Token newToken(int ofKind) + { + switch(ofKind) + { + default : return new Token(); + } + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/TokenMgrError.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/TokenMgrError.java new file mode 100644 index 0000000..6b54bfa --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/TokenMgrError.java @@ -0,0 +1,133 @@ +/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 2.1 */ +package com.lowagie.text.pdf.codec.postscript; + +public class TokenMgrError extends Error +{ + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occured. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt wass made to create a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the + * token manager to indicate a lexical error. + * Parameters : + * EOFSeen : indicates if EOF caused the lexicl error + * curLexState : lexical state in which this error occured + * errorLine : line number when the error occured + * errorColumn : column number when the error occured + * errorAfter : prefix that was seen before this error occured + * curchar : the offending character + * Note: You can customize the lexical error message by modifying this method. + */ + private static final String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return("Lexical error at line " + + errorLine + ", column " + + errorColumn + ". Encountered: " + + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") + + "after : \"" + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. + * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not + * of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/InputMeta.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/InputMeta.java new file mode 100644 index 0000000..084761f --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/InputMeta.java @@ -0,0 +1,112 @@ +/* + * $Id: InputMeta.java,v 1.3 2005/10/06 10:32:47 psoares33 Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; + +import java.io.*; +import java.awt.Color; +import com.lowagie.text.Image; + +public class InputMeta { + + InputStream in; + int length; + + public InputMeta(InputStream in) { + this.in = in; + } + + public int readWord() throws IOException{ + length += 2; + int k1 = in.read(); + if (k1 < 0) + return 0; + return (k1 + (in.read() << 8)) & 0xffff; + } + + public int readShort() throws IOException{ + int k = readWord(); + if (k > 0x7fff) + k -= 0x10000; + return k; + } + + public int readInt() throws IOException{ + length += 4; + int k1 = in.read(); + if (k1 < 0) + return 0; + int k2 = in.read() << 8; + int k3 = in.read() << 16; + return k1 + k2 + k3 + (in.read() << 24); + } + + public int readByte() throws IOException{ + ++length; + return in.read() & 0xff; + } + + public void skip(int len) throws IOException{ + length += len; + Image.skip(in, len); + } + + public int getLength() { + return length; + } + + public Color readColor() throws IOException{ + int red = readByte(); + int green = readByte(); + int blue = readByte(); + readByte(); + return new Color(red, green, blue); + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaBrush.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaBrush.java new file mode 100644 index 0000000..430e177 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaBrush.java @@ -0,0 +1,94 @@ +/* + * $Id: MetaBrush.java,v 1.2 2005/05/04 14:33:19 blowagie Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; +import java.io.IOException; +import java.awt.Color; + +public class MetaBrush extends MetaObject { + + public static final int BS_SOLID = 0; + public static final int BS_NULL = 1; + public static final int BS_HATCHED = 2; + public static final int BS_PATTERN = 3; + public static final int BS_DIBPATTERN = 5; + public static final int HS_HORIZONTAL = 0; + public static final int HS_VERTICAL = 1; + public static final int HS_FDIAGONAL = 2; + public static final int HS_BDIAGONAL = 3; + public static final int HS_CROSS = 4; + public static final int HS_DIAGCROSS = 5; + + int style = BS_SOLID; + int hatch; + Color color = Color.white; + + public MetaBrush() { + type = META_BRUSH; + } + + public void init(InputMeta in) throws IOException { + style = in.readWord(); + color = in.readColor(); + hatch = in.readWord(); + } + + public int getStyle() { + return style; + } + + public int getHatch() { + return hatch; + } + + public Color getColor() { + return color; + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaDo.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaDo.java new file mode 100644 index 0000000..ca77294 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaDo.java @@ -0,0 +1,760 @@ +/* + * $Id: MetaDo.java,v 1.3 2005/12/01 16:57:15 psoares33 Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; +import java.io.*; +import com.lowagie.text.pdf.*; +import com.lowagie.text.pdf.codec.BmpImage; +import com.lowagie.text.*; +import java.awt.Point; +import java.awt.Color; +import java.util.ArrayList; + +public class MetaDo { + + public static final int META_SETBKCOLOR = 0x0201; + public static final int META_SETBKMODE = 0x0102; + public static final int META_SETMAPMODE = 0x0103; + public static final int META_SETROP2 = 0x0104; + public static final int META_SETRELABS = 0x0105; + public static final int META_SETPOLYFILLMODE = 0x0106; + public static final int META_SETSTRETCHBLTMODE = 0x0107; + public static final int META_SETTEXTCHAREXTRA = 0x0108; + public static final int META_SETTEXTCOLOR = 0x0209; + public static final int META_SETTEXTJUSTIFICATION = 0x020A; + public static final int META_SETWINDOWORG = 0x020B; + public static final int META_SETWINDOWEXT = 0x020C; + public static final int META_SETVIEWPORTORG = 0x020D; + public static final int META_SETVIEWPORTEXT = 0x020E; + public static final int META_OFFSETWINDOWORG = 0x020F; + public static final int META_SCALEWINDOWEXT = 0x0410; + public static final int META_OFFSETVIEWPORTORG = 0x0211; + public static final int META_SCALEVIEWPORTEXT = 0x0412; + public static final int META_LINETO = 0x0213; + public static final int META_MOVETO = 0x0214; + public static final int META_EXCLUDECLIPRECT = 0x0415; + public static final int META_INTERSECTCLIPRECT = 0x0416; + public static final int META_ARC = 0x0817; + public static final int META_ELLIPSE = 0x0418; + public static final int META_FLOODFILL = 0x0419; + public static final int META_PIE = 0x081A; + public static final int META_RECTANGLE = 0x041B; + public static final int META_ROUNDRECT = 0x061C; + public static final int META_PATBLT = 0x061D; + public static final int META_SAVEDC = 0x001E; + public static final int META_SETPIXEL = 0x041F; + public static final int META_OFFSETCLIPRGN = 0x0220; + public static final int META_TEXTOUT = 0x0521; + public static final int META_BITBLT = 0x0922; + public static final int META_STRETCHBLT = 0x0B23; + public static final int META_POLYGON = 0x0324; + public static final int META_POLYLINE = 0x0325; + public static final int META_ESCAPE = 0x0626; + public static final int META_RESTOREDC = 0x0127; + public static final int META_FILLREGION = 0x0228; + public static final int META_FRAMEREGION = 0x0429; + public static final int META_INVERTREGION = 0x012A; + public static final int META_PAINTREGION = 0x012B; + public static final int META_SELECTCLIPREGION = 0x012C; + public static final int META_SELECTOBJECT = 0x012D; + public static final int META_SETTEXTALIGN = 0x012E; + public static final int META_CHORD = 0x0830; + public static final int META_SETMAPPERFLAGS = 0x0231; + public static final int META_EXTTEXTOUT = 0x0a32; + public static final int META_SETDIBTODEV = 0x0d33; + public static final int META_SELECTPALETTE = 0x0234; + public static final int META_REALIZEPALETTE = 0x0035; + public static final int META_ANIMATEPALETTE = 0x0436; + public static final int META_SETPALENTRIES = 0x0037; + public static final int META_POLYPOLYGON = 0x0538; + public static final int META_RESIZEPALETTE = 0x0139; + public static final int META_DIBBITBLT = 0x0940; + public static final int META_DIBSTRETCHBLT = 0x0b41; + public static final int META_DIBCREATEPATTERNBRUSH = 0x0142; + public static final int META_STRETCHDIB = 0x0f43; + public static final int META_EXTFLOODFILL = 0x0548; + public static final int META_DELETEOBJECT = 0x01f0; + public static final int META_CREATEPALETTE = 0x00f7; + public static final int META_CREATEPATTERNBRUSH = 0x01F9; + public static final int META_CREATEPENINDIRECT = 0x02FA; + public static final int META_CREATEFONTINDIRECT = 0x02FB; + public static final int META_CREATEBRUSHINDIRECT = 0x02FC; + public static final int META_CREATEREGION = 0x06FF; + + public PdfContentByte cb; + public InputMeta in; + int left; + int top; + int right; + int bottom; + int inch; + MetaState state = new MetaState(); + + public MetaDo(InputStream in, PdfContentByte cb) { + this.cb = cb; + this.in = new InputMeta(in); + } + + public void readAll() throws IOException, DocumentException{ + if (in.readInt() != 0x9AC6CDD7) { + throw new DocumentException("Not a placeable windows metafile"); + } + in.readWord(); + left = in.readShort(); + top = in.readShort(); + right = in.readShort(); + bottom = in.readShort(); + inch = in.readWord(); + state.setScalingX((float)(right - left) / (float)inch * 72f); + state.setScalingY((float)(bottom - top) / (float)inch * 72f); + state.setOffsetWx(left); + state.setOffsetWy(top); + state.setExtentWx(right - left); + state.setExtentWy(bottom - top); + in.readInt(); + in.readWord(); + in.skip(18); + + int tsize; + int function; + cb.setLineCap(1); + cb.setLineJoin(1); + for (;;) { + int lenMarker = in.getLength(); + tsize = in.readInt(); + if (tsize < 3) + break; + function = in.readWord(); + switch (function) { + case 0: + break; + case META_CREATEPALETTE: + case META_CREATEREGION: + case META_DIBCREATEPATTERNBRUSH: + state.addMetaObject(new MetaObject()); + break; + case META_CREATEPENINDIRECT: + { + MetaPen pen = new MetaPen(); + pen.init(in); + state.addMetaObject(pen); + break; + } + case META_CREATEBRUSHINDIRECT: + { + MetaBrush brush = new MetaBrush(); + brush.init(in); + state.addMetaObject(brush); + break; + } + case META_CREATEFONTINDIRECT: + { + MetaFont font = new MetaFont(); + font.init(in); + state.addMetaObject(font); + break; + } + case META_SELECTOBJECT: + { + int idx = in.readWord(); + state.selectMetaObject(idx, cb); + break; + } + case META_DELETEOBJECT: + { + int idx = in.readWord(); + state.deleteMetaObject(idx); + break; + } + case META_SAVEDC: + state.saveState(cb); + break; + case META_RESTOREDC: + { + int idx = in.readShort(); + state.restoreState(idx, cb); + break; + } + case META_SETWINDOWORG: + state.setOffsetWy(in.readShort()); + state.setOffsetWx(in.readShort()); + break; + case META_SETWINDOWEXT: + state.setExtentWy(in.readShort()); + state.setExtentWx(in.readShort()); + break; + case META_MOVETO: + { + int y = in.readShort(); + Point p = new Point(in.readShort(), y); + state.setCurrentPoint(p); + break; + } + case META_LINETO: + { + int y = in.readShort(); + int x = in.readShort(); + Point p = state.getCurrentPoint(); + cb.moveTo(state.transformX(p.x), state.transformY(p.y)); + cb.lineTo(state.transformX(x), state.transformY(y)); + cb.stroke(); + state.setCurrentPoint(new Point(x, y)); + break; + } + case META_POLYLINE: + { + state.setLineJoinPolygon(cb); + int len = in.readWord(); + int x = in.readShort(); + int y = in.readShort(); + cb.moveTo(state.transformX(x), state.transformY(y)); + for (int k = 1; k < len; ++k) { + x = in.readShort(); + y = in.readShort(); + cb.lineTo(state.transformX(x), state.transformY(y)); + } + cb.stroke(); + break; + } + case META_POLYGON: + { + if (isNullStrokeFill(false)) + break; + int len = in.readWord(); + int sx = in.readShort(); + int sy = in.readShort(); + cb.moveTo(state.transformX(sx), state.transformY(sy)); + for (int k = 1; k < len; ++k) { + int x = in.readShort(); + int y = in.readShort(); + cb.lineTo(state.transformX(x), state.transformY(y)); + } + cb.lineTo(state.transformX(sx), state.transformY(sy)); + strokeAndFill(); + break; + } + case META_POLYPOLYGON: + { + if (isNullStrokeFill(false)) + break; + int numPoly = in.readWord(); + int lens[] = new int[numPoly]; + for (int k = 0; k < lens.length; ++k) + lens[k] = in.readWord(); + for (int j = 0; j < lens.length; ++j) { + int len = lens[j]; + int sx = in.readShort(); + int sy = in.readShort(); + cb.moveTo(state.transformX(sx), state.transformY(sy)); + for (int k = 1; k < len; ++k) { + int x = in.readShort(); + int y = in.readShort(); + cb.lineTo(state.transformX(x), state.transformY(y)); + } + cb.lineTo(state.transformX(sx), state.transformY(sy)); + } + strokeAndFill(); + break; + } + case META_ELLIPSE: + { + if (isNullStrokeFill(state.getLineNeutral())) + break; + int b = in.readShort(); + int r = in.readShort(); + int t = in.readShort(); + int l = in.readShort(); + cb.arc(state.transformX(l), state.transformY(b), state.transformX(r), state.transformY(t), 0, 360); + strokeAndFill(); + break; + } + case META_ARC: + { + if (isNullStrokeFill(state.getLineNeutral())) + break; + float yend = state.transformY(in.readShort()); + float xend = state.transformX(in.readShort()); + float ystart = state.transformY(in.readShort()); + float xstart = state.transformX(in.readShort()); + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + float cx = (r + l) / 2; + float cy = (t + b) / 2; + float arc1 = getArc(cx, cy, xstart, ystart); + float arc2 = getArc(cx, cy, xend, yend); + arc2 -= arc1; + if (arc2 <= 0) + arc2 += 360; + cb.arc(l, b, r, t, arc1, arc2); + cb.stroke(); + break; + } + case META_PIE: + { + if (isNullStrokeFill(state.getLineNeutral())) + break; + float yend = state.transformY(in.readShort()); + float xend = state.transformX(in.readShort()); + float ystart = state.transformY(in.readShort()); + float xstart = state.transformX(in.readShort()); + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + float cx = (r + l) / 2; + float cy = (t + b) / 2; + float arc1 = getArc(cx, cy, xstart, ystart); + float arc2 = getArc(cx, cy, xend, yend); + arc2 -= arc1; + if (arc2 <= 0) + arc2 += 360; + ArrayList ar = PdfContentByte.bezierArc(l, b, r, t, arc1, arc2); + if (ar.size() == 0) + break; + float pt[] = (float [])ar.get(0); + cb.moveTo(cx, cy); + cb.lineTo(pt[0], pt[1]); + for (int k = 0; k < ar.size(); ++k) { + pt = (float [])ar.get(k); + cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]); + } + cb.lineTo(cx, cy); + strokeAndFill(); + break; + } + case META_CHORD: + { + if (isNullStrokeFill(state.getLineNeutral())) + break; + float yend = state.transformY(in.readShort()); + float xend = state.transformX(in.readShort()); + float ystart = state.transformY(in.readShort()); + float xstart = state.transformX(in.readShort()); + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + float cx = (r + l) / 2; + float cy = (t + b) / 2; + float arc1 = getArc(cx, cy, xstart, ystart); + float arc2 = getArc(cx, cy, xend, yend); + arc2 -= arc1; + if (arc2 <= 0) + arc2 += 360; + ArrayList ar = PdfContentByte.bezierArc(l, b, r, t, arc1, arc2); + if (ar.size() == 0) + break; + float pt[] = (float [])ar.get(0); + cx = pt[0]; + cy = pt[1]; + cb.moveTo(cx, cy); + for (int k = 0; k < ar.size(); ++k) { + pt = (float [])ar.get(k); + cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]); + } + cb.lineTo(cx, cy); + strokeAndFill(); + break; + } + case META_RECTANGLE: + { + if (isNullStrokeFill(true)) + break; + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + cb.rectangle(l, b, r - l, t - b); + strokeAndFill(); + break; + } + case META_ROUNDRECT: + { + if (isNullStrokeFill(true)) + break; + float h = state.transformY(0) - state.transformY(in.readShort()); + float w = state.transformX(in.readShort()) - state.transformX(0); + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + cb.roundRectangle(l, b, r - l, t - b, (h + w) / 4); + strokeAndFill(); + break; + } + case META_INTERSECTCLIPRECT: + { + float b = state.transformY(in.readShort()); + float r = state.transformX(in.readShort()); + float t = state.transformY(in.readShort()); + float l = state.transformX(in.readShort()); + cb.rectangle(l, b, r - l, t - b); + cb.eoClip(); + cb.newPath(); + break; + } + case META_EXTTEXTOUT: + { + int y = in.readShort(); + int x = in.readShort(); + int count = in.readWord(); + int flag = in.readWord(); + int x1 = 0; + int y1 = 0; + int x2 = 0; + int y2 = 0; + if ((flag & (MetaFont.ETO_CLIPPED | MetaFont.ETO_OPAQUE)) != 0) { + x1 = in.readShort(); + y1 = in.readShort(); + x2 = in.readShort(); + y2 = in.readShort(); + } + byte text[] = new byte[count]; + int k; + for (k = 0; k < count; ++k) { + byte c = (byte)in.readByte(); + if (c == 0) + break; + text[k] = c; + } + String s; + try { + s = new String(text, 0, k, "Cp1252"); + } + catch (UnsupportedEncodingException e) { + s = new String(text, 0, k); + } + outputText(x, y, flag, x1, y1, x2, y2, s); + break; + } + case META_TEXTOUT: + { + int count = in.readWord(); + byte text[] = new byte[count]; + int k; + for (k = 0; k < count; ++k) { + byte c = (byte)in.readByte(); + if (c == 0) + break; + text[k] = c; + } + String s; + try { + s = new String(text, 0, k, "Cp1252"); + } + catch (UnsupportedEncodingException e) { + s = new String(text, 0, k); + } + count = (count + 1) & 0xfffe; + in.skip(count - k); + int y = in.readShort(); + int x = in.readShort(); + outputText(x, y, 0, 0, 0, 0, 0, s); + break; + } + case META_SETBKCOLOR: + state.setCurrentBackgroundColor(in.readColor()); + break; + case META_SETTEXTCOLOR: + state.setCurrentTextColor(in.readColor()); + break; + case META_SETTEXTALIGN: + state.setTextAlign(in.readWord()); + break; + case META_SETBKMODE: + state.setBackgroundMode(in.readWord()); + break; + case META_SETPOLYFILLMODE: + state.setPolyFillMode(in.readWord()); + break; + case META_SETPIXEL: + { + Color color = in.readColor(); + int y = in.readShort(); + int x = in.readShort(); + cb.saveState(); + cb.setColorFill(color); + cb.rectangle(state.transformX(x), state.transformY(y), .2f, .2f); + cb.fill(); + cb.restoreState(); + break; + } + case META_DIBSTRETCHBLT: + case META_STRETCHDIB: { + int rop = in.readInt(); + if (function == META_STRETCHDIB) { + /*int usage = */ in.readWord(); + } + int srcHeight = in.readShort(); + int srcWidth = in.readShort(); + int ySrc = in.readShort(); + int xSrc = in.readShort(); + float destHeight = state.transformY(in.readShort()) - state.transformY(0); + float destWidth = state.transformX(in.readShort()) - state.transformX(0); + float yDest = state.transformY(in.readShort()); + float xDest = state.transformX(in.readShort()); + byte b[] = new byte[(tsize * 2) - (in.getLength() - lenMarker)]; + for (int k = 0; k < b.length; ++k) + b[k] = (byte)in.readByte(); + try { + ByteArrayInputStream inb = new ByteArrayInputStream(b); + Image bmp = BmpImage.getImage(inb, true, b.length); + cb.saveState(); + cb.rectangle(xDest, yDest, destWidth, destHeight); + cb.clip(); + cb.newPath(); + bmp.scaleAbsolute(destWidth * bmp.width() / srcWidth, -destHeight * bmp.height() / srcHeight); + bmp.setAbsolutePosition(xDest - destWidth * xSrc / srcWidth, yDest + destHeight * ySrc / srcHeight - bmp.scaledHeight()); + cb.addImage(bmp); + cb.restoreState(); + } + catch (Exception e) { + // empty on purpose + } + break; + } + } + in.skip((tsize * 2) - (in.getLength() - lenMarker)); + } + state.cleanup(cb); + } + + public void outputText(int x, int y, int flag, int x1, int y1, int x2, int y2, String text) throws IOException { + MetaFont font = state.getCurrentFont(); + float refX = state.transformX(x); + float refY = state.transformY(y); + float angle = state.transformAngle(font.getAngle()); + float sin = (float)Math.sin(angle); + float cos = (float)Math.cos(angle); + float fontSize = font.getFontSize(state); + BaseFont bf = font.getFont(); + int align = state.getTextAlign(); + float textWidth = bf.getWidthPoint(text, fontSize); + float tx = 0; + float ty = 0; + float descender = bf.getFontDescriptor(BaseFont.DESCENT, fontSize); + float ury = bf.getFontDescriptor(BaseFont.BBOXURY, fontSize); + cb.saveState(); + cb.concatCTM(cos, sin, -sin, cos, refX, refY); + if ((align & MetaState.TA_CENTER) == MetaState.TA_CENTER) + tx = -textWidth / 2; + else if ((align & MetaState.TA_RIGHT) == MetaState.TA_RIGHT) + tx = -textWidth; + if ((align & MetaState.TA_BASELINE) == MetaState.TA_BASELINE) + ty = 0; + else if ((align & MetaState.TA_BOTTOM) == MetaState.TA_BOTTOM) + ty = -descender; + else + ty = -ury; + Color textColor; + if (state.getBackgroundMode() == MetaState.OPAQUE) { + textColor = state.getCurrentBackgroundColor(); + cb.setColorFill(textColor); + cb.rectangle(tx, ty + descender, textWidth, ury - descender); + cb.fill(); + } + textColor = state.getCurrentTextColor(); + cb.setColorFill(textColor); + cb.beginText(); + cb.setFontAndSize(bf, fontSize); + cb.setTextMatrix(tx, ty); + cb.showText(text); + cb.endText(); + if (font.isUnderline()) { + cb.rectangle(tx, ty - fontSize / 4, textWidth, fontSize / 15); + cb.fill(); + } + if (font.isStrikeout()) { + cb.rectangle(tx, ty + fontSize / 3, textWidth, fontSize / 15); + cb.fill(); + } + cb.restoreState(); + } + + public boolean isNullStrokeFill(boolean isRectangle) { + MetaPen pen = state.getCurrentPen(); + MetaBrush brush = state.getCurrentBrush(); + boolean noPen = (pen.getStyle() == MetaPen.PS_NULL); + int style = brush.getStyle(); + boolean isBrush = (style == MetaBrush.BS_SOLID || (style == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE)); + boolean result = noPen && !isBrush; + if (!noPen) { + if (isRectangle) + state.setLineJoinRectangle(cb); + else + state.setLineJoinPolygon(cb); + } + return result; + } + + public void strokeAndFill(){ + MetaPen pen = state.getCurrentPen(); + MetaBrush brush = state.getCurrentBrush(); + int penStyle = pen.getStyle(); + int brushStyle = brush.getStyle(); + if (penStyle == MetaPen.PS_NULL) { + cb.closePath(); + if (state.getPolyFillMode() == MetaState.ALTERNATE) { + cb.eoFill(); + } + else { + cb.fill(); + } + } + else { + boolean isBrush = (brushStyle == MetaBrush.BS_SOLID || (brushStyle == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE)); + if (isBrush) { + if (state.getPolyFillMode() == MetaState.ALTERNATE) + cb.closePathEoFillStroke(); + else + cb.closePathFillStroke(); + } + else { + cb.closePathStroke(); + } + } + } + + static float getArc(float xCenter, float yCenter, float xDot, float yDot) { + double s = Math.atan2(yDot - yCenter, xDot - xCenter); + if (s < 0) + s += Math.PI * 2; + return (float)(s / Math.PI * 180); + } + + public static byte[] wrapBMP(Image image) throws IOException { + if (image.getOriginalType() != Image.ORIGINAL_BMP) + throw new IOException("Only BMP can be wrapped in WMF."); + InputStream imgIn; + byte data[] = null; + if (image.getOriginalData() == null) { + imgIn = image.url().openStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int b = 0; + while ((b = imgIn.read()) != -1) + out.write(b); + imgIn.close(); + data = out.toByteArray(); + } + else + data = image.getOriginalData(); + int sizeBmpWords = (data.length - 14 + 1) >>> 1; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + // write metafile header + writeWord(os, 1); + writeWord(os, 9); + writeWord(os, 0x0300); + writeDWord(os, 9 + 4 + 5 + 5 + (13 + sizeBmpWords) + 3); // total metafile size + writeWord(os, 1); + writeDWord(os, 14 + sizeBmpWords); // max record size + writeWord(os, 0); + // write records + writeDWord(os, 4); + writeWord(os, META_SETMAPMODE); + writeWord(os, 8); + + writeDWord(os, 5); + writeWord(os, META_SETWINDOWORG); + writeWord(os, 0); + writeWord(os, 0); + + writeDWord(os, 5); + writeWord(os, META_SETWINDOWEXT); + writeWord(os, (int)image.height()); + writeWord(os, (int)image.width()); + + writeDWord(os, 13 + sizeBmpWords); + writeWord(os, META_DIBSTRETCHBLT); + writeDWord(os, 0x00cc0020); + writeWord(os, (int)image.height()); + writeWord(os, (int)image.width()); + writeWord(os, 0); + writeWord(os, 0); + writeWord(os, (int)image.height()); + writeWord(os, (int)image.width()); + writeWord(os, 0); + writeWord(os, 0); + os.write(data, 14, data.length - 14); + if ((data.length & 1) == 1) + os.write(0); +// writeDWord(os, 14 + sizeBmpWords); +// writeWord(os, META_STRETCHDIB); +// writeDWord(os, 0x00cc0020); +// writeWord(os, 0); +// writeWord(os, (int)image.height()); +// writeWord(os, (int)image.width()); +// writeWord(os, 0); +// writeWord(os, 0); +// writeWord(os, (int)image.height()); +// writeWord(os, (int)image.width()); +// writeWord(os, 0); +// writeWord(os, 0); +// os.write(data, 14, data.length - 14); +// if ((data.length & 1) == 1) +// os.write(0); + + writeDWord(os, 3); + writeWord(os, 0); + os.close(); + return os.toByteArray(); + } + + public static void writeWord(OutputStream os, int v) throws IOException { + os.write(v & 0xff); + os.write((v >>> 8) & 0xff); + } + + public static void writeDWord(OutputStream os, int v) throws IOException { + writeWord(os, v & 0xffff); + writeWord(os, (v >>> 16) & 0xffff); + } +} \ No newline at end of file diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaFont.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaFont.java new file mode 100644 index 0000000..70da31d --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaFont.java @@ -0,0 +1,211 @@ +/* + * $Id: MetaFont.java,v 1.3 2005/11/02 18:03:45 psoares33 Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import com.lowagie.text.pdf.*; +import com.lowagie.text.Font; +import com.lowagie.text.FontFactory; +import com.lowagie.text.ExceptionConverter; + +public class MetaFont extends MetaObject { + static final String fontNames[] = { + "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", + "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", + "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic", + "Symbol", "ZapfDingbats"}; + + static final int MARKER_BOLD = 1; + static final int MARKER_ITALIC = 2; + static final int MARKER_COURIER = 0; + static final int MARKER_HELVETICA = 4; + static final int MARKER_TIMES = 8; + static final int MARKER_SYMBOL = 12; + + static final int DEFAULT_PITCH = 0; + static final int FIXED_PITCH = 1; + static final int VARIABLE_PITCH = 2; + static final int FF_DONTCARE = 0; + static final int FF_ROMAN = 1; + static final int FF_SWISS = 2; + static final int FF_MODERN = 3; + static final int FF_SCRIPT = 4; + static final int FF_DECORATIVE = 5; + static final int BOLDTHRESHOLD = 600; + static final int nameSize = 32; + static final int ETO_OPAQUE = 2; + static final int ETO_CLIPPED = 4; + + int height; + float angle; + int bold; + int italic; + boolean underline; + boolean strikeout; + int charset; + int pitchAndFamily; + String faceName = "arial"; + BaseFont font = null; + + public MetaFont() { + type = META_FONT; + } + + public void init(InputMeta in) throws IOException { + height = Math.abs(in.readShort()); + in.skip(2); + angle = (float)(in.readShort() / 1800.0 * Math.PI); + in.skip(2); + bold = (in.readShort() >= BOLDTHRESHOLD ? MARKER_BOLD : 0); + italic = (in.readByte() != 0 ? MARKER_ITALIC : 0); + underline = (in.readByte() != 0); + strikeout = (in.readByte() != 0); + charset = in.readByte(); + in.skip(3); + pitchAndFamily = in.readByte(); + byte name[] = new byte[nameSize]; + int k; + for (k = 0; k < nameSize; ++k) { + int c = in.readByte(); + if (c == 0) { + break; + } + name[k] = (byte)c; + } + try { + faceName = new String(name, 0, k, "Cp1252"); + } + catch (UnsupportedEncodingException e) { + faceName = new String(name, 0, k); + } + faceName = faceName.toLowerCase(); + } + + public BaseFont getFont() { + if (font != null) + return font; + Font ff2 = FontFactory.getFont(faceName, BaseFont.CP1252, true, 10, ((italic != 0) ? Font.ITALIC : 0) | ((bold != 0) ? Font.BOLD : 0)); + font = ff2.getBaseFont(); + if (font != null) + return font; + String fontName; + if (faceName.indexOf("courier") != -1 || faceName.indexOf("terminal") != -1 + || faceName.indexOf("fixedsys") != -1) { + fontName = fontNames[MARKER_COURIER + italic + bold]; + } + else if (faceName.indexOf("ms sans serif") != -1 || faceName.indexOf("arial") != -1 + || faceName.indexOf("system") != -1) { + fontName = fontNames[MARKER_HELVETICA + italic + bold]; + } + else if (faceName.indexOf("arial black") != -1) { + fontName = fontNames[MARKER_HELVETICA + italic + MARKER_BOLD]; + } + else if (faceName.indexOf("times") != -1 || faceName.indexOf("ms serif") != -1 + || faceName.indexOf("roman") != -1) { + fontName = fontNames[MARKER_TIMES + italic + bold]; + } + else if (faceName.indexOf("symbol") != -1) { + fontName = fontNames[MARKER_SYMBOL]; + } + else { + int pitch = pitchAndFamily & 3; + int family = (pitchAndFamily >> 4) & 7; + switch (family) { + case FF_MODERN: + fontName = fontNames[MARKER_COURIER + italic + bold]; + break; + case FF_ROMAN: + fontName = fontNames[MARKER_TIMES + italic + bold]; + break; + case FF_SWISS: + case FF_SCRIPT: + case FF_DECORATIVE: + fontName = fontNames[MARKER_HELVETICA + italic + bold]; + break; + default: + { + switch (pitch) { + case FIXED_PITCH: + fontName = fontNames[MARKER_COURIER + italic + bold]; + break; + default: + fontName = fontNames[MARKER_HELVETICA + italic + bold]; + break; + } + } + } + } + try { + font = BaseFont.createFont(fontName, "Cp1252", false); + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + + return font; + } + + public float getAngle() { + return angle; + } + + public boolean isUnderline() { + return underline; + } + + public boolean isStrikeout() { + return strikeout; + } + + public float getFontSize(MetaState state) { + return Math.abs(state.transformY(height) - state.transformY(0)) * 0.86f; + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaObject.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaObject.java new file mode 100644 index 0000000..d31fb02 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaObject.java @@ -0,0 +1,71 @@ +/* + * $Id: MetaObject.java,v 1.2 2005/05/04 14:33:18 blowagie Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; + +public class MetaObject { + public static final int META_NOT_SUPPORTED = 0; + public static final int META_PEN = 1; + public static final int META_BRUSH = 2; + public static final int META_FONT = 3; + public int type = META_NOT_SUPPORTED; + + public MetaObject() { + } + + public MetaObject(int type) { + this.type = type; + } + + public int getType() { + return type; + } + +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaPen.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaPen.java new file mode 100644 index 0000000..29b85ca --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaPen.java @@ -0,0 +1,91 @@ +/* + * $Id: MetaPen.java,v 1.2 2005/05/04 14:33:19 blowagie Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; +import java.io.IOException; +import java.awt.Color; + +public class MetaPen extends MetaObject { + + public static final int PS_SOLID = 0; + public static final int PS_DASH = 1; + public static final int PS_DOT = 2; + public static final int PS_DASHDOT = 3; + public static final int PS_DASHDOTDOT = 4; + public static final int PS_NULL = 5; + public static final int PS_INSIDEFRAME = 6; + + int style = PS_SOLID; + int penWidth = 1; + Color color = Color.black; + + public MetaPen() { + type = META_PEN; + } + + public void init(InputMeta in) throws IOException { + style = in.readWord(); + penWidth = in.readShort(); + in.readWord(); + color = in.readColor(); + } + + public int getStyle() { + return style; + } + + public int getPenWidth() { + return penWidth; + } + + public Color getColor() { + return color; + } +} diff --git a/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaState.java b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaState.java new file mode 100644 index 0000000..97f18f4 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/wmf/MetaState.java @@ -0,0 +1,372 @@ +/* + * $Id: MetaState.java,v 1.5 2005/12/01 16:57:15 psoares33 Exp $ + * $Name: $ + * + * Copyright 2001, 2002 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/ + */ + +package com.lowagie.text.pdf.codec.wmf; + +import java.util.ArrayList; +import java.util.Stack; +import java.awt.Point; +import java.awt.Color; +import com.lowagie.text.pdf.*; + +public class MetaState { + + public static final int TA_NOUPDATECP = 0; + public static final int TA_UPDATECP = 1; + public static final int TA_LEFT = 0; + public static final int TA_RIGHT = 2; + public static final int TA_CENTER = 6; + public static final int TA_TOP = 0; + public static final int TA_BOTTOM = 8; + public static final int TA_BASELINE = 24; + + public static final int TRANSPARENT = 1; + public static final int OPAQUE = 2; + + public static final int ALTERNATE = 1; + public static final int WINDING = 2; + + public Stack savedStates; + public ArrayList MetaObjects; + public Point currentPoint; + public MetaPen currentPen; + public MetaBrush currentBrush; + public MetaFont currentFont; + public Color currentBackgroundColor = Color.white; + public Color currentTextColor = Color.black; + public int backgroundMode = OPAQUE; + public int polyFillMode = ALTERNATE; + public int lineJoin = 1; + public int textAlign; + public int offsetWx; + public int offsetWy; + public int extentWx; + public int extentWy; + public float scalingX; + public float scalingY; + + + /** Creates new MetaState */ + public MetaState() { + savedStates = new Stack(); + MetaObjects = new ArrayList(); + currentPoint = new Point(0, 0); + currentPen = new MetaPen(); + currentBrush = new MetaBrush(); + currentFont = new MetaFont(); + } + + public MetaState(MetaState state) { + setMetaState(state); + } + + public void setMetaState(MetaState state) { + savedStates = state.savedStates; + MetaObjects = state.MetaObjects; + currentPoint = state.currentPoint; + currentPen = state.currentPen; + currentBrush = state.currentBrush; + currentFont = state.currentFont; + currentBackgroundColor = state.currentBackgroundColor; + currentTextColor = state.currentTextColor; + backgroundMode = state.backgroundMode; + polyFillMode = state.polyFillMode; + textAlign = state.textAlign; + lineJoin = state.lineJoin; + offsetWx = state.offsetWx; + offsetWy = state.offsetWy; + extentWx = state.extentWx; + extentWy = state.extentWy; + scalingX = state.scalingX; + scalingY = state.scalingY; + } + + public void addMetaObject(MetaObject object) { + for (int k = 0; k < MetaObjects.size(); ++k) { + if (MetaObjects.get(k) == null) { + MetaObjects.set(k, object); + return; + } + } + MetaObjects.add(object); + } + + public void selectMetaObject(int index, PdfContentByte cb) { + MetaObject obj = (MetaObject)MetaObjects.get(index); + if (obj == null) + return; + int style; + switch (obj.getType()) { + case MetaObject.META_BRUSH: + currentBrush = (MetaBrush)obj; + style = currentBrush.getStyle(); + if (style == MetaBrush.BS_SOLID) { + Color color = currentBrush.getColor(); + cb.setColorFill(color); + } + else if (style == MetaBrush.BS_HATCHED) { + Color color = currentBackgroundColor; + cb.setColorFill(color); + } + break; + case MetaObject.META_PEN: + { + currentPen = (MetaPen)obj; + style = currentPen.getStyle(); + if (style != MetaPen.PS_NULL) { + Color color = currentPen.getColor(); + cb.setColorStroke(color); + cb.setLineWidth(Math.abs((float)currentPen.getPenWidth() * scalingX / extentWx)); + switch (style) { + case MetaPen.PS_DASH: + cb.setLineDash(18, 6, 0); + break; + case MetaPen.PS_DASHDOT: + cb.setLiteral("[9 6 3 6]0 d\n"); + break; + case MetaPen.PS_DASHDOTDOT: + cb.setLiteral("[9 3 3 3 3 3]0 d\n"); + break; + case MetaPen.PS_DOT: + cb.setLineDash(3, 0); + break; + default: + cb.setLineDash(0); + break; + } + } + break; + } + case MetaObject.META_FONT: + { + currentFont = (MetaFont)obj; + break; + } + } + } + + public void deleteMetaObject(int index) { + MetaObjects.set(index, null); + } + + public void saveState(PdfContentByte cb) { + cb.saveState(); + MetaState state = new MetaState(this); + savedStates.push(state); + } + + public void restoreState(int index, PdfContentByte cb) { + int pops; + if (index < 0) + pops = Math.min(-index, savedStates.size()); + else + pops = Math.max(savedStates.size() - index, 0); + if (pops == 0) + return; + MetaState state = null; + while (pops-- != 0) { + cb.restoreState(); + state = (MetaState)savedStates.pop(); + } + setMetaState(state); + } + + public void cleanup(PdfContentByte cb) { + int k = savedStates.size(); + while (k-- > 0) + cb.restoreState(); + } + + public float transformX(int x) { + return ((float)x - offsetWx) * scalingX / extentWx; + } + + public float transformY(int y) { + return (1f - ((float)y - offsetWy) / extentWy) * scalingY; + } + + public void setScalingX(float scalingX) { + this.scalingX = scalingX; + } + + public void setScalingY(float scalingY) { + this.scalingY = scalingY; + } + + public void setOffsetWx(int offsetWx) { + this.offsetWx = offsetWx; + } + + public void setOffsetWy(int offsetWy) { + this.offsetWy = offsetWy; + } + + public void setExtentWx(int extentWx) { + this.extentWx = extentWx; + } + + public void setExtentWy(int extentWy) { + this.extentWy = extentWy; + } + + public float transformAngle(float angle) { + float ta = scalingY < 0 ? -angle : angle; + return (float)(scalingX < 0 ? Math.PI - ta : ta); + } + + public void setCurrentPoint(Point p) { + currentPoint = p; + } + + public Point getCurrentPoint() { + return currentPoint; + } + + public MetaBrush getCurrentBrush() { + return currentBrush; + } + + public MetaPen getCurrentPen() { + return currentPen; + } + + public MetaFont getCurrentFont() { + return currentFont; + } + + /** Getter for property currentBackgroundColor. + * @return Value of property currentBackgroundColor. + */ + public Color getCurrentBackgroundColor() { + return currentBackgroundColor; + } + + /** Setter for property currentBackgroundColor. + * @param currentBackgroundColor New value of property currentBackgroundColor. + */ + public void setCurrentBackgroundColor(Color currentBackgroundColor) { + this.currentBackgroundColor = currentBackgroundColor; + } + + /** Getter for property currentTextColor. + * @return Value of property currentTextColor. + */ + public Color getCurrentTextColor() { + return currentTextColor; + } + + /** Setter for property currentTextColor. + * @param currentTextColor New value of property currentTextColor. + */ + public void setCurrentTextColor(Color currentTextColor) { + this.currentTextColor = currentTextColor; + } + + /** Getter for property backgroundMode. + * @return Value of property backgroundMode. + */ + public int getBackgroundMode() { + return backgroundMode; + } + + /** Setter for property backgroundMode. + * @param backgroundMode New value of property backgroundMode. + */ + public void setBackgroundMode(int backgroundMode) { + this.backgroundMode = backgroundMode; + } + + /** Getter for property textAlign. + * @return Value of property textAlign. + */ + public int getTextAlign() { + return textAlign; + } + + /** Setter for property textAlign. + * @param textAlign New value of property textAlign. + */ + public void setTextAlign(int textAlign) { + this.textAlign = textAlign; + } + + /** Getter for property polyFillMode. + * @return Value of property polyFillMode. + */ + public int getPolyFillMode() { + return polyFillMode; + } + + /** Setter for property polyFillMode. + * @param polyFillMode New value of property polyFillMode. + */ + public void setPolyFillMode(int polyFillMode) { + this.polyFillMode = polyFillMode; + } + + public void setLineJoinRectangle(PdfContentByte cb) { + if (lineJoin != 0) { + lineJoin = 0; + cb.setLineJoin(0); + } + } + + public void setLineJoinPolygon(PdfContentByte cb) { + if (lineJoin == 0) { + lineJoin = 1; + cb.setLineJoin(1); + } + } + + public boolean getLineNeutral() { + return (lineJoin == 0); + } + +} -- cgit v1.2.3