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;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