org.jpedal.io.ColorObjectDecoder Maven / Gradle / Ivy
The newest version!
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
*
* (C) Copyright 1997-2013, IDRsolutions and Contributors.
*
* This file is part of JPedal
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* ColorObjectDecoder.java
* ---------------
*/
package org.jpedal.io;
import org.jpedal.color.ColorSpaces;
import org.jpedal.exception.PdfSecurityException;
import org.jpedal.objects.raw.ColorSpaceObject;
import org.jpedal.objects.raw.FunctionObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.NumberUtils;
/**
* specific to Color object
*/
public class ColorObjectDecoder extends ObjectDecoder {
private static final long serialVersionUID = -5485453047644686880L;
public ColorObjectDecoder(PdfFileReader objectReader) {
super(objectReader);
}
private static final boolean debugColorspace = false;// pdfObject.getObjectRefAsString().equals("194 0 R");//pdfObject.getDebugMode();// ||
// debugFastCode;
int handleColorSpaces(PdfObject pdfObject, int i, byte[] raw) {
if (debugColorspace) showData(pdfObject, raw, i);
int len = raw.length;
// ignore any spaces
while (raw[i] == 10 || raw[i] == 13 || raw[i] == 32 || raw[i] == '[' || raw[i] == ']') {
if (raw[i] == '[') // flag as indirect as encryption will need to use this level for key
pdfObject.maybeIndirect(true);
i++;
}
if (raw[i] == '/') {
/** read the first value which is ID **/
i++;
// move cursor to start of text
while (raw[i] == 10 || raw[i] == 13 || raw[i] == 32 || raw[i] == 47)
i++;
int keyStart = i, keyLength = 0;
// move cursor to end of text
while (raw[i] != 10 && raw[i] != 13 && raw[i] != 32 && raw[i] != 47 && raw[i] != 60 && raw[i] != 62 && raw[i] != '[' && raw[i] != ']') {
i++;
keyLength++;
if (i == len) break;
}
// store value
int constant = pdfObject.setConstant(PdfDictionary.ColorSpace, keyStart, keyLength, raw);
if (constant == PdfDictionary.I) // allow for abreviation in ID command
constant = ColorSpaces.Indexed;
if (debugColorspace) System.out.println(padding + ColorSpaces.IDtoString(constant) + " Colorspace");
i = setColorspace(pdfObject, i, raw, constant);
}
else
if (raw[i] == '<' && raw[i + 1] == '<') {
i = readObjectValue(pdfObject, i, raw);
}
else
if (raw[i] == '%') { // comments
while (raw[i] != 10 && raw[i] != 13)
i++;
}
else
if (raw[i] == '<') { // its array of hex values (ie )
i = readHexValue(pdfObject, i, raw);
}
else
if (raw[i] == '(') { // its array of hex values (ie (\000\0\027)
i = readStringValue(pdfObject, i, raw);
}
else { // assume its an object
i = readColorObjectValue(pdfObject, i, raw);
}
// roll back if no gap
if (i < len && (raw[i] == 47 || raw[i] == '>')) i--;
return i;
}
private int setColorspace(PdfObject pdfObject, int i, byte[] raw, int constant) {
switch (constant) {
case ColorSpaces.CalRGB: {
i = handleColorSpaces(pdfObject, i, raw);
i++;
break;
}
case ColorSpaces.CalGray: {
i = handleColorSpaces(pdfObject, i, raw);
i++;
break;
}
case ColorSpaces.DeviceCMYK: {
break;
}
case ColorSpaces.DeviceGray: {
break;
}
case ColorSpaces.DeviceN: {
i = readDeviceNvalue(pdfObject, i, raw);
break;
}
case ColorSpaces.DeviceRGB: {
break;
}
case ColorSpaces.ICC: {
i = readDictionaryFromRefOrDirect(-1, pdfObject, "", i, raw, PdfDictionary.ColorSpace);
break;
}
case ColorSpaces.Indexed: {
i = readIndexedColorspace(pdfObject, i, raw);
break;
}
case ColorSpaces.Lab: {
i = handleColorSpaces(pdfObject, i, raw);
i++;
break;
}
case ColorSpaces.Pattern: {
break;
}
case ColorSpaces.Separation: {
i = readSeparationColorspace(pdfObject, i, raw);
break;
}
}
return i;
}
private int readDeviceNvalue(PdfObject pdfObject, int i, byte[] raw) {
int endPoint = i;
while (endPoint < raw.length && raw[endPoint] != ']')
endPoint++;
// read Components
ArrayDecoder objDecoder = new ArrayDecoder(this.objectReader, i, endPoint, PdfDictionary.VALUE_IS_STRING_ARRAY);
i = objDecoder.readArray(false, raw, pdfObject, PdfDictionary.Components);
while (raw[i] == 93 || raw[i] == 32 || raw[i] == 10 || raw[i] == 13)
i++;
if (debugColorspace) {
System.out.println(padding + "i=" + i + " DeviceN Reading altColorspace >>" + (char) raw[i] + (char) raw[i + 1] + (char) raw[i + 2]
+ (char) raw[i + 3] + (char) raw[i + 4] + (char) raw[i + 5] + (char) raw[i + 6] + (char) raw[i + 7] + (char) raw[i + 8]);
System.out.println(padding + "before alt colorspace >>" + (char) raw[i] + (char) raw[i + 1] + (char) raw[i + 2] + (char) raw[i + 3]);
System.out.println("i=" + i + " >>" + (char) raw[i] + (char) raw[i + 1] + (char) raw[i + 2] + (char) raw[i + 3] + (char) raw[i + 4]
+ (char) raw[i + 5] + (char) raw[i + 6] + (char) raw[i + 7]);
}
// read the alt colorspace
PdfObject altColorSpace = new ColorSpaceObject(-1, 0);
i = handleColorSpaces(altColorSpace, i, raw);
pdfObject.setDictionary(PdfDictionary.AlternateSpace, altColorSpace);
i++;
// read the transform
PdfObject tintTransform = new FunctionObject(-1, 0);
i = handleColorSpaces(tintTransform, i, raw);
pdfObject.setDictionary(PdfDictionary.tintTransform, tintTransform);
// check for attributes
for (int ii = i; ii < raw.length; ii++) {
if (raw[ii] == ']') {
break;
}
else
if (raw[ii] == 32 || raw[ii] == 10 || raw[ii] == 13) {// ignore spaces
}
else {
i = ii;
// read the attributes
PdfObject attributesObj = new ColorSpaceObject(-1, 0);
i = handleColorSpaces(attributesObj, i, raw);
pdfObject.setDictionary(PdfDictionary.Process, attributesObj);
i--;
ii = raw.length;
}
}
i++;
return i;
}
private int readColorObjectValue(PdfObject pdfObject, int i, byte[] raw) {
if (debugColorspace) System.out.println(padding + "(assume object) starts with " + pdfObject + ' ' + pdfObject.getObjectRefAsString()
+ " data=" + new String(raw) + " i=" + i + ' ' + raw[i] + ' ' + (char) raw[i]);
// number
int keyStart2 = i;// keyLength2=0;
while (raw[i] != 10 && raw[i] != 13 && raw[i] != 32 && raw[i] != 47 && raw[i] != 60 && raw[i] != 62 && raw[i] != 93) {
i++;
}
int number = NumberUtils.parseInt(keyStart2, i, raw);
if (debugColorspace) System.out.println(">>number=" + number + ' ' + new String(raw, keyStart2, i - keyStart2));
// generation
while (raw[i] == 10 || raw[i] == 13 || raw[i] == 32 || raw[i] == 47 || raw[i] == 60)
i++;
keyStart2 = i;
// move cursor to end of reference
while (raw[i] != 10 && raw[i] != 13 && raw[i] != 32 && raw[i] != 47 && raw[i] != 60 && raw[i] != 62)
i++;
int generation = NumberUtils.parseInt(keyStart2, i, raw);
// move cursor to start of R
while (raw[i] == 10 || raw[i] == 13 || raw[i] == 32 || raw[i] == 47 || raw[i] == 60)
i++;
if (debugColorspace) System.out.println(padding + ">>" + number + ' ' + generation + ' ' + pdfObject.getObjectRefAsString());
if (raw[i] != 82) { // we are expecting R to end ref
throw new RuntimeException("3. Unexpected value in file " + (char) raw[i] + " - please send to IDRsolutions for analysis");
}
i++;
if (pdfObject.getObjectRefID() == -1 || pdfObject.maybeIndirect()) pdfObject.setRef(number, generation);
// read the Dictionary data
byte[] data = this.objectReader.readObjectAsByteArray(pdfObject, this.objectReader.isCompressed(number, generation), number, generation);
// allow for data in Linear object not yet loaded
if (data == null) {
pdfObject.setFullyResolved(false);
if (debugFastCode) System.out.println(padding + "Data not yet loaded");
if (LogWriter.isOutput()) LogWriter.writeLog("[Linearized] " + number + ' ' + generation + " R not yet available (9)");
i = raw.length;
}
else {
// allow for direct (ie /DeviceCMYK)
if (data[0] == '/') {
handleColorSpaces(pdfObject, 0, data);
}
else {
int j = 0;
if (data[0] != '[' && data[0] != '<') {
// lose obj at start
j = 3;
while (data[j - 1] != 106 && data[j - 2] != 98 && data[j - 3] != 111) {
j++;
}
}
if (debugColorspace) System.out.println("Read obj i=" + i + " j=" + j + ' ' + (char) data[j] + (char) data[j + 1]);
handleColorSpaces(pdfObject, j, data);
}
}
return i;
}
private int readIndexedColorspace(PdfObject pdfObject, int i, byte[] raw) {
// read the base value
PdfObject IndexedColorSpace = new ColorSpaceObject(-1, 0, true);
// IndexedColorSpace.setRef(pdfObject.getObjectRefAsString());
i = handleColorSpaces(IndexedColorSpace, i, raw);
pdfObject.setDictionary(PdfDictionary.Indexed, IndexedColorSpace);
// onto hival number
while (i < raw.length && (raw[i] == 32 || raw[i] == 13 || raw[i] == 10 || raw[i] == ']' || raw[i] == '>'))
i++;
if (debugColorspace) System.out.println(padding + "Indexed Reading hival starting at >>" + (char) raw[i] + (char) raw[i + 1]
+ (char) +raw[i + 2] + "<>" + (char) raw[i] + (char) raw[i + 1] + (char) +raw[i + 2] + "<>" + (char) raw[i] + (char) raw[i + 1]);
// read the alt colorspace
PdfObject altColorSpace = new ColorSpaceObject(-1, 0);
i = handleColorSpaces(altColorSpace, i, raw);
pdfObject.setDictionary(PdfDictionary.AlternateSpace, altColorSpace);
// allow for no gap
if (raw[i] != '<') i++;
// read the transform
PdfObject tintTransform = new FunctionObject(-1, 0);
if (debugColorspace) System.out.println(padding + "Separation Reading tintTransform " + (char) raw[i - 1] + (char) raw[i] + (char) raw[i + 1]
+ " into " + tintTransform);
i = handleColorSpaces(tintTransform, i, raw);
pdfObject.setDictionary(PdfDictionary.tintTransform, tintTransform);
i++;
return i;
}
private int readObjectValue(PdfObject pdfObject, int i, byte[] raw) {
if (debugColorspace) System.out.println(padding + "Direct object starting " + (char) raw[i] + (char) raw[i + 1] + (char) raw[i + 2] + " ref="
+ pdfObject.getObjectRefAsString());
i = convertDirectDictionaryToObject(pdfObject, "", i, raw, -1);
// allow for stream
/**
* look for stream afterwards
*/
if (pdfObject.hasStream()) {
int count = raw.length, ends = 0;
for (int xx = i; xx < count - 5; xx++) {
// avoid reading on subobject ie << /DecodeParams << >> >>
if (raw[xx] == '>' && raw[xx + 1] == '>') ends++;
if (ends == 2) {
if (debugColorspace) System.out.println(padding + "Ignore Stream as not in sub-object " + pdfObject);
break;
}
if (raw[xx] == 's' && raw[xx + 1] == 't' && raw[xx + 2] == 'r' && raw[xx + 3] == 'e' && raw[xx + 4] == 'a' && raw[xx + 5] == 'm') {
if (debugColorspace) System.out.println(padding + "2. Stream found afterwards");
readStreamIntoObject(pdfObject, xx, raw);
xx = count;
}
}
}
return i;
}
private int readHexValue(PdfObject pdfObject, int i, byte[] raw) {
i++;
// find end
int end = i, validCharCount = 0;
// here
while (raw[end] != '>') {
if (raw[end] != 32 && raw[end] != 10 && raw[end] != 13) validCharCount++;
end++;
}
int byteCount = validCharCount >> 1;
byte[] stream = new byte[byteCount];
int byteReached = 0, topHex, bottomHex;
while (true) {
while (raw[i] == 32 || raw[i] == 10 || raw[i] == 13)
i++;
topHex = raw[i];
// convert to number
if (topHex >= 'A' && topHex <= 'F') {
topHex = topHex - 55;
}
else
if (topHex >= 'a' && topHex <= 'f') {
topHex = topHex - 87;
}
else
if (topHex >= '0' && topHex <= '9') {
topHex = topHex - 48;
}
else throw new RuntimeException("Unexpected number " + (char) raw[i]);
i++;
while (raw[i] == 32 || raw[i] == 10 || raw[i] == 13)
i++;
bottomHex = raw[i];
if (bottomHex >= 'A' && bottomHex <= 'F') {
bottomHex = bottomHex - 55;
}
else
if (bottomHex >= 'a' && bottomHex <= 'f') {
bottomHex = bottomHex - 87;
}
else
if (bottomHex >= '0' && bottomHex <= '9') {
bottomHex = bottomHex - 48;
}
else throw new RuntimeException("Unexpected number " + (char) raw[i]);
i++;
// calc total
int finalValue = bottomHex + (topHex << 4);
stream[byteReached] = (byte) finalValue;
byteReached++;
// System.out.println((char)topHex+""+(char)bottomHex+" "+byteReached+"/"+byteCount);
if (byteReached == byteCount) break;
}
try {
if (this.decryption != null) stream = this.decryption.decrypt(stream, pdfObject.getObjectRefAsString(), false, null, false, false);
}
catch (PdfSecurityException e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
pdfObject.setDecodedStream(stream);
return i;
}
private int readStringValue(PdfObject pdfObject, int i, byte[] raw) {
i++; // move past (
int start = i;
// find end of textstream
while (true) {
if (raw[i] == ')' && (!ObjectUtils.isEscaped(raw, i) || raw[i - 1] == 0)) break;
i++;
}
byte[] nRaw = ObjectUtils.readEscapedValue(i, raw, start, false);
try {
if (this.decryption != null) nRaw = this.decryption.decrypt(nRaw, pdfObject.getObjectRefAsString(), false, null, false, false);
}
catch (PdfSecurityException e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
pdfObject.setDecodedStream(nRaw);
return i;
}
private static void showData(PdfObject pdfObject, byte[] raw, int i) {
System.out.println(padding + "Reading colorspace into " + pdfObject + " ref=" + pdfObject.getObjectRefAsString() + " i=" + i + " chars="
+ (char) raw[i] + (char) raw[i + 1] + (char) raw[i + 2] + (char) raw[i + 3] + (char) raw[i + 4]);
System.out.println(padding + "------------>");
for (int ii = i; ii < raw.length; ii++) {
System.out.print((char) raw[ii]);
if (ii > 5 && raw[ii - 5] == 's' && raw[ii - 4] == 't' && raw[ii - 3] == 'r' && raw[ii - 2] == 'e' && raw[ii - 1] == 'a'
&& raw[ii] == 'm') ii = raw.length;
}
System.out.println("<--------");
}
int processColorSpace(PdfObject pdfObject, String objectRef, int i, byte[] raw) {
// very specific type of object
// read the base value (2 cases - Colorspace pairs or value in XObject
if (!pdfObject.ignoreRecursion()) {
if (pdfObject.getObjectType() == PdfDictionary.ColorSpace) {// pairs
return handleColorSpaces(pdfObject, i, raw);
}
else { // Direct object in XObject
PdfObject ColorSpace;
// can be called in 2 diff ways and this is difference
boolean isKey = raw[i] == '/';
if (isKey) ColorSpace = new ColorSpaceObject(objectRef);
else ColorSpace = new ColorSpaceObject(-1, 0);
pdfObject.setDictionary(PdfDictionary.ColorSpace, ColorSpace);
if (ColorSpace.isDataExternal()) {
ColorSpace.isDataExternal(true);
if (!this.resolveFully(ColorSpace)) pdfObject.setFullyResolved(false);
}
return handleColorSpaces(ColorSpace, i, raw);
}
}
return i;
}
}