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