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