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 "../../include/fpdfdoc/fpdf_doc.h" 8const int nMaxRecursion = 32; 9int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) 10{ 11 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { 12 return 0; 13 } 14 CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0); 15 if (pPage == NULL) { 16 return 0; 17 } 18 if (pPage->GetType() == PDFOBJ_NUMBER) { 19 return pPage->GetInteger(); 20 } 21 if (pPage->GetType() != PDFOBJ_DICTIONARY) { 22 return 0; 23 } 24 return pDoc->GetPageIndex(pPage->GetObjNum()); 25} 26FX_DWORD CPDF_Dest::GetPageObjNum() 27{ 28 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { 29 return 0; 30 } 31 CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0); 32 if (pPage == NULL) { 33 return 0; 34 } 35 if (pPage->GetType() == PDFOBJ_NUMBER) { 36 return pPage->GetInteger(); 37 } 38 if (pPage->GetType() == PDFOBJ_DICTIONARY) { 39 return pPage->GetObjNum(); 40 } 41 return 0; 42} 43const FX_CHAR* g_sZoomModes[] = {"XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV", ""}; 44int CPDF_Dest::GetZoomMode() 45{ 46 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { 47 return 0; 48 } 49 CFX_ByteString mode; 50 CPDF_Object* pObj = ((CPDF_Array*)m_pObj)->GetElementValue(1); 51 mode = pObj ? pObj->GetString() : CFX_ByteString(); 52 int i = 0; 53 while (g_sZoomModes[i][0] != '\0') { 54 if (mode == g_sZoomModes[i]) { 55 return i + 1; 56 } 57 i ++; 58 } 59 return 0; 60} 61FX_FLOAT CPDF_Dest::GetParam(int index) 62{ 63 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { 64 return 0; 65 } 66 return ((CPDF_Array*)m_pObj)->GetNumber(2 + index); 67} 68CFX_ByteString CPDF_Dest::GetRemoteName() 69{ 70 if (m_pObj == NULL) { 71 return CFX_ByteString(); 72 } 73 return m_pObj->GetString(); 74} 75CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, FX_BSTR category) 76{ 77 if (pDoc->GetRoot() && pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))) 78 m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category); 79 else 80 m_pRoot = NULL; 81} 82static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, const CFX_ByteString& csName, 83 int& nIndex, CPDF_Array** ppFind, int nLevel = 0) 84{ 85 if (nLevel > nMaxRecursion) { 86 return NULL; 87 } 88 CPDF_Array* pLimits = pNode->GetArray(FX_BSTRC("Limits")); 89 if (pLimits != NULL) { 90 CFX_ByteString csLeft = pLimits->GetString(0); 91 CFX_ByteString csRight = pLimits->GetString(1); 92 if (csLeft.Compare(csRight) > 0) { 93 CFX_ByteString csTmp = csRight; 94 csRight = csLeft; 95 csLeft = csTmp; 96 } 97 if (csName.Compare(csLeft) < 0 || csName.Compare(csRight) > 0) { 98 return NULL; 99 } 100 } 101 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); 102 if (pNames) { 103 FX_DWORD dwCount = pNames->GetCount() / 2; 104 for (FX_DWORD i = 0; i < dwCount; i ++) { 105 CFX_ByteString csValue = pNames->GetString(i * 2); 106 FX_INT32 iCompare = csValue.Compare(csName); 107 if (iCompare <= 0) { 108 if (ppFind != NULL) { 109 *ppFind = pNames; 110 } 111 if (iCompare < 0) { 112 continue; 113 } 114 } else { 115 break; 116 } 117 nIndex += i; 118 return pNames->GetElementValue(i * 2 + 1); 119 } 120 nIndex += dwCount; 121 return NULL; 122 } 123 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); 124 if (pKids == NULL) { 125 return NULL; 126 } 127 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { 128 CPDF_Dictionary* pKid = pKids->GetDict(i); 129 if (pKid == NULL) { 130 continue; 131 } 132 CPDF_Object* pFound = SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1); 133 if (pFound) { 134 return pFound; 135 } 136 } 137 return NULL; 138} 139static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, int nIndex, int& nCurIndex, 140 CFX_ByteString& csName, CPDF_Array** ppFind, int nLevel = 0) 141{ 142 if (nLevel > nMaxRecursion) { 143 return NULL; 144 } 145 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); 146 if (pNames) { 147 int nCount = pNames->GetCount() / 2; 148 if (nIndex >= nCurIndex + nCount) { 149 nCurIndex += nCount; 150 return NULL; 151 } else { 152 if (ppFind != NULL) { 153 *ppFind = pNames; 154 } 155 csName = pNames->GetString((nIndex - nCurIndex) * 2); 156 return pNames->GetElementValue((nIndex - nCurIndex) * 2 + 1); 157 } 158 } 159 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); 160 if (pKids == NULL) { 161 return NULL; 162 } 163 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { 164 CPDF_Dictionary* pKid = pKids->GetDict(i); 165 if (pKid == NULL) { 166 continue; 167 } 168 CPDF_Object* pFound = SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1); 169 if (pFound) { 170 return pFound; 171 } 172 } 173 return NULL; 174} 175static int CountNames(CPDF_Dictionary* pNode, int nLevel = 0) 176{ 177 if (nLevel > nMaxRecursion) { 178 return 0; 179 } 180 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); 181 if (pNames) { 182 return pNames->GetCount() / 2; 183 } 184 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); 185 if (pKids == NULL) { 186 return 0; 187 } 188 int nCount = 0; 189 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { 190 CPDF_Dictionary* pKid = pKids->GetDict(i); 191 if (pKid == NULL) { 192 continue; 193 } 194 nCount += CountNames(pKid, nLevel + 1); 195 } 196 return nCount; 197} 198int CPDF_NameTree::GetCount() const 199{ 200 if (m_pRoot == NULL) { 201 return 0; 202 } 203 return ::CountNames(m_pRoot); 204} 205int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const 206{ 207 if (m_pRoot == NULL) { 208 return -1; 209 } 210 int nIndex = 0; 211 if (SearchNameNode(m_pRoot, csName, nIndex, NULL) == NULL) { 212 return -1; 213 } 214 return nIndex; 215} 216CPDF_Object* CPDF_NameTree::LookupValue(int nIndex, CFX_ByteString& csName) const 217{ 218 if (m_pRoot == NULL) { 219 return NULL; 220 } 221 int nCurIndex = 0; 222 return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, NULL); 223} 224CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const 225{ 226 if (m_pRoot == NULL) { 227 return NULL; 228 } 229 int nIndex = 0; 230 return SearchNameNode(m_pRoot, csName, nIndex, NULL); 231} 232CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc, FX_BSTR sName) 233{ 234 CPDF_Object* pValue = LookupValue(sName); 235 if (pValue == NULL) { 236 CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDict(FX_BSTRC("Dests")); 237 if (pDests == NULL) { 238 return NULL; 239 } 240 pValue = pDests->GetElementValue(sName); 241 } 242 if (pValue == NULL) { 243 return NULL; 244 } 245 if (pValue->GetType() == PDFOBJ_ARRAY) { 246 return (CPDF_Array*)pValue; 247 } 248 if (pValue->GetType() == PDFOBJ_DICTIONARY) { 249 return ((CPDF_Dictionary*)pValue)->GetArray(FX_BSTRC("D")); 250 } 251 return NULL; 252} 253#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ 254static CFX_WideString ChangeSlashToPlatform(FX_LPCWSTR str) 255{ 256 CFX_WideString result; 257 while (*str) { 258 if (*str == '/') { 259#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ 260 result += ':'; 261#else 262 result += '\\'; 263#endif 264 } else { 265 result += *str; 266 } 267 str++; 268 } 269 return result; 270} 271static CFX_WideString ChangeSlashToPDF(FX_LPCWSTR str) 272{ 273 CFX_WideString result; 274 while (*str) { 275 if (*str == '\\' || *str == ':') { 276 result += '/'; 277 } else { 278 result += *str; 279 } 280 str++; 281 } 282 return result; 283} 284#endif 285static CFX_WideString FILESPEC_DecodeFileName(FX_WSTR filepath) 286{ 287 if (filepath.GetLength() <= 1) { 288 return CFX_WideString(); 289 } 290#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ 291 if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac")) { 292 return ChangeSlashToPlatform(filepath.GetPtr() + 1); 293 } 294 return ChangeSlashToPlatform(filepath.GetPtr()); 295#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ 296 if (filepath.GetAt(0) != '/') { 297 return ChangeSlashToPlatform(filepath.GetPtr()); 298 } 299 if (filepath.GetAt(1) == '/') { 300 return ChangeSlashToPlatform(filepath.GetPtr() + 1); 301 } 302 if (filepath.GetAt(2) == '/') { 303 CFX_WideString result; 304 result += filepath.GetAt(1); 305 result += ':'; 306 result += ChangeSlashToPlatform(filepath.GetPtr() + 2); 307 return result; 308 } 309 CFX_WideString result; 310 result += '\\'; 311 result += ChangeSlashToPlatform(filepath.GetPtr()); 312 return result; 313#else 314 return filepath; 315#endif 316} 317FX_BOOL CPDF_FileSpec::GetFileName(CFX_WideString &csFileName) const 318{ 319 if (m_pObj == NULL) { 320 return FALSE; 321 } 322 if (m_pObj->GetType() == PDFOBJ_DICTIONARY) { 323 CPDF_Dictionary* pDict = (CPDF_Dictionary*)m_pObj; 324 csFileName = pDict->GetUnicodeText(FX_BSTRC("UF")); 325 if (csFileName.IsEmpty()) { 326 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("F"))); 327 } 328 if (pDict->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL")) { 329 return TRUE; 330 } 331 if (csFileName.IsEmpty()) { 332 if (pDict->KeyExist(FX_BSTRC("DOS"))) { 333 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("DOS"))); 334 } else if (pDict->KeyExist(FX_BSTRC("Mac"))) { 335 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Mac"))); 336 } else if (pDict->KeyExist(FX_BSTRC("Unix"))) { 337 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Unix"))); 338 } else { 339 return FALSE; 340 } 341 } 342 } else { 343 csFileName = CFX_WideString::FromLocal(m_pObj->GetString()); 344 } 345 csFileName = FILESPEC_DecodeFileName(csFileName); 346 return TRUE; 347} 348CPDF_FileSpec::CPDF_FileSpec() 349{ 350 m_pObj = CPDF_Dictionary::Create(); 351 if (m_pObj != NULL) { 352 ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("Type"), FX_BSTRC("Filespec")); 353 } 354} 355FX_BOOL CPDF_FileSpec::IsURL() const 356{ 357 if (m_pObj == NULL) { 358 return FALSE; 359 } 360 if (m_pObj->GetType() != PDFOBJ_DICTIONARY) { 361 return FALSE; 362 } 363 return ((CPDF_Dictionary*)m_pObj)->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL"); 364} 365CFX_WideString FILESPEC_EncodeFileName(FX_WSTR filepath) 366{ 367 if (filepath.GetLength() <= 1) { 368 return CFX_WideString(); 369 } 370#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ 371 if (filepath.GetAt(1) == ':') { 372 CFX_WideString result; 373 result = '/'; 374 result += filepath.GetAt(0); 375 if (filepath.GetAt(2) != '\\') { 376 result += '/'; 377 } 378 result += ChangeSlashToPDF(filepath.GetPtr() + 2); 379 return result; 380 } 381 if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') { 382 return ChangeSlashToPDF(filepath.GetPtr() + 1); 383 } 384 if (filepath.GetAt(0) == '\\') { 385 CFX_WideString result; 386 result = '/'; 387 result += ChangeSlashToPDF(filepath.GetPtr()); 388 return result; 389 } 390 return ChangeSlashToPDF(filepath.GetPtr()); 391#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ 392 if (filepath.Left(sizeof("Mac") - 1) == FX_WSTRC(L"Mac")) { 393 CFX_WideString result; 394 result = '/'; 395 result += ChangeSlashToPDF(filepath.GetPtr()); 396 return result; 397 } 398 return ChangeSlashToPDF(filepath.GetPtr()); 399#else 400 return filepath; 401#endif 402} 403CPDF_Stream* CPDF_FileSpec::GetFileStream() const 404{ 405 if (m_pObj == NULL) { 406 return NULL; 407 } 408 FX_INT32 iType = m_pObj->GetType(); 409 if (iType == PDFOBJ_STREAM) { 410 return (CPDF_Stream*)m_pObj; 411 } else if (iType == PDFOBJ_DICTIONARY) { 412 CPDF_Dictionary *pEF = ((CPDF_Dictionary*)m_pObj)->GetDict(FX_BSTRC("EF")); 413 if (pEF == NULL) { 414 return NULL; 415 } 416 return pEF->GetStream(FX_BSTRC("F")); 417 } 418 return NULL; 419} 420static void FPDFDOC_FILESPEC_SetFileName(CPDF_Object *pObj, FX_WSTR wsFileName, FX_BOOL bURL) 421{ 422 ASSERT(pObj != NULL); 423 CFX_WideString wsStr; 424 if (bURL) { 425 wsStr = wsFileName; 426 } else { 427 wsStr = FILESPEC_EncodeFileName(wsFileName); 428 } 429 FX_INT32 iType = pObj->GetType(); 430 if (iType == PDFOBJ_STRING) { 431 pObj->SetString(CFX_ByteString::FromUnicode(wsStr)); 432 } else if (iType == PDFOBJ_DICTIONARY) { 433 CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj; 434 pDict->SetAtString(FX_BSTRC("F"), CFX_ByteString::FromUnicode(wsStr)); 435 pDict->SetAtString(FX_BSTRC("UF"), PDF_EncodeText(wsStr)); 436 } 437} 438void CPDF_FileSpec::SetFileName(FX_WSTR wsFileName, FX_BOOL bURL) 439{ 440 ASSERT(m_pObj != NULL); 441 if (m_pObj->GetType() == PDFOBJ_DICTIONARY && bURL) { 442 ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("FS"), "URL"); 443 } 444 FPDFDOC_FILESPEC_SetFileName(m_pObj, wsFileName, bURL); 445} 446static CFX_WideString _MakeRoman(int num) 447{ 448 const int arabic[] = { 449 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 450 }; 451 const CFX_WideString roman[] = { 452 L"m", L"cm", L"d", L"cd", L"c", L"xc", L"l", L"xl", L"x", L"ix", L"v", L"iv", L"i" 453 }; 454 const int nMaxNum = 1000000; 455 num %= nMaxNum; 456 int i = 0; 457 CFX_WideString wsRomanNumber; 458 while (num > 0) { 459 while (num >= arabic[i]) { 460 num = num - arabic[i]; 461 wsRomanNumber += roman[i]; 462 } 463 i = i + 1; 464 } 465 return wsRomanNumber; 466} 467static CFX_WideString _MakeLetters(int num) 468{ 469 if (num == 0) { 470 return CFX_WideString(); 471 } 472 CFX_WideString wsLetters; 473 const int nMaxCount = 1000; 474 const int nLetterCount = 26; 475 num -= 1; 476 int count = num / nLetterCount + 1; 477 count %= nMaxCount; 478 FX_WCHAR ch = L'a' + num % nLetterCount; 479 for (int i = 0; i < count; i++) { 480 wsLetters += ch; 481 } 482 return wsLetters; 483} 484static CFX_WideString _GetLabelNumPortion(int num, const CFX_ByteString& bsStyle) 485{ 486 CFX_WideString wsNumPortion; 487 if (bsStyle.IsEmpty()) { 488 return wsNumPortion; 489 } 490 if (bsStyle == "D") { 491 wsNumPortion.Format(L"%d", num); 492 } else if (bsStyle == "R") { 493 wsNumPortion = _MakeRoman(num); 494 wsNumPortion.MakeUpper(); 495 } else if (bsStyle == "r") { 496 wsNumPortion = _MakeRoman(num); 497 } else if (bsStyle == "A") { 498 wsNumPortion = _MakeLetters(num); 499 wsNumPortion.MakeUpper(); 500 } else if (bsStyle == "a") { 501 wsNumPortion = _MakeLetters(num); 502 } 503 return wsNumPortion; 504} 505CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const 506{ 507 CFX_WideString wsLabel; 508 if (m_pDocument == NULL) { 509 return wsLabel; 510 } 511 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); 512 if (pPDFRoot == NULL) { 513 return wsLabel; 514 } 515 CPDF_Dictionary* pLabels = pPDFRoot->GetDict(FX_BSTRC("PageLabels")); 516 CPDF_NumberTree numberTree(pLabels); 517 CPDF_Object* pValue = NULL; 518 int n = nPage; 519 while (n >= 0) { 520 pValue = numberTree.LookupValue(n); 521 if (pValue != NULL) { 522 break; 523 } 524 n--; 525 } 526 if (pValue != NULL) { 527 pValue = pValue->GetDirect(); 528 if (pValue->GetType() == PDFOBJ_DICTIONARY) { 529 CPDF_Dictionary* pLabel = (CPDF_Dictionary*)pValue; 530 if (pLabel->KeyExist(FX_BSTRC("P"))) { 531 wsLabel += pLabel->GetUnicodeText(FX_BSTRC("P")); 532 } 533 CFX_ByteString bsNumberingStyle = pLabel->GetString(FX_BSTRC("S"), NULL); 534 int nLabelNum = nPage - n + pLabel->GetInteger(FX_BSTRC("St"), 1); 535 CFX_WideString wsNumPortion = _GetLabelNumPortion(nLabelNum, bsNumberingStyle); 536 wsLabel += wsNumPortion; 537 return wsLabel; 538 } 539 } 540 wsLabel.Format(L"%d", nPage + 1); 541 return wsLabel; 542} 543FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_BSTR bsLabel) const 544{ 545 if (m_pDocument == NULL) { 546 return -1; 547 } 548 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); 549 if (pPDFRoot == NULL) { 550 return -1; 551 } 552 int nPages = m_pDocument->GetPageCount(); 553 CFX_ByteString bsLbl; 554 CFX_ByteString bsOrig = bsLabel; 555 for (int i = 0; i < nPages; i++) { 556 bsLbl = PDF_EncodeText(GetLabel(i)); 557 if (!bsLbl.Compare(bsOrig)) { 558 return i; 559 } 560 } 561 bsLbl = bsOrig; 562 int nPage = FXSYS_atoi(bsLbl); 563 if (nPage > 0 && nPage <= nPages) { 564 return nPage; 565 } 566 return -1; 567} 568FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const 569{ 570 CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr()); 571 return GetPageByLabel(bsLabel); 572} 573