aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/lowagie/text/pdf/codec
diff options
context:
space:
mode:
authortknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
committertknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
commit6025b6016517c6d898d8957d1d7e03ba71431912 (patch)
treeb15bd6fa5ffe9588a9bca3f2b8a7e358f83b6eba /src/main/java/com/lowagie/text/pdf/codec
parentd2c77e820ab4aba8235d71275755021347b3ad10 (diff)
downloadpdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.gz
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.bz2
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.zip
Initial import of release 2.2.REL-2.2@923
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main/java/com/lowagie/text/pdf/codec')
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/BmpImage.java1282
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/CCITTG4Encoder.java600
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/GifImage.java593
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/PngImage.java987
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java296
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TIFFDirectory.java656
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java1477
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TIFFField.java472
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java255
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/TiffImage.java522
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java547
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java98
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java19
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java2772
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAEngine.java155
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAParser.java351
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserConstants.java60
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserTokenManager.java1011
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAPencil.java431
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PAToken.java66
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/PainterException.java20
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/ParseException.java192
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/Token.java81
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/postscript/TokenMgrError.java133
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/InputMeta.java112
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaBrush.java94
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaDo.java760
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaFont.java211
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaObject.java71
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaPen.java91
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/wmf/MetaState.java372
31 files changed, 14787 insertions, 0 deletions
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.
+ * <p>
+ * 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<sizep; i++) {
+ off = 3 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ } else {
+ sizep = palette.length/4;
+
+ if (sizep > 256) {
+ sizep = 256;
+ }
+
+ int off;
+ r = new byte[sizep];
+ g = new byte[sizep];
+ b = new byte[sizep];
+ for (int i=0; i<sizep; i++) {
+ off = 4 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ }
+
+ } else if (bitsPerPixel == 16) {
+ numBands = 3;
+ } else if (bitsPerPixel == 32) {
+ numBands = alphaMask == 0 ? 3 : 4;
+
+ // The number of bands in the SampleModel is determined by
+ // the length of the mask array passed in.
+ } else {
+ numBands = 3;
+ }
+ }
+
+ private byte[] getPalette(int group) {
+ if (palette == null)
+ return null;
+ byte np[] = new byte[palette.length / group * 3];
+ int e = palette.length / group;
+ for (int k = 0; k < e; ++k) {
+ int src = k * group;
+ int dest = k * 3;
+ np[dest + 2] = palette[src++];
+ np[dest + 1] = palette[src++];
+ np[dest] = palette[src];
+ }
+ return np;
+ }
+
+ private Image getImage() throws IOException, BadElementException {
+ byte bdata[] = null; // buffer for byte data
+
+ // if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
+ // bdata = (byte[])((DataBufferByte)tile.getDataBuffer()).getData();
+ // else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
+ // sdata = (short[])((DataBufferUShort)tile.getDataBuffer()).getData();
+ // else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
+ // idata = (int[])((DataBufferInt)tile.getDataBuffer()).getData();
+
+ // There should only be one tile.
+ switch(imageType) {
+
+ case VERSION_2_1_BIT:
+ // no compression
+ return read1Bit(3);
+
+ case VERSION_2_4_BIT:
+ // no compression
+ return read4Bit(3);
+
+ case VERSION_2_8_BIT:
+ // no compression
+ return read8Bit(3);
+
+ case VERSION_2_24_BIT:
+ // no compression
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_3_1_BIT:
+ // 1-bit images cannot be compressed.
+ return read1Bit(4);
+
+ case VERSION_3_4_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ return read4Bit(4);
+
+ case BI_RLE4:
+ return readRLE4();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_3_8_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ return read8Bit(4);
+
+ case BI_RLE8:
+ return readRLE8();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_3_24_BIT:
+ // 24-bit images are not compressed
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_3_NT_16_BIT:
+ return read1632Bit(false);
+
+ case VERSION_3_NT_32_BIT:
+ return read1632Bit(true);
+
+ case VERSION_4_1_BIT:
+ return read1Bit(4);
+
+ case VERSION_4_4_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ return read4Bit(4);
+
+ case BI_RLE4:
+ return readRLE4();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_4_8_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ return read8Bit(4);
+
+ case BI_RLE8:
+ return readRLE8();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_4_16_BIT:
+ return read1632Bit(false);
+
+ case VERSION_4_24_BIT:
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_4_32_BIT:
+ return read1632Bit(true);
+ }
+ return null;
+ }
+
+ private Image indexedModel(byte bdata[], int bpc, int paletteEntries) throws BadElementException {
+ Image img = new ImgRaw(width, height, 1, bpc, bdata);
+ PdfArray colorspace = new PdfArray();
+ colorspace.add(PdfName.INDEXED);
+ colorspace.add(PdfName.DEVICERGB);
+ byte np[] = getPalette(paletteEntries);
+ int len = np.length;
+ colorspace.add(new PdfNumber(len / 3 - 1));
+ colorspace.add(new PdfString(np));
+ PdfDictionary ad = new PdfDictionary();
+ ad.put(PdfName.COLORSPACE, colorspace);
+ img.setAdditional(ad);
+ return img;
+ }
+
+ // Deal with 1 Bit images using IndexColorModels
+ private Image read1Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[((width + 7) / 8) * height];
+ int padding = 0;
+ int bytesPerScanline = (int)Math.ceil((double)width/8.0);
+
+ int remainder = bytesPerScanline % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ int imSize = (bytesPerScanline + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1)*(bytesPerScanline + padding),
+ bdata,
+ i*bytesPerScanline, bytesPerScanline);
+ }
+ } else {
+
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (bytesPerScanline + padding),
+ bdata,
+ i * bytesPerScanline,
+ bytesPerScanline);
+ }
+ }
+ return indexedModel(bdata, 1, paletteEntries);
+ }
+
+ // Method to read a 4 bit BMP image data
+ private Image read4Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[((width + 1) / 2) * height];
+
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ int bytesPerScanline = (int)Math.ceil((double)width/2.0);
+ int remainder = bytesPerScanline % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ int imSize = (bytesPerScanline + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1)*(bytesPerScanline + padding),
+ bdata,
+ i*bytesPerScanline,
+ bytesPerScanline);
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (bytesPerScanline + padding),
+ bdata,
+ i * bytesPerScanline,
+ bytesPerScanline);
+ }
+ }
+ return indexedModel(bdata, 4, paletteEntries);
+ }
+
+ // Method to read 8 bit BMP image data
+ private Image read8Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[width * height];
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ // width * bitsPerPixel should be divisible by 32
+ int bitsPerScanline = width * 8;
+ if ( bitsPerScanline%32 != 0) {
+ padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
+ padding = (int)Math.ceil(padding/8.0);
+ }
+
+ int imSize = (width + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead, imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1) * (width + padding),
+ bdata,
+ i * width,
+ width);
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (width + padding),
+ bdata,
+ i * width,
+ width);
+ }
+ }
+ return indexedModel(bdata, 8, paletteEntries);
+ }
+
+ // Method to read 24 bit BMP image data
+ private void read24Bit(byte[] bdata) {
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ // width * bitsPerPixel should be divisible by 32
+ int bitsPerScanline = width * 24;
+ if ( bitsPerScanline%32 != 0) {
+ padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
+ padding = (int)Math.ceil(padding/8.0);
+ }
+
+
+ int imSize = ((width * 3 + 3) / 4 * 4) * height;
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ try {
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ int r = inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ if (r < 0)
+ break;
+ bytesRead += r;
+ }
+ } catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+
+ int l=0, count;
+
+ if (isBottomUp) {
+ int max = width*height*3-1;
+
+ count = -padding;
+ for (int i=0; i<height; i++) {
+ l = max - (i+1)*width*3 + 1;
+ count += padding;
+ for (int j=0; j<width; j++) {
+ bdata[l + 2] = values[count++];
+ bdata[l + 1] = values[count++];
+ bdata[l] = values[count++];
+ l += 3;
+ }
+ }
+ } else {
+ count = -padding;
+ for (int i=0; i<height; i++) {
+ count += padding;
+ for (int j=0; j<width; j++) {
+ bdata[l + 2] = values[count++];
+ bdata[l + 1] = values[count++];
+ bdata[l] = values[count++];
+ l += 3;
+ }
+ }
+ }
+ }
+
+ private int findMask(int mask) {
+ int k = 0;
+ for (; k < 32; ++k) {
+ if ((mask & 1) == 1)
+ break;
+ mask >>>= 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<width; j++) {
+ if (is32)
+ v = (int)readDWord(inputStream);
+ else
+ v = readWord(inputStream);
+ bdata[l++] = (byte)(((v >>> 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<padding; m++) {
+ inputStream.read();
+ }
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ for (int j=0; j<width; j++) {
+ if (is32)
+ v = (int)readDWord(inputStream);
+ else
+ v = readWord(inputStream);
+ bdata[l++] = (byte)(((v >>> 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<padding; m++) {
+ inputStream.read();
+ }
+ }
+ }
+ return new ImgRaw(width, height, 3, 8, bdata);
+ }
+
+ private Image readRLE8() throws IOException, BadElementException {
+
+ // If imageSize field is not provided, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ // Since data is compressed, decompress it
+ byte val[] = decodeRLE(true, values);
+
+ // Uncompressed data does not have any padding
+ imSize = width * height;
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ // int bytesPerScanline = (int)Math.ceil((double)width/8.0);
+ byte temp[] = new byte[val.length];
+ int bytesPerScanline = width;
+ for (int i=0; i<height; i++) {
+ System.arraycopy(val,
+ imSize - (i+1)*(bytesPerScanline),
+ temp,
+ i*bytesPerScanline, bytesPerScanline);
+ }
+ val = temp;
+ }
+ return indexedModel(val, 8, 4);
+ }
+
+ private Image readRLE4() throws IOException, BadElementException {
+
+ // If imageSize field is not specified, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ // Decompress the RLE4 compressed data.
+ byte val[] = decodeRLE(false, values);
+
+ // Invert it as it is bottom up format.
+ if (isBottomUp) {
+
+ byte inverted[] = val;
+ val = new byte[width * height];
+ int l = 0, index, lineEnd;
+
+ for (int i = height-1; i >= 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.
+ * <p>
+ * 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 <CODE>int</CODE> from an <CODE>InputStream</CODE>.
+ *
+ * @param is an <CODE>InputStream</CODE>
+ * @return the value of an <CODE>int</CODE>
+ */
+
+ public static final int getInt(InputStream is) throws IOException {
+ return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read();
+ }
+
+/**
+ * Gets a <CODE>word</CODE> from an <CODE>InputStream</CODE>.
+ *
+ * @param is an <CODE>InputStream</CODE>
+ * @return the value of an <CODE>int</CODE>
+ */
+
+ public static final int getWord(InputStream is) throws IOException {
+ return (is.read() << 8) + is.read();
+ }
+
+/**
+ * Gets a <CODE>String</CODE> from an <CODE>InputStream</CODE>.
+ *
+ * @param is an <CODE>InputStream</CODE>
+ * @return the value of an <CODE>int</CODE>
+ */
+
+ 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 <dkelly@etsinc.com) */
+ public static final int COMPRESSION_IT8CTPAD = 32895; /* IT8 CT w/padding */
+ public static final int COMPRESSION_IT8LW = 32896; /* IT8 Linework RLE */
+ public static final int COMPRESSION_IT8MP = 32897; /* IT8 Monochrome picture */
+ public static final int COMPRESSION_IT8BL = 32898; /* IT8 Binary line art */
+ /* compression codes 32908-32911 are reserved for Pixar */
+ public static final int COMPRESSION_PIXARFILM = 32908; /* Pixar companded 10bit LZW */
+ public static final int COMPRESSION_PIXARLOG = 32909; /* Pixar companded 11bit ZIP */
+ public static final int COMPRESSION_DEFLATE = 32946; /* Deflate compression */
+ public static final int COMPRESSION_ADOBE_DEFLATE = 8; /* Deflate compression, as recognized by Adobe */
+ /* compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> */
+ 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 <dkelly@etsinc.com) */
+ public static final int TIFFTAG_IT8SITE = 34016; /* site name */
+ public static final int TIFFTAG_IT8COLORSEQUENCE = 34017; /* color seq. [RGB,CMYK,etc] */
+ public static final int TIFFTAG_IT8HEADER = 34018; /* DDES Header */
+ public static final int TIFFTAG_IT8RASTERPADDING = 34019; /* raster scanline padding */
+ public static final int TIFFTAG_IT8BITSPERRUNLENGTH = 34020; /* # of bits in short run */
+ public static final int TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH = 34021;/* # of bits in long run */
+ public static final int TIFFTAG_IT8COLORTABLE = 34022; /* LW colortable */
+ public static final int TIFFTAG_IT8IMAGECOLORINDICATOR = 34023; /* BP/BL image color switch */
+ public static final int TIFFTAG_IT8BKGCOLORINDICATOR = 34024; /* BP/BL bg color switch */
+ public static final int TIFFTAG_IT8IMAGECOLORVALUE = 34025; /* BP/BL image color value */
+ public static final int TIFFTAG_IT8BKGCOLORVALUE = 34026; /* BP/BL bg color value */
+ public static final int TIFFTAG_IT8PIXELINTENSITYRANGE = 34027; /* MP pixel intensity value */
+ public static final int TIFFTAG_IT8TRANSPARENCYINDICATOR = 34028; /* HC transparency switch */
+ public static final int TIFFTAG_IT8COLORCHARACTERIZATION = 34029; /* color character. table */
+ /* tags 34232-34236 are private tags registered to Texas Instruments */
+ public static final int TIFFTAG_FRAMECOUNT = 34232; /* Sequence Frame Count */
+ /* tag 34750 is a private tag registered to Adobe? */
+ public static final int TIFFTAG_ICCPROFILE = 34675; /* ICC profile data */
+ /* tag 34377 is private tag registered to Adobe for PhotoShop */
+ public static final int TIFFTAG_PHOTOSHOP = 34377;
+ /* tag 34750 is a private tag registered to Pixel Magic */
+ public static final int TIFFTAG_JBIGOPTIONS = 34750; /* JBIG options */
+ /* tags 34908-34914 are private tags registered to SGI */
+ public static final int TIFFTAG_FAXRECVPARAMS = 34908; /* encoded Class 2 ses. parms */
+ public static final int TIFFTAG_FAXSUBADDRESS = 34909; /* received SubAddr string */
+ public static final int TIFFTAG_FAXRECVTIME = 34910; /* receive time (secs) */
+ /* tags 37439-37443 are registered to SGI <gregl@sgi.com> */
+ 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p><b> This class is not a committed part of the JAI API. It may
+ * be removed or changed in future releases of JAI.</b>
+ *
+ * @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 <code>SeekableStream</code>.
+ */
+ 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
+ * <code>TIFFDirectory</code>.
+ */
+ public long getIFDOffset() {
+ return IFDOffset;
+ }
+
+ /**
+ * Returns the offset of the next IFD after the IFD corresponding to this
+ * <code>TIFFDirectory</code>.
+ */
+ 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.
+ *
+ * <p> The TIFF file format is described in more detail in the
+ * comments for the TIFFDescriptor class.
+ *
+ * <p> 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.
+ *
+ * <p><b> This class is not a committed part of the JAI API. It may
+ * be removed or changed in future releases of JAI.</b>
+ *
+ * @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:
+ *
+ * <table border=1>
+ * <tr>
+ * <th> TIFF type </th> <th> Java type </th>
+ * <tr>
+ * <td><tt>TIFF_BYTE</tt></td> <td><tt>byte</tt></td>
+ * <tr>
+ * <td><tt>TIFF_ASCII</tt></td> <td><tt>String</tt></td>
+ * <tr>
+ * <td><tt>TIFF_SHORT</tt></td> <td><tt>char</tt></td>
+ * <tr>
+ * <td><tt>TIFF_LONG</tt></td> <td><tt>long</tt></td>
+ * <tr>
+ * <td><tt>TIFF_RATIONAL</tt></td> <td><tt>long[2]</tt></td>
+ * <tr>
+ * <td><tt>TIFF_SBYTE</tt></td> <td><tt>byte</tt></td>
+ * <tr>
+ * <td><tt>TIFF_UNDEFINED</tt></td> <td><tt>byte</tt></td>
+ * <tr>
+ * <td><tt>TIFF_SSHORT</tt></td> <td><tt>short</tt></td>
+ * <tr>
+ * <td><tt>TIFF_SLONG</tt></td> <td><tt>int</tt></td>
+ * <tr>
+ * <td><tt>TIFF_SRATIONAL</tt></td> <td><tt>int[2]</tt></td>
+ * <tr>
+ * <td><tt>TIFF_FLOAT</tt></td> <td><tt>float</tt></td>
+ * <tr>
+ * <td><tt>TIFF_DOUBLE</tt></td> <td><tt>double</tt></td>
+ * </table>
+ */
+ 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;
+ *
+ * <p> For data in TIFF_BYTE format, the application must take
+ * care when promoting the data to longer integral types
+ * to avoid sign extension.
+ *
+ * <p> 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).
+ *
+ * <p> 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).
+ *
+ * <p> 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).
+ *
+ * <p> 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).
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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].
+ *
+ * <p> 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.
+ *
+ * <p> 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].
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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 <code>TIFFField</code> with another
+ * <code>TIFFField</code> by comparing the tags.
+ *
+ * <p><b>Note: this class has a natural ordering that is inconsistent
+ * with <code>equals()</code>.</b>
+ *
+ * @throws IllegalArgumentException if the parameter is <code>null</code>.
+ * @throws ClassCastException if the parameter is not a
+ * <code>TIFFField</code>.
+ */
+ 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; i<string.length; i++) {
+ uncompData[dstIndex++] = string[i];
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte string[]) {
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Append <code>newString</code> to the end of <code>oldString</code>.
+ */
+ 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 <CODE>Image</CODE>
+ */
+ 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 <CODE>Image</CODE>
+ */
+ 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.<BR>
+ */
+ 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 <code>-1</code> 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 <code>-1</code> 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;
+ }
+
+ /**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2006</p>
+ *
+ * <p>Company: </p>
+ * @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<<j)) != 0) {
+ la1tokens[j] = true;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 25; i++) {
+ if (la1tokens[i]) {
+ jj_expentry = new int[1];
+ jj_expentry[0] = i;
+ jj_expentries.addElement(jj_expentry);
+ }
+ }
+ int[][] exptokseq = new int[jj_expentries.size()][];
+ for (int i = 0; i < jj_expentries.size(); i++) {
+ exptokseq[i] = (int[])jj_expentries.elementAt(i);
+ }
+ return new ParseException(token, exptokseq, tokenImage);
+ }
+
+ final public void enable_tracing() {
+ }
+
+ final public void disable_tracing() {
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserConstants.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserConstants.java
new file mode 100644
index 0000000..828ccfe
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAParserConstants.java
@@ -0,0 +1,60 @@
+/* Generated By:JavaCC: Do not edit this line. PAParserConstants.java */
+package com.lowagie.text.pdf.codec.postscript;
+
+public interface PAParserConstants {
+
+ int EOF = 0;
+ int WHITESPACE = 1;
+ int INTEGER_LITERAL = 5;
+ int DECIMAL_LITERAL = 6;
+ int HEX_LITERAL = 7;
+ int FLOATING_POINT_LITERAL = 8;
+ int EXPONENT = 9;
+ int STRING_LITERAL = 10;
+ int IDENTIFIER = 11;
+ int KEY_IDENTIFIER = 12;
+ int IMMEDIATE_IDENTIFIER = 13;
+ int LETTER = 14;
+ int DIGIT = 15;
+ int LBRACE = 16;
+ int RBRACE = 17;
+ int LBRACKET = 18;
+ int RBRACKET = 19;
+ int LDICT = 20;
+ int RDICT = 21;
+ int Nextchar = 22;
+ int HEX_STRING_LITERAL = 23;
+ int Instring = 24;
+
+ int DEFAULT = 0;
+ int WITHINSTRING = 1;
+
+ String[] tokenImage = {
+ "<EOF>",
+ "<WHITESPACE>",
+ "<token of kind 2>",
+ "<token of kind 3>",
+ "<token of kind 4>",
+ "<INTEGER_LITERAL>",
+ "<DECIMAL_LITERAL>",
+ "<HEX_LITERAL>",
+ "<FLOATING_POINT_LITERAL>",
+ "<EXPONENT>",
+ "<STRING_LITERAL>",
+ "<IDENTIFIER>",
+ "<KEY_IDENTIFIER>",
+ "<IMMEDIATE_IDENTIFIER>",
+ "<LETTER>",
+ "<DIGIT>",
+ "\"{\"",
+ "\"}\"",
+ "\"[\"",
+ "\"]\"",
+ "\"<<\"",
+ "\">>\"",
+ "<Nextchar>",
+ "\">\"",
+ "\"<\"",
+ };
+
+}
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: <result of getMessage>
+ */
+ 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 ? "<EOF> " : ("\"" + 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);
+ }
+
+}