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