Home | History | Annotate | Download | only in fpdfdoc
      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