From f7c84e910899237ffe673de83cdec6dc49f073b4 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Fri, 2 Feb 2018 00:01:39 +0000 Subject: Fix IWYU errors in fpdfppo.cpp. Also rename the file to fpdf_ppo.cpp so it is consistent with the public header file name. This makes the linter happy. Change-Id: Ib3608c78111842579898c55f1399982ab2549310 Reviewed-on: https://pdfium-review.googlesource.com/24970 Commit-Queue: Lei Zhang Reviewed-by: dsinclair --- BUILD.gn | 2 +- fpdfsdk/fpdf_ppo.cpp | 779 +++++++++++++++++++++++++++++++++++++++++++++++++++ fpdfsdk/fpdfppo.cpp | 778 -------------------------------------------------- 3 files changed, 780 insertions(+), 779 deletions(-) create mode 100644 fpdfsdk/fpdf_ppo.cpp delete mode 100644 fpdfsdk/fpdfppo.cpp diff --git a/BUILD.gn b/BUILD.gn index 54aa448b89..988d376ff5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -136,6 +136,7 @@ jumbo_static_library("pdfium") { "fpdfsdk/fpdf_dataavail.cpp", "fpdfsdk/fpdf_ext.cpp", "fpdfsdk/fpdf_flatten.cpp", + "fpdfsdk/fpdf_ppo.cpp", "fpdfsdk/fpdf_progressive.cpp", "fpdfsdk/fpdf_searchex.cpp", "fpdfsdk/fpdf_structtree.cpp", @@ -150,7 +151,6 @@ jumbo_static_library("pdfium") { "fpdfsdk/fpdfeditpath.cpp", "fpdfsdk/fpdfedittext.cpp", "fpdfsdk/fpdfformfill.cpp", - "fpdfsdk/fpdfppo.cpp", "fpdfsdk/fpdfsave.cpp", "fpdfsdk/fpdftext.cpp", "fpdfsdk/fpdfview.cpp", 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 +#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; +} + +bool GetPageNumbers(ByteString pageRange, + CPDF_Document* pSrcDoc, + std::vector* 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& pageNums, int nIndex); + bool ExportNPagesToOne(const std::vector& pageNums, + float destPageWidth, + float destPageHeight, + unsigned int numPagesOnXAxis, + unsigned int numPagesOnYAxis); + + private: + using ObjectNumberMap = 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 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 m_pDestPDFDoc; + UnownedPtr m_pSrcPDFDoc; + uint32_t m_xobjectNum = 0; + CFX_SizeF m_pageSize; + // Key is XObject name + std::map> 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, + 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(); + + CPDF_Object* pSrcContentObj = GetPageOrganizerPageContent(pSrcPageDict); + CPDF_Stream* pNewXObject = pDestDoc->NewIndirect( + nullptr, 0, + pdfium::MakeUnique(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(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("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 srcContentStream; + for (size_t i = 0; i < pSrcContentArray->GetCount(); ++i) { + CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i); + auto pAcc = pdfium::MakeRetain(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(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("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& content) { + 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 : m_xobjs) { + CPDF_Object* pObj = it.second.Get(); + pPageXObject->SetNewFor(it.first, m_pDestPDFDoc.Get(), + pObj->GetObjNum()); + } + + auto pDict = pdfium::MakeUnique( + m_pDestPDFDoc.Get()->GetByteStringPool()); + CPDF_Stream* pStream = m_pDestPDFDoc.Get()->NewIndirect( + nullptr, 0, std::move(pDict)); + pStream->SetData(content.raw_str(), content.GetLength()); + pCurPageDict->SetNewFor("Contents", m_pDestPDFDoc.Get(), + pStream->GetObjNum()); + m_xobjs.clear(); +} + +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); + + 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 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 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 output_doc( + FPDF_CreateNewDocument()); + if (!output_doc) + return nullptr; + + CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get()); + ASSERT(pDestDoc); + + std::vector 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; +} diff --git a/fpdfsdk/fpdfppo.cpp b/fpdfsdk/fpdfppo.cpp deleted file mode 100644 index 3e6f099277..0000000000 --- a/fpdfsdk/fpdfppo.cpp +++ /dev/null @@ -1,778 +0,0 @@ -// 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 "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; -} - -bool GetPageNumbers(ByteString pageRange, - CPDF_Document* pSrcDoc, - std::vector* 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& pageNums, int nIndex); - bool ExportNPagesToOne(const std::vector& pageNums, - float destPageWidth, - float destPageHeight, - unsigned int numPagesOnXAxis, - unsigned int numPagesOnYAxis); - - private: - using ObjectNumberMap = 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 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 m_pDestPDFDoc; - UnownedPtr m_pSrcPDFDoc; - uint32_t m_xobjectNum = 0; - CFX_SizeF m_pageSize; - // Key is XObject name - std::map> 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, - 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(); - - CPDF_Object* pSrcContentObj = GetPageOrganizerPageContent(pSrcPageDict); - CPDF_Stream* pNewXObject = pDestDoc->NewIndirect( - nullptr, 0, - pdfium::MakeUnique(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(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("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 srcContentStream; - for (size_t i = 0; i < pSrcContentArray->GetCount(); ++i) { - CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i); - auto pAcc = pdfium::MakeRetain(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(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("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& content) { - 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 : m_xobjs) { - CPDF_Object* pObj = it.second.Get(); - pPageXObject->SetNewFor(it.first, m_pDestPDFDoc.Get(), - pObj->GetObjNum()); - } - - auto pDict = pdfium::MakeUnique( - m_pDestPDFDoc.Get()->GetByteStringPool()); - CPDF_Stream* pStream = m_pDestPDFDoc.Get()->NewIndirect( - nullptr, 0, std::move(pDict)); - pStream->SetData(content.raw_str(), content.GetLength()); - pCurPageDict->SetNewFor("Contents", m_pDestPDFDoc.Get(), - pStream->GetObjNum()); - m_xobjs.clear(); -} - -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); - - 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 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 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 output_doc( - FPDF_CreateNewDocument()); - if (!output_doc) - return nullptr; - - CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get()); - ASSERT(pDestDoc); - - std::vector 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; -} -- cgit v1.2.3