1 // Copyright 2016 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 "core/fpdfdoc/cpdf_pagelabel.h"
8
9 #include "core/fpdfapi/parser/cpdf_dictionary.h"
10 #include "core/fpdfapi/parser/cpdf_document.h"
11 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
12 #include "core/fpdfdoc/cpdf_numbertree.h"
13
14 namespace {
15
MakeRoman(int num)16 CFX_WideString MakeRoman(int num) {
17 const int kArabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
18 const CFX_WideString kRoman[] = {L"m", L"cm", L"d", L"cd", L"c",
19 L"xc", L"l", L"xl", L"x", L"ix",
20 L"v", L"iv", L"i"};
21 const int kMaxNum = 1000000;
22
23 num %= kMaxNum;
24 int i = 0;
25 CFX_WideString wsRomanNumber;
26 while (num > 0) {
27 while (num >= kArabic[i]) {
28 num = num - kArabic[i];
29 wsRomanNumber += kRoman[i];
30 }
31 i = i + 1;
32 }
33 return wsRomanNumber;
34 }
35
MakeLetters(int num)36 CFX_WideString MakeLetters(int num) {
37 if (num == 0)
38 return CFX_WideString();
39
40 CFX_WideString wsLetters;
41 const int nMaxCount = 1000;
42 const int nLetterCount = 26;
43 --num;
44
45 int count = num / nLetterCount + 1;
46 count %= nMaxCount;
47 FX_WCHAR ch = L'a' + num % nLetterCount;
48 for (int i = 0; i < count; i++)
49 wsLetters += ch;
50 return wsLetters;
51 }
52
GetLabelNumPortion(int num,const CFX_ByteString & bsStyle)53 CFX_WideString GetLabelNumPortion(int num, const CFX_ByteString& bsStyle) {
54 CFX_WideString wsNumPortion;
55 if (bsStyle.IsEmpty())
56 return wsNumPortion;
57 if (bsStyle == "D") {
58 wsNumPortion.Format(L"%d", num);
59 } else if (bsStyle == "R") {
60 wsNumPortion = MakeRoman(num);
61 wsNumPortion.MakeUpper();
62 } else if (bsStyle == "r") {
63 wsNumPortion = MakeRoman(num);
64 } else if (bsStyle == "A") {
65 wsNumPortion = MakeLetters(num);
66 wsNumPortion.MakeUpper();
67 } else if (bsStyle == "a") {
68 wsNumPortion = MakeLetters(num);
69 }
70 return wsNumPortion;
71 }
72
73 } // namespace
74
CPDF_PageLabel(CPDF_Document * pDocument)75 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument)
76 : m_pDocument(pDocument) {}
77
GetLabel(int nPage,CFX_WideString * wsLabel) const78 bool CPDF_PageLabel::GetLabel(int nPage, CFX_WideString* wsLabel) const {
79 if (!m_pDocument)
80 return false;
81
82 if (nPage < 0 || nPage >= m_pDocument->GetPageCount())
83 return false;
84
85 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
86 if (!pPDFRoot)
87 return false;
88
89 CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels");
90 if (!pLabels)
91 return false;
92
93 CPDF_NumberTree numberTree(pLabels);
94 CPDF_Object* pValue = nullptr;
95 int n = nPage;
96 while (n >= 0) {
97 pValue = numberTree.LookupValue(n);
98 if (pValue)
99 break;
100 n--;
101 }
102
103 if (pValue) {
104 pValue = pValue->GetDirect();
105 if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
106 if (pLabel->KeyExist("P"))
107 *wsLabel += pLabel->GetUnicodeTextFor("P");
108
109 CFX_ByteString bsNumberingStyle = pLabel->GetStringFor("S", "");
110 int nLabelNum = nPage - n + pLabel->GetIntegerFor("St", 1);
111 CFX_WideString wsNumPortion =
112 GetLabelNumPortion(nLabelNum, bsNumberingStyle);
113 *wsLabel += wsNumPortion;
114 return true;
115 }
116 }
117 wsLabel->Format(L"%d", nPage + 1);
118 return true;
119 }
120
GetPageByLabel(const CFX_ByteStringC & bsLabel) const121 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_ByteStringC& bsLabel) const {
122 if (!m_pDocument)
123 return -1;
124
125 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
126 if (!pPDFRoot)
127 return -1;
128
129 int nPages = m_pDocument->GetPageCount();
130 for (int i = 0; i < nPages; i++) {
131 CFX_WideString str;
132 if (!GetLabel(i, &str))
133 continue;
134 if (PDF_EncodeText(str).Compare(bsLabel))
135 return i;
136 }
137
138 int nPage = FXSYS_atoi(CFX_ByteString(bsLabel).c_str()); // NUL terminate.
139 return nPage > 0 && nPage <= nPages ? nPage : -1;
140 }
141
GetPageByLabel(const CFX_WideStringC & wsLabel) const142 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const {
143 return GetPageByLabel(PDF_EncodeText(wsLabel.c_str()).AsStringC());
144 }
145