org.jpedal.io.types.Array Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
An Open Source JavaFX PDF Viewer
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
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
*
* ---------------
* Array.java
* ---------------
*/
package org.jpedal.io.types;
import java.util.ArrayList;
import org.jpedal.exception.PdfSecurityException;
import org.jpedal.io.ObjectDecoder;
import static org.jpedal.io.ObjectDecoder.debugFastCode;
import static org.jpedal.io.ObjectDecoder.padding;
import org.jpedal.io.ObjectUtils;
import org.jpedal.io.PdfFileReader;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.utils.LogWriter;
/**
* parse PDF array data from PDF for mixed and object values
*/
public class Array extends ObjectDecoder implements ArrayDecoder{
final ArrayList valuesRead=new ArrayList();
int i, j2;
int type;
final byte[] raw;
byte[] arrayData;
int PDFkeyInt;
int rawLength;
private String indirectRef;
private boolean isSingle;
public Array(final PdfFileReader pdfFileReader, final int i, final int type, final byte[] raw) {
super(pdfFileReader);
this.i = i;
this.type = type;
this.raw = raw;
if(raw!=null){
rawLength=raw.length;
}
}
private boolean findStart() {
if (debugFastCode) {
System.out.println(padding + "Reading array type=" + PdfDictionary.showArrayType(type) + ' ' + (char) raw[i] + ' ' + (char) raw[i + 1] + ' ' + (char) raw[i + 2] + ' ' + (char) raw[i + 3] + ' ' + (char) raw[i + 4]);
}
//roll on
if (raw[i] != 91 && raw[i] != '<') {
i++;
}
//ignore empty
if (raw[i] == '[' && raw[i + 1] == ']') {
return true;
}
//move cursor to start of text
while (raw[i] == 10 || raw[i] == 13 || raw[i] == 32) {
i++;
}
//allow for comment
if (raw[i] == 37) {
i = StreamReaderUtils.skipComment(raw, i);
}
return false;
}
private boolean readIndirect(final PdfObject pdfObject) throws RuntimeException {
isSingle=false;
//allow for indirect to 1 item
final int startI = i;
final int[] values=StreamReaderUtils.readRefFromStream(raw,i);
final int ref=values[0];
final int generation=values[1];
i=values[2];
//read the Dictionary data
arrayData = objectReader.readObjectAsByteArray(pdfObject, objectReader.isCompressed(ref, generation), ref, generation);
if(decryption!=null){
indirectRef=ref+" "+generation+" R";
}
//allow for data in Linear object not yet loaded
if (arrayData == null) {
pdfObject.setFullyResolved(false);
LogWriter.writeLog("[Linearized] " + pdfObject.getObjectRefAsString() + " not yet available (14)");
i = rawLength;
return true;
}
//lose obj at start and roll onto [
j2 = 0;
while (arrayData[j2] != 91) {
//allow for % comment
if (arrayData[j2] == '%') {
j2 = StreamReaderUtils.skipComment(arrayData, j2);
//roll back as [ may be next char
j2--;
}
//allow for null
if (StreamReaderUtils.isNull(arrayData, j2)) {
break;
}
//allow for empty
if (arrayData[j2] == 'e' && arrayData[j2 + 1] == 'n' && arrayData[j2 + 2] == 'd' && arrayData[j2 + 3] == 'o') {
break;
}
if (arrayData[j2] == 47) { //allow for value of type 32 0 obj /FlateDecode endob
// j2--;
break;
}
if (arrayData[j2] == '<' && arrayData[j2 + 1] == '<') { //also check ahead to pick up [<<
j2 = startI;
arrayData = raw;
if (debugFastCode) {
System.out.println(padding + "Single value, not indirect "+pdfObject.getObjectRefAsString());
}
isSingle=true;
break;
}
j2++;
}
return false;
}
@Override
public int readArray(final PdfObject pdfObject, final int PDFkeyInt) {
if(raw[i]!='/' && findStart()){ //will also exit if empty array []
return i + 1;
}
this.PDFkeyInt=PDFkeyInt;
j2=i;
arrayData=raw;
if(debugFastCode) {
System.out.println(padding + "Reading array type=" + PdfDictionary.showArrayType(type) + " into " + pdfObject + ' ' + (char) raw[i] + ' ' + (char) raw[i + 1] + ' ' + (char) raw[i + 2] + ' ' + (char) raw[i + 3] + ' ' + (char) raw[i + 4]);
}
//may need to add method to PdfObject is others as well as Mask (last test to allow for /Contents null
//0 never occurs but we set as flag if called from gotoDest/DefaultActionHandler
final boolean isIndirect=raw[i]!=91 && raw[i]!='(' && raw[0]!=0 && !StreamReaderUtils.isNull(raw,i) && StreamReaderUtils.handleIndirect(raw, i);
boolean singleKey=isFirstKeySingle();
//single value ie /Filter /FlateDecode or (text)
if((type==PdfDictionary.VALUE_IS_OBJECT_ARRAY || !singleKey) && isIndirect){
readIndirect(pdfObject);
singleKey=isFirstKeySingle();
}
scanElements(singleKey, pdfObject);
//put cursor in correct place (already there if ref)
if(!isIndirect) {
i = j2;
}
if(debugFastCode) {
showValues();
}
//roll back so loop works if no spaces
if(i='0' && raw[i]<='9'))) {
i--;
}
return i;
}
void scanElements(boolean singleKey,final PdfObject pdfObject) {
singleKey=isSingleKey();
findArrayStart();
final int arrayEnd=arrayData.length;
int keyStart=moveToStartOfNextValue(),currentElement = 0;
byte[] newValues;
while (j2 < arrayEnd && arrayData[j2] != 93) {
if(StreamReaderUtils.isEndObj(arrayData,j2)){
break;
}else if(arrayData[j2]=='>' && arrayData[j2+1]=='>'){
break;
}else if(arrayData[j2-1]=='/'){ //isKey
if(type==PdfDictionary.VALUE_IS_FLOAT_ARRAY ||type==PdfDictionary.VALUE_IS_INT_ARRAY){ //must be end of values in this case
j2--;
break;
}
newValues=writeKey();
}else if(StreamReaderUtils.isRef(arrayData, j2) || (arrayData[j2]=='<' && arrayData[j2+1]=='<')){
newValues = writeObject(keyStart);
}else if(StreamReaderUtils.isNumber(arrayData, j2)){
newValues=writeNumber();
}else if(StreamReaderUtils.isNull(arrayData,j2)){
newValues=writeNull();
}else if(arrayData[j2]=='('){
newValues=writeString(pdfObject);
}else if(StreamReaderUtils.isArray(arrayData, j2)){
newValues=writeArray();
}else if(arrayData[j2+1]=='<' && arrayData[j2+2]=='<'){
newValues = writeDirectDictionary(keyStart);
}else if(arrayData[j2]=='<'){
newValues=writeHexString(pdfObject);
}else{
newValues = writeGeneral(keyStart);
}
if (debugFastCode) {
System.out.println(padding + ">
break;
}
if(arrayData[j2]==60 && arrayData[j2+1]==60) { //allow for number then object (ie 12<') {
j2++;
}
return newValues;
}
byte[] writeDirectDictionary(final int keyStart) {
//allow for straight into a <<>>
j2++;
if(debugFastCode){
System.out.println(padding + "----double <<");
}
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
}
private byte[] writeArray(){ // [59 0 R /XYZ null 711 null ] or [/DeviceN [/Black] /DeviceCMYK 16 0 R]
int depth=0;
if(debugFastCode){
System.out.println(padding + "----array");
}
final int keyStart=j2;
while(arrayData[j2]!=']' || depth>0) {
if(arrayData[j2]=='['){
depth++;
}
j2++;
if(arrayData[j2]==']'){
depth--;
}
}
//exclude end bracket
j2++;
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
}
byte[] writeString(final PdfObject pdfObject) {
if(debugFastCode){
System.out.println(padding + "----string");
}
final int keyStart=j2+1;
while(true){
if(arrayData[j2]==')' && !ObjectUtils.isEscaped(arrayData, j2)) {
break;
}
j2++;
}
byte[] newValues = ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
j2++;
try {
if (!pdfObject.isInCompressedStream() && decryption!=null) {
String ref=pdfObject.getObjectRefAsString();
if(indirectRef!=null){
ref=indirectRef;
}
newValues = decryption.decrypt(newValues,ref, false, null, false, false);
}
}catch (final PdfSecurityException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
return newValues;
}
byte[] writeObject(final int keyStart) {
if(debugFastCode){
System.out.println(padding + "----ref or direct obj");
}
while(arrayData[j2]!='R' && arrayData[j2]!=']'){
//allow for embedded object
if(arrayData[j2]=='(' && !ObjectUtils.isEscaped(arrayData, j2)){
j2 = TextStream.skipToEnd(arrayData, j2);
}else if(arrayData[j2]=='<' && arrayData[j2+1]=='<'){
int levels=1;
if(debugFastCode) {
System.out.println(padding + "Reading Direct value");
}
while(levels>0){
j2++;
if (arrayData[j2] == '(' && !ObjectUtils.isEscaped(arrayData, j2)) {
j2 = TextStream.skipToEnd(arrayData, j2);
} else if (arrayData[j2] == '<' && arrayData[j2 + 1] == '<') {
j2++;
levels++;
}else if(arrayData[j2]=='>' && arrayData[j2+1]=='>'){
j2++;
levels--;
}
}
break;
}
j2++;
}
j2++;
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, true);
}
byte[] writeNumber() {
if(debugFastCode){
System.out.println(padding + "----number");
}
j2=StreamReaderUtils.skipSpaces(arrayData,j2);
final int keyStart=j2;
while(arrayData[j2]>='0' && arrayData[j2]<='9'){
j2++;
}
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
}
byte[] writeKey() {
if(debugFastCode){
System.out.println(padding + "----key");
}
int keyStart=j2;
j2=StreamReaderUtils.skipToEndOfKey(arrayData, j2+1);
//include / so we can differentiate /9 and 9
if(keyStart>0 && arrayData[keyStart-1]==47) {
keyStart--;
}
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
}
void findArrayStart() {
if(j2<0){
j2=0;
}
//skip [ and any spaces allow for [[ in recursion
boolean startFound=false;
while(arrayData[j2]==10 || arrayData[j2]==13 || arrayData[j2]==32 || (arrayData[j2]==91 && !startFound)){
if(arrayData[j2]==91) {
startFound = true;
}
j2++;
}
if (debugFastCode) {
if(j2>0){
System.out.println(padding + "----scanElements j2="+j2+" chars="+ arrayData[j2-1]+" "+ arrayData[j2]+" "+ arrayData[j2+1]);
}else{
System.out.println(padding + "----scanElements j2="+j2+" chars="+ arrayData[j2]+" "+ arrayData[j2+1]);
}
}
}
byte[] writeNull() {
if(debugFastCode){
System.out.println(padding + "----null");
}
final int keyStart=j2;
j2 += 4;
return ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
}
byte[] writeHexString(final PdfObject pdfObject) {
if(debugFastCode){
System.out.println(padding + "----hex string");
}
boolean hexString =true;
final int keyStart=j2+1;
while(true){
if(arrayData[j2]=='>') {
break;
}
if(arrayData[j2]=='/') {
hexString = false;
}
j2++;
}
byte[] newValues = ObjectUtils.readEscapedValue(j2, arrayData, keyStart, false);
if(hexString){
if(indirectRef==null){
newValues =StreamReaderUtils.handleHexString(newValues, decryption, pdfObject.getObjectRefAsString());
}else{
newValues =StreamReaderUtils.handleHexString(newValues, decryption, indirectRef);
}
}
j2++;
return newValues;
}
void fillArray(final int elementCount, final PdfObject pdfObject) {
final byte[][] finalByteValues = new byte[elementCount][];
for(int a=0;a