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