true if greyscale images will be converted to ARGB
- */
- public boolean isConvertGreyscaleImagesToArgb() {
- return convertGreyscaleImagesToArgb;
- }
+ /**
+ * Returns true if greyscale images will be converted to ARGB
+ */
+ public boolean isConvertGreyscaleImagesToArgb() {
+ return convertGreyscaleImagesToArgb;
+ }
- /**
- * If an image is higher than the given size (in pixels) then
- * the image will be rendered in chunks, rather than as one big image.
- * This may lead to lower memory consumption for e.g. scanned PDFs with
- * large images.
- *
- * Set to a value <= 0 to disable banded image rendering.
- * Defaults to 0 (off)
- *
- * @param aSize the height threshold at which to enable banded image rendering
- */
- public void setThresholdForBandedImageRendering(int aSize) {
- thresholdForBandedImageRendering = aSize;
- }
+ /**
+ * If an image is higher than the given size (in pixels) then
+ * the image will be rendered in chunks, rather than as one big image.
+ * This may lead to lower memory consumption for e.g. scanned PDFs with
+ * large images.
+ *
+ * Set to a value <= 0 to disable banded image rendering.
+ * Defaults to 0 (off)
+ *
+ * @param aSize the height threshold at which to enable banded image rendering
+ */
+ public void setThresholdForBandedImageRendering(int aSize) {
+ thresholdForBandedImageRendering = aSize;
+ }
- /**
- * Returns the image height threshold at which to enable banded image rendering.
- * @return the threshold value, or a value <= 0 if banded rendering is disabled
- */
- public int getThresholdForBandedImageRendering() {
- return thresholdForBandedImageRendering;
- }
+ /**
+ * Returns the image height threshold at which to enable banded image rendering.
+ * @return the threshold value, or a value <= 0 if banded rendering is disabled
+ */
+ public int getThresholdForBandedImageRendering() {
+ return thresholdForBandedImageRendering;
+ }
- /**
- * Is the color converting op switched on or off?
- * @return - the usage of this color convert op
- */
+ /**
+ * Is the color converting op switched on or off?
+ * @return - the usage of this color convert op
+ */
public boolean isAvoidColorConvertOp() {
return avoidColorConvertOp;
}
@@ -131,14 +131,14 @@ public void setUseBlurResizingForImages(boolean useBlurResizingForImages) {
* @param printSignatureField
*/
public void setPrintSignatureFields(boolean printSignatureFields) {
- this.printSigantureFields = printSignatureFields;
+ this.printSigantureFields = printSignatureFields;
}
/**
* @return true if signature fields will be printed on pdf
*/
public boolean isPrintSignatureFields() {
- return this.printSigantureFields;
+ return this.printSigantureFields;
}
/**
@@ -147,14 +147,14 @@ public boolean isPrintSignatureFields() {
* @param printStampAnnotation
*/
public void setPrintStampAnnotations(boolean printStampAnnotations) {
- this.printStampAnnotations = printStampAnnotations;
+ this.printStampAnnotations = printStampAnnotations;
}
/**
* @return true if stamp annotations will be printed on pdf
*/
public boolean isPrintStampAnnotations() {
- return this.printStampAnnotations;
+ return this.printStampAnnotations;
}
/**
@@ -163,15 +163,15 @@ public boolean isPrintStampAnnotations() {
* @param printWidgetAnnotations
*/
public void setPrintWidgetAnnotations(boolean printWidgetAnnotations) {
- this.printWidgetAnnotations = printWidgetAnnotations;
+ this.printWidgetAnnotations = printWidgetAnnotations;
}
/**
* @return true if widget annotations will be printed on pdf
*/
public boolean isPrintWidgetAnnotations() {
- return this.printWidgetAnnotations;
- }
+ return this.printWidgetAnnotations;
+ }
/**
* Print freetext annotations on pdf
@@ -179,14 +179,14 @@ public boolean isPrintWidgetAnnotations() {
* @param printFreetextAnnotations
*/
public void setPrintFreetextAnnotations(boolean printFreetextAnnotations) {
- this.printFreetextAnnotations = printFreetextAnnotations;
+ this.printFreetextAnnotations = printFreetextAnnotations;
}
/**
* @return true if freetext annotations will be printed on pdf
*/
public boolean isPrintFreetextAnnotations() {
- return this.printFreetextAnnotations;
+ return this.printFreetextAnnotations;
}
/**
@@ -195,13 +195,13 @@ public boolean isPrintFreetextAnnotations() {
* @param printLinkAnnotations
*/
public void setPrintLinkAnnotations(boolean printLinkAnnotations) {
- this.printLinkAnnotations = printLinkAnnotations;
+ this.printLinkAnnotations = printLinkAnnotations;
}
/**
* @return true if link annotations will be printed on pdf
*/
public boolean isPrintLinkAnnotations() {
- return this.printLinkAnnotations;
+ return this.printLinkAnnotations;
}
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/Identity8BitCharsetEncoder.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/Identity8BitCharsetEncoder.java
index e934c49c2..0eee52c1a 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/Identity8BitCharsetEncoder.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/Identity8BitCharsetEncoder.java
@@ -38,7 +38,7 @@ public Identity8BitCharsetEncoder() {
}
@Override
- protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
while (in.remaining() > 0) {
if (out.remaining() < 1) {
return CoderResult.OVERFLOW;
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/NameTree.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/NameTree.java
index 6f18fbcbc..d3e29bb5a 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/NameTree.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/NameTree.java
@@ -110,12 +110,12 @@ private PDFObject findInArray(PDFObject[] array, String key)
int comp = key.compareTo(posKey);
if (comp == 0) {
// they match. Return the value
- int tmp = (pos * 2) + 1;
- if(array.length>tmp){
+ int tmp = (pos * 2) + 1;
+ if(array.length>tmp){
return array[tmp];
- }else {
- return null;
- }
+ }else {
+ return null;
+ }
} else if (comp > 0) {
// too big, search the top half of the tree
start = pos + 1;
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDestination.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDestination.java
index beb2875a2..7756b8dce 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDestination.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDestination.java
@@ -35,265 +35,265 @@
*/
public class PDFDestination {
- /** The known types of destination */
- public static final int XYZ = 0;
- public static final int FIT = 1;
- public static final int FITH = 2;
- public static final int FITV = 3;
- public static final int FITR = 4;
- public static final int FITB = 5;
- public static final int FITBH = 6;
- public static final int FITBV = 7;
- /** the type of this destination (from the list above) */
- private int type;
- /** the page we refer to */
- private PDFObject pageObj;
- /** the left coordinate of the fit area, if applicable */
- private float left;
- /** the right coordinate of the fit area, if applicable */
- private float right;
- /** the top coordinate of the fit area, if applicable */
- private float top;
- /** the bottom coordinate of the fit area, if applicable */
- private float bottom;
- /** the zoom, if applicable */
- private float zoom;
+ /** The known types of destination */
+ public static final int XYZ = 0;
+ public static final int FIT = 1;
+ public static final int FITH = 2;
+ public static final int FITV = 3;
+ public static final int FITR = 4;
+ public static final int FITB = 5;
+ public static final int FITBH = 6;
+ public static final int FITBV = 7;
+ /** the type of this destination (from the list above) */
+ private int type;
+ /** the page we refer to */
+ private PDFObject pageObj;
+ /** the left coordinate of the fit area, if applicable */
+ private float left;
+ /** the right coordinate of the fit area, if applicable */
+ private float right;
+ /** the top coordinate of the fit area, if applicable */
+ private float top;
+ /** the bottom coordinate of the fit area, if applicable */
+ private float bottom;
+ /** the zoom, if applicable */
+ private float zoom;
- /**
- * Creates a new instance of PDFDestination
- *
- * @param pageObj
- * the page object this destination refers to
- * @param type
- * the type of page this object refers to
- */
- protected PDFDestination(PDFObject pageObj, int type) {
- this.pageObj = pageObj;
- this.type = type;
- }
+ /**
+ * Creates a new instance of PDFDestination
+ *
+ * @param pageObj
+ * the page object this destination refers to
+ * @param type
+ * the type of page this object refers to
+ */
+ protected PDFDestination(PDFObject pageObj, int type) {
+ this.pageObj = pageObj;
+ this.type = type;
+ }
- /**
- * Get a destination from either an array (explicit destination), a name
- * (named destination) or a string (name tree destination).
- *
- * @param obj
- * the PDFObject representing this destination
- * @param root
- * the root of the PDF object tree
- */
- public static PDFDestination getDestination(PDFObject obj, PDFObject root) throws IOException {
- // resolve string and name issues
- if (obj.getType() == PDFObject.NAME) {
- obj = getDestFromName(obj, root);
- } else if (obj.getType() == PDFObject.STRING) {
- obj = getDestFromString(obj, root);
- }
+ /**
+ * Get a destination from either an array (explicit destination), a name
+ * (named destination) or a string (name tree destination).
+ *
+ * @param obj
+ * the PDFObject representing this destination
+ * @param root
+ * the root of the PDF object tree
+ */
+ public static PDFDestination getDestination(PDFObject obj, PDFObject root) throws IOException {
+ // resolve string and name issues
+ if (obj.getType() == PDFObject.NAME) {
+ obj = getDestFromName(obj, root);
+ } else if (obj.getType() == PDFObject.STRING) {
+ obj = getDestFromString(obj, root);
+ }
- // make sure we have the right kind of object
- if (obj == null || obj.getType() != PDFObject.ARRAY) {
- throw new PDFParseException("Can't create destination from: " + obj);
- }
+ // make sure we have the right kind of object
+ if (obj == null || obj.getType() != PDFObject.ARRAY) {
+ throw new PDFParseException("Can't create destination from: " + obj);
+ }
- // the array is in the form [page type args ... ]
- PDFObject[] destArray = obj.getArray();
+ // the array is in the form [page type args ... ]
+ PDFObject[] destArray = obj.getArray();
- // create the destination based on the type
- PDFDestination dest = null;
- String type = destArray[1].getStringValue();
- if (type.equals("XYZ")) {
- dest = new PDFDestination(destArray[0], XYZ);
- } else if (type.equals("Fit")) {
- dest = new PDFDestination(destArray[0], FIT);
- } else if (type.equals("FitH")) {
- dest = new PDFDestination(destArray[0], FITH);
- } else if (type.equals("FitV")) {
- dest = new PDFDestination(destArray[0], FITV);
- } else if (type.equals("FitR")) {
- dest = new PDFDestination(destArray[0], FITR);
- } else if (type.equals("FitB")) {
- dest = new PDFDestination(destArray[0], FITB);
- } else if (type.equals("FitBH")) {
- dest = new PDFDestination(destArray[0], FITBH);
- } else if (type.equals("FitBV")) {
- dest = new PDFDestination(destArray[0], FITBV);
- } else {
- throw new PDFParseException("Unknown destination type: " + type);
- }
+ // create the destination based on the type
+ PDFDestination dest = null;
+ String type = destArray[1].getStringValue();
+ if (type.equals("XYZ")) {
+ dest = new PDFDestination(destArray[0], XYZ);
+ } else if (type.equals("Fit")) {
+ dest = new PDFDestination(destArray[0], FIT);
+ } else if (type.equals("FitH")) {
+ dest = new PDFDestination(destArray[0], FITH);
+ } else if (type.equals("FitV")) {
+ dest = new PDFDestination(destArray[0], FITV);
+ } else if (type.equals("FitR")) {
+ dest = new PDFDestination(destArray[0], FITR);
+ } else if (type.equals("FitB")) {
+ dest = new PDFDestination(destArray[0], FITB);
+ } else if (type.equals("FitBH")) {
+ dest = new PDFDestination(destArray[0], FITBH);
+ } else if (type.equals("FitBV")) {
+ dest = new PDFDestination(destArray[0], FITBV);
+ } else {
+ throw new PDFParseException("Unknown destination type: " + type);
+ }
- // now fill in the arguments based on the type
- switch (dest.getType()) {
- case XYZ:
- dest.setLeft(destArray[2].getFloatValue());
- dest.setTop(destArray[3].getFloatValue());
- dest.setZoom(destArray[4].getFloatValue());
- break;
- case FITH:
- if (destArray.length > 2) {
- dest.setTop(destArray[2].getFloatValue());
- } else {
- dest.setTop(0.0F);
- }
- break;
- case FITV:
- if (destArray.length > 2) {
- dest.setTop(destArray[2].getFloatValue());
- } else {
- dest.setTop(0.0F);
- }
- break;
- case FITR:
- dest.setLeft(destArray[2].getFloatValue());
- dest.setBottom(destArray[3].getFloatValue());
- dest.setRight(destArray[4].getFloatValue());
- dest.setTop(destArray[5].getFloatValue());
- break;
- case FITBH:
- if (destArray.length > 2) {
- dest.setTop(destArray[2].getFloatValue());
- } else {
- dest.setTop(0.0F);
- }
- break;
- case FITBV:
- if (destArray.length > 2) {
- dest.setTop(destArray[2].getFloatValue());
- } else {
- dest.setTop(0.0F);
- }
- break;
- }
+ // now fill in the arguments based on the type
+ switch (dest.getType()) {
+ case XYZ:
+ dest.setLeft(destArray[2].getFloatValue());
+ dest.setTop(destArray[3].getFloatValue());
+ dest.setZoom(destArray[4].getFloatValue());
+ break;
+ case FITH:
+ if (destArray.length > 2) {
+ dest.setTop(destArray[2].getFloatValue());
+ } else {
+ dest.setTop(0.0F);
+ }
+ break;
+ case FITV:
+ if (destArray.length > 2) {
+ dest.setTop(destArray[2].getFloatValue());
+ } else {
+ dest.setTop(0.0F);
+ }
+ break;
+ case FITR:
+ dest.setLeft(destArray[2].getFloatValue());
+ dest.setBottom(destArray[3].getFloatValue());
+ dest.setRight(destArray[4].getFloatValue());
+ dest.setTop(destArray[5].getFloatValue());
+ break;
+ case FITBH:
+ if (destArray.length > 2) {
+ dest.setTop(destArray[2].getFloatValue());
+ } else {
+ dest.setTop(0.0F);
+ }
+ break;
+ case FITBV:
+ if (destArray.length > 2) {
+ dest.setTop(destArray[2].getFloatValue());
+ } else {
+ dest.setTop(0.0F);
+ }
+ break;
+ }
- return dest;
- }
+ return dest;
+ }
- /**
- * Get the type of this destination
- */
- public int getType() {
- return this.type;
- }
+ /**
+ * Get the type of this destination
+ */
+ public int getType() {
+ return this.type;
+ }
- /**
- * Get the PDF Page object associated with this destination
- */
- public PDFObject getPage() {
- return this.pageObj;
- }
+ /**
+ * Get the PDF Page object associated with this destination
+ */
+ public PDFObject getPage() {
+ return this.pageObj;
+ }
- /**
- * Get the left coordinate value
- */
- public float getLeft() {
- return this.left;
- }
+ /**
+ * Get the left coordinate value
+ */
+ public float getLeft() {
+ return this.left;
+ }
- /**
- * Set the left coordinate value
- */
- public void setLeft(float left) {
- this.left = left;
- }
+ /**
+ * Set the left coordinate value
+ */
+ public void setLeft(float left) {
+ this.left = left;
+ }
- /**
- * Get the right coordinate value
- */
- public float getRight() {
- return this.right;
- }
+ /**
+ * Get the right coordinate value
+ */
+ public float getRight() {
+ return this.right;
+ }
- /**
- * Set the right coordinate value
- */
- public void setRight(float right) {
- this.right = right;
- }
+ /**
+ * Set the right coordinate value
+ */
+ public void setRight(float right) {
+ this.right = right;
+ }
- /**
- * Get the top coordinate value
- */
- public float getTop() {
- return this.top;
- }
+ /**
+ * Get the top coordinate value
+ */
+ public float getTop() {
+ return this.top;
+ }
- /**
- * Set the top coordinate value
- */
- public void setTop(float top) {
- this.top = top;
- }
+ /**
+ * Set the top coordinate value
+ */
+ public void setTop(float top) {
+ this.top = top;
+ }
- /**
- * Get the bottom coordinate value
- */
- public float getBottom() {
- return this.bottom;
- }
+ /**
+ * Get the bottom coordinate value
+ */
+ public float getBottom() {
+ return this.bottom;
+ }
- /**
- * Set the bottom coordinate value
- */
- public void setBottom(float bottom) {
- this.bottom = bottom;
- }
+ /**
+ * Set the bottom coordinate value
+ */
+ public void setBottom(float bottom) {
+ this.bottom = bottom;
+ }
- /**
- * Get the zoom value
- */
- public float getZoom() {
- return this.zoom;
- }
+ /**
+ * Get the zoom value
+ */
+ public float getZoom() {
+ return this.zoom;
+ }
- /**
- * Set the zoom value
- */
- public void setZoom(float zoom) {
- this.zoom = zoom;
- }
+ /**
+ * Set the zoom value
+ */
+ public void setZoom(float zoom) {
+ this.zoom = zoom;
+ }
- /**
- * Get a destination, given a name. This means the destination is in the
- * root node's dests dictionary.
- */
- private static PDFObject getDestFromName(PDFObject name, PDFObject root) throws IOException {
- // find the dests object in the root node
- PDFObject dests = root.getDictRef("Dests");
- if (dests != null) {
- // find this name in the dests dictionary
- return dests.getDictRef(name.getStringValue());
- }
+ /**
+ * Get a destination, given a name. This means the destination is in the
+ * root node's dests dictionary.
+ */
+ private static PDFObject getDestFromName(PDFObject name, PDFObject root) throws IOException {
+ // find the dests object in the root node
+ PDFObject dests = root.getDictRef("Dests");
+ if (dests != null) {
+ // find this name in the dests dictionary
+ return dests.getDictRef(name.getStringValue());
+ }
- // not found
- return null;
- }
+ // not found
+ return null;
+ }
- /**
- * Get a destination, given a string. This means the destination is in the
- * root node's names dictionary.
- */
- private static PDFObject getDestFromString(PDFObject str, PDFObject root) throws IOException {
- // find the names object in the root node
- PDFObject names = root.getDictRef("Names");
- if (names != null) {
- // find the dests entry in the names dictionary
- PDFObject dests = names.getDictRef("Dests");
- if (dests != null) {
- // create a name tree object
- NameTree tree = new NameTree(dests);
+ /**
+ * Get a destination, given a string. This means the destination is in the
+ * root node's names dictionary.
+ */
+ private static PDFObject getDestFromString(PDFObject str, PDFObject root) throws IOException {
+ // find the names object in the root node
+ PDFObject names = root.getDictRef("Names");
+ if (names != null) {
+ // find the dests entry in the names dictionary
+ PDFObject dests = names.getDictRef("Dests");
+ if (dests != null) {
+ // create a name tree object
+ NameTree tree = new NameTree(dests);
- // find the value we're looking for
- PDFObject obj = tree.find(str.getStringValue());
+ // find the value we're looking for
+ PDFObject obj = tree.find(str.getStringValue());
- // if we get back a dictionary, look for the /D value
- if (obj != null && obj.getType() == PDFObject.DICTIONARY) {
- obj = obj.getDictRef("D");
- }
+ // if we get back a dictionary, look for the /D value
+ if (obj != null && obj.getType() == PDFObject.DICTIONARY) {
+ obj = obj.getDictRef("D");
+ }
- // found it
- return obj;
- }
- }
+ // found it
+ return obj;
+ }
+ }
- // not found
- return null;
- }
+ // not found
+ return null;
+ }
}
\ No newline at end of file
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDocCharsetEncoder.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDocCharsetEncoder.java
index 12cae51bc..f60f83d48 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDocCharsetEncoder.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFDocCharsetEncoder.java
@@ -78,7 +78,7 @@ public PDFDocCharsetEncoder() {
}
@Override
- protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
while (in.remaining() > 0) {
if (out.remaining() < 1) {
return CoderResult.OVERFLOW;
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFFile.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFFile.java
index 517def048..72ae46e4e 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFFile.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFFile.java
@@ -111,11 +111,11 @@ public class PDFFile {
* protected and requires a password
*/
public PDFFile(ByteBuffer buf) throws IOException {
- this(buf, null);
+ this(buf, null);
}
public PDFFile(ByteBuffer buf, boolean doNotParse) throws IOException {
- this.buf = buf;
+ this.buf = buf;
}
/**
@@ -247,48 +247,48 @@ public synchronized PDFObject dereference(PDFXref ref, PDFDecrypter decrypter)
boolean compressed = this.objIdx[id].getCompressed();
if (!compressed) {
- int loc = this.objIdx[id].getFilePos();
- if (loc < 0) {
- return PDFObject.nullObj;
- }
-
- // move to where this object is
- this.buf.position(loc);
-
- // read the object and cache the reference
- obj= readObject(ref.getID(), ref.getGeneration(), decrypter);
+ int loc = this.objIdx[id].getFilePos();
+ if (loc < 0) {
+ return PDFObject.nullObj;
+ }
+
+ // move to where this object is
+ this.buf.position(loc);
+
+ // read the object and cache the reference
+ obj= readObject(ref.getID(), ref.getGeneration(), decrypter);
}
else { // compressed
- int compId = this.objIdx[id].getID();
- int idx = this.objIdx[id].getIndex();
- if (idx < 0)
- return PDFObject.nullObj;
- PDFXref compRef = new PDFXref(compId, 0);
- PDFObject compObj = dereference(compRef, decrypter);
- int first = compObj.getDictionary().get("First").getIntValue();
- int length = compObj.getDictionary().get("Length").getIntValue();
- int n = compObj.getDictionary().get("N").getIntValue();
- if (idx >= n)
- return PDFObject.nullObj;
- ByteBuffer strm = compObj.getStreamBuffer();
-
- ByteBuffer oldBuf = this.buf;
- this.buf = strm;
- // skip other nums
- for (int i=0; i- * 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.
RGB - // transformation when reading JPEGs does not adhere to the spec. - // We're just going to let java read this in - as it is, the - // standard - // jpeg reader looks for the specific Adobe marker header so that - // it may apply the transform, so that's good. If that marker - // isn't present, then it also applies a number of other heuristics - // to determine whether the transform should be applied. - // (http://java.sun.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html) - // In practice, it probably almost always does the right thing here, - // though note that the present or default value of the - // ColorTransform - // dictionary entry is not being observed, so there is scope for - // error. Hopefully the JAI reader does the same. - - // We might need to attempt this with multiple readers, so let's - // remember where the jpeg data starts - jpegData.mark(); - - JpegDecoder decoder = new JpegDecoder(jpegData, cm); - - IOException decodeEx = null; - try { - bi = decoder.decode(); - } catch (IOException e) { - decodeEx = e; - // The native readers weren't able to process the image. - // One common situation is that the image is YCCK/CMYK encoded, - // which isn't supported by the default jpeg readers. - // We've got a work-around we can attempt, though: - decoder.ycckcmykDecodeMode(true); - try { - bi = decoder.decode(); - } catch (IOException e2) { - // It probably wasn't the YCCK/CMYK issue! - // try the "old" implementation - bi = parseData(data, null); - return bi; - } - } - - // the decoder may have requested installation of a new color model - cm = decoder.getColorModel(); - - // make these immediately unreachable, as the referenced - // jpeg data might be quite large - jpegData = null; - decoder = null; - - if (bi == null) { - // This isn't pretty, but it's what's been happening - // previously, so we'll preserve it for the time - // being. At least we'll offer a hint now! - assert decodeEx != null; - throw new IOException(decodeEx.getMessage() + ". Maybe installing JAI for expanded image format " - + "support would help?", decodeEx); - } - } else { - // create the data buffer - DataBuffer db = new DataBufferByte(data, data.length); - - // pick a color model, based on the number of components and - // bits per component - cm = getColorModel(); - - // create a compatible raster - SampleModel sm = cm.createCompatibleSampleModel(getWidth(), getHeight()); - WritableRaster raster; - try { - raster = Raster.createWritableRaster(sm, db, new Point(0, 0)); - } catch (RasterFormatException e) { - int tempExpectedSize = getWidth() * getHeight() * getColorSpace().getNumComponents() - * Math.max(8, getBitsPerComponent()) / 8; - - if (tempExpectedSize < 3) { - tempExpectedSize = 3; - } - if (tempExpectedSize > 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 (?) - */ - 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 if (cm.getPixelSize() == 1 && cm.getNumComponents() == 1) { - // If the image is black and white only, convert it into - // BYTE_GRAY - // format - // This is a lot faster compared to just drawing the original - // image - - // Are pixels decoded? - int[] cc = new int[] { 0, 1 }; - PDFObject o = imageObj.getDictRef("Decode"); - if (o != null && o.getAt(0) != null) { - cc[0] = o.getAt(0).getIntValue(); - cc[1] = o.getAt(1).getIntValue(); - } - - final byte[] ncc = new byte[] { (byte) -cc[0], (byte) -cc[1] }; - - bi = biColorToGrayscale(raster, ncc); - // Return when there is no SMask - if (getSMask() == null) - return bi; - } else { - // Raster is already in a format which is supported by Java2D, - // such as RGB or Gray. - bi = new BufferedImage(cm, raster, true, null); - } - } - - // hack to avoid *very* slow conversion - ColorSpace cs = cm.getColorSpace(); - ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); - if (isGreyscale(cs) && bpc <= 8 && getDecode() == null && jpegData == null - && Configuration.getInstance().isConvertGreyscaleImagesToArgb()) { - bi = convertGreyscaleToArgb(data, bi); - } else if (!isImageMask() && cs instanceof ICC_ColorSpace && !cs.equals(rgbCS) - && !Configuration.getInstance().isAvoidColorConvertOp()) { - ColorConvertOp op = new ColorConvertOp(cs, rgbCS, null); - - BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - - bi = op.filter(bi, converted); - } - else if (cs.getType() == ColorSpace.TYPE_CMYK) { - // convert to ARGB for faster drawing without ColorConvertOp - BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = converted.createGraphics(); - graphics.drawImage(bi,0,0,null); - graphics.dispose(); - bi = converted; - } - - // add in the alpha data supplied by the SMask, if any - PDFImage sMaskImage = getSMask(); - if (sMaskImage != null) { - BufferedImage si = null; - try { - int w = bi.getWidth(); - int h = bi.getHeight(); - // if the bitmap is only a few pixels it just defines the color - boolean maskOnly = (w <= 2); - if (maskOnly) { - // use size of mask - si = sMaskImage.getImage(); - w = si.getWidth(); - h = si.getHeight(); - } - else if (sMaskImage.getHeight() != h && sMaskImage.getWidth() != w) { - // in case the two images do not have the same size, scale - if (sMaskImage.getHeight()*sMaskImage.getWidth() > w*h) { - // upscale image - si = sMaskImage.getImage(); - w = si.getWidth(); - h = si.getHeight(); - int hints = Image.SCALE_FAST; - Image scaledInstance = bi.getScaledInstance(w, h, hints ); - bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics graphics = bi.createGraphics(); - graphics.drawImage(scaledInstance, 0, 0, null); - graphics.dispose(); - } - else { - // upscale mask - si = scaleSMaskImage(sMaskImage); - } - } - else { - si = sMaskImage.getImage(); - } - PDFDebugger.debugImage(si, "smask" + this.imageObj.getObjNum()); - - BufferedImage outImage = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB); - PDFDebugger.debugImage(si, "outImage" + this.imageObj.getObjNum()); - int[] srcArray = new int[w]; - int[] maskArray = new int[w]; + private static int[][] GREY_TO_ARGB = new int[8][]; + + /** + * 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 final PDFObject imageObj; + /** true if the image is in encoded in JPEG*/ + private final boolean jpegDecode; - for (int i = 0; i < h; i++) { - if (maskOnly) { - // use first pixel color from image - Arrays.fill(srcArray, bi.getRGB(0,0)); - } - else { - // pixel row from image - bi.getRGB(0, i, w, 1, srcArray, 0, w); - } - // pixel row from mask - si.getRGB(0, i, w, 1, maskArray, 0, w); + /** + * Create an instance of a PDFImage + * @throws IOException if {@link PDFDecoder} throws one while evaluating if the image is a Jpeg + */ + protected PDFImage(PDFObject imageObj) throws IOException { + this.imageObj = imageObj; + this.jpegDecode = PDFDecoder.isLastFilter(imageObj, PDFDecoder.DCT_FILTERS); + } + + /** + * 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 + * @param useAsSMask + * - flag for switching colors in case image is used as sMask + * internally this is needed for handling transparency in smask + * images. + */ + public static PDFImage createImage(PDFObject obj, Map+ * 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.
RGB + // transformation when reading JPEGs does not adhere to the spec. + // We're just going to let java read this in - as it is, the + // standard + // jpeg reader looks for the specific Adobe marker header so that + // it may apply the transform, so that's good. If that marker + // isn't present, then it also applies a number of other heuristics + // to determine whether the transform should be applied. + // (http://java.sun.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html) + // In practice, it probably almost always does the right thing here, + // though note that the present or default value of the + // ColorTransform + // dictionary entry is not being observed, so there is scope for + // error. Hopefully the JAI reader does the same. + + // We might need to attempt this with multiple readers, so let's + // remember where the jpeg data starts + jpegData.mark(); + + JpegDecoder decoder = new JpegDecoder(jpegData, cm); + + IOException decodeEx = null; + try { + bi = decoder.decode(); + } catch (IOException e) { + decodeEx = e; + // The native readers weren't able to process the image. + // One common situation is that the image is YCCK/CMYK encoded, + // which isn't supported by the default jpeg readers. + // We've got a work-around we can attempt, though: + decoder.ycckcmykDecodeMode(true); + try { + bi = decoder.decode(); + } catch (IOException e2) { + // It probably wasn't the YCCK/CMYK issue! + // try the "old" implementation + bi = parseData(data, null); + return bi; + } + } + + // the decoder may have requested installation of a new color model + cm = decoder.getColorModel(); + + // make these immediately unreachable, as the referenced + // jpeg data might be quite large + jpegData = null; + decoder = null; + + if (bi == null) { + // This isn't pretty, but it's what's been happening + // previously, so we'll preserve it for the time + // being. At least we'll offer a hint now! + assert decodeEx != null; + throw new IOException(decodeEx.getMessage() + ". Maybe installing JAI for expanded image format " + + "support would help?", decodeEx); + } + } else { + // create the data buffer + DataBuffer db = new DataBufferByte(data, data.length); + + // pick a color model, based on the number of components and + // bits per component + cm = getColorModel(); + + // create a compatible raster + SampleModel sm = cm.createCompatibleSampleModel(getWidth(), getHeight()); + WritableRaster raster; + try { + raster = Raster.createWritableRaster(sm, db, new Point(0, 0)); + } catch (RasterFormatException e) { + int tempExpectedSize = getWidth() * getHeight() * getColorSpace().getNumComponents() + * Math.max(8, getBitsPerComponent()) / 8; + + if (tempExpectedSize < 3) { + tempExpectedSize = 3; + } + if (tempExpectedSize > 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 (?) + */ + 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 if (cm.getPixelSize() == 1 && cm.getNumComponents() == 1) { + // If the image is black and white only, convert it into + // BYTE_GRAY + // format + // This is a lot faster compared to just drawing the original + // image + + // Are pixels decoded? + int[] cc = new int[] { 0, 1 }; + PDFObject o = imageObj.getDictRef("Decode"); + if (o != null && o.getAt(0) != null) { + cc[0] = o.getAt(0).getIntValue(); + cc[1] = o.getAt(1).getIntValue(); + } + + final byte[] ncc = new byte[] { (byte) -cc[0], (byte) -cc[1] }; + + bi = biColorToGrayscale(raster, ncc); + // Return when there is no SMask + if (getSMask() == null) + return bi; + } else { + // Raster is already in a format which is supported by Java2D, + // such as RGB or Gray. + bi = new BufferedImage(cm, raster, true, null); + } + } + + // hack to avoid *very* slow conversion + ColorSpace cs = cm.getColorSpace(); + ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); + if (isGreyscale(cs) && bpc <= 8 && getDecode() == null && jpegData == null + && Configuration.getInstance().isConvertGreyscaleImagesToArgb()) { + bi = convertGreyscaleToArgb(data, bi); + } else if (!isImageMask() && cs instanceof ICC_ColorSpace && !cs.equals(rgbCS) + && !Configuration.getInstance().isAvoidColorConvertOp()) { + ColorConvertOp op = new ColorConvertOp(cs, rgbCS, null); + + BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); + + bi = op.filter(bi, converted); + } + else if (cs.getType() == ColorSpace.TYPE_CMYK) { + // convert to ARGB for faster drawing without ColorConvertOp + BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = converted.createGraphics(); + graphics.drawImage(bi,0,0,null); + graphics.dispose(); + bi = converted; + } + + // add in the alpha data supplied by the SMask, if any + PDFImage sMaskImage = getSMask(); + if (sMaskImage != null) { + BufferedImage si = null; + try { + int w = bi.getWidth(); + int h = bi.getHeight(); + // if the bitmap is only a few pixels it just defines the color + boolean maskOnly = (w <= 2); + if (maskOnly) { + // use size of mask + si = sMaskImage.getImage(); + w = si.getWidth(); + h = si.getHeight(); + } + else if (sMaskImage.getHeight() != h && sMaskImage.getWidth() != w) { + // in case the two images do not have the same size, scale + if (sMaskImage.getHeight()*sMaskImage.getWidth() > w*h) { + // upscale image + si = sMaskImage.getImage(); + w = si.getWidth(); + h = si.getHeight(); + int hints = Image.SCALE_FAST; + Image scaledInstance = bi.getScaledInstance(w, h, hints ); + bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics graphics = bi.createGraphics(); + graphics.drawImage(scaledInstance, 0, 0, null); + graphics.dispose(); + } + else { + // upscale mask + si = scaleSMaskImage(sMaskImage); + } + } + else { + si = sMaskImage.getImage(); + } + PDFDebugger.debugImage(si, "smask" + this.imageObj.getObjNum()); + + BufferedImage outImage = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB); + PDFDebugger.debugImage(si, "outImage" + this.imageObj.getObjNum()); + int[] srcArray = new int[w]; + int[] maskArray = new int[w]; - for (int j = 0; j < w; j++) { - int ac = 0xff000000; - // alpha from mask with color from image - maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac); - } - // write pixel row - outImage.setRGB(0, i, w, 1, maskArray, 0, w); - } + for (int i = 0; i < h; i++) { + if (maskOnly) { + // use first pixel color from image + Arrays.fill(srcArray, bi.getRGB(0,0)); + } + else { + // pixel row from image + bi.getRGB(0, i, w, 1, srcArray, 0, w); + } + // pixel row from mask + si.getRGB(0, i, w, 1, maskArray, 0, w); - bi = outImage; + for (int j = 0; j < w; j++) { + int ac = 0xff000000; + // alpha from mask with color from image + maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac); + } + // write pixel row + outImage.setRGB(0, i, w, 1, maskArray, 0, w); + } + + bi = outImage; } catch (PDFImageParseException e) { PDFDebugger.debug("Error parsing sMask image caused by:" + e.getMessage(), 100); } - } - - PDFDebugger.debugImage(bi, "result" + this.imageObj.getObjNum()); - return bi; - } - - /** - * Scale the softmask image to the size of the actual image - * - * @param sMaskImage - * @return - * @throws PDFImageParseException - */ - private BufferedImage scaleSMaskImage(PDFImage sMaskImage) throws PDFImageParseException { - BufferedImage before = sMaskImage.getImage(); - int w = before.getWidth(); - int h = before.getHeight(); - - if (PDFDebugger.DEBUG_IMAGES) { - PDFDebugger.debug("Scaling image from " + w + "/" + h + " to " + this.width + "/" + this.height); - } - BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - AffineTransform at = new AffineTransform(); - - at.scale(((double) this.width / w), ((double) this.height / h)); - - AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); - return scaleOp.filter(before, after); - } - - private boolean isGreyscale(ColorSpace aCs) { - return aCs == PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY).getColorSpace(); - } - - private BufferedImage convertGreyscaleToArgb(byte[] data, BufferedImage bi) { - // we use an optimised greyscale colour conversion, as with scanned - // greyscale/mono documents consisting of nothing but page-size - // images, using the ICC converter is perhaps 15 times slower than this - // method. Using an example scanned, mainly monochrome document, on this - // developer's machine pages took an average of 3s to render using the - // ICC converter filter, and around 115ms using this method. We use - // pre-calculated tables generated using the ICC converter to map - // between - // each possible greyscale value and its desired value in sRGB. - // We also try to avoid going through SampleModels, WritableRasters or - // BufferedImages as that takes about 3 times as long. - final int[] convertedPixels = new int[getWidth() * getHeight()]; - final WritableRaster r = bi.getRaster(); - int i = 0; - final int[] greyToArgbMap = getGreyToArgbMap(bpc); - if (bpc == 1) { - int calculatedLineBytes = (getWidth() + 7) / 8; - int rowStartByteIndex; - // avoid hitting the WritableRaster for the common 1 bpc case - if (greyToArgbMap[0] == 0 && greyToArgbMap[1] == 0xFFFFFFFF) { - // optimisation for common case of a direct map to full white - // and black, using bit twiddling instead of consulting the - // greyToArgb map - for (int y = 0; y < getHeight(); ++y) { - // each row is byte-aligned - rowStartByteIndex = y * calculatedLineBytes; - for (int x = 0; x < getWidth(); ++x) { - final byte b = data[rowStartByteIndex + x / 8]; - final int white = b >> (7 - (x & 7)) & 1; - // if white == 0, white - 1 will be 0xFFFFFFFF, - // which when xored with 0xFFFFFF will produce 0 - // if white == 1, white - 1 will be 0, - // which when xored with 0xFFFFFF will produce 0xFFFFFF - // (ignoring the top two bytes, which are always set - // high anyway) - convertedPixels[i] = 0xFF000000 | ((white - 1) ^ 0xFFFFFF); - ++i; - } - } - } else { - // 1 bpc case where we can't bit-twiddle and need to consult - // the map - for (int y = 0; y < getHeight(); ++y) { - rowStartByteIndex = y * calculatedLineBytes; - for (int x = 0; x < getWidth(); ++x) { - final byte b = data[rowStartByteIndex + x / 8]; - final int val = b >> (7 - (x & 7)) & 1; - convertedPixels[i] = greyToArgbMap[val]; - ++i; - } - } - } - } else { - for (int y = 0; y < getHeight(); ++y) { - for (int x = 0; x < getWidth(); ++x) { - final int greyscale = r.getSample(x, y, 0); - convertedPixels[i] = greyToArgbMap[greyscale]; - ++i; - } - } - } - - final ColorModel ccm = ColorModel.getRGBdefault(); - return new BufferedImage(ccm, - Raster.createPackedRaster(new DataBufferInt(convertedPixels, convertedPixels.length), getWidth(), - getHeight(), getWidth(), ((PackedColorModel) ccm).getMasks(), null), - false, null); - } - - private static int[] getGreyToArgbMap(int numBits) { - assert numBits <= 8; - int[] argbVals = GREY_TO_ARGB[numBits - 1]; - if (argbVals == null) { - argbVals = createGreyToArgbMap(numBits); - } - return argbVals; - } - - /** - * Create a map from all bit-patterns of a certain depth greyscale to the - * corresponding sRGB values via the ICC colorr converter. - * - * @param numBits - * the number of greyscale bits - * @return a 2^bits array of standard 32-bit ARGB fits for each greyscale - * value at that bitdepth - */ - private static int[] createGreyToArgbMap(int numBits) { - final ColorSpace greyCs = PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY).getColorSpace(); - - byte[] greyVals = new byte[1 << numBits]; - for (int i = 0; i < greyVals.length; ++i) { - greyVals[i] = (byte) (i & 0xFF); - } - - final int[] argbVals = new int[greyVals.length]; - final int mask = (1 << numBits) - 1; - final WritableRaster inRaster = Raster.createPackedRaster(new DataBufferByte(greyVals, greyVals.length), - greyVals.length, 1, greyVals.length, new int[] { mask }, null); - - final BufferedImage greyImage = new BufferedImage(new PdfComponentColorModel(greyCs, new int[] { numBits }), - inRaster, false, null); - - final ColorModel ccm = ColorModel.getRGBdefault(); - final WritableRaster outRaster = Raster.createPackedRaster(new DataBufferInt(argbVals, argbVals.length), - argbVals.length, 1, argbVals.length, ((PackedColorModel) ccm).getMasks(), null); - final BufferedImage srgbImage = new BufferedImage(ccm, outRaster, false, null); - - final ColorConvertOp op = new ColorConvertOp(greyCs, ColorSpace.getInstance(ColorSpace.CS_sRGB), null); - - op.filter(greyImage, srgbImage); - - GREY_TO_ARGB[numBits - 1] = argbVals; - return argbVals; - } - - /** - * Creates a new image of type {@link TYPE_BYTE_GRAY} which represents the - * given raster - * - * @param raster - * Raster of an image with just two colors, bitwise encoded - * @param ncc - * Array with two entries that describe the corresponding gray - * values - */ - private BufferedImage biColorToGrayscale(final WritableRaster raster, final byte[] ncc) { - - final byte[] bufferO = ((DataBufferByte) raster.getDataBuffer()).getData(); - - BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_GRAY); - - byte[] buffer = ((DataBufferByte) converted.getRaster().getDataBuffer()).getData(); - int i = 0; - final int height = converted.getHeight(); - final int width = converted.getWidth(); - for (int y = 0; y < height; y++) { - int base = y * width + 7; - if ((y + 1) * width < buffer.length) { - for (int x = 0; x < width; x += 8) { - final byte bits = bufferO[i]; - i++; - for (byte j = 7; j >= 0; j--) { - if (buffer.length <= (base - j)) { - break; - } - final int c = ((bits >>> j) & 1); - buffer[base - j] = ncc[c]; - } - base += 8; - } - } else { - for (int x = 0; x < width; x += 8) { - final byte bits = bufferO[i]; - i++; - for (byte j = 7; j >= 0; j--) { - if (base - j >= buffer.length) - break; - buffer[base - j] = ncc[((bits >>> j) & 1)]; - } - base += 8; - } - } - } - return converted; - } - - /** - * Get the image's width - */ - public int getWidth() { - return this.width; - } - - /** - * Set the image's width - */ - protected void setWidth(int width) { - this.width = width; - } - - /** - * Get the image's height - */ - public int getHeight() { - return this.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(); - this.colorKeyMask = null; - int[] masks = new int[maskObjects.length]; - for (int i = 0; i < masks.length; i++) { - masks[i] = maskObjects[i].getIntValue(); - } - this.colorKeyMask = masks; - } - - /** - * Get the colorspace associated with this image, or null if there isn't one - */ - protected PDFColorSpace getColorSpace() { - return this.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 this.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 this.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 this.sMask; - } - - /** - * Set the soft mask image - */ - protected void setSMask(PDFImage sMask) { - this.sMask = sMask; - } - - /** - * Get the decode array - */ - protected float[] getDecode() { - return this.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 (this.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 (this.colorKeyMask == null || this.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 < this.colorKeyMask.length; i += 2) { - for (int j = this.colorKeyMask[i]; j <= this.colorKeyMask[i + 1]; j++) { - aComps[(j * 4) + 3] = 0; // make transparent - } - } - return new IndexColorModel(getBitsPerComponent(), num, aComps, 0, true); - } - } else if (cs instanceof AlternateColorSpace) { - // ColorSpace altCS = new AltColorSpace(((AlternateColorSpace) - // cs).getFunktion(), cs.getColorSpace()); - ColorSpace altCS = cs.getColorSpace(); - int[] bits = new int[altCS.getNumComponents()]; - for (int i = 0; i < bits.length; i++) { - bits[i] = getBitsPerComponent(); - } - return new DecodeComponentColorModel(altCS, bits); - } else { - // If the image is a JPEG, then CMYK color space has been converted to RGB in DCTDecode - if (this.jpegDecode && cs.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { - ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); - int[] bits = new int[rgbCS.getNumComponents()]; - for (int i = 0; i < bits.length; i++) { - bits[i] = getBitsPerComponent(); - } - return new DecodeComponentColorModel(rgbCS, bits); - } - ColorSpace colorSpace = cs.getColorSpace(); - int[] bits = new int[colorSpace.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) { - this.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); - } - } - - /** - * 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 createColorModel() { - 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 decode != null ? new DecodeComponentColorModel(cs.getColorSpace(), bits) - : new PdfComponentColorModel(cs.getColorSpace(), bits); - } - } - - /** - * Decodes jpeg data, possibly attempting a manual YCCK decode if requested. - * Users should use {@link #getColorModel()} to see which color model should - * now be used after a successful decode. - */ - private class JpegDecoder { - /** The jpeg bytes */ - private final ByteBuffer jpegData; - /** The color model employed */ - private ColorModel cm; - /** Whether the YCCK/CMYK decode work-around should be used */ - private boolean ycckcmykDecodeMode = false; - - /** - * Class constructor - * - * @param jpegData - * the JPEG data - * @param cm - * the color model as presented in the PDF - */ - private JpegDecoder(ByteBuffer jpegData, ColorModel cm) { - this.jpegData = jpegData; - this.cm = cm; - } - - /** - * Identify whether the decoder should operate in YCCK/CMYK decode mode, - * whereby the YCCK Chroma is specifically looked for and the color - * model is changed to support converting raw YCCK color values, working - * around a lack of YCCK/CMYK report in the standard Java jpeg readers. - * Non-YCCK images will not be decoded while in this mode. - * - * @param ycckcmykDecodeMode - */ - public void ycckcmykDecodeMode(boolean ycckcmykDecodeMode) { - this.ycckcmykDecodeMode = ycckcmykDecodeMode; - } - - /** - * Get the color model that should be used now - * - * @return - */ - public ColorModel getColorModel() { - return cm; - } - - /** - * Attempt to decode the jpeg data - * - * @return the successfully decoded image - * @throws IOException - * if the image couldn't be decoded due to a lack of support - * or some IO problem - */ + } + + PDFDebugger.debugImage(bi, "result" + this.imageObj.getObjNum()); + return bi; + } + + /** + * Scale the softmask image to the size of the actual image + * + * @param sMaskImage + * @return + * @throws PDFImageParseException + */ + private BufferedImage scaleSMaskImage(PDFImage sMaskImage) throws PDFImageParseException { + BufferedImage before = sMaskImage.getImage(); + int w = before.getWidth(); + int h = before.getHeight(); + + if (PDFDebugger.DEBUG_IMAGES) { + PDFDebugger.debug("Scaling image from " + w + "/" + h + " to " + this.width + "/" + this.height); + } + BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + AffineTransform at = new AffineTransform(); + + at.scale(((double) this.width / w), ((double) this.height / h)); + + AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); + return scaleOp.filter(before, after); + } + + private boolean isGreyscale(ColorSpace aCs) { + return aCs == PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY).getColorSpace(); + } + + private BufferedImage convertGreyscaleToArgb(byte[] data, BufferedImage bi) { + // we use an optimised greyscale colour conversion, as with scanned + // greyscale/mono documents consisting of nothing but page-size + // images, using the ICC converter is perhaps 15 times slower than this + // method. Using an example scanned, mainly monochrome document, on this + // developer's machine pages took an average of 3s to render using the + // ICC converter filter, and around 115ms using this method. We use + // pre-calculated tables generated using the ICC converter to map + // between + // each possible greyscale value and its desired value in sRGB. + // We also try to avoid going through SampleModels, WritableRasters or + // BufferedImages as that takes about 3 times as long. + final int[] convertedPixels = new int[getWidth() * getHeight()]; + final WritableRaster r = bi.getRaster(); + int i = 0; + final int[] greyToArgbMap = getGreyToArgbMap(bpc); + if (bpc == 1) { + int calculatedLineBytes = (getWidth() + 7) / 8; + int rowStartByteIndex; + // avoid hitting the WritableRaster for the common 1 bpc case + if (greyToArgbMap[0] == 0 && greyToArgbMap[1] == 0xFFFFFFFF) { + // optimisation for common case of a direct map to full white + // and black, using bit twiddling instead of consulting the + // greyToArgb map + for (int y = 0; y < getHeight(); ++y) { + // each row is byte-aligned + rowStartByteIndex = y * calculatedLineBytes; + for (int x = 0; x < getWidth(); ++x) { + final byte b = data[rowStartByteIndex + x / 8]; + final int white = b >> (7 - (x & 7)) & 1; + // if white == 0, white - 1 will be 0xFFFFFFFF, + // which when xored with 0xFFFFFF will produce 0 + // if white == 1, white - 1 will be 0, + // which when xored with 0xFFFFFF will produce 0xFFFFFF + // (ignoring the top two bytes, which are always set + // high anyway) + convertedPixels[i] = 0xFF000000 | ((white - 1) ^ 0xFFFFFF); + ++i; + } + } + } else { + // 1 bpc case where we can't bit-twiddle and need to consult + // the map + for (int y = 0; y < getHeight(); ++y) { + rowStartByteIndex = y * calculatedLineBytes; + for (int x = 0; x < getWidth(); ++x) { + final byte b = data[rowStartByteIndex + x / 8]; + final int val = b >> (7 - (x & 7)) & 1; + convertedPixels[i] = greyToArgbMap[val]; + ++i; + } + } + } + } else { + for (int y = 0; y < getHeight(); ++y) { + for (int x = 0; x < getWidth(); ++x) { + final int greyscale = r.getSample(x, y, 0); + convertedPixels[i] = greyToArgbMap[greyscale]; + ++i; + } + } + } + + final ColorModel ccm = ColorModel.getRGBdefault(); + return new BufferedImage(ccm, + Raster.createPackedRaster(new DataBufferInt(convertedPixels, convertedPixels.length), getWidth(), + getHeight(), getWidth(), ((PackedColorModel) ccm).getMasks(), null), + false, null); + } + + private static int[] getGreyToArgbMap(int numBits) { + assert numBits <= 8; + int[] argbVals = GREY_TO_ARGB[numBits - 1]; + if (argbVals == null) { + argbVals = createGreyToArgbMap(numBits); + } + return argbVals; + } + + /** + * Create a map from all bit-patterns of a certain depth greyscale to the + * corresponding sRGB values via the ICC colorr converter. + * + * @param numBits + * the number of greyscale bits + * @return a 2^bits array of standard 32-bit ARGB fits for each greyscale + * value at that bitdepth + */ + private static int[] createGreyToArgbMap(int numBits) { + final ColorSpace greyCs = PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY).getColorSpace(); + + byte[] greyVals = new byte[1 << numBits]; + for (int i = 0; i < greyVals.length; ++i) { + greyVals[i] = (byte) (i & 0xFF); + } + + final int[] argbVals = new int[greyVals.length]; + final int mask = (1 << numBits) - 1; + final WritableRaster inRaster = Raster.createPackedRaster(new DataBufferByte(greyVals, greyVals.length), + greyVals.length, 1, greyVals.length, new int[] { mask }, null); + + final BufferedImage greyImage = new BufferedImage(new PdfComponentColorModel(greyCs, new int[] { numBits }), + inRaster, false, null); + + final ColorModel ccm = ColorModel.getRGBdefault(); + final WritableRaster outRaster = Raster.createPackedRaster(new DataBufferInt(argbVals, argbVals.length), + argbVals.length, 1, argbVals.length, ((PackedColorModel) ccm).getMasks(), null); + final BufferedImage srgbImage = new BufferedImage(ccm, outRaster, false, null); + + final ColorConvertOp op = new ColorConvertOp(greyCs, ColorSpace.getInstance(ColorSpace.CS_sRGB), null); + + op.filter(greyImage, srgbImage); + + GREY_TO_ARGB[numBits - 1] = argbVals; + return argbVals; + } + + /** + * Creates a new image of type {@link TYPE_BYTE_GRAY} which represents the + * given raster + * + * @param raster + * Raster of an image with just two colors, bitwise encoded + * @param ncc + * Array with two entries that describe the corresponding gray + * values + */ + private BufferedImage biColorToGrayscale(final WritableRaster raster, final byte[] ncc) { + + final byte[] bufferO = ((DataBufferByte) raster.getDataBuffer()).getData(); + + BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_BYTE_GRAY); + + byte[] buffer = ((DataBufferByte) converted.getRaster().getDataBuffer()).getData(); + int i = 0; + final int height = converted.getHeight(); + final int width = converted.getWidth(); + for (int y = 0; y < height; y++) { + int base = y * width + 7; + if ((y + 1) * width < buffer.length) { + for (int x = 0; x < width; x += 8) { + final byte bits = bufferO[i]; + i++; + for (byte j = 7; j >= 0; j--) { + if (buffer.length <= (base - j)) { + break; + } + final int c = ((bits >>> j) & 1); + buffer[base - j] = ncc[c]; + } + base += 8; + } + } else { + for (int x = 0; x < width; x += 8) { + final byte bits = bufferO[i]; + i++; + for (byte j = 7; j >= 0; j--) { + if (base - j >= buffer.length) + break; + buffer[base - j] = ncc[((bits >>> j) & 1)]; + } + base += 8; + } + } + } + return converted; + } + + /** + * Get the image's width + */ + public int getWidth() { + return this.width; + } + + /** + * Set the image's width + */ + protected void setWidth(int width) { + this.width = width; + } + + /** + * Get the image's height + */ + public int getHeight() { + return this.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(); + this.colorKeyMask = null; + int[] masks = new int[maskObjects.length]; + for (int i = 0; i < masks.length; i++) { + masks[i] = maskObjects[i].getIntValue(); + } + this.colorKeyMask = masks; + } + + /** + * Get the colorspace associated with this image, or null if there isn't one + */ + protected PDFColorSpace getColorSpace() { + return this.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 this.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 this.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 this.sMask; + } + + /** + * Set the soft mask image + */ + protected void setSMask(PDFImage sMask) { + this.sMask = sMask; + } + + /** + * Get the decode array + */ + protected float[] getDecode() { + return this.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 (this.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 (this.colorKeyMask == null || this.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 < this.colorKeyMask.length; i += 2) { + for (int j = this.colorKeyMask[i]; j <= this.colorKeyMask[i + 1]; j++) { + aComps[(j * 4) + 3] = 0; // make transparent + } + } + return new IndexColorModel(getBitsPerComponent(), num, aComps, 0, true); + } + } else if (cs instanceof AlternateColorSpace) { + // ColorSpace altCS = new AltColorSpace(((AlternateColorSpace) + // cs).getFunktion(), cs.getColorSpace()); + ColorSpace altCS = cs.getColorSpace(); + int[] bits = new int[altCS.getNumComponents()]; + for (int i = 0; i < bits.length; i++) { + bits[i] = getBitsPerComponent(); + } + return new DecodeComponentColorModel(altCS, bits); + } else { + // If the image is a JPEG, then CMYK color space has been converted to RGB in DCTDecode + if (this.jpegDecode && cs.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); + int[] bits = new int[rgbCS.getNumComponents()]; + for (int i = 0; i < bits.length; i++) { + bits[i] = getBitsPerComponent(); + } + return new DecodeComponentColorModel(rgbCS, bits); + } + ColorSpace colorSpace = cs.getColorSpace(); + int[] bits = new int[colorSpace.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) { + this.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); + } + } + + /** + * 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 createColorModel() { + 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 decode != null ? new DecodeComponentColorModel(cs.getColorSpace(), bits) + : new PdfComponentColorModel(cs.getColorSpace(), bits); + } + } + + /** + * Decodes jpeg data, possibly attempting a manual YCCK decode if requested. + * Users should use {@link #getColorModel()} to see which color model should + * now be used after a successful decode. + */ + private class JpegDecoder { + /** The jpeg bytes */ + private final ByteBuffer jpegData; + /** The color model employed */ + private ColorModel cm; + /** Whether the YCCK/CMYK decode work-around should be used */ + private boolean ycckcmykDecodeMode = false; + + /** + * Class constructor + * + * @param jpegData + * the JPEG data + * @param cm + * the color model as presented in the PDF + */ + private JpegDecoder(ByteBuffer jpegData, ColorModel cm) { + this.jpegData = jpegData; + this.cm = cm; + } + + /** + * Identify whether the decoder should operate in YCCK/CMYK decode mode, + * whereby the YCCK Chroma is specifically looked for and the color + * model is changed to support converting raw YCCK color values, working + * around a lack of YCCK/CMYK report in the standard Java jpeg readers. + * Non-YCCK images will not be decoded while in this mode. + * + * @param ycckcmykDecodeMode + */ + public void ycckcmykDecodeMode(boolean ycckcmykDecodeMode) { + this.ycckcmykDecodeMode = ycckcmykDecodeMode; + } + + /** + * Get the color model that should be used now + * + * @return + */ + public ColorModel getColorModel() { + return cm; + } + + /** + * Attempt to decode the jpeg data + * + * @return the successfully decoded image + * @throws IOException + * if the image couldn't be decoded due to a lack of support + * or some IO problem + */ private BufferedImage decode() throws IOException { byte[] jpegBytes = jpegData.hasArray() ? Arrays.copyOfRange(jpegData.array(), jpegData.position(), jpegData.limit()) @@ -1175,165 +1175,165 @@ private BufferedImage decode() throws IOException { } private BufferedImage readImage(ImageReader jpegReader, ImageReadParam param) throws IOException { - if (ycckcmykDecodeMode) { - // The standard Oracle Java JPEG readers can't deal with CMYK - // YCCK encoded images - // without a little help from us. We'll try and pick up such - // instances and work around it. - final IIOMetadata imageMeta = jpegReader.getImageMetadata(0); - if (imageMeta != null) { - final Node standardMeta = imageMeta.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); - if (standardMeta != null) { - final Node chroma = getChild(standardMeta, "Chroma"); - if (chroma != null) { - final Node csType = getChild(chroma, "ColorSpaceType"); - if (csType != null) { - final Attr csTypeNameNode = (Attr) csType.getAttributes().getNamedItem("name"); - if (csTypeNameNode != null) { - final String typeName = csTypeNameNode.getValue(); - final boolean YCCK; - if ((YCCK = "YCCK".equals(typeName)) || "CMYK".equals(typeName)) { - // If it's a YCCK image, then we can - // coax a workable image out of it - // by grabbing the raw raster and - // installing a YCCK converting - // color space wrapper around the - // existing (CMYK) color space; this - // will - // do the YCCK conversion for us - // - // If it's a CMYK image - just raster it - // in existing CMYK color space - - // first make sure we can get the - // unadjusted raster - final Raster raster = jpegReader.readRaster(0, param); - - if (YCCK) { - // and now use it with a YCCK - // converting color space. - PDFImage.this.colorSpace = new PDFColorSpace( - new YCCKColorSpace(colorSpace.getColorSpace())); - // re-calculate the color model - // since the color space has changed - cm = PDFImage.this.createColorModel(); - } - - return new BufferedImage(cm, Raster.createWritableRaster( - raster.getSampleModel(), raster.getDataBuffer(), null), true, null); - - } - } - } - } - } - } - - throw new IIOException("Neither YCCK nor CMYK image"); - - } else { - - if (param != null && param.getDestination() != null) { - // if we've already set up a destination image then we'll - // use it - return jpegReader.read(0, param); - } else { - // otherwise we'll create a new buffered image with the - // desired color model - //return new BufferedImage(cm, jpegReader.read(0, param).getRaster(), true, null); - BufferedImage bi = jpegReader.read(0, param); - try { - return new BufferedImage(cm, bi.getRaster(), true, null); - } catch (IllegalArgumentException raster_ByteInterleavedRaster) { - BufferedImage bi2 = new BufferedImage(bi.getWidth(), bi.getHeight(), - BufferedImage.TYPE_BYTE_INDEXED, - new IndexColorModel(8, 1, new byte[] { 0 }, new byte[] { 0 }, new byte[] { 0 }, 0)); - cm = bi2.getColorModel(); - return bi2; - } - } - } - - } - - /** - * Get a named child node - * - * @param aNode - * the node - * @param aChildName - * the name of the child node - * @return the first direct child node with that name or null if it - * doesn't exist - */ - private Node getChild(Node aNode, String aChildName) { - for (int i = 0; i < aNode.getChildNodes().getLength(); ++i) { - final Node child = aNode.getChildNodes().item(i); - if (child.getNodeName().equals(aChildName)) { - return child; - } - } - return null; - } - } - - /** - * A wrapper for ComponentColorSpace which normalizes based on the decode - * array. - */ - static class PdfComponentColorModel extends ComponentColorModel { - - int bitsPerComponent; - - public PdfComponentColorModel(ColorSpace cs, int[] bpc) { - super(cs, bpc, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); - - pixel_bits = bpc.length * bpc[0]; - this.bitsPerComponent = bpc[0]; - } - - @Override - public SampleModel createCompatibleSampleModel(int width, int height) { - - if (bitsPerComponent >= 8) { - assert bitsPerComponent == 8 || bitsPerComponent == 16; - final int numComponents = getNumComponents(); - int[] bandOffsets = new int[numComponents]; - for (int i = 0; i < numComponents; i++) { - bandOffsets[i] = i; - } - return new PixelInterleavedSampleModel(getTransferType(), width, height, numComponents, - width * numComponents, bandOffsets); - } else { - switch (getPixelSize()) { - case 1: - case 2: - case 4: - // pixels don't span byte boundaries, so we can use the - // standard multi pixel - // packing, which offers a slight performance advantage over - // the other sample - // model, which must consider such cases. Given that sample - // model interactions - // can dominate processing, this small distinction is - // worthwhile - return new MultiPixelPackedSampleModel(getTransferType(), width, height, getPixelSize()); - default: - // pixels will cross byte boundaries - assert getTransferType() == DataBuffer.TYPE_BYTE; - return new PdfSubByteSampleModel(width, height, getNumComponents(), bitsPerComponent); - } - } - } - - @Override - public boolean isCompatibleRaster(Raster raster) { - if (bitsPerComponent < 8 || getNumComponents() == 1) { - SampleModel sm = raster.getSampleModel(); - return sm.getSampleSize(0) == bitsPerComponent; - } - return super.isCompatibleRaster(raster); - } - - } + if (ycckcmykDecodeMode) { + // The standard Oracle Java JPEG readers can't deal with CMYK + // YCCK encoded images + // without a little help from us. We'll try and pick up such + // instances and work around it. + final IIOMetadata imageMeta = jpegReader.getImageMetadata(0); + if (imageMeta != null) { + final Node standardMeta = imageMeta.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); + if (standardMeta != null) { + final Node chroma = getChild(standardMeta, "Chroma"); + if (chroma != null) { + final Node csType = getChild(chroma, "ColorSpaceType"); + if (csType != null) { + final Attr csTypeNameNode = (Attr) csType.getAttributes().getNamedItem("name"); + if (csTypeNameNode != null) { + final String typeName = csTypeNameNode.getValue(); + final boolean YCCK; + if ((YCCK = "YCCK".equals(typeName)) || "CMYK".equals(typeName)) { + // If it's a YCCK image, then we can + // coax a workable image out of it + // by grabbing the raw raster and + // installing a YCCK converting + // color space wrapper around the + // existing (CMYK) color space; this + // will + // do the YCCK conversion for us + // + // If it's a CMYK image - just raster it + // in existing CMYK color space + + // first make sure we can get the + // unadjusted raster + final Raster raster = jpegReader.readRaster(0, param); + + if (YCCK) { + // and now use it with a YCCK + // converting color space. + PDFImage.this.colorSpace = new PDFColorSpace( + new YCCKColorSpace(colorSpace.getColorSpace())); + // re-calculate the color model + // since the color space has changed + cm = PDFImage.this.createColorModel(); + } + + return new BufferedImage(cm, Raster.createWritableRaster( + raster.getSampleModel(), raster.getDataBuffer(), null), true, null); + + } + } + } + } + } + } + + throw new IIOException("Neither YCCK nor CMYK image"); + + } else { + + if (param != null && param.getDestination() != null) { + // if we've already set up a destination image then we'll + // use it + return jpegReader.read(0, param); + } else { + // otherwise we'll create a new buffered image with the + // desired color model + //return new BufferedImage(cm, jpegReader.read(0, param).getRaster(), true, null); + BufferedImage bi = jpegReader.read(0, param); + try { + return new BufferedImage(cm, bi.getRaster(), true, null); + } catch (IllegalArgumentException raster_ByteInterleavedRaster) { + BufferedImage bi2 = new BufferedImage(bi.getWidth(), bi.getHeight(), + BufferedImage.TYPE_BYTE_INDEXED, + new IndexColorModel(8, 1, new byte[] { 0 }, new byte[] { 0 }, new byte[] { 0 }, 0)); + cm = bi2.getColorModel(); + return bi2; + } + } + } + + } + + /** + * Get a named child node + * + * @param aNode + * the node + * @param aChildName + * the name of the child node + * @return the first direct child node with that name or null if it + * doesn't exist + */ + private Node getChild(Node aNode, String aChildName) { + for (int i = 0; i < aNode.getChildNodes().getLength(); ++i) { + final Node child = aNode.getChildNodes().item(i); + if (child.getNodeName().equals(aChildName)) { + return child; + } + } + return null; + } + } + + /** + * A wrapper for ComponentColorSpace which normalizes based on the decode + * array. + */ + static class PdfComponentColorModel extends ComponentColorModel { + + int bitsPerComponent; + + public PdfComponentColorModel(ColorSpace cs, int[] bpc) { + super(cs, bpc, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + + pixel_bits = bpc.length * bpc[0]; + this.bitsPerComponent = bpc[0]; + } + + @Override + public SampleModel createCompatibleSampleModel(int width, int height) { + + if (bitsPerComponent >= 8) { + assert bitsPerComponent == 8 || bitsPerComponent == 16; + final int numComponents = getNumComponents(); + int[] bandOffsets = new int[numComponents]; + for (int i = 0; i < numComponents; i++) { + bandOffsets[i] = i; + } + return new PixelInterleavedSampleModel(getTransferType(), width, height, numComponents, + width * numComponents, bandOffsets); + } else { + switch (getPixelSize()) { + case 1: + case 2: + case 4: + // pixels don't span byte boundaries, so we can use the + // standard multi pixel + // packing, which offers a slight performance advantage over + // the other sample + // model, which must consider such cases. Given that sample + // model interactions + // can dominate processing, this small distinction is + // worthwhile + return new MultiPixelPackedSampleModel(getTransferType(), width, height, getPixelSize()); + default: + // pixels will cross byte boundaries + assert getTransferType() == DataBuffer.TYPE_BYTE; + return new PdfSubByteSampleModel(width, height, getNumComponents(), bitsPerComponent); + } + } + } + + @Override + public boolean isCompatibleRaster(Raster raster) { + if (bitsPerComponent < 8 || getNumComponents() == 1) { + SampleModel sm = raster.getSampleModel(); + return sm.getSampleSize(0) == bitsPerComponent; + } + return super.isCompatibleRaster(raster); + } + + } } \ No newline at end of file diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFObject.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFObject.java index ca76c8fff..1a3725abf 100644 --- a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFObject.java +++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFObject.java @@ -206,8 +206,8 @@ public PDFObject(PDFFile owner, PDFXref xref) { * @throws IOException */ public String getDictRefAsString(String name) throws IOException { - PDFObject ref = getDictRef(name); - return ref == null ? null : ref.getStringValue(); + PDFObject ref = getDictRef(name); + return ref == null ? null : ref.getStringValue(); } /** @@ -217,8 +217,8 @@ public String getDictRefAsString(String name) throws IOException { * @throws IOException */ public Boolean getDictRefAsBoolean(String name) throws IOException { - PDFObject ref = getDictRef(name); - return ref == null ? null : ref.getBooleanValue(); + PDFObject ref = getDictRef(name); + return ref == null ? null : ref.getBooleanValue(); } /** @@ -228,8 +228,8 @@ public Boolean getDictRefAsBoolean(String name) throws IOException { * @throws IOException */ public Integer getDictRefAsInt(String name) throws IOException { - PDFObject ref = getDictRef(name); - return ref == null ? null : ref.getIntValue(); + PDFObject ref = getDictRef(name); + return ref == null ? null : ref.getIntValue(); } /** @@ -239,16 +239,16 @@ public Integer getDictRefAsInt(String name) throws IOException { * @throws IOException */ public int[] getDictRefAsIntArray(String name) throws IOException { - PDFObject ref = getDictRef(name); - if (ref == null) { - return null; - } - PDFObject[] values = ref.getArray(); + PDFObject ref = getDictRef(name); + if (ref == null) { + return null; + } + PDFObject[] values = ref.getArray(); int[] result = new int[values.length]; for (int i = 0; i < values.length; i++) { - result[i] = values[i].getIntValue(); + result[i] = values[i].getIntValue(); } - return result; + return result; } /** @@ -258,16 +258,16 @@ public int[] getDictRefAsIntArray(String name) throws IOException { * @throws IOException */ public float[] getDictRefAsFloatArray(String name) throws IOException { - PDFObject ref = getDictRef(name); - if (ref == null) { - return null; - } - PDFObject[] values = ref.getArray(); - float[] result = new float[values.length]; + PDFObject ref = getDictRef(name); + if (ref == null) { + return null; + } + PDFObject[] values = ref.getArray(); + float[] result = new float[values.length]; for (int i = 0; i < values.length; i++) { - result[i] = values[i].getFloatValue(); + result[i] = values[i].getFloatValue(); } - return result; + return result; } @@ -278,8 +278,8 @@ public float[] getDictRefAsFloatArray(String name) throws IOException { * @throws IOException */ public Float getDictRefAsFloat(String name) throws IOException { - PDFObject ref = getDictRef(name); - return ref == null ? null : ref.getFloatValue(); + PDFObject ref = getDictRef(name); + return ref == null ? null : ref.getFloatValue(); } /** @@ -289,8 +289,8 @@ public Float getDictRefAsFloat(String name) throws IOException { * @throws IOException */ public Double getDictRefAsDouble(String name) throws IOException { - PDFObject ref = getDictRef(name); - return ref == null ? null : ref.getDoubleValue(); + PDFObject ref = getDictRef(name); + return ref == null ? null : ref.getDoubleValue(); } /** @@ -522,7 +522,7 @@ public String getStringValue() throws IOException { * @throws IOException */ public String getTextStringValue() throws IOException { - return PDFStringUtil.asTextString(getStringValue()); + return PDFStringUtil.asTextString(getStringValue()); } /** @@ -822,12 +822,12 @@ public boolean equals(Object o) { return false; } - - /** + + /** * Returns the root of this object. */ public PDFObject getRoot() { - return owner.getRoot(); + return owner.getRoot(); } } diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFPage.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFPage.java index 7483444b5..e9f86c397 100644 --- a/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFPage.java +++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/PDFPage.java @@ -225,12 +225,12 @@ public Image getImage(int width, int height, Rectangle2D clip, ImageObserver obs renderer.addObserver(observer); } - if (!renderer.isFinished()) { - renderer.go(wait); - if (renderer.getStatus() == Watchable.ERROR) { - PDFDebugger.debug("Error during reading image!"); - } - } + if (!renderer.isFinished()) { + renderer.go(wait); + if (renderer.getStatus() == Watchable.ERROR) { + PDFDebugger.debug("Error during reading image!"); + } + } } // return the image return image; @@ -695,12 +695,12 @@ public void setAnnots(Listnull in case the passed object is null
- * @throws IOException
- * @throws PDFParseException
- ************************************************************************/
- private FileSpec parseFileSpecification(PDFObject fileObj) throws PDFParseException, IOException {
- FileSpec file = null;
- if (fileObj != null) {
- file = new FileSpec();
- if(fileObj.getType() == PDFObject.DICTIONARY){
- file.setFileSystem(PdfObjectParseUtil.parseStringFromDict("FS", fileObj, false));
- file.setFileName(PdfObjectParseUtil.parseStringFromDict("F", fileObj, false));
- file.setUnicode(PdfObjectParseUtil.parseStringFromDict("UF", fileObj, false));
- file.setDosFileName(PdfObjectParseUtil.parseStringFromDict("DOS", fileObj, false));
- file.setMacFileName(PdfObjectParseUtil.parseStringFromDict("Mac", fileObj, false));
- file.setUnixFileName(PdfObjectParseUtil.parseStringFromDict("Unix", fileObj, false));
- file.setVolatileFile(PdfObjectParseUtil.parseBooleanFromDict("V", fileObj, false));
- file.setDescription(PdfObjectParseUtil.parseStringFromDict("Desc", fileObj, false));
- file.setId(fileObj.getDictRef("ID"));
- file.setEmbeddedFile(fileObj.getDictRef("EF"));
- file.setRelatedFile(fileObj.getDictRef("RF"));
- file.setCollectionItem(fileObj.getDictRef("CI"));
- }else if(fileObj.getType() == PDFObject.STRING){
- file.setFileName(fileObj.getStringValue());
- }else{
- throw new PDFParseException("File specification could not be parsed " +
- "(should be of type 'Dictionary' or 'String'): " + fileObj.toString());
- }
- }
- return file;
- }
-
-
- /*************************************************************************
- * Parse the windows specific launch parameters
- * @param winDict
- * @throws IOException - in case of a problem during parsing content
- ************************************************************************/
- private WinLaunchParam parseWinDict(PDFObject winDict) throws IOException {
- if (winDict == null) {
- return null;
- }
- WinLaunchParam param = new WinLaunchParam();
-
- // find and parse the file/application name
- param.setFileName(PdfObjectParseUtil.parseStringFromDict("F", winDict, true));
-
- // find and parse the directory
- param.setDirectory(PdfObjectParseUtil.parseStringFromDict("D", winDict, false));
-
- // find and parse the operation to be performed
- param.setOperation(PdfObjectParseUtil.parseStringFromDict("O", winDict, false));
-
- // find and parse the parameter to be passed to the application
- param.setParameter(PdfObjectParseUtil.parseStringFromDict("P", winDict, false));
-
- return param;
- }
-
- /*************************************************************************
- * The file / application to be opened
- * @return FileSpec
- ************************************************************************/
- public FileSpec getFileSpecification() {
- return this.file;
- }
-
- /*************************************************************************
- * Should a new window be opened for the file/application?
- * @return boolean
- ************************************************************************/
- public boolean isNewWindow() {
- return this.newWindow;
- }
-
- /*************************************************************************
- * Get the unix specific launch parameters.
- * Note: The dictionary is not specified yet in the PDF spec., so the PdfObject
- * which is returned here is not parsed.
- * @return PDFObject
- ************************************************************************/
- public PDFObject getUnixParam() {
- return this.unixParam;
- }
-
- /*************************************************************************
- * Get the mac specific launch parameters.
- * Note: The dictionary is not specified yet in the PDF spec., so the PdfObject
- * which is returned here is not parsed.
- * @return PDFObject
- ************************************************************************/
- public PDFObject getMacParam() {
- return this.macParam;
- }
-
- /*************************************************************************
- * Get the windows specific launch parameters.
- * @return WinLaunchParam
- ************************************************************************/
- public WinLaunchParam getWinParam() {
- return this.winParam;
- }
-
- /*****************************************************************************
- * Internal class for the windows specific launch parameters
- *
- * @version $Id: LaunchAction.java,v 1.1 2009-07-10 12:47:31 xond Exp $
- * @author xond
- * @since 08.07.2009
- ****************************************************************************/
- public class WinLaunchParam {
- private String fileName;
- private String directory;
- private String operation = "open";
- private String parameter;
-
- /*************************************************************************
- * The file/application name to be opened
- * @return String
- ************************************************************************/
- public String getFileName() {
- return this.fileName;
- }
-
- /*************************************************************************
- * The file/application name to be opened
- * @param fileName
- ************************************************************************/
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
- /*************************************************************************
- * The directory in standard DOS syntax
- * @return String
- ************************************************************************/
- public String getDirectory() {
- return this.directory;
- }
-
- /*************************************************************************
- * The directory in standard DOS syntax
- * @param directory
- ************************************************************************/
- public void setDirectory(String directory) {
- this.directory = directory;
- }
-
- /*************************************************************************
- * The operation to be performed (open or print). Ignored
- * in case the "F" parameter describes a file to be opened.
- * Default is "open".
- * @return String
- ************************************************************************/
- public String getOperation() {
- return this.operation;
- }
-
- /*************************************************************************
- * The operation to be performed ("open" or "print").Ignored
- * in case the "F" parameter describes a file to be opened.
- * Default is "open".
- * @param operation
- ************************************************************************/
- public void setOperation(String operation) {
- this.operation = operation;
- }
-
- /*************************************************************************
- * A parameter which shall be passed to the application. Ignored
- * in case the "F" parameter describes a file to be opened.
- * @return String
- ************************************************************************/
- public String getParameter() {
- return this.parameter;
- }
-
- /*************************************************************************
- * A parameter which shall be passed to the application. Ignored
- * in case the "F" parameter describes a file to be opened.
- * @param parameter
- ************************************************************************/
- public void setParameter(String parameter) {
- this.parameter = parameter;
- }
- }
-
- /*****************************************************************************
- * Inner class for storing a file specification
- *
- * @version $Id: LaunchAction.java,v 1.1 2009-07-10 12:47:31 xond Exp $
- * @author xond
- * @since 08.07.2009
- ****************************************************************************/
- public static class FileSpec{
- private String fileSystem;
- private String fileName;
- private String dosFileName;
- private String unixFileName;
- private String macFileName;
- private String unicode;
- private PDFObject id;
- private boolean volatileFile;
- private PDFObject embeddedFile;
- private PDFObject relatedFile;
- private String description;
- private PDFObject collectionItem;
-
- /*************************************************************************
- * The name of the file system that should be used to interpret this entry.
- * @return String
- ************************************************************************/
- public String getFileSystem() {
- return this.fileSystem;
- }
-
- /*************************************************************************
- * The name of the file system that should be used to interpret this entry.
- * @param fileSystem
- ************************************************************************/
- public void setFileSystem(String fileSystem) {
- this.fileSystem = fileSystem;
- }
-
- /*************************************************************************
- * Get the filename:
- * first try to get the file name for the used OS, if it's not available
- * return the common file name.
- * @return String
- ************************************************************************/
- public String getFileName() {
- String system = System.getProperty("os.name");
- if(system.startsWith("Windows")){
- if(this.dosFileName != null){
- return this.dosFileName;
- }
- }else if(system.startsWith("mac os x")){
- if(this.macFileName != null){
- return this.macFileName;
- }
- }else {
- if(this.unixFileName != null){
- return this.unixFileName;
- }
- }
- return this.fileName;
- }
-
- /*************************************************************************
- * The file name.
- * @param fileName
- ************************************************************************/
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
- /*************************************************************************
- * A file specification string representing a DOS file name.
- * @return String
- ************************************************************************/
- public String getDosFileName() {
- return this.dosFileName;
- }
-
- /*************************************************************************
- * A file specification string representing a DOS file name.
- * @param dosFileName
- ************************************************************************/
- public void setDosFileName(String dosFileName) {
- this.dosFileName = dosFileName;
- }
-
- /*************************************************************************
- * A file specification string representing a unix file name.
- * @return String
- ************************************************************************/
- public String getUnixFileName() {
- return this.unixFileName;
- }
-
- /*************************************************************************
- * A file specification string representing a unix file name.
- * @param unixFileName
- ************************************************************************/
- public void setUnixFileName(String unixFileName) {
- this.unixFileName = unixFileName;
- }
-
- /*************************************************************************
- * A file specification string representing a mac file name.
- * @return String
- ************************************************************************/
- public String getMacFileName() {
- return this.macFileName;
- }
-
- /*************************************************************************
- * A file specification string representing a mac file name.
- * @param macFileName
- ************************************************************************/
- public void setMacFileName(String macFileName) {
- this.macFileName = macFileName;
- }
-
- /*************************************************************************
- * Unicode file name
- * @return String
- ************************************************************************/
- public String getUnicode() {
- return this.unicode;
- }
-
- /*************************************************************************
- * Unicode file name
- * @param unicode
- ************************************************************************/
- public void setUnicode(String unicode) {
- this.unicode = unicode;
- }
-
- /*************************************************************************
- * ID - array of two byte strings constituting a file identifier, which
- * should be included in the referenced file.
- *
- * @return PDFObject
- ************************************************************************/
- public PDFObject getId() {
- return this.id;
- }
-
- /*************************************************************************
- * ID - array of two byte strings constituting a file identifier, which
- * should be included in the referenced file.
- *
- * @param id
- ************************************************************************/
- public void setId(PDFObject id) {
- this.id = id;
- }
-
- /*************************************************************************
- * Is the file volatile?
- * @return boolean
- ************************************************************************/
- public boolean isVolatileFile() {
- return this.volatileFile;
- }
-
- /*************************************************************************
- * Is the file volatile?
- * @param volatileFile
- ************************************************************************/
- public void setVolatileFile(boolean volatileFile) {
- this.volatileFile = volatileFile;
- }
-
- /*************************************************************************
- * Dictionary of embedded file streams
- * @return PDFObject
- ************************************************************************/
- public PDFObject getEmbeddedFile() {
- return this.embeddedFile;
- }
-
- /*************************************************************************
- * Dictionary of embedded file streams
- * @param embeddedFile
- ************************************************************************/
- public void setEmbeddedFile(PDFObject embeddedFile) {
- this.embeddedFile = embeddedFile;
- }
-
- /*************************************************************************
- * Dictionary of related files.
- * @return PDFObject
- ************************************************************************/
- public PDFObject getRelatedFile() {
- return this.relatedFile;
- }
-
- /*************************************************************************
- * Dictionary of related files.
- * @param relatedFile
- ************************************************************************/
- public void setRelatedFile(PDFObject relatedFile) {
- this.relatedFile = relatedFile;
- }
-
- /*************************************************************************
- * File specification description
- * @return String
- ************************************************************************/
- public String getDescription() {
- return this.description;
- }
-
- /*************************************************************************
- * File specification description
- * @param description
- ************************************************************************/
- public void setDescription(String description) {
- this.description = description;
- }
-
- /*************************************************************************
- * Collection item dictionary
- * @return PDFObject
- ************************************************************************/
- public PDFObject getCollectionItem() {
- return this.collectionItem;
- }
-
- /*************************************************************************
- * Collection item dictionary
- * @param collectionItem
- ************************************************************************/
- public void setCollectionItem(PDFObject collectionItem) {
- this.collectionItem = collectionItem;
- }
- }
+ // file separator according to PDF spec
+ public final static String SOLIDUS = "/";
+
+ /** the file/application to be opened (optional)*/
+ private FileSpec file;
+ /** should a new window be opened (optional)*/
+ private boolean newWindow = false;
+ private PDFObject unixParam;
+ private PDFObject macParam;
+ private WinLaunchParam winParam;
+
+ /**
+ * Creates a new instance of LaunchAction from an object
+ *
+ * @param obj - the PDFObject with the action information
+ * @param root - the root object
+ */
+ public LaunchAction(PDFObject obj, PDFObject root) throws IOException {
+ super("Launch");
+ // find the file/application and parse it
+ PDFObject fileObj = obj.getDictRef("F");
+ this.file = parseFileSpecification(fileObj);
+
+ // find the new window flag and parse it
+ PDFObject newWinObj = obj.getDictRef("NewWindow");
+ if (newWinObj != null) {
+ this.newWindow = newWinObj.getBooleanValue();
+ }
+ // parse the OS specific launch parameters:
+ this.winParam = parseWinDict(obj.getDictRef("Win"));
+ // unix and mac dictionaries are not further specified, so can not be parsed yet.
+ this.unixParam = obj.getDictRef("Unix");
+ this.macParam = obj.getDictRef("Mac");
+
+ // check if at least the file or one of the OS specific launch parameters is set:
+ if ((this.file == null)
+ && (this.winParam == null)
+ && (this.unixParam == null)
+ && (this.macParam == null)) {
+ throw new PDFParseException("Could not parse launch action (file or OS " +
+ "specific launch parameters are missing): " + obj.toString());
+ }
+ }
+
+ /*************************************************************************
+ * Is the file name absolute (if not, it is relative to the path of the
+ * currently opened PDF file).
+ * If the file name starts with a "/", it is considered to be absolute.
+ *
+ * @return boolean
+ ************************************************************************/
+ public static boolean isAbsolute(String fileName) {
+ return fileName.startsWith(SOLIDUS);
+ }
+
+ /*************************************************************************
+ * Parse the file specification object
+ * @param fileObj
+ * @return FileSpec - might be null in case the passed object is null
+ * @throws IOException
+ * @throws PDFParseException
+ ************************************************************************/
+ private FileSpec parseFileSpecification(PDFObject fileObj) throws PDFParseException, IOException {
+ FileSpec file = null;
+ if (fileObj != null) {
+ file = new FileSpec();
+ if(fileObj.getType() == PDFObject.DICTIONARY){
+ file.setFileSystem(PdfObjectParseUtil.parseStringFromDict("FS", fileObj, false));
+ file.setFileName(PdfObjectParseUtil.parseStringFromDict("F", fileObj, false));
+ file.setUnicode(PdfObjectParseUtil.parseStringFromDict("UF", fileObj, false));
+ file.setDosFileName(PdfObjectParseUtil.parseStringFromDict("DOS", fileObj, false));
+ file.setMacFileName(PdfObjectParseUtil.parseStringFromDict("Mac", fileObj, false));
+ file.setUnixFileName(PdfObjectParseUtil.parseStringFromDict("Unix", fileObj, false));
+ file.setVolatileFile(PdfObjectParseUtil.parseBooleanFromDict("V", fileObj, false));
+ file.setDescription(PdfObjectParseUtil.parseStringFromDict("Desc", fileObj, false));
+ file.setId(fileObj.getDictRef("ID"));
+ file.setEmbeddedFile(fileObj.getDictRef("EF"));
+ file.setRelatedFile(fileObj.getDictRef("RF"));
+ file.setCollectionItem(fileObj.getDictRef("CI"));
+ }else if(fileObj.getType() == PDFObject.STRING){
+ file.setFileName(fileObj.getStringValue());
+ }else{
+ throw new PDFParseException("File specification could not be parsed " +
+ "(should be of type 'Dictionary' or 'String'): " + fileObj.toString());
+ }
+ }
+ return file;
+ }
+
+
+ /*************************************************************************
+ * Parse the windows specific launch parameters
+ * @param winDict
+ * @throws IOException - in case of a problem during parsing content
+ ************************************************************************/
+ private WinLaunchParam parseWinDict(PDFObject winDict) throws IOException {
+ if (winDict == null) {
+ return null;
+ }
+ WinLaunchParam param = new WinLaunchParam();
+
+ // find and parse the file/application name
+ param.setFileName(PdfObjectParseUtil.parseStringFromDict("F", winDict, true));
+
+ // find and parse the directory
+ param.setDirectory(PdfObjectParseUtil.parseStringFromDict("D", winDict, false));
+
+ // find and parse the operation to be performed
+ param.setOperation(PdfObjectParseUtil.parseStringFromDict("O", winDict, false));
+
+ // find and parse the parameter to be passed to the application
+ param.setParameter(PdfObjectParseUtil.parseStringFromDict("P", winDict, false));
+
+ return param;
+ }
+
+ /*************************************************************************
+ * The file / application to be opened
+ * @return FileSpec
+ ************************************************************************/
+ public FileSpec getFileSpecification() {
+ return this.file;
+ }
+
+ /*************************************************************************
+ * Should a new window be opened for the file/application?
+ * @return boolean
+ ************************************************************************/
+ public boolean isNewWindow() {
+ return this.newWindow;
+ }
+
+ /*************************************************************************
+ * Get the unix specific launch parameters.
+ * Note: The dictionary is not specified yet in the PDF spec., so the PdfObject
+ * which is returned here is not parsed.
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getUnixParam() {
+ return this.unixParam;
+ }
+
+ /*************************************************************************
+ * Get the mac specific launch parameters.
+ * Note: The dictionary is not specified yet in the PDF spec., so the PdfObject
+ * which is returned here is not parsed.
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getMacParam() {
+ return this.macParam;
+ }
+
+ /*************************************************************************
+ * Get the windows specific launch parameters.
+ * @return WinLaunchParam
+ ************************************************************************/
+ public WinLaunchParam getWinParam() {
+ return this.winParam;
+ }
+
+ /*****************************************************************************
+ * Internal class for the windows specific launch parameters
+ *
+ * @version $Id: LaunchAction.java,v 1.1 2009-07-10 12:47:31 xond Exp $
+ * @author xond
+ * @since 08.07.2009
+ ****************************************************************************/
+ public class WinLaunchParam {
+ private String fileName;
+ private String directory;
+ private String operation = "open";
+ private String parameter;
+
+ /*************************************************************************
+ * The file/application name to be opened
+ * @return String
+ ************************************************************************/
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ /*************************************************************************
+ * The file/application name to be opened
+ * @param fileName
+ ************************************************************************/
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ /*************************************************************************
+ * The directory in standard DOS syntax
+ * @return String
+ ************************************************************************/
+ public String getDirectory() {
+ return this.directory;
+ }
+
+ /*************************************************************************
+ * The directory in standard DOS syntax
+ * @param directory
+ ************************************************************************/
+ public void setDirectory(String directory) {
+ this.directory = directory;
+ }
+
+ /*************************************************************************
+ * The operation to be performed (open or print). Ignored
+ * in case the "F" parameter describes a file to be opened.
+ * Default is "open".
+ * @return String
+ ************************************************************************/
+ public String getOperation() {
+ return this.operation;
+ }
+
+ /*************************************************************************
+ * The operation to be performed ("open" or "print").Ignored
+ * in case the "F" parameter describes a file to be opened.
+ * Default is "open".
+ * @param operation
+ ************************************************************************/
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+ /*************************************************************************
+ * A parameter which shall be passed to the application. Ignored
+ * in case the "F" parameter describes a file to be opened.
+ * @return String
+ ************************************************************************/
+ public String getParameter() {
+ return this.parameter;
+ }
+
+ /*************************************************************************
+ * A parameter which shall be passed to the application. Ignored
+ * in case the "F" parameter describes a file to be opened.
+ * @param parameter
+ ************************************************************************/
+ public void setParameter(String parameter) {
+ this.parameter = parameter;
+ }
+ }
+
+ /*****************************************************************************
+ * Inner class for storing a file specification
+ *
+ * @version $Id: LaunchAction.java,v 1.1 2009-07-10 12:47:31 xond Exp $
+ * @author xond
+ * @since 08.07.2009
+ ****************************************************************************/
+ public static class FileSpec{
+ private String fileSystem;
+ private String fileName;
+ private String dosFileName;
+ private String unixFileName;
+ private String macFileName;
+ private String unicode;
+ private PDFObject id;
+ private boolean volatileFile;
+ private PDFObject embeddedFile;
+ private PDFObject relatedFile;
+ private String description;
+ private PDFObject collectionItem;
+
+ /*************************************************************************
+ * The name of the file system that should be used to interpret this entry.
+ * @return String
+ ************************************************************************/
+ public String getFileSystem() {
+ return this.fileSystem;
+ }
+
+ /*************************************************************************
+ * The name of the file system that should be used to interpret this entry.
+ * @param fileSystem
+ ************************************************************************/
+ public void setFileSystem(String fileSystem) {
+ this.fileSystem = fileSystem;
+ }
+
+ /*************************************************************************
+ * Get the filename:
+ * first try to get the file name for the used OS, if it's not available
+ * return the common file name.
+ * @return String
+ ************************************************************************/
+ public String getFileName() {
+ String system = System.getProperty("os.name");
+ if(system.startsWith("Windows")){
+ if(this.dosFileName != null){
+ return this.dosFileName;
+ }
+ }else if(system.startsWith("mac os x")){
+ if(this.macFileName != null){
+ return this.macFileName;
+ }
+ }else {
+ if(this.unixFileName != null){
+ return this.unixFileName;
+ }
+ }
+ return this.fileName;
+ }
+
+ /*************************************************************************
+ * The file name.
+ * @param fileName
+ ************************************************************************/
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a DOS file name.
+ * @return String
+ ************************************************************************/
+ public String getDosFileName() {
+ return this.dosFileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a DOS file name.
+ * @param dosFileName
+ ************************************************************************/
+ public void setDosFileName(String dosFileName) {
+ this.dosFileName = dosFileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a unix file name.
+ * @return String
+ ************************************************************************/
+ public String getUnixFileName() {
+ return this.unixFileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a unix file name.
+ * @param unixFileName
+ ************************************************************************/
+ public void setUnixFileName(String unixFileName) {
+ this.unixFileName = unixFileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a mac file name.
+ * @return String
+ ************************************************************************/
+ public String getMacFileName() {
+ return this.macFileName;
+ }
+
+ /*************************************************************************
+ * A file specification string representing a mac file name.
+ * @param macFileName
+ ************************************************************************/
+ public void setMacFileName(String macFileName) {
+ this.macFileName = macFileName;
+ }
+
+ /*************************************************************************
+ * Unicode file name
+ * @return String
+ ************************************************************************/
+ public String getUnicode() {
+ return this.unicode;
+ }
+
+ /*************************************************************************
+ * Unicode file name
+ * @param unicode
+ ************************************************************************/
+ public void setUnicode(String unicode) {
+ this.unicode = unicode;
+ }
+
+ /*************************************************************************
+ * ID - array of two byte strings constituting a file identifier, which
+ * should be included in the referenced file.
+ *
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getId() {
+ return this.id;
+ }
+
+ /*************************************************************************
+ * ID - array of two byte strings constituting a file identifier, which
+ * should be included in the referenced file.
+ *
+ * @param id
+ ************************************************************************/
+ public void setId(PDFObject id) {
+ this.id = id;
+ }
+
+ /*************************************************************************
+ * Is the file volatile?
+ * @return boolean
+ ************************************************************************/
+ public boolean isVolatileFile() {
+ return this.volatileFile;
+ }
+
+ /*************************************************************************
+ * Is the file volatile?
+ * @param volatileFile
+ ************************************************************************/
+ public void setVolatileFile(boolean volatileFile) {
+ this.volatileFile = volatileFile;
+ }
+
+ /*************************************************************************
+ * Dictionary of embedded file streams
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getEmbeddedFile() {
+ return this.embeddedFile;
+ }
+
+ /*************************************************************************
+ * Dictionary of embedded file streams
+ * @param embeddedFile
+ ************************************************************************/
+ public void setEmbeddedFile(PDFObject embeddedFile) {
+ this.embeddedFile = embeddedFile;
+ }
+
+ /*************************************************************************
+ * Dictionary of related files.
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getRelatedFile() {
+ return this.relatedFile;
+ }
+
+ /*************************************************************************
+ * Dictionary of related files.
+ * @param relatedFile
+ ************************************************************************/
+ public void setRelatedFile(PDFObject relatedFile) {
+ this.relatedFile = relatedFile;
+ }
+
+ /*************************************************************************
+ * File specification description
+ * @return String
+ ************************************************************************/
+ public String getDescription() {
+ return this.description;
+ }
+
+ /*************************************************************************
+ * File specification description
+ * @param description
+ ************************************************************************/
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /*************************************************************************
+ * Collection item dictionary
+ * @return PDFObject
+ ************************************************************************/
+ public PDFObject getCollectionItem() {
+ return this.collectionItem;
+ }
+
+ /*************************************************************************
+ * Collection item dictionary
+ * @param collectionItem
+ ************************************************************************/
+ public void setCollectionItem(PDFObject collectionItem) {
+ this.collectionItem = collectionItem;
+ }
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PDFAction.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PDFAction.java
index 224969700..656098cdf 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PDFAction.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PDFAction.java
@@ -60,13 +60,13 @@ public static PDFAction getAction(PDFObject obj, PDFObject root)
if (type.equals("GoTo")) {
action = new GoToAction(obj, root);
}else if(type.equals("GoToE")){
- action = new GoToEAction(obj, root);
+ action = new GoToEAction(obj, root);
}else if(type.equals("GoToR")){
- action = new GoToRAction(obj, root);
+ action = new GoToRAction(obj, root);
}else if(type.equals("URI")){
- action = new UriAction(obj, root);
+ action = new UriAction(obj, root);
}else if(type.equals("Launch")){
- action = new LaunchAction(obj, root);
+ action = new LaunchAction(obj, root);
}
else {
/** [JK FIXME: Implement other action types! ] */
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PdfObjectParseUtil.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PdfObjectParseUtil.java
index 8c9e27974..a274b662d 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PdfObjectParseUtil.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/PdfObjectParseUtil.java
@@ -13,88 +13,88 @@
* @since 08.07.2009
****************************************************************************/
public class PdfObjectParseUtil {
-
- /*************************************************************************
- * Parse a String value with the given key from parent object. If it's mandatory
- * and not available, an exception will be thrown.
- * @param key
- * @param parent
- * @param mandatory
- * @return String - can be null if not mandatory
- * @throws IOException - in case of a parsing error
- ************************************************************************/
- public static String parseStringFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
- PDFObject val = parent;
- while (val.getType() == PDFObject.DICTIONARY) {
- val = val.getDictRef(key);
- if(val == null){
- if(mandatory){
- throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
- }
- return null;
- }
- }
- return val.getStringValue();
- }
+
+ /*************************************************************************
+ * Parse a String value with the given key from parent object. If it's mandatory
+ * and not available, an exception will be thrown.
+ * @param key
+ * @param parent
+ * @param mandatory
+ * @return String - can be null if not mandatory
+ * @throws IOException - in case of a parsing error
+ ************************************************************************/
+ public static String parseStringFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
+ PDFObject val = parent;
+ while (val.getType() == PDFObject.DICTIONARY) {
+ val = val.getDictRef(key);
+ if(val == null){
+ if(mandatory){
+ throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
+ }
+ return null;
+ }
+ }
+ return val.getStringValue();
+ }
- /*************************************************************************
- * Parse a Boolean value with the given key from parent object. If it's mandatory
- * and not available, an exception will be thrown.
- * @param key
- * @param parent
- * @param mandatory
- * @return boolean - false if not available and not mandatory
- * @throws IOException
- ************************************************************************/
- public static boolean parseBooleanFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
- PDFObject val = parent.getDictRef(key);
- if(val == null){
- if(mandatory){
- throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
- }
- return false;
- }
- return val.getBooleanValue();
- }
-
- /*************************************************************************
- * Parse a integer value with the given key from parent object. If it's mandatory
- * and not available, an exception will be thrown.
- * @param key
- * @param parent
- * @param mandatory
- * @return int - returns "0" in case the value is not a number
- * @throws IOException
- ************************************************************************/
- public static int parseIntegerFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
- PDFObject val = parent.getDictRef(key);
- if(val == null){
- if(mandatory){
- throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
- }
- return 0;
- }
- return val.getIntValue();
- }
-
- /*************************************************************************
- * Parse a destination object
- * @param key
- * @param parent
- * @param root
- * @param mandatory
- * @return PDFDestination - can be null if not mandatory
- * @throws IOException
- ************************************************************************/
- public static PDFDestination parseDestination(String key, PDFObject parent, PDFObject root, boolean mandatory) throws IOException{
- PDFObject destObj = parent.getDictRef(key);
- if (destObj == null) {
- if(mandatory){
- throw new PDFParseException("Error parsing destination " + parent);
- }
- return null;
- }
- return PDFDestination.getDestination(destObj, root);
+ /*************************************************************************
+ * Parse a Boolean value with the given key from parent object. If it's mandatory
+ * and not available, an exception will be thrown.
+ * @param key
+ * @param parent
+ * @param mandatory
+ * @return boolean - false if not available and not mandatory
+ * @throws IOException
+ ************************************************************************/
+ public static boolean parseBooleanFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
+ PDFObject val = parent.getDictRef(key);
+ if(val == null){
+ if(mandatory){
+ throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
+ }
+ return false;
+ }
+ return val.getBooleanValue();
+ }
+
+ /*************************************************************************
+ * Parse a integer value with the given key from parent object. If it's mandatory
+ * and not available, an exception will be thrown.
+ * @param key
+ * @param parent
+ * @param mandatory
+ * @return int - returns "0" in case the value is not a number
+ * @throws IOException
+ ************************************************************************/
+ public static int parseIntegerFromDict(String key, PDFObject parent, boolean mandatory) throws IOException{
+ PDFObject val = parent.getDictRef(key);
+ if(val == null){
+ if(mandatory){
+ throw new PDFParseException(key + "value could not be parsed : " + parent.toString());
+ }
+ return 0;
+ }
+ return val.getIntValue();
+ }
+
+ /*************************************************************************
+ * Parse a destination object
+ * @param key
+ * @param parent
+ * @param root
+ * @param mandatory
+ * @return PDFDestination - can be null if not mandatory
+ * @throws IOException
+ ************************************************************************/
+ public static PDFDestination parseDestination(String key, PDFObject parent, PDFObject root, boolean mandatory) throws IOException{
+ PDFObject destObj = parent.getDictRef(key);
+ if (destObj == null) {
+ if(mandatory){
+ throw new PDFParseException("Error parsing destination " + parent);
+ }
+ return null;
+ }
+ return PDFDestination.getDestination(destObj, root);
- }
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/UriAction.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/UriAction.java
index 1da70f75f..09a46f073 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/action/UriAction.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/action/UriAction.java
@@ -12,34 +12,34 @@
****************************************************************************/
public class UriAction extends PDFAction {
- /** The URL this action links to */
- private String uri;
-
- /*************************************************************************
- * Constructor, reading the URL from the given action object
- * @param type
- * @throws IOException - in case the action can not be parsed
- ************************************************************************/
- public UriAction(PDFObject obj, PDFObject root) throws IOException {
- super("URI");
- this.uri = PdfObjectParseUtil.parseStringFromDict("URI", obj, true);
- }
-
- /*************************************************************************
- * Constructor
- * @param type
- * @throws IOException
- ************************************************************************/
- public UriAction(String uri) throws IOException {
- super("URI");
- this.uri = uri;
- }
+ /** The URL this action links to */
+ private String uri;
+
+ /*************************************************************************
+ * Constructor, reading the URL from the given action object
+ * @param type
+ * @throws IOException - in case the action can not be parsed
+ ************************************************************************/
+ public UriAction(PDFObject obj, PDFObject root) throws IOException {
+ super("URI");
+ this.uri = PdfObjectParseUtil.parseStringFromDict("URI", obj, true);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param type
+ * @throws IOException
+ ************************************************************************/
+ public UriAction(String uri) throws IOException {
+ super("URI");
+ this.uri = uri;
+ }
- /*************************************************************************
- * Get the URI this action directs to
- * @return String
- ************************************************************************/
- public String getUri() {
- return this.uri;
- }
+ /*************************************************************************
+ * Get the URI this action directs to
+ * @return String
+ ************************************************************************/
+ public String getUri() {
+ return this.uri;
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationBorderStyle.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationBorderStyle.java
index 22f776d3d..6e5b0e45b 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationBorderStyle.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationBorderStyle.java
@@ -9,80 +9,80 @@
* @author Bernd Rosstauscher
*/
public class AnnotationBorderStyle {
-
- public enum BorderStyle {
- SOLID("S"),
- DASHED("D"),
- BEVELED("B"),
- INSET("I"),
- UNDERLINE("U");
+
+ public enum BorderStyle {
+ SOLID("S"),
+ DASHED("D"),
+ BEVELED("B"),
+ INSET("I"),
+ UNDERLINE("U");
- private String code;
-
- private BorderStyle(String code) {
- this.code = code;
- }
-
- public String getCode() {
- return code;
- }
-
- public static BorderStyle fromCode(String code) {
- for (BorderStyle bs : BorderStyle.values()) {
- if (bs.getCode().equals(code)) {
- return bs;
- }
- }
- return SOLID;
- }
-
- }
-
- private Integer width;
- private BorderStyle borderStyle;
- private int[] dashArray;
-
- /**
- * Creates a annotation border style
- */
- public AnnotationBorderStyle() {
- super();
- }
-
- /**
- * Parse a border style from a BS dictionary.
- * @param bs the pdf dictionary to parse.
- * @return the border style object.
- * @throws IOException
- */
- public static AnnotationBorderStyle parseFromDictionary(PDFObject bs) throws IOException {
- AnnotationBorderStyle result = new AnnotationBorderStyle();
- result.width = bs.getDictRefAsInt("W");
- result.borderStyle = BorderStyle.fromCode(bs.getDictRefAsString("S"));
- result.dashArray = bs.getDictRefAsIntArray("D");
- // TODO BE Border effect not supported yet
- return result;
- }
-
- /**
- * @return the border style enum
- */
- public BorderStyle getBorderStyle() {
- return borderStyle;
- }
-
- /**
- * @return the width of the border line
- */
- public Integer getWidth() {
- return width;
- }
-
- /**
- * @return specifying the dash for the border line
- */
- public int[] getDashArray() {
- return dashArray;
- }
+ private String code;
+
+ private BorderStyle(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public static BorderStyle fromCode(String code) {
+ for (BorderStyle bs : BorderStyle.values()) {
+ if (bs.getCode().equals(code)) {
+ return bs;
+ }
+ }
+ return SOLID;
+ }
+
+ }
+
+ private Integer width;
+ private BorderStyle borderStyle;
+ private int[] dashArray;
+
+ /**
+ * Creates a annotation border style
+ */
+ public AnnotationBorderStyle() {
+ super();
+ }
+
+ /**
+ * Parse a border style from a BS dictionary.
+ * @param bs the pdf dictionary to parse.
+ * @return the border style object.
+ * @throws IOException
+ */
+ public static AnnotationBorderStyle parseFromDictionary(PDFObject bs) throws IOException {
+ AnnotationBorderStyle result = new AnnotationBorderStyle();
+ result.width = bs.getDictRefAsInt("W");
+ result.borderStyle = BorderStyle.fromCode(bs.getDictRefAsString("S"));
+ result.dashArray = bs.getDictRefAsIntArray("D");
+ // TODO BE Border effect not supported yet
+ return result;
+ }
+
+ /**
+ * @return the border style enum
+ */
+ public BorderStyle getBorderStyle() {
+ return borderStyle;
+ }
+
+ /**
+ * @return the width of the border line
+ */
+ public Integer getWidth() {
+ return width;
+ }
+
+ /**
+ * @return specifying the dash for the border line
+ */
+ public int[] getDashArray() {
+ return dashArray;
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationType.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationType.java
index 43bc6969c..602233cf8 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationType.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/AnnotationType.java
@@ -7,100 +7,100 @@
*
*/
public enum AnnotationType{
- UNKNOWN("-", 0, PDFAnnotation.class),
- LINK("Link", 1, LinkAnnotation.class),
- WIDGET("Widget", 2, WidgetAnnotation.class),
- STAMP("Stamp", 3, StampAnnotation.class),
- FREETEXT("FreeText", 5, FreetextAnnotation.class),
- SIGNATURE("Sig", 6, WidgetAnnotation.class),
+ UNKNOWN("-", 0, PDFAnnotation.class),
+ LINK("Link", 1, LinkAnnotation.class),
+ WIDGET("Widget", 2, WidgetAnnotation.class),
+ STAMP("Stamp", 3, StampAnnotation.class),
+ FREETEXT("FreeText", 5, FreetextAnnotation.class),
+ SIGNATURE("Sig", 6, WidgetAnnotation.class),
- // added more annotation types. Most of them only with basic features
- // We render them all via the base class MarkupAnnotation
-
- TEXT("Text", 7, MarkupAnnotation.class),
- LINE("Line", 8, MarkupAnnotation.class),
- SQUARE("Square", 9, SquareAnnotation.class),
- CIRCLE("Circle", 10, CircleAnnotation.class),
- POLYGON("Polygon", 11, MarkupAnnotation.class),
- POLYLINE("PolyLine", 12, MarkupAnnotation.class),
- HIGHLIGHT("Highlight", 13, TextMarkupAnnotation.class),
- UNDERLINE("Underline", 14, TextMarkupAnnotation.class),
- SQUIGGLY("Squiggly", 15, TextMarkupAnnotation.class),
- STRIKEOUT("StrikeOut", 16, TextMarkupAnnotation.class),
- CARET("Caret", 17, MarkupAnnotation.class),
- INK("Ink", 18, MarkupAnnotation.class),
- //POPUP("Popup", 19, MarkupAnnotation.class),
- FILEATTACHMENT("FileAttachment", 20, PDFAnnotation.class),
- SOUND("Sound", 21, PDFAnnotation.class),
- MOVIE("Movie", 22, PDFAnnotation.class),
- SCREEN("Screen", 23, PDFAnnotation.class),
- PRINTERMARK("PrinterMark", 24, PDFAnnotation.class),
- TRAPNET("TrapNet", 25, PDFAnnotation.class),
- WATERMARK("Watermark", 26, PDFAnnotation.class),
- THREED("3D", 27, PDFAnnotation.class),
- REDACT("Redact", 28, MarkupAnnotation.class),
- ;
-
- /**
- * @return true if this annotation type should be displayed else false.
- */
- boolean displayAnnotation() {
- switch(this) {
- case STAMP: return Configuration.getInstance().isPrintStampAnnotations();
- case WIDGET: return Configuration.getInstance().isPrintWidgetAnnotations();
- case FREETEXT: return Configuration.getInstance().isPrintFreetextAnnotations();
- case LINK: return Configuration.getInstance().isPrintLinkAnnotations();
- case SIGNATURE: return Configuration.getInstance().isPrintSignatureFields();
- case UNKNOWN: return false;
- default: {
- // Fallback for all the annotation types that are currently mapped to MarkupAnnotation
- return MarkupAnnotation.class.isAssignableFrom(this.className)
- && Configuration.getInstance().isPrintFreetextAnnotations();
- }
- }
- }
-
- private String definition;
- private int internalId;
- private Class> className;
-
- private AnnotationType(String definition, int typeId, Class> className) {
- this.definition = definition;
- this.internalId = typeId;
- this.className = className;
- }
-
- /**
- * @return the definition
- */
- public String getDefinition() {
- return definition;
- }
- /**
- * @return the internalId
- */
- public int getInternalId() {
- return internalId;
- }
-
- /**
- * @return the className
- */
- public Class> getClassName() {
- return className;
- }
-
- /**
- * Get annotation type by it's type
- * @param definition
- * @return
- */
- public static AnnotationType getByDefinition(String definition) {
- for (AnnotationType type : values()) {
- if(type.definition.equals(definition)) {
- return type;
- }
- }
- return UNKNOWN;
- }
+ // added more annotation types. Most of them only with basic features
+ // We render them all via the base class MarkupAnnotation
+
+ TEXT("Text", 7, MarkupAnnotation.class),
+ LINE("Line", 8, MarkupAnnotation.class),
+ SQUARE("Square", 9, SquareAnnotation.class),
+ CIRCLE("Circle", 10, CircleAnnotation.class),
+ POLYGON("Polygon", 11, MarkupAnnotation.class),
+ POLYLINE("PolyLine", 12, MarkupAnnotation.class),
+ HIGHLIGHT("Highlight", 13, TextMarkupAnnotation.class),
+ UNDERLINE("Underline", 14, TextMarkupAnnotation.class),
+ SQUIGGLY("Squiggly", 15, TextMarkupAnnotation.class),
+ STRIKEOUT("StrikeOut", 16, TextMarkupAnnotation.class),
+ CARET("Caret", 17, MarkupAnnotation.class),
+ INK("Ink", 18, MarkupAnnotation.class),
+ //POPUP("Popup", 19, MarkupAnnotation.class),
+ FILEATTACHMENT("FileAttachment", 20, PDFAnnotation.class),
+ SOUND("Sound", 21, PDFAnnotation.class),
+ MOVIE("Movie", 22, PDFAnnotation.class),
+ SCREEN("Screen", 23, PDFAnnotation.class),
+ PRINTERMARK("PrinterMark", 24, PDFAnnotation.class),
+ TRAPNET("TrapNet", 25, PDFAnnotation.class),
+ WATERMARK("Watermark", 26, PDFAnnotation.class),
+ THREED("3D", 27, PDFAnnotation.class),
+ REDACT("Redact", 28, MarkupAnnotation.class),
+ ;
+
+ /**
+ * @return true if this annotation type should be displayed else false.
+ */
+ boolean displayAnnotation() {
+ switch(this) {
+ case STAMP: return Configuration.getInstance().isPrintStampAnnotations();
+ case WIDGET: return Configuration.getInstance().isPrintWidgetAnnotations();
+ case FREETEXT: return Configuration.getInstance().isPrintFreetextAnnotations();
+ case LINK: return Configuration.getInstance().isPrintLinkAnnotations();
+ case SIGNATURE: return Configuration.getInstance().isPrintSignatureFields();
+ case UNKNOWN: return false;
+ default: {
+ // Fallback for all the annotation types that are currently mapped to MarkupAnnotation
+ return MarkupAnnotation.class.isAssignableFrom(this.className)
+ && Configuration.getInstance().isPrintFreetextAnnotations();
+ }
+ }
+ }
+
+ private String definition;
+ private int internalId;
+ private Class> className;
+
+ private AnnotationType(String definition, int typeId, Class> className) {
+ this.definition = definition;
+ this.internalId = typeId;
+ this.className = className;
+ }
+
+ /**
+ * @return the definition
+ */
+ public String getDefinition() {
+ return definition;
+ }
+ /**
+ * @return the internalId
+ */
+ public int getInternalId() {
+ return internalId;
+ }
+
+ /**
+ * @return the className
+ */
+ public Class> getClassName() {
+ return className;
+ }
+
+ /**
+ * Get annotation type by it's type
+ * @param definition
+ * @return
+ */
+ public static AnnotationType getByDefinition(String definition) {
+ for (AnnotationType type : values()) {
+ if(type.definition.equals(definition)) {
+ return type;
+ }
+ }
+ return UNKNOWN;
+ }
}
\ No newline at end of file
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/CircleAnnotation.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/CircleAnnotation.java
index 63f2649a9..1c9f40965 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/CircleAnnotation.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/CircleAnnotation.java
@@ -10,17 +10,17 @@
* @author Bernd Rosstauscher
****************************************************************************/
public class CircleAnnotation extends MarkupAnnotation {
-
- // TODO Not all of this is fully implemented yet.
- // But it will work if the visual representation is done via an "Appearance Stream"
-
- /*************************************************************************
- * Constructor
- * @param annotObject
- * @throws IOException
- ************************************************************************/
- public CircleAnnotation(PDFObject annotObject) throws IOException {
- super(annotObject, AnnotationType.CIRCLE);
-
- }
+
+ // TODO Not all of this is fully implemented yet.
+ // But it will work if the visual representation is done via an "Appearance Stream"
+
+ /*************************************************************************
+ * Constructor
+ * @param annotObject
+ * @throws IOException
+ ************************************************************************/
+ public CircleAnnotation(PDFObject annotObject) throws IOException {
+ super(annotObject, AnnotationType.CIRCLE);
+
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/FreetextAnnotation.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/FreetextAnnotation.java
index 1de7c25a0..a2bf54850 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/FreetextAnnotation.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/FreetextAnnotation.java
@@ -12,13 +12,13 @@
* @since 28.03.2012
****************************************************************************/
public class FreetextAnnotation extends MarkupAnnotation {
-
- /*************************************************************************
- * Constructor
- * @param annotObject
- * @throws IOException
- ************************************************************************/
- public FreetextAnnotation(PDFObject annotObject) throws IOException {
- super(annotObject, AnnotationType.FREETEXT);
- }
+
+ /*************************************************************************
+ * Constructor
+ * @param annotObject
+ * @throws IOException
+ ************************************************************************/
+ public FreetextAnnotation(PDFObject annotObject) throws IOException {
+ super(annotObject, AnnotationType.FREETEXT);
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/LinkAnnotation.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/LinkAnnotation.java
index 99c7bd1f4..be3ca4497 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/LinkAnnotation.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/LinkAnnotation.java
@@ -27,41 +27,41 @@
****************************************************************************/
public class LinkAnnotation extends PDFAnnotation {
- private PDFAction action = null;
+ private PDFAction action = null;
- /*************************************************************************
- * Constructor
- * @param annotObject
- * @throws IOException
- ************************************************************************/
- public LinkAnnotation(PDFObject annotObject) throws IOException {
- super(annotObject, AnnotationType.LINK);
- // a link annotation can either have an action (GoTo or URI) or a destination (DEST)
- PDFObject actionObj = annotObject.getDictRef("A");
- if (actionObj != null) {
- this.action = PDFAction.getAction(actionObj, annotObject.getRoot());
- } else {
- // if a destination is given, create a GoToAction from it
- PDFObject dest = annotObject.getDictRef("Dest");
- if(dest == null) {
- dest = annotObject.getDictRef("DEST");
- }
- if (dest != null) {
- this.action = new GoToAction(PDFDestination.getDestination(dest, annotObject.getRoot()));
- } else {
- throw new PDFParseException(
- "Could not parse link annotation (no Action or Destination found): "
- + annotObject.toString());
- }
- }
- }
-
- /*************************************************************************
- * Get the contained PDFAction
- * @return PDFAction - can be null in case the contains
- * a destination object
- ************************************************************************/
- public PDFAction getAction() {
- return this.action;
- }
+ /*************************************************************************
+ * Constructor
+ * @param annotObject
+ * @throws IOException
+ ************************************************************************/
+ public LinkAnnotation(PDFObject annotObject) throws IOException {
+ super(annotObject, AnnotationType.LINK);
+ // a link annotation can either have an action (GoTo or URI) or a destination (DEST)
+ PDFObject actionObj = annotObject.getDictRef("A");
+ if (actionObj != null) {
+ this.action = PDFAction.getAction(actionObj, annotObject.getRoot());
+ } else {
+ // if a destination is given, create a GoToAction from it
+ PDFObject dest = annotObject.getDictRef("Dest");
+ if(dest == null) {
+ dest = annotObject.getDictRef("DEST");
+ }
+ if (dest != null) {
+ this.action = new GoToAction(PDFDestination.getDestination(dest, annotObject.getRoot()));
+ } else {
+ throw new PDFParseException(
+ "Could not parse link annotation (no Action or Destination found): "
+ + annotObject.toString());
+ }
+ }
+ }
+
+ /*************************************************************************
+ * Get the contained PDFAction
+ * @return PDFAction - can be null in case the contains
+ * a destination object
+ ************************************************************************/
+ public PDFAction getAction() {
+ return this.action;
+ }
}
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/MarkupAnnotation.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/MarkupAnnotation.java
index 6f5ed4e5c..ee4c676dd 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/MarkupAnnotation.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/annotation/MarkupAnnotation.java
@@ -23,104 +23,104 @@
****************************************************************************/
public class MarkupAnnotation extends PDFAnnotation {
- private PDFObject onAppearance;
- private PDFObject offAppearance;
- private List- * RunLength format consists of a sequence of a byte-oriented format based - * on run length. There are a series of "runs", where a run is a length byte - * followed by 1 to 128 bytes of data. If the length is 0-127, the following - * length+1 (1 to 128) bytes are to be copied. If the length is 129 through - * 255, the following single byte is copied 257-length (2 to 128) times. A - * length value of 128 means and End of Data (EOD). - * - * @param buf - * the RUnLEngth encoded bytes in a byte buffer - * - * @param params - * parameters to the decoder (ignored) - * @return the decoded bytes - */ - public static ByteBuffer decode(ByteBuffer buf, PDFObject params) throws PDFParseException { - RunLengthDecode me = new RunLengthDecode(buf); - return me.decode(); - } + /** + * decode an array of bytes in RunLength format. + *
+ * RunLength format consists of a sequence of a byte-oriented format based
+ * on run length. There are a series of "runs", where a run is a length byte
+ * followed by 1 to 128 bytes of data. If the length is 0-127, the following
+ * length+1 (1 to 128) bytes are to be copied. If the length is 129 through
+ * 255, the following single byte is copied 257-length (2 to 128) times. A
+ * length value of 128 means and End of Data (EOD).
+ *
+ * @param buf
+ * the RUnLEngth encoded bytes in a byte buffer
+ *
+ * @param params
+ * parameters to the decoder (ignored)
+ * @return the decoded bytes
+ */
+ public static ByteBuffer decode(ByteBuffer buf, PDFObject params) throws PDFParseException {
+ RunLengthDecode me = new RunLengthDecode(buf);
+ return me.decode();
+ }
}
\ No newline at end of file
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/IdentityDecrypter.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/IdentityDecrypter.java
index 64f4a8926..d47527699 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/IdentityDecrypter.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/IdentityDecrypter.java
@@ -35,7 +35,7 @@ public class IdentityDecrypter implements PDFDecrypter {
private static IdentityDecrypter INSTANCE = new IdentityDecrypter();
@Override
- public ByteBuffer decryptBuffer(String cryptFilterName,
+ public ByteBuffer decryptBuffer(String cryptFilterName,
PDFObject streamObj, ByteBuffer streamBuf)
throws PDFParseException {
@@ -47,7 +47,7 @@ public ByteBuffer decryptBuffer(String cryptFilterName,
}
@Override
- public String decryptString(int objNum, int objGen, String inputBasicString) {
+ public String decryptString(int objNum, int objGen, String inputBasicString) {
return inputBasicString;
}
@@ -56,17 +56,17 @@ public static IdentityDecrypter getInstance() {
}
@Override
- public boolean isEncryptionPresent() {
+ public boolean isEncryptionPresent() {
return false;
}
@Override
- public boolean isEncryptionPresent(String cryptFilterName) {
+ public boolean isEncryptionPresent(String cryptFilterName) {
return false;
}
@Override
- public boolean isOwnerAuthorised() {
+ public boolean isOwnerAuthorised() {
return false;
}
}
\ No newline at end of file
diff --git a/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/PDFPassword.java b/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/PDFPassword.java
index fb5272bb2..9c1e170af 100644
--- a/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/PDFPassword.java
+++ b/openpdf-renderer/src/main/java/org/openpdf/renderer/decrypt/PDFPassword.java
@@ -141,7 +141,7 @@ List
- * - * The type of the result is the same as the type of num1, - * unless num1 is the smallest (most negative) integer, - * in which case the result is a real number.
- * - * errors: stackunderflow, typecheck - */ - public void eval(Stack