diff options
Diffstat (limited to 'fpdfsdk/fpdf_ppo.cpp')
-rw-r--r-- | fpdfsdk/fpdf_ppo.cpp | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp new file mode 100644 index 0000000000..b5e128f150 --- /dev/null +++ b/fpdfsdk/fpdf_ppo.cpp @@ -0,0 +1,779 @@ +// 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 <algorithm> +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#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<size_t, size_t> 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<size_t, size_t> 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<uint32_t>* 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<size_t> 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<uint32_t>(atoi(cbMidRange.c_str())); + if (pageNum <= 0 || pageNum > nCount) + return false; + pageArray->push_back(pageNum); + } else { + uint32_t nStartPageNum = pdfium::base::checked_cast<uint32_t>( + 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<uint32_t>( + 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; +} + +bool GetPageNumbers(ByteString pageRange, + CPDF_Document* pSrcDoc, + std::vector<uint32_t>* pageArray) { + uint32_t nCount = pSrcDoc->GetPageCount(); + if (!pageRange.IsEmpty()) { + if (!ParserPageRangeString(pageRange, nCount, pageArray)) + return false; + } else { + for (uint32_t i = 1; i <= nCount; ++i) { + pageArray->push_back(i); + } + } + return true; +} + +} // namespace + +class CPDF_PageOrganizer { + public: + CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc); + ~CPDF_PageOrganizer(); + + bool PDFDocInit(); + bool ExportPage(const std::vector<uint32_t>& pageNums, int nIndex); + bool ExportNPagesToOne(const std::vector<uint32_t>& pageNums, + float destPageWidth, + float destPageHeight, + unsigned int numPagesOnXAxis, + unsigned int numPagesOnYAxis); + + private: + using ObjectNumberMap = std::map<uint32_t, uint32_t>; + + 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 content + // string with the xobject reference surrounded by the transformation matrix. + void AddSubPage(CPDF_Dictionary* pPageDict, + const CFX_PointF& position, + float scale, + ByteString* content); + CPDF_Object* MakeXObject(CPDF_Dictionary* pSrcPageDict, + CPDF_Document* pDestDoc); + void FinishPage(CPDF_Dictionary* pCurPageDict, const ByteString& content); + + UnownedPtr<CPDF_Document> m_pDestPDFDoc; + UnownedPtr<CPDF_Document> m_pSrcPDFDoc; + uint32_t m_xobjectNum = 0; + CFX_SizeF m_pageSize; + // Key is XObject name + std::map<ByteString, UnownedPtr<CPDF_Object>> 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<CPDF_String>("Producer", "PDFium", false); + + ByteString cbRootType = pNewRoot->GetStringFor("Type", ""); + if (cbRootType.IsEmpty()) + pNewRoot->SetNewFor<CPDF_Name>("Type", "Catalog"); + + CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages"); + CPDF_Dictionary* pNewPages = + pElement ? ToDictionary(pElement->GetDirect()) : nullptr; + if (!pNewPages) { + pNewPages = m_pDestPDFDoc->NewIndirect<CPDF_Dictionary>(); + pNewRoot->SetNewFor<CPDF_Reference>("Pages", m_pDestPDFDoc.Get(), + pNewPages->GetObjNum()); + } + + ByteString cbPageType = pNewPages->GetStringFor("Type", ""); + if (cbPageType.IsEmpty()) + pNewPages->SetNewFor<CPDF_Name>("Type", "Pages"); + + if (!pNewPages->GetArrayFor("Kids")) { + pNewPages->SetNewFor<CPDF_Number>("Count", 0); + pNewPages->SetNewFor<CPDF_Reference>( + "Kids", m_pDestPDFDoc.Get(), + m_pDestPDFDoc->NewIndirect<CPDF_Array>()->GetObjNum()); + } + + return true; +} + +void CPDF_PageOrganizer::AddSubPage(CPDF_Dictionary* pPageDict, + const CFX_PointF& position, + float scale, + ByteString* content) { + ++m_xobjectNum; + // TODO(Xlou): A better name schema to avoid possible object name collision. + ByteString xobjectName = ByteString::Format("X%d", m_xobjectNum); + m_xobjs[xobjectName] = MakeXObject(pPageDict, m_pDestPDFDoc.Get()); + + 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" + << "/" << xobjectName << " Do Q\n"; + *content += ByteString(contentStream); +} + +CPDF_Object* CPDF_PageOrganizer::MakeXObject(CPDF_Dictionary* pSrcPageDict, + CPDF_Document* pDestDoc) { + ASSERT(pSrcPageDict); + + auto pObjNumberMap = pdfium::MakeUnique<ObjectNumberMap>(); + + CPDF_Object* pSrcContentObj = GetPageOrganizerPageContent(pSrcPageDict); + CPDF_Stream* pNewXObject = pDestDoc->NewIndirect<CPDF_Stream>( + nullptr, 0, + pdfium::MakeUnique<CPDF_Dictionary>(pDestDoc->GetByteStringPool())); + CPDF_Dictionary* pNewXObjectDict = pNewXObject->GetDict(); + const ByteString resourceString = "Resources"; + if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, resourceString)) { + // Use a default empty resources if it does not exist. + pNewXObjectDict->SetNewFor<CPDF_Dictionary>(resourceString); + } + CPDF_Dictionary* pSrcRes = pSrcPageDict->GetDictFor(resourceString); + if (pSrcRes) { + uint32_t dwSrcPageResourcesObj = pSrcRes->GetObjNum(); + uint32_t dwNewXobjectResourcesObj = + pNewXObjectDict->GetDictFor(resourceString)->GetObjNum(); + (*pObjNumberMap)[dwSrcPageResourcesObj] = dwNewXobjectResourcesObj; + CPDF_Dictionary* pNewXORes = pNewXObjectDict->GetDictFor(resourceString); + UpdateReference(pNewXORes, pObjNumberMap.get()); + } + + pNewXObjectDict->SetNewFor<CPDF_Name>("Type", "XObject"); + pNewXObjectDict->SetNewFor<CPDF_Name>("Subtype", "Form"); + pNewXObjectDict->SetNewFor<CPDF_Number>("FormType", 1); + pNewXObjectDict->SetRectFor("BBox", GetTrimBox(pSrcPageDict)); + // TODO(xlou): add matrix field to pNewXObjectDict. + + if (CPDF_Array* pSrcContentArray = ToArray(pSrcContentObj)) { + ByteString srcContentStream; + for (size_t i = 0; i < pSrcContentArray->GetCount(); ++i) { + CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i); + auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream); + pAcc->LoadAllDataFiltered(); + ByteString sStream(pAcc->GetData(), pAcc->GetSize()); + srcContentStream += sStream; + srcContentStream += "\n"; + } + pNewXObject->SetDataAndRemoveFilter(srcContentStream.raw_str(), + srcContentStream.GetLength()); + } else { + CPDF_Stream* pStream = pSrcContentObj->AsStream(); + auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream); + pAcc->LoadAllDataFiltered(); + ByteString sStream(pAcc->GetData(), pAcc->GetSize()); + pNewXObject->SetDataAndRemoveFilter(sStream.raw_str(), sStream.GetLength()); + } + + return pNewXObject; +} + +// static +void CPDF_PageOrganizer::SetMediaBox(CPDF_Dictionary* pDestPageDict, + const CFX_SizeF& pagesize) { + CPDF_Array* pArray = pDestPageDict->SetNewFor<CPDF_Array>("MediaBox"); + pArray->AddNew<CPDF_Number>(0); + pArray->AddNew<CPDF_Number>(0); + pArray->AddNew<CPDF_Number>(pagesize.width); + pArray->AddNew<CPDF_Number>(pagesize.height); +} + +bool CPDF_PageOrganizer::ExportPage(const std::vector<uint32_t>& pageNums, + int nIndex) { + int curpage = nIndex; + auto pObjNumberMap = pdfium::MakeUnique<ObjectNumberMap>(); + 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<CPDF_Array>("MediaBox"); + pArray->AddNew<CPDF_Number>(0); + pArray->AddNew<CPDF_Number>(0); + pArray->AddNew<CPDF_Number>(612); + pArray->AddNew<CPDF_Number>(792); + } + } + + // 2 Resources - required + if (!CopyInheritable(pCurPageDict, pSrcPageDict, "Resources")) { + // Use a default empty resources if it does not exist. + pCurPageDict->SetNewFor<CPDF_Dictionary>("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& content) { + ASSERT(pCurPageDict); + + CPDF_Dictionary* pRes = pCurPageDict->GetDictFor("Resources"); + if (!pRes) + pRes = pCurPageDict->SetNewFor<CPDF_Dictionary>("Resources"); + + CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject"); + if (!pPageXObject) + pPageXObject = pRes->SetNewFor<CPDF_Dictionary>("XObject"); + + for (auto& it : m_xobjs) { + CPDF_Object* pObj = it.second.Get(); + pPageXObject->SetNewFor<CPDF_Reference>(it.first, m_pDestPDFDoc.Get(), + pObj->GetObjNum()); + } + + auto pDict = pdfium::MakeUnique<CPDF_Dictionary>( + m_pDestPDFDoc.Get()->GetByteStringPool()); + CPDF_Stream* pStream = m_pDestPDFDoc.Get()->NewIndirect<CPDF_Stream>( + nullptr, 0, std::move(pDict)); + pStream->SetData(content.raw_str(), content.GetLength()); + pCurPageDict->SetNewFor<CPDF_Reference>("Contents", m_pDestPDFDoc.Get(), + pStream->GetObjNum()); + m_xobjs.clear(); +} + +bool CPDF_PageOrganizer::ExportNPagesToOne( + const std::vector<uint32_t>& 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); + + 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 content; + size_t innerPageMax = + std::min(outerPage + numPagesPerSheet, pageNums.size()); + 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, + &content); + } + + // Finish up the current page. + FinishPage(pCurPageDict, content); + ++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<CPDF_Object> 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<uint32_t> pageArray; + if (!GetPageNumbers(pagerange, pSrcDoc, &pageArray)) + return false; + + CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc); + + if (!pageOrg.PDFDocInit()) + return false; + + return pageOrg.ExportPage(pageArray, 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<void, FPDFDocumentDeleter> output_doc( + FPDF_CreateNewDocument()); + if (!output_doc) + return nullptr; + + CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get()); + ASSERT(pDestDoc); + + std::vector<uint32_t> pageArray; + if (!GetPageNumbers(nullptr, pSrcDoc, &pageArray)) + return nullptr; + + CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc); + if (!pageOrg.PDFDocInit() || + !pageOrg.ExportNPagesToOne(pageArray, 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; +} |