Land: Protect against a seg fault in CPDF_StructElementImpl constructor.
[pdfium.git] / core / src / fpdfdoc / doc_tagged.cpp
index 95ae8d5..c5fe39b 100644 (file)
-// Copyright 2014 PDFium Authors. All rights reserved.\r
-// Use of this source code is governed by a BSD-style license that can be\r
-// found in the LICENSE file.\r
\r
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
-\r
-#include "../../include/fpdfapi/fpdf_parser.h"\r
-#include "../../include/fpdfapi/fpdf_page.h"\r
-#include "../../include/fpdfdoc/fpdf_tagged.h"\r
-#include "tagged_int.h"\r
-const int nMaxRecursion = 32;\r
-static FX_BOOL IsTagged(const CPDF_Document* pDoc)\r
-{\r
-    CPDF_Dictionary* pCatalog = pDoc->GetRoot();\r
-    CPDF_Dictionary* pMarkInfo = pCatalog->GetDict(FX_BSTRC("MarkInfo"));\r
-    return pMarkInfo != NULL && pMarkInfo->GetInteger(FX_BSTRC("Marked"));\r
-}\r
-CPDF_StructTree* CPDF_StructTree::LoadPage(const CPDF_Document* pDoc, const CPDF_Dictionary* pPageDict)\r
-{\r
-    if (!IsTagged(pDoc)) {\r
-        return NULL;\r
-    }\r
-    CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);\r
-    if (pTree == NULL) {\r
-        return NULL;\r
-    }\r
-    pTree->LoadPageTree(pPageDict);\r
-    return pTree;\r
-}\r
-CPDF_StructTree* CPDF_StructTree::LoadDoc(const CPDF_Document* pDoc)\r
-{\r
-    if (!IsTagged(pDoc)) {\r
-        return NULL;\r
-    }\r
-    CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);\r
-    if (pTree == NULL) {\r
-        return NULL;\r
-    }\r
-    pTree->LoadDocTree();\r
-    return pTree;\r
-}\r
-CPDF_StructTreeImpl::CPDF_StructTreeImpl(const CPDF_Document* pDoc)\r
-{\r
-    CPDF_Dictionary* pCatalog = pDoc->GetRoot();\r
-    m_pTreeRoot = pCatalog->GetDict(FX_BSTRC("StructTreeRoot"));\r
-    if (m_pTreeRoot == NULL) {\r
-        return;\r
-    }\r
-    m_pRoleMap = m_pTreeRoot->GetDict(FX_BSTRC("RoleMap"));\r
-}\r
-CPDF_StructTreeImpl::~CPDF_StructTreeImpl()\r
-{\r
-    for (int i = 0; i < m_Kids.GetSize(); i ++)\r
-        if (m_Kids[i]) {\r
-            m_Kids[i]->Release();\r
-        }\r
-}\r
-void CPDF_StructTreeImpl::LoadDocTree()\r
-{\r
-    m_pPage = NULL;\r
-    if (m_pTreeRoot == NULL) {\r
-        return;\r
-    }\r
-    CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
-    if (pKids == NULL) {\r
-        return;\r
-    }\r
-    if (pKids->GetType() == PDFOBJ_DICTIONARY) {\r
-        CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, (CPDF_Dictionary*)pKids);\r
-        if (pStructElementImpl == NULL) {\r
-            return;\r
-        }\r
-        m_Kids.Add(pStructElementImpl);\r
-        return;\r
-    }\r
-    if (pKids->GetType() != PDFOBJ_ARRAY) {\r
-        return;\r
-    }\r
-    CPDF_Array* pArray = (CPDF_Array*)pKids;\r
-    for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
-        CPDF_Dictionary* pKid = pArray->GetDict(i);\r
-        CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, pKid);\r
-        if (pStructElementImpl == NULL) {\r
-            return;\r
-        }\r
-        m_Kids.Add(pStructElementImpl);\r
-    }\r
-}\r
-void CPDF_StructTreeImpl::LoadPageTree(const CPDF_Dictionary* pPageDict)\r
-{\r
-    m_pPage = pPageDict;\r
-    if (m_pTreeRoot == NULL) {\r
-        return;\r
-    }\r
-    CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
-    if (pKids == NULL) {\r
-        return;\r
-    }\r
-    FX_DWORD dwKids = 0;\r
-    if (pKids->GetType() == PDFOBJ_DICTIONARY) {\r
-        dwKids = 1;\r
-    } else if (pKids->GetType() == PDFOBJ_ARRAY) {\r
-        dwKids = ((CPDF_Array*)pKids)->GetCount();\r
-    } else {\r
-        return;\r
-    }\r
-    FX_DWORD i;\r
-    m_Kids.SetSize(dwKids);\r
-    for (i = 0; i < dwKids; i ++) {\r
-        m_Kids[i] = NULL;\r
-    }\r
-    CFX_MapPtrToPtr element_map;\r
-    CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDict(FX_BSTRC("ParentTree"));\r
-    if (pParentTree == NULL) {\r
-        return;\r
-    }\r
-    CPDF_NumberTree parent_tree(pParentTree);\r
-    int parents_id = pPageDict->GetInteger(FX_BSTRC("StructParents"), -1);\r
-    if (parents_id >= 0) {\r
-        CPDF_Object* pParents = parent_tree.LookupValue(parents_id);\r
-        if (pParents == NULL || pParents->GetType() != PDFOBJ_ARRAY) {\r
-            return;\r
-        }\r
-        CPDF_Array* pParentArray = (CPDF_Array*)pParents;\r
-        for (i = 0; i < pParentArray->GetCount(); i ++) {\r
-            CPDF_Dictionary* pParent = pParentArray->GetDict(i);\r
-            if (pParent == NULL) {\r
-                continue;\r
-            }\r
-            AddPageNode(pParent, element_map);\r
-        }\r
-    }\r
-}\r
-CPDF_StructElementImpl* CPDF_StructTreeImpl::AddPageNode(CPDF_Dictionary* pDict, CFX_MapPtrToPtr& map, int nLevel)\r
-{\r
-    if (nLevel > nMaxRecursion) {\r
-        return NULL;\r
-    }\r
-    CPDF_StructElementImpl* pElement = NULL;\r
-    if (map.Lookup(pDict, (FX_LPVOID&)pElement)) {\r
-        return pElement;\r
-    }\r
-    pElement = FX_NEW CPDF_StructElementImpl(this, NULL, pDict);\r
-    if (pElement == NULL) {\r
-        return NULL;\r
-    }\r
-    map.SetAt(pDict, pElement);\r
-    CPDF_Dictionary* pParent = pDict->GetDict(FX_BSTRC("P"));\r
-    if (pParent == NULL || pParent->GetString(FX_BSTRC("Type")) == FX_BSTRC("StructTreeRoot")) {\r
-        if (!AddTopLevelNode(pDict, pElement)) {\r
-            pElement->Release();\r
-            map.RemoveKey(pDict);\r
-        }\r
-    } else {\r
-        CPDF_StructElementImpl* pParentElement = AddPageNode(pParent, map, nLevel + 1);\r
-        FX_BOOL bSave = FALSE;\r
-        for (int i = 0; i < pParentElement->m_Kids.GetSize(); i ++) {\r
-            if (pParentElement->m_Kids[i].m_Type != CPDF_StructKid::Element) {\r
-                continue;\r
-            }\r
-            if (pParentElement->m_Kids[i].m_Element.m_pDict != pDict) {\r
-                continue;\r
-            }\r
-            pParentElement->m_Kids[i].m_Element.m_pElement = pElement->Retain();\r
-            bSave = TRUE;\r
-        }\r
-        if (!bSave) {\r
-            pElement->Release();\r
-            map.RemoveKey(pDict);\r
-        }\r
-    }\r
-    return pElement;\r
-}\r
-FX_BOOL CPDF_StructTreeImpl::AddTopLevelNode(CPDF_Dictionary* pDict, CPDF_StructElementImpl* pElement)\r
-{\r
-    CPDF_Object *pObj = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));\r
-    if (!pObj) {\r
-        return FALSE;\r
-    }\r
-    if (pObj->GetType() == PDFOBJ_DICTIONARY) {\r
-        if (pObj->GetObjNum() == pDict->GetObjNum()) {\r
-            if (m_Kids[0]) {\r
-                m_Kids[0]->Release();\r
-            }\r
-            m_Kids[0] = pElement->Retain();\r
-        } else {\r
-            return FALSE;\r
-        }\r
-    }\r
-    if (pObj->GetType() == PDFOBJ_ARRAY) {\r
-        CPDF_Array* pTopKids = (CPDF_Array*)pObj;\r
-        FX_DWORD i;\r
-        FX_BOOL bSave = FALSE;\r
-        for (i = 0; i < pTopKids->GetCount(); i ++) {\r
-            CPDF_Reference* pKidRef = (CPDF_Reference*)pTopKids->GetElement(i);\r
-            if (pKidRef->GetType() != PDFOBJ_REFERENCE || pKidRef->GetRefObjNum() != pDict->GetObjNum()) {\r
-                continue;\r
-            }\r
-            if (m_Kids[i]) {\r
-                m_Kids[i]->Release();\r
-            }\r
-            m_Kids[i] = pElement->Retain();\r
-            bSave = TRUE;\r
-        }\r
-        if (!bSave) {\r
-            return FALSE;\r
-        }\r
-    }\r
-    return TRUE;\r
-}\r
-CPDF_StructElementImpl::CPDF_StructElementImpl(CPDF_StructTreeImpl* pTree, CPDF_StructElementImpl* pParent, CPDF_Dictionary* pDict)\r
-    : m_RefCount(0)\r
-{\r
-    m_pTree = pTree;\r
-    m_pDict = pDict;\r
-    m_Type = pDict->GetString(FX_BSTRC("S"));\r
-    CFX_ByteString mapped = pTree->m_pRoleMap->GetString(m_Type);\r
-    if (!mapped.IsEmpty()) {\r
-        m_Type = mapped;\r
-    }\r
-    m_pParent = pParent;\r
-    LoadKids(pDict);\r
-}\r
-CPDF_StructElementImpl::~CPDF_StructElementImpl()\r
-{\r
-    for (int i = 0; i < m_Kids.GetSize(); i ++) {\r
-        if (m_Kids[i].m_Type == CPDF_StructKid::Element && m_Kids[i].m_Element.m_pElement) {\r
-            ((CPDF_StructElementImpl*)m_Kids[i].m_Element.m_pElement)->Release();\r
-        }\r
-    }\r
-}\r
-CPDF_StructElementImpl* CPDF_StructElementImpl::Retain()\r
-{\r
-    m_RefCount++;\r
-    return this;\r
-}\r
-void CPDF_StructElementImpl::Release()\r
-{\r
-    if(--m_RefCount < 1) {\r
-        delete this;\r
-    }\r
-}\r
-void CPDF_StructElementImpl::LoadKids(CPDF_Dictionary* pDict)\r
-{\r
-    CPDF_Object* pObj = pDict->GetElement(FX_BSTRC("Pg"));\r
-    FX_DWORD PageObjNum = 0;\r
-    if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {\r
-        PageObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();\r
-    }\r
-    CPDF_Object* pKids = pDict->GetElementValue(FX_BSTRC("K"));\r
-    if (pKids == NULL) {\r
-        return;\r
-    }\r
-    if (pKids->GetType() == PDFOBJ_ARRAY) {\r
-        CPDF_Array* pArray = (CPDF_Array*)pKids;\r
-        m_Kids.SetSize(pArray->GetCount());\r
-        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
-            CPDF_Object* pKid = pArray->GetElementValue(i);\r
-            LoadKid(PageObjNum, pKid, &m_Kids[i]);\r
-        }\r
-    } else {\r
-        m_Kids.SetSize(1);\r
-        LoadKid(PageObjNum, pKids, &m_Kids[0]);\r
-    }\r
-}\r
-void CPDF_StructElementImpl::LoadKid(FX_DWORD PageObjNum, CPDF_Object* pKidObj, CPDF_StructKid* pKid)\r
-{\r
-    pKid->m_Type = CPDF_StructKid::Invalid;\r
-    if (pKidObj == NULL) {\r
-        return;\r
-    }\r
-    if (pKidObj->GetType() == PDFOBJ_NUMBER) {\r
-        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
-            return;\r
-        }\r
-        pKid->m_Type = CPDF_StructKid::PageContent;\r
-        pKid->m_PageContent.m_ContentId = pKidObj->GetInteger();\r
-        pKid->m_PageContent.m_PageObjNum = PageObjNum;\r
-        return;\r
-    }\r
-    if (pKidObj->GetType() != PDFOBJ_DICTIONARY) {\r
-        return;\r
-    }\r
-    CPDF_Dictionary* pKidDict = (CPDF_Dictionary*)pKidObj;\r
-    CPDF_Object* pPageObj = pKidDict->GetElement(FX_BSTRC("Pg"));\r
-    if (pPageObj && pPageObj->GetType() == PDFOBJ_REFERENCE) {\r
-        PageObjNum = ((CPDF_Reference*)pPageObj)->GetRefObjNum();\r
-    }\r
-    CFX_ByteString type = pKidDict->GetString(FX_BSTRC("Type"));\r
-    if (type == FX_BSTRC("MCR")) {\r
-        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
-            return;\r
-        }\r
-        pKid->m_Type = CPDF_StructKid::StreamContent;\r
-        CPDF_Object* pStreamObj = pKidDict->GetElement(FX_BSTRC("Stm"));\r
-        if (pStreamObj && pStreamObj->GetType() == PDFOBJ_REFERENCE) {\r
-            pKid->m_StreamContent.m_RefObjNum = ((CPDF_Reference*)pStreamObj)->GetRefObjNum();\r
-        } else {\r
-            pKid->m_StreamContent.m_RefObjNum = 0;\r
-        }\r
-        pKid->m_StreamContent.m_PageObjNum = PageObjNum;\r
-        pKid->m_StreamContent.m_ContentId = pKidDict->GetInteger(FX_BSTRC("MCID"));\r
-    } else if (type == FX_BSTRC("OBJR")) {\r
-        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {\r
-            return;\r
-        }\r
-        pKid->m_Type = CPDF_StructKid::Object;\r
-        CPDF_Object* pObj = pKidDict->GetElement(FX_BSTRC("Obj"));\r
-        if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {\r
-            pKid->m_Object.m_RefObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();\r
-        } else {\r
-            pKid->m_Object.m_RefObjNum = 0;\r
-        }\r
-        pKid->m_Object.m_PageObjNum = PageObjNum;\r
-    } else {\r
-        pKid->m_Type = CPDF_StructKid::Element;\r
-        pKid->m_Element.m_pDict = pKidDict;\r
-        if (m_pTree->m_pPage == NULL) {\r
-            pKid->m_Element.m_pElement = FX_NEW CPDF_StructElementImpl(m_pTree, this, pKidDict);\r
-        } else {\r
-            pKid->m_Element.m_pElement = NULL;\r
-        }\r
-    }\r
-}\r
-static CPDF_Dictionary* FindAttrDict(CPDF_Object* pAttrs, FX_BSTR owner, FX_FLOAT nLevel = 0.0F)\r
-{\r
-    if (nLevel > nMaxRecursion) {\r
-        return NULL;\r
-    }\r
-    if (pAttrs == NULL) {\r
-        return NULL;\r
-    }\r
-    CPDF_Dictionary* pDict = NULL;\r
-    if (pAttrs->GetType() == PDFOBJ_DICTIONARY) {\r
-        pDict = (CPDF_Dictionary*)pAttrs;\r
-    } else if (pAttrs->GetType() == PDFOBJ_STREAM) {\r
-        pDict = ((CPDF_Stream*)pAttrs)->GetDict();\r
-    } else if (pAttrs->GetType() == PDFOBJ_ARRAY) {\r
-        CPDF_Array* pArray = (CPDF_Array*)pAttrs;\r
-        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
-            CPDF_Object* pElement = pArray->GetElementValue(i);\r
-            pDict = FindAttrDict(pElement, owner, nLevel + 1);\r
-            if (pDict) {\r
-                return pDict;\r
-            }\r
-        }\r
-    }\r
-    if (pDict && pDict->GetString(FX_BSTRC("O")) == owner) {\r
-        return pDict;\r
-    }\r
-    return NULL;\r
-}\r
-CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, FX_FLOAT fLevel)\r
-{\r
-    if (fLevel > nMaxRecursion) {\r
-        return NULL;\r
-    }\r
-    if (bInheritable) {\r
-        CPDF_Object* pAttr = GetAttr(owner, name, FALSE);\r
-        if (pAttr) {\r
-            return pAttr;\r
-        }\r
-        if (m_pParent == NULL) {\r
-            return NULL;\r
-        }\r
-        return m_pParent->GetAttr(owner, name, TRUE, fLevel + 1);\r
-    }\r
-    CPDF_Object* pA = m_pDict->GetElementValue(FX_BSTRC("A"));\r
-    if (pA) {\r
-        CPDF_Dictionary* pAttrDict = FindAttrDict(pA, owner);\r
-        if (pAttrDict) {\r
-            CPDF_Object* pAttr = pAttrDict->GetElementValue(name);\r
-            if (pAttr) {\r
-                return pAttr;\r
-            }\r
-        }\r
-    }\r
-    CPDF_Object* pC = m_pDict->GetElementValue(FX_BSTRC("C"));\r
-    if (pC == NULL) {\r
-        return NULL;\r
-    }\r
-    CPDF_Dictionary* pClassMap = m_pTree->m_pTreeRoot->GetDict(FX_BSTRC("ClassMap"));\r
-    if (pClassMap == NULL) {\r
-        return NULL;\r
-    }\r
-    if (pC->GetType() == PDFOBJ_ARRAY) {\r
-        CPDF_Array* pArray = (CPDF_Array*)pC;\r
-        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {\r
-            CFX_ByteString class_name = pArray->GetString(i);\r
-            CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);\r
-            if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {\r
-                return pClassDict->GetElementValue(name);\r
-            }\r
-        }\r
-        return NULL;\r
-    }\r
-    CFX_ByteString class_name = pC->GetString();\r
-    CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);\r
-    if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {\r
-        return pClassDict->GetElementValue(name);\r
-    }\r
-    return NULL;\r
-}\r
-CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, int subindex)\r
-{\r
-    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable);\r
-    if (pAttr == NULL || subindex == -1 || pAttr->GetType() != PDFOBJ_ARRAY) {\r
-        return pAttr;\r
-    }\r
-    CPDF_Array* pArray = (CPDF_Array*)pAttr;\r
-    if (subindex >= (int)pArray->GetCount()) {\r
-        return pAttr;\r
-    }\r
-    return pArray->GetElementValue(subindex);\r
-}\r
-CFX_ByteString CPDF_StructElementImpl::GetName(FX_BSTR owner, FX_BSTR name, FX_BSTR default_value, FX_BOOL bInheritable, int subindex)\r
-{\r
-    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
-    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NAME) {\r
-        return default_value;\r
-    }\r
-    return pAttr->GetString();\r
-}\r
-FX_ARGB        CPDF_StructElementImpl::GetColor(FX_BSTR owner, FX_BSTR name, FX_ARGB default_value, FX_BOOL bInheritable, int subindex)\r
-{\r
-    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
-    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_ARRAY) {\r
-        return default_value;\r
-    }\r
-    CPDF_Array* pArray = (CPDF_Array*)pAttr;\r
-    return 0xff000000 | ((int)(pArray->GetNumber(0) * 255) << 16) | ((int)(pArray->GetNumber(1) * 255) << 8) | (int)(pArray->GetNumber(2) * 255);\r
-}\r
-FX_FLOAT CPDF_StructElementImpl::GetNumber(FX_BSTR owner, FX_BSTR name, FX_FLOAT default_value, FX_BOOL bInheritable, int subindex)\r
-{\r
-    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
-    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {\r
-        return default_value;\r
-    }\r
-    return pAttr->GetNumber();\r
-}\r
-int    CPDF_StructElementImpl::GetInteger(FX_BSTR owner, FX_BSTR name, int default_value, FX_BOOL bInheritable, int subindex)\r
-{\r
-    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);\r
-    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {\r
-        return default_value;\r
-    }\r
-    return pAttr->GetInteger();\r
-}\r
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "../../include/fpdfapi/fpdf_parser.h"
+#include "../../include/fpdfapi/fpdf_page.h"
+#include "../../include/fpdfdoc/fpdf_tagged.h"
+#include "tagged_int.h"
+const int nMaxRecursion = 32;
+static FX_BOOL IsTagged(const CPDF_Document* pDoc)
+{
+    CPDF_Dictionary* pCatalog = pDoc->GetRoot();
+    CPDF_Dictionary* pMarkInfo = pCatalog->GetDict(FX_BSTRC("MarkInfo"));
+    return pMarkInfo != NULL && pMarkInfo->GetInteger(FX_BSTRC("Marked"));
+}
+CPDF_StructTree* CPDF_StructTree::LoadPage(const CPDF_Document* pDoc, const CPDF_Dictionary* pPageDict)
+{
+    if (!IsTagged(pDoc)) {
+        return NULL;
+    }
+    CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);
+    if (pTree == NULL) {
+        return NULL;
+    }
+    pTree->LoadPageTree(pPageDict);
+    return pTree;
+}
+CPDF_StructTree* CPDF_StructTree::LoadDoc(const CPDF_Document* pDoc)
+{
+    if (!IsTagged(pDoc)) {
+        return NULL;
+    }
+    CPDF_StructTreeImpl* pTree = FX_NEW CPDF_StructTreeImpl(pDoc);
+    if (pTree == NULL) {
+        return NULL;
+    }
+    pTree->LoadDocTree();
+    return pTree;
+}
+CPDF_StructTreeImpl::CPDF_StructTreeImpl(const CPDF_Document* pDoc)
+{
+    CPDF_Dictionary* pCatalog = pDoc->GetRoot();
+    m_pTreeRoot = pCatalog->GetDict(FX_BSTRC("StructTreeRoot"));
+    if (m_pTreeRoot == NULL) {
+        return;
+    }
+    m_pRoleMap = m_pTreeRoot->GetDict(FX_BSTRC("RoleMap"));
+}
+CPDF_StructTreeImpl::~CPDF_StructTreeImpl()
+{
+    for (int i = 0; i < m_Kids.GetSize(); i ++)
+        if (m_Kids[i]) {
+            m_Kids[i]->Release();
+        }
+}
+void CPDF_StructTreeImpl::LoadDocTree()
+{
+    m_pPage = NULL;
+    if (m_pTreeRoot == NULL) {
+        return;
+    }
+    CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));
+    if (pKids == NULL) {
+        return;
+    }
+    if (pKids->GetType() == PDFOBJ_DICTIONARY) {
+        CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, (CPDF_Dictionary*)pKids);
+        if (pStructElementImpl == NULL) {
+            return;
+        }
+        m_Kids.Add(pStructElementImpl);
+        return;
+    }
+    if (pKids->GetType() != PDFOBJ_ARRAY) {
+        return;
+    }
+    CPDF_Array* pArray = (CPDF_Array*)pKids;
+    for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {
+        CPDF_Dictionary* pKid = pArray->GetDict(i);
+        CPDF_StructElementImpl* pStructElementImpl = FX_NEW CPDF_StructElementImpl(this, NULL, pKid);
+        if (pStructElementImpl == NULL) {
+            return;
+        }
+        m_Kids.Add(pStructElementImpl);
+    }
+}
+void CPDF_StructTreeImpl::LoadPageTree(const CPDF_Dictionary* pPageDict)
+{
+    m_pPage = pPageDict;
+    if (m_pTreeRoot == NULL) {
+        return;
+    }
+    CPDF_Object* pKids = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));
+    if (pKids == NULL) {
+        return;
+    }
+    FX_DWORD dwKids = 0;
+    if (pKids->GetType() == PDFOBJ_DICTIONARY) {
+        dwKids = 1;
+    } else if (pKids->GetType() == PDFOBJ_ARRAY) {
+        dwKids = ((CPDF_Array*)pKids)->GetCount();
+    } else {
+        return;
+    }
+    FX_DWORD i;
+    m_Kids.SetSize(dwKids);
+    for (i = 0; i < dwKids; i ++) {
+        m_Kids[i] = NULL;
+    }
+    CFX_MapPtrToPtr element_map;
+    CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDict(FX_BSTRC("ParentTree"));
+    if (pParentTree == NULL) {
+        return;
+    }
+    CPDF_NumberTree parent_tree(pParentTree);
+    int parents_id = pPageDict->GetInteger(FX_BSTRC("StructParents"), -1);
+    if (parents_id >= 0) {
+        CPDF_Object* pParents = parent_tree.LookupValue(parents_id);
+        if (pParents == NULL || pParents->GetType() != PDFOBJ_ARRAY) {
+            return;
+        }
+        CPDF_Array* pParentArray = (CPDF_Array*)pParents;
+        for (i = 0; i < pParentArray->GetCount(); i ++) {
+            CPDF_Dictionary* pParent = pParentArray->GetDict(i);
+            if (pParent == NULL) {
+                continue;
+            }
+            AddPageNode(pParent, element_map);
+        }
+    }
+}
+CPDF_StructElementImpl* CPDF_StructTreeImpl::AddPageNode(CPDF_Dictionary* pDict, CFX_MapPtrToPtr& map, int nLevel)
+{
+    if (nLevel > nMaxRecursion) {
+        return NULL;
+    }
+    CPDF_StructElementImpl* pElement = NULL;
+    if (map.Lookup(pDict, (FX_LPVOID&)pElement)) {
+        return pElement;
+    }
+    pElement = FX_NEW CPDF_StructElementImpl(this, NULL, pDict);
+    if (pElement == NULL) {
+        return NULL;
+    }
+    map.SetAt(pDict, pElement);
+    CPDF_Dictionary* pParent = pDict->GetDict(FX_BSTRC("P"));
+    if (pParent == NULL || pParent->GetString(FX_BSTRC("Type")) == FX_BSTRC("StructTreeRoot")) {
+        if (!AddTopLevelNode(pDict, pElement)) {
+            pElement->Release();
+            map.RemoveKey(pDict);
+        }
+    } else {
+        CPDF_StructElementImpl* pParentElement = AddPageNode(pParent, map, nLevel + 1);
+        FX_BOOL bSave = FALSE;
+        for (int i = 0; i < pParentElement->m_Kids.GetSize(); i ++) {
+            if (pParentElement->m_Kids[i].m_Type != CPDF_StructKid::Element) {
+                continue;
+            }
+            if (pParentElement->m_Kids[i].m_Element.m_pDict != pDict) {
+                continue;
+            }
+            pParentElement->m_Kids[i].m_Element.m_pElement = pElement->Retain();
+            bSave = TRUE;
+        }
+        if (!bSave) {
+            pElement->Release();
+            map.RemoveKey(pDict);
+        }
+    }
+    return pElement;
+}
+FX_BOOL CPDF_StructTreeImpl::AddTopLevelNode(CPDF_Dictionary* pDict, CPDF_StructElementImpl* pElement)
+{
+    CPDF_Object *pObj = m_pTreeRoot->GetElementValue(FX_BSTRC("K"));
+    if (!pObj) {
+        return FALSE;
+    }
+    if (pObj->GetType() == PDFOBJ_DICTIONARY) {
+        if (pObj->GetObjNum() == pDict->GetObjNum()) {
+            if (m_Kids[0]) {
+                m_Kids[0]->Release();
+            }
+            m_Kids[0] = pElement->Retain();
+        } else {
+            return FALSE;
+        }
+    }
+    if (pObj->GetType() == PDFOBJ_ARRAY) {
+        CPDF_Array* pTopKids = (CPDF_Array*)pObj;
+        FX_DWORD i;
+        FX_BOOL bSave = FALSE;
+        for (i = 0; i < pTopKids->GetCount(); i ++) {
+            CPDF_Object* pKidRef = pTopKids->GetElement(i);
+            if (pKidRef == NULL || pKidRef->GetType() != PDFOBJ_REFERENCE) {
+                continue;
+            }
+            if (((CPDF_Reference*) pKidRef)->GetRefObjNum() != pDict->GetObjNum()) {
+                continue;
+            }
+            if (m_Kids[i]) {
+                m_Kids[i]->Release();
+            }
+            m_Kids[i] = pElement->Retain();
+            bSave = TRUE;
+        }
+        if (!bSave) {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+CPDF_StructElementImpl::CPDF_StructElementImpl(CPDF_StructTreeImpl* pTree, CPDF_StructElementImpl* pParent, CPDF_Dictionary* pDict)
+    : m_RefCount(0)
+{
+    m_pTree = pTree;
+    m_pDict = pDict;
+    m_Type = pDict->GetString(FX_BSTRC("S"));
+    if (pTree->m_pRoleMap) {
+        CFX_ByteString mapped = pTree->m_pRoleMap->GetString(m_Type);
+        if (!mapped.IsEmpty()) {
+            m_Type = mapped;
+        }
+    }
+    m_pParent = pParent;
+    LoadKids(pDict);
+}
+CPDF_StructElementImpl::~CPDF_StructElementImpl()
+{
+    for (int i = 0; i < m_Kids.GetSize(); i ++) {
+        if (m_Kids[i].m_Type == CPDF_StructKid::Element && m_Kids[i].m_Element.m_pElement) {
+            ((CPDF_StructElementImpl*)m_Kids[i].m_Element.m_pElement)->Release();
+        }
+    }
+}
+CPDF_StructElementImpl* CPDF_StructElementImpl::Retain()
+{
+    m_RefCount++;
+    return this;
+}
+void CPDF_StructElementImpl::Release()
+{
+    if(--m_RefCount < 1) {
+        delete this;
+    }
+}
+void CPDF_StructElementImpl::LoadKids(CPDF_Dictionary* pDict)
+{
+    CPDF_Object* pObj = pDict->GetElement(FX_BSTRC("Pg"));
+    FX_DWORD PageObjNum = 0;
+    if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {
+        PageObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();
+    }
+    CPDF_Object* pKids = pDict->GetElementValue(FX_BSTRC("K"));
+    if (pKids == NULL) {
+        return;
+    }
+    if (pKids->GetType() == PDFOBJ_ARRAY) {
+        CPDF_Array* pArray = (CPDF_Array*)pKids;
+        m_Kids.SetSize(pArray->GetCount());
+        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {
+            CPDF_Object* pKid = pArray->GetElementValue(i);
+            LoadKid(PageObjNum, pKid, &m_Kids[i]);
+        }
+    } else {
+        m_Kids.SetSize(1);
+        LoadKid(PageObjNum, pKids, &m_Kids[0]);
+    }
+}
+void CPDF_StructElementImpl::LoadKid(FX_DWORD PageObjNum, CPDF_Object* pKidObj, CPDF_StructKid* pKid)
+{
+    pKid->m_Type = CPDF_StructKid::Invalid;
+    if (pKidObj == NULL) {
+        return;
+    }
+    if (pKidObj->GetType() == PDFOBJ_NUMBER) {
+        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+            return;
+        }
+        pKid->m_Type = CPDF_StructKid::PageContent;
+        pKid->m_PageContent.m_ContentId = pKidObj->GetInteger();
+        pKid->m_PageContent.m_PageObjNum = PageObjNum;
+        return;
+    }
+    if (pKidObj->GetType() != PDFOBJ_DICTIONARY) {
+        return;
+    }
+    CPDF_Dictionary* pKidDict = (CPDF_Dictionary*)pKidObj;
+    CPDF_Object* pPageObj = pKidDict->GetElement(FX_BSTRC("Pg"));
+    if (pPageObj && pPageObj->GetType() == PDFOBJ_REFERENCE) {
+        PageObjNum = ((CPDF_Reference*)pPageObj)->GetRefObjNum();
+    }
+    CFX_ByteString type = pKidDict->GetString(FX_BSTRC("Type"));
+    if (type == FX_BSTRC("MCR")) {
+        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+            return;
+        }
+        pKid->m_Type = CPDF_StructKid::StreamContent;
+        CPDF_Object* pStreamObj = pKidDict->GetElement(FX_BSTRC("Stm"));
+        if (pStreamObj && pStreamObj->GetType() == PDFOBJ_REFERENCE) {
+            pKid->m_StreamContent.m_RefObjNum = ((CPDF_Reference*)pStreamObj)->GetRefObjNum();
+        } else {
+            pKid->m_StreamContent.m_RefObjNum = 0;
+        }
+        pKid->m_StreamContent.m_PageObjNum = PageObjNum;
+        pKid->m_StreamContent.m_ContentId = pKidDict->GetInteger(FX_BSTRC("MCID"));
+    } else if (type == FX_BSTRC("OBJR")) {
+        if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+            return;
+        }
+        pKid->m_Type = CPDF_StructKid::Object;
+        CPDF_Object* pObj = pKidDict->GetElement(FX_BSTRC("Obj"));
+        if (pObj && pObj->GetType() == PDFOBJ_REFERENCE) {
+            pKid->m_Object.m_RefObjNum = ((CPDF_Reference*)pObj)->GetRefObjNum();
+        } else {
+            pKid->m_Object.m_RefObjNum = 0;
+        }
+        pKid->m_Object.m_PageObjNum = PageObjNum;
+    } else {
+        pKid->m_Type = CPDF_StructKid::Element;
+        pKid->m_Element.m_pDict = pKidDict;
+        if (m_pTree->m_pPage == NULL) {
+            pKid->m_Element.m_pElement = FX_NEW CPDF_StructElementImpl(m_pTree, this, pKidDict);
+        } else {
+            pKid->m_Element.m_pElement = NULL;
+        }
+    }
+}
+static CPDF_Dictionary* FindAttrDict(CPDF_Object* pAttrs, FX_BSTR owner, FX_FLOAT nLevel = 0.0F)
+{
+    if (nLevel > nMaxRecursion) {
+        return NULL;
+    }
+    if (pAttrs == NULL) {
+        return NULL;
+    }
+    CPDF_Dictionary* pDict = NULL;
+    if (pAttrs->GetType() == PDFOBJ_DICTIONARY) {
+        pDict = (CPDF_Dictionary*)pAttrs;
+    } else if (pAttrs->GetType() == PDFOBJ_STREAM) {
+        pDict = ((CPDF_Stream*)pAttrs)->GetDict();
+    } else if (pAttrs->GetType() == PDFOBJ_ARRAY) {
+        CPDF_Array* pArray = (CPDF_Array*)pAttrs;
+        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {
+            CPDF_Object* pElement = pArray->GetElementValue(i);
+            pDict = FindAttrDict(pElement, owner, nLevel + 1);
+            if (pDict) {
+                return pDict;
+            }
+        }
+    }
+    if (pDict && pDict->GetString(FX_BSTRC("O")) == owner) {
+        return pDict;
+    }
+    return NULL;
+}
+CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, FX_FLOAT fLevel)
+{
+    if (fLevel > nMaxRecursion) {
+        return NULL;
+    }
+    if (bInheritable) {
+        CPDF_Object* pAttr = GetAttr(owner, name, FALSE);
+        if (pAttr) {
+            return pAttr;
+        }
+        if (m_pParent == NULL) {
+            return NULL;
+        }
+        return m_pParent->GetAttr(owner, name, TRUE, fLevel + 1);
+    }
+    CPDF_Object* pA = m_pDict->GetElementValue(FX_BSTRC("A"));
+    if (pA) {
+        CPDF_Dictionary* pAttrDict = FindAttrDict(pA, owner);
+        if (pAttrDict) {
+            CPDF_Object* pAttr = pAttrDict->GetElementValue(name);
+            if (pAttr) {
+                return pAttr;
+            }
+        }
+    }
+    CPDF_Object* pC = m_pDict->GetElementValue(FX_BSTRC("C"));
+    if (pC == NULL) {
+        return NULL;
+    }
+    CPDF_Dictionary* pClassMap = m_pTree->m_pTreeRoot->GetDict(FX_BSTRC("ClassMap"));
+    if (pClassMap == NULL) {
+        return NULL;
+    }
+    if (pC->GetType() == PDFOBJ_ARRAY) {
+        CPDF_Array* pArray = (CPDF_Array*)pC;
+        for (FX_DWORD i = 0; i < pArray->GetCount(); i ++) {
+            CFX_ByteString class_name = pArray->GetString(i);
+            CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);
+            if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {
+                return pClassDict->GetElementValue(name);
+            }
+        }
+        return NULL;
+    }
+    CFX_ByteString class_name = pC->GetString();
+    CPDF_Dictionary* pClassDict = pClassMap->GetDict(class_name);
+    if (pClassDict && pClassDict->GetString(FX_BSTRC("O")) == owner) {
+        return pClassDict->GetElementValue(name);
+    }
+    return NULL;
+}
+CPDF_Object* CPDF_StructElementImpl::GetAttr(FX_BSTR owner, FX_BSTR name, FX_BOOL bInheritable, int subindex)
+{
+    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable);
+    if (pAttr == NULL || subindex == -1 || pAttr->GetType() != PDFOBJ_ARRAY) {
+        return pAttr;
+    }
+    CPDF_Array* pArray = (CPDF_Array*)pAttr;
+    if (subindex >= (int)pArray->GetCount()) {
+        return pAttr;
+    }
+    return pArray->GetElementValue(subindex);
+}
+CFX_ByteString CPDF_StructElementImpl::GetName(FX_BSTR owner, FX_BSTR name, FX_BSTR default_value, FX_BOOL bInheritable, int subindex)
+{
+    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NAME) {
+        return default_value;
+    }
+    return pAttr->GetString();
+}
+FX_ARGB        CPDF_StructElementImpl::GetColor(FX_BSTR owner, FX_BSTR name, FX_ARGB default_value, FX_BOOL bInheritable, int subindex)
+{
+    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_ARRAY) {
+        return default_value;
+    }
+    CPDF_Array* pArray = (CPDF_Array*)pAttr;
+    return 0xff000000 | ((int)(pArray->GetNumber(0) * 255) << 16) | ((int)(pArray->GetNumber(1) * 255) << 8) | (int)(pArray->GetNumber(2) * 255);
+}
+FX_FLOAT CPDF_StructElementImpl::GetNumber(FX_BSTR owner, FX_BSTR name, FX_FLOAT default_value, FX_BOOL bInheritable, int subindex)
+{
+    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {
+        return default_value;
+    }
+    return pAttr->GetNumber();
+}
+int    CPDF_StructElementImpl::GetInteger(FX_BSTR owner, FX_BSTR name, int default_value, FX_BOOL bInheritable, int subindex)
+{
+    CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+    if (pAttr == NULL || pAttr->GetType() != PDFOBJ_NUMBER) {
+        return default_value;
+    }
+    return pAttr->GetInteger();
+}