/* * Copyright (c) 2023, Nico Weber * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include // PDF 1.7 spec, 3.9 Functions namespace PDF { struct Bound { float lower; float upper; }; class SampledFunction final : public Function { public: virtual PDFErrorOr> evaluate(ReadonlySpan) const override; }; PDFErrorOr> SampledFunction::evaluate(ReadonlySpan) const { return Error(Error::Type::RenderingUnsupported, "SampledFunction not yet implemented"_string); } class ExponentialInterpolationFunction final : public Function { public: virtual PDFErrorOr> evaluate(ReadonlySpan) const override; }; PDFErrorOr> ExponentialInterpolationFunction::evaluate(ReadonlySpan) const { return Error(Error::Type::RenderingUnsupported, "ExponentialInterpolationFunction not yet implemented"_string); } class StitchingFunction final : public Function { public: virtual PDFErrorOr> evaluate(ReadonlySpan) const override; }; PDFErrorOr> StitchingFunction::evaluate(ReadonlySpan) const { return Error(Error::Type::RenderingUnsupported, "StitchingFunction not yet implemented"_string); } class PostScriptCalculatorFunction final : public Function { public: virtual PDFErrorOr> evaluate(ReadonlySpan) const override; }; PDFErrorOr> PostScriptCalculatorFunction::evaluate(ReadonlySpan) const { return Error(Error::Type::RenderingUnsupported, "PostScriptCalculatorFunction not yet implemented"_string); } PDFErrorOr> Function::create(Document* document, NonnullRefPtr object) { if (!object->is() && !object->is()) return Error { Error::Type::MalformedPDF, "Function object must be dict or stream" }; auto function_dict = object->is() ? object->cast() : object->cast()->dict(); // "TABLE 3.35 Entries common to all function dictionaries" if (!function_dict->contains(CommonNames::FunctionType)) return Error { Error::Type::MalformedPDF, "Function requires /FunctionType" }; auto function_type = TRY(document->resolve_to(function_dict->get_value(CommonNames::FunctionType))); if (!function_dict->contains(CommonNames::Domain)) return Error { Error::Type::MalformedPDF, "Function requires /Domain" }; auto domain_array = TRY(function_dict->get_array(document, CommonNames::Domain)); if (domain_array->size() % 2 != 0) return Error { Error::Type::MalformedPDF, "Function /Domain size not multiple of 2" }; Vector domain; for (size_t i = 0; i < domain_array->size(); i += 2) { domain.append({ domain_array->at(i).to_float(), domain_array->at(i + 1).to_float() }); if (domain.last().lower > domain.last().upper) return Error { Error::Type::MalformedPDF, "Function /Domain lower bound > upper bound" }; } // Can't use PDFErrorOr with Optional::map() Optional> optional_range; if (function_dict->contains(CommonNames::Range)) { auto range_array = TRY(function_dict->get_array(document, CommonNames::Range)); if (range_array->size() % 2 != 0) return Error { Error::Type::MalformedPDF, "Function /Range size not multiple of 2" }; Vector range; for (size_t i = 0; i < range_array->size(); i += 2) { range.append({ range_array->at(i).to_float(), range_array->at(i + 1).to_float() }); if (range.last().lower > range.last().upper) return Error { Error::Type::MalformedPDF, "Function /Range lower bound > upper bound" }; } optional_range = move(range); } switch (function_type) { case 0: return adopt_ref(*new SampledFunction()); // The spec has no entry for `1`. case 2: return adopt_ref(*new ExponentialInterpolationFunction()); case 3: return adopt_ref(*new StitchingFunction()); case 4: return adopt_ref(*new PostScriptCalculatorFunction()); default: dbgln("invalid function type {}", function_type); return Error(Error::Type::MalformedPDF, "Function has unkonwn type"_string); } } }