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