// 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 #include #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" #include "third_party/base/ptr_util.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}, }; } // namespace CPDF_PSOP::CPDF_PSOP() : m_op(PSOP_PROC), m_value(0), m_proc(pdfium::MakeUnique()) {} 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() {} 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() {} 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(pdfium::MakeUnique()); 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(const ByteStringView& word) { AddOperator(word); } void CPDF_PSProc::AddOperator(const ByteStringView& word) { const auto* pFound = std::lower_bound( std::begin(kPsOpNames), std::end(kPsOpNames), word, [](const PDF_PSOpName& name, const ByteStringView& word) { return name.name < word; }); if (pFound != std::end(kPsOpNames) && pFound->name == word) m_Operators.push_back(pdfium::MakeUnique(pFound->op)); else m_Operators.push_back(pdfium::MakeUnique(FX_atof(word))); } CPDF_PSEngine::CPDF_PSEngine() : m_StackCount(0) {} CPDF_PSEngine::~CPDF_PSEngine() {} 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(Pop()); } bool CPDF_PSEngine::Parse(pdfium::span 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(FXSYS_round(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(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(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(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; }