/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.pdfbox.pdmodel.common.function; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.common.PDRange; import java.io.IOException; /** * This class represents a type 3 function in a PDF document. * * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> * @version $Revision: 1.2 $ */ public class PDFunctionType3 extends PDFunction { private COSArray functions = null; private COSArray encode = null; private COSArray bounds = null; /** * Constructor. * * @param functionStream The function . */ public PDFunctionType3(COSBase functionStream) { super( functionStream ); } /** * {@inheritDoc} */ public int getFunctionType() { return 3; } /** * {@inheritDoc} */ public float[] eval(float[] input) throws IOException { //This function is known as a "stitching" function. Based on the input, it decides which child function to call. // All functions in the array are 1-value-input functions //See PDF Reference section 3.9.3. PDFunction function = null; float x = input[0]; PDRange domain = getDomainForInput(0); // clip input value to domain x = clipToRange(x, domain.getMin(), domain.getMax()); COSArray functionsArray = getFunctions(); int numberOfFunctions = functionsArray.size(); // This doesn't make sense but it may happen ... if (numberOfFunctions == 1) { function = PDFunction.create(functionsArray.get(0)); PDRange encRange = getEncodeForParameter(0); x = interpolate(x, domain.getMin(), domain.getMax(), encRange.getMin(), encRange.getMax()); } else { float[] boundsValues = getBounds().toFloatArray(); int boundsSize = boundsValues.length; // create a combined array containing the domain and the bounds values // domain.min, bounds[0], bounds[1], ...., bounds[boundsSize-1], domain.max float[] partitionValues = new float[boundsSize+2]; int partitionValuesSize = partitionValues.length; partitionValues[0] = domain.getMin(); partitionValues[partitionValuesSize-1] = domain.getMax(); System.arraycopy(boundsValues, 0, partitionValues, 1, boundsSize); // find the partition for (int i=0; i < partitionValuesSize-1; i++) { if ( x >= partitionValues[i] && (x < partitionValues[i+1] || (i == partitionValuesSize - 2 && x == partitionValues[i+1]))) { function = PDFunction.create(functionsArray.get(i)); PDRange encRange = getEncodeForParameter(i); x = interpolate(x, partitionValues[i], partitionValues[i+1], encRange.getMin(), encRange.getMax()); break; } } } float[] functionValues = new float[]{x}; // calculate the output values using the chosen function float[] functionResult = function.eval(functionValues); // clip to range if available return clipToRange(functionResult); } /** * Returns all functions values as COSArray. * * @return the functions array. */ public COSArray getFunctions() { if (functions == null) { functions = (COSArray)(getDictionary().getDictionaryObject( COSName.FUNCTIONS )); } return functions; } /** * Returns all bounds values as COSArray. * * @return the bounds array. */ public COSArray getBounds() { if (bounds == null) { bounds = (COSArray)(getDictionary().getDictionaryObject( COSName.BOUNDS )); } return bounds; } /** * Returns all encode values as COSArray. * * @return the encode array. */ public COSArray getEncode() { if (encode == null) { encode = (COSArray)(getDictionary().getDictionaryObject( COSName.ENCODE )); } return encode; } /** * Get the encode for the input parameter. * * @param paramNum The function parameter number. * * @return The encode parameter range or null if none is set. */ private PDRange getEncodeForParameter(int n) { COSArray encodeValues = getEncode(); return new PDRange( encodeValues, n ); } }