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