0

Expose pdfium functionality for N-up mojo service.

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}
This commit is contained in:
xlou
2018-07-21 01:19:17 +00:00
committed by Commit Bot
parent a4a64fc062
commit 71c45c8975
10 changed files with 483 additions and 18 deletions

@ -6,6 +6,8 @@
#include <stdint.h>
#include <utility>
#if defined(OS_WIN)
#include <windows.h>
#endif
@ -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

@ -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)
@ -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_

@ -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"
@ -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,

@ -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;
@ -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,
@ -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,

@ -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"
@ -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,

@ -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

@ -37,24 +37,6 @@ bool ShouldDoNup(int pages_per_sheet) {
return pages_per_sheet > 1;
}
// Check the source doc orientation. Returns true if the doc is landscape.
// For now the orientation of the doc is determined by its first page's
// orientation. Improvement can be added in the future to better determine the
// orientation of the source docs that have mixed orientation.
// TODO(xlou): rotate pages if the source doc has mixed orientation. So that
// the orientation of all pages of the doc are uniform. Pages of square size
// will not be rotated.
bool IsSourcePdfLandscape(FPDF_DOCUMENT doc) {
DCHECK(doc);
ScopedFPDFPage pdf_page(FPDF_LoadPage(doc, 0));
DCHECK(pdf_page);
bool is_source_landscape =
FPDF_GetPageWidth(pdf_page.get()) > FPDF_GetPageHeight(pdf_page.get());
return is_source_landscape;
}
// Set the destination page size and content area in points based on source
// page rotation and orientation.
//
@ -235,6 +217,17 @@ std::vector<uint32_t> PDFiumPrint::GetPageNumbersFromPrintPageNumberRange(
return page_numbers;
}
bool PDFiumPrint::IsSourcePdfLandscape(FPDF_DOCUMENT doc) {
DCHECK(doc);
ScopedFPDFPage pdf_page(FPDF_LoadPage(doc, 0));
DCHECK(pdf_page);
bool is_source_landscape =
FPDF_GetPageWidth(pdf_page.get()) > FPDF_GetPageHeight(pdf_page.get());
return is_source_landscape;
}
pp::Buffer_Dev PDFiumPrint::PrintPagesAsRasterPDF(
const PP_PrintPageNumberRange_Dev* page_ranges,
uint32_t page_range_count,

@ -40,6 +40,15 @@ class PDFiumPrint {
const PP_PrintSettings_Dev& print_settings,
const PP_PdfPrintSettings_Dev& pdf_print_settings);
// Check the source doc orientation. Returns true if the doc is landscape.
// For now the orientation of the doc is determined by its first page's
// orientation. Improvement can be added in the future to better determine
// the orientation of the source docs that have mixed orientation.
// TODO(xlou): rotate pages if the source doc has mixed orientation. So that
// the orientation of all pages of the doc are uniform. Pages of square size
// will not be rotated.
static bool IsSourcePdfLandscape(FPDF_DOCUMENT doc);
private:
FPDF_DOCUMENT CreateSinglePageRasterPdf(
double source_page_width,

@ -0,0 +1,50 @@
%PDF-1.7
%<25><><EFBFBD><EFBFBD>
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj <<
/Type /Pages
/MediaBox [ 0 0 200 300 ]
/Count 1
/Kids [ 3 0 R ]
>>
endobj
3 0 obj <<
/Type /Page
/Parent 2 0 R
/Contents 4 0 R
>>
endobj
4 0 obj <<
>>
stream
q
0 0 0 rg
0 290 10 10 re B*
10 150 50 30 re B*
0 0 1 rg
190 290 10 10 re B*
70 232 50 30 re B*
0 1 0 rg
190 0 10 10 re B*
130 150 50 30 re B*
1 0 0 rg
0 0 10 10 re B*
70 67 50 30 re B*
Q
endstream
endobj
xref
0 5
0000000000 65535 f
0000000015 00000 n
0000000068 00000 n
0000000161 00000 n
0000000230 00000 n
trailer<< /Root 1 0 R /Size 5 >>
startxref
456
%%EOF

@ -0,0 +1,122 @@
%PDF-1.7
%<25><><EFBFBD><EFBFBD>
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj <<
/Type /Pages
/MediaBox [ 0 0 200 250 ]
/Count 5
/Kids [ 3 0 R 5 0 R 7 0 R 9 0 R 11 0 R ]
>>
endobj
3 0 obj <<
/Type /Page
/Parent 2 0 R
/Contents 4 0 R
>>
endobj
4 0 obj <<
/Length 49
>>
stream
q
1 1 0 rg
100 0 30 50 re B*
70 67 50 30 re B*
Q
endstream
endobj
5 0 obj <<
/Type /Page
/Parent 2 0 R
/Rotate 90
/Contents 6 0 R
>>
endobj
6 0 obj <<
/Length 49
>>
stream
q
0 1 1 rg
100 0 30 50 re B*
70 67 50 30 re B*
Q
endstream
endobj
7 0 obj <<
/Type /Page
/Parent 2 0 R
/Contents 8 0 R
>>
endobj
8 0 obj <<
/Length 49
>>
stream
q
1 0 0 rg
100 0 30 50 re B*
70 67 50 30 re B*
Q
endstream
endobj
9 0 obj <<
/Type /Page
/Parent 2 0 R
/Contents 10 0 R
>>
endobj
10 0 obj <<
/Length 51
>>
stream
q
0 1 0 rg
100 0 30 50 re B*
100 150 50 30 re B*
Q
endstream
endobj
11 0 obj <<
/Type /Page
/Parent 2 0 R
/Contents 12 0 R
>>
endobj
12 0 obj <<
/Length 50
>>
stream
q
0 0 0 rg
0 90 80 60 re B*
100 150 50 30 re B*
Q
endstream
endobj
xref
0 13
0000000000 65535 f
0000000015 00000 n
0000000068 00000 n
0000000186 00000 n
0000000255 00000 n
0000000355 00000 n
0000000437 00000 n
0000000537 00000 n
0000000606 00000 n
0000000706 00000 n
0000000776 00000 n
0000000879 00000 n
0000000950 00000 n
trailer <<
/Root 1 0 R
/Size 13
>>
startxref
1052
%%EOF