Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jpedal.function.PostscriptFactory Maven / Gradle / Ivy
/*
* ===========================================
* 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-2015 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
*
* ---------------
* PostscriptFactory.java
* ---------------
*/
package org.jpedal.function;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.NumberUtils;
public class PostscriptFactory {
static final int[] scale={1,26*26,26*26*26,26*26*26*26}; //alphabet so base 26 makes it unique
static final double toBase10=Math.log(10);
private int level;
private static final byte START_BRACE = 123;
private static final byte END_BRACE = 125;
private final byte[] stream;
private final int streamLength;
private int ptr;
private static final boolean debug = false;
protected boolean testingFunction;
protected double[] stack;
private double[] safeStack;
protected int[] stackType;
private int[] safeStackType;
protected int stkPtr;
private int safeStkPtr;
protected int stkTypePtr;
private int safeStkTypePrt;
protected int currentType;
boolean cont;
/** constant for conversion*/
static final double radiansToDegrees=180f/Math.PI;
//value for boolean true
protected static final double isTrue=1;
// value for boolean false
protected static final double isFalse=0;
// ----- PS types ------
// PS intger (java int)
protected static final int PS_INTEGER = 1;
// PS real (java double)
protected static final int PS_REAL = 2;
// PS boolean (java boolean)
protected static final int PS_BOOLEAN = 3;
// left in just in case as a safety feature
protected static final int PS_UNKNOWN = 0;
public PostscriptFactory(final byte[] stream){
this.stream=stream; //raw data
streamLength=stream.length;
//System.out.println("-> PostScript STREAM data:");
//System.out.println("");
//for(int i=0;i4)
keyLength=4;
for(int j=0;j4)
System.out.println("final protected static int PS_"+cmds[i].substring(0,4)+" = "+mappedKey+";");
else
System.out.println("final protected static int PS_"+cmds[i]+" = "+mappedKey+";");
}
// build cases
System.out.println("//identify command\nprotected static int getCommandID(int value) {\nint id = -1;\nswitch (value) {");
for(int i=0;i3)
keyLength=3;
for(int j=0;j4){
System.out.println("case PS_"+cmds[i].substring(0,4)+":");
System.out.println("id=PS_"+cmds[i].substring(0,4)+";");
}else{
System.out.println("case PS_"+cmds[i]+":");
System.out.println("id=PS_"+cmds[i]+";");
}
System.out.println("break;");
}
System.out.println("\n}\nreturn id;\n}");
// build getString
//System.out.println("//identify command\nprotected static int getCommandID(int value) {\nint id = -1;\nswitch (value) {");
for(int i=0;i3)
keyLength=3;
for(int j=0;j4){
System.out.println("case PS_"+cmds[i].substring(0,4)+":");
}else{
System.out.println("case PS_"+cmds[i]+":");
}
System.out.println("str=\""+cmds[i]+"\";");
System.out.println("break;");
}
System.out.println("\n}\nreturn id;\n}");
}
/**/
//unique id for abs
protected static final int PS_abs = 317044;
//unique id for add
protected static final int PS_add = 54756;
//unique id for atan
protected static final int PS_atan = 5953532;
//unique id for ceiling
protected static final int PS_ceil = 5170050;
//unique id for cos
protected static final int PS_cos = 325834;
//unique id for cvi
protected static final int PS_cvi = 154806;
//unique id for cvr
protected static final int PS_cvr = 312990;
//unique id for div
protected static final int PS_div = 374507;
//unique id for exp
protected static final int PS_exp = 279192;
//unique id for floor
protected static final int PS_floo = 6651169;
//unique id for idiv
protected static final int PS_idiv = 9739140;
//unique id for ln
protected static final int PS_ln = 8799;
//unique id for log
protected static final int PS_log = 114931;
//unique id for mod
protected static final int PS_mod = 62204;
//unique id for mul
protected static final int PS_mul = 206868;
//unique id for neg
protected static final int PS_neg = 108173;
//unique id for sin
protected static final int PS_sin = 233914;
//unique id for sqrt
protected static final int PS_sqrt = 8992170;
//unique id for sub
protected static final int PS_sub = 31114;
//unique id for round
protected static final int PS_roun = 6301689;
//unique id for truncate
protected static final int PS_trun = 6303719;
//unique id for and
protected static final int PS_and = 61516;
//unique id for bitshift
protected static final int PS_bits = 8564921;
//unique id for eq
protected static final int PS_eq = 10820;
//unique id for false
protected static final int PS_fals = 8418909;
//unique id for ge
protected static final int PS_ge = 2710;
//unique id for gt
protected static final int PS_gt = 12850;
//unique id for le
protected static final int PS_le = 2715;
//unique id for lt
protected static final int PS_lt = 12855;
//unique id for ne
protected static final int PS_ne = 2717;
//unique id for not
protected static final int PS_not = 343421;
//unique id for or
protected static final int PS_or = 11506;
//unique id for true
protected static final int PS_true = 2190935;
//unique id for xor
protected static final int PS_xor = 308279;
//unique id for if
protected static final int PS_if = 3388;
//unique id for ifelse
protected static final int PS_ifel = 5100428;
//unique id for copy
protected static final int PS_copy = 11240530;
//unique id for exch
protected static final int PS_exch = 3249536;
//unique id for pop
protected static final int PS_pop = 273119;
//unique id for dup
protected static final int PS_dup = 277163;
//unique id for index
protected static final int PS_inde = 1889428;
//unique id for roll
protected static final int PS_roll = 5229553;
//identify command
protected static int getCommandID(final byte[] cmds) {
//default no key value
int id = -1;
//build key with same formula we used to create Constants (we've checked they are unique)
int key=0;
int keyLength=cmds.length;
if(keyLength>4) {
keyLength = 4;
}
for(int j=0;j0) {
firstInt <<= shift;
}
if(shift<0) {
firstInt >>= -shift;
}
push(firstInt, PS_INTEGER);
break;
case PostscriptFactory.PS_ceil:
// get element of the stack
first = pop();
fType = currentType;
// if negative, cast to int will strip the dec part
if(first<0){
push((int) first,fType);
} else {
final int temp = (int) first;
// has even the smallest dec part, round the number up
if(first>temp){
push(temp+1, fType);
} else {
push(first, fType);
}
}
break;
case PostscriptFactory.PS_copy:
/**
* In PS the function is designed to work with any object or
* type. Due to smaller amount of types and procedures implemented
* by adobe, we shall only implement one case (when int is a param).
*/
firstInt = popInt();
fType = currentType;
if(fType==PS_INTEGER && firstInt>0){
final double[] items = new double[firstInt];
final int[] types = new int[firstInt];
// take elements off
for(int i=0; i< items.length;i++){
items[i] = pop();
types[i] = currentType;
}
// put them back on (remember about the order)
for(int ii=items.length;ii>0 ;ii--){
push(items[ii-1], types[ii-1]);
}
// and now put the copied ones on top
for(int ii=items.length;ii>0 ;ii--){
push(items[ii-1], types[ii-1]);
}
}else if(fType==PS_INTEGER && firstInt==0){
//no need to do anything
} else if(LogWriter.isRunningFromIDE){
// never expected to happend, while dealing with PDF
throw new RuntimeException("Critical error in PS_copy");
}
break;
case PostscriptFactory.PS_cos:
// calculates the cos of a given angle (angle given in deg)
first = pop();
// calc deg -> rad
final double rad = (first/radiansToDegrees);
double angle=Math.cos(rad);
//allow for rounding error
if(angle>0 && angle<0.0000001) {
angle = 0;
} else if(angle<0 && angle>-0.0000001) {
angle = 0;
}
//push(Math.cos(rad));
push(angle, PS_REAL);
break;
case PostscriptFactory.PS_cvi:
// convert to integer
first = pop();
push((int) first, PS_INTEGER);
break;
case PostscriptFactory.PS_cvr:
// convert to a double.
first = pop();
push(first, PS_REAL);
break;
case PostscriptFactory.PS_div:
// dividing, resutlt is a double (has decimal part)
first = pop();
second = pop();
push(second/first,PS_REAL);
break;
case PostscriptFactory.PS_dup:
calculateDup();
break;
case PostscriptFactory.PS_eq:
// pushes true if the objects are equal, and false if they are not
first = pop();
second = pop();
if(first==second){
push(isTrue, PS_BOOLEAN);
} else {
push(isFalse, PS_BOOLEAN);
}
break;
case PostscriptFactory.PS_exch:
//exchange the top 2 elements
// check if enough elements on the stack
if(stack.length<2) {
throw new RuntimeException("EXCH - not enough elements on the stack");
}
first = pop();
fType = currentType;
second = pop();
sType = currentType;
push(first, fType);
push(second, sType);
break;
case PostscriptFactory.PS_exp:
// raises second to the ower of first.
first = pop();
second = pop();
push(Math.pow(second,first), PS_REAL);
break;
case PostscriptFactory.PS_fals:
//puts a false boolean value at the top of the stacks
push(0,PS_BOOLEAN);
break;
case PostscriptFactory.PS_floo:
// puts the lower value back on to the stack
// 3.2 -> 3.0 , -4.8 -> -5.0
first = pop();
fType = currentType;
if(first>0){
push((int) first, fType);
} else {
final int temp = (int) first;
if(temp>first){
push(temp-1, fType);
} else {
push(first, fType);
}
}
break;
case PostscriptFactory.PS_ge:
// if the first operand is greater or equal than the other push true else false
first = pop();
fType = currentType;
second = pop();
sType = currentType;
if((fType==PS_INTEGER || fType==PS_REAL) && (sType==PS_INTEGER || sType==PS_REAL)){
if(second>=first){
push(1, PS_BOOLEAN);
}else{
push(0, PS_BOOLEAN);
}
} else if(LogWriter.isRunningFromIDE){
// should never happend, will exit
throw new RuntimeException("Critical error in PS_ge");
}
break;
case PostscriptFactory.PS_gt:
// if the first operand is greater than the other push true else false
first = pop();
fType = currentType;
second = pop();
sType = currentType;
if((fType==PS_INTEGER || fType==PS_REAL) && (sType==PS_INTEGER || sType==PS_REAL)){
if(second>first){
push(1, PS_BOOLEAN);
}else{
push(0, PS_BOOLEAN);
}
} else if(LogWriter.isRunningFromIDE){
// should never happend, will exit
throw new RuntimeException("Critical error in PS_gt");
}
break;
case PostscriptFactory.PS_idiv:
// like div but the result is striped of its dec part
final int one = popInt();
final int two = popInt();
push((two/one), PS_INTEGER);
break;
case PostscriptFactory.PS_if:
/**
* No examples found to properly test this method.
* According to doc first would recieve a instruction
* set, which execution depends on second being true(exec)
* or false(do not exec).
*/
if(!cont){
System.arraycopy(safeStack, 0, stack, 0, 100);
System.arraycopy(safeStackType, 0, stackType, 0, 100);
this.stkPtr= safeStkPtr;
this.stkTypePtr = safeStkTypePrt;
}
cont = false;
break;
case PostscriptFactory.PS_inde:
calculateIndex();
break;
case PostscriptFactory.PS_le:
// if the first operand is less or equal than the other push true else false
first = pop();
fType = currentType;
second = pop();
sType = currentType;
if((fType==PS_INTEGER || fType==PS_REAL) && (sType==PS_INTEGER || sType==PS_REAL)){
if(second<=first){
push(1, PS_BOOLEAN);
}else{
push(0, PS_BOOLEAN);
}
} else if(LogWriter.isRunningFromIDE){
// should never happend, will exit
throw new RuntimeException("Critical error in PS_le");
}
break;
case PostscriptFactory.PS_lt:
// if the first operand is less than the other push true else false
first = pop();
fType = currentType;
second = pop();
sType = currentType;
if((fType==PS_INTEGER || fType==PS_REAL) && (sType==PS_INTEGER || sType==PS_REAL)){
if(second0){
push((int) first, fType);
} else {
final int tem = (int) first;
if(tem>first){
push((tem-1), fType);
} else {
push((int) first, fType);
}
}
break;
case PostscriptFactory.PS_sin:
// calc sin of a given angle
first = pop();
push(Math.sin(first/PostscriptFactory.radiansToDegrees), PS_REAL);
break;
case PostscriptFactory.PS_sqrt:
first = pop();
// make sure the number is not negative
if(first>=0){
push(Math.sqrt(first), PS_REAL);
} else {
System.err.println("SQRT - cant sqrt a negative number!");
}
break;
case PostscriptFactory.PS_sub:
// subtract the element at the top of the
// stack form the one blow it.
// check if enough elements on the stack
if(stack.length<2) {
throw new RuntimeException("SUB - not enough elements on the stack");
}
first = pop();
fType = currentType;
second = pop();
sType = currentType;
if(fType == PS_REAL || sType == PS_REAL) {
push(second - first, PS_REAL);
} else {
push(second - first, PS_INTEGER);
}
/**
* If the result would happend to be outside of integer range
* the PS_type should be set to real, for the time being I am leaving
* it as integer though as the other situation is quite unliekly to
* happend.
*/
break;
case PostscriptFactory.PS_trun:
// strip the decimal part so
// 3.2 -> 3.0 , -4.8 -> -4.0
first = pop();
fType = currentType;
push((int) first, fType);
break;
case PostscriptFactory.PS_true:
// puts a true boolean val at the top of the stack
push(1,PS_BOOLEAN);
break;
case PostscriptFactory.PS_xor:
firstInt = popInt();
fType = currentType;
final int secondInt = popInt();
sType = currentType;
if(fType == PS_BOOLEAN && sType == PS_BOOLEAN){
push((firstInt^secondInt),PS_BOOLEAN);
} else if (fType == PS_INTEGER && sType == PS_INTEGER){
push((firstInt^secondInt),PS_INTEGER);
} else if(LogWriter.isRunningFromIDE){
// should never happend, will exit
throw new RuntimeException("Critical error in PS_xor");
}
break;
//==============================
//flag error
default:
returnValue=-1;
break;
}
return returnValue;
}
private void calculateAtan() {
final double first;
final double second;
first = pop();
second = pop();
// both params cant be zero!
if(first==0 && second==0){
System.err.println("ATAN - invalid parameters");
}
// calc the tangent
final double tangent = second/first;
// depending on which quardrant in the x,y space we end up in
//
if(first>=0 && second>=0){
// 0 to 90 - 1st quadrant
push(Math.toDegrees(Math.atan(tangent)), PS_REAL);
} else if (first>0 && second<=0){
// 90 to 180 - 2nd quadrant
double tmp = Math.toDegrees(Math.atan(tangent));
if(tmp<0) {
tmp = -tmp;
}
push(tmp+90, PS_REAL);
} else if (first<=0 && second<=0){
// 180 to 270 - 3rd quadrant
double tmp = Math.toDegrees(Math.atan(tangent));
if(tmp<0) {
tmp = -tmp;
}
push(tmp+180, PS_REAL);
} else if (first<=0 && second>=0){
// 270 to 360 - 4th quadrant
double tmp = Math.toDegrees(Math.atan(tangent));
if(tmp<0) {
tmp = -tmp;
}
push(tmp+270, PS_REAL);
}
}
private void calculateDup() {
// get duplicate and place it on the stack
final double value=pop();
final int type = currentType;
push(value, type);
push(value, type);
}
private void calculateIndex() {
// get the n element
final int n = popInt();
if(n==0){
calculateDup();
} else if (n>0) {
final double[] temp;
final int [] types;
temp = new double[n];
types = new int[n];
// take n elements of the stack
for(int i=0; i< temp.length;i++){
temp[i] = pop();
types[i] = currentType;
}
// copy the remaining one
final double val=pop();
final int fType = currentType;
push(val, fType);
//put rest back (allow for reverse order)
for(int ii=temp.length;ii>0 ;ii--){
push(temp[ii-1], types[ii-1]);
}
// put the copied one on top of the stack
push(val, fType);
} else if (n<0) {
System.err.println("-> Index : critical error, n has to be nonnegative");
}
}
private void calculateRoll() {
int amount=popInt();
int numberOfElements=popInt();
if(numberOfElements<0 && LogWriter.isRunningFromIDE){
throw new RuntimeException("-> Roll : critical error");
}
// allow for case when rolling over a larger number than elements on stack
if(numberOfElements>stkPtr){
numberOfElements=stkPtr;
}
if(amount>0){
// top elements
final double[] topTemp = new double[amount];
final int[] topTypes = new int[amount];
if(numberOfElements-amount<=0){
return;
}
// bottom elements
final double[] bottomTemp = new double[numberOfElements-amount];
final int[] bottomTypes = new int[numberOfElements-amount];
// take top elements off
for(int i =0;i0 ;ii--){
push(topTemp[ii-1], topTypes[ii-1]);
}
// put whats left back on top of the stk
for(int yy=bottomTemp.length;yy>0 ;yy--){
push(bottomTemp[yy-1], bottomTypes[yy-1]);
}
} else if(amount<0){
amount=-amount;
// top elements
final double[] topTemp = new double[numberOfElements-amount];
final int[] topTypes = new int[numberOfElements-amount];
// bottom elements
final double[] bottomTemp = new double[amount];
final int[] bottomTypes = new int[amount];
// take top elements off
for(int i =0;i0 ;ii--){
push(topTemp[ii-1], topTypes[ii-1]);
}
// put whats left back on top of the stk
for(int yy=bottomTemp.length;yy>0 ;yy--){
push(bottomTemp[yy-1], bottomTypes[yy-1]);
}
}
}
/**
* work through Postscript stream reading commands and executing
*/
public double[] executePostscript() {
boolean firstBracket = false;
//reset pointer to start in stream
this.ptr=0;
if(debug){
System.out.println("-----stream data--------\n");
for(int aa=0;aa0){
cont = true;
}
}else {
throw new RuntimeException("Possible syntax error in PostScript stream!");
}
}
firstBracket = true;
}else{
final int ID=getCommandID(nextVal);
if(ID==-1){ //read parameter and put on stack
try{
final double number=convertToDouble(nextVal);
if(debug) {
System.out.println("number=" + number);
}
//determine if it is a double or int
final int numberInt = (int) number;
if(numberInt == number) {
push(number, PS_INTEGER);
} else {
push(number, PS_REAL);
}
}catch(final Exception e){
LogWriter.writeLog("Exception " + e);
}
}else{ //execute commands
//System.out.println(" ID value : " + ID);
final int result=execute(ID);
if(result==-1 && LogWriter.isRunningFromIDE){
throw new RuntimeException("Unsupported command with value "+PostscriptUtils.toString(ID));
}
}
//show stack
if(debug) {
final StringBuilder str=new StringBuilder("Stack now ");
for(int ii=0;ii=streamLength) {
break;
}
}
return stack;
}
/**
* put number on stack
* @param number
*/
private void push(final double number, final int type) {
if(stkPtr>99 || stkTypePtr>99){ //error
if(LogWriter.isRunningFromIDE){
throw new RuntimeException("Stack or stackType overflow");
}
}else{
stack[stkPtr]=number;
stackType[stkTypePtr] = type;
}
stkPtr++;
stkTypePtr++;
}
/**
* take number from stack
*/
@SuppressWarnings("UnusedAssignment")
private double pop() {
double value=0;
stkPtr--;
stkTypePtr--;
if(stkTypePtr<0){
if(LogWriter.isRunningFromIDE){ //error
throw new RuntimeException("Stack type underflow");
}
} else {
currentType = stackType[stkTypePtr];
}
if(stkPtr<0 ){ //error
if(LogWriter.isRunningFromIDE){
throw new RuntimeException("Stack underflow");
}
}else {
value = stack[stkPtr];
}
return value;
}
/**
* take number from stack
*/
private int popInt() {
return (int)pop();
}
/**
* convert byteStream to double primitive
* @param nextVal
* @return
*/
private static double convertToDouble(final byte[] stream) {
final double d;
final int start=0;
final int charCount=stream.length;
int ptr=charCount;
int intStart=0;
boolean isMinus=false;
//hand optimised float code
//find decimal point
for(int j=charCount-1;j>-1;j--){
if(stream[start+j]==46){ //'.'=46
ptr=j;
break;
}
}
int intChars=ptr;
//allow for minus
if(stream[start]==43){ //'+'=43
intChars--;
intStart++;
}else if(stream[start]==45){ //'-'=45
//intChars--;
intStart++;
isMinus=true;
}
//optimisations
final int intNumbers=intChars-intStart;
int decNumbers=charCount-ptr;
if((intNumbers>3)){ //non-optimised to cover others
isMinus=false;
d=Double.parseDouble(new String(stream));
}else{
if(decNumbers>6){ //old code used this accuracy so kept to avoid lots of minor changes
decNumbers=6;
}
d = NumberUtils.convertStreamFromDouble(stream, start + intStart, start + ptr, intNumbers, decNumbers);
}
if(isMinus) {
return -d;
} else {
return d;
}
}
private byte[] getNextValue() {
final int start;
int end;
int next;
byte[] returnValue=null;
//skip to start of next value
while(ptr=streamLength) {
break;
}
next=stream[ptr];
if(next==32 || next ==13 || next==10 || next==START_BRACE || next==END_BRACE) {
break;
}
}
//track level of recusrion and increment on start to make loop roll on
if(stream[start]==START_BRACE){
ptr++;
level++;
}else if(stream[start]==END_BRACE){
level--;
}
end=ptr;
//put value into array to return for further processing
if(end>=start){
//strip and excess zeros from numbers (ie 1.00000000000 becomes 1)
// while(end-start>1 && (stream[end-1]=='0' || stream[end-1]=='.')) {
// end--;
// }
final int len=end-start;
returnValue=new byte[len];
System.arraycopy(stream, start, returnValue, start - start, end - start);
}
if(debug){
System.out.print(">>>>>>>> ");
for(int aa=start;aa© 2015 - 2025 Weber Informatics LLC | Privacy Policy