org.jpedal.function.PostscriptFactory 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
*
* ---------------
* PostscriptFactory.java
* ---------------
*/
package org.jpedal.function;
public class PostscriptFactory {
static final int[] scale = { 1, 26 * 26, 26 * 26 * 26, 26 * 26 * 26 * 26 }; // alphabet so base 26 makes it unique
final static double toBase10 = Math.log(10);
private int level = 0;
private static final byte START_BRACE = 123;
private static final byte END_BRACE = 125;
private byte[] stream;
private int ptr, streamLength;
static final private boolean debug = false;
protected boolean testingFunction = false;
protected double[] stack;
private double[] safeStack;
protected int[] stackType;
private int[] safeStackType;
protected int stkPtr = 0;
private int safeStkPtr = 0;
protected int stkTypePtr = 0;
private int safeStkTypePrt = 0;
protected int currentType = 0;
boolean cont = false;
/** constant for conversion */
final static double radiansToDegrees = 180f / Math.PI;
// value for boolean true
static protected final double isTrue = 1;
// value for boolean false
static protected final double isFalse = 0;
// ----- PS types ------
// PS intger (java int)
static protected final int PS_INTEGER = 1;
// PS real (java double)
static protected final int PS_REAL = 2;
// PS boolean (java boolean)
static protected final int PS_BOOLEAN = 3;
// left in just in case as a safety feature
static protected final int PS_UNKNOWN = 0;
public PostscriptFactory(byte[] stream) {
this.stream = stream; // raw data
this.streamLength = stream.length;
// System.out.println("-> PostScript STREAM data:");
// System.out.println("");
for (int i = 0; i < this.streamLength; i++) {
// System.out.print((char)stream[i]);
}
// System.out.println("");
}
// unique id for abs
final protected static int PS_abs = 317044;
// unique id for add
final protected static int PS_add = 54756;
// unique id for atan
final protected static int PS_atan = 5953532;
// unique id for ceiling
final protected static int PS_ceil = 5170050;
// unique id for cos
final protected static int PS_cos = 325834;
// unique id for cvi
final protected static int PS_cvi = 154806;
// unique id for cvr
final protected static int PS_cvr = 312990;
// unique id for div
final protected static int PS_div = 374507;
// unique id for exp
final protected static int PS_exp = 279192;
// unique id for floor
final protected static int PS_floo = 6651169;
// unique id for idiv
final protected static int PS_idiv = 9739140;
// unique id for ln
final protected static int PS_ln = 8799;
// unique id for log
final protected static int PS_log = 114931;
// unique id for mod
final protected static int PS_mod = 62204;
// unique id for mul
final protected static int PS_mul = 206868;
// unique id for neg
final protected static int PS_neg = 108173;
// unique id for sin
final protected static int PS_sin = 233914;
// unique id for sqrt
final protected static int PS_sqrt = 8992170;
// unique id for sub
final protected static int PS_sub = 31114;
// unique id for round
final protected static int PS_roun = 6301689;
// unique id for truncate
final protected static int PS_trun = 6303719;
// unique id for and
final protected static int PS_and = 61516;
// unique id for bitshift
final protected static int PS_bits = 8564921;
// unique id for eq
final protected static int PS_eq = 10820;
// unique id for false
final protected static int PS_fals = 8418909;
// unique id for ge
final protected static int PS_ge = 2710;
// unique id for gt
final protected static int PS_gt = 12850;
// unique id for le
final protected static int PS_le = 2715;
// unique id for lt
final protected static int PS_lt = 12855;
// unique id for ne
final protected static int PS_ne = 2717;
// unique id for not
final protected static int PS_not = 343421;
// unique id for or
final protected static int PS_or = 11506;
// unique id for true
final protected static int PS_true = 2190935;
// unique id for xor
final protected static int PS_xor = 308279;
// unique id for if
final protected static int PS_if = 3388;
// unique id for ifelse
final protected static int PS_ifel = 5100428;
// unique id for copy
final protected static int PS_copy = 11240530;
// unique id for exch
final protected static int PS_exch = 3249536;
// unique id for pop
final protected static int PS_pop = 273119;
// unique id for dup
final protected static int PS_dup = 277163;
// unique id for index
final protected static int PS_inde = 1889428;
// unique id for roll
final protected static int PS_roll = 5229553;
// get string for command
protected static String toString(int id) {
String str;
switch (id) {
case PS_abs:
str = "abs";
break;
case PS_add:
str = "add";
break;
case PS_atan:
str = "atan";
break;
case PS_ceil:
str = "ceiling";
break;
case PS_cos:
str = "cos";
break;
case PS_cvi:
str = "cvi";
break;
case PS_cvr:
str = "cvr";
break;
case PS_div:
str = "div";
break;
case PS_exp:
str = "exp";
break;
case PS_floo:
str = "floor";
break;
case PS_idiv:
str = "idiv";
break;
case PS_ln:
str = "ln";
break;
case PS_log:
str = "log";
break;
case PS_mod:
str = "mod";
break;
case PS_mul:
str = "mul";
break;
case PS_neg:
str = "neg";
break;
case PS_sin:
str = "sin";
break;
case PS_sqrt:
str = "sqrt";
break;
case PS_sub:
str = "sub";
break;
case PS_roun:
str = "round";
break;
case PS_trun:
str = "truncate";
break;
case PS_and:
str = "and";
break;
case PS_bits:
str = "bitshift";
break;
case PS_eq:
str = "eq";
break;
case PS_fals:
str = "false";
break;
case PS_ge:
str = "ge";
break;
case PS_gt:
str = "gt";
break;
case PS_le:
str = "le";
break;
case PS_lt:
str = "lt";
break;
case PS_ne:
str = "ne";
break;
case PS_not:
str = "not";
break;
case PS_or:
str = "or";
break;
case PS_true:
str = "true";
break;
case PS_xor:
str = "xor";
break;
case PS_if:
str = "if";
break;
case PS_ifel:
str = "ifelse";
break;
case PS_copy:
str = "copy";
break;
case PS_exch:
str = "exch";
break;
case PS_pop:
str = "pop";
break;
case PS_dup:
str = "dup";
break;
case PS_inde:
str = "index";
break;
case PS_roll:
str = "roll";
break;
default:
str = "UNKNOWN";
}
return str;
}
// identify command
protected static int getCommandID(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; j < keyLength; j++)
key = key + (cmds[j] - 'a') * scale[j];
switch (key) {
case PS_abs:
id = PS_abs;
break;
case PS_add:
id = PS_add;
break;
case PS_atan:
id = PS_atan;
break;
case PS_ceil:
id = PS_ceil;
break;
case PS_cos:
id = PS_cos;
break;
case PS_cvi:
id = PS_cvi;
break;
case PS_cvr:
id = PS_cvr;
break;
case PS_div:
id = PS_div;
break;
case PS_exp:
id = PS_exp;
break;
case PS_floo:
id = PS_floo;
break;
case PS_idiv:
id = PS_idiv;
break;
case PS_ln:
id = PS_ln;
break;
case PS_log:
id = PS_log;
break;
case PS_mod:
id = PS_mod;
break;
case PS_mul:
id = PS_mul;
break;
case PS_neg:
id = PS_neg;
break;
case PS_sin:
id = PS_sin;
break;
case PS_sqrt:
id = PS_sqrt;
break;
case PS_sub:
id = PS_sub;
break;
case PS_roun:
id = PS_roun;
break;
case PS_trun:
id = PS_trun;
break;
case PS_and:
id = PS_and;
break;
case PS_bits:
id = PS_bits;
break;
case PS_eq:
id = PS_eq;
break;
case PS_fals:
id = PS_fals;
break;
case PS_ge:
id = PS_ge;
break;
case PS_gt:
id = PS_gt;
break;
case PS_le:
id = PS_le;
break;
case PS_lt:
id = PS_lt;
break;
case PS_ne:
id = PS_ne;
break;
case PS_not:
id = PS_not;
break;
case PS_or:
id = PS_or;
break;
case PS_true:
id = PS_true;
break;
case PS_xor:
id = PS_xor;
break;
case PS_if:
id = PS_if;
break;
case PS_ifel:
id = PS_ifel;
break;
case PS_copy:
id = PS_copy;
break;
case PS_exch:
id = PS_exch;
break;
case PS_pop:
id = PS_pop;
break;
case PS_dup:
id = PS_dup;
break;
case PS_inde:
id = PS_inde;
break;
case PS_roll:
id = PS_roll;
break;
}
return id;
}
/**
*
* @param id
*
* execute commands or return -1 as error
*/
protected int execute(int id) {
// will be reset by default if command unrecognised
int returnValue = 0;
// code taken from elsewhere so ignore
// debug data
int fType = 0;
int sType = 0;
int firstInt;
double first;
double second;
// ADD COMMANDS in AlphaBetical order!!!!
switch (id) {
case PostscriptFactory.PS_abs:
// take val of the top of the stack
first = pop();
// put the absolute val back at the top
if (first < 0) {
push(-first, PS_INTEGER);
}
else {
push(first, PS_INTEGER);
}
break;
case PostscriptFactory.PS_add:
// simple add
// check if enough elements on the stack
if (this.stack.length < 2) throw new RuntimeException("ADD - not enough elements on the stack");
first = pop();
fType = this.currentType;
second = pop();
sType = this.currentType;
if (fType == PS_REAL || sType == PS_REAL) push(first + second, PS_REAL);
else push(first + second, PS_INTEGER);
break;
case PostscriptFactory.PS_and:
// performs a bitwise and on int and booleans
first = pop();
fType = this.currentType;
second = pop();
sType = this.currentType;
if (fType == PS_INTEGER && sType == PS_INTEGER) push((int) first & (int) second, PS_INTEGER);
else
if (fType == PS_BOOLEAN && sType == PS_BOOLEAN) push((int) first & (int) second, PS_BOOLEAN);
else {}
break;
case PostscriptFactory.PS_atan:
calculateAtan();
break;
case PostscriptFactory.PS_bits:
// bit shift operation times shift
int shift = popInt();
fType = this.currentType;
firstInt = popInt();
sType = this.currentType;
if (fType != PS_INTEGER || sType != PS_INTEGER) {}
if (shift > 0) firstInt = firstInt << shift;
if (shift < 0) firstInt = firstInt >> -shift;
push(firstInt, PS_INTEGER);
break;
case PostscriptFactory.PS_ceil:
// get element of the stack
first = pop();
fType = this.currentType;
// if negative, cast to int will strip the dec part
if (first < 0) {
push((int) first, fType);
}
else {
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 = this.currentType;
if (fType == PS_INTEGER && firstInt > 0) {
double[] items = new double[firstInt];
int[] types = new int[firstInt];
// take elements off
for (int i = 0; i < items.length; i++) {
items[i] = pop();
types[i] = this.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
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 (this.stack.length < 2) throw new RuntimeException("EXCH - not enough elements on the stack");
first = pop();
fType = this.currentType;
second = pop();
sType = this.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 = this.currentType;
if (first > 0) {
push((int) first, fType);
}
else {
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 = this.currentType;
second = pop();
sType = this.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 = this.currentType;
second = pop();
sType = this.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
int one = popInt();
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 (!this.cont) {
System.arraycopy(this.safeStack, 0, this.stack, 0, 100);
System.arraycopy(this.safeStackType, 0, this.stackType, 0, 100);
this.stkPtr = this.safeStkPtr;
this.stkTypePtr = this.safeStkTypePrt;
}
this.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 = this.currentType;
second = pop();
sType = this.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 = this.currentType;
second = pop();
sType = this.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_ln:
// the same as log but the base is e (natural logarithm)
first = pop();
push(Math.log(first), PS_REAL);
break;
case PostscriptFactory.PS_log:
// calculates the log bse 10 of the top element on the stack (takes it off)
// and puts the res back on top
first = pop();
// push(Math.log10(first), PS_REAL);
push(Math.log(first) / toBase10, PS_REAL);
break;
case PostscriptFactory.PS_mod:
// get top two elements of the stack
if (fType != PS_INTEGER || sType != PS_INTEGER) {
System.err.println("PS_mod - both values must be integers!");
}
first = pop();
fType = this.currentType;
second = pop();
sType = this.currentType;
// put result on stack
push(second % first, PS_INTEGER);
break;
case PostscriptFactory.PS_mul:
// multiply two top elements of the stack
// check if enough elements on the stack
if (this.stack.length < 2) throw new RuntimeException("MUL - not enough elements on the stack");
first = pop();
second = pop();
/**
* 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.
*/
push(first * second, PS_INTEGER);
break;
case PostscriptFactory.PS_ne:
// pop two obj of the stack, if equal push FALSE else TRUE
first = pop();
second = pop();
if (first != second) {
push(isTrue, PS_BOOLEAN);
}
else {
push(isFalse, PS_BOOLEAN);
}
break;
case PostscriptFactory.PS_neg:
// showStack();
// if top of the stack is zero don't negate
double temp = pop();
fType = this.currentType;
if (temp != 0) {
push(-temp, fType);
}
else {
push(temp, fType);
}
// showStack();
break;
case PostscriptFactory.PS_not:
first = pop();
fType = this.currentType;
// int type=getType(first);
if (first == 0 && fType == PS_BOOLEAN) {
// assume is boolean
push(isTrue, PS_BOOLEAN);
}
else
if (first == 1 && fType == PS_BOOLEAN) {
// assume is boolean
push(isFalse, PS_BOOLEAN);
}
else {
// push bitwise not
push(~(int) first, PS_INTEGER);
}
break;
case PostscriptFactory.PS_or:
// prefroms bitwise or, works with boolean and int
first = pop();
fType = this.currentType;
second = pop();
fType = this.currentType;
if (fType == PS_BOOLEAN && sType == PS_BOOLEAN) push((int) first | (int) second, PS_BOOLEAN);
else
if (fType == PS_INTEGER && sType == PS_INTEGER) push((int) first | (int) second, PS_INTEGER);
else {}
break;
case PostscriptFactory.PS_pop:
// discard the the element at the top of the stack
pop();
break;
case PostscriptFactory.PS_roll:
calculateRoll();
break;
case PostscriptFactory.PS_roun:
// works almost like PS_floo
first = pop();
fType = this.currentType;
first = first + 0.5f;
if (first > 0) {
push((int) first, fType);
}
else {
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 (this.stack.length < 2) throw new RuntimeException("SUB - not enough elements on the stack");
first = pop();
fType = this.currentType;
second = pop();
sType = this.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 = this.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 = this.currentType;
int secondInt = popInt();
sType = this.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() {
double first;
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
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
double value = pop();
int type = this.currentType;
push(value, type);
push(value, type);
}
private void calculateIndex() {
// get the n element
int n = popInt();
if (n == 0) {
calculateDup();
}
else
if (n > 0) {
double[] temp;
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] = this.currentType;
}
// copy the remaining one
double val = pop();
int fType = this.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 > this.stkPtr) {
numberOfElements = this.stkPtr;
}
if (amount > 0) {
// top elements
double[] topTemp = new double[amount];
int[] topTypes = new int[amount];
if (numberOfElements - amount <= 0) {
return;
}
// bottom elements
double[] bottomTemp = new double[numberOfElements - amount];
int[] bottomTypes = new int[numberOfElements - amount];
// take top elements off
for (int i = 0; i < topTemp.length; i++) {
topTemp[i] = pop();
topTypes[i] = this.currentType;
}
// take bottom elements of the stack
for (int y = 0; y < bottomTemp.length; y++) {
bottomTemp[y] = pop();
bottomTypes[y] = this.currentType;
}
// stack should be empty here
// put what was on top before first on the stack
for (int ii = topTemp.length; ii > 0; 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
double[] topTemp = new double[numberOfElements - amount];
int[] topTypes = new int[numberOfElements - amount];
// bottom elements
double[] bottomTemp = new double[amount];
int[] bottomTypes = new int[amount];
// take top elements off
for (int i = 0; i < topTemp.length; i++) {
topTemp[i] = pop();
topTypes[i] = this.currentType;
}
// take bottom elements of the stack
for (int y = 0; y < bottomTemp.length; y++) {
bottomTemp[y] = pop();
bottomTypes[y] = this.currentType;
}
// stack should be empty here
// put what was on top before first on the stack
for (int ii = topTemp.length; ii > 0; 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; aa < this.streamLength; aa++) {
System.out.print((char) this.stream[aa]);
}
System.out.print("<<<<");
System.out.println("-------------\n");
}
this.level = 0; // regression level reached { increases and } decreases
byte[] nextVal;
// read next value
while (this.ptr < this.streamLength) {
// get key
nextVal = getNextValue();
// we have value so get Command ID
if (nextVal != null) {
/**
* execute command or read value
*/
if (nextVal.length == 1 && (nextVal[0] == START_BRACE || nextVal[0] == END_BRACE)) { // recursion
if (firstBracket && (nextVal[0] == START_BRACE)) {
double i = pop();
int fType = this.currentType;
this.safeStack = new double[100];
this.safeStackType = new int[100];
System.arraycopy(this.stack, 0, this.safeStack, 0, 100);
System.arraycopy(this.stackType, 0, this.safeStackType, 0, 100);
this.safeStkPtr = this.stkPtr;
this.safeStkTypePrt = this.stkTypePtr;
if (fType == PS_BOOLEAN) {
if (i > 0) {
this.cont = true;
}
}
else throw new RuntimeException("Possible syntax error in PostScript stream!");
}
firstBracket = true;
}
else {
int ID = getCommandID(nextVal);
if (ID == -1) { // read parameter and put on stack
try {
double number = convertToDouble(nextVal);
if (debug) System.out.println("number=" + number);
// determine if it is a double or int
int numberInt = (int) number;
if (numberInt == number) push(number, PS_INTEGER);
else push(number, PS_REAL);
}
catch (Exception e) {
}
}
else { // execute commands
// System.out.println(" ID value : " + ID);
int result = execute(ID);
if (result == -1) {}
}
// show stack
if (debug) showStack();
}
}
// should be set to 1 on first call as first entry should be {
if (this.level == 0) break;
// safety trap
if (this.ptr >= this.streamLength) break;
}
return this.stack;
}
private void showStack() {
String str = "Stack now ";
for (int ii = 0; ii < this.stkPtr; ii++)
str = str + this.stack[ii] + ' ';
System.out.println(str);
}
/**
* put number on stack
*
* @param number
*/
private void push(double number, int type) {
if (this.stkPtr > 99 || this.stkTypePtr > 99) { // error
}
else {
this.stack[this.stkPtr] = number;
this.stackType[this.stkTypePtr] = type;
}
this.stkPtr++;
this.stkTypePtr++;
}
/**
* take number from stack
*/
private double pop() {
double value = 0;
this.stkPtr--;
this.stkTypePtr--;
if (this.stkTypePtr < 0) { // error
}
else this.currentType = this.stackType[this.stkTypePtr];
if (this.stkPtr < 0) { // error
}
else value = this.stack[this.stkPtr];
return value;
}
/**
* take number from stack
*/
private int popInt() {
return (int) pop();
}
/**
* convert byteStream to double primitive
*
*/
private static double convertToDouble(byte[] stream) {
// System.err.println("---------");
// for(int ii=0;ii -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
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 {
double units = 0d, tens = 0d, hundreds = 0d, tenths = 0d, hundredths = 0d, thousands = 0d, tenthousands = 0d, hunthousands = 0d;
int c;
// hundreds
if (intNumbers > 2) {
c = stream[start + intStart] - 48;
switch (c) {
case 1:
hundreds = 100.0d;
break;
case 2:
hundreds = 200.0d;
break;
case 3:
hundreds = 300.0d;
break;
case 4:
hundreds = 400.0d;
break;
case 5:
hundreds = 500.0d;
break;
case 6:
hundreds = 600.0d;
break;
case 7:
hundreds = 700.0d;
break;
case 8:
hundreds = 800.0d;
break;
case 9:
hundreds = 900.0d;
break;
}
intStart++;
}
// tens
if (intNumbers > 1) {
c = stream[start + intStart] - 48;
switch (c) {
case 1:
tens = 10.0d;
break;
case 2:
tens = 20.0d;
break;
case 3:
tens = 30.0d;
break;
case 4:
tens = 40.0d;
break;
case 5:
tens = 50.0d;
break;
case 6:
tens = 60.0d;
break;
case 7:
tens = 70.0d;
break;
case 8:
tens = 80.0d;
break;
case 9:
tens = 90.0d;
break;
}
intStart++;
}
// units
if (intNumbers > 0) {
c = stream[start + intStart] - 48;
switch (c) {
case 1:
units = 1.0d;
break;
case 2:
units = 2.0d;
break;
case 3:
units = 3.0d;
break;
case 4:
units = 4.0d;
break;
case 5:
units = 5.0d;
break;
case 6:
units = 6.0d;
break;
case 7:
units = 7.0d;
break;
case 8:
units = 8.0d;
break;
case 9:
units = 9.0d;
break;
}
}
// tenths
if (decNumbers > 1) {
ptr++; // move beyond.
c = stream[start + ptr] - 48;
switch (c) {
case 1:
tenths = 0.1d;
break;
case 2:
tenths = 0.2d;
break;
case 3:
tenths = 0.3d;
break;
case 4:
tenths = 0.4d;
break;
case 5:
tenths = 0.5d;
break;
case 6:
tenths = 0.6d;
break;
case 7:
tenths = 0.7d;
break;
case 8:
tenths = 0.8d;
break;
case 9:
tenths = 0.9d;
break;
}
}
// hundredths
if (decNumbers > 2) {
ptr++; // move beyond.
// c=value.charAt(floatptr)-48;
c = stream[start + ptr] - 48;
switch (c) {
case 1:
hundredths = 0.01d;
break;
case 2:
hundredths = 0.02d;
break;
case 3:
hundredths = 0.03d;
break;
case 4:
hundredths = 0.04d;
break;
case 5:
hundredths = 0.05d;
break;
case 6:
hundredths = 0.06d;
break;
case 7:
hundredths = 0.07d;
break;
case 8:
hundredths = 0.08d;
break;
case 9:
hundredths = 0.09d;
break;
}
}
// thousands
if (decNumbers > 3) {
ptr++; // move beyond.
c = stream[start + ptr] - 48;
switch (c) {
case 1:
thousands = 0.001d;
break;
case 2:
thousands = 0.002d;
break;
case 3:
thousands = 0.003d;
break;
case 4:
thousands = 0.004d;
break;
case 5:
thousands = 0.005d;
break;
case 6:
thousands = 0.006d;
break;
case 7:
thousands = 0.007d;
break;
case 8:
thousands = 0.008d;
break;
case 9:
thousands = 0.009d;
break;
}
}
// tenthousands
if (decNumbers > 4) {
ptr++; // move beyond.
c = stream[start + ptr] - 48;
switch (c) {
case 1:
tenthousands = 0.0001d;
break;
case 2:
tenthousands = 0.0002d;
break;
case 3:
tenthousands = 0.0003d;
break;
case 4:
tenthousands = 0.0004d;
break;
case 5:
tenthousands = 0.0005d;
break;
case 6:
tenthousands = 0.0006d;
break;
case 7:
tenthousands = 0.0007d;
break;
case 8:
tenthousands = 0.0008d;
break;
case 9:
tenthousands = 0.0009d;
break;
}
}
// tenthousands
if (decNumbers > 5) {
ptr++; // move beyond.
c = stream[start + ptr] - 48;
switch (c) {
case 1:
hunthousands = 0.00001d;
break;
case 2:
hunthousands = 0.00002d;
break;
case 3:
hunthousands = 0.00003d;
break;
case 4:
hunthousands = 0.00004d;
break;
case 5:
hunthousands = 0.00005d;
break;
case 6:
hunthousands = 0.00006d;
break;
case 7:
hunthousands = 0.00007d;
break;
case 8:
hunthousands = 0.00008d;
break;
case 9:
hunthousands = 0.00009d;
break;
}
}
dec = tenths + hundredths + thousands + tenthousands + hunthousands;
num = hundreds + tens + units;
d = num + dec;
}
if (isMinus) return -d;
else return d;
}
private byte[] getNextValue() {
int start, end, next;
byte[] returnValue = null;
// skip to start of next value
while (this.ptr < this.streamLength && (this.stream[this.ptr] == 10 || this.stream[this.ptr] == 13 || this.stream[this.ptr] == 32))
this.ptr++;
// log start
start = this.ptr;
// find end
while (this.ptr < this.streamLength) {
next = this.stream[this.ptr];
if (next == START_BRACE) break;
this.ptr++;
if (this.ptr >= this.streamLength) break;
next = this.stream[this.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 (this.stream[start] == START_BRACE) {
this.ptr++;
this.level++;
}
else
if (this.stream[start] == END_BRACE) {
this.level--;
}
end = this.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 && (this.stream[end - 1] == '0' || this.stream[end - 1] == '.'))
end--;
int len = end - start;
returnValue = new byte[len];
System.arraycopy(this.stream, start, returnValue, start - start, end - start);
}
if (debug) {
System.out.print(">>>>>>>> ");
for (int aa = start; aa < end; aa++)
System.out.print((char) this.stream[aa]);
System.out.println(" <<<");
}
return returnValue;
}
/**
* put input values onto stack for processing
*
* @param values
*/
public void resetStacks(float[] values) {
// rest stack
// may need to change structure- try for double
this.stack = new double[100];
this.stackType = new int[100];
this.stkPtr = 0;
this.stkTypePtr = 0;
for (int ii = 0; ii < 100; ii++)
this.stack[ii] = 0d;
for (int iii = 0; iii < 100; iii++)
this.stackType[iii] = 0;
for (float value : values) {
if (debug) System.out.println("Added to stack " + value + " count=" + values.length);
push(value, PS_REAL);
}
}
}