// 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 "public/fpdf_ppo.h" #include #include #include #include "core/include/fpdfapi/cpdf_array.h" #include "core/include/fpdfapi/cpdf_document.h" #include "core/include/fpdfapi/cpdf_name.h" #include "core/include/fpdfapi/cpdf_number.h" #include "core/include/fpdfapi/cpdf_reference.h" #include "core/include/fpdfapi/cpdf_string.h" #include "fpdfsdk/include/fsdk_define.h" #include "third_party/base/stl_util.h" class CPDF_PageOrganizer { public: using ObjectNumberMap = std::map; CPDF_PageOrganizer(); ~CPDF_PageOrganizer(); FX_BOOL PDFDocInit(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc); FX_BOOL ExportPage(CPDF_Document* pSrcPDFDoc, std::vector* pPageNums, CPDF_Document* pDestPDFDoc, int nIndex); CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict, CFX_ByteString nSrctag); FX_BOOL UpdateReference(CPDF_Object* pObj, CPDF_Document* pDoc, ObjectNumberMap* pObjNumberMap); FX_DWORD GetNewObjId(CPDF_Document* pDoc, ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef); }; CPDF_PageOrganizer::CPDF_PageOrganizer() {} CPDF_PageOrganizer::~CPDF_PageOrganizer() {} FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc) { if (!pDestPDFDoc || !pSrcPDFDoc) return FALSE; CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot(); if (!pNewRoot) return FALSE; // Set the document information CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo(); if (!DInfoDict) return FALSE; CFX_ByteString producerstr; producerstr.Format("PDFium"); DInfoDict->SetAt("Producer", new CPDF_String(producerstr, FALSE)); // Set type CFX_ByteString cbRootType = pNewRoot->GetStringBy("Type", ""); if (cbRootType.Equal("")) { pNewRoot->SetAt("Type", new CPDF_Name("Catalog")); } CPDF_Object* pElement = pNewRoot->GetElement("Pages"); CPDF_Dictionary* pNewPages = pElement ? ToDictionary(pElement->GetDirect()) : nullptr; if (!pNewPages) { pNewPages = new CPDF_Dictionary; FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages); pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON)); } CFX_ByteString cbPageType = pNewPages->GetStringBy("Type", ""); if (cbPageType.Equal("")) { pNewPages->SetAt("Type", new CPDF_Name("Pages")); } CPDF_Array* pKeysArray = pNewPages->GetArrayBy("Kids"); if (!pKeysArray) { CPDF_Array* pNewKids = new CPDF_Array; FX_DWORD Kidsobjnum = -1; Kidsobjnum = pDestPDFDoc->AddIndirectObject(pNewKids); pNewPages->SetAt("Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum)); pNewPages->SetAt("Count", new CPDF_Number(0)); } return TRUE; } FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document* pSrcPDFDoc, std::vector* pPageNums, CPDF_Document* pDestPDFDoc, int nIndex) { int curpage = nIndex; std::unique_ptr pObjNumberMap(new ObjectNumberMap); int nSize = pdfium::CollectionSize(*pPageNums); for (int i = 0; i < nSize; ++i) { CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage); CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(pPageNums->at(i) - 1); if (!pSrcPageDict || !pCurPageDict) return FALSE; // Clone the page dictionary for (const auto& it : *pSrcPageDict) { const CFX_ByteString& cbSrcKeyStr = it.first; CPDF_Object* pObj = it.second; if (cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent"))) { if (pCurPageDict->KeyExist(cbSrcKeyStr)) pCurPageDict->RemoveAt(cbSrcKeyStr); pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone()); } } // inheritable item CPDF_Object* pInheritable = nullptr; // 1 MediaBox //required if (!pCurPageDict->KeyExist("MediaBox")) { pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox"); if (!pInheritable) { // Search the "CropBox" from source page dictionary, // if not exists,we take the letter size. pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); if (pInheritable) { pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); } else { // Make the default size to be letter size (8.5'x11') CPDF_Array* pArray = new CPDF_Array; pArray->AddNumber(0); pArray->AddNumber(0); pArray->AddNumber(612); pArray->AddNumber(792); pCurPageDict->SetAt("MediaBox", pArray); } } else { pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); } } // 2 Resources //required if (!pCurPageDict->KeyExist("Resources")) { pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources"); if (!pInheritable) return FALSE; pCurPageDict->SetAt("Resources", pInheritable->Clone()); } // 3 CropBox //Optional if (!pCurPageDict->KeyExist("CropBox")) { pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); if (pInheritable) pCurPageDict->SetAt("CropBox", pInheritable->Clone()); } // 4 Rotate //Optional if (!pCurPageDict->KeyExist("Rotate")) { pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate"); if (pInheritable) pCurPageDict->SetAt("Rotate", pInheritable->Clone()); } // Update the reference FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum(); FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum(); (*pObjNumberMap)[dwOldPageObj] = dwNewPageObj; UpdateReference(pCurPageDict, pDestPDFDoc, pObjNumberMap.get()); ++curpage; } return TRUE; } CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag( CPDF_Dictionary* pDict, CFX_ByteString nSrctag) { if (!pDict || nSrctag.IsEmpty()) return nullptr; if (!pDict->KeyExist("Parent") || !pDict->KeyExist("Type")) return nullptr; CPDF_Object* pType = pDict->GetElement("Type")->GetDirect(); if (!ToName(pType)) return nullptr; if (pType->GetString().Compare("Page")) return nullptr; CPDF_Dictionary* pp = ToDictionary(pDict->GetElement("Parent")->GetDirect()); if (!pp) return nullptr; if (pDict->KeyExist((const char*)nSrctag)) return pDict->GetElement((const char*)nSrctag); while (pp) { if (pp->KeyExist((const char*)nSrctag)) return pp->GetElement((const char*)nSrctag); if (!pp->KeyExist("Parent")) break; pp = ToDictionary(pp->GetElement("Parent")->GetDirect()); } return nullptr; } FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj, CPDF_Document* pDoc, ObjectNumberMap* pObjNumberMap) { switch (pObj->GetType()) { case CPDF_Object::REFERENCE: { CPDF_Reference* pReference = pObj->AsReference(); FX_DWORD newobjnum = GetNewObjId(pDoc, pObjNumberMap, pReference); if (newobjnum == 0) return FALSE; pReference->SetRef(pDoc, newobjnum); break; } case CPDF_Object::DICTIONARY: { CPDF_Dictionary* pDict = pObj->AsDictionary(); auto it = pDict->begin(); while (it != pDict->end()) { const CFX_ByteString& key = it->first; CPDF_Object* pNextObj = it->second; ++it; if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") || !FXSYS_strcmp(key, "First")) { continue; } if (pNextObj) { if (!UpdateReference(pNextObj, pDoc, pObjNumberMap)) pDict->RemoveAt(key); } else { return FALSE; } } break; } case CPDF_Object::ARRAY: { CPDF_Array* pArray = pObj->AsArray(); FX_DWORD count = pArray->GetCount(); for (FX_DWORD i = 0; i < count; ++i) { CPDF_Object* pNextObj = pArray->GetElement(i); if (!pNextObj) return FALSE; if (!UpdateReference(pNextObj, pDoc, pObjNumberMap)) return FALSE; } break; } case CPDF_Object::STREAM: { CPDF_Stream* pStream = pObj->AsStream(); CPDF_Dictionary* pDict = pStream->GetDict(); if (pDict) { if (!UpdateReference(pDict, pDoc, pObjNumberMap)) return FALSE; } else { return FALSE; } break; } default: break; } return TRUE; } FX_DWORD CPDF_PageOrganizer::GetNewObjId(CPDF_Document* pDoc, ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef) { if (!pRef) return 0; FX_DWORD dwObjnum = pRef->GetRefObjNum(); FX_DWORD dwNewObjNum = 0; const auto it = pObjNumberMap->find(dwObjnum); if (it != pObjNumberMap->end()) dwNewObjNum = it->second; if (dwNewObjNum) return dwNewObjNum; CPDF_Object* pDirect = pRef->GetDirect(); if (!pDirect) return 0; CPDF_Object* pClone = pDirect->Clone(); if (!pClone) return 0; if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) { if (pDictClone->KeyExist("Type")) { CFX_ByteString strType = pDictClone->GetStringBy("Type"); if (!FXSYS_stricmp(strType, "Pages")) { pDictClone->Release(); return 4; } if (!FXSYS_stricmp(strType, "Page")) { pDictClone->Release(); return 0; } } } dwNewObjNum = pDoc->AddIndirectObject(pClone); (*pObjNumberMap)[dwObjnum] = dwNewObjNum; if (!UpdateReference(pClone, pDoc, pObjNumberMap)) { pClone->Release(); return 0; } return dwNewObjNum; } FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, std::vector* pageArray, int nCount) { if (rangstring.GetLength() != 0) { rangstring.Remove(' '); int nLength = rangstring.GetLength(); CFX_ByteString cbCompareString("0123456789-,"); for (int i = 0; i < nLength; ++i) { if (cbCompareString.Find(rangstring[i]) == -1) return FALSE; } CFX_ByteString cbMidRange; int nStringFrom = 0; int nStringTo = 0; while (nStringTo < nLength) { nStringTo = rangstring.Find(',', nStringFrom); if (nStringTo == -1) nStringTo = nLength; cbMidRange = rangstring.Mid(nStringFrom, nStringTo - nStringFrom); int nMid = cbMidRange.Find('-'); if (nMid == -1) { long lPageNum = atol(cbMidRange); if (lPageNum <= 0 || lPageNum > nCount) return FALSE; pageArray->push_back((FX_WORD)lPageNum); } else { int nStartPageNum = atol(cbMidRange.Mid(0, nMid)); if (nStartPageNum == 0) return FALSE; ++nMid; int nEnd = cbMidRange.GetLength() - nMid; if (nEnd == 0) return FALSE; int nEndPageNum = atol(cbMidRange.Mid(nMid, nEnd)); if (nStartPageNum < 0 || nStartPageNum > nEndPageNum || nEndPageNum > nCount) { return FALSE; } for (int i = nStartPageNum; i <= nEndPageNum; ++i) { pageArray->push_back(i); } } nStringFrom = nStringTo + 1; } } return TRUE; } DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc, FPDF_BYTESTRING pagerange, int index) { CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc); if (!dest_doc) return FALSE; CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); if (!pSrcDoc) return FALSE; std::vector pageArray; int nCount = pSrcDoc->GetPageCount(); if (pagerange) { if (!ParserPageRangeString(pagerange, &pageArray, nCount)) return FALSE; } else { for (int i = 1; i <= nCount; ++i) { pageArray.push_back(i); } } CPDF_PageOrganizer pageOrg; pageOrg.PDFDocInit(pDestDoc, pSrcDoc); return pageOrg.ExportPage(pSrcDoc, &pageArray, pDestDoc, index); } DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) { CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc); if (!pDstDoc) return FALSE; CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); if (!pSrcDoc) return FALSE; CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot(); pSrcDict = pSrcDict->GetDictBy("ViewerPreferences"); if (!pSrcDict) return FALSE; CPDF_Dictionary* pDstDict = pDstDoc->GetRoot(); if (!pDstDict) return FALSE; pDstDict->SetAt("ViewerPreferences", pSrcDict->Clone(TRUE)); return TRUE; }