Clean up CPDF_AnnotList.
[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)
384     return nullptr;
385   if (CPDF_Stream* pStream = m_pObj->AsStream())
386     return pStream;
387   if (CPDF_Dictionary* pEF = m_pObj->AsDictionary()->GetDict(FX_BSTRC("EF")))
388     return pEF->GetStream(FX_BSTRC("F"));
389   return nullptr;
390 }
391 static void FPDFDOC_FILESPEC_SetFileName(CPDF_Object* pObj,
392                                          const CFX_WideStringC& wsFileName,
393                                          FX_BOOL bURL) {
394   ASSERT(pObj != NULL);
395   CFX_WideString wsStr;
396   if (bURL) {
397     wsStr = wsFileName;
398   } else {
399     wsStr = FILESPEC_EncodeFileName(wsFileName);
400   }
401   if (pObj->IsString()) {
402     pObj->SetString(CFX_ByteString::FromUnicode(wsStr));
403   } else if (CPDF_Dictionary* pDict = pObj->AsDictionary()) {
404     pDict->SetAtString(FX_BSTRC("F"), CFX_ByteString::FromUnicode(wsStr));
405     pDict->SetAtString(FX_BSTRC("UF"), PDF_EncodeText(wsStr));
406   }
407 }
408 void CPDF_FileSpec::SetFileName(const CFX_WideStringC& wsFileName,
409                                 FX_BOOL bURL) {
410   ASSERT(m_pObj != NULL);
411   if (bURL) {
412     if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
413       pDict->SetAtName(FX_BSTRC("FS"), "URL");
414     }
415   }
416   FPDFDOC_FILESPEC_SetFileName(m_pObj, wsFileName, bURL);
417 }
418 static CFX_WideString _MakeRoman(int num) {
419   const int arabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
420   const CFX_WideString roman[] = {L"m",  L"cm", L"d",  L"cd", L"c",
421                                   L"xc", L"l",  L"xl", L"x",  L"ix",
422                                   L"v",  L"iv", L"i"};
423   const int nMaxNum = 1000000;
424   num %= nMaxNum;
425   int i = 0;
426   CFX_WideString wsRomanNumber;
427   while (num > 0) {
428     while (num >= arabic[i]) {
429       num = num - arabic[i];
430       wsRomanNumber += roman[i];
431     }
432     i = i + 1;
433   }
434   return wsRomanNumber;
435 }
436 static CFX_WideString _MakeLetters(int num) {
437   if (num == 0) {
438     return CFX_WideString();
439   }
440   CFX_WideString wsLetters;
441   const int nMaxCount = 1000;
442   const int nLetterCount = 26;
443   num -= 1;
444   int count = num / nLetterCount + 1;
445   count %= nMaxCount;
446   FX_WCHAR ch = L'a' + num % nLetterCount;
447   for (int i = 0; i < count; i++) {
448     wsLetters += ch;
449   }
450   return wsLetters;
451 }
452 static CFX_WideString _GetLabelNumPortion(int num,
453                                           const CFX_ByteString& bsStyle) {
454   CFX_WideString wsNumPortion;
455   if (bsStyle.IsEmpty()) {
456     return wsNumPortion;
457   }
458   if (bsStyle == "D") {
459     wsNumPortion.Format(L"%d", num);
460   } else if (bsStyle == "R") {
461     wsNumPortion = _MakeRoman(num);
462     wsNumPortion.MakeUpper();
463   } else if (bsStyle == "r") {
464     wsNumPortion = _MakeRoman(num);
465   } else if (bsStyle == "A") {
466     wsNumPortion = _MakeLetters(num);
467     wsNumPortion.MakeUpper();
468   } else if (bsStyle == "a") {
469     wsNumPortion = _MakeLetters(num);
470   }
471   return wsNumPortion;
472 }
473 CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const {
474   CFX_WideString wsLabel;
475   if (m_pDocument == NULL) {
476     return wsLabel;
477   }
478   CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
479   if (pPDFRoot == NULL) {
480     return wsLabel;
481   }
482   CPDF_Dictionary* pLabels = pPDFRoot->GetDict(FX_BSTRC("PageLabels"));
483   CPDF_NumberTree numberTree(pLabels);
484   CPDF_Object* pValue = NULL;
485   int n = nPage;
486   while (n >= 0) {
487     pValue = numberTree.LookupValue(n);
488     if (pValue != NULL) {
489       break;
490     }
491     n--;
492   }
493   if (pValue != NULL) {
494     pValue = pValue->GetDirect();
495     if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
496       if (pLabel->KeyExist(FX_BSTRC("P"))) {
497         wsLabel += pLabel->GetUnicodeText(FX_BSTRC("P"));
498       }
499       CFX_ByteString bsNumberingStyle = pLabel->GetString(FX_BSTRC("S"), NULL);
500       int nLabelNum = nPage - n + pLabel->GetInteger(FX_BSTRC("St"), 1);
501       CFX_WideString wsNumPortion =
502           _GetLabelNumPortion(nLabelNum, bsNumberingStyle);
503       wsLabel += wsNumPortion;
504       return wsLabel;
505     }
506   }
507   wsLabel.Format(L"%d", nPage + 1);
508   return wsLabel;
509 }
510 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_ByteStringC& bsLabel) const {
511   if (m_pDocument == NULL) {
512     return -1;
513   }
514   CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
515   if (pPDFRoot == NULL) {
516     return -1;
517   }
518   int nPages = m_pDocument->GetPageCount();
519   CFX_ByteString bsLbl;
520   CFX_ByteString bsOrig = bsLabel;
521   for (int i = 0; i < nPages; i++) {
522     bsLbl = PDF_EncodeText(GetLabel(i));
523     if (!bsLbl.Compare(bsOrig)) {
524       return i;
525     }
526   }
527   bsLbl = bsOrig;
528   int nPage = FXSYS_atoi(bsLbl);
529   if (nPage > 0 && nPage <= nPages) {
530     return nPage;
531   }
532   return -1;
533 }
534 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const {
535   CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr());
536   return GetPageByLabel(bsLabel);
537 }