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