• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
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 <array>
10 #include <utility>
11 
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_document.h"
14 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
15 #include "core/fpdfdoc/cpdf_numbertree.h"
16 #include "core/fxcrt/stl_util.h"
17 
18 namespace {
19 
MakeRoman(int num)20 WideString MakeRoman(int num) {
21   constexpr auto kArabic = fxcrt::ToArray<const int>(
22       {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1});
23   const auto kRoman = fxcrt::ToArray<const WideStringView>(
24       {L"m", L"cm", L"d", L"cd", L"c", L"xc", L"l", L"xl", L"x", L"ix", L"v",
25        L"iv", L"i"});
26   constexpr int kMaxNum = 1000000;
27 
28   num %= kMaxNum;
29   int i = 0;
30   WideString result;
31   result.Reserve(10);  // Should cover most use cases.
32   while (num > 0) {
33     while (num >= kArabic[i]) {
34       num -= kArabic[i];
35       result += kRoman[i];
36     }
37     ++i;
38   }
39   return result;
40 }
41 
MakeLetters(int num)42 WideString MakeLetters(int num) {
43   if (num == 0) {
44     return WideString();
45   }
46 
47   constexpr int kMaxCount = 1000;
48   constexpr int kLetterCount = 26;
49 
50   --num;
51   const int count = (num / kLetterCount + 1) % kMaxCount;
52   const wchar_t ch = L'a' + num % kLetterCount;
53 
54   WideString result;
55   {
56     auto result_span = result.GetBuffer(count);
57     fxcrt::Fill(result_span, ch);
58     result.ReleaseBuffer(count);
59   }
60   return result;
61 }
62 
GetLabelNumPortion(int num,const ByteString & style)63 WideString GetLabelNumPortion(int num, const ByteString& style) {
64   if (style.IsEmpty()) {
65     return WideString();
66   }
67   if (style == "D") {
68     return WideString::FormatInteger(num);
69   }
70   if (style == "R") {
71     WideString result = MakeRoman(num);
72     result.MakeUpper();
73     return result;
74   }
75   if (style == "r") {
76     return MakeRoman(num);
77   }
78   if (style == "A") {
79     WideString result = MakeLetters(num);
80     result.MakeUpper();
81     return result;
82   }
83   if (style == "a") {
84     return MakeLetters(num);
85   }
86   return WideString();
87 }
88 
89 }  // namespace
90 
CPDF_PageLabel(CPDF_Document * doc)91 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* doc) : doc_(doc) {}
92 
93 CPDF_PageLabel::~CPDF_PageLabel() = default;
94 
GetLabel(int page_index) const95 std::optional<WideString> CPDF_PageLabel::GetLabel(int page_index) const {
96   if (!doc_) {
97     return std::nullopt;
98   }
99 
100   if (page_index < 0 || page_index >= doc_->GetPageCount()) {
101     return std::nullopt;
102   }
103 
104   const CPDF_Dictionary* root_dict = doc_->GetRoot();
105   if (!root_dict) {
106     return std::nullopt;
107   }
108 
109   RetainPtr<const CPDF_Dictionary> labels_dict =
110       root_dict->GetDictFor("PageLabels");
111   if (!labels_dict) {
112     return std::nullopt;
113   }
114 
115   CPDF_NumberTree number_tree(std::move(labels_dict));
116   RetainPtr<const CPDF_Object> label_value;
117   std::optional<CPDF_NumberTree::KeyValue> lower_bound =
118       number_tree.GetLowerBound(page_index);
119   if (lower_bound.has_value()) {
120     label_value = lower_bound.value().value;
121   }
122 
123   const CPDF_Dictionary* label_dict =
124       label_value ? label_value->GetDirect()->AsDictionary() : nullptr;
125   if (!label_dict) {
126     return WideString::FormatInteger(page_index + 1);
127   }
128 
129   WideString label;
130   if (label_dict->KeyExist("P")) {
131     label = label_dict->GetUnicodeTextFor("P");
132   }
133 
134   ByteString style = label_dict->GetByteStringFor("S", ByteString());
135   int label_number =
136       page_index - lower_bound.value().key + label_dict->GetIntegerFor("St", 1);
137   label += GetLabelNumPortion(label_number, style);
138   return label;
139 }
140