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 = ((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 }
GetParam(int index)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 }
GetRemoteName()66 CFX_ByteString CPDF_Dest::GetRemoteName()
67 {
68 if (m_pObj == NULL) {
69 return CFX_ByteString();
70 }
71 return m_pObj->GetString();
72 }
CPDF_NameTree(CPDF_Document * pDoc,FX_BSTR category)73 CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, FX_BSTR category)
74 {
75 m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category);
76 }
SearchNameNode(CPDF_Dictionary * pNode,const CFX_ByteString & csName,int & nIndex,CPDF_Array ** ppFind,int nLevel=0)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 }
SearchNameNode(CPDF_Dictionary * pNode,int nIndex,int & nCurIndex,CFX_ByteString & csName,CPDF_Array ** ppFind,int nLevel=0)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 }
CountNames(CPDF_Dictionary * pNode,int nLevel=0)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 }
GetCount() const193 int CPDF_NameTree::GetCount() const
194 {
195 if (m_pRoot == NULL) {
196 return 0;
197 }
198 return ::CountNames(m_pRoot);
199 }
GetIndex(const CFX_ByteString & csName) const200 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 }
LookupValue(int nIndex,CFX_ByteString & csName) const211 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 }
LookupValue(const CFX_ByteString & csName) const219 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 }
LookupNamedDest(CPDF_Document * pDoc,FX_BSTR sName)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 }
ChangeSlashToPlatform(FX_LPCWSTR str)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 }
FILESPEC_DecodeFileName(FX_WSTR filepath)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 }
GetFileName(CFX_WideString & csFileName) const299 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 }
CPDF_FileSpec()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 }
IsURL() const337 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 }
ChangeSlashToPDF(FX_LPCWSTR str)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 }
FILESPEC_EncodeFileName(FX_WSTR filepath)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 }
GetFileStream() const398 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 }
FPDFDOC_FILESPEC_SetFileName(CPDF_Object * pObj,FX_WSTR wsFileName,FX_BOOL bURL)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 }
SetFileName(FX_WSTR wsFileName,FX_BOOL bURL)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 }
_MakeRoman(int num)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 }
_MakeLetters(int num)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 }
_GetLabelNumPortion(int num,const CFX_ByteString & bsStyle)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 }
GetLabel(int nPage) const500 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 }
GetPageByLabel(FX_BSTR bsLabel) const538 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 }
GetPageByLabel(FX_WSTR wsLabel) const563 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