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     // We don't need page module or render module, but
     62     // initialize them to keep the code sane.
     63     CPDF_ModuleMgr* module_mgr = CPDF_ModuleMgr::Get();
     64     module_mgr->InitPageModule();
     65 
     66     m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
     67     m_pIndirectObjs = m_pDoc->GetHolder();
     68 
     69     // Setup the root directory.
     70     m_pRootObj = pdfium::MakeUnique<CPDF_Dictionary>();
     71     m_pDoc->SetRoot(m_pRootObj.get());
     72   }
     73 
     74   void TearDown() override {
     75     m_pRootObj.reset();
     76     m_pIndirectObjs = nullptr;
     77     m_pDoc.reset();
     78     CPDF_ModuleMgr::Destroy();
     79   }
     80 
     81   std::vector<DictObjInfo> CreateDictObjs(int num) {
     82     std::vector<DictObjInfo> info;
     83     for (int i = 0; i < num; ++i) {
     84       // Objects created will be released by the document.
     85       CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
     86       info.push_back({obj->GetObjNum(), obj});
     87     }
     88     return info;
     89   }
     90 
     91  protected:
     92   std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
     93   CPDF_IndirectObjectHolder* m_pIndirectObjs;
     94   std::unique_ptr<CPDF_Dictionary> m_pRootObj;
     95 };
     96 
     97 TEST_F(PDFDocTest, FindBookmark) {
     98   {
     99     // No bookmark information.
    100     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    101         GetFPDFWideString(L"");
    102     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    103 
    104     title = GetFPDFWideString(L"Preface");
    105     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    106   }
    107   {
    108     // Empty bookmark tree.
    109     m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
    110     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    111         GetFPDFWideString(L"");
    112     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    113 
    114     title = GetFPDFWideString(L"Preface");
    115     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    116   }
    117   {
    118     // Check on a regular bookmark tree.
    119     auto bookmarks = CreateDictObjs(3);
    120 
    121     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    122     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    123                                                 bookmarks[0].num);
    124     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
    125                                                 bookmarks[2].num);
    126 
    127     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    128     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    129                                                 bookmarks[0].num);
    130     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs,
    131                                                 bookmarks[1].num);
    132 
    133     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    134     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    135     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
    136                                                 bookmarks[1].num);
    137     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
    138                                                 bookmarks[2].num);
    139 
    140     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
    141                                           bookmarks[0].num);
    142 
    143     // Title with no match.
    144     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    145         GetFPDFWideString(L"Chapter 3");
    146     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    147 
    148     // Title with partial match only.
    149     title = GetFPDFWideString(L"Chapter");
    150     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    151 
    152     // Title with a match.
    153     title = GetFPDFWideString(L"Chapter 2");
    154     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    155 
    156     // Title match is case insensitive.
    157     title = GetFPDFWideString(L"cHaPter 2");
    158     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    159   }
    160   {
    161     // Circular bookmarks in depth.
    162     auto bookmarks = CreateDictObjs(3);
    163 
    164     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    165     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    166                                                 bookmarks[0].num);
    167     bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
    168                                                 bookmarks[2].num);
    169 
    170     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    171     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    172                                                 bookmarks[1].num);
    173     bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
    174                                                 bookmarks[1].num);
    175 
    176     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    177     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    178     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
    179                                                 bookmarks[1].num);
    180     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
    181                                                 bookmarks[2].num);
    182 
    183     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
    184                                           bookmarks[0].num);
    185 
    186     // Title with no match.
    187     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    188         GetFPDFWideString(L"Chapter 3");
    189     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    190 
    191     // Title with a match.
    192     title = GetFPDFWideString(L"Chapter 2");
    193     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    194   }
    195   {
    196     // Circular bookmarks in breadth.
    197     auto bookmarks = CreateDictObjs(4);
    198 
    199     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
    200     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    201                                                 bookmarks[0].num);
    202     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
    203                                                 bookmarks[2].num);
    204 
    205     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
    206     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    207                                                 bookmarks[0].num);
    208     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
    209                                                 bookmarks[3].num);
    210 
    211     bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
    212     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
    213                                                 bookmarks[0].num);
    214     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
    215                                                 bookmarks[1].num);
    216 
    217     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
    218     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
    219     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
    220                                                 bookmarks[1].num);
    221     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
    222                                                 bookmarks[2].num);
    223 
    224     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
    225                                           bookmarks[0].num);
    226 
    227     // Title with no match.
    228     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
    229         GetFPDFWideString(L"Chapter 8");
    230     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    231 
    232     // Title with a match.
    233     title = GetFPDFWideString(L"Chapter 3");
    234     EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
    235   }
    236 }
    237 
    238 TEST_F(PDFDocTest, GetLocationInPage) {
    239   auto array = pdfium::MakeUnique<CPDF_Array>();
    240   array->AddNew<CPDF_Number>(0);  // Page Index.
    241   array->AddNew<CPDF_Name>("XYZ");
    242   array->AddNew<CPDF_Number>(4);  // X
    243   array->AddNew<CPDF_Number>(5);  // Y
    244   array->AddNew<CPDF_Number>(6);  // Zoom.
    245 
    246   FPDF_BOOL hasX;
    247   FPDF_BOOL hasY;
    248   FPDF_BOOL hasZoom;
    249   FS_FLOAT x;
    250   FS_FLOAT y;
    251   FS_FLOAT zoom;
    252 
    253   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    254                                          &x, &y, &zoom));
    255   EXPECT_TRUE(hasX);
    256   EXPECT_TRUE(hasY);
    257   EXPECT_TRUE(hasZoom);
    258   EXPECT_EQ(4, x);
    259   EXPECT_EQ(5, y);
    260   EXPECT_EQ(6, zoom);
    261 
    262   array->SetNewAt<CPDF_Null>(2);
    263   array->SetNewAt<CPDF_Null>(3);
    264   array->SetNewAt<CPDF_Null>(4);
    265   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    266                                          &x, &y, &zoom));
    267   EXPECT_FALSE(hasX);
    268   EXPECT_FALSE(hasY);
    269   EXPECT_FALSE(hasZoom);
    270 
    271   array = pdfium::MakeUnique<CPDF_Array>();
    272   EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
    273                                           &x, &y, &zoom));
    274 }
    275