/* * $Id: FunctionType4.java,v 1.3 2009-02-12 13:53:59 tomoke Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.sun.pdfview.function; import java.io.IOException; import java.nio.ByteBuffer; import java.util.*; import com.sun.pdfview.PDFObject; import com.sun.pdfview.PDFParseException; /** * <p>A PostScript function is represented as a stream containing code * written in a small subset of the PostScript language. * This reference is taken from the (3200-1:2008:7.10.5)<p> * * http://www.adobe.com/devnet/acrobat/pdfs/adobe_supplement_iso32000.pdf * </p> */ public class FunctionType4 extends PDFFunction { /** the set of all Operations we support. These operations are defined * in Appendix B - Operators.*/ private static HashSet<Operation> operationSet = null; /** the list of tokens and sub-expressions. */ private LinkedList tokens = new LinkedList(); /** the stack of operations. The stack contents should all be Comparable. */ private LinkedList<Object> stack = new LinkedList<Object>(); /** Creates a new instance of FunctionType4 */ protected FunctionType4() { super(TYPE_4); if (operationSet == null) { initOperations(); } } /** * Initialize the operations that we can perform. */ private void initOperations() { /** these operators consider the left hand arguments as deeper in * the stack than the right hand arguments, thus the right-hand is * is the top of the stack and is popped first. * * Operation details in PostScript Language Reference Manual: * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf * Chapter 8 - Operator Details */ if (operationSet == null) { operationSet = new HashSet<Operation>(); // Arithmetic Operators operationSet.add(new Operation("abs") { /** * <i>num1</i> <b>abs</b> <i>num2</i> <p> * * The type of the result is the same as the type of num1, * unless num1 is the smallest (most negative) integer, * in which case the result is a real number.<p> * * errors: stackunderflow, typecheck */ void eval() { pushDouble(Math.abs(popDouble())); } }); operationSet.add(new Operation("add") { /** * <i>num1 num2</i> <b>add</b> <i>sum</i> <p> * * If both operands are integers and the result is * within integer range, the result is an integer; * otherwise, the result is a real number.<p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { pushDouble(popDouble() + popDouble()); } }); operationSet.add(new Operation("atan") { /** * <i>num den</i> <b>atan</b> <i>angle</i> <p> * * returns the angle (in degress between * 0 and 360) whose tangent is num divided by den. * Either num or den may be 0, but not both. The signs * of num and den determine the quadrant in which the * result will lie: positive num yeilds a result in the * positive y plane, while a positive den yeilds a result in * the positive x plane. The result is a real number.<p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { double den = popDouble(); double num = popDouble(); if (den == 0.0) { pushDouble(90.0); } else { pushDouble(Math.toDegrees(Math.atan(num / den))); } } }); operationSet.add(new Operation("ceiling") { /** * <i>num1</i> <b>ceiling</b> <i>num2</i> <p> * * returns the least integer value greater than or equal * to num1. The type of the result is the same as the type * of the operand. <p> * * errors: stackunderflow, typecheck */ void eval() { pushDouble(Math.ceil(popDouble())); } }); operationSet.add(new Operation("cvi") { /** * <i>num</i> <b>cvi</b> <i>int</i> <u>or</u> <i>string</i> <b>cvi</b> <i>int</i> <p> * * takes an integer, real, or string and produces an * integer result. If the operand is an integer, cvi * simply returns it. If the operand is a real number, * it truncates any fractional part (that is, rounds * it toward 0) and converts it to an integer. * If the operand is a string, cvi invokes the equivalent * of the token operator to interpret the characters * of the string as a number according to the PostScript * syntax rules. If that number is a real number, cvi converts * it to an integer. * A rangecheck error occurs if a real number is too * large to convert to an integer. <p> * * errors: invalidaccess, rangecheck, stackunderflow, * syntaxError, typecheck, */ void eval() { pushDouble((double) ((int) popDouble())); } }); operationSet.add(new Operation("cvr") { /** * <i>num</i> <b>cvr</b> <i>real</i> <u>or</u> <i>string</i> <b>cvr</b> <i>real</i> <p> * * (convert to real) takes an integer, real, or string * object and produces a real result. If the operand * is an integer, cvr converts it to a real number. * If the operand is a real number, cvr simply returns it. * If the operand is a string, cvr invokes the equivalent * of the token operator to interpret the characters of * the string as a number according to the PostScript * syntax rules. If that number is an integer, cvr converts * it to a real number. <p> * * errors: invalidaccess, limitcheck, stackunderflow, * syntaxerror, typecheck, undefinedresult */ void eval() { // YOUR CODE IN THIS SPACE } }); operationSet.add(new Operation("div") { /** * <i>num1 num2</i> <b>div</b> <i>quotient</i> <p> * * divides num1 by num2, producing a result that is * always a real number even if both operands are integers. * Use idiv instead if the operands are integers and an * integer result is desired. <p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushDouble(num1 / num2); } }); operationSet.add(new Operation("exp") { /** * <i>base exponent</i> <b>exp</b> <i>real</i> <p> * * raises base to the exponent power. The operands may be * either integers or real numbers. If the exponent has a * fractional part, the result is meaningful only if the * base is nonnegative. The result is always a real number. <p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { double exponent = popDouble(); double base = popDouble(); pushDouble(Math.pow(exponent, base)); } }); operationSet.add(new Operation("floor") { /** * <i>num1</i> <b>floor</b> <i>num2</i> <p> * * returns the greatest integer value less than or equal * to num1. The type of the result is the same as the type * of the operand. <p> * * errors: stackunderflow, typecheck */ void eval() { pushDouble(Math.floor(popDouble())); } }); operationSet.add(new Operation("idiv") { /** * <i>int1 int2</i> <b>idiv</b> <i>quotient</i> <p> * * divides int1 by int2 and returns the integer part * of the quotient, with any fractional part discarded. * Both operands of idiv must be integers and the result * is an integer. <p> * * stackunderflow, typecheck, undefinedresult */ void eval() { long int2 = popLong(); long int1 = popLong(); pushLong(int1 / int2); } }); operationSet.add(new Operation("ln") { /** * <i>num</i> <b>ln</b> <i>real</i> <p> * * returns the natural logarithm (base e) of num. * The result is a real number. <p> * * errors: rangecheck, stackunderflow, typecheck */ void eval() { pushDouble(Math.log(popDouble())); } }); operationSet.add(new Operation("log") { /** * <i>num</i> <b>log</b> <i>real</i> <p> * * returns the common logarithm (base 10) of num. * The result is a real number. <p> * * errors: rangecheck, stackunderflow, typecheck */ void eval() { pushDouble(Math.log10(popDouble())); } }); operationSet.add(new Operation("mod") { /** * <i>int1 int2</i> <b>mod</b> <i>remainder</i> <p> * * returns the remainder that results from * dividing int1 by int2. The sign of the result * is the same as the sign of the dividend int1. * Both operands must be integers and the result * is an integer. <p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { long int2 = popLong(); long int1 = popLong(); pushLong(int1 % int2); } }); operationSet.add(new Operation("mul") { /** * <i>num1 num2</i> <b>mul</b> <i>product</i> <p> * * returns the product of num1 and num2. * If both operands are integers and the result * is within integer range, the result is an integer; * otherwise, the result is a real number. <p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { pushDouble(popDouble() * popDouble()); } }); operationSet.add(new Operation("neg") { /** * <i>num1</i> <b>neg</b> <i>num2</i> <p> * * returns the negative of num1. The type of the result * is the same as the type of num1 unless num1 is the * smallest (most negative) integer, in which case the * result is a real number. <p> * * errors: stackunderflow, typecheck */ void eval() { pushDouble(-popDouble()); } }); operationSet.add(new Operation("round") { /** * <i>num1</i> <b>round</b> <i>num2</i> <p> * * returns the integer value nearest to num1. * If num1 is equally close to its two nearest * integers, round returns the greater of the two. * The type of the result is the same as * the type of the operand. <p> * * errors: stackunderflow, typecheck */ void eval() { pushLong(Math.round(popDouble())); } }); operationSet.add(new Operation("sin") { /** * <i>angle</i> <b>sin</b> <i>real</i> <p> * * returns the sine of angle, which is interpreted as an * angle in degrees. The result is a real number. <p> * * errors: stackunderflow, typecheck */ void eval() { double radians = Math.toRadians(popDouble()); pushDouble(Math.toDegrees(Math.sin(radians))); } }); operationSet.add(new Operation("sqrt") { /** * <i>num</i> <b>sqrt</b> <i>real</i> <p> * * returns the square root of num, which must be a * nonnegative number. The result is a real number. <p> * * errors: rangecheck, stackunderflow, typecheck */ void eval() { pushDouble(Math.sqrt(popDouble())); } }); operationSet.add(new Operation("sub") { /** * <i>num1 num2</i> <b>sub</b> <i>difference</i> <p> * * returns the result of subtracting num2 from num1. * If both operands are integers and the result is within * integer range, the result is an integer; otherwise, * the result is a real number. <p> * * errors: stackunderflow, typecheck, undefinedresult */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushDouble(num1 - num2); } }); operationSet.add(new Operation("truncate") { /** * <i>num1</i> <b>truncate</b> <i>num2</i> <p> * * truncates num1 toward 0 by removing its fractional part. * The type of the result is the same as the type of the * operand. <p> * * errors: stackunderflow, typecheck */ void eval() { double num1 = popDouble(); pushDouble(((double) ((long) num1) - num1)); } }); // Relational, boolean, and bitwise operators operationSet.add(new Operation("and") { /** * <i>bool1|int1 bool2|int2</i> <b>and</b> <i>bool3|int3</i> <p> * * returns the logical conjunction of the operands * if they are boolean. If the operands are integers, * and returns the bitwise "and" of their binary * representations. <p> * * errors: stackunderflow, typecheck */ void eval() { pushLong(popLong() & popLong()); } }); operationSet.add(new Operation("bitshift") { /** * <i>int1 <i>shift</i> <b>bitshift</b> <i>int2</i> <p> * * shifts the binary representation of int1 left by * shift bits and returns the result. Bits shifted out * are lost; bits shifted in are 0. If shift is negative, * a right shift by –shift bits is performed. * This operation produces an arithmetically correct * result only for positive values of int1. * Both int1 and shift must be integers. <p> * * errors: stackunderflow, typecheck */ void eval() { long shift = popLong(); long int1 = popLong(); pushLong(int1 << shift); } }); operationSet.add(new Operation("eq") { /** * <i>any1 <i>any2</i> <b>eq</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes\ * true if they are equal, or false if not. * The definition of equality depends on the types of * the objects being compared. * Simple objects are equal if their types and values * are the same. Strings are equal if their lengths and * individual elements are equal. Other composite objects * (arrays and dictionaries) are equal only if they share * the same value. Separate values are considered unequal, * even if all the components of those values are the * same. * This operator performs some type conversions. * Integers and real numbers can be compared freely: * an integer and a real number representing the same * mathematical value are considered equal by eq. * Strings and names can likewise be compared freely: * a name defined by some sequence of characters is equal * to a string whose elements are the same sequence of * characters. * The literal/executable and access attributes of * objects are not considered in comparisons * between objects. <p> * * errors: invalidaccess, stackunderflow */ void eval() { pushBoolean(popObject().equals(popObject())); } }); operationSet.add(new Operation("false") { /** * <b>false</b> <i>false</i> <p> * * pushes a boolean object whose value is false on the * operand stack. false is not an operator; it is a name in * systemdict associated with the boolean value false. <p> * * errors: stackoverflow */ void eval() { pushBoolean(false); } }); operationSet.add(new Operation("ge") { /** * <i>num1 num2</i> <b>ge</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes true * if the first operand is greater than or equal to the second, * or false otherwise. If both operands are numbers, * ge compares their mathematical values. If both operands * are strings, ge compares them element by element, treating * the elements as integers in the range 0 to 255, to determine * whether the first string is lexically greater than or equal * to the second. If the operands are of other types or one * is a string and the other is a number, a typecheck * error occurs. <p> * * errors: invalidaccess, stackunderflow, typecheck */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushBoolean(num1 >= num2); } }); operationSet.add(new Operation("gt") { /** * <i>num1 num2</i> <b>gt</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes true * if the first operand is greater than the second, or * false otherwise. If both operands are numbers, gt compares * their mathematical values. If both operands are strings, * gt compares them element by element, treating the elements * as integers in the range 0 to 255, to determine whether * the first string is lexically greater than the second. * If the operands are of other types or one is a string * and the other is a number, a typecheck error occurs. <p> * * errors: invalidaccess, stackunderflow, typecheck */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushBoolean(num1 > num2); } }); operationSet.add(new Operation("le") { /** * <i>num1 num2</i> <b>le</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes true * if the first operand is less than or equal to the second, * or false otherwise. If both operands are numbers, le * compares their mathematical values. If both operands are * strings, le compares them element by element, treating * the elements as integers in the range 0 to 255, * to determine whether the first string is lexically less * than or equal to the second. If the operands are of other * types or one is a string and the other is a number, a * typecheck error occurs.<p> * * errors: invalidaccess, stackunderflow, typecheck */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushBoolean(num1 <= num2); } }); operationSet.add(new Operation("lt") { /** * <i>num1 num2</i> <b>lt</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes true * if the first operand is less than the second, or false * otherwise. If both operands are numbers, lt compares * their mathematical values. If both operands are strings, * lt compares them element by element, treating the elements * as integers in the range 0 to 255, to determine whether * the first string is lexically less than the second. * If the operands are of other types or one is a string * and the other is a number, a typecheck error occurs. <p> * * errors: invalidaccess, stackunderflow, typecheck */ void eval() { double num2 = popDouble(); double num1 = popDouble(); pushBoolean(num1 < num2); } }); operationSet.add(new Operation("ne") { /** * <i>any1 any2</i> <b>ne</b> <i>bool</i> <p> * * pops two objects from the operand stack and pushes false * if they are equal, or true if not. What it means for objects * to be equal is presented in the description of the * eq operator. <p> * * errors: invalidaccess, stackunderflow */ void eval() { pushBoolean(!popObject().equals(popObject())); } }); operationSet.add(new Operation("not") { /** * <i>bool1|int1</i> <b>not</b> <i>bool2|int2</i> <p> * * returns the logical negation of the operand if it is * boolean. If the operand is an integer, not returns the * bitwise complement (ones complement) of its binary * representation. <p> * * errors: stackunderflow, typecheck */ void eval() { pushLong(~popLong()); } }); operationSet.add(new Operation("or") { /** * <i>bool1|int1 bool2|int2</i> <b>or</b> <i>bool3|int3</i> <p> * * returns the logical disjunction of the operands if they * are boolean. If the operands are integers, or returns * the bitwise "inclusive or" of their binary representations. <p> * * errors: stackunderflow, typecheck */ void eval() { pushLong(popLong() | popLong()); } }); operationSet.add(new Operation("true") { /** * <b>true</b> <i>true</i> <p> * * pushes a boolean object whose value is true on the operand * stack. true is not an operator; it is a name in systemdict * associated with the boolean value true. <p> * * errors: stackoverflow */ void eval() { pushBoolean(true); } }); operationSet.add(new Operation("xor") { /** * <i>bool1|int1 bool2|int2</i> <b>xor</b> <i>bool3|int3</i> <p> * * returns the logical "exclusive or" of the operands if they * are boolean. If the operands are integers, xor returns the * bitwise "exclusive or" of their binary representations. <p> * * errors: stackunderflow, typecheck */ void eval() { pushLong(popLong() ^ popLong()); } }); // Conditional Operators operationSet.add(new Operation("if") { /** * <i>bool {proc}</i> <b>if</b> - <p> * * removes both operands from the stack, then executes proc * if bool is true. The if operator pushes no results of * its own on the operand stack, but proc may do so (see * Section 3.5, "Execution"). <p> * * Examples <p> * 3 4 lt {(3 is less than 4)} if <p> * * errors: stackunderflow, typecheck */ void eval() { if (popBoolean()) { stack.addFirst(popExpression()); } else { popExpression(); } } }); operationSet.add(new Operation("ifelse") { /** * <i>bool {expr1} {expr2}</i> <b>ifelse</b> - <p> * * removes all three operands from the stack, then * executes proc1 if bool is true or proc2 if bool is false. * The ifelse operator pushes no results of its own on the * operand stack, but the procedure it executes may do so * (see Section 3.5, "Execution"). <p> * * Examples <p> * 4 3 lt {(TruePart)} {(FalsePart)} ifelse <br> * results in FalsePart, since 4 is not less than 3 <p> * * errors: stackunderflow, typecheck */ void eval() { // execute expr1 if bool is true, expr2 if false if (popBoolean()) { // expression.push(popExpression()); popExpression(); } else { popExpression(); // expression.push(popExpression()); } } }); // Stack Operators operationSet.add(new Operation("copy") { /** * <i>any1 ... anyn n</i> <b>copy</b> <i>any1 ... anyn any1 ... anyn</i> * <i>array1 array2</i> <b>copy</b> <i>subarray2</i> <br> * <i>string1 string2</i> <b>copy</b> <i>substring2</i> <p> * * performs two entirely different functions, depending on the * type of the topmost operand. * In the first form, where the top element on the operand * stack is a nonnegative integer n, copy pops n from the * stack and duplicates the top n elements on the stack * as shown above. This form of copy operates only on the * objects themselves, not on the values of composite objects. <p> * * Examples<br> * (a) (b) (c) 2 copy Þ (a) (b) (c) (b) (c) <br> * (a) (b) (c) 0 copy Þ (a) (b) (c) <p> * * In the other forms, copy copies all the elements of the * first composite object into the second. The composite * object operands must be of the same type, except that * a packed array can be copied into an array (and only into * an array—copy cannot copy into packed arrays, because * they are read-only). This form of copy copies the value of * a composite object. This is quite different from dup and * other operators that copy only the objects themselves * (see Section 3.3.1, "Simple and Composite Objects"). * However, copy performs only one level of copying. * It does not apply recursively to elements that are * themselves composite objects; instead, the values * of those elements become shared. In the case of arrays or * strings, the length of the second object must be at least as * great as the first; copy returns the initial subarray or * substring of the second operand into which the elements * were copied. Any remaining elements of array2 or * string2 are unaffected. <p> * * Example: <br> * /a1 [1 2 3] def<br> * a1 dup length array copy Þ [1 2 3] <p> * * errors: invalidaccess, rangecheck, stackoverflow, * stackunderflow, typecheck */ void eval() { long count = popLong(); // ???? Object obj = stack.removeFirst(); stack.addFirst(obj); stack.addFirst(obj); } }); operationSet.add(new Operation("dup") { /** * <i>any</i> <b>dup</b> <i>any any</i> <p> * * duplicates the top element on the operand stack. * dup copies only the object; the value of a composite * object is not copied but is shared. * See Section 3.3, "Data Types and Objects." <p> * * errors: stackoverflow, stackunderflow */ void eval() { Object obj = popObject(); pushObject(obj); pushObject(obj); } }); operationSet.add(new Operation("exch") { void eval() { // <i>any1 any2</i> <b>exch</b> <i>any2 any1</i> - exchange top of stack Object any1 = popObject(); Object any2 = popObject(); pushObject(any2); pushObject(any1); } }); operationSet.add(new Operation("index") { void eval() { // <i>anyn ... any0 n</i> <b>index</b> <i>anyn ... any0 anyn</i> Object obj = stack.removeFirst(); stack.addFirst(obj); stack.addFirst(obj); } }); operationSet.add(new Operation("pop") { void eval() { // discard top element stack.removeFirst(); } }); operationSet.add(new Operation("roll") { void eval() { // <i>anyn-1 ... any0 n j</i> <b>roll</b> <i>any(j-1)mod n ... anyn-1 ... any</i> // Roll n elements up j times Object obj = stack.removeFirst(); stack.addFirst(obj); stack.addFirst(obj); } }); } } /** Read the function information from a PDF Object */ protected void parse(PDFObject obj) throws IOException { // read the postscript from the stream readPS(obj.getStreamBuffer()); throw new PDFParseException("Unsupported function type 4."); } /** * Map from <i>m</i> input values to <i>n</i> output values. * The number of inputs <i>m</i> must be exactly one half the size of the * domain. The number of outputs should match one half the size of the * range. * * @param inputs an array of <i>m</i> input values * @param outputs an array of size <i>n</i> which will be filled * with the output values, or null to return a new array */ protected void doFunction(float[] inputs, int inputOffset, float[] outputs, int outputOffset) { } private boolean popBoolean() { return false; } private void pushBoolean(boolean arg) { } private double popDouble() { return 0; } private void pushDouble(double arg) { } private Expression popExpression() { return null; } private void pushExpression(Expression expresson) { } private long popLong() { return 0L; } private void pushLong(long arg) { } private Object popObject() { return stack.removeFirst(); } private void pushObject(Object obj) { stack.addFirst(obj); } /** * <p>parse the postscript operators and aguments from the stream.</p> * * <p>Syntax is to read a set of tokens, including expressions and * to queue them as they come including other expressions. Expressions are * enclosed in curly brackets and constitute a reference to the * expression body.</p> * * @param buf the stream of postscript tokens */ private void readPS(ByteBuffer buf) { } class Expression extends LinkedList { public boolean equals(Object obj) { if (obj instanceof Expression) { // actually validate the list contents are the same expressions return true; } return false; } } abstract class Operation { private String operatorName; public Operation(String operatorName) { if (operatorName == null) { throw new RuntimeException("Cannot have a null operator name"); } this.operatorName = operatorName; } public String getOperatorName() { return operatorName; } /** * evaluate the function, popping the stack as needed and pushing results. */ abstract void eval(); /** * return true if our operator is the same as the supplied one. * * @param obj * @return */ public boolean equals(Object obj) { if (obj instanceof Operation) { return ((Operation) obj).operatorName.equals(operatorName); } else if (obj instanceof String) { return operatorName.equals(obj); } return false; } } }