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