blob: 4ec8fb74fda5be9915a9d7978e9eff6bfe64a2e7 [file] [log] [blame]
// 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_Array*)m_pObj)->GetElementValue(1)->GetString();
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)
{
m_pRoot = pDoc->GetRoot()->GetDict(FX_BSTRC("Names"))->GetDict(category);
}
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);
FX_INT32 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;
}
static CFX_WideString ChangeSlashToPlatform(FX_LPCWSTR str)
{
CFX_WideString result;
while (*str) {
if (*str == '/') {
#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
result += ':';
#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
result += '\\';
#else
result += *str;
#endif
} else {
result += *str;
}
str++;
}
return result;
}
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");
}
static CFX_WideString ChangeSlashToPDF(FX_LPCWSTR str)
{
CFX_WideString result;
while (*str) {
if (*str == '\\' || *str == ':') {
result += '/';
} else {
result += *str;
}
str++;
}
return result;
}
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;
}
FX_INT32 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);
}
FX_INT32 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((FX_LPCWSTR)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((FX_LPCWSTR)L"%d", nPage + 1);
return wsLabel;
}
FX_INT32 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;
}
FX_INT32 CPDF_PageLabel::GetPageByLabel(FX_WSTR wsLabel) const
{
CFX_ByteString bsLabel = PDF_EncodeText((CFX_WideString)wsLabel);
return GetPageByLabel(bsLabel);
}