Skip to content

Commit

Permalink
Expose pdfium functionality for N-up mojo service.
Browse files Browse the repository at this point in the history
Added two new functions to expose pdfium functionality to the
to-be-created N-up conversion mojo service.

ConvertPdfPagesToNupPdf will take multiple PDF pages and import them
into a N-up PDF.

ConvertPdfDocumentToNupPdf will take an entire PDF document and convert
it to a N-up PDF document.

In printing, 2-up, 3-up, or more generally N-up refers to a page
layout strategy in which multiple pages are composited onto a
single page.

Bug: 775999
Change-Id: I11edc90ee717fcb295a35e3784f4ed4e72ae8117
Reviewed-on: https://chromium-review.googlesource.com/1134484
Commit-Queue: Shirleen Lou <xlou@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577052}
  • Loading branch information
xlou authored and Commit Bot committed Jul 21, 2018
1 parent a4a64fc commit 71c45c8
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 18 deletions.
35 changes: 35 additions & 0 deletions pdf/pdf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <stdint.h>

#include <utility>

#if defined(OS_WIN)
#include <windows.h>
#endif
Expand Down Expand Up @@ -143,4 +145,37 @@ bool RenderPDFPageToBitmap(const void* pdf_buffer,
pdf_buffer, pdf_buffer_size, page_number, settings, bitmap_buffer);
}

bool ConvertPdfPagesToNupPdf(
std::vector<base::span<const uint8_t>> input_buffers,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) {
ScopedSdkInitializer scoped_sdk_initializer;
if (!scoped_sdk_initializer.Init())
return false;

PDFEngineExports* engine_exports = PDFEngineExports::Get();
return engine_exports->ConvertPdfPagesToNupPdf(
std::move(input_buffers), pages_per_sheet, page_size_width,
page_size_height, dest_pdf_buffer, dest_pdf_buffer_size);
}

bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) {
ScopedSdkInitializer scoped_sdk_initializer;
if (!scoped_sdk_initializer.Init())
return false;

PDFEngineExports* engine_exports = PDFEngineExports::Get();
return engine_exports->ConvertPdfDocumentToNupPdf(
input_buffer, pages_per_sheet, page_size_width, page_size_height,
dest_pdf_buffer, dest_pdf_buffer_size);
}

} // namespace chrome_pdf
56 changes: 56 additions & 0 deletions pdf/pdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#ifndef PDF_PDF_H_
#define PDF_PDF_H_

#include <vector>

#include "base/containers/span.h"
#include "build/build_config.h"

#if defined(OS_WIN)
Expand Down Expand Up @@ -126,6 +129,59 @@ bool RenderPDFPageToBitmap(const void* pdf_buffer,
bool autorotate,
bool use_color);

// Convert multiple PDF pages into a N-up PDF.
// |input_buffers| is the vector of buffers with each buffer contains a PDF.
// If any of the PDFs contains multiple pages, only the first page of the
// document is used.
// |pages_per_sheet| is the number of pages to put on one sheet.
// |page_size_width| is the width of the output page size, measured in PDF
// "user space" units.
// |page_size_height| is the height of the output page size, measured in PDF
// "user space" units.
// |dest_pdf_buffer| is the output N-up PDF page. Caller takes ownership, and
// needs to free the memory.
// |dest_pdf_buffer_size| is the size of output N-up PDF page.
//
// |page_size_width| and |page_size_height| are the print media size. The page
// size of the output N-up PDF is determined by the |pages_per_sheet|, the
// orientation of the PDF pages contained in the |input_buffers|, and the media
// page size |page_size_width| and |page_size_height|. For example, when
// |page_size_width| = 512, |page_size_height| = 792, |pages_per_sheet| = 2, and
// the orientation of |input_buffers| = portrait, the output N-up PDF will have
// |page_size_width| = 792, and |page_size_height| = 512.
// See printing::NupParameters for more details on how the output page
// orientation is determined, to understand why |page_size_width| and
// |page_size_height| may be swapped in some cases.
bool ConvertPdfPagesToNupPdf(
std::vector<base::span<const uint8_t>> input_buffers,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size);

// Convert a PDF document to a N-up PDF document.
// |input_buffer| is the buffer that contains the entire PDF document to be
// converted to a N-up PDF document.
// |pages_per_sheet| is the number of pages to put on one sheet.
// |page_size_width| is the width of the media page size, measured in PDF
// "user space" units.
// |page_size_height| is the height of the media page size, measured in PDF
// "user space" units.
// |dest_pdf_buffer| is the output N-up PDF page. Caller takes ownership, and
// needs to free the memory.
// |dest_pdf_buffer_size| is the size of output N-up PDF document.
//
// Refer to the description of ConvertPdfPagesToNupPdf to understand how the
// output page size |page_size_width| and |page_size_height| will be calculated.
// The algorithm used to determine the output page size is the same.
bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size);

} // namespace chrome_pdf

#endif // PDF_PDF_H_
19 changes: 19 additions & 0 deletions pdf/pdf_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <string>
#include <vector>

#include "base/containers/span.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
Expand Down Expand Up @@ -464,6 +465,24 @@ class PDFEngineExports {
const RenderingSettings& settings,
void* bitmap_buffer) = 0;

// See the definition of ConvertPdfPagesToNupPdf in pdf.cc for details.
virtual bool ConvertPdfPagesToNupPdf(
std::vector<base::span<const uint8_t>> input_buffers,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) = 0;

// See the definition of ConvertPdfDocumentToNupPdf in pdf.cc for details.
virtual bool ConvertPdfDocumentToNupPdf(
base::span<const uint8_t> input_buffer,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) = 0;

virtual bool GetPDFDocInfo(const void* pdf_buffer,
int buffer_size,
int* page_count,
Expand Down
97 changes: 97 additions & 0 deletions pdf/pdfium/pdfium_engine_exports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
#include <utility>

#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "pdf/pdfium/pdfium_mem_buffer_file_write.h"
#include "pdf/pdfium/pdfium_print.h"
#include "printing/nup_parameters.h"
#include "printing/units.h"
#include "third_party/pdfium/public/cpp/fpdf_scopers.h"
#include "third_party/pdfium/public/fpdf_ppo.h"
#include "third_party/pdfium/public/fpdfview.h"

using printing::ConvertUnitDouble;
Expand Down Expand Up @@ -89,6 +94,66 @@ int CalculatePosition(FPDF_PAGE page,
return rotate;
}

ScopedFPDFDocument LoadPdfData(base::span<const uint8_t> pdf_buffer) {
ScopedFPDFDocument doc;
if (base::IsValueInRangeForNumericType<int>(pdf_buffer.size())) {
doc.reset(
FPDF_LoadMemDocument(pdf_buffer.data(), pdf_buffer.size(), nullptr));
}
return doc;
}

ScopedFPDFDocument CreatePdfDoc(
std::vector<base::span<const uint8_t>> input_buffers) {
ScopedFPDFDocument doc(FPDF_CreateNewDocument());

size_t index = 0;
for (auto input_buffer : input_buffers) {
ScopedFPDFDocument single_page_doc = LoadPdfData(input_buffer);
if (!FPDF_ImportPages(doc.get(), single_page_doc.get(), "1", index++)) {
return nullptr;
}
}

return doc;
}

bool CreateNupPdfDocument(FPDF_DOCUMENT doc,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) {
if (!dest_pdf_buffer || !dest_pdf_buffer_size)
return false;

printing::NupParameters nup_params;
bool is_landscape = PDFiumPrint::IsSourcePdfLandscape(doc);
nup_params.SetParameters(pages_per_sheet, is_landscape);
bool paper_is_landscape = page_size_width > page_size_height;
if (nup_params.landscape() != paper_is_landscape)
std::swap(page_size_width, page_size_height);

ScopedFPDFDocument output_doc_nup(FPDF_ImportNPagesToOne(
doc, page_size_width, page_size_height, nup_params.num_pages_on_x_axis(),
nup_params.num_pages_on_y_axis()));

if (!output_doc_nup)
return false;

// Copy data to the |dest_pdf_buffer| here.
// TODO(thestig): Avoid this copy.
PDFiumMemBufferFileWrite output_file_write;
if (!FPDF_SaveAsCopy(output_doc_nup.get(), &output_file_write, 0)) {
return false;
}
*dest_pdf_buffer = malloc(output_file_write.size());
memcpy(*dest_pdf_buffer, output_file_write.buffer().c_str(),
output_file_write.size());
*dest_pdf_buffer_size = output_file_write.size();
return true;
}

} // namespace

PDFEngineExports::RenderingSettings::RenderingSettings(int dpi_x,
Expand Down Expand Up @@ -245,6 +310,38 @@ bool PDFiumEngineExports::RenderPDFPageToBitmap(
return true;
}

bool PDFiumEngineExports::ConvertPdfPagesToNupPdf(
std::vector<base::span<const uint8_t>> input_buffers,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) {
ScopedFPDFDocument doc = CreatePdfDoc(std::move(input_buffers));
if (!doc)
return false;

return CreateNupPdfDocument(doc.get(), pages_per_sheet, page_size_width,
page_size_height, dest_pdf_buffer,
dest_pdf_buffer_size);
}

bool PDFiumEngineExports::ConvertPdfDocumentToNupPdf(
base::span<const uint8_t> input_buffer,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) {
ScopedFPDFDocument doc = LoadPdfData(input_buffer);
if (!doc)
return false;

return CreateNupPdfDocument(doc.get(), pages_per_sheet, page_size_width,
page_size_height, dest_pdf_buffer,
dest_pdf_buffer_size);
}

bool PDFiumEngineExports::GetPDFDocInfo(const void* pdf_buffer,
int buffer_size,
int* page_count,
Expand Down
14 changes: 14 additions & 0 deletions pdf/pdfium/pdfium_engine_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>

#include "base/containers/span.h"
#include "build/build_config.h"
#include "pdf/pdf_engine.h"

Expand Down Expand Up @@ -36,6 +37,19 @@ class PDFiumEngineExports : public PDFEngineExports {
int page_number,
const RenderingSettings& settings,
void* bitmap_buffer) override;
bool ConvertPdfPagesToNupPdf(
std::vector<base::span<const uint8_t>> input_buffers,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) override;
bool ConvertPdfDocumentToNupPdf(base::span<const uint8_t> input_buffer,
size_t pages_per_sheet,
size_t page_size_width,
size_t page_size_height,
void** dest_pdf_buffer,
size_t* dest_pdf_buffer_size) override;
bool GetPDFDocInfo(const void* pdf_buffer,
int buffer_size,
int* page_count,
Expand Down
70 changes: 70 additions & 0 deletions pdf/pdfium/pdfium_engine_exports_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,74 @@ TEST_F(PDFiumEngineExportsTest, GetPDFPageSizeByIndex) {
}
}

TEST_F(PDFiumEngineExportsTest, ConvertPdfPagesToNupPdf) {
base::FilePath pdf_path =
pdf_data_dir().Append(FILE_PATH_LITERAL("rectangles.pdf"));
std::string pdf_data;
ASSERT_TRUE(base::ReadFileToString(pdf_path, &pdf_data));

std::vector<base::span<const uint8_t>> pdf_buffers;

EXPECT_FALSE(
ConvertPdfPagesToNupPdf(pdf_buffers, 1, 512, 792, nullptr, nullptr));

pdf_buffers.push_back(base::as_bytes(base::make_span(pdf_data)));
pdf_buffers.push_back(base::as_bytes(base::make_span(pdf_data)));

void* output_pdf_buffer;
size_t output_pdf_buffer_size;
ASSERT_TRUE(ConvertPdfPagesToNupPdf(
pdf_buffers, 2, 512, 792, &output_pdf_buffer, &output_pdf_buffer_size));
ASSERT_GT(output_pdf_buffer_size, 0U);
ASSERT_NE(output_pdf_buffer, nullptr);
int page_count;
ASSERT_TRUE(GetPDFDocInfo(output_pdf_buffer, output_pdf_buffer_size,
&page_count, nullptr));
ASSERT_EQ(1, page_count);

double width;
double height;
ASSERT_TRUE(GetPDFPageSizeByIndex(output_pdf_buffer, output_pdf_buffer_size,
0, &width, &height));
EXPECT_DOUBLE_EQ(792.0, width);
EXPECT_DOUBLE_EQ(512.0, height);

free(output_pdf_buffer);
}

TEST_F(PDFiumEngineExportsTest, ConvertPdfDocumentToNupPdf) {
base::FilePath pdf_path =
pdf_data_dir().Append(FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
std::string pdf_data;
ASSERT_TRUE(base::ReadFileToString(pdf_path, &pdf_data));

base::span<const uint8_t> pdf_buffer;

EXPECT_FALSE(
ConvertPdfDocumentToNupPdf(pdf_buffer, 1, 512, 792, nullptr, nullptr));

pdf_buffer = base::as_bytes(base::make_span(pdf_data));

void* output_pdf_buffer;
size_t output_pdf_buffer_size;
ASSERT_TRUE(ConvertPdfDocumentToNupPdf(
pdf_buffer, 4, 512, 792, &output_pdf_buffer, &output_pdf_buffer_size));
ASSERT_GT(output_pdf_buffer_size, 0U);
ASSERT_NE(output_pdf_buffer, nullptr);
int page_count;
ASSERT_TRUE(GetPDFDocInfo(output_pdf_buffer, output_pdf_buffer_size,
&page_count, nullptr));
ASSERT_EQ(2, page_count);
for (int page_number = 0; page_number < page_count; ++page_number) {
double width;
double height;
ASSERT_TRUE(GetPDFPageSizeByIndex(output_pdf_buffer, output_pdf_buffer_size,
page_number, &width, &height));
EXPECT_DOUBLE_EQ(512.0, width);
EXPECT_DOUBLE_EQ(792.0, height);
}

free(output_pdf_buffer);
}

} // namespace chrome_pdf
Loading

0 comments on commit 71c45c8

Please sign in to comment.