Home | History | Annotate | Download | only in fpdfsdk
      1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "public/fpdf_doc.h"
      8 
      9 #include <memory>
     10 #include <set>
     11 
     12 #include "core/fpdfapi/page/cpdf_page.h"
     13 #include "core/fpdfapi/parser/cpdf_array.h"
     14 #include "core/fpdfapi/parser/cpdf_document.h"
     15 #include "core/fpdfdoc/cpdf_bookmark.h"
     16 #include "core/fpdfdoc/cpdf_bookmarktree.h"
     17 #include "core/fpdfdoc/cpdf_dest.h"
     18 #include "core/fpdfdoc/cpdf_pagelabel.h"
     19 #include "fpdfsdk/fsdk_define.h"
     20 #include "third_party/base/ptr_util.h"
     21 #include "third_party/base/stl_util.h"
     22 
     23 namespace {
     24 
     25 CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree,
     26                            CPDF_Bookmark bookmark,
     27                            const WideString& title,
     28                            std::set<CPDF_Dictionary*>* visited) {
     29   // Return if already checked to avoid circular calling.
     30   if (pdfium::ContainsKey(*visited, bookmark.GetDict()))
     31     return CPDF_Bookmark();
     32   visited->insert(bookmark.GetDict());
     33 
     34   if (bookmark.GetDict() &&
     35       bookmark.GetTitle().CompareNoCase(title.c_str()) == 0) {
     36     // First check this item.
     37     return bookmark;
     38   }
     39 
     40   // Go into children items.
     41   CPDF_Bookmark child = tree.GetFirstChild(bookmark);
     42   while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) {
     43     // Check this item and its children.
     44     CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
     45     if (found.GetDict())
     46       return found;
     47     child = tree.GetNextSibling(child);
     48   }
     49   return CPDF_Bookmark();
     50 }
     51 
     52 CPDF_LinkList* GetLinkList(CPDF_Page* page) {
     53   if (!page)
     54     return nullptr;
     55 
     56   CPDF_Document* pDoc = page->m_pDocument.Get();
     57   std::unique_ptr<CPDF_LinkList>* pHolder = pDoc->LinksContext();
     58   if (!pHolder->get())
     59     *pHolder = pdfium::MakeUnique<CPDF_LinkList>();
     60   return pHolder->get();
     61 }
     62 
     63 }  // namespace
     64 
     65 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
     66 FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
     67   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
     68   if (!pDoc)
     69     return nullptr;
     70   CPDF_BookmarkTree tree(pDoc);
     71   CPDF_Bookmark bookmark =
     72       CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
     73   return tree.GetFirstChild(bookmark).GetDict();
     74 }
     75 
     76 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
     77 FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
     78   if (!pDict)
     79     return nullptr;
     80   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
     81   if (!pDoc)
     82     return nullptr;
     83   CPDF_BookmarkTree tree(pDoc);
     84   CPDF_Bookmark bookmark =
     85       CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
     86   return tree.GetNextSibling(bookmark).GetDict();
     87 }
     88 
     89 FPDF_EXPORT unsigned long FPDF_CALLCONV
     90 FPDFBookmark_GetTitle(FPDF_BOOKMARK pDict, void* buffer, unsigned long buflen) {
     91   if (!pDict)
     92     return 0;
     93   CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
     94   WideString title = bookmark.GetTitle();
     95   return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
     96 }
     97 
     98 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
     99 FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
    100   if (!title || title[0] == 0)
    101     return nullptr;
    102   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    103   if (!pDoc)
    104     return nullptr;
    105   CPDF_BookmarkTree tree(pDoc);
    106   size_t len = WideString::WStringLength(title);
    107   WideString encodedTitle = WideString::FromUTF16LE(title, len);
    108   std::set<CPDF_Dictionary*> visited;
    109   return FindBookmark(tree, CPDF_Bookmark(), encodedTitle, &visited).GetDict();
    110 }
    111 
    112 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFBookmark_GetDest(FPDF_DOCUMENT document,
    113                                                          FPDF_BOOKMARK pDict) {
    114   if (!pDict)
    115     return nullptr;
    116   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    117   if (!pDoc)
    118     return nullptr;
    119   CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    120   CPDF_Dest dest = bookmark.GetDest(pDoc);
    121   if (dest.GetObject())
    122     return dest.GetObject();
    123   // If this bookmark is not directly associated with a dest, we try to get
    124   // action
    125   CPDF_Action action = bookmark.GetAction();
    126   if (!action.GetDict())
    127     return nullptr;
    128   return action.GetDest(pDoc).GetObject();
    129 }
    130 
    131 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
    132 FPDFBookmark_GetAction(FPDF_BOOKMARK pDict) {
    133   if (!pDict)
    134     return nullptr;
    135   CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    136   return bookmark.GetAction().GetDict();
    137 }
    138 
    139 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION pDict) {
    140   if (!pDict)
    141     return PDFACTION_UNSUPPORTED;
    142 
    143   CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    144   CPDF_Action::ActionType type = action.GetType();
    145   switch (type) {
    146     case CPDF_Action::GoTo:
    147       return PDFACTION_GOTO;
    148     case CPDF_Action::GoToR:
    149       return PDFACTION_REMOTEGOTO;
    150     case CPDF_Action::URI:
    151       return PDFACTION_URI;
    152     case CPDF_Action::Launch:
    153       return PDFACTION_LAUNCH;
    154     default:
    155       return PDFACTION_UNSUPPORTED;
    156   }
    157 }
    158 
    159 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
    160                                                        FPDF_ACTION pDict) {
    161   if (!pDict)
    162     return nullptr;
    163   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    164   if (!pDoc)
    165     return nullptr;
    166   CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    167   return action.GetDest(pDoc).GetObject();
    168 }
    169 
    170 FPDF_EXPORT unsigned long FPDF_CALLCONV
    171 FPDFAction_GetFilePath(FPDF_ACTION pDict, void* buffer, unsigned long buflen) {
    172   unsigned long type = FPDFAction_GetType(pDict);
    173   if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH)
    174     return 0;
    175 
    176   CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    177   ByteString path = action.GetFilePath().UTF8Encode();
    178   unsigned long len = path.GetLength() + 1;
    179   if (buffer && len <= buflen)
    180     memcpy(buffer, path.c_str(), len);
    181   return len;
    182 }
    183 
    184 FPDF_EXPORT unsigned long FPDF_CALLCONV
    185 FPDFAction_GetURIPath(FPDF_DOCUMENT document,
    186                       FPDF_ACTION pDict,
    187                       void* buffer,
    188                       unsigned long buflen) {
    189   if (!pDict)
    190     return 0;
    191   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    192   if (!pDoc)
    193     return 0;
    194   CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    195   ByteString path = action.GetURI(pDoc);
    196   unsigned long len = path.GetLength() + 1;
    197   if (buffer && len <= buflen)
    198     memcpy(buffer, path.c_str(), len);
    199   return len;
    200 }
    201 
    202 FPDF_EXPORT unsigned long FPDF_CALLCONV
    203 FPDFDest_GetPageIndex(FPDF_DOCUMENT document, FPDF_DEST pDict) {
    204   if (!pDict)
    205     return 0;
    206   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    207   if (!pDoc)
    208     return 0;
    209   CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
    210   return dest.GetPageIndex(pDoc);
    211 }
    212 
    213 FPDF_EXPORT unsigned long FPDF_CALLCONV
    214 FPDFDest_GetView(FPDF_DEST pDict,
    215                  unsigned long* pNumParams,
    216                  FS_FLOAT* pParams) {
    217   if (!pDict) {
    218     *pNumParams = 0;
    219     return 0;
    220   }
    221 
    222   CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
    223   unsigned long nParams = dest.GetNumParams();
    224   ASSERT(nParams <= 4);
    225   *pNumParams = nParams;
    226   for (unsigned long i = 0; i < nParams; ++i)
    227     pParams[i] = dest.GetParam(i);
    228   return dest.GetZoomMode();
    229 }
    230 
    231 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    232 FPDFDest_GetLocationInPage(FPDF_DEST pDict,
    233                            FPDF_BOOL* hasXVal,
    234                            FPDF_BOOL* hasYVal,
    235                            FPDF_BOOL* hasZoomVal,
    236                            FS_FLOAT* x,
    237                            FS_FLOAT* y,
    238                            FS_FLOAT* zoom) {
    239   if (!pDict)
    240     return false;
    241 
    242   auto dest = pdfium::MakeUnique<CPDF_Dest>(static_cast<CPDF_Object*>(pDict));
    243 
    244   // FPDF_BOOL is an int, GetXYZ expects bools.
    245   bool bHasX;
    246   bool bHasY;
    247   bool bHasZoom;
    248   if (!dest->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
    249     return false;
    250 
    251   *hasXVal = bHasX;
    252   *hasYVal = bHasY;
    253   *hasZoomVal = bHasZoom;
    254   return true;
    255 }
    256 
    257 FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
    258                                                             double x,
    259                                                             double y) {
    260   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    261   if (!pPage)
    262     return nullptr;
    263 
    264   CPDF_LinkList* pLinkList = GetLinkList(pPage);
    265   if (!pLinkList)
    266     return nullptr;
    267 
    268   return pLinkList
    269       ->GetLinkAtPoint(pPage,
    270                        CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
    271                        nullptr)
    272       .GetDict();
    273 }
    274 
    275 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
    276                                                             double x,
    277                                                             double y) {
    278   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    279   if (!pPage)
    280     return -1;
    281 
    282   CPDF_LinkList* pLinkList = GetLinkList(pPage);
    283   if (!pLinkList)
    284     return -1;
    285 
    286   int z_order = -1;
    287   pLinkList->GetLinkAtPoint(
    288       pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
    289       &z_order);
    290   return z_order;
    291 }
    292 
    293 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
    294                                                      FPDF_LINK pDict) {
    295   if (!pDict)
    296     return nullptr;
    297   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    298   if (!pDoc)
    299     return nullptr;
    300   CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    301   FPDF_DEST dest = link.GetDest(pDoc).GetObject();
    302   if (dest)
    303     return dest;
    304   // If this link is not directly associated with a dest, we try to get action
    305   CPDF_Action action = link.GetAction();
    306   if (!action.GetDict())
    307     return nullptr;
    308   return action.GetDest(pDoc).GetObject();
    309 }
    310 
    311 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK pDict) {
    312   if (!pDict)
    313     return nullptr;
    314 
    315   CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
    316   return link.GetAction().GetDict();
    317 }
    318 
    319 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
    320                                                        int* startPos,
    321                                                        FPDF_LINK* linkAnnot) {
    322   if (!startPos || !linkAnnot)
    323     return false;
    324   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    325   if (!pPage || !pPage->m_pFormDict)
    326     return false;
    327   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
    328   if (!pAnnots)
    329     return false;
    330   for (size_t i = *startPos; i < pAnnots->GetCount(); i++) {
    331     CPDF_Dictionary* pDict =
    332         ToDictionary(static_cast<CPDF_Object*>(pAnnots->GetDirectObjectAt(i)));
    333     if (!pDict)
    334       continue;
    335     if (pDict->GetStringFor("Subtype") == "Link") {
    336       *startPos = static_cast<int>(i + 1);
    337       *linkAnnot = static_cast<FPDF_LINK>(pDict);
    338       return true;
    339     }
    340   }
    341   return false;
    342 }
    343 
    344 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK linkAnnot,
    345                                                           FS_RECTF* rect) {
    346   if (!linkAnnot || !rect)
    347     return false;
    348   CPDF_Dictionary* pAnnotDict =
    349       ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
    350   FSRECTFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"), rect);
    351   return true;
    352 }
    353 
    354 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK linkAnnot) {
    355   if (!linkAnnot)
    356     return 0;
    357   CPDF_Dictionary* pAnnotDict =
    358       ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
    359   CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
    360   if (!pArray)
    361     return 0;
    362   return static_cast<int>(pArray->GetCount() / 8);
    363 }
    364 
    365 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    366 FPDFLink_GetQuadPoints(FPDF_LINK linkAnnot,
    367                        int quadIndex,
    368                        FS_QUADPOINTSF* quadPoints) {
    369   if (!linkAnnot || !quadPoints)
    370     return false;
    371   CPDF_Dictionary* pAnnotDict =
    372       ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
    373   CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
    374   if (!pArray)
    375     return false;
    376 
    377   if (quadIndex < 0 ||
    378       static_cast<size_t>(quadIndex) >= pArray->GetCount() / 8 ||
    379       (static_cast<size_t>(quadIndex * 8 + 7) >= pArray->GetCount())) {
    380     return false;
    381   }
    382 
    383   quadPoints->x1 = pArray->GetNumberAt(quadIndex * 8);
    384   quadPoints->y1 = pArray->GetNumberAt(quadIndex * 8 + 1);
    385   quadPoints->x2 = pArray->GetNumberAt(quadIndex * 8 + 2);
    386   quadPoints->y2 = pArray->GetNumberAt(quadIndex * 8 + 3);
    387   quadPoints->x3 = pArray->GetNumberAt(quadIndex * 8 + 4);
    388   quadPoints->y3 = pArray->GetNumberAt(quadIndex * 8 + 5);
    389   quadPoints->x4 = pArray->GetNumberAt(quadIndex * 8 + 6);
    390   quadPoints->y4 = pArray->GetNumberAt(quadIndex * 8 + 7);
    391   return true;
    392 }
    393 
    394 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
    395                                                          FPDF_BYTESTRING tag,
    396                                                          void* buffer,
    397                                                          unsigned long buflen) {
    398   if (!tag)
    399     return 0;
    400   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    401   if (!pDoc)
    402     return 0;
    403   pDoc->LoadDocumentInfo();
    404   const CPDF_Dictionary* pInfo = pDoc->GetInfo();
    405   if (!pInfo)
    406     return 0;
    407   WideString text = pInfo->GetUnicodeTextFor(tag);
    408   return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
    409 }
    410 
    411 FPDF_EXPORT unsigned long FPDF_CALLCONV
    412 FPDF_GetPageLabel(FPDF_DOCUMENT document,
    413                   int page_index,
    414                   void* buffer,
    415                   unsigned long buflen) {
    416   if (page_index < 0)
    417     return 0;
    418 
    419   // CPDF_PageLabel can deal with NULL |document|.
    420   CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
    421   Optional<WideString> str = label.GetLabel(page_index);
    422   return str.has_value()
    423              ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
    424              : 0;
    425 }
    426