All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sejda.sambox.pdmodel.common.function.PDFunction Maven / Gradle / Ivy

Go to download

An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam related projects

There is a newer version: 3.0.21
Show newest version
/*
 * 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.sejda.sambox.pdmodel.common.function;

import java.io.IOException;

import org.sejda.sambox.cos.COSArray;
import org.sejda.sambox.cos.COSBase;
import org.sejda.sambox.cos.COSDictionary;
import org.sejda.sambox.cos.COSName;
import org.sejda.sambox.cos.COSObjectable;
import org.sejda.sambox.cos.COSStream;
import org.sejda.sambox.pdmodel.common.PDRange;
import org.sejda.sambox.pdmodel.common.PDStream;

/**
 * This class represents a function in a PDF document.
 *
 * @author Ben Litchfield
 */
public abstract class PDFunction implements COSObjectable
{

    private PDStream functionStream = null;
    private COSDictionary functionDictionary = null;
    private COSArray domain = null;
    private COSArray range = null;
    private int numberOfInputValues = -1;
    private int numberOfOutputValues = -1;

    /**
     * Constructor.
     *
     * @param function The function stream.
     */
    public PDFunction(COSBase function)
    {
        if (function instanceof COSStream)
        {
            functionStream = new PDStream((COSStream) function);
            functionStream.getCOSObject().setItem(COSName.TYPE, COSName.FUNCTION);
        }
        else if (function instanceof COSDictionary)
        {
            functionDictionary = (COSDictionary) function;
        }
    }

    /**
     * Returns the function type.
     * 

* Possible values are: *

* 0 - Sampled function 2 - Exponential interpolation function 3 - Stitching function 4 - * PostScript calculator function * * @return the function type. */ public abstract int getFunctionType(); /** * Returns the stream. * * @return The stream for this object. */ @Override public COSDictionary getCOSObject() { if (functionStream != null) { return functionStream.getCOSObject(); } return functionDictionary; } /** * Returns the underlying PDStream. * * @return The stream. */ protected PDStream getPDStream() { return functionStream; } /** * Create the correct PD Model function based on the COS base function. * * @param function The COS function dictionary. * @return The PDModel Function object. * @throws IOException If we are unable to create the PDFunction object. */ public static PDFunction create(COSBase function) throws IOException { if (function == COSName.IDENTITY) { return new PDFunctionTypeIdentity(null); } COSBase base = function.getCOSObject(); if (!(base instanceof COSDictionary functionDictionary)) { throw new IOException( "Unexpected type for function, expected COSDictionary but was: " + base); } int functionType = functionDictionary.getInt(COSName.FUNCTION_TYPE); switch (functionType) { case 0: return new PDFunctionType0(functionDictionary); case 2: return new PDFunctionType2(functionDictionary); case 3: return new PDFunctionType3(functionDictionary); case 4: return new PDFunctionType4(functionDictionary); default: throw new IOException("Error: Unknown function type " + functionType); } } /** * This will get the number of output parameters that have a range specified. A range for output * parameters is optional so this may return zero for a function that does have output * parameters, this will simply return the number that have the range specified. * * @return The number of output parameters that have a range specified. */ public int getNumberOfOutputParameters() { if (numberOfOutputValues == -1) { COSArray rangeValues = getRangeValues(); if (rangeValues == null) { numberOfOutputValues = 0; } else { numberOfOutputValues = rangeValues.size() / 2; } } return numberOfOutputValues; } /** * This will get the range for a certain output parameters. This is will never return null. If * it is not present then the range 0 to 0 will be returned. * * @param n The output parameter number to get the range for. * @return The range for this component. */ public PDRange getRangeForOutput(int n) { COSArray rangeValues = getRangeValues(); return new PDRange(rangeValues, n); } /** * This will set the range values. * * @param rangeValues The new range values. */ public void setRangeValues(COSArray rangeValues) { range = rangeValues; getCOSObject().setItem(COSName.RANGE, rangeValues); } /** * This will get the number of input parameters that have a domain specified. * * @return The number of input parameters that have a domain specified. */ public int getNumberOfInputParameters() { if (numberOfInputValues == -1) { COSArray array = getDomainValues(); numberOfInputValues = array.size() / 2; } return numberOfInputValues; } /** * This will get the range for a certain input parameter. This is will never return null. If it * is not present then the range 0 to 0 will be returned. * * @param n The parameter number to get the domain for. * @return The domain range for this component. */ public PDRange getDomainForInput(int n) { COSArray domainValues = getDomainValues(); return new PDRange(domainValues, n); } /** * This will set the domain values. * * @param domainValues The new domain values. */ public void setDomainValues(COSArray domainValues) { domain = domainValues; getCOSObject().setItem(COSName.DOMAIN, domainValues); } /** * @deprecated Replaced by {@link #eval(float[] input)} */ @Deprecated public COSArray eval(COSArray input) throws IOException { float[] outputValues = eval(input.toFloatArray()); COSArray array = new COSArray(); array.setFloatArray(outputValues); return array; } /** * Evaluates the function at the given input. ReturnValue = f(input) * * @param input The array of input values for the function. In many cases will be an array of a * single value, but not always. * @return The of outputs the function returns based on those inputs. In many cases will be an * array of a single value, but not always. * @throws IOException an IOExcpetion is thrown if something went wrong processing the * function. */ public abstract float[] eval(float[] input) throws IOException; /** * Returns all ranges for the output values as COSArray . Required for type 0 and type 4 * functions * * @return the ranges array. */ protected COSArray getRangeValues() { if (range == null) { range = getCOSObject().getDictionaryObject(COSName.RANGE, COSArray.class); } return range; } /** * Returns all domains for the input values as COSArray. Required for all function types. * * @return the domains array. */ private COSArray getDomainValues() { if (domain == null) { domain = getCOSObject().getDictionaryObject(COSName.DOMAIN, COSArray.class); } return domain; } /** * Clip the given input values to the ranges. * * @param inputValues the input values * @return the clipped values */ protected float[] clipToRange(float[] inputValues) { COSArray rangesArray = getRangeValues(); float[] result; if (rangesArray != null && rangesArray.size() > 0) { float[] rangeValues = rangesArray.toFloatArray(); int numberOfRanges = rangeValues.length / 2; result = new float[numberOfRanges]; for (int i = 0; i < numberOfRanges; i++) { int index = i << 1; result[i] = clipToRange(inputValues[i], rangeValues[index], rangeValues[index + 1]); } } else { result = inputValues; } return result; } /** * Clip the given input value to the given range. * * @param x the input value * @param rangeMin the min value of the range * @param rangeMax the max value of the range * @return the clipped value */ protected float clipToRange(float x, float rangeMin, float rangeMax) { if (x < rangeMin) { return rangeMin; } if (x > rangeMax) { return rangeMax; } return x; } /** * For a given value of x, interpolate calculates the y value on the line defined by the two * points (xRangeMin , xRangeMax ) and (yRangeMin , yRangeMax ). * * @param x the to be interpolated value. * @param xRangeMin the min value of the x range * @param xRangeMax the max value of the x range * @param yRangeMin the min value of the y range * @param yRangeMax the max value of the y range * @return the interpolated y value */ protected float interpolate(float x, float xRangeMin, float xRangeMax, float yRangeMin, float yRangeMax) { return yRangeMin + ((x - xRangeMin) * (yRangeMax - yRangeMin) / (xRangeMax - xRangeMin)); } /** * {@inheritDoc} */ @Override public String toString() { return "FunctionType" + getFunctionType(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy