use crate::{
error::{ParseError, PdfResult},
objects::{Dictionary, Object, ObjectType},
pdf_enum,
stream::Stream,
Resolve,
};
use self::{
exponential_interpolation::ExponentialInterpolationFunction,
postscript_calculator::PostScriptCalculatorFunction, sampled::SampledFunction,
stitching::StitchingFunction,
};
mod exponential_interpolation;
mod postscript_calculator;
mod sampled;
mod stitching;
#[derive(Debug)]
pub struct Function {
domain: Vec<f32>,
range: Option<Vec<f32>>,
subtype: FunctionSubtype,
}
#[derive(Debug)]
pub(crate) enum StreamOrDict {
Stream(Stream),
Dict(Dictionary),
}
impl StreamOrDict {
pub fn dict(&mut self) -> &mut Dictionary {
match self {
Self::Dict(dict) => dict,
Self::Stream(stream) => &mut stream.dict.other,
}
}
pub fn expect_stream(self) -> PdfResult<Stream> {
match self {
Self::Dict(dict) => Err(ParseError::MismatchedObjectType {
expected: ObjectType::Stream,
found: Object::Dictionary(dict),
}),
Self::Stream(stream) => Ok(stream),
}
}
}
impl Function {
pub fn from_obj(obj: Object, resolver: &mut dyn Resolve) -> PdfResult<Self> {
let obj = resolver.resolve(obj)?;
let mut stream_or_dict = if let Ok(stream) = resolver.assert_stream(obj.clone()) {
StreamOrDict::Stream(stream)
} else {
StreamOrDict::Dict(resolver.assert_dict(obj)?)
};
let dict = stream_or_dict.dict();
let domain = dict
.expect_arr("Domain", resolver)?
.into_iter()
.map(|obj| resolver.assert_number(obj))
.collect::<PdfResult<Vec<f32>>>()?;
let range = dict
.get_arr("Range", resolver)?
.map(|arr| {
arr.into_iter()
.map(|obj| resolver.assert_number(obj))
.collect::<PdfResult<Vec<f32>>>()
})
.transpose()?;
let subtype = FunctionSubtype::from_stream_or_dict(stream_or_dict, resolver)?;
Ok(Self {
domain,
range,
subtype,
})
}
}
#[derive(Debug)]
enum FunctionSubtype {
Sampled(SampledFunction),
ExponentialInterpolation(ExponentialInterpolationFunction),
Stitching(StitchingFunction),
PostScriptCalculator(PostScriptCalculatorFunction),
}
impl FunctionSubtype {
pub fn from_stream_or_dict(
mut stream_or_dict: StreamOrDict,
resolver: &mut dyn Resolve,
) -> PdfResult<Self> {
let dict = stream_or_dict.dict();
let subtype = FunctionType::from_integer(dict.expect_integer("FunctionType", resolver)?)?;
Ok(match subtype {
FunctionType::Sampled => FunctionSubtype::Sampled(SampledFunction::from_stream(
stream_or_dict.expect_stream()?,
resolver,
)?),
FunctionType::ExponentialInterpolation => FunctionSubtype::ExponentialInterpolation(
ExponentialInterpolationFunction::from_dict(dict, resolver)?,
),
FunctionType::Stitching => {
FunctionSubtype::Stitching(StitchingFunction::from_dict(dict, resolver)?)
}
FunctionType::PostScriptCalculator => {
FunctionSubtype::PostScriptCalculator(PostScriptCalculatorFunction::from_stream(
stream_or_dict.expect_stream()?,
resolver,
)?)
}
})
}
}
pdf_enum!(
int
#[derive(Debug, Clone, Copy)]
enum FunctionType {
Sampled = 0,
ExponentialInterpolation = 2,
Stitching = 3,
PostScriptCalculator = 4,
}
);
#[derive(Debug)]
pub enum SpotFunction {
Predefined(PredefinedSpotFunction),
Function(Function),
}
impl SpotFunction {
pub fn from_obj(obj: Object, resolver: &mut dyn Resolve) -> PdfResult<Self> {
Ok(if let Object::Name(ref name) = obj {
SpotFunction::Predefined(PredefinedSpotFunction::from_str(name)?)
} else {
SpotFunction::Function(Function::from_obj(obj, resolver)?)
})
}
}
pdf_enum!(
#[derive(Debug)]
pub enum PredefinedSpotFunction {
SimpleDot = "SimpleDot",
InvertedSimpleDot = "InvertedSimpleDot",
DoubleDot = "DoubleDot",
InvertedDoubleDot = "InvertedDoubleDot",
CosineDot = "CosineDot",
Double = "Double",
Line = "Line",
LineX = "LineX",
LineY = "LineY",
Round = "Round",
Ellipse = "Ellipse",
EllipseA = "EllipseA",
InvertedEllipseA = "InvertedEllipseA",
EllipseB = "EllipseB",
EllipseC = "EllipseC",
InvertedEllipseC = "InvertedEllipseC",
Square = "Square",
Cross = "Cross",
Rhomboid = "Rhomboid",
Diamond = "Diamond",
}
);
#[derive(Debug)]
pub enum TransferFunction {
Identity,
Default,
Single(Function),
Colorants {
a: Function,
b: Function,
c: Function,
d: Function,
},
}
impl TransferFunction {
pub fn from_obj(obj: Object, resolver: &mut dyn Resolve) -> PdfResult<Self> {
Ok(if obj.name_is("Identity") {
TransferFunction::Identity
} else {
TransferFunction::Single(Function::from_obj(obj, resolver)?)
})
}
}