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