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;
GetPageIndex(CPDF_Document * pDoc)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 }
GetPageObjNum()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", ""};
GetZoomMode()44 int 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 }
GetParam(int index)61 FX_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 }
GetRemoteName()68 CFX_ByteString CPDF_Dest::GetRemoteName()
69 {
70 if (m_pObj == NULL) {
71 return CFX_ByteString();
72 }
73 return m_pObj->GetString();
74 }
CPDF_NameTree(CPDF_Document * pDoc,FX_BSTR category)75 CPDF_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 }
SearchNameNode(CPDF_Dictionary * pNode,const CFX_ByteString & csName,int & nIndex,CPDF_Array ** ppFind,int nLevel=0)82 static 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 }
SearchNameNode(CPDF_Dictionary * pNode,int nIndex,int & nCurIndex,CFX_ByteString & csName,CPDF_Array ** ppFind,int nLevel=0)139 static 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 }
CountNames(CPDF_Dictionary * pNode,int nLevel=0)175 static 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 }
GetCount() const198 int CPDF_NameTree::GetCount() const
199 {
200 if (m_pRoot == NULL) {
201 return 0;
202 }
203 return ::CountNames(m_pRoot);
204 }
GetIndex(const CFX_ByteString & csName) const205 int 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 }
LookupValue(int nIndex,CFX_ByteString & csName) const216 CPDF_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 }
LookupValue(const CFX_ByteString & csName) const224 CPDF_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 }
LookupNamedDest(CPDF_Document * pDoc,FX_BSTR sName)232 CPDF_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_
ChangeSlashToPlatform(FX_LPCWSTR str)254 static 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 }
ChangeSlashToPDF(FX_LPCWSTR str)271 static 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
FILESPEC_DecodeFileName(FX_WSTR filepath)285 static 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 }
GetFileName(CFX_WideString & csFileName) const317 FX_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 }
CPDF_FileSpec()348 CPDF_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 }
IsURL() const355 FX_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 }
FILESPEC_EncodeFileName(FX_WSTR filepath)365 CFX_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 }
GetFileStream() const403 CPDF_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 }
FPDFDOC_FILESPEC_SetFileName(CPDF_Object * pObj,FX_WSTR wsFileName,FX_BOOL bURL)420 static 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 }
SetFileName(FX_WSTR wsFileName,FX_BOOL bURL)438 void 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 }
_MakeRoman(int num)446 static 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 }
_MakeLetters(int num)467 static 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 }
_GetLabelNumPortion(int num,const CFX_ByteString & bsStyle)484 static 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 }
GetLabel(int nPage) const505 CFX_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 }
GetPageByLabel(FX_BSTR bsLabel) const543 FX_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 }
GetPageByLabel(FX_WSTR wsLabel) const568 FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const
569 {
570 CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr());
571 return GetPageByLabel(bsLabel);
572 }
573