// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "../../include/fpdfdoc/fpdf_doc.h" const int nMaxRecursion = 32; int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) { if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { return 0; } CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0); if (pPage == NULL) { return 0; } if (pPage->GetType() == PDFOBJ_NUMBER) { return pPage->GetInteger(); } if (pPage->GetType() != PDFOBJ_DICTIONARY) { return 0; } return pDoc->GetPageIndex(pPage->GetObjNum()); } FX_DWORD CPDF_Dest::GetPageObjNum() { if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { return 0; } CPDF_Object* pPage = ((CPDF_Array*)m_pObj)->GetElementValue(0); if (pPage == NULL) { return 0; } if (pPage->GetType() == PDFOBJ_NUMBER) { return pPage->GetInteger(); } if (pPage->GetType() == PDFOBJ_DICTIONARY) { return pPage->GetObjNum(); } return 0; } const FX_CHAR* g_sZoomModes[] = {"XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV", ""}; int CPDF_Dest::GetZoomMode() { if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { return 0; } CFX_ByteString mode; CPDF_Object* pObj = ((CPDF_Array*)m_pObj)->GetElementValue(1); mode = pObj ? pObj->GetString() : CFX_ByteString(); int i = 0; while (g_sZoomModes[i][0] != '\0') { if (mode == g_sZoomModes[i]) { return i + 1; } i ++; } return 0; } FX_FLOAT CPDF_Dest::GetParam(int index) { if (m_pObj == NULL || m_pObj->GetType() != PDFOBJ_ARRAY) { return 0; } return ((CPDF_Array*)m_pObj)->GetNumber(2 + index); } CFX_ByteString CPDF_Dest::GetRemoteName() { if (m_pObj == NULL) { return CFX_ByteString(); } return m_pObj->GetString(); } CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, FX_BSTR category) { if (pDoc->GetRoot() && pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))) m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category); else m_pRoot = NULL; } static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, const CFX_ByteString& csName, int& nIndex, CPDF_Array** ppFind, int nLevel = 0) { if (nLevel > nMaxRecursion) { return NULL; } CPDF_Array* pLimits = pNode->GetArray(FX_BSTRC("Limits")); if (pLimits != NULL) { CFX_ByteString csLeft = pLimits->GetString(0); CFX_ByteString csRight = pLimits->GetString(1); if (csLeft.Compare(csRight) > 0) { CFX_ByteString csTmp = csRight; csRight = csLeft; csLeft = csTmp; } if (csName.Compare(csLeft) < 0 || csName.Compare(csRight) > 0) { return NULL; } } CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); if (pNames) { FX_DWORD dwCount = pNames->GetCount() / 2; for (FX_DWORD i = 0; i < dwCount; i ++) { CFX_ByteString csValue = pNames->GetString(i * 2); int32_t iCompare = csValue.Compare(csName); if (iCompare <= 0) { if (ppFind != NULL) { *ppFind = pNames; } if (iCompare < 0) { continue; } } else { break; } nIndex += i; return pNames->GetElementValue(i * 2 + 1); } nIndex += dwCount; return NULL; } CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); if (pKids == NULL) { return NULL; } for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { CPDF_Dictionary* pKid = pKids->GetDict(i); if (pKid == NULL) { continue; } CPDF_Object* pFound = SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1); if (pFound) { return pFound; } } return NULL; } static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, int nIndex, int& nCurIndex, CFX_ByteString& csName, CPDF_Array** ppFind, int nLevel = 0) { if (nLevel > nMaxRecursion) { return NULL; } CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); if (pNames) { int nCount = pNames->GetCount() / 2; if (nIndex >= nCurIndex + nCount) { nCurIndex += nCount; return NULL; } else { if (ppFind != NULL) { *ppFind = pNames; } csName = pNames->GetString((nIndex - nCurIndex) * 2); return pNames->GetElementValue((nIndex - nCurIndex) * 2 + 1); } } CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); if (pKids == NULL) { return NULL; } for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { CPDF_Dictionary* pKid = pKids->GetDict(i); if (pKid == NULL) { continue; } CPDF_Object* pFound = SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1); if (pFound) { return pFound; } } return NULL; } static int CountNames(CPDF_Dictionary* pNode, int nLevel = 0) { if (nLevel > nMaxRecursion) { return 0; } CPDF_Array* pNames = pNode->GetArray(FX_BSTRC("Names")); if (pNames) { return pNames->GetCount() / 2; } CPDF_Array* pKids = pNode->GetArray(FX_BSTRC("Kids")); if (pKids == NULL) { return 0; } int nCount = 0; for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) { CPDF_Dictionary* pKid = pKids->GetDict(i); if (pKid == NULL) { continue; } nCount += CountNames(pKid, nLevel + 1); } return nCount; } int CPDF_NameTree::GetCount() const { if (m_pRoot == NULL) { return 0; } return ::CountNames(m_pRoot); } int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const { if (m_pRoot == NULL) { return -1; } int nIndex = 0; if (SearchNameNode(m_pRoot, csName, nIndex, NULL) == NULL) { return -1; } return nIndex; } CPDF_Object* CPDF_NameTree::LookupValue(int nIndex, CFX_ByteString& csName) const { if (m_pRoot == NULL) { return NULL; } int nCurIndex = 0; return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, NULL); } CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const { if (m_pRoot == NULL) { return NULL; } int nIndex = 0; return SearchNameNode(m_pRoot, csName, nIndex, NULL); } CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc, FX_BSTR sName) { CPDF_Object* pValue = LookupValue(sName); if (pValue == NULL) { CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDict(FX_BSTRC("Dests")); if (pDests == NULL) { return NULL; } pValue = pDests->GetElementValue(sName); } if (pValue == NULL) { return NULL; } if (pValue->GetType() == PDFOBJ_ARRAY) { return (CPDF_Array*)pValue; } if (pValue->GetType() == PDFOBJ_DICTIONARY) { return ((CPDF_Dictionary*)pValue)->GetArray(FX_BSTRC("D")); } return NULL; } #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ static CFX_WideString ChangeSlashToPlatform(const FX_WCHAR* str) { CFX_WideString result; while (*str) { if (*str == '/') { #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ result += ':'; #else result += '\\'; #endif } else { result += *str; } str++; } return result; } static CFX_WideString ChangeSlashToPDF(const FX_WCHAR* str) { CFX_WideString result; while (*str) { if (*str == '\\' || *str == ':') { result += '/'; } else { result += *str; } str++; } return result; } #endif static CFX_WideString FILESPEC_DecodeFileName(FX_WSTR filepath) { if (filepath.GetLength() <= 1) { return CFX_WideString(); } #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac")) { return ChangeSlashToPlatform(filepath.GetPtr() + 1); } return ChangeSlashToPlatform(filepath.GetPtr()); #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ if (filepath.GetAt(0) != '/') { return ChangeSlashToPlatform(filepath.GetPtr()); } if (filepath.GetAt(1) == '/') { return ChangeSlashToPlatform(filepath.GetPtr() + 1); } if (filepath.GetAt(2) == '/') { CFX_WideString result; result += filepath.GetAt(1); result += ':'; result += ChangeSlashToPlatform(filepath.GetPtr() + 2); return result; } CFX_WideString result; result += '\\'; result += ChangeSlashToPlatform(filepath.GetPtr()); return result; #else return filepath; #endif } FX_BOOL CPDF_FileSpec::GetFileName(CFX_WideString &csFileName) const { if (m_pObj == NULL) { return FALSE; } if (m_pObj->GetType() == PDFOBJ_DICTIONARY) { CPDF_Dictionary* pDict = (CPDF_Dictionary*)m_pObj; csFileName = pDict->GetUnicodeText(FX_BSTRC("UF")); if (csFileName.IsEmpty()) { csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("F"))); } if (pDict->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL")) { return TRUE; } if (csFileName.IsEmpty()) { if (pDict->KeyExist(FX_BSTRC("DOS"))) { csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("DOS"))); } else if (pDict->KeyExist(FX_BSTRC("Mac"))) { csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Mac"))); } else if (pDict->KeyExist(FX_BSTRC("Unix"))) { csFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("Unix"))); } else { return FALSE; } } } else { csFileName = CFX_WideString::FromLocal(m_pObj->GetString()); } csFileName = FILESPEC_DecodeFileName(csFileName); return TRUE; } CPDF_FileSpec::CPDF_FileSpec() { m_pObj = CPDF_Dictionary::Create(); if (m_pObj != NULL) { ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("Type"), FX_BSTRC("Filespec")); } } FX_BOOL CPDF_FileSpec::IsURL() const { if (m_pObj == NULL) { return FALSE; } if (m_pObj->GetType() != PDFOBJ_DICTIONARY) { return FALSE; } return ((CPDF_Dictionary*)m_pObj)->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL"); } CFX_WideString FILESPEC_EncodeFileName(FX_WSTR filepath) { if (filepath.GetLength() <= 1) { return CFX_WideString(); } #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ if (filepath.GetAt(1) == ':') { CFX_WideString result; result = '/'; result += filepath.GetAt(0); if (filepath.GetAt(2) != '\\') { result += '/'; } result += ChangeSlashToPDF(filepath.GetPtr() + 2); return result; } if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') { return ChangeSlashToPDF(filepath.GetPtr() + 1); } if (filepath.GetAt(0) == '\\') { CFX_WideString result; result = '/'; result += ChangeSlashToPDF(filepath.GetPtr()); return result; } return ChangeSlashToPDF(filepath.GetPtr()); #elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ if (filepath.Left(sizeof("Mac") - 1) == FX_WSTRC(L"Mac")) { CFX_WideString result; result = '/'; result += ChangeSlashToPDF(filepath.GetPtr()); return result; } return ChangeSlashToPDF(filepath.GetPtr()); #else return filepath; #endif } CPDF_Stream* CPDF_FileSpec::GetFileStream() const { if (m_pObj == NULL) { return NULL; } int32_t iType = m_pObj->GetType(); if (iType == PDFOBJ_STREAM) { return (CPDF_Stream*)m_pObj; } else if (iType == PDFOBJ_DICTIONARY) { CPDF_Dictionary *pEF = ((CPDF_Dictionary*)m_pObj)->GetDict(FX_BSTRC("EF")); if (pEF == NULL) { return NULL; } return pEF->GetStream(FX_BSTRC("F")); } return NULL; } static void FPDFDOC_FILESPEC_SetFileName(CPDF_Object *pObj, FX_WSTR wsFileName, FX_BOOL bURL) { ASSERT(pObj != NULL); CFX_WideString wsStr; if (bURL) { wsStr = wsFileName; } else { wsStr = FILESPEC_EncodeFileName(wsFileName); } int32_t iType = pObj->GetType(); if (iType == PDFOBJ_STRING) { pObj->SetString(CFX_ByteString::FromUnicode(wsStr)); } else if (iType == PDFOBJ_DICTIONARY) { CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj; pDict->SetAtString(FX_BSTRC("F"), CFX_ByteString::FromUnicode(wsStr)); pDict->SetAtString(FX_BSTRC("UF"), PDF_EncodeText(wsStr)); } } void CPDF_FileSpec::SetFileName(FX_WSTR wsFileName, FX_BOOL bURL) { ASSERT(m_pObj != NULL); if (m_pObj->GetType() == PDFOBJ_DICTIONARY && bURL) { ((CPDF_Dictionary*)m_pObj)->SetAtName(FX_BSTRC("FS"), "URL"); } FPDFDOC_FILESPEC_SetFileName(m_pObj, wsFileName, bURL); } static CFX_WideString _MakeRoman(int num) { const int arabic[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; const CFX_WideString roman[] = { 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" }; const int nMaxNum = 1000000; num %= nMaxNum; int i = 0; CFX_WideString wsRomanNumber; while (num > 0) { while (num >= arabic[i]) { num = num - arabic[i]; wsRomanNumber += roman[i]; } i = i + 1; } return wsRomanNumber; } static CFX_WideString _MakeLetters(int num) { if (num == 0) { return CFX_WideString(); } CFX_WideString wsLetters; const int nMaxCount = 1000; const int nLetterCount = 26; num -= 1; int count = num / nLetterCount + 1; count %= nMaxCount; FX_WCHAR ch = L'a' + num % nLetterCount; for (int i = 0; i < count; i++) { wsLetters += ch; } return wsLetters; } static CFX_WideString _GetLabelNumPortion(int num, const CFX_ByteString& bsStyle) { CFX_WideString wsNumPortion; if (bsStyle.IsEmpty()) { return wsNumPortion; } if (bsStyle == "D") { wsNumPortion.Format(L"%d", num); } else if (bsStyle == "R") { wsNumPortion = _MakeRoman(num); wsNumPortion.MakeUpper(); } else if (bsStyle == "r") { wsNumPortion = _MakeRoman(num); } else if (bsStyle == "A") { wsNumPortion = _MakeLetters(num); wsNumPortion.MakeUpper(); } else if (bsStyle == "a") { wsNumPortion = _MakeLetters(num); } return wsNumPortion; } CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const { CFX_WideString wsLabel; if (m_pDocument == NULL) { return wsLabel; } CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); if (pPDFRoot == NULL) { return wsLabel; } CPDF_Dictionary* pLabels = pPDFRoot->GetDict(FX_BSTRC("PageLabels")); CPDF_NumberTree numberTree(pLabels); CPDF_Object* pValue = NULL; int n = nPage; while (n >= 0) { pValue = numberTree.LookupValue(n); if (pValue != NULL) { break; } n--; } if (pValue != NULL) { pValue = pValue->GetDirect(); if (pValue->GetType() == PDFOBJ_DICTIONARY) { CPDF_Dictionary* pLabel = (CPDF_Dictionary*)pValue; if (pLabel->KeyExist(FX_BSTRC("P"))) { wsLabel += pLabel->GetUnicodeText(FX_BSTRC("P")); } CFX_ByteString bsNumberingStyle = pLabel->GetString(FX_BSTRC("S"), NULL); int nLabelNum = nPage - n + pLabel->GetInteger(FX_BSTRC("St"), 1); CFX_WideString wsNumPortion = _GetLabelNumPortion(nLabelNum, bsNumberingStyle); wsLabel += wsNumPortion; return wsLabel; } } wsLabel.Format(L"%d", nPage + 1); return wsLabel; } int32_t CPDF_PageLabel::GetPageByLabel(FX_BSTR bsLabel) const { if (m_pDocument == NULL) { return -1; } CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); if (pPDFRoot == NULL) { return -1; } int nPages = m_pDocument->GetPageCount(); CFX_ByteString bsLbl; CFX_ByteString bsOrig = bsLabel; for (int i = 0; i < nPages; i++) { bsLbl = PDF_EncodeText(GetLabel(i)); if (!bsLbl.Compare(bsOrig)) { return i; } } bsLbl = bsOrig; int nPage = FXSYS_atoi(bsLbl); if (nPage > 0 && nPage <= nPages) { return nPage; } return -1; } int32_t CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const { CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr()); return GetPageByLabel(bsLabel); }