1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/page/cpdf_function.h"
8
9 #include <vector>
10
11 #include "core/fpdfapi/page/cpdf_expintfunc.h"
12 #include "core/fpdfapi/page/cpdf_psfunc.h"
13 #include "core/fpdfapi/page/cpdf_sampledfunc.h"
14 #include "core/fpdfapi/page/cpdf_stitchfunc.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_stream.h"
18 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
19 #include "core/fxcrt/fx_safe_types.h"
20 #include "third_party/base/ptr_util.h"
21 #include "third_party/base/stl_util.h"
22
23 // static
Load(const CPDF_Object * pFuncObj)24 std::unique_ptr<CPDF_Function> CPDF_Function::Load(
25 const CPDF_Object* pFuncObj) {
26 std::set<const CPDF_Object*> visited;
27 return Load(pFuncObj, &visited);
28 }
29
30 // static
Load(const CPDF_Object * pFuncObj,std::set<const CPDF_Object * > * pVisited)31 std::unique_ptr<CPDF_Function> CPDF_Function::Load(
32 const CPDF_Object* pFuncObj,
33 std::set<const CPDF_Object*>* pVisited) {
34 if (!pFuncObj)
35 return nullptr;
36
37 if (pdfium::ContainsKey(*pVisited, pFuncObj))
38 return nullptr;
39 pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pFuncObj);
40
41 int iType = -1;
42 if (const CPDF_Stream* pStream = pFuncObj->AsStream())
43 iType = pStream->GetDict()->GetIntegerFor("FunctionType");
44 else if (const CPDF_Dictionary* pDict = pFuncObj->AsDictionary())
45 iType = pDict->GetIntegerFor("FunctionType");
46
47 std::unique_ptr<CPDF_Function> pFunc;
48 Type type = IntegerToFunctionType(iType);
49 if (type == Type::kType0Sampled)
50 pFunc = pdfium::MakeUnique<CPDF_SampledFunc>();
51 else if (type == Type::kType2ExpotentialInterpolation)
52 pFunc = pdfium::MakeUnique<CPDF_ExpIntFunc>();
53 else if (type == Type::kType3Stitching)
54 pFunc = pdfium::MakeUnique<CPDF_StitchFunc>();
55 else if (type == Type::kType4PostScript)
56 pFunc = pdfium::MakeUnique<CPDF_PSFunc>();
57
58 if (!pFunc || !pFunc->Init(pFuncObj, pVisited))
59 return nullptr;
60
61 return pFunc;
62 }
63
64 // static
IntegerToFunctionType(int iType)65 CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) {
66 switch (iType) {
67 case 0:
68 case 2:
69 case 3:
70 case 4:
71 return static_cast<Type>(iType);
72 default:
73 return Type::kTypeInvalid;
74 }
75 }
76
CPDF_Function(Type type)77 CPDF_Function::CPDF_Function(Type type) : m_Type(type) {}
78
79 CPDF_Function::~CPDF_Function() = default;
80
Init(const CPDF_Object * pObj,std::set<const CPDF_Object * > * pVisited)81 bool CPDF_Function::Init(const CPDF_Object* pObj,
82 std::set<const CPDF_Object*>* pVisited) {
83 const CPDF_Stream* pStream = pObj->AsStream();
84 const CPDF_Dictionary* pDict =
85 pStream ? pStream->GetDict() : pObj->AsDictionary();
86
87 const CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
88 if (!pDomains)
89 return false;
90
91 m_nInputs = pDomains->size() / 2;
92 if (m_nInputs == 0)
93 return false;
94
95 size_t nInputs = m_nInputs * 2;
96 m_Domains = ReadArrayElementsToVector(pDomains, nInputs);
97
98 const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
99 m_nOutputs = pRanges ? pRanges->size() / 2 : 0;
100
101 // Ranges are required for type 0 and type 4 functions. A non-zero
102 // |m_nOutputs| here implied Ranges meets the requirements.
103 bool bRangeRequired =
104 m_Type == Type::kType0Sampled || m_Type == Type::kType4PostScript;
105 if (bRangeRequired && m_nOutputs == 0)
106 return false;
107
108 if (m_nOutputs > 0) {
109 size_t nOutputs = m_nOutputs * 2;
110 m_Ranges = ReadArrayElementsToVector(pRanges, nOutputs);
111 }
112
113 uint32_t old_outputs = m_nOutputs;
114 if (!v_Init(pObj, pVisited))
115 return false;
116
117 if (!m_Ranges.empty() && m_nOutputs > old_outputs) {
118 FX_SAFE_SIZE_T nOutputs = m_nOutputs;
119 nOutputs *= 2;
120 m_Ranges.resize(nOutputs.ValueOrDie());
121 }
122 return true;
123 }
124
Call(const float * inputs,uint32_t ninputs,float * results,int * nresults) const125 bool CPDF_Function::Call(const float* inputs,
126 uint32_t ninputs,
127 float* results,
128 int* nresults) const {
129 if (m_nInputs != ninputs)
130 return false;
131
132 *nresults = m_nOutputs;
133 std::vector<float> clamped_inputs(m_nInputs);
134 for (uint32_t i = 0; i < m_nInputs; i++) {
135 clamped_inputs[i] =
136 pdfium::clamp(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1]);
137 }
138 if (!v_Call(clamped_inputs.data(), results))
139 return false;
140
141 if (m_Ranges.empty())
142 return true;
143
144 for (uint32_t i = 0; i < m_nOutputs; i++) {
145 results[i] =
146 pdfium::clamp(results[i], m_Ranges[i * 2], m_Ranges[i * 2 + 1]);
147 }
148 return true;
149 }
150
151 // See PDF Reference 1.7, page 170.
Interpolate(float x,float xmin,float xmax,float ymin,float ymax) const152 float CPDF_Function::Interpolate(float x,
153 float xmin,
154 float xmax,
155 float ymin,
156 float ymax) const {
157 float divisor = xmax - xmin;
158 return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0);
159 }
160
ToSampledFunc() const161 const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const {
162 return m_Type == Type::kType0Sampled
163 ? static_cast<const CPDF_SampledFunc*>(this)
164 : nullptr;
165 }
166
ToExpIntFunc() const167 const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const {
168 return m_Type == Type::kType2ExpotentialInterpolation
169 ? static_cast<const CPDF_ExpIntFunc*>(this)
170 : nullptr;
171 }
172
ToStitchFunc() const173 const CPDF_StitchFunc* CPDF_Function::ToStitchFunc() const {
174 return m_Type == Type::kType3Stitching
175 ? static_cast<const CPDF_StitchFunc*>(this)
176 : nullptr;
177 }
178