| // Copyright 2017 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "core/fpdfapi/page/cpdf_psengine.h" | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <limits> | |
| #include <utility> | |
| #include "core/fpdfapi/parser/cpdf_simple_parser.h" | |
| #include "core/fxcrt/fx_safe_types.h" | |
| #include "core/fxcrt/fx_string.h" | |
| #include "third_party/base/logging.h" | |
| namespace { | |
| struct PDF_PSOpName { | |
| const char* name; | |
| PDF_PSOP op; | |
| }; | |
| constexpr PDF_PSOpName kPsOpNames[] = { | |
| {"abs", PSOP_ABS}, | |
| {"add", PSOP_ADD}, | |
| {"and", PSOP_AND}, | |
| {"atan", PSOP_ATAN}, | |
| {"bitshift", PSOP_BITSHIFT}, | |
| {"ceiling", PSOP_CEILING}, | |
| {"copy", PSOP_COPY}, | |
| {"cos", PSOP_COS}, | |
| {"cvi", PSOP_CVI}, | |
| {"cvr", PSOP_CVR}, | |
| {"div", PSOP_DIV}, | |
| {"dup", PSOP_DUP}, | |
| {"eq", PSOP_EQ}, | |
| {"exch", PSOP_EXCH}, | |
| {"exp", PSOP_EXP}, | |
| {"false", PSOP_FALSE}, | |
| {"floor", PSOP_FLOOR}, | |
| {"ge", PSOP_GE}, | |
| {"gt", PSOP_GT}, | |
| {"idiv", PSOP_IDIV}, | |
| {"if", PSOP_IF}, | |
| {"ifelse", PSOP_IFELSE}, | |
| {"index", PSOP_INDEX}, | |
| {"le", PSOP_LE}, | |
| {"ln", PSOP_LN}, | |
| {"log", PSOP_LOG}, | |
| {"lt", PSOP_LT}, | |
| {"mod", PSOP_MOD}, | |
| {"mul", PSOP_MUL}, | |
| {"ne", PSOP_NE}, | |
| {"neg", PSOP_NEG}, | |
| {"not", PSOP_NOT}, | |
| {"or", PSOP_OR}, | |
| {"pop", PSOP_POP}, | |
| {"roll", PSOP_ROLL}, | |
| {"round", PSOP_ROUND}, | |
| {"sin", PSOP_SIN}, | |
| {"sqrt", PSOP_SQRT}, | |
| {"sub", PSOP_SUB}, | |
| {"true", PSOP_TRUE}, | |
| {"truncate", PSOP_TRUNCATE}, | |
| {"xor", PSOP_XOR}, | |
| }; | |
| // Round half up is a nearest integer round with half-way numbers always rounded | |
| // up. Example: -5.5 rounds to -5. | |
| float RoundHalfUp(float f) { | |
| if (std::isnan(f)) | |
| return 0; | |
| if (f > std::numeric_limits<float>::max() - 0.5f) | |
| return std::numeric_limits<float>::max(); | |
| return floor(f + 0.5f); | |
| } | |
| } // namespace | |
| CPDF_PSOP::CPDF_PSOP() | |
| : m_op(PSOP_PROC), m_value(0), m_proc(std::make_unique<CPDF_PSProc>()) {} | |
| CPDF_PSOP::CPDF_PSOP(PDF_PSOP op) : m_op(op), m_value(0) { | |
| ASSERT(m_op != PSOP_CONST); | |
| ASSERT(m_op != PSOP_PROC); | |
| } | |
| CPDF_PSOP::CPDF_PSOP(float value) : m_op(PSOP_CONST), m_value(value) {} | |
| CPDF_PSOP::~CPDF_PSOP() = default; | |
| float CPDF_PSOP::GetFloatValue() const { | |
| if (m_op == PSOP_CONST) | |
| return m_value; | |
| NOTREACHED(); | |
| return 0; | |
| } | |
| CPDF_PSProc* CPDF_PSOP::GetProc() const { | |
| if (m_op == PSOP_PROC) | |
| return m_proc.get(); | |
| NOTREACHED(); | |
| return nullptr; | |
| } | |
| bool CPDF_PSEngine::Execute() { | |
| return m_MainProc.Execute(this); | |
| } | |
| CPDF_PSProc::CPDF_PSProc() {} | |
| CPDF_PSProc::~CPDF_PSProc() = default; | |
| bool CPDF_PSProc::Parse(CPDF_SimpleParser* parser, int depth) { | |
| if (depth > kMaxDepth) | |
| return false; | |
| while (1) { | |
| ByteStringView word = parser->GetWord(); | |
| if (word.IsEmpty()) | |
| return false; | |
| if (word == "}") | |
| return true; | |
| if (word == "{") { | |
| m_Operators.push_back(std::make_unique<CPDF_PSOP>()); | |
| if (!m_Operators.back()->GetProc()->Parse(parser, depth + 1)) | |
| return false; | |
| continue; | |
| } | |
| AddOperator(word); | |
| } | |
| } | |
| bool CPDF_PSProc::Execute(CPDF_PSEngine* pEngine) { | |
| for (size_t i = 0; i < m_Operators.size(); ++i) { | |
| const PDF_PSOP op = m_Operators[i]->GetOp(); | |
| if (op == PSOP_PROC) | |
| continue; | |
| if (op == PSOP_CONST) { | |
| pEngine->Push(m_Operators[i]->GetFloatValue()); | |
| continue; | |
| } | |
| if (op == PSOP_IF) { | |
| if (i == 0 || m_Operators[i - 1]->GetOp() != PSOP_PROC) | |
| return false; | |
| if (pEngine->PopInt()) | |
| m_Operators[i - 1]->GetProc()->Execute(pEngine); | |
| } else if (op == PSOP_IFELSE) { | |
| if (i < 2 || m_Operators[i - 1]->GetOp() != PSOP_PROC || | |
| m_Operators[i - 2]->GetOp() != PSOP_PROC) { | |
| return false; | |
| } | |
| size_t offset = pEngine->PopInt() ? 2 : 1; | |
| m_Operators[i - offset]->GetProc()->Execute(pEngine); | |
| } else { | |
| pEngine->DoOperator(op); | |
| } | |
| } | |
| return true; | |
| } | |
| void CPDF_PSProc::AddOperatorForTesting(ByteStringView word) { | |
| AddOperator(word); | |
| } | |
| void CPDF_PSProc::AddOperator(ByteStringView word) { | |
| const auto* pFound = | |
| std::lower_bound(std::begin(kPsOpNames), std::end(kPsOpNames), word, | |
| [](const PDF_PSOpName& name, ByteStringView word) { | |
| return name.name < word; | |
| }); | |
| if (pFound != std::end(kPsOpNames) && pFound->name == word) | |
| m_Operators.push_back(std::make_unique<CPDF_PSOP>(pFound->op)); | |
| else | |
| m_Operators.push_back(std::make_unique<CPDF_PSOP>(StringToFloat(word))); | |
| } | |
| CPDF_PSEngine::CPDF_PSEngine() = default; | |
| CPDF_PSEngine::~CPDF_PSEngine() = default; | |
| void CPDF_PSEngine::Push(float v) { | |
| if (m_StackCount < kPSEngineStackSize) | |
| m_Stack[m_StackCount++] = v; | |
| } | |
| float CPDF_PSEngine::Pop() { | |
| return m_StackCount > 0 ? m_Stack[--m_StackCount] : 0; | |
| } | |
| int CPDF_PSEngine::PopInt() { | |
| return static_cast<int>(Pop()); | |
| } | |
| bool CPDF_PSEngine::Parse(pdfium::span<const uint8_t> input) { | |
| CPDF_SimpleParser parser(input); | |
| return parser.GetWord() == "{" && m_MainProc.Parse(&parser, 0); | |
| } | |
| bool CPDF_PSEngine::DoOperator(PDF_PSOP op) { | |
| int i1; | |
| int i2; | |
| float d1; | |
| float d2; | |
| FX_SAFE_INT32 result; | |
| switch (op) { | |
| case PSOP_ADD: | |
| d1 = Pop(); | |
| d2 = Pop(); | |
| Push(d1 + d2); | |
| break; | |
| case PSOP_SUB: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 - d2); | |
| break; | |
| case PSOP_MUL: | |
| d1 = Pop(); | |
| d2 = Pop(); | |
| Push(d1 * d2); | |
| break; | |
| case PSOP_DIV: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 / d2); | |
| break; | |
| case PSOP_IDIV: | |
| i2 = PopInt(); | |
| i1 = PopInt(); | |
| if (i2) { | |
| result = i1; | |
| result /= i2; | |
| Push(result.ValueOrDefault(0)); | |
| } else { | |
| Push(0); | |
| } | |
| break; | |
| case PSOP_MOD: | |
| i2 = PopInt(); | |
| i1 = PopInt(); | |
| if (i2) { | |
| result = i1; | |
| result %= i2; | |
| Push(result.ValueOrDefault(0)); | |
| } else { | |
| Push(0); | |
| } | |
| break; | |
| case PSOP_NEG: | |
| d1 = Pop(); | |
| Push(-d1); | |
| break; | |
| case PSOP_ABS: | |
| d1 = Pop(); | |
| Push(fabs(d1)); | |
| break; | |
| case PSOP_CEILING: | |
| d1 = Pop(); | |
| Push(ceil(d1)); | |
| break; | |
| case PSOP_FLOOR: | |
| d1 = Pop(); | |
| Push(floor(d1)); | |
| break; | |
| case PSOP_ROUND: | |
| d1 = Pop(); | |
| Push(RoundHalfUp(d1)); | |
| break; | |
| case PSOP_TRUNCATE: | |
| i1 = PopInt(); | |
| Push(i1); | |
| break; | |
| case PSOP_SQRT: | |
| d1 = Pop(); | |
| Push(sqrt(d1)); | |
| break; | |
| case PSOP_SIN: | |
| d1 = Pop(); | |
| Push(sin(d1 * FX_PI / 180.0f)); | |
| break; | |
| case PSOP_COS: | |
| d1 = Pop(); | |
| Push(cos(d1 * FX_PI / 180.0f)); | |
| break; | |
| case PSOP_ATAN: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| d1 = atan2(d1, d2) * 180.0 / FX_PI; | |
| if (d1 < 0) { | |
| d1 += 360; | |
| } | |
| Push(d1); | |
| break; | |
| case PSOP_EXP: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(FXSYS_pow(d1, d2)); | |
| break; | |
| case PSOP_LN: | |
| d1 = Pop(); | |
| Push(log(d1)); | |
| break; | |
| case PSOP_LOG: | |
| d1 = Pop(); | |
| Push(log10(d1)); | |
| break; | |
| case PSOP_CVI: | |
| i1 = PopInt(); | |
| Push(i1); | |
| break; | |
| case PSOP_CVR: | |
| break; | |
| case PSOP_EQ: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 == d2); | |
| break; | |
| case PSOP_NE: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 != d2); | |
| break; | |
| case PSOP_GT: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 > d2); | |
| break; | |
| case PSOP_GE: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 >= d2); | |
| break; | |
| case PSOP_LT: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 < d2); | |
| break; | |
| case PSOP_LE: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d1 <= d2); | |
| break; | |
| case PSOP_AND: | |
| i1 = PopInt(); | |
| i2 = PopInt(); | |
| Push(i1 & i2); | |
| break; | |
| case PSOP_OR: | |
| i1 = PopInt(); | |
| i2 = PopInt(); | |
| Push(i1 | i2); | |
| break; | |
| case PSOP_XOR: | |
| i1 = PopInt(); | |
| i2 = PopInt(); | |
| Push(i1 ^ i2); | |
| break; | |
| case PSOP_NOT: | |
| i1 = PopInt(); | |
| Push(!i1); | |
| break; | |
| case PSOP_BITSHIFT: { | |
| int shift = PopInt(); | |
| result = PopInt(); | |
| if (shift > 0) { | |
| result <<= shift; | |
| } else { | |
| // Avoids unsafe negation of INT_MIN. | |
| FX_SAFE_INT32 safe_shift = shift; | |
| result >>= (-safe_shift).ValueOrDefault(0); | |
| } | |
| Push(result.ValueOrDefault(0)); | |
| break; | |
| } | |
| case PSOP_TRUE: | |
| Push(1); | |
| break; | |
| case PSOP_FALSE: | |
| Push(0); | |
| break; | |
| case PSOP_POP: | |
| Pop(); | |
| break; | |
| case PSOP_EXCH: | |
| d2 = Pop(); | |
| d1 = Pop(); | |
| Push(d2); | |
| Push(d1); | |
| break; | |
| case PSOP_DUP: | |
| d1 = Pop(); | |
| Push(d1); | |
| Push(d1); | |
| break; | |
| case PSOP_COPY: { | |
| int n = PopInt(); | |
| if (n < 0 || m_StackCount + n > kPSEngineStackSize || | |
| n > static_cast<int>(m_StackCount)) | |
| break; | |
| for (int i = 0; i < n; i++) | |
| m_Stack[m_StackCount + i] = m_Stack[m_StackCount + i - n]; | |
| m_StackCount += n; | |
| break; | |
| } | |
| case PSOP_INDEX: { | |
| int n = PopInt(); | |
| if (n < 0 || n >= static_cast<int>(m_StackCount)) | |
| break; | |
| Push(m_Stack[m_StackCount - n - 1]); | |
| break; | |
| } | |
| case PSOP_ROLL: { | |
| int j = PopInt(); | |
| int n = PopInt(); | |
| if (j == 0 || n == 0 || m_StackCount == 0) | |
| break; | |
| if (n < 0 || n > static_cast<int>(m_StackCount)) | |
| break; | |
| j %= n; | |
| if (j > 0) | |
| j -= n; | |
| auto* begin_it = std::begin(m_Stack) + m_StackCount - n; | |
| auto* middle_it = begin_it - j; | |
| auto* end_it = std::begin(m_Stack) + m_StackCount; | |
| std::rotate(begin_it, middle_it, end_it); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| return true; | |
| } |