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 = ((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} 59FX_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} 66CFX_ByteString CPDF_Dest::GetRemoteName() 67{ 68 if (m_pObj == NULL) { 69 return CFX_ByteString(); 70 } 71 return m_pObj->GetString(); 72} 73CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, FX_BSTR category) 74{ 75 m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category); 76} 77static 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} 134static 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} 170static 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} 193int CPDF_NameTree::GetCount() const 194{ 195 if (m_pRoot == NULL) { 196 return 0; 197 } 198 return ::CountNames(m_pRoot); 199} 200int 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} 211CPDF_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} 219CPDF_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} 227CPDF_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} 248static 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} 267static 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} 299FX_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} 330CPDF_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} 337FX_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} 347static 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} 360CFX_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} 398CPDF_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} 415static 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} 433void 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} 441static 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} 462static 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} 479static 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} 500CFX_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} 538FX_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} 563FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const 564{ 565 CFX_ByteString bsLabel = PDF_EncodeText((CFX_WideString)wsLabel); 566 return GetPageByLabel(bsLabel); 567} 568