Initial commit.
[pdfium.git] / core / src / fpdfdoc / doc_tagged.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_page.h"\r
9 #include "../../include/fpdfdoc/fpdf_tagged.h"\r
10 #include "tagged_int.h"\r
11 const int nMaxRecursion = 32;\r
12 static FX_BOOL IsTagged(const CPDF_Document* pDoc)\r
13 {\r
14     CPDF_Dictionary* pCatalog = pDoc->GetRoot();\r
15     CPDF_Dictionary* pMarkInfo = pCatalog->GetDict(FX_BSTRC("MarkInfo"));\r
16     return pMarkInfo != NULL && pMarkInfo->GetInteger(FX_BSTRC("Marked"));\r
17 }\r
18 CPDF_StructTree* CPDF_StructTree::LoadPage(const CPDF_Document* pDoc, const CPDF_Dictionary* pPageDict)\r
19 {\r
20     if (!IsTagged(pDoc)) {\r
21         return NULL;\r
22     }\r
23     CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);\r
24     if (pTree == NULL) {\r
25         return NULL;\r
26     }\r
27     pTree->LoadPageTree(pPageDict);\r
28     return pTree;\r
29 }\r
30 CPDF_StructTree* CPDF_StructTree::LoadDoc(const CPDF_Document* pDoc)\r
31 {\r
32     if (!IsTagged(pDoc)) {\r
33         return NULL;\r
34     }\r
35     CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);\r
36     if (pTree == NULL) {\r
37         return NULL;\r
38     }\r
39     pTree->LoadDocTree();\r
40     return pTree;\r
41 }\r
42 CPDF_StructTreeImpl::CPDF_StructTreeImpl(const CPDF_Document* pDoc)\r
43 {\r
44     CPDF_Dictionary* pCatalog = pDoc->GetRoot();\r
45     m_pTreeRoot = pCatalog->GetDict(FX_BSTRC("StructTreeRoot"));\r
46     if (m_pTreeRoot == NULL) {\r
47         return;\r
48     }\r
49     m_pRoleMap = m_pTreeRoot->GetDict(FX_BSTRC("RoleMap"));\r
50 }\r
51 CPDF_StructTreeImpl::~CPDF_StructTreeImpl()\r
52 {\r
53     for (int i = 0; i < m_Kids.GetSize(); i ++)\r
54         if (m_Kids[i]) {\r
55             m_Kids[i]->Release();\r
56         }\r
57 }\r
58 void CPDF_StructTreeImpl::LoadDocTree()\r
59 {\r
60     m_pPage = NULL;\r
61     if (m_pTreeRoot == NULL) {\r
62         return;\r
63     }\r
64     CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
65     if (pKids == NULL) {\r
66         return;\r
67     }\r
68     if (pKids->GetType() == PDFOBJ_DICTIONARY) {\r
69         CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, (CPDF_Dictionary*)pKids);\r
70         if (pStructElementImpl == NULL) {\r
71             return;\r
72         }\r
73         m_Kids.Add(pStructElementImpl);\r
74         return;\r
75     }\r
76     if (pKids->GetType() != PDFOBJ_ARRAY) {\r
77         return;\r
78     }\r
79     CPDF_Array* pArray = (CPDF_Array*)pKids;\r
80     for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
81         CPDF_Dictionary* pKid = pArray->GetDict(i);\r
82         CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, pKid);\r
83         if (pStructElementImpl == NULL) {\r
84             return;\r
85         }\r
86         m_Kids.Add(pStructElementImpl);\r
87     }\r
88 }\r
89 void CPDF_StructTreeImpl::LoadPageTree(const CPDF_Dictionary* pPageDict)\r
90 {\r
91     m_pPage = pPageDict;\r
92     if (m_pTreeRoot == NULL) {\r
93         return;\r
94     }\r
95     CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
96     if (pKids == NULL) {\r
97         return;\r
98     }\r
99     FX_DWORD dwKids = 0;\r
100     if (pKids->GetType() == PDFOBJ_DICTIONARY) {\r
101         dwKids = 1;\r
102     } else if (pKids->GetType() == PDFOBJ_ARRAY) {\r
103         dwKids = ((CPDF_Array*)pKids)->GetCount();\r
104     } else {\r
105         return;\r
106     }\r
107     FX_DWORD i;\r
108     m_Kids.SetSize(dwKids);\r
109     for (i = 0; i < dwKids; i ++) {\r
110         m_Kids[i] = NULL;\r
111     }\r
112     CFX_MapPtrToPtr element_map;\r
113     CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDict(FX_BSTRC("ParentTree"));\r
114     if (pParentTree == NULL) {\r
115         return;\r
116     }\r
117     CPDF_NumberTree parent_tree(pParentTree);\r
118     int parents_id = pPageDict->GetInteger(FX_BSTRC("StructParents"), -1);\r
119     if (parents_id >= 0) {\r
120         CPDF_Object* pParents = parent_tree.LookupValue(parents_id);\r
121         if (pParents == NULL || pParents->GetType() != PDFOBJ_ARRAY) {\r
122             return;\r
123         }\r
124         CPDF_Array* pParentArray = (CPDF_Array*)pParents;\r
125         for (i = 0; i < pParentArray->GetCount(); i ++) {\r
126             CPDF_Dictionary* pParent = pParentArray->GetDict(i);\r
127             if (pParent == NULL) {\r
128                 continue;\r
129             }\r
130             AddPageNode(pParent, element_map);\r
131         }\r
132     }\r
133 }\r
134 CPDF_StructElementImpl* CPDF_StructTreeImpl::AddPageNode(CPDF_Dictionary* pDict, CFX_MapPtrToPtr& map, int nLevel)\r
135 {\r
136     if (nLevel > nMaxRecursion) {\r
137         return NULL;\r
138     }\r
139     CPDF_StructElementImpl* pElement = NULL;\r
140     if (map.Lookup(pDict, (FX_LPVOID&)pElement)) {\r
141         return pElement;\r
142     }\r
143     pElement = FX_NEW CPDF_StructElementImpl(this, NULL, pDict);\r
144     if (pElement == NULL) {\r
145         return NULL;\r
146     }\r
147     map.SetAt(pDict, pElement);\r
148     CPDF_Dictionary* pParent = pDict->GetDict(FX_BSTRC("P"));\r
149     if (pParent == NULL || pParent->GetString(FX_BSTRC("Type")) == FX_BSTRC("StructTreeRoot")) {\r
150         if (!AddTopLevelNode(pDict, pElement)) {\r
151             pElement->Release();\r
152             map.RemoveKey(pDict);\r
153         }\r
154     } else {\r
155         CPDF_StructElementImpl* pParentElement = AddPageNode(pParent, map, nLevel + 1);\r
156         FX_BOOL bSave = FALSE;\r
157         for (int i = 0; i < pParentElement->m_Kids.GetSize(); i ++) {\r
158             if (pParentElement->m_Kids[i].m_Type != CPDF_StructKid::Element) {\r
159                 continue;\r
160             }\r
161             if (pParentElement->m_Kids[i].m_Element.m_pDict != pDict) {\r
162                 continue;\r
163             }\r
164             pParentElement->m_Kids[i].m_Element.m_pElement = pElement->Retain();\r
165             bSave = TRUE;\r
166         }\r
167         if (!bSave) {\r
168             pElement->Release();\r
169             map.RemoveKey(pDict);\r
170         }\r
171     }\r
172     return pElement;\r
173 }\r
174 FX_BOOL CPDF_StructTreeImpl::AddTopLevelNode(CPDF_Dictionary* pDict, CPDF_StructElementImpl* pElement)\r
175 {\r
176     CPDF_Object *pObj = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
177     if (!pObj) {\r
178         return FALSE;\r
179     }\r
180     if (pObj->GetType() == PDFOBJ_DICTIONARY) {\r
181         if (pObj->GetObjNum() == pDict->GetObjNum()) {\r
182             if (m_Kids[0]) {\r
183                 m_Kids[0]->Release();\r
184             }\r
185             m_Kids[0] = pElement->Retain();\r
186         } else {\r
187             return FALSE;\r
188         }\r
189     }\r
190     if (pObj->GetType() == PDFOBJ_ARRAY) {\r
191         CPDF_Array* pTopKids = (CPDF_Array*)pObj;\r
192         FX_DWORD i;\r
193         FX_BOOL bSave = FALSE;\r
194         for (i = 0; i < pTopKids->GetCount(); i ++) {\r
195             CPDF_Reference* pKidRef = (CPDF_Reference*)pTopKids->GetElement(i);\r
196             if (pKidRef->GetType() != PDFOBJ_REFERENCE || pKidRef->GetRefObjNum() != pDict->GetObjNum()) {\r
197                 continue;\r
198             }\r
199             if (m_Kids[i]) {\r
200                 m_Kids[i]->Release();\r
201             }\r
202             m_Kids[i] = pElement->Retain();\r
203             bSave = TRUE;\r
204         }\r
205         if (!bSave) {\r
206             return FALSE;\r
207         }\r
208     }\r
209     return TRUE;\r
210 }\r
211 CPDF_StructElementImpl::CPDF_StructElementImpl(CPDF_StructTreeImpl* pTree, CPDF_StructElementImpl* pParent, CPDF_Dictionary* pDict)\r
212     : m_RefCount(0)\r
213 {\r
214     m_pTree = pTree;\r
215     m_pDict = pDict;\r
216     m_Type = pDict->GetString(FX_BSTRC("S"));\r
217     CFX_ByteString mapped = pTree->m_pRoleMap->GetString(m_Type);\r
218     if (!mapped.IsEmpty()) {\r
219         m_Type = mapped;\r
220     }\r
221     m_pParent = pParent;\r
222     LoadKids(pDict);\r
223 }\r
224 CPDF_StructElementImpl::~CPDF_StructElementImpl()\r
225 {\r
226     for (int i = 0; i < m_Kids.GetSize(); i ++) {\r
227         if (m_Kids[i].m_Type == CPDF_StructKid::Element && m_Kids[i].m_Element.m_pElement) {\r
228             ((CPDF_StructElementImpl*)m_Kids[i].m_Element.m_pElement)->Release();\r
229         }\r
230     }\r
231 }\r
232 CPDF_StructElementImpl* CPDF_StructElementImpl::Retain()\r
233 {\r
234     m_RefCount++;\r
235     return this;\r
236 }\r
237 void CPDF_StructElementImpl::Release()\r
238 {\r
239     if(--m_RefCount < 1) {\r
240         delete this;\r
241     }\r
242 }\r
243 void CPDF_StructElementImpl::LoadKids(CPDF_Dictionary* pDict)\r
244 {\r
245     CPDF_Object* pObj = pDict->GetElement(FX_BSTRC("Pg"));\r
246     FX_DWORD PageObjNum = 0;\r
247     if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {\r
248         PageObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();\r
249     }\r
250     CPDF_Object* pKids = pDict->GetElementValue(FX_BSTRC("K"));\r
251     if (pKids == NULL) {\r
252         return;\r
253     }\r
254     if (pKids->GetType() == PDFOBJ_ARRAY) {\r
255         CPDF_Array* pArray = (CPDF_Array*)pKids;\r
256         m_Kids.SetSize(pArray->GetCount());\r
257         for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
258             CPDF_Object* pKid = pArray->GetElementValue(i);\r
259             LoadKid(PageObjNum, pKid, &m_Kids[i]);\r
260         }\r
261     } else {\r
262         m_Kids.SetSize(1);\r
263         LoadKid(PageObjNum, pKids, &m_Kids[0]);\r
264     }\r
265 }\r
266 void CPDF_StructElementImpl::LoadKid(FX_DWORD PageObjNum, CPDF_Object* pKidObj, CPDF_StructKid* pKid)\r
267 {\r
268     pKid->m_Type = CPDF_StructKid::Invalid;\r
269     if (pKidObj == NULL) {\r
270         return;\r
271     }\r
272     if (pKidObj->GetType() == PDFOBJ_NUMBER) {\r
273         if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
274             return;\r
275         }\r
276         pKid->m_Type = CPDF_StructKid::PageContent;\r
277         pKid->m_PageContent.m_ContentId = pKidObj->GetInteger();\r
278         pKid->m_PageContent.m_PageObjNum = PageObjNum;\r
279         return;\r
280     }\r
281     if (pKidObj->GetType() != PDFOBJ_DICTIONARY) {\r
282         return;\r
283     }\r
284     CPDF_Dictionary* pKidDict = (CPDF_Dictionary*)pKidObj;\r
285     CPDF_Object* pPageObj = pKidDict->GetElement(FX_BSTRC("Pg"));\r
286     if (pPageObj && pPageObj->GetType() == PDFOBJ_REFERENCE) {\r
287         PageObjNum = ((CPDF_Reference*)pPageObj)->GetRefObjNum();\r
288     }\r
289     CFX_ByteString type = pKidDict->GetString(FX_BSTRC("Type"));\r
290     if (type == FX_BSTRC("MCR")) {\r
291         if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
292             return;\r
293         }\r
294         pKid->m_Type = CPDF_StructKid::StreamContent;\r
295         CPDF_Object* pStreamObj = pKidDict->GetElement(FX_BSTRC("Stm"));\r
296         if (pStreamObj && pStreamObj->GetType() == PDFOBJ_REFERENCE) {\r
297             pKid->m_StreamContent.m_RefObjNum = ((CPDF_Reference*)pStreamObj)->GetRefObjNum();\r
298         } else {\r
299             pKid->m_StreamContent.m_RefObjNum = 0;\r
300         }\r
301         pKid->m_StreamContent.m_PageObjNum = PageObjNum;\r
302         pKid->m_StreamContent.m_ContentId = pKidDict->GetInteger(FX_BSTRC("MCID"));\r
303     } else if (type == FX_BSTRC("OBJR")) {\r
304         if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
305             return;\r
306         }\r
307         pKid->m_Type = CPDF_StructKid::Object;\r
308         CPDF_Object* pObj = pKidDict->GetElement(FX_BSTRC("Obj"));\r
309         if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {\r
310             pKid->m_Object.m_RefObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();\r
311         } else {\r
312             pKid->m_Object.m_RefObjNum = 0;\r
313         }\r
314         pKid->m_Object.m_PageObjNum = PageObjNum;\r
315     } else {\r
316         pKid->m_Type = CPDF_StructKid::Element;\r
317         pKid->m_Element.m_pDict = pKidDict;\r
318         if (m_pTree->m_pPage == NULL) {\r
319             pKid->m_Element.m_pElement = FX_NEW CPDF_StructElementImpl(m_pTree, this, pKidDict);\r
320         } else {\r
321             pKid->m_Element.m_pElement = NULL;\r
322         }\r
323     }\r
324 }\r
325 static CPDF_Dictionary* FindAttrDict(CPDF_Object* pAttrs, FX_BSTR owner, FX_FLOAT nLevel = 0.0F)\r
326 {\r
327     if (nLevel > nMaxRecursion) {\r
328         return NULL;\r
329     }\r
330     if (pAttrs == NULL) {\r
331         return NULL;\r
332     }\r
333     CPDF_Dictionary* pDict = NULL;\r
334     if (pAttrs->GetType() == PDFOBJ_DICTIONARY) {\r
335         pDict = (CPDF_Dictionary*)pAttrs;\r
336     } else if (pAttrs->GetType() == PDFOBJ_STREAM) {\r
337         pDict = ((CPDF_Stream*)pAttrs)->GetDict();\r
338     } else if (pAttrs->GetType() == PDFOBJ_ARRAY) {\r
339         CPDF_Array* pArray = (CPDF_Array*)pAttrs;\r
340         for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
341             CPDF_Object* pElement = pArray->GetElementValue(i);\r
342             pDict = FindAttrDict(pElement, owner, nLevel + 1);\r
343             if (pDict) {\r
344                 return pDict;\r
345             }\r
346         }\r
347     }\r
348     if (pDict && pDict->GetString(FX_BSTRC("O")) == owner) {\r
349         return pDict;\r
350     }\r
351     return NULL;\r
352 }\r
353 CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, FX_FLOAT fLevel)\r
354 {\r
355     if (fLevel > nMaxRecursion) {\r
356         return NULL;\r
357     }\r
358     if (bInheritable) {\r
359         CPDF_Object* pAttr = GetAttr(owner, name, FALSE);\r
360         if (pAttr) {\r
361             return pAttr;\r
362         }\r
363         if (m_pParent == NULL) {\r
364             return NULL;\r
365         }\r
366         return m_pParent->GetAttr(owner, name, TRUE, fLevel + 1);\r
367     }\r
368     CPDF_Object* pA = m_pDict->GetElementValue(FX_BSTRC("A"));\r
369     if (pA) {\r
370         CPDF_Dictionary* pAttrDict = FindAttrDict(pA, owner);\r
371         if (pAttrDict) {\r
372             CPDF_Object* pAttr = pAttrDict->GetElementValue(name);\r
373             if (pAttr) {\r
374                 return pAttr;\r
375             }\r
376         }\r
377     }\r
378     CPDF_Object* pC = m_pDict->GetElementValue(FX_BSTRC("C"));\r
379     if (pC == NULL) {\r
380         return NULL;\r
381     }\r
382     CPDF_Dictionary* pClassMap = m_pTree->m_pTreeRoot->GetDict(FX_BSTRC("ClassMap"));\r
383     if (pClassMap == NULL) {\r
384         return NULL;\r
385     }\r
386     if (pC->GetType() == PDFOBJ_ARRAY) {\r
387         CPDF_Array* pArray = (CPDF_Array*)pC;\r
388         for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
389             CFX_ByteString class_name = pArray->GetString(i);\r
390             CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);\r
391             if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {\r
392                 return pClassDict->GetElementValue(name);\r
393             }\r
394         }\r
395         return NULL;\r
396     }\r
397     CFX_ByteString class_name = pC->GetString();\r
398     CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);\r
399     if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {\r
400         return pClassDict->GetElementValue(name);\r
401     }\r
402     return NULL;\r
403 }\r
404 CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, int subindex)\r
405 {\r
406     CPDF_Object* pAttr = GetAttr(owner, name, bInheritable);\r
407     if (pAttr == NULL || subindex == -1 || pAttr->GetType() != PDFOBJ_ARRAY) {\r
408         return pAttr;\r
409     }\r
410     CPDF_Array* pArray = (CPDF_Array*)pAttr;\r
411     if (subindex >= (int)pArray->GetCount()) {\r
412         return pAttr;\r
413     }\r
414     return pArray->GetElementValue(subindex);\r
415 }\r
416 CFX_ByteString CPDF_StructElementImpl::GetName(FX_BSTR owner, FX_BSTR name, FX_BSTR default_value, FX_BOOL bInheritable, int subindex)\r
417 {\r
418     CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
419     if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NAME) {\r
420         return default_value;\r
421     }\r
422     return pAttr->GetString();\r
423 }\r
424 FX_ARGB CPDF_StructElementImpl::GetColor(FX_BSTR owner, FX_BSTR name, FX_ARGB default_value, FX_BOOL bInheritable, int subindex)\r
425 {\r
426     CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
427     if (pAttr == NULL || pAttr->GetType() != PDFOBJ_ARRAY) {\r
428         return default_value;\r
429     }\r
430     CPDF_Array* pArray = (CPDF_Array*)pAttr;\r
431     return 0xff000000 | ((int)(pArray->GetNumber(0) * 255) << 16) | ((int)(pArray->GetNumber(1) * 255) << 8) | (int)(pArray->GetNumber(2) * 255);\r
432 }\r
433 FX_FLOAT CPDF_StructElementImpl::GetNumber(FX_BSTR owner, FX_BSTR name, FX_FLOAT default_value, FX_BOOL bInheritable, int subindex)\r
434 {\r
435     CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
436     if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {\r
437         return default_value;\r
438     }\r
439     return pAttr->GetNumber();\r
440 }\r
441 int     CPDF_StructElementImpl::GetInteger(FX_BSTR owner, FX_BSTR name, int default_value, FX_BOOL bInheritable, int subindex)\r
442 {\r
443     CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
444     if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {\r
445         return default_value;\r
446     }\r
447     return pAttr->GetInteger();\r
448 }\r