Split CPDF_PageOrganizer into multiple classes.
Retain CPDF_PageOrganizer as a base class. Add CPDF_PageExporter and
CPDF_NPageToOneExporter sub-classes. The sub-classes only do one type of
exporting. Therefore it is much easier to understand what part of the
original combined CPDF_PageOrganizer is used where.
Change-Id: I424ef9c32b101d23e8397fc9a656d9b0b5da6a27
Reviewed-on: https://pdfium-review.googlesource.com/26011
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: Shirleen Lou <xlou@chromium.org>
diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp
index f51bb22..efec4b2 100644
--- a/fpdfsdk/fpdf_ppo.cpp
+++ b/fpdfsdk/fpdf_ppo.cpp
@@ -283,50 +283,18 @@
return page_numbers;
}
-} // namespace
-
class CPDF_PageOrganizer {
- public:
+ protected:
+ // Map source page object number to XObject object number.
+ using ObjectNumberMap = std::map<uint32_t, uint32_t>;
+
CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc);
~CPDF_PageOrganizer();
+ // Must be called after construction before doing anything else.
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:
- // Map source page object number to XObject object number.
- using ObjectNumberMap = std::map<uint32_t, uint32_t>;
- // Map page object number to XObject object name.
- using PageXObjectMap = std::map<uint32_t, ByteString>;
- // Map XObject's object name to it's object number.
- using XObjectNameNumberMap = std::map<ByteString, 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
- // 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);
CPDF_Document* dest() { return m_pDestPDFDoc.Get(); }
const CPDF_Document* dest() const { return m_pDestPDFDoc.Get(); }
@@ -334,13 +302,11 @@
CPDF_Document* src() { return m_pSrcPDFDoc.Get(); }
const CPDF_Document* src() const { return m_pSrcPDFDoc.Get(); }
+ private:
+ uint32_t GetNewObjId(ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef);
+
UnownedPtr<CPDF_Document> const m_pDestPDFDoc;
UnownedPtr<CPDF_Document> const 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,
@@ -389,249 +355,6 @@
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, dest());
- (*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<CPDF_Stream>(
- nullptr, 0,
- pdfium::MakeUnique<CPDF_Dictionary>(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<CPDF_Dictionary>(bsResourceString);
- }
- uint32_t dwSrcPageObj = pSrcPageDict->GetObjNum();
- uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum();
- (*pObjNumberMap)[dwSrcPageObj] = dwNewXobjectObj;
- UpdateReference(pNewXObjectDict, pObjNumberMap);
-
- 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 bsSrcContentStream;
- 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 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<CPDF_StreamAcc>(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<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 = dest()->CreateNewPage(curpage);
- CPDF_Dictionary* pSrcPageDict = src()->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& bsContent,
- XObjectNameNumberMap* pXObjNameNumberMap) {
- 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 : *pXObjNameNumberMap) {
- pPageXObject->SetNewFor<CPDF_Reference>(it.first, dest(), it.second);
- }
-
- auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(dest()->GetByteStringPool());
- CPDF_Stream* pStream =
- dest()->NewIndirect<CPDF_Stream>(nullptr, 0, std::move(pDict));
- pStream->SetData(bsContent.raw_str(), bsContent.GetLength());
- pCurPageDict->SetNewFor<CPDF_Reference>("Contents", dest(),
- pStream->GetObjNum());
-}
-
-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);
- // 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 = dest()->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 = src()->GetPage(pageNums[innerPage] - 1);
- if (!pSrcPageDict)
- return false;
-
- CPDF_Page srcPage(src(), 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()) {
@@ -722,6 +445,337 @@
return dwNewObjNum;
}
+// Copies pages from a source document into a destination document.
+// This class is intended to be used once via ExportPage() and then destroyed.
+class CPDF_PageExporter : public CPDF_PageOrganizer {
+ public:
+ CPDF_PageExporter(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc);
+ ~CPDF_PageExporter();
+
+ // For the pages from the source document with |pageNums| as their page
+ // numbers, insert them into the destination document at page |nIndex|.
+ // |pageNums| is 1-based.
+ // |nIndex| is 0-based.
+ bool ExportPage(const std::vector<uint32_t>& pageNums, int nIndex);
+};
+
+CPDF_PageExporter::CPDF_PageExporter(CPDF_Document* pDestPDFDoc,
+ CPDF_Document* pSrcPDFDoc)
+ : CPDF_PageOrganizer(pDestPDFDoc, pSrcPDFDoc) {}
+
+CPDF_PageExporter::~CPDF_PageExporter() = default;
+
+bool CPDF_PageExporter::ExportPage(const std::vector<uint32_t>& pageNums,
+ int nIndex) {
+ if (!PDFDocInit())
+ return false;
+
+ int curpage = nIndex;
+ auto pObjNumberMap = pdfium::MakeUnique<ObjectNumberMap>();
+ for (size_t i = 0; i < pageNums.size(); ++i) {
+ CPDF_Dictionary* pCurPageDict = dest()->CreateNewPage(curpage);
+ CPDF_Dictionary* pSrcPageDict = src()->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;
+}
+
+// Copies pages from a source document into a destination document. Creates 1
+// page in the destination document for every N source pages. This class is
+// intended to be used once via ExportNPagesToOne() and then destroyed.
+class CPDF_NPageToOneExporter : public CPDF_PageOrganizer {
+ public:
+ CPDF_NPageToOneExporter(CPDF_Document* pDestPDFDoc,
+ CPDF_Document* pSrcPDFDoc);
+ ~CPDF_NPageToOneExporter();
+
+ // For the pages from the source document with |pageNums| as their page
+ // numbers, insert them into the destination document, starting at page 0.
+ // |pageNums| is 1-based.
+ // |destPageWidth| and |destPageHeight| are the destination document page
+ // dimensions, measured in pixels.
+ // |numPagesOnXAxis| and |numPagesOnXAxis| together defines how many source
+ // pages fit on one destination page.
+ bool ExportNPagesToOne(const std::vector<uint32_t>& pageNums,
+ float destPageWidth,
+ float destPageHeight,
+ unsigned int numPagesOnXAxis,
+ unsigned int numPagesOnYAxis);
+
+ private:
+ // Map page object number to XObject object name.
+ using PageXObjectMap = std::map<uint32_t, ByteString>;
+ // Map XObject's object name to it's object number.
+ using XObjectNameNumberMap = std::map<ByteString, uint32_t>;
+
+ static void SetMediaBox(CPDF_Dictionary* pDestPageDict,
+ const CFX_SizeF& pagesize);
+
+ // 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);
+
+ uint32_t m_xobjectNum = 0;
+ CFX_SizeF m_pageSize;
+ XObjectNameNumberMap m_xobjs;
+};
+
+CPDF_NPageToOneExporter::CPDF_NPageToOneExporter(CPDF_Document* pDestPDFDoc,
+ CPDF_Document* pSrcPDFDoc)
+ : CPDF_PageOrganizer(pDestPDFDoc, pSrcPDFDoc) {}
+
+CPDF_NPageToOneExporter::~CPDF_NPageToOneExporter() = default;
+
+bool CPDF_NPageToOneExporter::ExportNPagesToOne(
+ const std::vector<uint32_t>& pageNums,
+ float destPageWidth,
+ float destPageHeight,
+ unsigned int numPagesOnXAxis,
+ unsigned int numPagesOnYAxis) {
+ if (!PDFDocInit())
+ return false;
+
+ FX_SAFE_SIZE_T safe_numPagesPerSheet = numPagesOnXAxis;
+ safe_numPagesPerSheet *= numPagesOnYAxis;
+ if (!safe_numPagesPerSheet.IsValid())
+ return false;
+
+ size_t numPagesPerSheet = safe_numPagesPerSheet.ValueOrDie();
+
+ // 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 = dest()->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 = src()->GetPage(pageNums[innerPage] - 1);
+ if (!pSrcPageDict)
+ return false;
+
+ CPDF_Page srcPage(src(), 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;
+}
+
+// static
+void CPDF_NPageToOneExporter::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);
+}
+
+void CPDF_NPageToOneExporter::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, dest());
+ (*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_NPageToOneExporter::MakeXObject(CPDF_Dictionary* pSrcPageDict,
+ ObjectNumberMap* pObjNumberMap,
+ CPDF_Document* pDestDoc) {
+ ASSERT(pSrcPageDict);
+
+ 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 bsResourceString = "Resources";
+ if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, bsResourceString)) {
+ // Use a default empty resources if it does not exist.
+ pNewXObjectDict->SetNewFor<CPDF_Dictionary>(bsResourceString);
+ }
+ uint32_t dwSrcPageObj = pSrcPageDict->GetObjNum();
+ uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum();
+ (*pObjNumberMap)[dwSrcPageObj] = dwNewXobjectObj;
+ UpdateReference(pNewXObjectDict, pObjNumberMap);
+
+ 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 bsSrcContentStream;
+ 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 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<CPDF_StreamAcc>(pStream);
+ pAcc->LoadAllDataFiltered();
+ ByteString bsStream(pAcc->GetData(), pAcc->GetSize());
+ pNewXObject->SetDataAndRemoveFilter(bsStream.raw_str(),
+ bsStream.GetLength());
+ }
+
+ return pNewXObject->GetObjNum();
+}
+
+void CPDF_NPageToOneExporter::FinishPage(
+ CPDF_Dictionary* pCurPageDict,
+ const ByteString& bsContent,
+ XObjectNameNumberMap* pXObjNameNumberMap) {
+ 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 : *pXObjNameNumberMap) {
+ pPageXObject->SetNewFor<CPDF_Reference>(it.first, dest(), it.second);
+ }
+
+ auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(dest()->GetByteStringPool());
+ CPDF_Stream* pStream =
+ dest()->NewIndirect<CPDF_Stream>(nullptr, 0, std::move(pDict));
+ pStream->SetData(bsContent.raw_str(), bsContent.GetLength());
+ pCurPageDict->SetNewFor<CPDF_Reference>("Contents", dest(),
+ pStream->GetObjNum());
+}
+
+} // namespace
+
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
FPDF_BYTESTRING pagerange,
@@ -738,12 +792,8 @@
if (page_numbers.empty())
return false;
- CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc);
-
- if (!pageOrg.PDFDocInit())
- return false;
-
- return pageOrg.ExportPage(page_numbers, index);
+ CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
+ return exporter.ExportPage(page_numbers, index);
}
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
@@ -773,13 +823,18 @@
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;
+ if (num_pages_on_x_axis == 1 && num_pages_on_y_axis == 1) {
+ CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
+ if (!exporter.ExportPage(page_numbers, 0))
+ return nullptr;
+ return output_doc.release();
}
+ CPDF_NPageToOneExporter exporter(pDestDoc, pSrcDoc);
+ if (!exporter.ExportNPagesToOne(page_numbers, output_width, output_height,
+ num_pages_on_x_axis, num_pages_on_y_axis)) {
+ return nullptr;
+ }
return output_doc.release();
}