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;
GetPageIndex(CPDF_Document * pDoc)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 }
GetPageObjNum()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", ""};
GetZoomMode()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 }
GetParam(int index)57 FX_FLOAT CPDF_Dest::GetParam(int index) {
58 CPDF_Array* pArray = ToArray(m_pObj);
59 return pArray ? pArray->GetNumber(2 + index) : 0;
60 }
GetRemoteName()61 CFX_ByteString CPDF_Dest::GetRemoteName() {
62 return m_pObj ? m_pObj->GetString() : CFX_ByteString();
63 }
CPDF_NameTree(CPDF_Document * pDoc,const CFX_ByteStringC & category)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 }
SearchNameNode(CPDF_Dictionary * pNode,const CFX_ByteString & csName,int & nIndex,CPDF_Array ** ppFind,int nLevel=0)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 }
SearchNameNode(CPDF_Dictionary * pNode,int nIndex,int & nCurIndex,CFX_ByteString & csName,CPDF_Array ** ppFind,int nLevel=0)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 }
CountNames(CPDF_Dictionary * pNode,int nLevel=0)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 }
GetCount() const192 int CPDF_NameTree::GetCount() const {
193 if (!m_pRoot) {
194 return 0;
195 }
196 return ::CountNames(m_pRoot);
197 }
GetIndex(const CFX_ByteString & csName) const198 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 }
LookupValue(int nIndex,CFX_ByteString & csName) const208 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 }
LookupValue(const CFX_ByteString & csName) const216 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 }
LookupNamedDest(CPDF_Document * pDoc,const CFX_ByteStringC & sName)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_
ChangeSlashToPlatform(const FX_WCHAR * str)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 }
ChangeSlashToPDF(const FX_WCHAR * str)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
FILESPEC_DecodeFileName(const CFX_WideStringC & filepath)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 }
GetFileName(CFX_WideString & csFileName) const302 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 }
CPDF_FileSpec()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 }
IsURL() const337 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 }
FILESPEC_EncodeFileName(const CFX_WideStringC & filepath)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 }
GetFileStream() const380 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 }
FPDFDOC_FILESPEC_SetFileName(CPDF_Object * pObj,const CFX_WideStringC & wsFileName,FX_BOOL bURL)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 }
SetFileName(const CFX_WideStringC & wsFileName,FX_BOOL bURL)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 }
_MakeRoman(int num)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 }
_MakeLetters(int num)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 }
_GetLabelNumPortion(int num,const CFX_ByteString & bsStyle)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 }
GetLabel(int nPage) const469 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 }
GetPageByLabel(const CFX_ByteStringC & bsLabel) const506 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 }
GetPageByLabel(const CFX_WideStringC & wsLabel) const530 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const {
531 CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr());
532 return GetPageByLabel(bsLabel);
533 }
534