001: /*
002: * $Id: PDFFunction.java,v 1.3 2007/12/20 18:33:35 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview.function;
023:
024: import com.sun.pdfview.PDFObject;
025: import com.sun.pdfview.PDFParseException;
026:
027: import java.io.IOException;
028:
029: /**
030: * <p>PDF Functions are defined in the reference as Section 3.9.</p>
031: *
032: * <p>A PDF function maps some set of <i>m</i> inputs into some set
033: * of <i>n</i> outputs. There are 4 types of functions:
034: * <ul><li>Type 0: Sampled functions. (PDF 1.2)<br>
035: * A sampled function (type 0) uses a table of sample values
036: * to define the function. Various techniques are used to
037: * interpolate values between the sample values
038: * (see Section 3.9.1, “Type 0 (Sampled) Functions�).</li>
039: * <li>Type 2: Exponential Interpolation. (PDF 1.3)<br>
040: * An exponential interpolation function (type 2)
041: * defines a set of coefficients for an exponential function
042: * (see Section 3.9.2,
043: * “Type 2 (Exponential Interpolation) Functions�).</li>
044: * <li>Type 3: Stitching functions. (PDF 1.3)<br>
045: * A stitching function (type 3) is a combination of
046: * other functions, partitioned across a domain
047: * (see Section 3.9.3, “Type 3 (Stitching) Functions�).</li>
048: * <li>Type 4: Postscript calculations. (PDF 1.3)<br>
049: * A PostScript calculator function (type 4) uses operators
050: * from the PostScript language to describe an arithmetic
051: * expression (see Section 3.9.4,
052: * “Type 4 (PostScript Calculator) Functions�).</li>
053: * </ul>
054: * </p>
055: *
056: * <p>
057: * The function interface contains a single method, <i>calculate</i> which
058: * takes an array of <i>m</i> floats an interprets them into an array of
059: * </i>n</i> floats.
060: * <p>
061: * PDFFunctions do not have accessible constructors. Instead, use the
062: * static <i>getFunction()</i> method to read a functions from a PDF Object.
063: *
064: */
065: public abstract class PDFFunction {
066: /** The known function types */
067: public static final int TYPE_0 = 0;
068: public static final int TYPE_2 = 2;
069: public static final int TYPE_3 = 3;
070: public static final int TYPE_4 = 4;
071:
072: /** the type of this function from the list of known types */
073: private int type;
074:
075: /** the input domain of this function, an array of 2 * <i>m</i> floats */
076: private float[] domain;
077:
078: /** the output range of this functions, and array of 2 * <i>n</i> floats */
079: private float[] range;
080:
081: /** Creates a new instance of PDFFunction */
082: protected PDFFunction(int type) {
083: this .type = type;
084: }
085:
086: /**
087: * Get a PDFFunction from a PDFObject
088: */
089: public static PDFFunction getFunction(PDFObject obj)
090: throws IOException {
091: PDFFunction function;
092: int type;
093: float[] domain = null;
094: float[] range = null;
095:
096: // read the function type (required)
097: PDFObject typeObj = obj.getDictRef("FunctionType");
098: if (typeObj == null) {
099: throw new PDFParseException(
100: "No FunctionType specified in function!");
101: }
102: type = typeObj.getIntValue();
103:
104: // read the function's domain (required)
105: PDFObject domainObj = obj.getDictRef("Domain");
106: if (domainObj == null) {
107: throw new PDFParseException(
108: "No Range specified in function!");
109: }
110:
111: PDFObject[] domainAry = domainObj.getArray();
112: domain = new float[domainAry.length];
113: for (int i = 0; i < domainAry.length; i++) {
114: domain[i] = domainAry[i].getFloatValue();
115: }
116:
117: // read the function's range (optional)
118: PDFObject rangeObj = obj.getDictRef("Range");
119: if (rangeObj != null) {
120: PDFObject[] rangeAry = rangeObj.getArray();
121: range = new float[rangeAry.length];
122: for (int i = 0; i < rangeAry.length; i++) {
123: range[i] = rangeAry[i].getFloatValue();
124: }
125: }
126:
127: // now create the acual function object
128: switch (type) {
129: case TYPE_0:
130: function = new FunctionType0();
131: break;
132: case TYPE_2:
133: function = new FunctionType2();
134: break;
135: default:
136: throw new PDFParseException("Unsupported function type: "
137: + type);
138: }
139:
140: // fill in the domain and optionally the range
141: function.setDomain(domain);
142: if (range != null) {
143: function.setRange(range);
144: }
145:
146: // now initialize the function
147: function.parse(obj);
148:
149: return function;
150: }
151:
152: /**
153: * Get the type of this function
154: *
155: * @return one of the types of function (0-4)
156: */
157: public int getType() {
158: return type;
159: }
160:
161: /**
162: * Get the number of inputs, <i>m</i>, required by this function
163: *
164: * @return the number of input values expected by this function
165: */
166: public int getNumInputs() {
167: return (domain.length / 2);
168: }
169:
170: /**
171: * Get the number of outputs, <i>n</i>, returned by this function
172: *
173: * @return the number of output values this function will return
174: */
175: public int getNumOutputs() {
176: return (range.length / 2);
177: }
178:
179: /**
180: * Get a component of the domain of this function
181: *
182: * @param i the index into the domain array, which has size 2 * <i>m</i>.
183: * the <i>i</i>th entry in the array has index 2<i>i</i>,
184: * 2<i>i</i> + 1
185: * @return the <i>i</i>th entry in the domain array
186: */
187: protected float getDomain(int i) {
188: return domain[i];
189: }
190:
191: /**
192: * Set the domain of this function
193: */
194: protected void setDomain(float[] domain) {
195: this .domain = domain;
196: }
197:
198: /**
199: * Get a component of the range of this function
200: *
201: * @param i the index into the range array, which has size 2 * <i>n</i>.
202: * the <i>i</i>th entry in the array has index 2<i>i</i>,
203: * 2<i>i</i> + 1
204: * @return the <i>i</i>th entry in the range array
205: */
206: protected float getRange(int i) {
207: return range[i];
208: }
209:
210: /**
211: * Set the range of this function
212: */
213: protected void setRange(float[] range) {
214: this .range = range;
215: }
216:
217: /**
218: * Map from <i>m</i> input values to <i>n</i> output values.
219: * The number of inputs <i>m</i> must be exactly one half the size of the
220: * domain. The number of outputs should match one half the size of the
221: * range.
222: *
223: * @param inputs an array of >= <i>m</i> input values
224: * @return the array of <i>n</i> output values
225: */
226: public float[] calculate(float[] inputs) {
227: float[] outputs = new float[getNumOutputs()];
228: calculate(inputs, 0, outputs, 0);
229: return outputs;
230: }
231:
232: /**
233: * Map from <i>m</i> input values to <i>n</i> output values.
234: * The number of inputs <i>m</i> must be exactly one half the size of the
235: * domain. The number of outputs should match one half the size of the
236: * range.
237: *
238: * @param inputs an array of >= <i>m</i> input values
239: * @param inputOffset the offset into the input array to read from
240: * @param outputs an array of size >= <i>n</i> which will be filled
241: * with the output values
242: * @param outputOffset the offset into the output array to write to
243: * @return the array of <i>n</i> output values
244: */
245: public float[] calculate(float[] inputs, int inputOffset,
246: float[] outputs, int outputOffset) {
247: // check the inputs
248: if (inputs.length - inputOffset < getNumInputs()) {
249: throw new IllegalArgumentException(
250: "Wrong number of inputs to function!");
251: }
252:
253: // check the outputs
254: if (outputs.length - outputOffset < getNumOutputs()) {
255: throw new IllegalArgumentException(
256: "Wrong number of outputs for function!");
257: }
258:
259: // clip the inputs to domain
260: for (int i = 0; i < inputs.length; i++) {
261: // clip to the domain -- min(max(x<i>, domain<2i>), domain<2i+1>)
262: inputs[i] = Math.max(inputs[i], getDomain(2 * i));
263: inputs[i] = Math.min(inputs[i], getDomain((2 * i) + 1));
264: }
265:
266: // do the actual calculation
267: doFunction(inputs, inputOffset, outputs, outputOffset);
268:
269: // clip the outputs to range
270: for (int i = 0; i < outputs.length; i++) {
271: // clip to range -- min(max(r<i>, range<2i>), range<2i + 1>)
272: outputs[i] = Math.max(outputs[i], getRange(2 * i));
273: outputs[i] = Math.min(outputs[i], getRange((2 * i) + 1));
274: }
275:
276: return outputs;
277: }
278:
279: /**
280: * Subclasses must implement this method to perform the actual function
281: * on the given set of data. Note that the inputs are guaranteed to be
282: * clipped to the domain, while the outputs will be automatically clipped
283: * to the range after being returned from this function.
284: *
285: * @param inputs guaranteed to be at least as big as
286: * <code>getNumInputs()</code> and all values within range
287: * @param inputOffset the offset into the inputs array to read from
288: * @param outputs guaranteed to be at least as big as
289: * <code>getNumOutputs()</code>, but not yet clipped to domain
290: * @param outputOffset the offset into the output array to write to
291: */
292: protected abstract void doFunction(float[] inputs, int inputOffset,
293: float[] outputs, int outputOffset);
294:
295: /** Read the function information from a PDF Object */
296: protected abstract void parse(PDFObject obj) throws IOException;
297: }
|