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