Merge to XFA: Convert CPDF_IccProfileMap to use std::map.
[pdfium.git] / core / src / fpdfapi / fpdf_page / fpdf_page_doc.cpp
1 // Copyright 2014 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 "../../../include/fpdfapi/fpdf_page.h"
8 #include "../../../include/fpdfapi/fpdf_module.h"
9 #include "../../../include/fdrm/fx_crypt.h"
10 #include "../fpdf_font/font_int.h"
11 #include "pageint.h"
12
13 class CPDF_PageModule : public CPDF_PageModuleDef
14 {
15 public:
16     CPDF_PageModule() : m_StockGrayCS(PDFCS_DEVICEGRAY), m_StockRGBCS(PDFCS_DEVICERGB),
17         m_StockCMYKCS(PDFCS_DEVICECMYK) {}
18     virtual ~CPDF_PageModule() {}
19     virtual FX_BOOL Installed()
20     {
21         return TRUE;
22     }
23     virtual CPDF_DocPageData* CreateDocData(CPDF_Document* pDoc)
24     {
25         return new CPDF_DocPageData(pDoc);
26     }
27     virtual void ReleaseDoc(CPDF_Document* pDoc);
28     virtual void ClearDoc(CPDF_Document* pDoc);
29     virtual CPDF_FontGlobals* GetFontGlobals()
30     {
31         return &m_FontGlobals;
32     }
33     virtual void ClearStockFont(CPDF_Document* pDoc)
34     {
35         m_FontGlobals.Clear(pDoc);
36     }
37     virtual CPDF_ColorSpace* GetStockCS(int family);
38     virtual void NotifyCJKAvailable();
39     CPDF_FontGlobals m_FontGlobals;
40     CPDF_DeviceCS m_StockGrayCS;
41     CPDF_DeviceCS m_StockRGBCS;
42     CPDF_DeviceCS m_StockCMYKCS;
43     CPDF_PatternCS m_StockPatternCS;
44 };
45 CPDF_ColorSpace* CPDF_PageModule::GetStockCS(int family)
46 {
47     if (family == PDFCS_DEVICEGRAY) {
48         return &m_StockGrayCS;
49     }
50     if (family == PDFCS_DEVICERGB) {
51         return &m_StockRGBCS;
52     }
53     if (family == PDFCS_DEVICECMYK) {
54         return &m_StockCMYKCS;
55     }
56     if (family == PDFCS_PATTERN) {
57         return &m_StockPatternCS;
58     }
59     return NULL;
60 }
61 void CPDF_ModuleMgr::InitPageModule()
62 {
63     delete m_pPageModule;
64     m_pPageModule = new CPDF_PageModule;
65 }
66 void CPDF_PageModule::ReleaseDoc(CPDF_Document* pDoc)
67 {
68     delete pDoc->GetPageData();
69 }
70 void CPDF_PageModule::ClearDoc(CPDF_Document* pDoc)
71 {
72     pDoc->GetPageData()->Clear(FALSE);
73 }
74 void CPDF_PageModule::NotifyCJKAvailable()
75 {
76     m_FontGlobals.m_CMapManager.ReloadAll();
77 }
78 CPDF_Font* CPDF_Document::LoadFont(CPDF_Dictionary* pFontDict)
79 {
80     if (!pFontDict) {
81         return NULL;
82     }
83     return GetValidatePageData()->GetFont(pFontDict, FALSE);
84 }
85 CPDF_Font* CPDF_Document::FindFont(CPDF_Dictionary* pFontDict)
86 {
87     if (!pFontDict) {
88         return NULL;
89     }
90     return GetValidatePageData()->GetFont(pFontDict, TRUE);
91 }
92 CPDF_StreamAcc* CPDF_Document::LoadFontFile(CPDF_Stream* pStream)
93 {
94     if (pStream == NULL) {
95         return NULL;
96     }
97     return GetValidatePageData()->GetFontFileStreamAcc(pStream);
98 }
99 CPDF_ColorSpace* _CSFromName(const CFX_ByteString& name);
100 CPDF_ColorSpace* CPDF_Document::LoadColorSpace(CPDF_Object* pCSObj, CPDF_Dictionary* pResources)
101 {
102     return GetValidatePageData()->GetColorSpace(pCSObj, pResources);
103 }
104 CPDF_Pattern* CPDF_Document::LoadPattern(CPDF_Object* pPatternObj, FX_BOOL bShading, const CFX_AffineMatrix* matrix)
105 {
106     return GetValidatePageData()->GetPattern(pPatternObj, bShading, matrix);
107 }
108 CPDF_IccProfile* CPDF_Document::LoadIccProfile(CPDF_Stream* pStream)
109 {
110     return GetValidatePageData()->GetIccProfile(pStream);
111 }
112 CPDF_Image* CPDF_Document::LoadImageF(CPDF_Object* pObj)
113 {
114     if (!pObj) {
115         return NULL;
116     }
117     FXSYS_assert(pObj->GetObjNum());
118     return GetValidatePageData()->GetImage(pObj);
119 }
120 void CPDF_Document::RemoveColorSpaceFromPageData(CPDF_Object* pCSObj)
121 {
122     if (!pCSObj) {
123         return;
124     }
125     GetPageData()->ReleaseColorSpace(pCSObj);
126 }
127 CPDF_DocPageData::CPDF_DocPageData(CPDF_Document *pPDFDoc)
128     : m_pPDFDoc(pPDFDoc),
129       m_FontFileMap(),
130       m_bForceClear(FALSE)
131 {
132     m_FontFileMap.InitHashTable(32);
133 }
134
135 CPDF_DocPageData::~CPDF_DocPageData()
136 {
137     Clear(FALSE);
138     Clear(TRUE);
139
140     for (auto& it : m_PatternMap)
141         delete it.second;
142     m_PatternMap.clear();
143
144     for (auto& it : m_FontMap)
145         delete it.second;
146     m_FontMap.clear();
147
148     for (auto& it : m_ColorSpaceMap)
149         delete it.second;
150     m_ColorSpaceMap.clear();
151 }
152
153 void CPDF_DocPageData::Clear(FX_BOOL bForceRelease)
154 {
155     m_bForceClear = bForceRelease;
156
157     for (auto& it : m_PatternMap) {
158         CPDF_CountedPattern* ptData = it.second;
159         if (!ptData->m_Obj)
160             continue;
161
162         if (bForceRelease || ptData->m_nCount < 2) {
163             ptData->m_Obj->SetForceClear(bForceRelease);
164             delete ptData->m_Obj;
165             ptData->m_Obj = nullptr;
166         }
167     }
168
169     for (auto& it : m_FontMap) {
170         CPDF_CountedFont* fontData = it.second;
171         if (!fontData->m_Obj)
172             continue;
173
174         if (bForceRelease || fontData->m_nCount < 2) {
175             delete fontData->m_Obj;
176             fontData->m_Obj = nullptr;
177         }
178     }
179
180     for (auto& it : m_ColorSpaceMap) {
181         CPDF_CountedColorSpace* csData = it.second;
182         if (!csData->m_Obj)
183             continue;
184
185         if (bForceRelease || csData->m_nCount < 2) {
186             csData->m_Obj->ReleaseCS();
187             csData->m_Obj = nullptr;
188         }
189     }
190
191     for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end();) {
192         auto curr_it = it++;
193         CPDF_CountedIccProfile* ipData = curr_it->second;
194         if (!ipData->m_Obj)
195             continue;
196
197         if (bForceRelease || ipData->m_nCount < 2) {
198             CPDF_Stream* ipKey = curr_it->first;
199             FX_POSITION pos2 = m_HashProfileMap.GetStartPosition();
200             while (pos2) {
201                 CFX_ByteString bsKey;
202                 CPDF_Stream* pFindStream = nullptr;
203                 m_HashProfileMap.GetNextAssoc(pos2, bsKey, (void*&)pFindStream);
204                 if (ipKey == pFindStream) {
205                     m_HashProfileMap.RemoveKey(bsKey);
206                     break;
207                 }
208             }
209             delete ipData->m_Obj;
210             delete ipData;
211             m_IccProfileMap.erase(curr_it);
212         }
213     }
214
215     FX_POSITION pos = m_FontFileMap.GetStartPosition();
216     while (pos) {
217         CPDF_Stream* ftKey;
218         CPDF_CountedObject<CPDF_StreamAcc*>* ftData;
219         m_FontFileMap.GetNextAssoc(pos, ftKey, ftData);
220         if (!ftData->m_Obj) {
221             continue;
222         }
223         if (bForceRelease || ftData->m_nCount < 2) {
224             delete ftData->m_Obj;
225             delete ftData;
226             m_FontFileMap.RemoveKey(ftKey);
227         }
228     }
229
230     for (auto it = m_ImageMap.begin(); it != m_ImageMap.end();) {
231         auto curr_it = it++;
232         CPDF_CountedImage* imageData = curr_it->second;
233         if (!imageData->m_Obj)
234             continue;
235
236         if (bForceRelease || imageData->m_nCount < 2) {
237             delete imageData->m_Obj;
238             delete imageData;
239             m_ImageMap.erase(curr_it);
240         }
241     }
242 }
243
244 CPDF_Font* CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict, FX_BOOL findOnly)
245 {
246     if (!pFontDict) {
247         return NULL;
248     }
249     if (findOnly) {
250         auto it = m_FontMap.find(pFontDict);
251         if (it != m_FontMap.end()) {
252             CPDF_CountedFont* fontData = it->second;
253             if (!fontData->m_Obj)
254                 return nullptr;
255
256             fontData->m_nCount++;
257             return fontData->m_Obj;
258         }
259         return nullptr;
260     }
261
262     CPDF_CountedFont* fontData = nullptr;
263     auto it = m_FontMap.find(pFontDict);
264     if (it != m_FontMap.end()) {
265         fontData = it->second;
266         if (fontData->m_Obj) {
267             fontData->m_nCount++;
268             return fontData->m_Obj;
269         }
270     }
271
272     FX_BOOL bNew = FALSE;
273     if (!fontData) {
274         fontData = new CPDF_CountedFont;
275         bNew = TRUE;
276     }
277     CPDF_Font* pFont = CPDF_Font::CreateFontF(m_pPDFDoc, pFontDict);
278     if (!pFont) {
279         if (bNew)
280             delete fontData;
281         return nullptr;
282     }
283     fontData->m_nCount = 2;
284     fontData->m_Obj = pFont;
285     if (bNew)
286         m_FontMap[pFontDict] = fontData;
287     return pFont;
288 }
289
290 CPDF_Font* CPDF_DocPageData::GetStandardFont(const CFX_ByteStringC& fontName, CPDF_FontEncoding* pEncoding)
291 {
292     if (fontName.IsEmpty())
293         return nullptr;
294
295     for (auto& it : m_FontMap) {
296         CPDF_CountedFont* fontData = it.second;
297         CPDF_Font* pFont = fontData->m_Obj;
298         if (!pFont)
299             continue;
300         if (pFont->GetBaseFont() != fontName)
301             continue;
302         if (pFont->IsEmbedded())
303             continue;
304         if (pFont->GetFontType() != PDFFONT_TYPE1)
305             continue;
306         if (pFont->GetFontDict()->KeyExist(FX_BSTRC("Widths")))
307             continue;
308
309         CPDF_Type1Font* pT1Font = pFont->GetType1Font();
310         if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding))
311             continue;
312
313         fontData->m_nCount++;
314         return pFont;
315     }
316
317     CPDF_Dictionary* pDict = new CPDF_Dictionary;
318     pDict->SetAtName(FX_BSTRC("Type"), FX_BSTRC("Font"));
319     pDict->SetAtName(FX_BSTRC("Subtype"), FX_BSTRC("Type1"));
320     pDict->SetAtName(FX_BSTRC("BaseFont"), fontName);
321     if (pEncoding) {
322         pDict->SetAt(FX_BSTRC("Encoding"), pEncoding->Realize());
323     }
324     m_pPDFDoc->AddIndirectObject(pDict);
325     CPDF_CountedFont* fontData = new CPDF_CountedFont;
326     CPDF_Font* pFont = CPDF_Font::CreateFontF(m_pPDFDoc, pDict);
327     if (!pFont) {
328         delete fontData;
329         return nullptr;
330     }
331     fontData->m_nCount = 2;
332     fontData->m_Obj = pFont;
333     m_FontMap[pDict] = fontData;
334     return pFont;
335 }
336
337 void CPDF_DocPageData::ReleaseFont(CPDF_Dictionary* pFontDict)
338 {
339     if (!pFontDict)
340         return;
341
342     auto it = m_FontMap.find(pFontDict);
343     if (it == m_FontMap.end())
344         return;
345
346     CPDF_CountedFont* fontData = it->second;
347     if (fontData->m_Obj && --fontData->m_nCount == 0) {
348         delete fontData->m_Obj;
349         fontData->m_Obj = nullptr;
350     }
351 }
352
353 CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(CPDF_Object* pCSObj, CPDF_Dictionary* pResources)
354 {
355     if (!pCSObj) {
356         return NULL;
357     }
358     if (pCSObj->GetType() == PDFOBJ_NAME) {
359         CFX_ByteString name = pCSObj->GetConstString();
360         CPDF_ColorSpace* pCS = _CSFromName(name);
361         if (!pCS && pResources) {
362             CPDF_Dictionary* pList = pResources->GetDict(FX_BSTRC("ColorSpace"));
363             if (pList) {
364                 pCSObj = pList->GetElementValue(name);
365                 return GetColorSpace(pCSObj, NULL);
366             }
367         }
368         if (pCS == NULL || pResources == NULL) {
369             return pCS;
370         }
371         CPDF_Dictionary* pColorSpaces = pResources->GetDict(FX_BSTRC("ColorSpace"));
372         if (pColorSpaces == NULL) {
373             return pCS;
374         }
375         CPDF_Object* pDefaultCS = NULL;
376         switch (pCS->GetFamily()) {
377             case PDFCS_DEVICERGB:
378                 pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultRGB"));
379                 break;
380             case PDFCS_DEVICEGRAY:
381                 pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultGray"));
382                 break;
383             case PDFCS_DEVICECMYK:
384                 pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultCMYK"));
385                 break;
386         }
387         if (pDefaultCS == NULL) {
388             return pCS;
389         }
390         return GetColorSpace(pDefaultCS, NULL);
391     }
392
393     if (pCSObj->GetType() != PDFOBJ_ARRAY)
394         return nullptr;
395     CPDF_Array* pArray = (CPDF_Array*)pCSObj;
396     if (pArray->GetCount() == 0)
397         return nullptr;
398     if (pArray->GetCount() == 1)
399         return GetColorSpace(pArray->GetElementValue(0), pResources);
400
401     CPDF_CountedColorSpace* csData = nullptr;
402     auto it = m_ColorSpaceMap.find(pCSObj);
403     if (it != m_ColorSpaceMap.end()) {
404         csData = it->second;
405         if (csData->m_Obj) {
406             csData->m_nCount++;
407             return csData->m_Obj;
408         }
409     }
410
411     CPDF_ColorSpace* pCS = CPDF_ColorSpace::Load(m_pPDFDoc, pArray);
412     if (!pCS)
413         return nullptr;
414
415     if (!csData) {
416         csData = new CPDF_CountedColorSpace;
417         m_ColorSpaceMap[pCSObj] = csData;
418     }
419     csData->m_nCount = 2;
420     csData->m_Obj = pCS;
421     return pCS;
422 }
423
424 CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(CPDF_Object* pCSObj)
425 {
426     if (!pCSObj)
427         return nullptr;
428
429     auto it = m_ColorSpaceMap.find(pCSObj);
430     if (it == m_ColorSpaceMap.end())
431         return nullptr;
432
433     CPDF_CountedColorSpace* csData = it->second;
434     if (!csData->m_Obj)
435         return nullptr;
436
437     csData->m_nCount++;
438     return csData->m_Obj;
439 }
440
441 void CPDF_DocPageData::ReleaseColorSpace(CPDF_Object* pColorSpace)
442 {
443     if (!pColorSpace)
444         return;
445
446     auto it = m_ColorSpaceMap.find(pColorSpace);
447     if (it == m_ColorSpaceMap.end())
448         return;
449
450     CPDF_CountedColorSpace* csData = it->second;
451     if (csData->m_Obj && --csData->m_nCount == 0) {
452         csData->m_Obj->ReleaseCS();
453         csData->m_Obj = nullptr;
454     }
455 }
456
457 CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj, FX_BOOL bShading, const CFX_AffineMatrix* matrix)
458 {
459     if (!pPatternObj)
460         return nullptr;
461
462     CPDF_CountedPattern* ptData = nullptr;
463     auto it = m_PatternMap.find(pPatternObj);
464     if (it != m_PatternMap.end()) {
465         ptData = it->second;
466         if (ptData->m_Obj) {
467             ptData->m_nCount++;
468             return ptData->m_Obj;
469         }
470     }
471     CPDF_Pattern* pPattern = nullptr;
472     if (bShading) {
473         pPattern = new CPDF_ShadingPattern(m_pPDFDoc, pPatternObj, bShading, matrix);
474     } else {
475         CPDF_Dictionary* pDict = pPatternObj ? pPatternObj->GetDict() : nullptr;
476         if (pDict) {
477             int type = pDict->GetInteger(FX_BSTRC("PatternType"));
478             if (type == 1) {
479                 pPattern = new CPDF_TilingPattern(m_pPDFDoc, pPatternObj, matrix);
480             } else if (type == 2) {
481                 pPattern = new CPDF_ShadingPattern(m_pPDFDoc, pPatternObj, FALSE, matrix);
482             }
483         }
484     }
485     if (!pPattern)
486         return nullptr;
487
488     if (!ptData) {
489         ptData = new CPDF_CountedPattern;
490         m_PatternMap[pPatternObj] = ptData;
491     }
492     ptData->m_nCount = 2;
493     ptData->m_Obj = pPattern;
494     return pPattern;
495 }
496
497 void CPDF_DocPageData::ReleasePattern(CPDF_Object* pPatternObj)
498 {
499     if (!pPatternObj)
500         return;
501
502     auto it = m_PatternMap.find(pPatternObj);
503     if (it == m_PatternMap.end())
504         return;
505
506     CPDF_CountedPattern* ptData = it->second;
507     if (ptData->m_Obj && --ptData->m_nCount == 0) {
508         delete ptData->m_Obj;
509         ptData->m_Obj = nullptr;
510     }
511 }
512
513 CPDF_Image* CPDF_DocPageData::GetImage(CPDF_Object* pImageStream)
514 {
515     if (!pImageStream)
516         return nullptr;
517
518     const FX_DWORD dwImageObjNum = pImageStream->GetObjNum();
519     auto it = m_ImageMap.find(dwImageObjNum);
520     if (it != m_ImageMap.end()) {
521         CPDF_CountedImage* imageData = it->second;
522         imageData->m_nCount++;
523         return imageData->m_Obj;
524     }
525     CPDF_CountedImage* imageData = new CPDF_CountedImage;
526     CPDF_Image* pImage = new CPDF_Image(m_pPDFDoc);
527     pImage->LoadImageF((CPDF_Stream*)pImageStream, FALSE);
528     imageData->m_nCount = 2;
529     imageData->m_Obj = pImage;
530     m_ImageMap[dwImageObjNum] = imageData;
531     return pImage;
532 }
533
534 void CPDF_DocPageData::ReleaseImage(CPDF_Object* pImageStream)
535 {
536     if (!pImageStream || !pImageStream->GetObjNum())
537         return;
538
539     auto it = m_ImageMap.find(pImageStream->GetObjNum());
540     if (it == m_ImageMap.end())
541         return;
542
543     CPDF_CountedImage* image = it->second;
544     if (!image)
545         return;
546
547     if ((--image->m_nCount) == 0) {
548         delete image->m_Obj;
549         delete image;
550         m_ImageMap.erase(it);
551     }
552 }
553
554 CPDF_IccProfile* CPDF_DocPageData::GetIccProfile(CPDF_Stream* pIccProfileStream)
555 {
556     if (!pIccProfileStream)
557         return NULL;
558
559     auto it = m_IccProfileMap.find(pIccProfileStream);
560     if (it != m_IccProfileMap.end()) {
561         CPDF_CountedIccProfile* ipData = it->second;
562         ipData->m_nCount++;
563         return ipData->m_Obj;
564     }
565
566     CPDF_StreamAcc stream;
567     stream.LoadAllData(pIccProfileStream, FALSE);
568     uint8_t digest[20];
569     CPDF_Stream* pCopiedStream = nullptr;
570     CRYPT_SHA1Generate(stream.GetData(), stream.GetSize(), digest);
571     if (m_HashProfileMap.Lookup(CFX_ByteStringC(digest, 20), (void*&)pCopiedStream)) {
572         auto it_copied_stream = m_IccProfileMap.find(pCopiedStream);
573         CPDF_CountedIccProfile* ipData = it_copied_stream->second;
574         ipData->m_nCount++;
575         return ipData->m_Obj;
576     }
577     CPDF_IccProfile* pProfile = new CPDF_IccProfile(stream.GetData(), stream.GetSize());
578     CPDF_CountedIccProfile* ipData = new CPDF_CountedIccProfile;
579     ipData->m_nCount = 2;
580     ipData->m_Obj = pProfile;
581     m_IccProfileMap[pIccProfileStream] = ipData;
582     m_HashProfileMap.SetAt(CFX_ByteStringC(digest, 20), pIccProfileStream);
583     return pProfile;
584 }
585
586 void CPDF_DocPageData::ReleaseIccProfile(CPDF_IccProfile* pIccProfile)
587 {
588     ASSERT(pIccProfile);
589
590     for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end(); ++it) {
591         CPDF_CountedIccProfile* profile = it->second;
592         if (profile->m_Obj != pIccProfile)
593             continue;
594
595         if ((--profile->m_nCount) == 0) {
596             delete profile->m_Obj;
597             delete profile;
598             m_IccProfileMap.erase(it);
599             return;
600         }
601     }
602 }
603
604 CPDF_StreamAcc* CPDF_DocPageData::GetFontFileStreamAcc(CPDF_Stream* pFontStream)
605 {
606     if (!pFontStream) {
607         return NULL;
608     }
609     CPDF_CountedObject<CPDF_StreamAcc*>* ftData;
610     if (m_FontFileMap.Lookup(pFontStream, ftData)) {
611         ftData->m_nCount ++;
612         return ftData->m_Obj;
613     }
614     ftData = new CPDF_CountedObject<CPDF_StreamAcc*>;
615     CPDF_StreamAcc* pFontFile = new CPDF_StreamAcc;
616     CPDF_Dictionary* pFontDict = pFontStream->GetDict();
617     int32_t org_size = pFontDict->GetInteger(FX_BSTRC("Length1")) + pFontDict->GetInteger(FX_BSTRC("Length2")) + pFontDict->GetInteger(FX_BSTRC("Length3"));
618     if (org_size < 0) {
619         org_size = 0;
620     }
621     pFontFile->LoadAllData(pFontStream, FALSE, org_size);
622     ftData->m_nCount = 2;
623     ftData->m_Obj = pFontFile;
624     m_FontFileMap.SetAt(pFontStream, ftData);
625     return pFontFile;
626 }
627 void CPDF_DocPageData::ReleaseFontFileStreamAcc(CPDF_Stream* pFontStream, FX_BOOL bForce)
628 {
629     if (!pFontStream)
630         return;
631
632     CPDF_CountedObject<CPDF_StreamAcc*>* findData = nullptr;
633     if (!m_FontFileMap.Lookup(pFontStream, findData))
634         return;
635     if (!findData)
636         return;
637
638     if ((--findData->m_nCount) == 0 || bForce) {
639         delete findData->m_Obj;
640         delete findData;
641         m_FontFileMap.RemoveKey(pFontStream);
642     }
643 }
644
645 CPDF_CountedColorSpace* CPDF_DocPageData::FindColorSpacePtr(CPDF_Object* pCSObj) const
646 {
647     if (!pCSObj)
648         return nullptr;
649
650     auto it = m_ColorSpaceMap.find(pCSObj);
651     return it != m_ColorSpaceMap.end() ? it->second : nullptr;
652 }
653
654 CPDF_CountedPattern* CPDF_DocPageData::FindPatternPtr(CPDF_Object* pPatternObj) const
655 {
656     if (!pPatternObj)
657         return nullptr;
658
659     auto it = m_PatternMap.find(pPatternObj);
660     return it != m_PatternMap.end() ? it->second : nullptr;
661 }