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 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 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 52 WideString GetLabelNumPortion(int num, const ByteString& bsStyle) { 53 if (bsStyle.IsEmpty()) 54 return L""; 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 L""; 72 } 73 74 } // namespace 75 76 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument) 77 : m_pDocument(pDocument) {} 78 79 CPDF_PageLabel::~CPDF_PageLabel() {} 80 81 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 CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels"); 93 if (!pLabels) 94 return {}; 95 96 CPDF_NumberTree numberTree(pLabels); 97 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 (CPDF_Dictionary* pLabel = pValue->AsDictionary()) { 110 if (pLabel->KeyExist("P")) 111 label += pLabel->GetUnicodeTextFor("P"); 112 113 ByteString bsNumberingStyle = pLabel->GetStringFor("S", ""); 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 124 int32_t CPDF_PageLabel::GetPageByLabel(const ByteStringView& bsLabel) const { 125 if (!m_pDocument) 126 return -1; 127 128 const CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); 129 if (!pPDFRoot) 130 return -1; 131 132 int nPages = m_pDocument->GetPageCount(); 133 for (int i = 0; i < nPages; i++) { 134 Optional<WideString> str = GetLabel(i); 135 if (!str.has_value()) 136 continue; 137 if (PDF_EncodeText(str.value()).Compare(bsLabel)) 138 return i; 139 } 140 141 int nPage = FXSYS_atoi(ByteString(bsLabel).c_str()); // NUL terminate. 142 return nPage > 0 && nPage <= nPages ? nPage : -1; 143 } 144 145 int32_t CPDF_PageLabel::GetPageByLabel(const WideStringView& wsLabel) const { 146 // TODO(tsepez): check usage of c_str() below. 147 return GetPageByLabel( 148 PDF_EncodeText(wsLabel.unterminated_c_str()).AsStringView()); 149 } 150