7#include "core/fpdfapi/edit/cpdf_npagetooneexporter.h"
14#include "constants/page_object.h"
15#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
16#include "core/fpdfapi/page/cpdf_page.h"
17#include "core/fpdfapi/parser/cpdf_array.h"
18#include "core/fpdfapi/parser/cpdf_dictionary.h"
19#include "core/fpdfapi/parser/cpdf_document.h"
20#include "core/fpdfapi/parser/cpdf_name.h"
21#include "core/fpdfapi/parser/cpdf_number.h"
22#include "core/fpdfapi/parser/cpdf_object.h"
23#include "core/fpdfapi/parser/cpdf_reference.h"
24#include "core/fpdfapi/parser/cpdf_stream.h"
25#include "core/fpdfapi/parser/cpdf_stream_acc.h"
26#include "core/fxcrt/check.h"
27#include "core/fxcrt/fx_safe_types.h"
28#include "core/fxcrt/fx_string_wrappers.h"
29#include "core/fxcrt/span.h"
39 size_t pages_on_x_axis,
40 size_t pages_on_y_axis);
52 std::pair<size_t, size_t> ConvertPageOrder()
const;
63 const size_t pages_on_x_axis_;
64 const size_t pages_on_y_axis_;
65 const size_t pages_per_sheet_;
69 size_t sub_page_index_ = 0;
72NupState::NupState(
const CFX_SizeF& pagesize,
73 size_t pages_on_x_axis,
74 size_t pages_on_y_axis)
75 : dest_page_size_(pagesize),
76 pages_on_x_axis_(pages_on_x_axis),
77 pages_on_y_axis_(pages_on_y_axis),
78 pages_per_sheet_(pages_on_x_axis * pages_on_y_axis) {
79 DCHECK(pages_on_x_axis_ > 0);
80 DCHECK(pages_on_y_axis_ > 0);
81 DCHECK(dest_page_size_.width > 0);
82 DCHECK(dest_page_size_.height > 0);
84 sub_page_size_.width = dest_page_size_.width / pages_on_x_axis_;
85 sub_page_size_.height = dest_page_size_.height / pages_on_y_axis_;
88std::pair<size_t, size_t> NupState::ConvertPageOrder()
const {
89 size_t sub_x = sub_page_index_ % pages_on_x_axis_;
90 size_t sub_y = sub_page_index_ / pages_on_x_axis_;
93 sub_y = pages_on_y_axis_ - sub_y - 1;
95 return {sub_x, sub_y};
103 settings.sub_page_start_point.x = sub_x * sub_page_size_.width;
104 settings.sub_page_start_point.y = sub_y * sub_page_size_.height;
106 const float x_scale = sub_page_size_.width / pagesize.width;
107 const float y_scale = sub_page_size_.height / pagesize.height;
108 settings
.scale =
std::min(x_scale, y_scale);
110 float sub_width = pagesize.width * settings
.scale;
111 float sub_height = pagesize.height * settings
.scale;
112 if (x_scale > y_scale) {
113 settings.sub_page_start_point.x += (sub_page_size_.width - sub_width) / 2;
115 settings.sub_page_start_point.y += (sub_page_size_.height - sub_height) / 2;
120CPDF_NPageToOneExporter::
NupPageSettings NupState::CalculateNewPagePosition(
122 if (sub_page_index_ >= pages_per_sheet_) {
126 auto [sub_x, sub_y] = ConvertPageOrder();
128 return CalculatePageEdit(sub_x, sub_y, pagesize);
137 matrix.Translate(settings.sub_page_start_point.x,
138 settings.sub_page_start_point.y);
140 fxcrt::ostringstream content_stream;
141 content_stream <<
"q\n";
142 WriteMatrix(content_stream, matrix) <<
" cm\n"
143 <<
"/" << xobject_name <<
" Do Q\n";
156 pdfium::span<
const uint32_t> page_indices,
158 size_t pages_on_x_axis,
159 size_t pages_on_y_axis) {
164 FX_SAFE_SIZE_T safe_pages_per_sheet = pages_on_x_axis;
165 safe_pages_per_sheet *= pages_on_y_axis;
166 if (!safe_pages_per_sheet.IsValid()) {
171 src_page_xobject_map_.clear();
172 size_t pages_per_sheet = safe_pages_per_sheet.ValueOrDie();
173 NupState n_up_state(dest_page_size, pages_on_x_axis, pages_on_y_axis);
176 const CFX_FloatRect dest_page_rect(0, 0, dest_page_size.width,
177 dest_page_size.height);
178 for (size_t outer_page_index = 0; outer_page_index < page_indices.size();
179 outer_page_index += pages_per_sheet) {
180 xobject_name_to_number_map_.clear();
183 dest()->CreateNewPage(curpage.ValueOrDie());
184 if (!dest_page_dict) {
190 size_t inner_page_max =
191 std::min(outer_page_index + pages_per_sheet, page_indices.size());
192 for (size_t i = outer_page_index; i < inner_page_max; ++i) {
194 src()->GetMutablePageDictionary(page_indices[i]);
195 if (!src_page_dict) {
199 auto src_page =
pdfium::MakeRetain<CPDF_Page>(src(), src_page_dict);
200 src_page->AddPageImageCache();
202 n_up_state.CalculateNewPagePosition(src_page->GetPageSize());
203 content += AddSubPage(src_page, settings);
206 FinishPage(dest_page_dict, content);
217 return GenerateSubPageContentStream(xobject_name, settings);
220ByteString CPDF_NPageToOneExporter::AddSubPage(
223 uint32_t src_page_obj_num = src_page->GetDict()->GetObjNum();
224 const auto it = src_page_xobject_map_.find(src_page_obj_num);
225 ByteString xobject_name = it != src_page_xobject_map_.end()
227 : MakeXObjectFromPage(src_page);
228 return GenerateSubPageContentStream(xobject_name, settings);
231RetainPtr<CPDF_Stream> CPDF_NPageToOneExporter::MakeXObjectFromPageRaw(
235 src_page_dict->GetDirectObjectFor(pdfium::page_object::kContents);
240 static const char kResourceString[] =
"Resources";
246 uint32_t src_page_obj_num = src_page_dict->GetObjNum();
247 uint32_t new_xobject_obj_num = new_xobject_dict->GetObjNum();
250 new_xobject_dict->SetNewFor<CPDF_Name>(
"Type",
"XObject");
251 new_xobject_dict->SetNewFor<CPDF_Name>(
"Subtype",
"Form");
252 new_xobject_dict->SetNewFor<CPDF_Number>(
"FormType", 1);
253 new_xobject_dict->SetRectFor(
"BBox", src_page->GetBBox());
254 new_xobject_dict->SetMatrixFor(
"Matrix", src_page->GetPageMatrix());
258 const CPDF_Array* src_contents_array = src_contents->AsArray();
259 if (!src_contents_array) {
260 RetainPtr<
const CPDF_Stream> stream(src_contents->AsStream());
261 auto acc =
pdfium::MakeRetain<CPDF_StreamAcc>(
std::move(stream));
262 acc->LoadAllDataFiltered();
263 new_xobject->SetDataAndRemoveFilter(acc->GetSpan());
267 for (size_t i = 0; i < src_contents_array->size(); ++i) {
268 RetainPtr<
const CPDF_Stream> stream = src_contents_array->GetStreamAt(i);
269 auto acc =
pdfium::MakeRetain<CPDF_StreamAcc>(
std::move(stream));
270 acc->LoadAllDataFiltered();
272 src_content_stream
+= "\n";
274 new_xobject->SetDataAndRemoveFilter(src_content_stream.unsigned_span());
278ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage(
280 RetainPtr<CPDF_Stream> new_xobject = MakeXObjectFromPageRaw(src_page);
284 xobject_name_to_number_map_[xobject_name] = new_xobject->GetObjNum();
285 src_page_xobject_map_[src_page->GetDict()->GetObjNum()] = xobject_name;
292 src()->GetMutablePageDictionary(src_page_index);
293 if (!src_page_dict) {
297 auto src_page =
pdfium::MakeRetain<CPDF_Page>(src(), src_page_dict);
299 xobject->dest_doc = dest();
300 xobject->xobject.Reset(MakeXObjectFromPageRaw(src_page));
304void CPDF_NPageToOneExporter::FinishPage(
308 dest_page_dict->GetOrCreateDictFor(pdfium::page_object::kResources);
310 for (
auto& it : xobject_name_to_number_map_) {
311 xobject->SetNewFor<CPDF_Reference>(it.first, dest(), it.second);
316 stream->SetData(content.unsigned_span());
318 dest(), stream->GetObjNum());
fxcrt::ByteString ByteString
void Scale(float sx, float sy)
std::vector< RetainPtr< CPDF_Object > >::const_iterator const_iterator
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
bool ExportNPagesToOne(pdfium::span< const uint32_t > page_indices, const CFX_SizeF &dest_page_size, size_t pages_on_x_axis, size_t pages_on_y_axis)
static ByteString GenerateSubPageContentStreamForTesting(const ByteString &xobject_name, const NupPageSettings &settings)
CPDF_NPageToOneExporter(CPDF_Document *dest_doc, CPDF_Document *src_doc)
~CPDF_NPageToOneExporter()
std::unique_ptr< XObjectContext > CreateXObjectContextFromPage(int src_page_index)
void ClearObjectNumberMap()
static bool CopyInheritable(RetainPtr< CPDF_Dictionary > dest_page_dict, RetainPtr< const CPDF_Dictionary > src_page_dict, const ByteString &key)
CPDF_PageOrganizer(CPDF_Document *dest_doc, CPDF_Document *src_doc)
bool UpdateReference(RetainPtr< CPDF_Object > obj)
void AddObjectMapping(uint32_t old_page_obj_num, uint32_t new_page_obj_num)
static ByteString Format(const char *pFormat,...)
ByteString & operator+=(const char *str)
CFX_STemplate< float > CFX_SizeF
pdfium::CheckedNumeric< int32_t > FX_SAFE_INT32
fxcrt::ByteStringView ByteStringView