1 // Copyright 2016 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_shadingpattern.h" 8 9 #include <algorithm> 10 11 #include "core/fpdfapi/page/cpdf_docpagedata.h" 12 #include "core/fpdfapi/page/cpdf_function.h" 13 #include "core/fpdfapi/parser/cpdf_array.h" 14 #include "core/fpdfapi/parser/cpdf_dictionary.h" 15 #include "core/fpdfapi/parser/cpdf_document.h" 16 #include "core/fpdfapi/parser/cpdf_object.h" 17 #include "core/fpdfapi/parser/cpdf_stream.h" 18 19 namespace { 20 21 ShadingType ToShadingType(int type) { 22 return (type > static_cast<int>(kInvalidShading) && 23 type < static_cast<int>(kMaxShading)) 24 ? static_cast<ShadingType>(type) 25 : kInvalidShading; 26 } 27 28 } // namespace 29 30 CPDF_ShadingPattern::CPDF_ShadingPattern(CPDF_Document* pDoc, 31 CPDF_Object* pPatternObj, 32 bool bShading, 33 const CFX_Matrix& parentMatrix) 34 : CPDF_Pattern(pDoc, bShading ? nullptr : pPatternObj, parentMatrix), 35 m_ShadingType(kInvalidShading), 36 m_bShadingObj(bShading), 37 m_pShadingObj(pPatternObj), 38 m_pCS(nullptr), 39 m_pCountedCS(nullptr) { 40 assert(document()); 41 if (!bShading) { 42 m_pShadingObj = pattern_obj()->GetDict()->GetDirectObjectFor("Shading"); 43 SetPatternToFormMatrix(); 44 } 45 } 46 47 CPDF_ShadingPattern::~CPDF_ShadingPattern() { 48 CPDF_ColorSpace* pCountedCS = m_pCountedCS ? m_pCountedCS->get() : nullptr; 49 if (pCountedCS) { 50 auto* pPageData = document()->GetPageData(); 51 if (pPageData) { 52 m_pCS.Release(); // Give up unowned reference first. 53 pPageData->ReleaseColorSpace(pCountedCS->GetArray()); 54 } 55 } 56 } 57 58 CPDF_TilingPattern* CPDF_ShadingPattern::AsTilingPattern() { 59 return nullptr; 60 } 61 62 CPDF_ShadingPattern* CPDF_ShadingPattern::AsShadingPattern() { 63 return this; 64 } 65 66 bool CPDF_ShadingPattern::Load() { 67 if (m_ShadingType != kInvalidShading) 68 return true; 69 70 CPDF_Dictionary* pShadingDict = 71 m_pShadingObj ? m_pShadingObj->GetDict() : nullptr; 72 if (!pShadingDict) 73 return false; 74 75 m_pFunctions.clear(); 76 CPDF_Object* pFunc = pShadingDict->GetDirectObjectFor("Function"); 77 if (pFunc) { 78 if (CPDF_Array* pArray = pFunc->AsArray()) { 79 m_pFunctions.resize(std::min<size_t>(pArray->GetCount(), 4)); 80 for (size_t i = 0; i < m_pFunctions.size(); ++i) 81 m_pFunctions[i] = CPDF_Function::Load(pArray->GetDirectObjectAt(i)); 82 } else { 83 m_pFunctions.push_back(CPDF_Function::Load(pFunc)); 84 } 85 } 86 CPDF_Object* pCSObj = pShadingDict->GetDirectObjectFor("ColorSpace"); 87 if (!pCSObj) 88 return false; 89 90 CPDF_DocPageData* pDocPageData = document()->GetPageData(); 91 m_pCS = pDocPageData->GetColorSpace(pCSObj, nullptr); 92 93 // The color space is required and cannot be a Pattern space, according to the 94 // PDF 1.7 spec, page 305. 95 if (!m_pCS || m_pCS->GetFamily() == PDFCS_PATTERN) 96 return false; 97 98 m_pCountedCS = pDocPageData->FindColorSpacePtr(m_pCS->GetArray()); 99 100 m_ShadingType = ToShadingType(pShadingDict->GetIntegerFor("ShadingType")); 101 102 return Validate(); 103 } 104 105 bool CPDF_ShadingPattern::Validate() const { 106 if (m_ShadingType == kInvalidShading) 107 return false; 108 109 // We expect to have a stream if our shading type is a mesh. 110 if (IsMeshShading() && !ToStream(m_pShadingObj.Get())) 111 return false; 112 113 // Validate color space 114 switch (m_ShadingType) { 115 case kFunctionBasedShading: 116 case kAxialShading: 117 case kRadialShading: { 118 if (m_pCS->GetFamily() == PDFCS_INDEXED) 119 return false; 120 break; 121 } 122 case kFreeFormGouraudTriangleMeshShading: 123 case kLatticeFormGouraudTriangleMeshShading: 124 case kCoonsPatchMeshShading: 125 case kTensorProductPatchMeshShading: { 126 if (!m_pFunctions.empty() && m_pCS->GetFamily() == PDFCS_INDEXED) 127 return false; 128 break; 129 } 130 default: { 131 NOTREACHED(); 132 return false; 133 } 134 } 135 136 uint32_t nNumColorSpaceComponents = m_pCS->CountComponents(); 137 switch (m_ShadingType) { 138 case kFunctionBasedShading: { 139 // Either one 2-to-N function or N 2-to-1 functions. 140 if (!ValidateFunctions(1, 2, nNumColorSpaceComponents) && 141 !ValidateFunctions(nNumColorSpaceComponents, 2, 1)) { 142 return false; 143 } 144 break; 145 } 146 case kAxialShading: 147 case kRadialShading: { 148 // Either one 1-to-N function or N 1-to-1 functions. 149 if (!ValidateFunctions(1, 1, nNumColorSpaceComponents) && 150 !ValidateFunctions(nNumColorSpaceComponents, 1, 1)) { 151 return false; 152 } 153 break; 154 } 155 case kFreeFormGouraudTriangleMeshShading: 156 case kLatticeFormGouraudTriangleMeshShading: 157 case kCoonsPatchMeshShading: 158 case kTensorProductPatchMeshShading: { 159 // Either no function, one 1-to-N function, or N 1-to-1 functions. 160 if (!m_pFunctions.empty() && 161 !ValidateFunctions(1, 1, nNumColorSpaceComponents) && 162 !ValidateFunctions(nNumColorSpaceComponents, 1, 1)) { 163 return false; 164 } 165 break; 166 } 167 default: { 168 NOTREACHED(); 169 return false; 170 } 171 } 172 return true; 173 } 174 175 bool CPDF_ShadingPattern::ValidateFunctions( 176 uint32_t nExpectedNumFunctions, 177 uint32_t nExpectedNumInputs, 178 uint32_t nExpectedNumOutputs) const { 179 if (m_pFunctions.size() != nExpectedNumFunctions) 180 return false; 181 182 pdfium::base::CheckedNumeric<uint32_t> nTotalOutputs = 0; 183 for (const auto& function : m_pFunctions) { 184 if (!function) 185 return false; 186 187 if (function->CountInputs() != nExpectedNumInputs || 188 function->CountOutputs() != nExpectedNumOutputs) { 189 return false; 190 } 191 192 nTotalOutputs += function->CountOutputs(); 193 } 194 195 return nTotalOutputs.IsValid(); 196 } 197