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
~CPDF_PageLabel()79 CPDF_PageLabel::~CPDF_PageLabel() {}
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