Home | History | Annotate | Download | only in fpdfdoc
      1 // Copyright 2014 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/include/fpdfdoc/fpdf_doc.h"
      8 
      9 const int nMaxRecursion = 32;
     10 int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) {
     11   CPDF_Array* pArray = ToArray(m_pObj);
     12   if (!pArray)
     13     return 0;
     14 
     15   CPDF_Object* pPage = pArray->GetElementValue(0);
     16   if (!pPage)
     17     return 0;
     18   if (pPage->IsNumber())
     19     return pPage->GetInteger();
     20   if (!pPage->IsDictionary())
     21     return 0;
     22   return pDoc->GetPageIndex(pPage->GetObjNum());
     23 }
     24 FX_DWORD CPDF_Dest::GetPageObjNum() {
     25   CPDF_Array* pArray = ToArray(m_pObj);
     26   if (!pArray)
     27     return 0;
     28 
     29   CPDF_Object* pPage = pArray->GetElementValue(0);
     30   if (!pPage)
     31     return 0;
     32   if (pPage->IsNumber())
     33     return pPage->GetInteger();
     34   if (pPage->IsDictionary())
     35     return pPage->GetObjNum();
     36   return 0;
     37 }
     38 const FX_CHAR* g_sZoomModes[] = {"XYZ",  "Fit",   "FitH",  "FitV", "FitR",
     39                                  "FitB", "FitBH", "FitBV", ""};
     40 int CPDF_Dest::GetZoomMode() {
     41   CPDF_Array* pArray = ToArray(m_pObj);
     42   if (!pArray)
     43     return 0;
     44 
     45   CFX_ByteString mode;
     46   CPDF_Object* pObj = pArray->GetElementValue(1);
     47   mode = pObj ? pObj->GetString() : CFX_ByteString();
     48   int i = 0;
     49   while (g_sZoomModes[i][0] != '\0') {
     50     if (mode == g_sZoomModes[i]) {
     51       return i + 1;
     52     }
     53     i++;
     54   }
     55   return 0;
     56 }
     57 FX_FLOAT CPDF_Dest::GetParam(int index) {
     58   CPDF_Array* pArray = ToArray(m_pObj);
     59   return pArray ? pArray->GetNumber(2 + index) : 0;
     60 }
     61 CFX_ByteString CPDF_Dest::GetRemoteName() {
     62   return m_pObj ? m_pObj->GetString() : CFX_ByteString();
     63 }
     64 CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc,
     65                              const CFX_ByteStringC& category) {
     66   if (pDoc->GetRoot() && pDoc->GetRoot()->GetDict("Names"))
     67     m_pRoot = pDoc->GetRoot()->GetDict("Names")->GetDict(category);
     68   else
     69     m_pRoot = NULL;
     70 }
     71 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
     72                                    const CFX_ByteString& csName,
     73                                    int& nIndex,
     74                                    CPDF_Array** ppFind,
     75                                    int nLevel = 0) {
     76   if (nLevel > nMaxRecursion) {
     77     return NULL;
     78   }
     79   CPDF_Array* pLimits = pNode->GetArray("Limits");
     80   if (pLimits) {
     81     CFX_ByteString csLeft = pLimits->GetString(0);
     82     CFX_ByteString csRight = pLimits->GetString(1);
     83     if (csLeft.Compare(csRight) > 0) {
     84       CFX_ByteString csTmp = csRight;
     85       csRight = csLeft;
     86       csLeft = csTmp;
     87     }
     88     if (csName.Compare(csLeft) < 0 || csName.Compare(csRight) > 0) {
     89       return NULL;
     90     }
     91   }
     92   CPDF_Array* pNames = pNode->GetArray("Names");
     93   if (pNames) {
     94     FX_DWORD dwCount = pNames->GetCount() / 2;
     95     for (FX_DWORD i = 0; i < dwCount; i++) {
     96       CFX_ByteString csValue = pNames->GetString(i * 2);
     97       int32_t iCompare = csValue.Compare(csName);
     98       if (iCompare <= 0) {
     99         if (ppFind) {
    100           *ppFind = pNames;
    101         }
    102         if (iCompare < 0) {
    103           continue;
    104         }
    105       } else {
    106         break;
    107       }
    108       nIndex += i;
    109       return pNames->GetElementValue(i * 2 + 1);
    110     }
    111     nIndex += dwCount;
    112     return NULL;
    113   }
    114   CPDF_Array* pKids = pNode->GetArray("Kids");
    115   if (!pKids) {
    116     return NULL;
    117   }
    118   for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
    119     CPDF_Dictionary* pKid = pKids->GetDict(i);
    120     if (!pKid) {
    121       continue;
    122     }
    123     CPDF_Object* pFound =
    124         SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1);
    125     if (pFound) {
    126       return pFound;
    127     }
    128   }
    129   return NULL;
    130 }
    131 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
    132                                    int nIndex,
    133                                    int& nCurIndex,
    134                                    CFX_ByteString& csName,
    135                                    CPDF_Array** ppFind,
    136                                    int nLevel = 0) {
    137   if (nLevel > nMaxRecursion) {
    138     return NULL;
    139   }
    140   CPDF_Array* pNames = pNode->GetArray("Names");
    141   if (pNames) {
    142     int nCount = pNames->GetCount() / 2;
    143     if (nIndex >= nCurIndex + nCount) {
    144       nCurIndex += nCount;
    145       return NULL;
    146     }
    147     if (ppFind) {
    148       *ppFind = pNames;
    149     }
    150     csName = pNames->GetString((nIndex - nCurIndex) * 2);
    151     return pNames->GetElementValue((nIndex - nCurIndex) * 2 + 1);
    152   }
    153   CPDF_Array* pKids = pNode->GetArray("Kids");
    154   if (!pKids) {
    155     return NULL;
    156   }
    157   for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
    158     CPDF_Dictionary* pKid = pKids->GetDict(i);
    159     if (!pKid) {
    160       continue;
    161     }
    162     CPDF_Object* pFound =
    163         SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1);
    164     if (pFound) {
    165       return pFound;
    166     }
    167   }
    168   return NULL;
    169 }
    170 static int CountNames(CPDF_Dictionary* pNode, int nLevel = 0) {
    171   if (nLevel > nMaxRecursion) {
    172     return 0;
    173   }
    174   CPDF_Array* pNames = pNode->GetArray("Names");
    175   if (pNames) {
    176     return pNames->GetCount() / 2;
    177   }
    178   CPDF_Array* pKids = pNode->GetArray("Kids");
    179   if (!pKids) {
    180     return 0;
    181   }
    182   int nCount = 0;
    183   for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
    184     CPDF_Dictionary* pKid = pKids->GetDict(i);
    185     if (!pKid) {
    186       continue;
    187     }
    188     nCount += CountNames(pKid, nLevel + 1);
    189   }
    190   return nCount;
    191 }
    192 int CPDF_NameTree::GetCount() const {
    193   if (!m_pRoot) {
    194     return 0;
    195   }
    196   return ::CountNames(m_pRoot);
    197 }
    198 int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const {
    199   if (!m_pRoot) {
    200     return -1;
    201   }
    202   int nIndex = 0;
    203   if (!SearchNameNode(m_pRoot, csName, nIndex, NULL)) {
    204     return -1;
    205   }
    206   return nIndex;
    207 }
    208 CPDF_Object* CPDF_NameTree::LookupValue(int nIndex,
    209                                         CFX_ByteString& csName) const {
    210   if (!m_pRoot) {
    211     return NULL;
    212   }
    213   int nCurIndex = 0;
    214   return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, NULL);
    215 }
    216 CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const {
    217   if (!m_pRoot) {
    218     return NULL;
    219   }
    220   int nIndex = 0;
    221   return SearchNameNode(m_pRoot, csName, nIndex, NULL);
    222 }
    223 CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc,
    224                                            const CFX_ByteStringC& sName) {
    225   CPDF_Object* pValue = LookupValue(sName);
    226   if (!pValue) {
    227     CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDict("Dests");
    228     if (!pDests)
    229       return nullptr;
    230     pValue = pDests->GetElementValue(sName);
    231   }
    232   if (!pValue)
    233     return nullptr;
    234   if (CPDF_Array* pArray = pValue->AsArray())
    235     return pArray;
    236   if (CPDF_Dictionary* pDict = pValue->AsDictionary())
    237     return pDict->GetArray("D");
    238   return nullptr;
    239 }
    240 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || \
    241     _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    242 static CFX_WideString ChangeSlashToPlatform(const FX_WCHAR* str) {
    243   CFX_WideString result;
    244   while (*str) {
    245     if (*str == '/') {
    246 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
    247       result += ':';
    248 #else
    249       result += '\\';
    250 #endif
    251     } else {
    252       result += *str;
    253     }
    254     str++;
    255   }
    256   return result;
    257 }
    258 static CFX_WideString ChangeSlashToPDF(const FX_WCHAR* str) {
    259   CFX_WideString result;
    260   while (*str) {
    261     if (*str == '\\' || *str == ':') {
    262       result += '/';
    263     } else {
    264       result += *str;
    265     }
    266     str++;
    267   }
    268   return result;
    269 }
    270 #endif
    271 static CFX_WideString FILESPEC_DecodeFileName(const CFX_WideStringC& filepath) {
    272   if (filepath.GetLength() <= 1) {
    273     return CFX_WideString();
    274   }
    275 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
    276   if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac")) {
    277     return ChangeSlashToPlatform(filepath.GetPtr() + 1);
    278   }
    279   return ChangeSlashToPlatform(filepath.GetPtr());
    280 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    281   if (filepath.GetAt(0) != '/') {
    282     return ChangeSlashToPlatform(filepath.GetPtr());
    283   }
    284   if (filepath.GetAt(1) == '/') {
    285     return ChangeSlashToPlatform(filepath.GetPtr() + 1);
    286   }
    287   if (filepath.GetAt(2) == '/') {
    288     CFX_WideString result;
    289     result += filepath.GetAt(1);
    290     result += ':';
    291     result += ChangeSlashToPlatform(filepath.GetPtr() + 2);
    292     return result;
    293   }
    294   CFX_WideString result;
    295   result += '\\';
    296   result += ChangeSlashToPlatform(filepath.GetPtr());
    297   return result;
    298 #else
    299   return filepath;
    300 #endif
    301 }
    302 FX_BOOL CPDF_FileSpec::GetFileName(CFX_WideString& csFileName) const {
    303   if (!m_pObj) {
    304     return FALSE;
    305   }
    306   if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
    307     csFileName = pDict->GetUnicodeText("UF");
    308     if (csFileName.IsEmpty()) {
    309       csFileName = CFX_WideString::FromLocal(pDict->GetString("F"));
    310     }
    311     if (pDict->GetString("FS") == "URL") {
    312       return TRUE;
    313     }
    314     if (csFileName.IsEmpty()) {
    315       if (pDict->KeyExist("DOS")) {
    316         csFileName = CFX_WideString::FromLocal(pDict->GetString("DOS"));
    317       } else if (pDict->KeyExist("Mac")) {
    318         csFileName = CFX_WideString::FromLocal(pDict->GetString("Mac"));
    319       } else if (pDict->KeyExist("Unix")) {
    320         csFileName = CFX_WideString::FromLocal(pDict->GetString("Unix"));
    321       } else {
    322         return FALSE;
    323       }
    324     }
    325   } else {
    326     csFileName = CFX_WideString::FromLocal(m_pObj->GetString());
    327   }
    328   csFileName = FILESPEC_DecodeFileName(csFileName);
    329   return TRUE;
    330 }
    331 CPDF_FileSpec::CPDF_FileSpec() {
    332   m_pObj = new CPDF_Dictionary;
    333   if (CPDF_Dictionary* pDict = ToDictionary(m_pObj)) {
    334     pDict->SetAtName("Type", "Filespec");
    335   }
    336 }
    337 FX_BOOL CPDF_FileSpec::IsURL() const {
    338   if (CPDF_Dictionary* pDict = ToDictionary(m_pObj)) {
    339     return pDict->GetString("FS") == "URL";
    340   }
    341   return FALSE;
    342 }
    343 CFX_WideString FILESPEC_EncodeFileName(const CFX_WideStringC& filepath) {
    344   if (filepath.GetLength() <= 1) {
    345     return CFX_WideString();
    346   }
    347 #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    348   if (filepath.GetAt(1) == ':') {
    349     CFX_WideString result;
    350     result = '/';
    351     result += filepath.GetAt(0);
    352     if (filepath.GetAt(2) != '\\') {
    353       result += '/';
    354     }
    355     result += ChangeSlashToPDF(filepath.GetPtr() + 2);
    356     return result;
    357   }
    358   if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') {
    359     return ChangeSlashToPDF(filepath.GetPtr() + 1);
    360   }
    361   if (filepath.GetAt(0) == '\\') {
    362     CFX_WideString result;
    363     result = '/';
    364     result += ChangeSlashToPDF(filepath.GetPtr());
    365     return result;
    366   }
    367   return ChangeSlashToPDF(filepath.GetPtr());
    368 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
    369   if (filepath.Left(sizeof("Mac") - 1) == FX_WSTRC(L"Mac")) {
    370     CFX_WideString result;
    371     result = '/';
    372     result += ChangeSlashToPDF(filepath.GetPtr());
    373     return result;
    374   }
    375   return ChangeSlashToPDF(filepath.GetPtr());
    376 #else
    377   return filepath;
    378 #endif
    379 }
    380 CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
    381   if (!m_pObj)
    382     return nullptr;
    383   if (CPDF_Stream* pStream = m_pObj->AsStream())
    384     return pStream;
    385   if (CPDF_Dictionary* pEF = m_pObj->AsDictionary()->GetDict("EF"))
    386     return pEF->GetStream("F");
    387   return nullptr;
    388 }
    389 static void FPDFDOC_FILESPEC_SetFileName(CPDF_Object* pObj,
    390                                          const CFX_WideStringC& wsFileName,
    391                                          FX_BOOL bURL) {
    392   CFX_WideString wsStr;
    393   if (bURL) {
    394     wsStr = wsFileName;
    395   } else {
    396     wsStr = FILESPEC_EncodeFileName(wsFileName);
    397   }
    398   if (pObj->IsString()) {
    399     pObj->SetString(CFX_ByteString::FromUnicode(wsStr));
    400   } else if (CPDF_Dictionary* pDict = pObj->AsDictionary()) {
    401     pDict->SetAtString("F", CFX_ByteString::FromUnicode(wsStr));
    402     pDict->SetAtString("UF", PDF_EncodeText(wsStr));
    403   }
    404 }
    405 void CPDF_FileSpec::SetFileName(const CFX_WideStringC& wsFileName,
    406                                 FX_BOOL bURL) {
    407   if (bURL) {
    408     if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
    409       pDict->SetAtName("FS", "URL");
    410     }
    411   }
    412   FPDFDOC_FILESPEC_SetFileName(m_pObj, wsFileName, bURL);
    413 }
    414 static CFX_WideString _MakeRoman(int num) {
    415   const int arabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    416   const CFX_WideString roman[] = {L"m",  L"cm", L"d",  L"cd", L"c",
    417                                   L"xc", L"l",  L"xl", L"x",  L"ix",
    418                                   L"v",  L"iv", L"i"};
    419   const int nMaxNum = 1000000;
    420   num %= nMaxNum;
    421   int i = 0;
    422   CFX_WideString wsRomanNumber;
    423   while (num > 0) {
    424     while (num >= arabic[i]) {
    425       num = num - arabic[i];
    426       wsRomanNumber += roman[i];
    427     }
    428     i = i + 1;
    429   }
    430   return wsRomanNumber;
    431 }
    432 static CFX_WideString _MakeLetters(int num) {
    433   if (num == 0) {
    434     return CFX_WideString();
    435   }
    436   CFX_WideString wsLetters;
    437   const int nMaxCount = 1000;
    438   const int nLetterCount = 26;
    439   num -= 1;
    440   int count = num / nLetterCount + 1;
    441   count %= nMaxCount;
    442   FX_WCHAR ch = L'a' + num % nLetterCount;
    443   for (int i = 0; i < count; i++) {
    444     wsLetters += ch;
    445   }
    446   return wsLetters;
    447 }
    448 static CFX_WideString _GetLabelNumPortion(int num,
    449                                           const CFX_ByteString& bsStyle) {
    450   CFX_WideString wsNumPortion;
    451   if (bsStyle.IsEmpty()) {
    452     return wsNumPortion;
    453   }
    454   if (bsStyle == "D") {
    455     wsNumPortion.Format(L"%d", num);
    456   } else if (bsStyle == "R") {
    457     wsNumPortion = _MakeRoman(num);
    458     wsNumPortion.MakeUpper();
    459   } else if (bsStyle == "r") {
    460     wsNumPortion = _MakeRoman(num);
    461   } else if (bsStyle == "A") {
    462     wsNumPortion = _MakeLetters(num);
    463     wsNumPortion.MakeUpper();
    464   } else if (bsStyle == "a") {
    465     wsNumPortion = _MakeLetters(num);
    466   }
    467   return wsNumPortion;
    468 }
    469 CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const {
    470   CFX_WideString wsLabel;
    471   if (!m_pDocument) {
    472     return wsLabel;
    473   }
    474   CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
    475   if (!pPDFRoot) {
    476     return wsLabel;
    477   }
    478   CPDF_Dictionary* pLabels = pPDFRoot->GetDict("PageLabels");
    479   CPDF_NumberTree numberTree(pLabels);
    480   CPDF_Object* pValue = NULL;
    481   int n = nPage;
    482   while (n >= 0) {
    483     pValue = numberTree.LookupValue(n);
    484     if (pValue) {
    485       break;
    486     }
    487     n--;
    488   }
    489   if (pValue) {
    490     pValue = pValue->GetDirect();
    491     if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
    492       if (pLabel->KeyExist("P")) {
    493         wsLabel += pLabel->GetUnicodeText("P");
    494       }
    495       CFX_ByteString bsNumberingStyle = pLabel->GetString("S", NULL);
    496       int nLabelNum = nPage - n + pLabel->GetInteger("St", 1);
    497       CFX_WideString wsNumPortion =
    498           _GetLabelNumPortion(nLabelNum, bsNumberingStyle);
    499       wsLabel += wsNumPortion;
    500       return wsLabel;
    501     }
    502   }
    503   wsLabel.Format(L"%d", nPage + 1);
    504   return wsLabel;
    505 }
    506 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_ByteStringC& bsLabel) const {
    507   if (!m_pDocument) {
    508     return -1;
    509   }
    510   CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
    511   if (!pPDFRoot) {
    512     return -1;
    513   }
    514   int nPages = m_pDocument->GetPageCount();
    515   CFX_ByteString bsLbl;
    516   CFX_ByteString bsOrig = bsLabel;
    517   for (int i = 0; i < nPages; i++) {
    518     bsLbl = PDF_EncodeText(GetLabel(i));
    519     if (!bsLbl.Compare(bsOrig)) {
    520       return i;
    521     }
    522   }
    523   bsLbl = bsOrig;
    524   int nPage = FXSYS_atoi(bsLbl);
    525   if (nPage > 0 && nPage <= nPages) {
    526     return nPage;
    527   }
    528   return -1;
    529 }
    530 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const {
    531   CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr());
    532   return GetPageByLabel(bsLabel);
    533 }
    534