// Copyright 2014 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/fpdf_page/pageint.h" #include #include #include #include #include #include "core/fpdfapi/fpdf_parser/include/cpdf_array.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_dictionary.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_simple_parser.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_stream.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_stream_acc.h" #include "core/fxcrt/include/fx_safe_types.h" #include "third_party/base/numerics/safe_conversions_impl.h" namespace { enum PDF_PSOP { PSOP_ADD, PSOP_SUB, PSOP_MUL, PSOP_DIV, PSOP_IDIV, PSOP_MOD, PSOP_NEG, PSOP_ABS, PSOP_CEILING, PSOP_FLOOR, PSOP_ROUND, PSOP_TRUNCATE, PSOP_SQRT, PSOP_SIN, PSOP_COS, PSOP_ATAN, PSOP_EXP, PSOP_LN, PSOP_LOG, PSOP_CVI, PSOP_CVR, PSOP_EQ, PSOP_NE, PSOP_GT, PSOP_GE, PSOP_LT, PSOP_LE, PSOP_AND, PSOP_OR, PSOP_XOR, PSOP_NOT, PSOP_BITSHIFT, PSOP_TRUE, PSOP_FALSE, PSOP_IF, PSOP_IFELSE, PSOP_POP, PSOP_EXCH, PSOP_DUP, PSOP_COPY, PSOP_INDEX, PSOP_ROLL, PSOP_PROC, PSOP_CONST }; class CPDF_PSEngine; class CPDF_PSProc; class CPDF_PSOP { public: explicit CPDF_PSOP(PDF_PSOP op) : m_op(op), m_value(0) { ASSERT(m_op != PSOP_CONST); ASSERT(m_op != PSOP_PROC); } explicit CPDF_PSOP(FX_FLOAT value) : m_op(PSOP_CONST), m_value(value) {} explicit CPDF_PSOP(std::unique_ptr proc) : m_op(PSOP_PROC), m_value(0), m_proc(std::move(proc)) {} FX_FLOAT GetFloatValue() const { if (m_op == PSOP_CONST) return m_value; ASSERT(false); return 0; } CPDF_PSProc* GetProc() const { if (m_op == PSOP_PROC) return m_proc.get(); ASSERT(false); return nullptr; } PDF_PSOP GetOp() const { return m_op; } private: const PDF_PSOP m_op; const FX_FLOAT m_value; std::unique_ptr m_proc; }; class CPDF_PSProc { public: CPDF_PSProc() {} ~CPDF_PSProc() {} FX_BOOL Parse(CPDF_SimpleParser* parser); FX_BOOL Execute(CPDF_PSEngine* pEngine); private: std::vector> m_Operators; }; const uint32_t PSENGINE_STACKSIZE = 100; class CPDF_PSEngine { public: CPDF_PSEngine(); ~CPDF_PSEngine(); FX_BOOL Parse(const FX_CHAR* str, int size); FX_BOOL Execute() { return m_MainProc.Execute(this); } FX_BOOL DoOperator(PDF_PSOP op); void Reset() { m_StackCount = 0; } void Push(FX_FLOAT value); void Push(int value) { Push((FX_FLOAT)value); } FX_FLOAT Pop(); uint32_t GetStackSize() const { return m_StackCount; } private: FX_FLOAT m_Stack[PSENGINE_STACKSIZE]; uint32_t m_StackCount; CPDF_PSProc m_MainProc; }; FX_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 (static_cast(pEngine->Pop())) 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 = static_cast(pEngine->Pop()) ? 2 : 1; m_Operators[i - offset]->GetProc()->Execute(pEngine); } else { pEngine->DoOperator(op); } } return TRUE; } CPDF_PSEngine::CPDF_PSEngine() { m_StackCount = 0; } CPDF_PSEngine::~CPDF_PSEngine() {} void CPDF_PSEngine::Push(FX_FLOAT v) { if (m_StackCount == PSENGINE_STACKSIZE) { return; } m_Stack[m_StackCount++] = v; } FX_FLOAT CPDF_PSEngine::Pop() { if (m_StackCount == 0) { return 0; } return m_Stack[--m_StackCount]; } const struct PDF_PSOpName { const FX_CHAR* name; PDF_PSOP op; } PDF_PSOpNames[] = {{"add", PSOP_ADD}, {"sub", PSOP_SUB}, {"mul", PSOP_MUL}, {"div", PSOP_DIV}, {"idiv", PSOP_IDIV}, {"mod", PSOP_MOD}, {"neg", PSOP_NEG}, {"abs", PSOP_ABS}, {"ceiling", PSOP_CEILING}, {"floor", PSOP_FLOOR}, {"round", PSOP_ROUND}, {"truncate", PSOP_TRUNCATE}, {"sqrt", PSOP_SQRT}, {"sin", PSOP_SIN}, {"cos", PSOP_COS}, {"atan", PSOP_ATAN}, {"exp", PSOP_EXP}, {"ln", PSOP_LN}, {"log", PSOP_LOG}, {"cvi", PSOP_CVI}, {"cvr", PSOP_CVR}, {"eq", PSOP_EQ}, {"ne", PSOP_NE}, {"gt", PSOP_GT}, {"ge", PSOP_GE}, {"lt", PSOP_LT}, {"le", PSOP_LE}, {"and", PSOP_AND}, {"or", PSOP_OR}, {"xor", PSOP_XOR}, {"not", PSOP_NOT}, {"bitshift", PSOP_BITSHIFT}, {"true", PSOP_TRUE}, {"false", PSOP_FALSE}, {"if", PSOP_IF}, {"ifelse", PSOP_IFELSE}, {"pop", PSOP_POP}, {"exch", PSOP_EXCH}, {"dup", PSOP_DUP}, {"copy", PSOP_COPY}, {"index", PSOP_INDEX}, {"roll", PSOP_ROLL}}; FX_BOOL CPDF_PSEngine::Parse(const FX_CHAR* str, int size) { CPDF_SimpleParser parser((uint8_t*)str, size); CFX_ByteStringC word = parser.GetWord(); if (word != "{") { return FALSE; } return m_MainProc.Parse(&parser); } FX_BOOL CPDF_PSProc::Parse(CPDF_SimpleParser* parser) { while (1) { CFX_ByteStringC word = parser->GetWord(); if (word.IsEmpty()) { return FALSE; } if (word == "}") { return TRUE; } if (word == "{") { std::unique_ptr proc(new CPDF_PSProc); std::unique_ptr op(new CPDF_PSOP(std::move(proc))); m_Operators.push_back(std::move(op)); if (!m_Operators.back()->GetProc()->Parse(parser)) { return FALSE; } } else { bool found = false; for (const PDF_PSOpName& op_name : PDF_PSOpNames) { if (word == CFX_ByteStringC(op_name.name)) { std::unique_ptr op(new CPDF_PSOP(op_name.op)); m_Operators.push_back(std::move(op)); found = true; break; } } if (!found) { std::unique_ptr op(new CPDF_PSOP(FX_atof(word))); m_Operators.push_back(std::move(op)); } } } } FX_BOOL CPDF_PSEngine::DoOperator(PDF_PSOP op) { int i1, i2; FX_FLOAT d1, d2; 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 = (int)Pop(); i1 = (int)Pop(); Push(i1 / i2); break; case PSOP_MOD: i2 = (int)Pop(); i1 = (int)Pop(); Push(i1 % i2); break; case PSOP_NEG: d1 = Pop(); Push(-d1); break; case PSOP_ABS: d1 = Pop(); Push((FX_FLOAT)FXSYS_fabs(d1)); break; case PSOP_CEILING: d1 = Pop(); Push((FX_FLOAT)FXSYS_ceil(d1)); break; case PSOP_FLOOR: d1 = Pop(); Push((FX_FLOAT)FXSYS_floor(d1)); break; case PSOP_ROUND: d1 = Pop(); Push(FXSYS_round(d1)); break; case PSOP_TRUNCATE: i1 = (int)Pop(); Push(i1); break; case PSOP_SQRT: d1 = Pop(); Push((FX_FLOAT)FXSYS_sqrt(d1)); break; case PSOP_SIN: d1 = Pop(); Push((FX_FLOAT)FXSYS_sin(d1 * FX_PI / 180.0f)); break; case PSOP_COS: d1 = Pop(); Push((FX_FLOAT)FXSYS_cos(d1 * FX_PI / 180.0f)); break; case PSOP_ATAN: d2 = Pop(); d1 = Pop(); d1 = (FX_FLOAT)(FXSYS_atan2(d1, d2) * 180.0 / FX_PI); if (d1 < 0) { d1 += 360; } Push(d1); break; case PSOP_EXP: d2 = Pop(); d1 = Pop(); Push((FX_FLOAT)FXSYS_pow(d1, d2)); break; case PSOP_LN: d1 = Pop(); Push((FX_FLOAT)FXSYS_log(d1)); break; case PSOP_LOG: d1 = Pop(); Push((FX_FLOAT)FXSYS_log10(d1)); break; case PSOP_CVI: i1 = (int)Pop(); Push(i1); break; case PSOP_CVR: break; case PSOP_EQ: d2 = Pop(); d1 = Pop(); Push((int)(d1 == d2)); break; case PSOP_NE: d2 = Pop(); d1 = Pop(); Push((int)(d1 != d2)); break; case PSOP_GT: d2 = Pop(); d1 = Pop(); Push((int)(d1 > d2)); break; case PSOP_GE: d2 = Pop(); d1 = Pop(); Push((int)(d1 >= d2)); break; case PSOP_LT: d2 = Pop(); d1 = Pop(); Push((int)(d1 < d2)); break; case PSOP_LE: d2 = Pop(); d1 = Pop(); Push((int)(d1 <= d2)); break; case PSOP_AND: i1 = (int)Pop(); i2 = (int)Pop(); Push(i1 & i2); break; case PSOP_OR: i1 = (int)Pop(); i2 = (int)Pop(); Push(i1 | i2); break; case PSOP_XOR: i1 = (int)Pop(); i2 = (int)Pop(); Push(i1 ^ i2); break; case PSOP_NOT: i1 = (int)Pop(); Push((int)!i1); break; case PSOP_BITSHIFT: { int shift = (int)Pop(); int i = (int)Pop(); if (shift > 0) { Push(i << shift); } else { Push(i >> -shift); } 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 = static_cast(Pop()); if (n < 0 || m_StackCount + n > PSENGINE_STACKSIZE || 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 = static_cast(Pop()); if (n < 0 || n >= static_cast(m_StackCount)) break; Push(m_Stack[m_StackCount - n - 1]); break; } case PSOP_ROLL: { int j = static_cast(Pop()); int n = static_cast(Pop()); if (m_StackCount == 0) break; if (n < 0 || n > static_cast(m_StackCount)) break; if (j < 0) { for (int i = 0; i < -j; i++) { FX_FLOAT first = m_Stack[m_StackCount - n]; for (int ii = 0; ii < n - 1; ii++) m_Stack[m_StackCount - n + ii] = m_Stack[m_StackCount - n + ii + 1]; m_Stack[m_StackCount - 1] = first; } } else { for (int i = 0; i < j; i++) { FX_FLOAT last = m_Stack[m_StackCount - 1]; int ii; for (ii = 0; ii < n - 1; ii++) m_Stack[m_StackCount - ii - 1] = m_Stack[m_StackCount - ii - 2]; m_Stack[m_StackCount - ii - 1] = last; } } break; } default: break; } return TRUE; } // See PDF Reference 1.7, page 170, table 3.36. bool IsValidBitsPerSample(uint32_t x) { switch (x) { case 1: case 2: case 4: case 8: case 12: case 16: case 24: case 32: return true; default: return false; } } // See PDF Reference 1.7, page 170. FX_FLOAT PDF_Interpolate(FX_FLOAT x, FX_FLOAT xmin, FX_FLOAT xmax, FX_FLOAT ymin, FX_FLOAT ymax) { FX_FLOAT divisor = xmax - xmin; return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0); } class CPDF_PSFunc : public CPDF_Function { public: CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {} ~CPDF_PSFunc() override {} // CPDF_Function FX_BOOL v_Init(CPDF_Object* pObj) override; FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override; private: CPDF_PSEngine m_PS; }; FX_BOOL CPDF_PSFunc::v_Init(CPDF_Object* pObj) { CPDF_StreamAcc acc; acc.LoadAllData(pObj->AsStream(), FALSE); return m_PS.Parse(reinterpret_cast(acc.GetData()), acc.GetSize()); } FX_BOOL CPDF_PSFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const { CPDF_PSEngine& PS = const_cast(m_PS); PS.Reset(); for (uint32_t i = 0; i < m_nInputs; i++) PS.Push(inputs[i]); PS.Execute(); if (PS.GetStackSize() < m_nOutputs) return FALSE; for (uint32_t i = 0; i < m_nOutputs; i++) results[m_nOutputs - i - 1] = PS.Pop(); return TRUE; } } // namespace CPDF_SampledFunc::CPDF_SampledFunc() : CPDF_Function(Type::kType0Sampled) {} CPDF_SampledFunc::~CPDF_SampledFunc() {} FX_BOOL CPDF_SampledFunc::v_Init(CPDF_Object* pObj) { CPDF_Stream* pStream = pObj->AsStream(); if (!pStream) return false; CPDF_Dictionary* pDict = pStream->GetDict(); CPDF_Array* pSize = pDict->GetArrayBy("Size"); CPDF_Array* pEncode = pDict->GetArrayBy("Encode"); CPDF_Array* pDecode = pDict->GetArrayBy("Decode"); m_nBitsPerSample = pDict->GetIntegerBy("BitsPerSample"); if (!IsValidBitsPerSample(m_nBitsPerSample)) return FALSE; m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample); m_pSampleStream.reset(new CPDF_StreamAcc); m_pSampleStream->LoadAllData(pStream, FALSE); FX_SAFE_UINT32 nTotalSampleBits = 1; m_EncodeInfo.resize(m_nInputs); for (uint32_t i = 0; i < m_nInputs; i++) { m_EncodeInfo[i].sizes = pSize ? pSize->GetIntegerAt(i) : 0; if (!pSize && i == 0) m_EncodeInfo[i].sizes = pDict->GetIntegerBy("Size"); nTotalSampleBits *= m_EncodeInfo[i].sizes; if (pEncode) { m_EncodeInfo[i].encode_min = pEncode->GetFloatAt(i * 2); m_EncodeInfo[i].encode_max = pEncode->GetFloatAt(i * 2 + 1); } else { m_EncodeInfo[i].encode_min = 0; m_EncodeInfo[i].encode_max = m_EncodeInfo[i].sizes == 1 ? 1 : (FX_FLOAT)m_EncodeInfo[i].sizes - 1; } } nTotalSampleBits *= m_nBitsPerSample; nTotalSampleBits *= m_nOutputs; FX_SAFE_UINT32 nTotalSampleBytes = nTotalSampleBits; nTotalSampleBytes += 7; nTotalSampleBytes /= 8; if (!nTotalSampleBytes.IsValid() || nTotalSampleBytes.ValueOrDie() == 0 || nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize()) { return FALSE; } m_DecodeInfo.resize(m_nOutputs); for (uint32_t i = 0; i < m_nOutputs; i++) { if (pDecode) { m_DecodeInfo[i].decode_min = pDecode->GetFloatAt(2 * i); m_DecodeInfo[i].decode_max = pDecode->GetFloatAt(2 * i + 1); } else { m_DecodeInfo[i].decode_min = m_pRanges[i * 2]; m_DecodeInfo[i].decode_max = m_pRanges[i * 2 + 1]; } } return TRUE; } FX_BOOL CPDF_SampledFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const { int pos = 0; CFX_FixedBufGrow encoded_input_buf(m_nInputs); FX_FLOAT* encoded_input = encoded_input_buf; CFX_FixedBufGrow int_buf(m_nInputs * 2); uint32_t* index = int_buf; uint32_t* blocksize = index + m_nInputs; for (uint32_t i = 0; i < m_nInputs; i++) { if (i == 0) blocksize[i] = 1; else blocksize[i] = blocksize[i - 1] * m_EncodeInfo[i - 1].sizes; encoded_input[i] = PDF_Interpolate(inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1], m_EncodeInfo[i].encode_min, m_EncodeInfo[i].encode_max); index[i] = std::min((uint32_t)std::max(0.f, encoded_input[i]), m_EncodeInfo[i].sizes - 1); pos += index[i] * blocksize[i]; } FX_SAFE_INT32 bits_to_output = m_nOutputs; bits_to_output *= m_nBitsPerSample; if (!bits_to_output.IsValid()) return FALSE; FX_SAFE_INT32 bitpos = pos; bitpos *= bits_to_output.ValueOrDie(); if (!bitpos.IsValid()) return FALSE; FX_SAFE_INT32 range_check = bitpos; range_check += bits_to_output.ValueOrDie(); if (!range_check.IsValid()) return FALSE; const uint8_t* pSampleData = m_pSampleStream->GetData(); if (!pSampleData) return FALSE; for (uint32_t j = 0; j < m_nOutputs; j++) { uint32_t sample = GetBits32(pSampleData, bitpos.ValueOrDie() + j * m_nBitsPerSample, m_nBitsPerSample); FX_FLOAT encoded = (FX_FLOAT)sample; for (uint32_t i = 0; i < m_nInputs; i++) { if (index[i] == m_EncodeInfo[i].sizes - 1) { if (index[i] == 0) encoded = encoded_input[i] * (FX_FLOAT)sample; } else { FX_SAFE_INT32 bitpos2 = blocksize[i]; bitpos2 += pos; bitpos2 *= m_nOutputs; bitpos2 += j; bitpos2 *= m_nBitsPerSample; if (!bitpos2.IsValid()) return FALSE; uint32_t sample1 = GetBits32(pSampleData, bitpos2.ValueOrDie(), m_nBitsPerSample); encoded += (encoded_input[i] - index[i]) * ((FX_FLOAT)sample1 - (FX_FLOAT)sample); } } results[j] = PDF_Interpolate(encoded, 0, (FX_FLOAT)m_SampleMax, m_DecodeInfo[j].decode_min, m_DecodeInfo[j].decode_max); } return TRUE; } CPDF_ExpIntFunc::CPDF_ExpIntFunc() : CPDF_Function(Type::kType2ExpotentialInterpolation), m_pBeginValues(nullptr), m_pEndValues(nullptr) {} CPDF_ExpIntFunc::~CPDF_ExpIntFunc() { FX_Free(m_pBeginValues); FX_Free(m_pEndValues); } FX_BOOL CPDF_ExpIntFunc::v_Init(CPDF_Object* pObj) { CPDF_Dictionary* pDict = pObj->GetDict(); if (!pDict) { return FALSE; } CPDF_Array* pArray0 = pDict->GetArrayBy("C0"); if (m_nOutputs == 0) { m_nOutputs = 1; if (pArray0) { m_nOutputs = pArray0->GetCount(); } } CPDF_Array* pArray1 = pDict->GetArrayBy("C1"); m_pBeginValues = FX_Alloc2D(FX_FLOAT, m_nOutputs, 2); m_pEndValues = FX_Alloc2D(FX_FLOAT, m_nOutputs, 2); for (uint32_t i = 0; i < m_nOutputs; i++) { m_pBeginValues[i] = pArray0 ? pArray0->GetFloatAt(i) : 0.0f; m_pEndValues[i] = pArray1 ? pArray1->GetFloatAt(i) : 1.0f; } m_Exponent = pDict->GetFloatBy("N"); m_nOrigOutputs = m_nOutputs; if (m_nOutputs && m_nInputs > INT_MAX / m_nOutputs) { return FALSE; } m_nOutputs *= m_nInputs; return TRUE; } FX_BOOL CPDF_ExpIntFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const { for (uint32_t i = 0; i < m_nInputs; i++) for (uint32_t j = 0; j < m_nOrigOutputs; j++) { results[i * m_nOrigOutputs + j] = m_pBeginValues[j] + (FX_FLOAT)FXSYS_pow(inputs[i], m_Exponent) * (m_pEndValues[j] - m_pBeginValues[j]); } return TRUE; } CPDF_StitchFunc::CPDF_StitchFunc() : CPDF_Function(Type::kType3Stitching), m_pBounds(nullptr), m_pEncode(nullptr) {} CPDF_StitchFunc::~CPDF_StitchFunc() { FX_Free(m_pBounds); FX_Free(m_pEncode); } FX_BOOL CPDF_StitchFunc::v_Init(CPDF_Object* pObj) { CPDF_Dictionary* pDict = pObj->GetDict(); if (!pDict) { return FALSE; } if (m_nInputs != kRequiredNumInputs) { return FALSE; } CPDF_Array* pArray = pDict->GetArrayBy("Functions"); if (!pArray) { return FALSE; } uint32_t nSubs = pArray->GetCount(); if (nSubs == 0) return FALSE; m_nOutputs = 0; for (uint32_t i = 0; i < nSubs; i++) { CPDF_Object* pSub = pArray->GetDirectObjectAt(i); if (pSub == pObj) return FALSE; std::unique_ptr pFunc(CPDF_Function::Load(pSub)); if (!pFunc) return FALSE; // Check that the input dimensionality is 1, and that all output // dimensionalities are the same. if (pFunc->CountInputs() != kRequiredNumInputs) return FALSE; if (pFunc->CountOutputs() != m_nOutputs) { if (m_nOutputs) return FALSE; m_nOutputs = pFunc->CountOutputs(); } m_pSubFunctions.push_back(std::move(pFunc)); } m_pBounds = FX_Alloc(FX_FLOAT, nSubs + 1); m_pBounds[0] = m_pDomains[0]; pArray = pDict->GetArrayBy("Bounds"); if (!pArray) return FALSE; for (uint32_t i = 0; i < nSubs - 1; i++) m_pBounds[i + 1] = pArray->GetFloatAt(i); m_pBounds[nSubs] = m_pDomains[1]; m_pEncode = FX_Alloc2D(FX_FLOAT, nSubs, 2); pArray = pDict->GetArrayBy("Encode"); if (!pArray) return FALSE; for (uint32_t i = 0; i < nSubs * 2; i++) m_pEncode[i] = pArray->GetFloatAt(i); return TRUE; } FX_BOOL CPDF_StitchFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* outputs) const { FX_FLOAT input = inputs[0]; size_t i; for (i = 0; i < m_pSubFunctions.size() - 1; i++) { if (input < m_pBounds[i + 1]) break; } input = PDF_Interpolate(input, m_pBounds[i], m_pBounds[i + 1], m_pEncode[i * 2], m_pEncode[i * 2 + 1]); int nresults; m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, outputs, nresults); return TRUE; } // static std::unique_ptr CPDF_Function::Load(CPDF_Object* pFuncObj) { std::unique_ptr pFunc; if (!pFuncObj) return pFunc; int iType = -1; if (CPDF_Stream* pStream = pFuncObj->AsStream()) iType = pStream->GetDict()->GetIntegerBy("FunctionType"); else if (CPDF_Dictionary* pDict = pFuncObj->AsDictionary()) iType = pDict->GetIntegerBy("FunctionType"); Type type = IntegerToFunctionType(iType); if (type == Type::kType0Sampled) pFunc.reset(new CPDF_SampledFunc()); else if (type == Type::kType2ExpotentialInterpolation) pFunc.reset(new CPDF_ExpIntFunc()); else if (type == Type::kType3Stitching) pFunc.reset(new CPDF_StitchFunc()); else if (type == Type::kType4PostScript) pFunc.reset(new CPDF_PSFunc()); if (!pFunc || !pFunc->Init(pFuncObj)) return std::unique_ptr(); return pFunc; } // static CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) { switch (iType) { case 0: case 2: case 3: case 4: return static_cast(iType); default: return Type::kTypeInvalid; } } CPDF_Function::CPDF_Function(Type type) : m_pDomains(nullptr), m_pRanges(nullptr), m_Type(type) {} CPDF_Function::~CPDF_Function() { FX_Free(m_pDomains); FX_Free(m_pRanges); } FX_BOOL CPDF_Function::Init(CPDF_Object* pObj) { CPDF_Stream* pStream = pObj->AsStream(); CPDF_Dictionary* pDict = pStream ? pStream->GetDict() : pObj->AsDictionary(); CPDF_Array* pDomains = pDict->GetArrayBy("Domain"); if (!pDomains) return FALSE; m_nInputs = pDomains->GetCount() / 2; if (m_nInputs == 0) return FALSE; m_pDomains = FX_Alloc2D(FX_FLOAT, m_nInputs, 2); for (uint32_t i = 0; i < m_nInputs * 2; i++) { m_pDomains[i] = pDomains->GetFloatAt(i); } CPDF_Array* pRanges = pDict->GetArrayBy("Range"); m_nOutputs = 0; if (pRanges) { m_nOutputs = pRanges->GetCount() / 2; m_pRanges = FX_Alloc2D(FX_FLOAT, m_nOutputs, 2); for (uint32_t i = 0; i < m_nOutputs * 2; i++) m_pRanges[i] = pRanges->GetFloatAt(i); } uint32_t old_outputs = m_nOutputs; if (!v_Init(pObj)) return FALSE; if (m_pRanges && m_nOutputs > old_outputs) { m_pRanges = FX_Realloc(FX_FLOAT, m_pRanges, m_nOutputs * 2); if (m_pRanges) { FXSYS_memset(m_pRanges + (old_outputs * 2), 0, sizeof(FX_FLOAT) * (m_nOutputs - old_outputs) * 2); } } return TRUE; } FX_BOOL CPDF_Function::Call(FX_FLOAT* inputs, uint32_t ninputs, FX_FLOAT* results, int& nresults) const { if (m_nInputs != ninputs) { return FALSE; } nresults = m_nOutputs; for (uint32_t i = 0; i < m_nInputs; i++) { if (inputs[i] < m_pDomains[i * 2]) inputs[i] = m_pDomains[i * 2]; else if (inputs[i] > m_pDomains[i * 2 + 1]) inputs[i] = m_pDomains[i * 2] + 1; } v_Call(inputs, results); if (m_pRanges) { for (uint32_t i = 0; i < m_nOutputs; i++) { if (results[i] < m_pRanges[i * 2]) results[i] = m_pRanges[i * 2]; else if (results[i] > m_pRanges[i * 2 + 1]) results[i] = m_pRanges[i * 2 + 1]; } } return TRUE; } const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const { return m_Type == Type::kType0Sampled ? static_cast(this) : nullptr; } const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const { return m_Type == Type::kType2ExpotentialInterpolation ? static_cast(this) : nullptr; } const CPDF_StitchFunc* CPDF_Function::ToStitchFunc() const { return m_Type == Type::kType3Stitching ? static_cast(this) : nullptr; }