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 WideString MakeRoman(int num) {
17   const int kArabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
18   const WideString kRoman[] = {L"m",  L"cm", L"d",  L"cd", L"c",  L"xc", L"l",
19                                L"xl", L"x",  L"ix", L"v",  L"iv", L"i"};
20   const int kMaxNum = 1000000;
21 
22   num %= kMaxNum;
23   int i = 0;
24   WideString wsRomanNumber;
25   while (num > 0) {
26     while (num >= kArabic[i]) {
27       num = num - kArabic[i];
28       wsRomanNumber += kRoman[i];
29     }
30     i = i + 1;
31   }
32   return wsRomanNumber;
33 }
34 
MakeLetters(int num)35 WideString MakeLetters(int num) {
36   if (num == 0)
37     return WideString();
38 
39   WideString wsLetters;
40   const int nMaxCount = 1000;
41   const int nLetterCount = 26;
42   --num;
43 
44   int count = num / nLetterCount + 1;
45   count %= nMaxCount;
46   wchar_t ch = L'a' + num % nLetterCount;
47   for (int i = 0; i < count; i++)
48     wsLetters += ch;
49   return wsLetters;
50 }
51 
GetLabelNumPortion(int num,const ByteString & bsStyle)52 WideString GetLabelNumPortion(int num, const ByteString& bsStyle) {
53   if (bsStyle.IsEmpty())
54     return WideString();
55   if (bsStyle == "D")
56     return WideString::Format(L"%d", num);
57   if (bsStyle == "R") {
58     WideString wsNumPortion = MakeRoman(num);
59     wsNumPortion.MakeUpper();
60     return wsNumPortion;
61   }
62   if (bsStyle == "r")
63     return MakeRoman(num);
64   if (bsStyle == "A") {
65     WideString wsNumPortion = MakeLetters(num);
66     wsNumPortion.MakeUpper();
67     return wsNumPortion;
68   }
69   if (bsStyle == "a")
70     return MakeLetters(num);
71   return WideString();
72 }
73 
74 }  // namespace
75 
CPDF_PageLabel(CPDF_Document * pDocument)76 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument)
77     : m_pDocument(pDocument) {}
78 
79 CPDF_PageLabel::~CPDF_PageLabel() = default;
80 
GetLabel(int nPage) const81 Optional<WideString> CPDF_PageLabel::GetLabel(int nPage) const {
82   if (!m_pDocument)
83     return {};
84 
85   if (nPage < 0 || nPage >= m_pDocument->GetPageCount())
86     return {};
87 
88   const CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
89   if (!pPDFRoot)
90     return {};
91 
92   const CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels");
93   if (!pLabels)
94     return {};
95 
96   CPDF_NumberTree numberTree(pLabels);
97   const CPDF_Object* pValue = nullptr;
98   int n = nPage;
99   while (n >= 0) {
100     pValue = numberTree.LookupValue(n);
101     if (pValue)
102       break;
103     n--;
104   }
105 
106   WideString label;
107   if (pValue) {
108     pValue = pValue->GetDirect();
109     if (const CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
110       if (pLabel->KeyExist("P"))
111         label += pLabel->GetUnicodeTextFor("P");
112 
113       ByteString bsNumberingStyle = pLabel->GetStringFor("S", ByteString());
114       int nLabelNum = nPage - n + pLabel->GetIntegerFor("St", 1);
115       WideString wsNumPortion = GetLabelNumPortion(nLabelNum, bsNumberingStyle);
116       label += wsNumPortion;
117       return {label};
118     }
119   }
120   label = WideString::Format(L"%d", nPage + 1);
121   return {label};
122 }
123