• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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