From c54d55d661897212c26488d78f1953f414444d0d Mon Sep 17 00:00:00 2001
From: contrudar
Date: Wed, 26 Oct 2016 12:03:11 +0300
Subject: [PATCH 1/2] fix the black pixel issue
---
PdfView/build.gradle | 6 +-
.../main/java/com/sun/pdfview/PDFImage.java | 1317 ++++++++---------
.../main/java/com/sun/pdfview/PDFParser.java | 166 +--
.../pdfview/colorspace/ICCIColorSpace.java | 60 -
.../sun/pdfview/colorspace/PDFColorSpace.java | 309 ++--
.../com/sun/pdfview/decode/DCTDecode.java | 95 +-
.../andpdf/pdfviewer/PdfViewerFragment.java | 83 --
.../java/net/sf/andpdf/utils/MathUtils.java | 78 -
.../java/net/sf/andpdf/utils/VectorF.java | 63 -
9 files changed, 919 insertions(+), 1258 deletions(-)
delete mode 100644 PdfView/src/main/java/com/sun/pdfview/colorspace/ICCIColorSpace.java
delete mode 100755 PdfView/src/main/java/net/sf/andpdf/utils/MathUtils.java
delete mode 100644 PdfView/src/main/java/net/sf/andpdf/utils/VectorF.java
diff --git a/PdfView/build.gradle b/PdfView/build.gradle
index 5083f1d..dde31a8 100644
--- a/PdfView/build.gradle
+++ b/PdfView/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 21
- buildToolsVersion "23.0.2"
+ compileSdkVersion 25
+ buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 14
@@ -17,7 +17,7 @@ android {
}
dependencies {
- provided 'com.android.support:appcompat-v7:24.2.1'
+ provided 'com.android.support:appcompat-v7:25.0.0'
compile('com.github.chrisbanes:PhotoView:1.3.0') {
exclude group: 'com.android.support', module: 'support-v4'
}
diff --git a/PdfView/src/main/java/com/sun/pdfview/PDFImage.java b/PdfView/src/main/java/com/sun/pdfview/PDFImage.java
index a7c0db8..007796b 100644
--- a/PdfView/src/main/java/com/sun/pdfview/PDFImage.java
+++ b/PdfView/src/main/java/com/sun/pdfview/PDFImage.java
@@ -20,694 +20,691 @@
*/
package com.sun.pdfview;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.Log;
-import com.sun.pdfview.colorspace.PDFColorSpace;
-import com.sun.pdfview.function.FunctionType0;
+import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
+import com.sun.pdfview.colorspace.PDFColorSpace;
+import com.sun.pdfview.function.FunctionType0;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Bitmap.Config;
+import android.util.Log;
+
+
+
/**
* Encapsulates a PDF Image
*/
public class PDFImage {
- private static final String TAG = "AWTPDF.pdfimage";
+ private static final String TAG = "AWTPDF.pdfimage";
- public static boolean sShowImages;
+ public static boolean sShowImages;
- public static void dump(PDFObject obj) throws IOException {
- p("dumping PDF object: " + obj);
- if (obj == null) {
- return;
- }
- HashMap dict = obj.getDictionary();
- p(" dict = " + dict);
- for (Object key : dict.keySet()) {
- p("key = " + key + " value = " + dict.get(key));
- }
- }
-
- public static void p(String string) {
- System.out.println(string);
- }
-
- /**
- * color key mask. Array of start/end pairs of ranges of color components to
- * mask out. If a component falls within any of the ranges it is clear.
- */
- private int[] colorKeyMask = null;
- /** the width of this image in pixels */
- private int width;
- /** the height of this image in pixels */
- private int height;
- /** the colorspace to interpret the samples in */
- private PDFColorSpace colorSpace;
- /** the number of bits per sample component */
- private int bpc;
- /** whether this image is a mask or not */
- private boolean imageMask = false;
- /** the SMask image, if any */
- private PDFImage sMask;
- /** the decode array */
- private float[] decode;
- /** the actual image data */
- private PDFObject imageObj;
-
- /**
- * Create an instance of a PDFImage
- */
- protected PDFImage(PDFObject imageObj) {
- this.imageObj = imageObj;
- }
-
- /**
- * Read a PDFImage from an image dictionary and stream
- *
- * @param obj the PDFObject containing the image's dictionary and stream
- * @param resources the current resources
- */
- public static PDFImage createImage(PDFObject obj, Map resources) throws IOException {
- // create the image
- PDFImage image = new PDFImage(obj);
-
- // get the width (required)
- PDFObject widthObj = obj.getDictRef("Width");
- if (widthObj == null) {
- throw new PDFParseException("Unable to read image width: " + obj);
- }
- image.setWidth(widthObj.getIntValue());
-
- // get the height (required)
- PDFObject heightObj = obj.getDictRef("Height");
- if (heightObj == null) {
- throw new PDFParseException("Unable to get image height: " + obj);
- }
- image.setHeight(heightObj.getIntValue());
-
- // figure out if we are an image mask (optional)
- PDFObject imageMaskObj = obj.getDictRef("ImageMask");
- if (imageMaskObj != null) {
- image.setImageMask(imageMaskObj.getBooleanValue());
- }
-
- // read the bpc and colorspace (required except for masks)
- if (image.isImageMask()) {
- image.setBitsPerComponent(1);
-
- // create the indexed color space for the mask
- // [PATCHED by michal.busta@gmail.com] - default value od Decode according to PDF spec. is [0, 1]
- // so the color arry should be:
- int[] colors = { Color.BLACK, Color.WHITE };
-
- PDFObject imageMaskDecode = obj.getDictRef("Decode");
- if (imageMaskDecode != null) {
- PDFObject[] array = imageMaskDecode.getArray();
- float decode0 = array[0].getFloatValue();
- if (decode0 == 1.0f) {
- colors = new int[] { Color.WHITE, Color.BLACK };
+ public static void dump(PDFObject obj) throws IOException {
+ p("dumping PDF object: " + obj);
+ if (obj == null) {
+ return;
}
- }
- // TODO [FHe]: support for indexed colorspace
- image.setColorSpace(PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY));
- //image.setColorSpace(new IndexedColor(colors));
- } else {
- // get the bits per component (required)
- PDFObject bpcObj = obj.getDictRef("BitsPerComponent");
- if (bpcObj == null) {
- throw new PDFParseException("Unable to get bits per component: " + obj);
- }
- image.setBitsPerComponent(bpcObj.getIntValue());
-
- // get the color space (required)
- PDFObject csObj = obj.getDictRef("ColorSpace");
- if (csObj == null) {
- throw new PDFParseException("No ColorSpace for image: " + obj);
- }
-
- PDFColorSpace cs = PDFColorSpace.getColorSpace(csObj, resources);
- image.setColorSpace(cs);
- }
-
- // read the decode array
- PDFObject decodeObj = obj.getDictRef("Decode");
- if (decodeObj != null) {
- PDFObject[] decodeArray = decodeObj.getArray();
-
- float[] decode = new float[decodeArray.length];
- for (int i = 0; i < decodeArray.length; i++) {
- decode[i] = decodeArray[i].getFloatValue();
- }
-
- image.setDecode(decode);
- }
-
- // read the soft mask.
- // If ImageMask is true, this entry must not be present.
- // (See implementation note 52 in Appendix H.)
- if (imageMaskObj == null) {
- PDFObject sMaskObj = obj.getDictRef("SMask");
- if (sMaskObj == null) {
- // try the explicit mask, if there is no SoftMask
- sMaskObj = obj.getDictRef("Mask");
- }
-
- if (sMaskObj != null) {
- if (sMaskObj.getType() == PDFObject.STREAM) {
- try {
- PDFImage sMaskImage = PDFImage.createImage(sMaskObj, resources);
- image.setSMask(sMaskImage);
- } catch (IOException ex) {
- p("ERROR: there was a problem parsing the mask for this object");
- dump(obj);
- ex.printStackTrace(System.out);
- }
- } else if (sMaskObj.getType() == PDFObject.ARRAY) {
- // retrieve the range of the ColorKeyMask
- // colors outside this range will not be painted.
- try {
- image.setColorKeyMask(sMaskObj);
- } catch (IOException ex) {
- p("ERROR: there was a problem parsing the color mask for this object");
- dump(obj);
- ex.printStackTrace(System.out);
- }
+ HashMap dict = obj.getDictionary();
+ p(" dict = " + dict);
+ for (Object key : dict.keySet()) {
+ p("key = " + key + " value = " + dict.get(key));
}
- }
}
- return image;
- }
-
- /**
- * Get the image that this PDFImage generates.
- *
- * @return a buffered image containing the decoded image data
- */
- public Bitmap getImage() {
- try {
- Bitmap bi = (Bitmap) imageObj.getCache();
-
- if (bi == null) {
- if (!sShowImages) throw new UnsupportedOperationException("do not show images");
- byte[] imgBytes = imageObj.getStream();
- bi = parseData(imgBytes);
- // TODO [FHe]: is the cache useful on Android?
- imageObj.setCache(bi);
- }
- // if(bi != null)
- // ImageIO.write(bi, "png", new File("/tmp/test/" + System.identityHashCode(this) + ".png"));
- return bi;
- } catch (IOException ioe) {
- System.out.println("Error reading image");
- ioe.printStackTrace();
- return null;
- } catch (OutOfMemoryError e) {
- // fix for too large images
- Log.e(TAG, "image too large (OutOfMemoryError)");
- int size = 15;
- int max = size - 1;
- int half = size / 2 - 1;
- Bitmap bi = Bitmap.createBitmap(size, size, Config.RGB_565);
- Canvas c = new Canvas(bi);
- c.drawColor(Color.RED);
- Paint p = new Paint();
- p.setColor(Color.WHITE);
- c.drawLine(0, 0, max, max, p);
- c.drawLine(0, max, max, 0, p);
- c.drawLine(half, 0, half, max, p);
- c.drawLine(0, half, max, half, p);
- return bi;
+ public static void p(String string) {
+ System.out.println(string);
}
- }
+ /** color key mask. Array of start/end pairs of ranges of color components to
+ * mask out. If a component falls within any of the ranges it is clear. */
+ private int[] colorKeyMask = null;
+ /** the width of this image in pixels */
+ private int width;
+ /** the height of this image in pixels */
+ private int height;
+ /** the colorspace to interpret the samples in */
+ private PDFColorSpace colorSpace;
+ /** the number of bits per sample component */
+ private int bpc;
+ /** whether this image is a mask or not */
+ private boolean imageMask = false;
+ /** the SMask image, if any */
+ private PDFImage sMask;
+ /** the decode array */
+ private float[] decode;
+ /** the actual image data */
+ private PDFObject imageObj;
- private Bitmap parseData(byte[] imgBytes) {
- Bitmap bi;
- long startTime = System.currentTimeMillis();
- // parse the stream data into an actual image
- Log.i(TAG, "Creating Image width="
- + getWidth()
- + ", Height="
- + getHeight()
- + ", bpc="
- + getBitsPerComponent()
- + ",cs="
- + colorSpace);
- if (colorSpace == null) {
- throw new UnsupportedOperationException("image without colorspace");
- } else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_RGB) {
- int maxH = getHeight();
- int maxW = getWidth();
- if (imgBytes.length == 2 * maxW * maxH) {
- // decoded JPEG as RGB565
- bi = Bitmap.createBitmap(maxW, maxH, Config.RGB_565);
- bi.copyPixelsFromBuffer(ByteBuffer.wrap(imgBytes));
- } else if (imgBytes.length == 4 * maxW * maxH) {
- // create RGB image
- bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
- bi.copyPixelsFromBuffer(ByteBuffer.wrap(imgBytes));
+ /**
+ * Create an instance of a PDFImage
+ */
+ protected PDFImage(PDFObject imageObj) {
+ this.imageObj = imageObj;
+ }
- } else {
- bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
- int[] line = new int[maxW];
- int n = 0;
- for (int h = 0; h < maxH; h++) {
- for (int w = 0; w < getWidth(); w++) {
- line[w] = ((0xff & (int) imgBytes[n]) << 8 | (0xff & (int) imgBytes[n + 1])) << 8
- | (0xff & (int) imgBytes[n + 2])
- | 0xFF000000;
- // line[w] = Color.rgb(0xff&(int)imgBytes[n], 0xff&(int)imgBytes[n+1],0xff&(int)imgBytes[n+2]);
- if (-line[w] == 0xFFFFFF + 1) {
- line[w] = 0xFFFFFFFF;
+ /**
+ * Read a PDFImage from an image dictionary and stream
+ *
+ * @param obj the PDFObject containing the image's dictionary and stream
+ * @param resources the current resources
+ */
+ public static PDFImage createImage(PDFObject obj, Map resources)
+ throws IOException {
+ // create the image
+ PDFImage image = new PDFImage(obj);
+
+ // get the width (required)
+ PDFObject widthObj = obj.getDictRef("Width");
+ if (widthObj == null) {
+ throw new PDFParseException("Unable to read image width: " + obj);
+ }
+ image.setWidth(widthObj.getIntValue());
+
+ // get the height (required)
+ PDFObject heightObj = obj.getDictRef("Height");
+ if (heightObj == null) {
+ throw new PDFParseException("Unable to get image height: " + obj);
+ }
+ image.setHeight(heightObj.getIntValue());
+
+ // figure out if we are an image mask (optional)
+ PDFObject imageMaskObj = obj.getDictRef("ImageMask");
+ if (imageMaskObj != null) {
+ image.setImageMask(imageMaskObj.getBooleanValue());
+ }
+
+ // read the bpc and colorspace (required except for masks)
+ if (image.isImageMask()) {
+ image.setBitsPerComponent(1);
+
+ // create the indexed color space for the mask
+ // [PATCHED by michal.busta@gmail.com] - default value od Decode according to PDF spec. is [0, 1]
+ // so the color arry should be:
+ int[] colors = {Color.BLACK, Color.WHITE};
+
+ PDFObject imageMaskDecode = obj.getDictRef("Decode");
+ if (imageMaskDecode != null) {
+ PDFObject[] array = imageMaskDecode.getArray();
+ float decode0 = array[0].getFloatValue();
+ if (decode0 == 1.0f) {
+ colors = new int[]{Color.WHITE, Color.BLACK};
+ }
}
- n += 3;
- }
- bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
+ // TODO [FHe]: support for indexed colorspace
+ image.setColorSpace(PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY));
+// image.setColorSpace(new IndexedColor(colors));
+ } else {
+ // get the bits per component (required)
+ PDFObject bpcObj = obj.getDictRef("BitsPerComponent");
+ if (bpcObj == null) {
+ throw new PDFParseException("Unable to get bits per component: " + obj);
+ }
+ image.setBitsPerComponent(bpcObj.getIntValue());
+
+ // get the color space (required)
+ PDFObject csObj = obj.getDictRef("ColorSpace");
+ if (csObj == null) {
+ throw new PDFParseException("No ColorSpace for image: " + obj);
+ }
+
+ PDFColorSpace cs = PDFColorSpace.getColorSpace(csObj, resources);
+ image.setColorSpace(cs);
}
- }
- } else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_GRAY) {
- // create gray image
- bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
- int maxH = getHeight();
- int maxW = getWidth();
- int[] line = new int[maxW];
- int n = 0;
- for (int h = 0; h < maxH; h++) {
- for (int w = 0; w < getWidth(); w++) {
- int gray = 0xff & (int) imgBytes[n];
- line[w] = (gray << 8 | gray) << 8 | gray | 0xFF000000;
- n += 1;
+
+ // read the decode array
+ PDFObject decodeObj = obj.getDictRef("Decode");
+ if (decodeObj != null) {
+ PDFObject[] decodeArray = decodeObj.getArray();
+
+ float[] decode = new float[decodeArray.length];
+ for (int i = 0; i < decodeArray.length; i++) {
+ decode[i] = decodeArray[i].getFloatValue();
+ }
+
+ image.setDecode(decode);
}
- bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
- }
- } else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_INDEXED) {
- // create indexed image
- bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
- int maxH = getHeight();
- int maxW = getWidth();
- int[] line = new int[maxW];
- int[] comps = new int[1];
- int n = 0;
- for (int h = 0; h < maxH; h++) {
- for (int w = 0; w < getWidth(); w++) {
- comps[0] = imgBytes[n] & 0xff;
- line[w] = colorSpace.toColor(comps);
- n += 1;
+
+ // read the soft mask.
+ // If ImageMask is true, this entry must not be present.
+ // (See implementation note 52 in Appendix H.)
+ if (imageMaskObj == null) {
+ PDFObject sMaskObj = obj.getDictRef("SMask");
+ if (sMaskObj == null) {
+ // try the explicit mask, if there is no SoftMask
+ sMaskObj = obj.getDictRef("Mask");
+ }
+
+ if (sMaskObj != null) {
+ if (sMaskObj.getType() == PDFObject.STREAM) {
+ try {
+ PDFImage sMaskImage = PDFImage.createImage(sMaskObj, resources);
+ image.setSMask(sMaskImage);
+ } catch (IOException ex) {
+ p("ERROR: there was a problem parsing the mask for this object");
+ dump(obj);
+ ex.printStackTrace(System.out);
+ }
+ } else if (sMaskObj.getType() == PDFObject.ARRAY) {
+ // retrieve the range of the ColorKeyMask
+ // colors outside this range will not be painted.
+ try {
+ image.setColorKeyMask(sMaskObj);
+ } catch (IOException ex) {
+ p("ERROR: there was a problem parsing the color mask for this object");
+ dump(obj);
+ ex.printStackTrace(System.out);
+ }
+ }
+ }
}
- bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
- }
- } else {
- throw new UnsupportedOperationException("image with unsupported colorspace " + colorSpace);
- }
- long stopTime = System.currentTimeMillis();
- Log.i(TAG, "millis for converting image=" + (stopTime - startTime));
- return bi;
- }
- // /**
- // *
Parse the image stream into a buffered image. Note that this is
- // * guaranteed to be called after all the other setXXX methods have been
- // * called.
- // *
- // *
NOTE: the color convolving is extremely slow on large images.
- // * It would be good to see if it could be moved out into the rendering
- // * phases, where we might be able to scale the image down first.
data.length) {
- // byte[] tempLargerData = new byte[tempExpectedSize];
- // System.arraycopy (data, 0, tempLargerData, 0, data.length);
- // db = new DataBufferByte (tempLargerData, tempExpectedSize);
- // raster =
- // Raster.createWritableRaster (sm, db, new Point (0, 0));
- // } else {
- // throw e;
- // }
- // }
- //
- // /*
- // * Workaround for a bug on the Mac -- a class cast exception in
- // * drawImage() due to the wrong data buffer type (?)
- // */
- // BufferedImage bi = null;
- // if (cm instanceof IndexColorModel) {
- // IndexColorModel icm = (IndexColorModel) cm;
- //
- // // choose the image type based on the size
- // int type = BufferedImage.TYPE_BYTE_BINARY;
- // if (getBitsPerComponent() == 8) {
- // type = BufferedImage.TYPE_BYTE_INDEXED;
- // }
- //
- // // create the image with an explicit indexed color model.
- // bi = new BufferedImage(getWidth(), getHeight(), type, icm);
- //
- // // set the data explicitly as well
- // bi.setData(raster);
- // } else {
- // bi = new BufferedImage(cm, raster, true, null);
- // }
- //
- // // hack to avoid *very* slow conversion
- // ColorSpace cs = cm.getColorSpace();
- // ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- //
- // // add in the alpha data supplied by the SMask, if any
- // PDFImage sMaskImage = getSMask();
- // if (sMaskImage != null) {
- // BufferedImage si = sMaskImage.getImage();
- //
- // BufferedImage outImage = new BufferedImage(getWidth(),
- // getHeight(), BufferedImage.TYPE_INT_ARGB);
- //
- // int[] srcArray = new int[width];
- // int[] maskArray = new int[width];
- //
- // for (int i = 0; i < height; i++) {
- // bi.getRGB(0, i, width, 1, srcArray, 0, width);
- // si.getRGB(0, i, width, 1, maskArray, 0, width);
- //
- // for (int j = 0; j < width; j++) {
- // int ac = 0xff000000;
- //
- // maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac);
- // }
- //
- // outImage.setRGB(0, i, width, 1, maskArray, 0, width);
- // }
- //
- // bi = outImage;
- // }
- //
- // return (bi);
- // }
-
- /**
- * Get the image's width
- */
- public int getWidth() {
- return width;
- }
-
- /**
- * Set the image's width
- */
- protected void setWidth(int width) {
- this.width = width;
- }
-
- /**
- * Get the image's height
- */
- public int getHeight() {
- return height;
- }
-
- /**
- * Set the image's height
- */
- protected void setHeight(int height) {
- this.height = height;
- }
-
- /**
- * set the color key mask. It is an array of start/end entries
- * to indicate ranges of color indicies that should be masked out.
- */
- private void setColorKeyMask(PDFObject maskArrayObject) throws IOException {
- PDFObject[] maskObjects = maskArrayObject.getArray();
- colorKeyMask = null;
- int[] masks = new int[maskObjects.length];
- for (int i = 0; i < masks.length; i++) {
- masks[i] = maskObjects[i].getIntValue();
- }
- colorKeyMask = masks;
- }
-
- /**
- * Get the colorspace associated with this image, or null if there
- * isn't one
- */
- protected PDFColorSpace getColorSpace() {
- return colorSpace;
- }
-
- /**
- * Set the colorspace associated with this image
- */
- protected void setColorSpace(PDFColorSpace colorSpace) {
- this.colorSpace = colorSpace;
- }
-
- /**
- * Get the number of bits per component sample
- */
- protected int getBitsPerComponent() {
- return bpc;
- }
-
- /**
- * Set the number of bits per component sample
- */
- protected void setBitsPerComponent(int bpc) {
- this.bpc = bpc;
- }
-
- /**
- * Return whether or not this is an image mask
- */
- public boolean isImageMask() {
- return imageMask;
- }
-
- /**
- * Set whether or not this is an image mask
- */
- public void setImageMask(boolean imageMask) {
- this.imageMask = imageMask;
- }
-
- /**
- * Return the soft mask associated with this image
- */
- public PDFImage getSMask() {
- return sMask;
- }
-
- /**
- * Set the soft mask image
- */
- protected void setSMask(PDFImage sMask) {
- this.sMask = sMask;
- }
-
- /**
- * Get the decode array
- */
- protected float[] getDecode() {
- return decode;
- }
-
- /**
- * Set the decode array
- */
- protected void setDecode(float[] decode) {
- this.decode = decode;
- }
-
- // /**
- // * get a Java ColorModel consistent with the current color space,
- // * number of bits per component and decode array
- // *
- // * @param bpc the number of bits per component
- // */
- // private ColorModel getColorModel() {
- // PDFColorSpace cs = getColorSpace();
- //
- // if (cs instanceof IndexedColor) {
- // IndexedColor ics = (IndexedColor) cs;
- //
- // byte[] components = ics.getColorComponents();
- // int num = ics.getCount();
- //
- // // process the decode array
- // if (decode != null) {
- // byte[] normComps = new byte[components.length];
- //
- // // move the components array around
- // for (int i = 0; i < num; i++) {
- // byte[] orig = new byte[1];
- // orig[0] = (byte) i;
- //
- // float[] res = normalize(orig, null, 0);
- // int idx = (int) res[0];
- //
- // normComps[i * 3] = components[idx * 3];
- // normComps[(i * 3) + 1] = components[(idx * 3) + 1];
- // normComps[(i * 3) + 2] = components[(idx * 3) + 2];
- // }
- //
- // components = normComps;
- // }
- //
- // // make sure the size of the components array is 2 ^ numBits
- // // since if it's not, Java will complain
- // int correctCount = 1 << getBitsPerComponent();
- // if (correctCount < num) {
- // byte[] fewerComps = new byte[correctCount * 3];
- //
- // System.arraycopy(components, 0, fewerComps, 0, correctCount * 3);
- //
- // components = fewerComps;
- // num = correctCount;
- // }
- // if (colorKeyMask == null || colorKeyMask.length == 0) {
- // return new IndexColorModel(getBitsPerComponent(), num, components,
- // 0, false);
- // } else {
- // byte[] aComps = new byte[num * 4];
- // int idx = 0;
- // for (int i = 0; i < num; i++) {
- // aComps[idx++] = components[(i * 3)];
- // aComps[idx++] = components[(i * 3) + 1];
- // aComps[idx++] = components[(i * 3) + 2];
- // aComps[idx++] = (byte) 0xFF;
- // }
- // for (int i = 0; i < colorKeyMask.length; i += 2) {
- // for (int j = colorKeyMask[i]; j <= colorKeyMask[i + 1]; j++) {
- // aComps[(j * 4) + 3] = 0; // make transparent
- // }
- // }
- // return new IndexColorModel(getBitsPerComponent(), num, aComps,
- // 0, true);
- // }
- // } else {
- // int[] bits = new int[cs.getNumComponents()];
- // for (int i = 0; i < bits.length; i++) {
- // bits[i] = getBitsPerComponent();
- // }
- //
- // return new DecodeComponentColorModel(cs.getColorSpace(), bits);
- // }
- // }
-
- /**
- * Normalize an array of values to match the decode array
- */
- private float[] normalize(byte[] pixels, float[] normComponents, int normOffset) {
- if (normComponents == null) {
- normComponents = new float[normOffset + pixels.length];
+ return image;
}
- float[] decodeArray = getDecode();
+ /**
+ * Get the image that this PDFImage generates.
+ *
+ * @return a buffered image containing the decoded image data
+ */
+ public Bitmap getImage() {
+ try {
+ Bitmap bi = (Bitmap) imageObj.getCache();
- for (int i = 0; i < pixels.length; i++) {
- int val = pixels[i] & 0xff;
- int pow = ((int) Math.pow(2, getBitsPerComponent())) - 1;
- float ymin = decodeArray[i * 2];
- float ymax = decodeArray[(i * 2) + 1];
-
- normComponents[normOffset + i] = FunctionType0.interpolate(val, 0, pow, ymin, ymax);
+ if (bi == null) {
+ if (!sShowImages)
+ throw new UnsupportedOperationException("do not show images");
+ byte[] imgBytes = imageObj.getStream();
+ bi = parseData(imgBytes);
+ // TODO [FHe]: is the cache useful on Android?
+ imageObj.setCache(bi);
+ }
+// if(bi != null)
+// ImageIO.write(bi, "png", new File("/tmp/test/" + System.identityHashCode(this) + ".png"));
+ return bi;
+ } catch (IOException ioe) {
+ System.out.println("Error reading image");
+ ioe.printStackTrace();
+ return null;
+ } catch (OutOfMemoryError e) {
+ // fix for too large images
+ Log.e(TAG, "image too large (OutOfMemoryError)");
+ int size = 15;
+ int max = size-1;
+ int half = size/2-1;
+ Bitmap bi = Bitmap.createBitmap(size, size, Config.RGB_565);
+ Canvas c = new Canvas(bi);
+ c.drawColor(Color.RED);
+ Paint p = new Paint();
+ p.setColor(Color.WHITE);
+ c.drawLine(0, 0, max, max, p);
+ c.drawLine(0, max, max, 0, p);
+ c.drawLine(half, 0, half, max, p);
+ c.drawLine(0, half, max, half, p);
+ return bi;
+ }
}
- return normComponents;
- }
+ private Bitmap parseData(byte[] imgBytes) {
+ Bitmap bi;
+ long startTime = System.currentTimeMillis();
+ // parse the stream data into an actual image
+ Log.i(TAG, "Creating Image width="+getWidth() + ", Height="+getHeight()+", bpc="+getBitsPerComponent()+",cs="+colorSpace);
+ if (colorSpace == null) {
+ throw new UnsupportedOperationException("image without colorspace");
+ } else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_RGB) {
+ int maxH = getHeight();
+ int maxW = getWidth();
+ if (imgBytes.length == 2*maxW*maxH) {
+ // decoded JPEG as RGB565
+ bi = Bitmap.createBitmap(maxW, maxH, Config.RGB_565);
+ bi.copyPixelsFromBuffer(ByteBuffer.wrap(imgBytes));
+ }
+ else {
+ // create RGB image
+ bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
+ int[] line = new int[maxW];
+ int n=0;
+ for (int h = 0; h data.length) {
+// byte[] tempLargerData = new byte[tempExpectedSize];
+// System.arraycopy (data, 0, tempLargerData, 0, data.length);
+// db = new DataBufferByte (tempLargerData, tempExpectedSize);
+// raster =
+// Raster.createWritableRaster (sm, db, new Point (0, 0));
+// } else {
+// throw e;
+// }
+// }
+//
+// /*
+// * Workaround for a bug on the Mac -- a class cast exception in
+// * drawImage() due to the wrong data buffer type (?)
+// */
+// BufferedImage bi = null;
+// if (cm instanceof IndexColorModel) {
+// IndexColorModel icm = (IndexColorModel) cm;
+//
+// // choose the image type based on the size
+// int type = BufferedImage.TYPE_BYTE_BINARY;
+// if (getBitsPerComponent() == 8) {
+// type = BufferedImage.TYPE_BYTE_INDEXED;
+// }
+//
+// // create the image with an explicit indexed color model.
+// bi = new BufferedImage(getWidth(), getHeight(), type, icm);
+//
+// // set the data explicitly as well
+// bi.setData(raster);
+// } else {
+// bi = new BufferedImage(cm, raster, true, null);
+// }
+//
+// // hack to avoid *very* slow conversion
+// ColorSpace cs = cm.getColorSpace();
+// ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+//
+// // add in the alpha data supplied by the SMask, if any
+// PDFImage sMaskImage = getSMask();
+// if (sMaskImage != null) {
+// BufferedImage si = sMaskImage.getImage();
+//
+// BufferedImage outImage = new BufferedImage(getWidth(),
+// getHeight(), BufferedImage.TYPE_INT_ARGB);
+//
+// int[] srcArray = new int[width];
+// int[] maskArray = new int[width];
+//
+// for (int i = 0; i < height; i++) {
+// bi.getRGB(0, i, width, 1, srcArray, 0, width);
+// si.getRGB(0, i, width, 1, maskArray, 0, width);
+//
+// for (int j = 0; j < width; j++) {
+// int ac = 0xff000000;
+//
+// maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac);
+// }
+//
+// outImage.setRGB(0, i, width, 1, maskArray, 0, width);
+// }
+//
+// bi = outImage;
+// }
+//
+// return (bi);
+// }
+
+ /**
+ * Get the image's width
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Set the image's width
+ */
+ protected void setWidth(int width) {
+ this.width = width;
+ }
+
+ /**
+ * Get the image's height
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Set the image's height
+ */
+ protected void setHeight(int height) {
+ this.height = height;
+ }
+
+ /**
+ * set the color key mask. It is an array of start/end entries
+ * to indicate ranges of color indicies that should be masked out.
+ *
+ * @param maskArrayObject
+ */
+ private void setColorKeyMask(PDFObject maskArrayObject) throws IOException {
+ PDFObject[] maskObjects = maskArrayObject.getArray();
+ colorKeyMask = null;
+ int[] masks = new int[maskObjects.length];
+ for (int i = 0; i < masks.length; i++) {
+ masks[i] = maskObjects[i].getIntValue();
+ }
+ colorKeyMask = masks;
+ }
+
+ /**
+ * Get the colorspace associated with this image, or null if there
+ * isn't one
+ */
+ protected PDFColorSpace getColorSpace() {
+ return colorSpace;
+ }
+
+ /**
+ * Set the colorspace associated with this image
+ */
+ protected void setColorSpace(PDFColorSpace colorSpace) {
+ this.colorSpace = colorSpace;
+ }
+
+ /**
+ * Get the number of bits per component sample
+ */
+ protected int getBitsPerComponent() {
+ return bpc;
+ }
+
+ /**
+ * Set the number of bits per component sample
+ */
+ protected void setBitsPerComponent(int bpc) {
+ this.bpc = bpc;
+ }
+
+ /**
+ * Return whether or not this is an image mask
+ */
+ public boolean isImageMask() {
+ return imageMask;
+ }
+
+ /**
+ * Set whether or not this is an image mask
+ */
+ public void setImageMask(boolean imageMask) {
+ this.imageMask = imageMask;
+ }
+
+ /**
+ * Return the soft mask associated with this image
+ */
+ public PDFImage getSMask() {
+ return sMask;
+ }
+
+ /**
+ * Set the soft mask image
+ */
+ protected void setSMask(PDFImage sMask) {
+ this.sMask = sMask;
+ }
+
+ /**
+ * Get the decode array
+ */
+ protected float[] getDecode() {
+ return decode;
+ }
+
+ /**
+ * Set the decode array
+ */
+ protected void setDecode(float[] decode) {
+ this.decode = decode;
+ }
+
+// /**
+// * get a Java ColorModel consistent with the current color space,
+// * number of bits per component and decode array
+// *
+// * @param bpc the number of bits per component
+// */
+// private ColorModel getColorModel() {
+// PDFColorSpace cs = getColorSpace();
+//
+// if (cs instanceof IndexedColor) {
+// IndexedColor ics = (IndexedColor) cs;
+//
+// byte[] components = ics.getColorComponents();
+// int num = ics.getCount();
+//
+// // process the decode array
+// if (decode != null) {
+// byte[] normComps = new byte[components.length];
+//
+// // move the components array around
+// for (int i = 0; i < num; i++) {
+// byte[] orig = new byte[1];
+// orig[0] = (byte) i;
+//
+// float[] res = normalize(orig, null, 0);
+// int idx = (int) res[0];
+//
+// normComps[i * 3] = components[idx * 3];
+// normComps[(i * 3) + 1] = components[(idx * 3) + 1];
+// normComps[(i * 3) + 2] = components[(idx * 3) + 2];
+// }
+//
+// components = normComps;
+// }
+//
+// // make sure the size of the components array is 2 ^ numBits
+// // since if it's not, Java will complain
+// int correctCount = 1 << getBitsPerComponent();
+// if (correctCount < num) {
+// byte[] fewerComps = new byte[correctCount * 3];
+//
+// System.arraycopy(components, 0, fewerComps, 0, correctCount * 3);
+//
+// components = fewerComps;
+// num = correctCount;
+// }
+// if (colorKeyMask == null || colorKeyMask.length == 0) {
+// return new IndexColorModel(getBitsPerComponent(), num, components,
+// 0, false);
+// } else {
+// byte[] aComps = new byte[num * 4];
+// int idx = 0;
+// for (int i = 0; i < num; i++) {
+// aComps[idx++] = components[(i * 3)];
+// aComps[idx++] = components[(i * 3) + 1];
+// aComps[idx++] = components[(i * 3) + 2];
+// aComps[idx++] = (byte) 0xFF;
+// }
+// for (int i = 0; i < colorKeyMask.length; i += 2) {
+// for (int j = colorKeyMask[i]; j <= colorKeyMask[i + 1]; j++) {
+// aComps[(j * 4) + 3] = 0; // make transparent
+// }
+// }
+// return new IndexColorModel(getBitsPerComponent(), num, aComps,
+// 0, true);
+// }
+// } else {
+// int[] bits = new int[cs.getNumComponents()];
+// for (int i = 0; i < bits.length; i++) {
+// bits[i] = getBitsPerComponent();
+// }
+//
+// return new DecodeComponentColorModel(cs.getColorSpace(), bits);
+// }
+// }
+
+ /**
+ * Normalize an array of values to match the decode array
+ */
+ private float[] normalize(byte[] pixels, float[] normComponents,
+ int normOffset) {
+ if (normComponents == null) {
+ normComponents = new float[normOffset + pixels.length];
+ }
+
+ float[] decodeArray = getDecode();
+
+ for (int i = 0; i < pixels.length; i++) {
+ int val = pixels[i] & 0xff;
+ int pow = ((int) Math.pow(2, getBitsPerComponent())) - 1;
+ float ymin = decodeArray[i * 2];
+ float ymax = decodeArray[(i * 2) + 1];
+
+ normComponents[normOffset + i] =
+ FunctionType0.interpolate(val, 0, pow, ymin, ymax);
+ }
+
+ return normComponents;
+ }
+
+// /**
+// * A wrapper for ComponentColorSpace which normalizes based on the
+// * decode array.
+// */
+// class DecodeComponentColorModel extends ComponentColorModel {
+//
+// public DecodeComponentColorModel(ColorSpace cs, int[] bpc) {
+// super(cs, bpc, false, false, Transparency.OPAQUE,
+// DataBuffer.TYPE_BYTE);
+//
+// if (bpc != null) {
+// pixel_bits = bpc.length * bpc[0];
+// }
+// }
+//
+// @Override
+// public SampleModel createCompatibleSampleModel(int width, int height) {
+// // workaround -- create a MultiPixelPackedSample models for
+// // single-sample, less than 8bpp color models
+// if (getNumComponents() == 1 && getPixelSize() < 8) {
+// return new MultiPixelPackedSampleModel(getTransferType(),
+// width,
+// height,
+// getPixelSize());
+// }
+//
+// return super.createCompatibleSampleModel(width, height);
+// }
+//
+// @Override
+// public boolean isCompatibleRaster(Raster raster) {
+// if (getNumComponents() == 1 && getPixelSize() < 8) {
+// SampleModel sm = raster.getSampleModel();
+//
+// if (sm instanceof MultiPixelPackedSampleModel) {
+// return (sm.getSampleSize(0) == getPixelSize());
+// } else {
+// return false;
+// }
+// }
+//
+// return super.isCompatibleRaster(raster);
+// }
+//
+// @Override
+// public float[] getNormalizedComponents(Object pixel,
+// float[] normComponents, int normOffset) {
+// if (getDecode() == null) {
+// return super.getNormalizedComponents(pixel, normComponents,
+// normOffset);
+// }
+//
+// return normalize((byte[]) pixel, normComponents, normOffset);
+// }
+// }
}
diff --git a/PdfView/src/main/java/com/sun/pdfview/PDFParser.java b/PdfView/src/main/java/com/sun/pdfview/PDFParser.java
index 397e165..93283bf 100644
--- a/PdfView/src/main/java/com/sun/pdfview/PDFParser.java
+++ b/PdfView/src/main/java/com/sun/pdfview/PDFParser.java
@@ -54,9 +54,7 @@ import com.sun.pdfview.font.PDFFont;
*/
public class PDFParser extends BaseWatchable {
- /**
- * emit a file of DCT stream data.
- */
+ /** emit a file of DCT stream data. */
public final static String DEBUG_DCTDECODE_DATA = "debugdctdecode";
static final boolean RELEASE = true;
static final int PDF_CMDS_RANGE1_MIN = 1;
@@ -78,13 +76,11 @@ public class PDFParser extends BaseWatchable {
private boolean resend = false;
private Tok tok;
private boolean catchexceptions; // Indicates state of BX...EX
- /**
- * a weak reference to the page we render into. For the page
+ /** a weak reference to the page we render into. For the page
* to remain available, some other code must retain a strong reference to it.
*/
private WeakReference pageRef;
- /**
- * the actual command, for use within a singe iteration. Note that
+ /** the actual command, for use within a singe iteration. Note that
* this must be released at the end of each iteration to assure the
* page can be collected if not in use
*/
@@ -92,7 +88,7 @@ public class PDFParser extends BaseWatchable {
// ---- result variables
byte[] stream;
HashMap resources;
- // public static int debuglevel = 4000;
+// public static int debuglevel = 4000;
// TODO [FHe]: changed for debugging
public static int debuglevel = -1;
@@ -125,7 +121,7 @@ public class PDFParser extends BaseWatchable {
* on to a PDFParser.
*/
public PDFParser(PDFPage cmds, byte[] stream,
- HashMap resources) {
+ HashMap resources) {
super();
this.pageRef = new WeakReference(cmds);
@@ -141,76 +137,43 @@ public class PDFParser extends BaseWatchable {
/////////////////////////////////////////////////////////////////
// B E G I N R E A D E R S E C T I O N
/////////////////////////////////////////////////////////////////
-
/**
* a token from a PDF Stream
*/
static class Tok {
- /**
- * begin bracket <
- */
+ /** begin bracket < */
public static final int BRKB = 11;
- /**
- * end bracket >
- */
+ /** end bracket > */
public static final int BRKE = 10;
- /**
- * begin array [
- */
+ /** begin array [ */
public static final int ARYB = 9;
- /**
- * end array ]
- */
+ /** end array ] */
public static final int ARYE = 8;
- /**
- * String (, readString looks for trailing )
- */
+ /** String (, readString looks for trailing ) */
public static final int STR = 7;
- /**
- * begin brace {
- */
+ /** begin brace { */
public static final int BRCB = 5;
- /**
- * end brace }
- */
+ /** end brace } */
public static final int BRCE = 4;
- /**
- * number
- */
+ /** number */
public static final int NUM = 3;
- /**
- * keyword
- */
+ /** keyword */
public static final int CMD = 2;
- /**
- * name (begins with /)
- */
+ /** name (begins with /) */
public static final int NAME = 1;
- /**
- * unknown token
- */
+ /** unknown token */
public static final int UNK = 0;
- /**
- * end of stream
- */
+ /** end of stream */
public static final int EOF = -1;
- /**
- * the string value of a STR, NAME, or CMD token
- */
+ /** the string value of a STR, NAME, or CMD token */
public String name;
- /**
- * the value of a NUM token
- */
+ /** the value of a NUM token */
public double value;
- /**
- * the type of the token
- */
+ /** the type of the token */
public int type;
- /**
- * a printable representation of the token
- */
+ /** a printable representation of the token */
@Override
public String toString() {
if (type == NUM) {
@@ -409,7 +372,7 @@ public class PDFParser extends BaseWatchable {
* character, which has already been read, and end with a balanced ')'
* character. A '\' character starts an escape sequence of up
* to three octal digits.
- *
+ *
*
Parenthesis must be enclosed by a balanced set of parenthesis,
* so a string may enclose balanced parenthesis.
*
@@ -421,7 +384,7 @@ public class PDFParser extends BaseWatchable {
int parenLevel = 0;
final StringBuffer sb = new StringBuffer();
- for (int to = stream_.length; loc < to; ) {
+ for (int to = stream_.length; loc < to;) {
int c = stream_[loc++];
if (c == ')') {
if (parenLevel-- == 0) {
@@ -462,7 +425,7 @@ public class PDFParser extends BaseWatchable {
* character, which has already been read, and end with a '>'
* character. Each byte in the array is made up of two hex characters,
* the first being the high-order bit.
- *
+ *
* We translate the byte arrays into char arrays by combining two bytes
* into a character, and then translate the character array into a string.
* [JK FIXME this is probably a really bad idea!]
@@ -477,7 +440,7 @@ public class PDFParser extends BaseWatchable {
char w = (char) 0;
// read individual bytes and format into a character array
- for (int to = stream_.length; (loc < to) && (stream_[loc] != '>'); ) {
+ for (int to = stream_.length; (loc < to) && (stream_[loc] != '>');) {
final char c = (char) stream_[loc];
byte b = (byte) 0;
@@ -515,7 +478,6 @@ public class PDFParser extends BaseWatchable {
/////////////////////////////////////////////////////////////////
// B E G I N P A R S E R S E C T I O N
/////////////////////////////////////////////////////////////////
-
/**
* Called to prepare for some iterations
*/
@@ -543,16 +505,16 @@ public class PDFParser extends BaseWatchable {
/**
* parse the stream. commands are added to the PDFPage initialized
* in the constructor as they are encountered.
- *
+ *
* Page numbers in comments refer to the Adobe PDF specification.
* commands are listed in PDF spec 32000-1:2008 in Table A.1
*
* @return
Watchable.RUNNING when there are commands to be processed
- *
Watchable.COMPLETED when the page is done and all
- * the commands have been processed
- *
Watchable.STOPPED if the page we are rendering into is
- * no longer available
- *
+ *
Watchable.COMPLETED when the page is done and all
+ * the commands have been processed
+ *
Watchable.STOPPED if the page we are rendering into is
+ * no longer available
+ *
*/
public int iterate() throws Exception {
// make sure the page is still available, and create the reference
@@ -700,7 +662,7 @@ public class PDFParser extends BaseWatchable {
path = new Path();
break;
case 'f':
- // the fall-through is intended!
+ // the fall-through is intended!
case 'F':
// fill the path (close/not close identical)
cmds.addPath(path, PDFShapeCmd.FILL | clip);
@@ -990,11 +952,11 @@ public class PDFParser extends BaseWatchable {
break;
case 'Q' + ('q' << 8):
processQCmd();
- // 'q'-cmd
- // push the parser state
- parserStates.push((ParserState) state.clone());
- // push graphics state
- cmds.addPush();
+ // 'q'-cmd
+ // push the parser state
+ parserStates.push((ParserState) state.clone());
+ // push graphics state
+ cmds.addPush();
break;
default:
if (catchexceptions) {
@@ -1063,7 +1025,6 @@ public class PDFParser extends BaseWatchable {
path = null;
cmds = null;
}
-
boolean errorwritten = false;
public void dumpStreamToError() {
@@ -1107,12 +1068,10 @@ public class PDFParser extends BaseWatchable {
/////////////////////////////////////////////////////////////////
// H E L P E R S
/////////////////////////////////////////////////////////////////
-
/**
* get a property from a named dictionary in the resources of this
* content stream.
- *
- * @param name the name of the property in the dictionary
+ * @param name the name of the property in the dictionary
* @param inDict the name of the dictionary in the resources
* @return the value of the property in the dictionary
*/
@@ -1133,7 +1092,6 @@ public class PDFParser extends BaseWatchable {
* Insert a PDF object into the command stream. The object must
* either be an Image or a Form, which is a set of PDF commands
* in a stream.
- *
* @param obj the object to insert, an Image or a Form.
*/
private void doXObject(PDFObject obj) throws IOException {
@@ -1153,9 +1111,8 @@ public class PDFParser extends BaseWatchable {
/**
* Parse image data into a Java BufferedImage and add the image
* command to the page.
- *
* @param obj contains the image data, and a dictionary describing
- * the width, height and color space of the image.
+ * the width, height and color space of the image.
*/
private void doImage(PDFObject obj) throws IOException {
cmds.addImage(PDFImage.createImage(obj, resources));
@@ -1165,9 +1122,8 @@ public class PDFParser extends BaseWatchable {
* Inject a stream of PDF commands onto the page. Optimized to cache
* a parsed stream of commands, so that each Form object only needs
* to be parsed once.
- *
* @param obj a stream containing the PDF commands, a transformation
- * matrix, bounding box, and resources.
+ * matrix, bounding box, and resources.
*/
private void doForm(PDFObject obj) throws IOException {
// check to see if we've already parsed this sucker
@@ -1230,7 +1186,6 @@ public class PDFParser extends BaseWatchable {
//
// return patternSpace.getPaint(pattern, components, resources);
// }
-
/**
* Parse the next object out of the PDF stream. This could be a
* Double, a String, a HashMap (dictionary), Object[] array, or
@@ -1243,13 +1198,13 @@ public class PDFParser extends BaseWatchable {
case Tok.NUM:
return new Double(tok.value);
case Tok.STR:
- // the fall-through is intended!
+ // the fall-through is intended!
case Tok.NAME:
return tok.name;
case Tok.BRKB: {
final HashMap hm = new HashMap();
String name = null;
- for (Object obj = null; (obj = parseObject()) != null; ) {
+ for (Object obj = null; (obj = parseObject()) != null;) {
if (name == null) {
name = (String) obj;
} else {
@@ -1265,7 +1220,7 @@ public class PDFParser extends BaseWatchable {
case Tok.ARYB: {
// build an array
final ArrayList