
org.jpedal.function.PostscriptFactory 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-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;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{
//
}
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 {
//
}
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 {
//
}
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 {
//
}
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 {
//
}
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){
//
}
// 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){
if (LogWriter.isOutput()) {
LogWriter.writeLog("Caught an Exception " + e);
}
//
}
}else{ //execute commands
//System.out.println(" ID value : " + ID);
final int result=execute(ID);
//
if(result==-1){
//
}
}
//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
//
}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){ //error
//
} else {
currentType = stackType[stkTypePtr];
}
if(stkPtr<0 ){ //error
//
}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