Home | History | Annotate | Download | only in fpdfsdk
      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 #include "public/fpdf_doc.h"
      6 
      7 #include <memory>
      8 #include <vector>
      9 
     10 #include "core/fpdfapi/cpdf_modulemgr.h"
     11 #include "core/fpdfapi/parser/cpdf_array.h"
     12 #include "core/fpdfapi/parser/cpdf_document.h"
     13 #include "core/fpdfapi/parser/cpdf_name.h"
     14 #include "core/fpdfapi/parser/cpdf_null.h"
     15 #include "core/fpdfapi/parser/cpdf_number.h"
     16 #include "core/fpdfapi/parser/cpdf_parser.h"
     17 #include "core/fpdfapi/parser/cpdf_reference.h"
     18 #include "core/fpdfapi/parser/cpdf_string.h"
     19 #include "core/fpdfdoc/cpdf_dest.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 #include "testing/test_support.h"
     22 #include "third_party/base/ptr_util.h"
     23 
     24 #ifdef PDF_ENABLE_XFA
     25 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
     26 #endif  // PDF_ENABLE_XFA
     27 
     28 class CPDF_TestDocument : public CPDF_Document {
     29  public:
     30   CPDF_TestDocument() : CPDF_Document(nullptr) {}
     31 
     32   void SetRoot(CPDF_Dictionary* root) { m_pRootDict = root; }
     33   CPDF_IndirectObjectHolder* GetHolder() { return this; }
     34 };
     35 
     36 #ifdef PDF_ENABLE_XFA
     37 class CPDF_TestXFAContext : public CPDFXFA_Context {
     38  public:
     39   CPDF_TestXFAContext()
     40       : CPDFXFA_Context(pdfium::MakeUnique<CPDF_TestDocument>()) {}
     41 
     42   void SetRoot(CPDF_Dictionary* root) {
     43     reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
     44   }
     45 
     46   CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
     47 };
     48 using CPDF_TestPdfDocument = CPDF_TestXFAContext;
     49 #else   // PDF_ENABLE_XFA
     50 using CPDF_TestPdfDocument = CPDF_TestDocument;
     51 #endif  // PDF_ENABLE_XFA
     52 
     53 class PDFDocTest : public testing::Test {
     54  public:
     55   struct DictObjInfo {
     56     uint32_t num;
     57     CPDF_Dictionary* obj;
     58   };
     59 
     60   void SetUp() override {
     61     CPDF_ModuleMgr::Get()->Init();
     62 
     63     m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
     64     m_pIndirectObjs = m_pDoc->GetHolder();
     65 
     66     // Setup the root directory.
     67     m_pRootObj = pdfium::MakeUnique<CPDF_Dictionary>();
     68     m_pDoc->SetRoot(m_pRootObj.get());
     69   }
     70 
     71   void TearDown() override {
     72     m_pRootObj.reset();
     73     m_pIndirectObjs = nullptr;
     74     m_pDoc.reset();
     75     CPDF_ModuleMgr::Destroy();
     76   }
     77 
     78   std::vector<DictObjInfo> CreateDictObjs(int num) {
     79     std::vector<DictObjInfo> info;
     80     for (int i = 0; i < num; ++i) {
     81       // Objects created will be released by the document.
     82       CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
     83       info.push_back({obj->GetObjNum(), obj});
     84     }
     85     return info;
     86   }
     87 
     88  protected:
     89   std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
     90   UnownedPtr<CPDF_IndirectObjectHolder> m_pIndirectObjs;
     91   std::unique_ptr<CPDF_Dictionary> m_pRootObj;
     92 };
     93 
     94 TEST_F(PDFDocTest, FindBookmark) {
     95   {
     96     // No bookmark information.
     97     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
     98         GetFPDFWideString(L"");
     99     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    100 
    101     title = GetFPDFWideString(L"Preface");
    102     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    103   }
    104   {
    105     // Empty bookmark tree.
    106     m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
    107     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    108         GetFPDFWideString(L"");
    109     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    110 
    111     title = GetFPDFWideString(L"Preface");
    112     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    113   }
    114   {
    115     // Check on a regular bookmark tree.
    116     auto bookmarks = CreateDictObjs(3);
    117 
    118     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    119     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    120                                                 bookmarks[0].num);
    121     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
    122                                                 bookmarks[2].num);
    123 
    124     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    125     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    126                                                 bookmarks[0].num);
    127     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs.Get(),
    128                                                 bookmarks[1].num);
    129 
    130     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    131     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    132     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
    133                                                 bookmarks[1].num);
    134     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
    135                                                 bookmarks[2].num);
    136 
    137     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
    138                                           bookmarks[0].num);
    139 
    140     // Title with no match.
    141     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    142         GetFPDFWideString(L"Chapter 3");
    143     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    144 
    145     // Title with partial match only.
    146     title = GetFPDFWideString(L"Chapter");
    147     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    148 
    149     // Title with a match.
    150     title = GetFPDFWideString(L"Chapter 2");
    151     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    152 
    153     // Title match is case insensitive.
    154     title = GetFPDFWideString(L"cHaPter 2");
    155     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    156   }
    157   {
    158     // Circular bookmarks in depth.
    159     auto bookmarks = CreateDictObjs(3);
    160 
    161     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    162     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    163                                                 bookmarks[0].num);
    164     bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
    165                                                 bookmarks[2].num);
    166 
    167     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    168     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    169                                                 bookmarks[1].num);
    170     bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
    171                                                 bookmarks[1].num);
    172 
    173     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    174     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    175     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
    176                                                 bookmarks[1].num);
    177     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
    178                                                 bookmarks[2].num);
    179 
    180     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
    181                                           bookmarks[0].num);
    182 
    183     // Title with no match.
    184     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    185         GetFPDFWideString(L"Chapter 3");
    186     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    187 
    188     // Title with a match.
    189     title = GetFPDFWideString(L"Chapter 2");
    190     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    191   }
    192   {
    193     // Circular bookmarks in breadth.
    194     auto bookmarks = CreateDictObjs(4);
    195 
    196     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    197     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    198                                                 bookmarks[0].num);
    199     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
    200                                                 bookmarks[2].num);
    201 
    202     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    203     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    204                                                 bookmarks[0].num);
    205     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
    206                                                 bookmarks[3].num);
    207 
    208     bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
    209     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
    210                                                 bookmarks[0].num);
    211     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
    212                                                 bookmarks[1].num);
    213 
    214     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    215     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    216     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
    217                                                 bookmarks[1].num);
    218     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
    219                                                 bookmarks[2].num);
    220 
    221     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
    222                                           bookmarks[0].num);
    223 
    224     // Title with no match.
    225     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    226         GetFPDFWideString(L"Chapter 8");
    227     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    228 
    229     // Title with a match.
    230     title = GetFPDFWideString(L"Chapter 3");
    231     EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    232   }
    233 }
    234 
    235 TEST_F(PDFDocTest, GetLocationInPage) {
    236   auto array = pdfium::MakeUnique<CPDF_Array>();
    237   array->AddNew<CPDF_Number>(0);  // Page Index.
    238   array->AddNew<CPDF_Name>("XYZ");
    239   array->AddNew<CPDF_Number>(4);  // X
    240   array->AddNew<CPDF_Number>(5);  // Y
    241   array->AddNew<CPDF_Number>(6);  // Zoom.
    242 
    243   FPDF_BOOL hasX;
    244   FPDF_BOOL hasY;
    245   FPDF_BOOL hasZoom;
    246   FS_FLOAT x;
    247   FS_FLOAT y;
    248   FS_FLOAT zoom;
    249 
    250   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    251                                          &x, &y, &zoom));
    252   EXPECT_TRUE(hasX);
    253   EXPECT_TRUE(hasY);
    254   EXPECT_TRUE(hasZoom);
    255   EXPECT_EQ(4, x);
    256   EXPECT_EQ(5, y);
    257   EXPECT_EQ(6, zoom);
    258 
    259   array->SetNewAt<CPDF_Null>(2);
    260   array->SetNewAt<CPDF_Null>(3);
    261   array->SetNewAt<CPDF_Null>(4);
    262   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    263                                          &x, &y, &zoom));
    264   EXPECT_FALSE(hasX);
    265   EXPECT_FALSE(hasY);
    266   EXPECT_FALSE(hasZoom);
    267 
    268   array = pdfium::MakeUnique<CPDF_Array>();
    269   EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    270                                           &x, &y, &zoom));
    271 }
    272