Initial commit.
[pdfium.git] / core / src / fpdfapi / fpdf_parser / fpdf_parser_document.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4  \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../include/fpdfapi/fpdf_parser.h"\r
8 #include "../../../include/fpdfapi/fpdf_module.h"\r
9 extern FX_LPVOID PDFPreviewInitCache(CPDF_Document* pDoc);\r
10 extern void PDFPreviewClearCache(FX_LPVOID pCache);\r
11 CPDF_Document::CPDF_Document(IPDF_DocParser* pParser) : CPDF_IndirectObjects(pParser)\r
12 {\r
13     ASSERT(pParser != NULL);\r
14     m_pRootDict = NULL;\r
15     m_pInfoDict = NULL;\r
16     m_bLinearized = FALSE;\r
17     m_dwFirstPageNo = 0;\r
18     m_dwFirstPageObjNum = 0;\r
19     m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this);\r
20     m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this);\r
21 }\r
22 CPDF_DocPageData* CPDF_Document::GetValidatePageData()\r
23 {\r
24     if (m_pDocPage) {\r
25         return m_pDocPage;\r
26     }\r
27     m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this);\r
28     return m_pDocPage;\r
29 }\r
30 CPDF_DocRenderData* CPDF_Document::GetValidateRenderData()\r
31 {\r
32     if (m_pDocRender) {\r
33         return m_pDocRender;\r
34     }\r
35     m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this);\r
36     return m_pDocRender;\r
37 }\r
38 void CPDF_Document::LoadDoc()\r
39 {\r
40     m_LastObjNum = m_pParser->GetLastObjNum();\r
41     CPDF_Object* pRootObj = GetIndirectObject(m_pParser->GetRootObjNum());\r
42     if (pRootObj == NULL) {\r
43         return;\r
44     }\r
45     m_pRootDict = pRootObj->GetDict();\r
46     if (m_pRootDict == NULL) {\r
47         return;\r
48     }\r
49     CPDF_Object* pInfoObj = GetIndirectObject(m_pParser->GetInfoObjNum());\r
50     if (pInfoObj) {\r
51         m_pInfoDict = pInfoObj->GetDict();\r
52     }\r
53     CPDF_Array* pIDArray = m_pParser->GetIDArray();\r
54     if (pIDArray) {\r
55         m_ID1 = pIDArray->GetString(0);\r
56         m_ID2 = pIDArray->GetString(1);\r
57     }\r
58     m_PageList.SetSize(_GetPageCount());\r
59 }\r
60 void CPDF_Document::LoadAsynDoc(CPDF_Dictionary *pLinearized)\r
61 {\r
62     m_bLinearized = TRUE;\r
63     m_LastObjNum = m_pParser->GetLastObjNum();\r
64     m_pRootDict = GetIndirectObject(m_pParser->GetRootObjNum())->GetDict();\r
65     if (m_pRootDict == NULL) {\r
66         return;\r
67     }\r
68     m_pInfoDict = GetIndirectObject(m_pParser->GetInfoObjNum())->GetDict();\r
69     CPDF_Array* pIDArray = m_pParser->GetIDArray();\r
70     if (pIDArray) {\r
71         m_ID1 = pIDArray->GetString(0);\r
72         m_ID2 = pIDArray->GetString(1);\r
73     }\r
74     FX_DWORD dwPageCount = 0;\r
75     CPDF_Object *pCount = pLinearized->GetElement(FX_BSTRC("N"));\r
76     if (pCount && pCount->GetType() == PDFOBJ_NUMBER) {\r
77         dwPageCount = pCount->GetInteger();\r
78     }\r
79     m_PageList.SetSize(dwPageCount);\r
80     CPDF_Object *pNo = pLinearized->GetElement(FX_BSTRC("P"));\r
81     if (pNo && pNo->GetType() == PDFOBJ_NUMBER) {\r
82         m_dwFirstPageNo = pNo->GetInteger();\r
83     }\r
84     CPDF_Object *pObjNum = pLinearized->GetElement(FX_BSTRC("O"));\r
85     if (pObjNum && pObjNum->GetType() == PDFOBJ_NUMBER) {\r
86         m_dwFirstPageObjNum = pObjNum->GetInteger();\r
87     }\r
88 }\r
89 void CPDF_Document::LoadPages()\r
90 {\r
91     m_PageList.SetSize(_GetPageCount());\r
92 }\r
93 extern void FPDF_TTFaceMapper_ReleaseDoc(CPDF_Document*);\r
94 CPDF_Document::~CPDF_Document()\r
95 {\r
96     if (m_pDocRender) {\r
97         CPDF_ModuleMgr::Get()->GetRenderModule()->DestroyDocData(m_pDocRender);\r
98     }\r
99     if (m_pDocPage) {\r
100         CPDF_ModuleMgr::Get()->GetPageModule()->ReleaseDoc(this);\r
101         CPDF_ModuleMgr::Get()->GetPageModule()->ClearStockFont(this);\r
102     }\r
103 }\r
104 #define         FX_MAX_PAGE_LEVEL                       1024\r
105 CPDF_Dictionary* CPDF_Document::_FindPDFPage(CPDF_Dictionary* pPages, int iPage, int nPagesToGo, int level)\r
106 {\r
107     CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));\r
108     if (pKidList == NULL) {\r
109         if (nPagesToGo == 0) {\r
110             return pPages;\r
111         }\r
112         return NULL;\r
113     }\r
114     if (level >= FX_MAX_PAGE_LEVEL) {\r
115         return NULL;\r
116     }\r
117     int nKids = pKidList->GetCount();\r
118     for (int i = 0; i < nKids; i ++) {\r
119         CPDF_Dictionary* pKid = pKidList->GetDict(i);\r
120         if (pKid == NULL) {\r
121             nPagesToGo --;\r
122             continue;\r
123         }\r
124         if (pKid == pPages) {\r
125             continue;\r
126         }\r
127         if (!pKid->KeyExist(FX_BSTRC("Kids"))) {\r
128             if (nPagesToGo == 0) {\r
129                 return pKid;\r
130             }\r
131             m_PageList.SetAt(iPage - nPagesToGo, pKid->GetObjNum());\r
132             nPagesToGo --;\r
133         } else {\r
134             int nPages = pKid->GetInteger(FX_BSTRC("Count"));\r
135             if (nPagesToGo < nPages) {\r
136                 return _FindPDFPage(pKid, iPage, nPagesToGo, level + 1);\r
137             }\r
138             nPagesToGo -= nPages;\r
139         }\r
140     }\r
141     return NULL;\r
142 }\r
143 CPDF_Dictionary* CPDF_Document::GetPage(int iPage)\r
144 {\r
145     if (iPage < 0 || iPage >= m_PageList.GetSize()) {\r
146         return NULL;\r
147     }\r
148     if (m_bLinearized && (iPage == (int)m_dwFirstPageNo)) {\r
149         CPDF_Object* pObj = GetIndirectObject(m_dwFirstPageObjNum);\r
150         if (pObj && pObj->GetType() == PDFOBJ_DICTIONARY) {\r
151             return (CPDF_Dictionary*)pObj;\r
152         }\r
153     }\r
154     int objnum = m_PageList.GetAt(iPage);\r
155     if (objnum) {\r
156         CPDF_Object* pObj = GetIndirectObject(objnum);\r
157         ASSERT(pObj->GetType() == PDFOBJ_DICTIONARY);\r
158         return (CPDF_Dictionary*)pObj;\r
159     }\r
160     CPDF_Dictionary* pRoot = GetRoot();\r
161     if (pRoot == NULL) {\r
162         return NULL;\r
163     }\r
164     CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));\r
165     if (pPages == NULL) {\r
166         return NULL;\r
167     }\r
168     CPDF_Dictionary* pPage = _FindPDFPage(pPages, iPage, iPage, 0);\r
169     if (pPage == NULL) {\r
170         return NULL;\r
171     }\r
172     m_PageList.SetAt(iPage, pPage->GetObjNum());\r
173     return pPage;\r
174 }\r
175 int CPDF_Document::_FindPageIndex(CPDF_Dictionary* pNode, FX_DWORD& skip_count, FX_DWORD objnum, int& index, int level)\r
176 {\r
177     if (pNode->KeyExist(FX_BSTRC("Kids"))) {\r
178         CPDF_Array* pKidList = pNode->GetArray(FX_BSTRC("Kids"));\r
179         if (pKidList == NULL) {\r
180             return -1;\r
181         }\r
182         if (level >= FX_MAX_PAGE_LEVEL) {\r
183             return -1;\r
184         }\r
185         FX_DWORD count = pNode->GetInteger(FX_BSTRC("Count"));\r
186         if (count <= skip_count) {\r
187             skip_count -= count;\r
188             index += count;\r
189             return -1;\r
190         }\r
191         if (count && count == pKidList->GetCount()) {\r
192             for (FX_DWORD i = 0; i < count; i ++) {\r
193                 CPDF_Reference* pKid = (CPDF_Reference*)pKidList->GetElement(i);\r
194                 if (pKid && pKid->GetType() == PDFOBJ_REFERENCE) {\r
195                     if (pKid->GetRefObjNum() == objnum) {\r
196                         m_PageList.SetAt(index + i, objnum);\r
197                         return index + i;\r
198                     }\r
199                 }\r
200             }\r
201         }\r
202         for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {\r
203             CPDF_Dictionary* pKid = pKidList->GetDict(i);\r
204             if (pKid == NULL) {\r
205                 continue;\r
206             }\r
207             if (pKid == pNode) {\r
208                 continue;\r
209             }\r
210             int found_index = _FindPageIndex(pKid, skip_count, objnum, index, level + 1);\r
211             if (found_index >= 0) {\r
212                 return found_index;\r
213             }\r
214         }\r
215     } else {\r
216         if (objnum == pNode->GetObjNum()) {\r
217             return index;\r
218         }\r
219         if (skip_count) {\r
220             skip_count--;\r
221         }\r
222         index ++;\r
223     }\r
224     return -1;\r
225 }\r
226 int CPDF_Document::GetPageIndex(FX_DWORD objnum)\r
227 {\r
228     FX_DWORD nPages = m_PageList.GetSize();\r
229     FX_DWORD skip_count = 0;\r
230     FX_BOOL bSkipped = FALSE;\r
231     for (FX_DWORD i = 0; i < nPages; i ++) {\r
232         FX_DWORD objnum1 = m_PageList.GetAt(i);\r
233         if (objnum1 == objnum) {\r
234             return i;\r
235         }\r
236         if (!bSkipped && objnum1 == 0) {\r
237             skip_count = i;\r
238             bSkipped = TRUE;\r
239         }\r
240     }\r
241     CPDF_Dictionary* pRoot = GetRoot();\r
242     if (pRoot == NULL) {\r
243         return -1;\r
244     }\r
245     CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));\r
246     if (pPages == NULL) {\r
247         return -1;\r
248     }\r
249     int index = 0;\r
250     return _FindPageIndex(pPages, skip_count, objnum, index);\r
251 }\r
252 int CPDF_Document::GetPageCount() const\r
253 {\r
254     return m_PageList.GetSize();\r
255 }\r
256 static int _CountPages(CPDF_Dictionary* pPages, int level)\r
257 {\r
258     if (level > 128) {\r
259         return 0;\r
260     }\r
261     int count = pPages->GetInteger(FX_BSTRC("Count"));\r
262     if (count > 0 && count < FPDF_PAGE_MAX_NUM) {\r
263         return count;\r
264     }\r
265     CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));\r
266     if (pKidList == NULL) {\r
267         return 0;\r
268     }\r
269     count = 0;\r
270     for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {\r
271         CPDF_Dictionary* pKid = pKidList->GetDict(i);\r
272         if (pKid == NULL) {\r
273             continue;\r
274         }\r
275         if (!pKid->KeyExist(FX_BSTRC("Kids"))) {\r
276             count ++;\r
277         } else {\r
278             count += _CountPages(pKid, level + 1);\r
279         }\r
280     }\r
281     pPages->SetAtInteger(FX_BSTRC("Count"), count);\r
282     return count;\r
283 }\r
284 int CPDF_Document::_GetPageCount() const\r
285 {\r
286     CPDF_Dictionary* pRoot = GetRoot();\r
287     if (pRoot == NULL) {\r
288         return 0;\r
289     }\r
290     CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));\r
291     if (pPages == NULL) {\r
292         return 0;\r
293     }\r
294     if (!pPages->KeyExist(FX_BSTRC("Kids"))) {\r
295         return 1;\r
296     }\r
297     return _CountPages(pPages, 0);\r
298 }\r
299 static FX_BOOL _EnumPages(CPDF_Dictionary* pPages, IPDF_EnumPageHandler* pHandler)\r
300 {\r
301     CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids"));\r
302     if (pKidList == NULL) {\r
303         return pHandler->EnumPage(pPages);\r
304     }\r
305     for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) {\r
306         CPDF_Dictionary* pKid = pKidList->GetDict(i);\r
307         if (pKid == NULL) {\r
308             continue;\r
309         }\r
310         if (!pKid->KeyExist(FX_BSTRC("Kids"))) {\r
311             if (!pHandler->EnumPage(pKid)) {\r
312                 return FALSE;\r
313             }\r
314         } else {\r
315             return _EnumPages(pKid, pHandler);\r
316         }\r
317     }\r
318     return TRUE;\r
319 }\r
320 void CPDF_Document::EnumPages(IPDF_EnumPageHandler* pHandler)\r
321 {\r
322     CPDF_Dictionary* pRoot = GetRoot();\r
323     if (pRoot == NULL) {\r
324         return;\r
325     }\r
326     CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages"));\r
327     if (pPages == NULL) {\r
328         return;\r
329     }\r
330     _EnumPages(pPages, pHandler);\r
331 }\r
332 FX_BOOL CPDF_Document::IsContentUsedElsewhere(FX_DWORD objnum, CPDF_Dictionary* pThisPageDict)\r
333 {\r
334     for (int i = 0; i < m_PageList.GetSize(); i ++) {\r
335         CPDF_Dictionary* pPageDict = GetPage(i);\r
336         if (pPageDict == pThisPageDict) {\r
337             continue;\r
338         }\r
339         CPDF_Object* pContents = pPageDict->GetElement(FX_BSTRC("Contents"));\r
340         if (pContents == NULL) {\r
341             continue;\r
342         }\r
343         if (pContents->GetDirectType() == PDFOBJ_ARRAY) {\r
344             CPDF_Array* pArray = (CPDF_Array*)pContents->GetDirect();\r
345             for (FX_DWORD j = 0; j < pArray->GetCount(); j ++) {\r
346                 CPDF_Reference* pRef = (CPDF_Reference*)pArray->GetElement(j);\r
347                 if (pRef->GetRefObjNum() == objnum) {\r
348                     return TRUE;\r
349                 }\r
350             }\r
351         } else if (pContents->GetObjNum() == objnum) {\r
352             return TRUE;\r
353         }\r
354     }\r
355     return FALSE;\r
356 }\r
357 FX_DWORD CPDF_Document::GetUserPermissions(FX_BOOL bCheckRevision) const\r
358 {\r
359     if (m_pParser == NULL) {\r
360         return (FX_DWORD) - 1;\r
361     }\r
362     return m_pParser->GetPermissions(bCheckRevision);\r
363 }\r
364 FX_BOOL CPDF_Document::IsOwner() const\r
365 {\r
366     if (m_pParser == NULL) {\r
367         return TRUE;\r
368     }\r
369     return m_pParser->IsOwner();\r
370 }\r
371 FX_BOOL CPDF_Document::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) const\r
372 {\r
373     {\r
374         CPDF_Object* pObj;\r
375         if (m_IndirectObjs.Lookup((FX_LPVOID)(FX_UINTPTR)objnum, (FX_LPVOID&)pObj)) {\r
376             bForm = pObj->GetType() == PDFOBJ_STREAM &&\r
377                     ((CPDF_Stream*)pObj)->GetDict()->GetString(FX_BSTRC("Subtype")) == FX_BSTRC("Form");\r
378             return TRUE;\r
379         }\r
380     }\r
381     if (m_pParser == NULL) {\r
382         bForm = FALSE;\r
383         return TRUE;\r
384     }\r
385     return m_pParser->IsFormStream(objnum, bForm);\r
386 }\r
387 void CPDF_Document::ClearPageData()\r
388 {\r
389     if (m_pDocPage) {\r
390         CPDF_ModuleMgr::Get()->GetPageModule()->ClearDoc(this);\r
391     }\r
392 }\r
393 void CPDF_Document::ClearRenderData()\r
394 {\r
395     if (m_pDocRender) {\r
396         CPDF_ModuleMgr::Get()->GetRenderModule()->ClearDocData(m_pDocRender);\r
397     }\r
398 }\r