// 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 #include #include "core/fpdfapi/page/cpdf_page.h" #include "core/fpdfapi/page/cpdf_pageobject.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_object.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/cpdf_stream_acc.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fxcrt/retain_ptr.h" #include "core/fxcrt/unowned_ptr.h" #include "fpdfsdk/fsdk_define.h" #include "public/cpp/fpdf_deleters.h" #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" namespace { // Struct that stores sub page origin and scale information. When importing // more than one pages onto the same page, most likely the pages will need to be // scaled down, and scale is in range of (0, 1) exclusive. struct NupPageSettings { CFX_PointF subPageStartPoint; float scale; }; // Calculates the N-up parameters. When importing multiple pages into one page. // The space of output page is evenly divided along the X axis and Y axis based // on the input |numPagesOnXAxis| and |numPagesOnYAxis|. class NupState { public: NupState(float destPageWidth, float destPageHeight, unsigned int numPagesOnXAxis, unsigned int numPagesOnYAxis); // Calculate sub page origin and scale with the source page |inWidth| and // |inHeight| and new page |m_subPageWidth| and |m_subPageWidth|. With the // result stored in out parameter |ret|. void CalculateNewPagePosition(float inWidth, float inHeight, NupPageSettings* ret); private: // Helper function to get the subX, subY pair based on |m_subPageIndex|. // The space of output page is evenly divided into slots along x and y axis. // subX and subY are 0-based indices that indicate which allocation slot to // use. std::pair ConvertPageOrder() const; // Given the |subx| and |suby| subpage position within a page, and a source // page with dimensions of |inPageWidth| x |inPageHeight|, calculate the sub // page's origin and scale, and store them in |ret|. void CalculatePageEdit(size_t subx, size_t suby, float inPageWidth, float inPageHeight, NupPageSettings* ret) const; const size_t m_numPagesOnXAxis; const size_t m_numPagesOnYAxis; const float m_destPageWidth; const float m_destPageHeight; const size_t m_numPagesPerSheet; float m_subPageWidth; float m_subPageHeight; // A 0-based index, in range of (0, m_numPagesPerSheet - 1) inclusive. size_t m_subPageIndex = 0; }; NupState::NupState(float destPageWidth, float destPageHeight, unsigned int numPagesOnXAxis, unsigned int numPagesOnYAxis) : m_numPagesOnXAxis(numPagesOnXAxis), m_numPagesOnYAxis(numPagesOnYAxis), m_destPageWidth(destPageWidth), m_destPageHeight(destPageHeight), m_numPagesPerSheet(numPagesOnXAxis * numPagesOnYAxis) { ASSERT(m_numPagesOnXAxis > 0); ASSERT(m_numPagesOnYAxis > 0); ASSERT(m_destPageWidth > 0); ASSERT(m_destPageHeight > 0); m_subPageWidth = m_destPageWidth / m_numPagesOnXAxis; m_subPageHeight = m_destPageHeight / m_numPagesOnYAxis; } std::pair NupState::ConvertPageOrder() const { size_t subX = m_subPageIndex % m_numPagesOnXAxis; size_t subY = m_subPageIndex / m_numPagesOnXAxis; // Y Axis, pages start from the top of the output page. subY = m_numPagesOnYAxis - subY - 1; return {subX, subY}; } void NupState::CalculatePageEdit(size_t subXPos, size_t subYPos, float inPageWidth, float inPageHeight, NupPageSettings* pageEdit) const { pageEdit->subPageStartPoint.x = subXPos * m_subPageWidth; pageEdit->subPageStartPoint.y = subYPos * m_subPageHeight; const float xScale = m_subPageWidth / inPageWidth; const float yScale = m_subPageHeight / inPageHeight; pageEdit->scale = std::min(xScale, yScale); float subWidth = inPageWidth * pageEdit->scale; float subHeight = inPageHeight * pageEdit->scale; if (xScale > yScale) pageEdit->subPageStartPoint.x += (m_subPageWidth - subWidth) / 2; else pageEdit->subPageStartPoint.y += (m_subPageHeight - subHeight) / 2; } void NupState::CalculateNewPagePosition(float inWidth, float inHeight, NupPageSettings* pageEdit) { if (m_subPageIndex >= m_numPagesPerSheet) m_subPageIndex = 0; size_t subX; size_t subY; std::tie(subX, subY) = ConvertPageOrder(); CalculatePageEdit(subX, subY, inWidth, inHeight, pageEdit); ++m_subPageIndex; } CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict, const ByteString& bsSrcTag) { if (!pDict || bsSrcTag.IsEmpty()) return nullptr; if (!pDict->KeyExist("Parent") || !pDict->KeyExist("Type")) return nullptr; CPDF_Object* pType = pDict->GetObjectFor("Type")->GetDirect(); if (!ToName(pType)) return nullptr; if (pType->GetString().Compare("Page")) return nullptr; CPDF_Dictionary* pp = ToDictionary(pDict->GetObjectFor("Parent")->GetDirect()); if (!pp) return nullptr; if (pDict->KeyExist(bsSrcTag)) return pDict->GetObjectFor(bsSrcTag); while (pp) { if (pp->KeyExist(bsSrcTag)) return pp->GetObjectFor(bsSrcTag); if (!pp->KeyExist("Parent")) break; pp = ToDictionary(pp->GetObjectFor("Parent")->GetDirect()); } return nullptr; } CFX_FloatRect GetMediaBox(CPDF_Dictionary* pPageDict) { CPDF_Object* pMediaBox = PageDictGetInheritableTag(pPageDict, "MediaBox"); CPDF_Array* pArray = ToArray(pMediaBox->GetDirect()); if (!pArray) return CFX_FloatRect(); return pArray->GetRect(); } CFX_FloatRect GetCropBox(CPDF_Dictionary* pPageDict) { if (pPageDict->KeyExist("CropBox")) return pPageDict->GetRectFor("CropBox"); return GetMediaBox(pPageDict); } CFX_FloatRect GetTrimBox(CPDF_Dictionary* pPageDict) { if (pPageDict->KeyExist("TrimBox")) return pPageDict->GetRectFor("TrimBox"); return GetCropBox(pPageDict); } CPDF_Object* GetPageOrganizerPageContent(CPDF_Dictionary* pPageDict) { return pPageDict ? pPageDict->GetDirectObjectFor("Contents") : nullptr; } bool CopyInheritable(CPDF_Dictionary* pCurPageDict, CPDF_Dictionary* pSrcPageDict, const ByteString& key) { if (pCurPageDict->KeyExist(key)) return true; CPDF_Object* pInheritable = PageDictGetInheritableTag(pSrcPageDict, key); if (!pInheritable) return false; pCurPageDict->SetFor(key, pInheritable->Clone()); return true; } bool ParserPageRangeString(ByteString rangstring, uint32_t nCount, std::vector* pageArray) { if (rangstring.IsEmpty()) return true; rangstring.Remove(' '); size_t nLength = rangstring.GetLength(); ByteString cbCompareString("0123456789-,"); for (size_t i = 0; i < nLength; ++i) { if (!cbCompareString.Contains(rangstring[i])) return false; } ByteString cbMidRange; size_t nStringFrom = 0; Optional nStringTo = 0; while (nStringTo < nLength) { nStringTo = rangstring.Find(',', nStringFrom); if (!nStringTo.has_value()) nStringTo = nLength; cbMidRange = rangstring.Mid(nStringFrom, nStringTo.value() - nStringFrom); auto nMid = cbMidRange.Find('-'); if (!nMid.has_value()) { uint32_t pageNum = pdfium::base::checked_cast(atoi(cbMidRange.c_str())); if (pageNum <= 0 || pageNum > nCount) return false; pageArray->push_back(pageNum); } else { uint32_t nStartPageNum = pdfium::base::checked_cast( atoi(cbMidRange.Left(nMid.value()).c_str())); if (nStartPageNum == 0) return false; nMid = nMid.value() + 1; size_t nEnd = cbMidRange.GetLength() - nMid.value(); if (nEnd == 0) return false; uint32_t nEndPageNum = pdfium::base::checked_cast( atoi(cbMidRange.Mid(nMid.value(), nEnd).c_str())); if (nStartPageNum < 0 || nStartPageNum > nEndPageNum || nEndPageNum > nCount) { return false; } for (uint32_t i = nStartPageNum; i <= nEndPageNum; ++i) { pageArray->push_back(i); } } nStringFrom = nStringTo.value() + 1; } return true; } std::vector GetPageNumbers(const CPDF_Document& doc, const ByteString& bsPageRange) { std::vector page_numbers; uint32_t nCount = doc.GetPageCount(); if (bsPageRange.IsEmpty()) { for (uint32_t i = 1; i <= nCount; ++i) page_numbers.push_back(i); } else { if (!ParserPageRangeString(bsPageRange, nCount, &page_numbers)) page_numbers.clear(); } return page_numbers; } } // namespace class CPDF_PageOrganizer { public: CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc); ~CPDF_PageOrganizer(); bool PDFDocInit(); bool ExportPage(const std::vector& pageNums, int nIndex); bool ExportNPagesToOne(const std::vector& pageNums, float destPageWidth, float destPageHeight, unsigned int numPagesOnXAxis, unsigned int numPagesOnYAxis); private: // Map source page object number to XObject object number. using ObjectNumberMap = std::map; // Map page object number to XObject object name. using PageXObjectMap = std::map; // Map XObject's object name to it's object number. using XObjectNameNumberMap = std::map; static void SetMediaBox(CPDF_Dictionary* pDestPageDict, const CFX_SizeF& pagesize); bool UpdateReference(CPDF_Object* pObj, ObjectNumberMap* pObjNumberMap); uint32_t GetNewObjId(ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef); // Creates a xobject from the source page dictionary, and appends the // bsContent string with the xobject reference surrounded by the // transformation matrix. void AddSubPage(CPDF_Dictionary* pPageDict, const CFX_PointF& position, float scale, ObjectNumberMap* pObjNumberMap, PageXObjectMap* pPageXObjectMap, XObjectNameNumberMap* pXObjNameNumberMap, ByteString* bsContent); uint32_t MakeXObject(CPDF_Dictionary* pSrcPageDict, ObjectNumberMap* pObjNumberMap, CPDF_Document* pDestDoc); void FinishPage(CPDF_Dictionary* pCurPageDict, const ByteString& bsContent, XObjectNameNumberMap* pXObjNameNumberMap); UnownedPtr m_pDestPDFDoc; UnownedPtr m_pSrcPDFDoc; uint32_t m_xobjectNum = 0; CFX_SizeF m_pageSize; // Mapping of XObject name and XObject object number of the entire document. // Key is XObject name. XObjectNameNumberMap m_xobjs; }; CPDF_PageOrganizer::CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc) : m_pDestPDFDoc(pDestPDFDoc), m_pSrcPDFDoc(pSrcPDFDoc) {} CPDF_PageOrganizer::~CPDF_PageOrganizer() {} bool CPDF_PageOrganizer::PDFDocInit() { ASSERT(m_pDestPDFDoc); ASSERT(m_pSrcPDFDoc); CPDF_Dictionary* pNewRoot = m_pDestPDFDoc->GetRoot(); if (!pNewRoot) return false; CPDF_Dictionary* pDocInfoDict = m_pDestPDFDoc->GetInfo(); if (!pDocInfoDict) return false; pDocInfoDict->SetNewFor("Producer", "PDFium", false); ByteString cbRootType = pNewRoot->GetStringFor("Type", ""); if (cbRootType.IsEmpty()) pNewRoot->SetNewFor("Type", "Catalog"); CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages"); CPDF_Dictionary* pNewPages = pElement ? ToDictionary(pElement->GetDirect()) : nullptr; if (!pNewPages) { pNewPages = m_pDestPDFDoc->NewIndirect(); pNewRoot->SetNewFor("Pages", m_pDestPDFDoc.Get(), pNewPages->GetObjNum()); } ByteString cbPageType = pNewPages->GetStringFor("Type", ""); if (cbPageType.IsEmpty()) pNewPages->SetNewFor("Type", "Pages"); if (!pNewPages->GetArrayFor("Kids")) { pNewPages->SetNewFor("Count", 0); pNewPages->SetNewFor( "Kids", m_pDestPDFDoc.Get(), m_pDestPDFDoc->NewIndirect()->GetObjNum()); } return true; } void CPDF_PageOrganizer::AddSubPage(CPDF_Dictionary* pPageDict, const CFX_PointF& position, float scale, ObjectNumberMap* pObjNumberMap, PageXObjectMap* pPageXObjectMap, XObjectNameNumberMap* pXObjNameNumberMap, ByteString* bsContent) { uint32_t dwPageObjnum = pPageDict->GetObjNum(); ByteString bsXObjectName; const auto it = pPageXObjectMap->find(dwPageObjnum); if (it != pPageXObjectMap->end()) { bsXObjectName = it->second; } else { ++m_xobjectNum; // TODO(Xlou): A better name schema to avoid possible object name collision. bsXObjectName = ByteString::Format("X%d", m_xobjectNum); m_xobjs[bsXObjectName] = MakeXObject(pPageDict, pObjNumberMap, m_pDestPDFDoc.Get()); (*pPageXObjectMap)[dwPageObjnum] = bsXObjectName; } (*pXObjNameNumberMap)[bsXObjectName] = m_xobjs[bsXObjectName]; CFX_Matrix matrix; matrix.Scale(scale, scale); matrix.Translate(position.x, position.y); std::ostringstream contentStream; contentStream << "q\n" << matrix.a << " " << matrix.b << " " << matrix.c << " " << matrix.d << " " << matrix.e << " " << matrix.f << " cm\n" << "/" << bsXObjectName << " Do Q\n"; *bsContent += ByteString(contentStream); } uint32_t CPDF_PageOrganizer::MakeXObject(CPDF_Dictionary* pSrcPageDict, ObjectNumberMap* pObjNumberMap, CPDF_Document* pDestDoc) { ASSERT(pSrcPageDict); CPDF_Object* pSrcContentObj = GetPageOrganizerPageContent(pSrcPageDict); CPDF_Stream* pNewXObject = pDestDoc->NewIndirect( nullptr, 0, pdfium::MakeUnique(pDestDoc->GetByteStringPool())); CPDF_Dictionary* pNewXObjectDict = pNewXObject->GetDict(); const ByteString bsResourceString = "Resources"; if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, bsResourceString)) { // Use a default empty resources if it does not exist. pNewXObjectDict->SetNewFor(bsResourceString); } uint32_t dwSrcPageObj = pSrcPageDict->GetObjNum(); uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum(); (*pObjNumberMap)[dwSrcPageObj] = dwNewXobjectObj; UpdateReference(pNewXObjectDict, pObjNumberMap); pNewXObjectDict->SetNewFor("Type", "XObject"); pNewXObjectDict->SetNewFor("Subtype", "Form"); pNewXObjectDict->SetNewFor("FormType", 1); pNewXObjectDict->SetRectFor("BBox", GetTrimBox(pSrcPageDict)); // TODO(xlou): add matrix field to pNewXObjectDict. if (CPDF_Array* pSrcContentArray = ToArray(pSrcContentObj)) { ByteString bsSrcContentStream; for (size_t i = 0; i < pSrcContentArray->GetCount(); ++i) { CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i); auto pAcc = pdfium::MakeRetain(pStream); pAcc->LoadAllDataFiltered(); ByteString bsStream(pAcc->GetData(), pAcc->GetSize()); bsSrcContentStream += bsStream; bsSrcContentStream += "\n"; } pNewXObject->SetDataAndRemoveFilter(bsSrcContentStream.raw_str(), bsSrcContentStream.GetLength()); } else { CPDF_Stream* pStream = pSrcContentObj->AsStream(); auto pAcc = pdfium::MakeRetain(pStream); pAcc->LoadAllDataFiltered(); ByteString bsStream(pAcc->GetData(), pAcc->GetSize()); pNewXObject->SetDataAndRemoveFilter(bsStream.raw_str(), bsStream.GetLength()); } return pNewXObject->GetObjNum(); } // static void CPDF_PageOrganizer::SetMediaBox(CPDF_Dictionary* pDestPageDict, const CFX_SizeF& pagesize) { CPDF_Array* pArray = pDestPageDict->SetNewFor("MediaBox"); pArray->AddNew(0); pArray->AddNew(0); pArray->AddNew(pagesize.width); pArray->AddNew(pagesize.height); } bool CPDF_PageOrganizer::ExportPage(const std::vector& pageNums, int nIndex) { int curpage = nIndex; auto pObjNumberMap = pdfium::MakeUnique(); for (size_t i = 0; i < pageNums.size(); ++i) { CPDF_Dictionary* pCurPageDict = m_pDestPDFDoc->CreateNewPage(curpage); CPDF_Dictionary* pSrcPageDict = m_pSrcPDFDoc->GetPage(pageNums[i] - 1); if (!pSrcPageDict || !pCurPageDict) return false; // Clone the page dictionary for (const auto& it : *pSrcPageDict) { const ByteString& cbSrcKeyStr = it.first; if (cbSrcKeyStr == "Type" || cbSrcKeyStr == "Parent") continue; CPDF_Object* pObj = it.second.get(); pCurPageDict->SetFor(cbSrcKeyStr, pObj->Clone()); } // inheritable item // Even though some entries are required by the PDF spec, there exist // PDFs that omit them. Set some defaults in this case. // 1 MediaBox - required if (!CopyInheritable(pCurPageDict, pSrcPageDict, "MediaBox")) { // Search for "CropBox" in the source page dictionary. // If it does not exist, use the default letter size. CPDF_Object* pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); if (pInheritable) { pCurPageDict->SetFor("MediaBox", pInheritable->Clone()); } else { // Make the default size letter size (8.5"x11") CPDF_Array* pArray = pCurPageDict->SetNewFor("MediaBox"); pArray->AddNew(0); pArray->AddNew(0); pArray->AddNew(612); pArray->AddNew(792); } } // 2 Resources - required if (!CopyInheritable(pCurPageDict, pSrcPageDict, "Resources")) { // Use a default empty resources if it does not exist. pCurPageDict->SetNewFor("Resources"); } // 3 CropBox - optional CopyInheritable(pCurPageDict, pSrcPageDict, "CropBox"); // 4 Rotate - optional CopyInheritable(pCurPageDict, pSrcPageDict, "Rotate"); // Update the reference uint32_t dwOldPageObj = pSrcPageDict->GetObjNum(); uint32_t dwNewPageObj = pCurPageDict->GetObjNum(); (*pObjNumberMap)[dwOldPageObj] = dwNewPageObj; UpdateReference(pCurPageDict, pObjNumberMap.get()); ++curpage; } return true; } void CPDF_PageOrganizer::FinishPage(CPDF_Dictionary* pCurPageDict, const ByteString& bsContent, XObjectNameNumberMap* pXObjNameNumberMap) { ASSERT(pCurPageDict); CPDF_Dictionary* pRes = pCurPageDict->GetDictFor("Resources"); if (!pRes) pRes = pCurPageDict->SetNewFor("Resources"); CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject"); if (!pPageXObject) pPageXObject = pRes->SetNewFor("XObject"); for (auto& it : *pXObjNameNumberMap) { pPageXObject->SetNewFor(it.first, m_pDestPDFDoc.Get(), it.second); } auto pDict = pdfium::MakeUnique( m_pDestPDFDoc.Get()->GetByteStringPool()); CPDF_Stream* pStream = m_pDestPDFDoc.Get()->NewIndirect( nullptr, 0, std::move(pDict)); pStream->SetData(bsContent.raw_str(), bsContent.GetLength()); pCurPageDict->SetNewFor("Contents", m_pDestPDFDoc.Get(), pStream->GetObjNum()); } bool CPDF_PageOrganizer::ExportNPagesToOne( const std::vector& pageNums, float destPageWidth, float destPageHeight, unsigned int numPagesOnXAxis, unsigned int numPagesOnYAxis) { FX_SAFE_SIZE_T safe_numPagesPerSheet = numPagesOnXAxis; safe_numPagesPerSheet *= numPagesOnYAxis; if (!safe_numPagesPerSheet.IsValid()) return false; size_t numPagesPerSheet = safe_numPagesPerSheet.ValueOrDie(); if (numPagesPerSheet == 1) return ExportPage(pageNums, 0); // Mapping of source page object number and XObject object number. // Used to update refernece. ObjectNumberMap objectNumberMap; // Mapping of source page object number and XObject name of the entire doc. // If there are two pages that are identical and have the same object number, // we can reuse one created XObject. PageXObjectMap pageXObjectMap; const CFX_SizeF pagesize(destPageWidth, destPageHeight); NupState nupState(destPageWidth, destPageHeight, numPagesOnXAxis, numPagesOnYAxis); size_t curpage = 0; for (size_t outerPage = 0; outerPage < pageNums.size(); outerPage += numPagesPerSheet) { // Create a new page CPDF_Dictionary* pCurPageDict = m_pDestPDFDoc->CreateNewPage(curpage); if (!pCurPageDict) return false; SetMediaBox(pCurPageDict, pagesize); ByteString bsContent; size_t innerPageMax = std::min(outerPage + numPagesPerSheet, pageNums.size()); // Mapping of XObject name and XObject object number of one page. XObjectNameNumberMap xObjectNameNumberMap; for (size_t innerPage = outerPage; innerPage < innerPageMax; ++innerPage) { CPDF_Dictionary* pSrcPageDict = m_pSrcPDFDoc->GetPage(pageNums[innerPage] - 1); if (!pSrcPageDict) return false; CPDF_Page srcPage(m_pSrcPDFDoc.Get(), pSrcPageDict, true); NupPageSettings pgEdit; nupState.CalculateNewPagePosition(srcPage.GetPageWidth(), srcPage.GetPageHeight(), &pgEdit); AddSubPage(pSrcPageDict, pgEdit.subPageStartPoint, pgEdit.scale, &objectNumberMap, &pageXObjectMap, &xObjectNameNumberMap, &bsContent); } // Finish up the current page. FinishPage(pCurPageDict, bsContent, &xObjectNameNumberMap); ++curpage; } return true; } bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj, ObjectNumberMap* pObjNumberMap) { switch (pObj->GetType()) { case CPDF_Object::REFERENCE: { CPDF_Reference* pReference = pObj->AsReference(); uint32_t newobjnum = GetNewObjId(pObjNumberMap, pReference); if (newobjnum == 0) return false; pReference->SetRef(m_pDestPDFDoc.Get(), newobjnum); break; } case CPDF_Object::DICTIONARY: { CPDF_Dictionary* pDict = pObj->AsDictionary(); auto it = pDict->begin(); while (it != pDict->end()) { const ByteString& key = it->first; CPDF_Object* pNextObj = it->second.get(); ++it; if (key == "Parent" || key == "Prev" || key == "First") continue; if (!pNextObj) return false; if (!UpdateReference(pNextObj, pObjNumberMap)) pDict->RemoveFor(key); } break; } case CPDF_Object::ARRAY: { CPDF_Array* pArray = pObj->AsArray(); for (size_t i = 0; i < pArray->GetCount(); ++i) { CPDF_Object* pNextObj = pArray->GetObjectAt(i); if (!pNextObj) return false; if (!UpdateReference(pNextObj, pObjNumberMap)) return false; } break; } case CPDF_Object::STREAM: { CPDF_Stream* pStream = pObj->AsStream(); CPDF_Dictionary* pDict = pStream->GetDict(); if (!pDict) return false; if (!UpdateReference(pDict, pObjNumberMap)) return false; break; } default: break; } return true; } uint32_t CPDF_PageOrganizer::GetNewObjId(ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef) { if (!pRef) return 0; uint32_t dwObjnum = pRef->GetRefObjNum(); uint32_t 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; std::unique_ptr pClone = pDirect->Clone(); if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) { if (pDictClone->KeyExist("Type")) { ByteString strType = pDictClone->GetStringFor("Type"); if (!FXSYS_stricmp(strType.c_str(), "Pages")) return 4; if (!FXSYS_stricmp(strType.c_str(), "Page")) return 0; } } CPDF_Object* pUnownedClone = m_pDestPDFDoc->AddIndirectObject(std::move(pClone)); dwNewObjNum = pUnownedClone->GetObjNum(); (*pObjNumberMap)[dwObjnum] = dwNewObjNum; if (!UpdateReference(pUnownedClone, pObjNumberMap)) return 0; return dwNewObjNum; } FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV 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 page_numbers = GetPageNumbers(*pSrcDoc, pagerange); if (page_numbers.empty()) return false; CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc); if (!pageOrg.PDFDocInit()) return false; return pageOrg.ExportPage(page_numbers, index); } FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc, float output_width, float output_height, unsigned int num_pages_on_x_axis, unsigned int num_pages_on_y_axis) { CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); if (!pSrcDoc) return nullptr; if (output_width <= 0 || output_height <= 0 || num_pages_on_x_axis <= 0 || num_pages_on_y_axis <= 0) { return nullptr; } std::unique_ptr output_doc( FPDF_CreateNewDocument()); if (!output_doc) return nullptr; CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get()); ASSERT(pDestDoc); std::vector page_numbers = GetPageNumbers(*pSrcDoc, ByteString()); if (page_numbers.empty()) return nullptr; CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc); if (!pageOrg.PDFDocInit() || !pageOrg.ExportNPagesToOne(page_numbers, output_width, output_height, num_pages_on_x_axis, num_pages_on_y_axis)) { return nullptr; } return output_doc.release(); } FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV 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->GetDictFor("ViewerPreferences"); if (!pSrcDict) return false; CPDF_Dictionary* pDstDict = pDstDoc->GetRoot(); if (!pDstDict) return false; pDstDict->SetFor("ViewerPreferences", pSrcDict->CloneDirectObject()); return true; }