123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- /*
- * Copyright (c) 2023, Nico Weber <thakis@chromium.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/NonnullOwnPtr.h>
- #include <LibPDF/CommonNames.h>
- #include <LibPDF/Document.h>
- #include <LibPDF/Function.h>
- #include <LibPDF/ObjectDerivatives.h>
- // PDF 1.7 spec, 3.9 Functions
- namespace PDF {
- struct Bound {
- float lower;
- float upper;
- };
- class SampledFunction final : public Function {
- public:
- virtual PDFErrorOr<ReadonlySpan<float>> evaluate(ReadonlySpan<float>) const override;
- };
- PDFErrorOr<ReadonlySpan<float>> SampledFunction::evaluate(ReadonlySpan<float>) const
- {
- return Error(Error::Type::RenderingUnsupported, "SampledFunction not yet implemented"_string);
- }
- // 3.9.2 Type 2 (Exponential Interpolation) Functions
- class ExponentialInterpolationFunction final : public Function {
- public:
- static PDFErrorOr<NonnullRefPtr<ExponentialInterpolationFunction>> create(Document*, Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<DictObject>);
- virtual PDFErrorOr<ReadonlySpan<float>> evaluate(ReadonlySpan<float>) const override;
- private:
- Bound m_domain;
- Optional<Vector<Bound>> m_range;
- Vector<float> m_c0;
- Vector<float> m_c1;
- float m_n;
- Vector<float> mutable m_values;
- };
- PDFErrorOr<NonnullRefPtr<ExponentialInterpolationFunction>>
- ExponentialInterpolationFunction::create(Document* document, Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<DictObject> function_dict)
- {
- if (domain.size() != 1)
- return Error { Error::Type::MalformedPDF, "Function exponential requires domain with 1 entry" };
- // "TABLE 3.37 Additional entries specific to a type 2 function dictionary"
- if (!function_dict->contains(CommonNames::N))
- return Error { Error::Type::MalformedPDF, "Function exponential requires /N" };
- auto n = TRY(document->resolve(function_dict->get_value(CommonNames::N))).to_float();
- Vector<float> c0;
- if (function_dict->contains(CommonNames::C0)) {
- auto c0_array = TRY(function_dict->get_array(document, CommonNames::C0));
- for (size_t i = 0; i < c0_array->size(); i++)
- c0.append(c0_array->at(i).to_float());
- } else {
- c0.append(0.0f);
- }
- Vector<float> c1;
- if (function_dict->contains(CommonNames::C1)) {
- auto c1_array = TRY(function_dict->get_array(document, CommonNames::C1));
- for (size_t i = 0; i < c1_array->size(); i++)
- c1.append(c1_array->at(i).to_float());
- } else {
- c1.append(1.0f);
- }
- if (c0.size() != c1.size())
- return Error { Error::Type::MalformedPDF, "Function exponential mismatching C0 and C1 arrays" };
- if (range.has_value()) {
- if (range->size() != c0.size())
- return Error { Error::Type::MalformedPDF, "Function exponential mismatching Range and C arrays" };
- }
- // "Values of Domain must constrain x in such a way that if N is not an integer,
- // all values of x must be non-negative, and if N is negative, no value of x may be zero."
- if (n != (int)n && domain[0].lower < 0)
- return Error { Error::Type::MalformedPDF, "Function exponential requires non-negative bound for non-integer N" };
- if (n < 0 && (domain[0].lower <= 0 && domain[0].upper >= 0))
- return Error { Error::Type::MalformedPDF, "Function exponential with negative N requires non-zero domain" };
- auto function = adopt_ref(*new ExponentialInterpolationFunction());
- function->m_domain = domain[0];
- function->m_range = move(range);
- function->m_c0 = move(c0);
- function->m_c1 = move(c1);
- function->m_n = n;
- function->m_values.resize(function->m_c0.size());
- return function;
- }
- PDFErrorOr<ReadonlySpan<float>> ExponentialInterpolationFunction::evaluate(ReadonlySpan<float> xs) const
- {
- if (xs.size() != 1)
- return Error { Error::Type::MalformedPDF, "Function argument size does not match domain size" };
- float const x = clamp(xs[0], m_domain.lower, m_domain.upper);
- for (size_t i = 0; i < m_c0.size(); ++i)
- m_values[i] = m_c0[i] + pow(x, m_n) * (m_c1[i] - m_c0[i]);
- if (m_range.has_value()) {
- for (size_t i = 0; i < m_c0.size(); ++i)
- m_values[i] = clamp(m_values[i], m_range.value()[i].lower, m_range.value()[i].upper);
- }
- return m_values;
- }
- class StitchingFunction final : public Function {
- public:
- static PDFErrorOr<NonnullRefPtr<StitchingFunction>> create(Document*, Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<DictObject>);
- virtual PDFErrorOr<ReadonlySpan<float>> evaluate(ReadonlySpan<float>) const override;
- private:
- StitchingFunction(Vector<NonnullRefPtr<Function>>);
- Bound m_domain;
- Optional<Vector<Bound>> m_range;
- Vector<NonnullRefPtr<Function>> m_functions;
- Vector<float> m_bounds;
- Vector<Bound> m_encode;
- Vector<float> mutable m_result;
- };
- StitchingFunction::StitchingFunction(Vector<NonnullRefPtr<Function>> functions)
- : m_functions(move(functions))
- {
- }
- PDFErrorOr<NonnullRefPtr<StitchingFunction>>
- StitchingFunction::create(Document* document, Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<DictObject> dict)
- {
- if (domain.size() != 1)
- return Error { Error::Type::MalformedPDF, "Function stitching requires domain with 1 entry" };
- // "TABLE 3.38 Additional entries specific to a type 3 function dictionary"
- if (!dict->contains(CommonNames::Functions))
- return Error { Error::Type::MalformedPDF, "Function stitching requires /Functions" };
- auto functions_array = TRY(dict->get_array(document, CommonNames::Functions));
- Vector<NonnullRefPtr<Function>> functions;
- for (size_t i = 0; i < functions_array->size(); i++) {
- auto function = TRY(Function::create(document, functions_array->get_object_at(i)));
- functions.append(move(function));
- }
- if (functions.is_empty())
- return Error { Error::Type::MalformedPDF, "Function stitching requires at least one function" };
- if (!dict->contains(CommonNames::Bounds))
- return Error { Error::Type::MalformedPDF, "Function stitching requires /Bounds" };
- auto bounds_array = TRY(dict->get_array(document, CommonNames::Bounds));
- if (bounds_array->size() != functions.size() - 1)
- return Error { Error::Type::MalformedPDF, "Function stitching /Bounds size does not match /Functions size" };
- Vector<float> bounds;
- for (size_t i = 0; i < bounds_array->size(); i++) {
- bounds.append(bounds_array->at(i).to_float());
- if (i > 0 && bounds[i - 1] >= bounds[i])
- return Error { Error::Type::MalformedPDF, "Function stitching /Bounds not strictly increasing" };
- }
- if (!bounds.is_empty()) {
- if (domain[0].lower == domain[0].upper)
- return Error { Error::Type::MalformedPDF, "Function stitching /Bounds requires non-zero domain" };
- if (domain[0].lower >= bounds[0] || bounds.last() >= domain[0].upper)
- return Error { Error::Type::MalformedPDF, "Function stitching /Bounds out of domain" };
- }
- if (!dict->contains(CommonNames::Encode))
- return Error { Error::Type::MalformedPDF, "Function stitching requires /Encode" };
- auto encode_array = TRY(dict->get_array(document, CommonNames::Encode));
- if (encode_array->size() != functions.size() * 2)
- return Error { Error::Type::MalformedPDF, "Function stitching /Encode size does not match /Functions size" };
- Vector<Bound> encode;
- for (size_t i = 0; i < encode_array->size(); i += 2) {
- encode.append({ encode_array->at(i).to_float(), encode_array->at(i + 1).to_float() });
- if (encode.last().lower > encode.last().upper)
- return Error { Error::Type::MalformedPDF, "Function stitching /Encode lower bound > upper bound" };
- }
- auto function = adopt_ref(*new StitchingFunction(move(functions)));
- function->m_domain = domain[0];
- function->m_range = move(range);
- function->m_bounds = move(bounds);
- function->m_encode = move(encode);
- if (function->m_range.has_value())
- function->m_result.resize(function->m_range.value().size());
- return function;
- }
- PDFErrorOr<ReadonlySpan<float>> StitchingFunction::evaluate(ReadonlySpan<float>) const
- {
- return Error(Error::Type::RenderingUnsupported, "StitchingFunction not yet implemented"_string);
- }
- class PostScriptCalculatorFunction final : public Function {
- public:
- static PDFErrorOr<NonnullRefPtr<PostScriptCalculatorFunction>> create(Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<StreamObject>);
- virtual PDFErrorOr<ReadonlySpan<float>> evaluate(ReadonlySpan<float>) const override;
- private:
- // TABLE 3.39 Operators in type 4 functions
- enum class OperatorType {
- Operand,
- // Arithmetic operators
- Abs,
- Add,
- Atan,
- Ceiling,
- Cos,
- Cvi,
- Cvr,
- Div,
- Exp,
- Floor,
- Idiv,
- Ln,
- Log,
- Mod,
- Mul,
- Neg,
- Round,
- Sin,
- Sqrt,
- Sub,
- Truncate,
- // Relational, boolean, and bitwise operators
- And,
- Bitshift,
- Eq,
- False,
- Ge,
- Gt,
- Le,
- Lt,
- Ne,
- Not,
- Or,
- True,
- Xor,
- // Conditional operators
- If,
- IfElse,
- // Stack operators
- Copy,
- Dup,
- Exch,
- Index,
- Pop,
- Roll,
- };
- static Optional<OperatorType> parse_operator(Reader&);
- struct IfElse;
- struct Token {
- // FIXME: Could nan-box this.
- OperatorType type;
- Variant<Empty, float, int> value {};
- };
- struct IfElse {
- Vector<Token> if_true;
- Vector<Token> if_false;
- };
- static PDFErrorOr<Vector<Token>> parse_postscript_calculator_function(Reader&, Vector<NonnullOwnPtr<IfElse>>&);
- struct Stack {
- Array<float, 100> stack;
- size_t top { 0 };
- PDFErrorOr<void> push(float value)
- {
- if (top == stack.size())
- return Error { Error::Type::RenderingUnsupported, "PostScript stack overflow"_string };
- stack[top++] = value;
- return {};
- }
- PDFErrorOr<float> pop()
- {
- if (top == 0)
- return Error { Error::Type::RenderingUnsupported, "PostScript stack underflow"_string };
- return stack[--top];
- }
- };
- PDFErrorOr<void> execute(Vector<Token> const&, Stack&) const;
- Vector<Bound> m_domain;
- Vector<Bound> m_range;
- Vector<Token> m_tokens;
- Vector<NonnullOwnPtr<IfElse>> m_if_elses;
- Vector<float> mutable m_result;
- };
- Optional<PostScriptCalculatorFunction::OperatorType> PostScriptCalculatorFunction::parse_operator(Reader& reader)
- {
- auto match_keyword = [&](char const* keyword) {
- if (reader.matches(keyword)) {
- reader.consume((int)strlen(keyword));
- return true;
- }
- return false;
- };
- if (match_keyword("abs"))
- return OperatorType::Abs;
- if (match_keyword("add"))
- return OperatorType::Add;
- if (match_keyword("atan"))
- return OperatorType::Atan;
- if (match_keyword("ceiling"))
- return OperatorType::Ceiling;
- if (match_keyword("cos"))
- return OperatorType::Cos;
- if (match_keyword("cvi"))
- return OperatorType::Cvi;
- if (match_keyword("cvr"))
- return OperatorType::Cvr;
- if (match_keyword("div"))
- return OperatorType::Div;
- if (match_keyword("exp"))
- return OperatorType::Exp;
- if (match_keyword("floor"))
- return OperatorType::Floor;
- if (match_keyword("idiv"))
- return OperatorType::Idiv;
- if (match_keyword("ln"))
- return OperatorType::Ln;
- if (match_keyword("log"))
- return OperatorType::Log;
- if (match_keyword("mod"))
- return OperatorType::Mod;
- if (match_keyword("mul"))
- return OperatorType::Mul;
- if (match_keyword("neg"))
- return OperatorType::Neg;
- if (match_keyword("round"))
- return OperatorType::Round;
- if (match_keyword("sin"))
- return OperatorType::Sin;
- if (match_keyword("sqrt"))
- return OperatorType::Sqrt;
- if (match_keyword("sub"))
- return OperatorType::Sub;
- if (match_keyword("truncate"))
- return OperatorType::Truncate;
- if (match_keyword("and"))
- return OperatorType::And;
- if (match_keyword("bitshift"))
- return OperatorType::Bitshift;
- if (match_keyword("eq"))
- return OperatorType::Eq;
- if (match_keyword("false"))
- return OperatorType::False;
- if (match_keyword("ge"))
- return OperatorType::Ge;
- if (match_keyword("gt"))
- return OperatorType::Gt;
- if (match_keyword("le"))
- return OperatorType::Le;
- if (match_keyword("lt"))
- return OperatorType::Lt;
- if (match_keyword("ne"))
- return OperatorType::Ne;
- if (match_keyword("not"))
- return OperatorType::Not;
- if (match_keyword("or"))
- return OperatorType::Or;
- if (match_keyword("true"))
- return OperatorType::True;
- if (match_keyword("xor"))
- return OperatorType::Xor;
- // If and Ifelse handled elsewhere.
- if (match_keyword("copy"))
- return OperatorType::Copy;
- if (match_keyword("dup"))
- return OperatorType::Dup;
- if (match_keyword("exch"))
- return OperatorType::Exch;
- if (match_keyword("index"))
- return OperatorType::Index;
- if (match_keyword("pop"))
- return OperatorType::Pop;
- if (match_keyword("roll"))
- return OperatorType::Roll;
- return {};
- }
- PDFErrorOr<Vector<PostScriptCalculatorFunction::Token>>
- PostScriptCalculatorFunction::parse_postscript_calculator_function(Reader& reader, Vector<NonnullOwnPtr<IfElse>>& if_elses)
- {
- // Assumes valid syntax.
- reader.consume_whitespace();
- if (!reader.consume('{'))
- return Error { Error::Type::MalformedPDF, "PostScript expected '{'" };
- Vector<PostScriptCalculatorFunction::Token> tokens;
- while (!reader.matches('}')) {
- if (reader.consume_whitespace())
- continue;
- if (reader.matches('{')) {
- auto if_true = TRY(parse_postscript_calculator_function(reader, if_elses));
- reader.consume_whitespace();
- if (reader.matches("if")) {
- reader.consume(2);
- tokens.append({ OperatorType::If, (int)if_elses.size() });
- if_elses.append(adopt_own(*new IfElse { move(if_true), {} }));
- continue;
- }
- VERIFY(reader.matches('{'));
- auto if_false = TRY(parse_postscript_calculator_function(reader, if_elses));
- reader.consume_whitespace();
- if (reader.matches("ifelse")) {
- reader.consume(6);
- tokens.append({ OperatorType::IfElse, (int)if_elses.size() });
- if_elses.append(adopt_own(*new IfElse { move(if_true), move(if_false) }));
- continue;
- }
- return Error { Error::Type::MalformedPDF, "PostScript confused parsing {}-delimited expressions"_string };
- }
- if (reader.matches_number()) {
- // FIXME: Nicer float conversion.
- char const* start = reinterpret_cast<char const*>(reader.bytes().slice(reader.offset()).data());
- char* endptr;
- float value = strtof(start, &endptr);
- reader.move_by(endptr - start);
- tokens.append({ OperatorType::Operand, value });
- continue;
- }
- if (Optional<OperatorType> op = parse_operator(reader); op.has_value()) {
- tokens.append({ op.value() });
- continue;
- }
- return Error { Error::Type::MalformedPDF, "PostScript unknown operator"_string };
- }
- VERIFY(reader.consume('}'));
- return tokens;
- }
- PDFErrorOr<NonnullRefPtr<PostScriptCalculatorFunction>>
- PostScriptCalculatorFunction::create(Vector<Bound> domain, Optional<Vector<Bound>> range, NonnullRefPtr<StreamObject> stream)
- {
- if (!range.has_value())
- return Error { Error::Type::MalformedPDF, "Function type 4 requires /Range" };
- Vector<NonnullOwnPtr<IfElse>> if_elses;
- Reader reader { stream->bytes() };
- auto tokens = TRY(parse_postscript_calculator_function(reader, if_elses));
- auto function = adopt_ref(*new PostScriptCalculatorFunction());
- function->m_domain = move(domain);
- function->m_range = move(range.value());
- function->m_tokens = move(tokens);
- function->m_if_elses = move(if_elses);
- return function;
- }
- PDFErrorOr<void> PostScriptCalculatorFunction::execute(Vector<Token> const& tokens, Stack& stack) const
- {
- for (auto const& token : tokens) {
- switch (token.type) {
- case OperatorType::Operand:
- TRY(stack.push(token.value.get<float>()));
- break;
- case OperatorType::Abs:
- TRY(stack.push(fabsf(TRY(stack.pop()))));
- break;
- case OperatorType::Add: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a + b));
- break;
- }
- case OperatorType::Atan: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(AK::to_degrees(atan2f(b, a))));
- break;
- }
- case OperatorType::Ceiling:
- TRY(stack.push(ceilf(TRY(stack.pop()))));
- break;
- case OperatorType::Cos:
- TRY(stack.push(cosf(AK::to_radians(TRY(stack.pop())))));
- break;
- case OperatorType::Cvi:
- TRY(stack.push((int)TRY(stack.pop())));
- break;
- case OperatorType::Cvr:
- TRY(stack.push(TRY(stack.pop())));
- break;
- case OperatorType::Div: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a / b));
- break;
- }
- case OperatorType::Exp:
- TRY(stack.push(expf(TRY(stack.pop()))));
- break;
- case OperatorType::Floor:
- TRY(stack.push(floorf(TRY(stack.pop()))));
- break;
- case OperatorType::Idiv: {
- int b = (int)TRY(stack.pop());
- int a = (int)TRY(stack.pop());
- TRY(stack.push(a / b));
- break;
- }
- case OperatorType::Ln:
- TRY(stack.push(logf(TRY(stack.pop()))));
- break;
- case OperatorType::Log:
- TRY(stack.push(log10f(TRY(stack.pop()))));
- break;
- case OperatorType::Mod: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(fmodf(a, b)));
- break;
- }
- case OperatorType::Mul: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a * b));
- break;
- }
- case OperatorType::Neg:
- TRY(stack.push(-TRY(stack.pop())));
- break;
- case OperatorType::Round:
- TRY(stack.push(roundf(TRY(stack.pop()))));
- break;
- case OperatorType::Sin:
- TRY(stack.push(sinf(AK::to_radians(TRY(stack.pop())))));
- break;
- case OperatorType::Sqrt:
- TRY(stack.push(sqrtf(TRY(stack.pop()))));
- break;
- case OperatorType::Sub: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a - b));
- break;
- }
- case OperatorType::Truncate:
- TRY(stack.push(truncf(TRY(stack.pop()))));
- break;
- case OperatorType::And: {
- int b = (int)TRY(stack.pop());
- int a = (int)TRY(stack.pop());
- TRY(stack.push(a & b));
- break;
- }
- case OperatorType::Bitshift: {
- int b = (int)TRY(stack.pop());
- int a = (int)TRY(stack.pop());
- if (b >= 0)
- TRY(stack.push(a << b));
- else
- TRY(stack.push(a >> -b));
- break;
- }
- case OperatorType::Eq: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a == b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::False:
- TRY(stack.push(0.0f));
- break;
- case OperatorType::Ge: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a >= b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Gt: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a > b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Le: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a <= b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Lt: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a < b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Ne: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(a != b ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Not: {
- TRY(stack.push(TRY(stack.pop()) == 0.0f ? 1.0f : 0.0f));
- break;
- }
- case OperatorType::Or: {
- int b = (int)TRY(stack.pop());
- int a = (int)TRY(stack.pop());
- TRY(stack.push(a | b));
- break;
- }
- case OperatorType::True:
- TRY(stack.push(1.0f));
- break;
- case OperatorType::Xor: {
- int b = (int)TRY(stack.pop());
- int a = (int)TRY(stack.pop());
- TRY(stack.push(a ^ b));
- break;
- }
- case OperatorType::If: {
- auto const& if_else = m_if_elses[token.value.get<int>()];
- VERIFY(if_else->if_false.is_empty());
- if (TRY(stack.pop()) != 0.0f)
- TRY(execute(if_else->if_true, stack));
- break;
- }
- case OperatorType::IfElse: {
- auto const& if_else = m_if_elses[token.value.get<int>()];
- if (TRY(stack.pop()) != 0.0f)
- TRY(execute(if_else->if_true, stack));
- else
- TRY(execute(if_else->if_false, stack));
- break;
- }
- case OperatorType::Copy: {
- int n = (int)TRY(stack.pop());
- if (n < 0)
- return Error { Error::Type::RenderingUnsupported, "PostScript copy with negative argument"_string };
- if ((size_t)n > stack.top)
- return Error { Error::Type::RenderingUnsupported, "PostScript copy with argument larger than stack"_string };
- for (int i = 0; i < n; ++i)
- TRY(stack.push(stack.stack[stack.top - n]));
- break;
- }
- case OperatorType::Dup:
- TRY(stack.push(stack.stack[stack.top - 1]));
- break;
- case OperatorType::Exch: {
- float b = TRY(stack.pop());
- float a = TRY(stack.pop());
- TRY(stack.push(b));
- TRY(stack.push(a));
- break;
- }
- case OperatorType::Index: {
- int i = (int)TRY(stack.pop());
- if (i < 0)
- return Error { Error::Type::RenderingUnsupported, "PostScript index with negative argument"_string };
- if ((size_t)i >= stack.top)
- return Error { Error::Type::RenderingUnsupported, "PostScript index with argument larger than stack"_string };
- TRY(stack.push(stack.stack[stack.top - 1 - i]));
- break;
- }
- case OperatorType::Pop:
- TRY(stack.pop());
- break;
- case OperatorType::Roll: {
- int j = -(int)TRY(stack.pop());
- int n = (int)TRY(stack.pop());
- if (n < 0)
- return Error { Error::Type::RenderingUnsupported, "PostScript roll with negative argument"_string };
- if ((size_t)n > stack.top)
- return Error { Error::Type::RenderingUnsupported, "PostScript roll with argument larger than stack"_string };
- if (j < 0)
- j += n;
- if (j < 0)
- return Error { Error::Type::RenderingUnsupported, "PostScript roll with negative argument"_string };
- if (j > n)
- return Error { Error::Type::RenderingUnsupported, "PostScript roll with argument larger than stack"_string };
- // http://pointer-overloading.blogspot.com/2013/09/algorithms-rotating-one-dimensional.html
- auto elements = stack.stack.span().slice(stack.top - n, n);
- elements.slice(0, j).reverse();
- elements.slice(j).reverse();
- elements.reverse();
- break;
- }
- }
- }
- return {};
- }
- PDFErrorOr<ReadonlySpan<float>> PostScriptCalculatorFunction::evaluate(ReadonlySpan<float> xs) const
- {
- if (xs.size() != m_domain.size())
- return Error { Error::Type::MalformedPDF, "Function argument size does not match domain size" };
- Stack stack;
- for (size_t i = 0; i < xs.size(); ++i)
- TRY(stack.push(clamp(xs[i], m_domain[i].lower, m_domain[i].upper)));
- TRY(execute(m_tokens, stack));
- if (stack.top != m_range.size())
- return Error { Error::Type::MalformedPDF, "Postscript result size does not match range size"_string };
- // FIXME: Does this need reversing?
- m_result.resize(stack.top);
- for (size_t i = 0; i < stack.top; ++i)
- m_result[i] = clamp(stack.stack[i], m_range[i].lower, m_range[i].upper);
- return m_result;
- }
- PDFErrorOr<NonnullRefPtr<Function>> Function::create(Document* document, NonnullRefPtr<Object> object)
- {
- if (!object->is<DictObject>() && !object->is<StreamObject>())
- return Error { Error::Type::MalformedPDF, "Function object must be dict or stream" };
- auto function_dict = object->is<DictObject>() ? object->cast<DictObject>() : object->cast<StreamObject>()->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<int>(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<Bound> 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<Vector<Bound>> 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<Bound> 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:
- // FIXME: spec is not clear on if this should work with a StreamObject.
- return ExponentialInterpolationFunction::create(document, move(domain), move(optional_range), function_dict);
- case 3:
- // FIXME: spec is not clear on if this should work with a StreamObject.
- return StitchingFunction::create(document, move(domain), move(optional_range), function_dict);
- case 4:
- if (!object->is<StreamObject>())
- return Error { Error::Type::MalformedPDF, "Function type 4 requires stream object" };
- return PostScriptCalculatorFunction::create(move(domain), move(optional_range), object->cast<StreamObject>());
- default:
- dbgln("invalid function type {}", function_type);
- return Error(Error::Type::MalformedPDF, "Function has unkonwn type"_string);
- }
- }
- }
|