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.
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
7 #include "../../include/fpdfdoc/fpdf_doc.h"
8 const int nMaxRecursion = 32;
9 int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc)
11 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) {
14 CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0);
18 if (pPage->GetType() == PDFOBJ_NUMBER) {
19 return pPage->GetInteger();
21 if (pPage->GetType() != PDFOBJ_DICTIONARY) {
24 return pDoc->GetPageIndex(pPage->GetObjNum());
26 FX_DWORD CPDF_Dest::GetPageObjNum()
28 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) {
31 CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0);
35 if (pPage->GetType() == PDFOBJ_NUMBER) {
36 return pPage->GetInteger();
38 if (pPage->GetType() == PDFOBJ_DICTIONARY) {
39 return pPage->GetObjNum();
43 const FX_CHAR* g_sZoomModes[] = {"XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV", ""};
44 int CPDF_Dest::GetZoomMode()
46 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) {
50 CPDF_Object* pObj = ((CPDF_Array*)m_pObj)->GetElementValue(1);
51 mode = pObj ? pObj->GetString() : CFX_ByteString();
53 while (g_sZoomModes[i][0] != '\0') {
54 if (mode == g_sZoomModes[i]) {
61 FX_FLOAT CPDF_Dest::GetParam(int index)
63 if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) {
66 return ((CPDF_Array*)m_pObj)->GetNumber(2 + index);
68 CFX_ByteString CPDF_Dest::GetRemoteName()
71 return CFX_ByteString();
73 return m_pObj->GetString();
75 CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, FX_BSTR category)
77 if (pDoc->GetRoot() && pDoc->GetRoot()->GetDict(FX_BSTRC("Names")))
78 m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category);
82 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, const CFX_ByteString& csName,
83 int& nIndex, CPDF_Array** ppFind, int nLevel = 0)
85 if (nLevel > nMaxRecursion) {
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;
97 if (csName.Compare(csLeft) < 0 || csName.Compare(csRight) > 0) {
101 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names"));
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);
108 if (ppFind != NULL) {
118 return pNames->GetElementValue(i * 2 + 1);
123 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids"));
127 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) {
128 CPDF_Dictionary* pKid = pKids->GetDict(i);
132 CPDF_Object* pFound = SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1);
139 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, int nIndex, int& nCurIndex,
140 CFX_ByteString& csName, CPDF_Array** ppFind, int nLevel = 0)
142 if (nLevel > nMaxRecursion) {
145 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names"));
147 int nCount = pNames->GetCount() / 2;
148 if (nIndex >= nCurIndex + nCount) {
152 if (ppFind != NULL) {
155 csName = pNames->GetString((nIndex - nCurIndex) * 2);
156 return pNames->GetElementValue((nIndex - nCurIndex) * 2 + 1);
159 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids"));
163 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) {
164 CPDF_Dictionary* pKid = pKids->GetDict(i);
168 CPDF_Object* pFound = SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1);
175 static int CountNames(CPDF_Dictionary* pNode, int nLevel = 0)
177 if (nLevel > nMaxRecursion) {
180 CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names"));
182 return pNames->GetCount() / 2;
184 CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids"));
189 for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) {
190 CPDF_Dictionary* pKid = pKids->GetDict(i);
194 nCount += CountNames(pKid, nLevel + 1);
198 int CPDF_NameTree::GetCount() const
200 if (m_pRoot == NULL) {
203 return ::CountNames(m_pRoot);
205 int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const
207 if (m_pRoot == NULL) {
211 if (SearchNameNode(m_pRoot, csName, nIndex, NULL) == NULL) {
216 CPDF_Object* CPDF_NameTree::LookupValue(int nIndex, CFX_ByteString& csName) const
218 if (m_pRoot == NULL) {
222 return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, NULL);
224 CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const
226 if (m_pRoot == NULL) {
230 return SearchNameNode(m_pRoot, csName, nIndex, NULL);
232 CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc, FX_BSTR sName)
234 CPDF_Object* pValue = LookupValue(sName);
235 if (pValue == NULL) {
236 CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDict(FX_BSTRC("Dests"));
237 if (pDests == NULL) {
240 pValue = pDests->GetElementValue(sName);
242 if (pValue == NULL) {
245 if (pValue->GetType() == PDFOBJ_ARRAY) {
246 return (CPDF_Array*)pValue;
248 if (pValue->GetType() == PDFOBJ_DICTIONARY) {
249 return ((CPDF_Dictionary*)pValue)->GetArray(FX_BSTRC("D"));
253 static CFX_WideString ChangeSlashToPlatform(FX_LPCWSTR str)
255 CFX_WideString result;
258 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
260 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
272 static CFX_WideString FILESPEC_DecodeFileName(FX_WSTR filepath)
274 if (filepath.GetLength() <= 1) {
275 return CFX_WideString();
277 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
278 if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac")) {
279 return ChangeSlashToPlatform(filepath.GetPtr() + 1);
281 return ChangeSlashToPlatform(filepath.GetPtr());
282 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
283 if (filepath.GetAt(0) != '/') {
284 return ChangeSlashToPlatform(filepath.GetPtr());
286 if (filepath.GetAt(1) == '/') {
287 return ChangeSlashToPlatform(filepath.GetPtr() + 1);
289 if (filepath.GetAt(2) == '/') {
290 CFX_WideString result;
291 result += filepath.GetAt(1);
293 result += ChangeSlashToPlatform(filepath.GetPtr() + 2);
296 CFX_WideString result;
298 result += ChangeSlashToPlatform(filepath.GetPtr());
304 FX_BOOL CPDF_FileSpec::GetFileName(CFX_WideString &csFileName) const
306 if (m_pObj == NULL) {
309 if (m_pObj->GetType() == PDFOBJ_DICTIONARY) {
310 CPDF_Dictionary* pDict = (CPDF_Dictionary*)m_pObj;
311 csFileName = pDict->GetUnicodeText(FX_BSTRC("UF"));
312 if (csFileName.IsEmpty()) {
313 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("F")));
315 if (pDict->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL")) {
318 if (csFileName.IsEmpty()) {
319 if (pDict->KeyExist(FX_BSTRC("DOS"))) {
320 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("DOS")));
321 } else if (pDict->KeyExist(FX_BSTRC("Mac"))) {
322 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Mac")));
323 } else if (pDict->KeyExist(FX_BSTRC("Unix"))) {
324 csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Unix")));
330 csFileName = CFX_WideString::FromLocal(m_pObj->GetString());
332 csFileName = FILESPEC_DecodeFileName(csFileName);
335 CPDF_FileSpec::CPDF_FileSpec()
337 m_pObj = CPDF_Dictionary::Create();
338 if (m_pObj != NULL) {
339 ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("Type"), FX_BSTRC("Filespec"));
342 FX_BOOL CPDF_FileSpec::IsURL() const
344 if (m_pObj == NULL) {
347 if (m_pObj->GetType() != PDFOBJ_DICTIONARY) {
350 return ((CPDF_Dictionary*)m_pObj)->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL");
352 static CFX_WideString ChangeSlashToPDF(FX_LPCWSTR str)
354 CFX_WideString result;
356 if (*str == '\\' || *str == ':') {
365 CFX_WideString FILESPEC_EncodeFileName(FX_WSTR filepath)
367 if (filepath.GetLength() <= 1) {
368 return CFX_WideString();
370 #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
371 if (filepath.GetAt(1) == ':') {
372 CFX_WideString result;
374 result += filepath.GetAt(0);
375 if (filepath.GetAt(2) != '\\') {
378 result += ChangeSlashToPDF(filepath.GetPtr() + 2);
381 if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') {
382 return ChangeSlashToPDF(filepath.GetPtr() + 1);
384 if (filepath.GetAt(0) == '\\') {
385 CFX_WideString result;
387 result += ChangeSlashToPDF(filepath.GetPtr());
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;
395 result += ChangeSlashToPDF(filepath.GetPtr());
398 return ChangeSlashToPDF(filepath.GetPtr());
403 CPDF_Stream* CPDF_FileSpec::GetFileStream() const
405 if (m_pObj == NULL) {
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"));
416 return pEF->GetStream(FX_BSTRC("F"));
420 static void FPDFDOC_FILESPEC_SetFileName(CPDF_Object *pObj, FX_WSTR wsFileName, FX_BOOL bURL)
422 ASSERT(pObj != NULL);
423 CFX_WideString wsStr;
427 wsStr = FILESPEC_EncodeFileName(wsFileName);
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));
438 void CPDF_FileSpec::SetFileName(FX_WSTR wsFileName, FX_BOOL bURL)
440 ASSERT(m_pObj != NULL);
441 if (m_pObj->GetType() == PDFOBJ_DICTIONARY && bURL) {
442 ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("FS"), "URL");
444 FPDFDOC_FILESPEC_SetFileName(m_pObj, wsFileName, bURL);
446 static CFX_WideString _MakeRoman(int num)
448 const int arabic[] = {
449 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1
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"
454 const int nMaxNum = 1000000;
457 CFX_WideString wsRomanNumber;
459 while (num >= arabic[i]) {
460 num = num - arabic[i];
461 wsRomanNumber += roman[i];
465 return wsRomanNumber;
467 static CFX_WideString _MakeLetters(int num)
470 return CFX_WideString();
472 CFX_WideString wsLetters;
473 const int nMaxCount = 1000;
474 const int nLetterCount = 26;
476 int count = num / nLetterCount + 1;
478 FX_WCHAR ch = L'a' + num % nLetterCount;
479 for (int i = 0; i < count; i++) {
484 static CFX_WideString _GetLabelNumPortion(int num, const CFX_ByteString& bsStyle)
486 CFX_WideString wsNumPortion;
487 if (bsStyle.IsEmpty()) {
490 if (bsStyle == "D") {
491 wsNumPortion.Format((FX_LPCWSTR)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);
505 CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const
507 CFX_WideString wsLabel;
508 if (m_pDocument == NULL) {
511 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
512 if (pPDFRoot == NULL) {
515 CPDF_Dictionary* pLabels = pPDFRoot->GetDict(FX_BSTRC("PageLabels"));
516 CPDF_NumberTree numberTree(pLabels);
517 CPDF_Object* pValue = NULL;
520 pValue = numberTree.LookupValue(n);
521 if (pValue != NULL) {
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"));
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;
540 wsLabel.Format((FX_LPCWSTR)L"%d", nPage + 1);
543 FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_BSTR bsLabel) const
545 if (m_pDocument == NULL) {
548 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
549 if (pPDFRoot == NULL) {
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)) {
562 int nPage = FXSYS_atoi(bsLbl);
563 if (nPage > 0 && nPage <= nPages) {
568 FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const
570 CFX_ByteString bsLabel = PDF_EncodeText((CFX_WideString)wsLabel);
571 return GetPageByLabel(bsLabel);