0

arc: Use PrintRenderer to render a preview document

When printing from an ARC app, use the provided PrintRenderer to render
a preview document for pint preview. Also, flatten the preview document
received from ARC before displaying it in print preview.

Bug: b:140576300
Test: Print from an ARC app and verify a preview document is displayed
Change-Id: Iae7dcb2184afea51300f2d68edf3af40e55e6f13
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1853324
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Brian White <bcwhite@chromium.org>
Reviewed-by: Yusuke Sato <yusukes@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713013}
This commit is contained in:
Jesse Schettler
2019-11-06 15:21:27 +00:00
committed by Commit Bot
parent 6ad2f520d9
commit a092a99c42
25 changed files with 573 additions and 49 deletions

@ -82,6 +82,7 @@ source_set("chromeos") {
"//chrome/services/app_service:lib",
"//chrome/services/app_service/public/cpp:app_update",
"//chrome/services/file_util/public/cpp",
"//chrome/services/printing/public/mojom",
"//chrome/services/wilco_dtc_supportd/public/mojom",
"//chromeos",
"//chromeos/assistant:buildflags",

@ -4,26 +4,175 @@
#include "chrome/browser/chromeos/arc/print_spooler/print_session_impl.h"
#include <limits>
#include <utility>
#include "ash/public/cpp/arc_custom_tab.h"
#include "base/bind.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h"
#include "chrome/browser/printing/print_view_manager_common.h"
#include "chrome/browser/printing/printing_service.h"
#include "components/arc/mojom/print_common.mojom.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/base/shared_memory_utils.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "net/base/filename_util.h"
#include "printing/page_range.h"
#include "printing/print_job_constants.h"
#include "printing/print_settings.h"
#include "printing/print_settings_conversion.h"
#include "printing/units.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/size.h"
namespace arc {
namespace {
constexpr int kMinimumPdfSize = 50;
// Converts a color mode to its Mojo type.
mojom::PrintColorMode ToArcColorMode(int color_mode) {
return printing::IsColorModelSelected(color_mode)
? mojom::PrintColorMode::COLOR
: mojom::PrintColorMode::MONOCHROME;
}
// Converts a duplex mode to its Mojo type.
mojom::PrintDuplexMode ToArcDuplexMode(int duplex_mode) {
switch (duplex_mode) {
case printing::LONG_EDGE:
return mojom::PrintDuplexMode::LONG_EDGE;
case printing::SHORT_EDGE:
return mojom::PrintDuplexMode::SHORT_EDGE;
default:
return mojom::PrintDuplexMode::NONE;
}
}
// Gets and builds the print attributes from the job settings.
mojom::PrintAttributesPtr GetPrintAttributes(const base::Value& job_settings) {
// PrintMediaSize:
const base::Value* media_size_value =
job_settings.FindDictKey(printing::kSettingMediaSize);
if (!media_size_value)
return nullptr;
// Vendor ID will be empty when Destination is Save as PDF.
const std::string* vendor_id =
media_size_value->FindStringKey(printing::kSettingMediaSizeVendorId);
std::string id = "PDF";
if (vendor_id && !vendor_id->empty()) {
id = *vendor_id;
}
base::Optional<int> width_microns =
media_size_value->FindIntKey(printing::kSettingMediaSizeWidthMicrons);
base::Optional<int> height_microns =
media_size_value->FindIntKey(printing::kSettingMediaSizeHeightMicrons);
if (!width_microns.has_value() || !height_microns.has_value())
return nullptr;
// Swap the width and height if layout is landscape.
base::Optional<bool> landscape =
job_settings.FindBoolKey(printing::kSettingLandscape);
if (!landscape.has_value())
return nullptr;
gfx::Size size_micron;
if (landscape.value()) {
size_micron = gfx::Size(height_microns.value(), width_microns.value());
} else {
size_micron = gfx::Size(width_microns.value(), height_microns.value());
}
gfx::Size size_mil =
gfx::ScaleToRoundedSize(size_micron, 1.0f / printing::kMicronsPerMil);
mojom::PrintMediaSizePtr media_size = mojom::PrintMediaSize::New(
id, "ARC", size_mil.width(), size_mil.height());
// PrintResolution:
int horizontal_dpi = job_settings.FindIntKey(printing::kSettingDpiHorizontal)
.value_or(printing::kDefaultPdfDpi);
int vertical_dpi = job_settings.FindIntKey(printing::kSettingDpiVertical)
.value_or(printing::kDefaultPdfDpi);
// PrintMargins:
// Chrome uses margins to fit content to the printable area. Android uses
// margins to crop content to the printable area. Set margins to 0 to prevent
// cropping.
mojom::PrintMarginsPtr margins = mojom::PrintMargins::New(0, 0, 0, 0);
// PrintColorMode:
base::Optional<int> color = job_settings.FindIntKey(printing::kSettingColor);
if (!color.has_value())
return nullptr;
mojom::PrintColorMode color_mode = ToArcColorMode(color.value());
// PrintDuplexMode:
base::Optional<int> duplex =
job_settings.FindIntKey(printing::kSettingDuplexMode);
if (!duplex.has_value())
return nullptr;
mojom::PrintDuplexMode duplex_mode = ToArcDuplexMode(duplex.value());
return mojom::PrintAttributes::New(
std::move(media_size), gfx::Size(horizontal_dpi, vertical_dpi),
std::move(margins), color_mode, duplex_mode);
}
// Creates a PrintDocumentRequest from the provided |job_settings|. Uses helper
// functions to parse |job_settings|.
mojom::PrintDocumentRequestPtr PrintDocumentRequestFromJobSettings(
const base::Value& job_settings) {
return mojom::PrintDocumentRequest::New(
printing::GetPageRangesFromJobSettings(job_settings),
GetPrintAttributes(job_settings));
}
// Uses the provided ScopedHandle to read a preview document from ARC into
// read-only shared memory.
base::ReadOnlySharedMemoryRegion ReadPreviewDocument(
mojo::ScopedHandle preview_document,
size_t data_size) {
base::PlatformFile platform_file;
if (mojo::UnwrapPlatformFile(std::move(preview_document), &platform_file) !=
MOJO_RESULT_OK) {
return base::ReadOnlySharedMemoryRegion();
}
base::File src_file(platform_file);
if (!src_file.IsValid()) {
DPLOG(ERROR) << "Source file is invalid.";
return base::ReadOnlySharedMemoryRegion();
}
base::MappedReadOnlyRegion region_mapping =
mojo::CreateReadOnlySharedMemoryRegion(data_size);
if (!region_mapping.IsValid())
return std::move(region_mapping.region);
bool success = src_file.ReadAndCheck(
0, region_mapping.mapping.GetMemoryAsSpan<uint8_t>());
if (!success) {
DPLOG(ERROR) << "Error reading PDF.";
return base::ReadOnlySharedMemoryRegion();
}
return std::move(region_mapping.region);
}
} // namespace
// static
mojom::PrintSessionHostPtr PrintSessionImpl::Create(
std::unique_ptr<content::WebContents> web_contents,
@ -78,6 +227,58 @@ PrintSessionImpl::~PrintSessionImpl() {
base::BindOnce(&DeletePrintDocument, file_path));
}
void PrintSessionImpl::CreatePreviewDocument(
base::Value job_settings,
CreatePreviewDocumentCallback callback) {
mojom::PrintDocumentRequestPtr request =
PrintDocumentRequestFromJobSettings(job_settings);
if (!request || !request->attributes) {
std::move(callback).Run(base::ReadOnlySharedMemoryRegion());
return;
}
instance_->CreatePreviewDocument(
std::move(request),
base::BindOnce(&PrintSessionImpl::OnPreviewDocumentCreated,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void PrintSessionImpl::OnPreviewDocumentCreated(
CreatePreviewDocumentCallback callback,
mojo::ScopedHandle preview_document,
int64_t data_size) {
if (data_size < kMinimumPdfSize ||
!base::IsValueInRangeForNumericType<size_t>(data_size)) {
std::move(callback).Run(base::ReadOnlySharedMemoryRegion());
return;
}
base::PostTaskAndReplyWithResult(
FROM_HERE, {base::ThreadPool(), base::MayBlock()},
base::BindOnce(&ReadPreviewDocument, std::move(preview_document),
static_cast<size_t>(data_size)),
base::BindOnce(&PrintSessionImpl::OnPreviewDocumentRead,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void PrintSessionImpl::OnPreviewDocumentRead(
CreatePreviewDocumentCallback callback,
base::ReadOnlySharedMemoryRegion preview_document_region) {
if (!preview_document_region.IsValid()) {
std::move(callback).Run(std::move(preview_document_region));
return;
}
if (!pdf_flattener_.is_bound()) {
GetPrintingService()->BindPdfFlattener(
pdf_flattener_.BindNewPipeAndPassReceiver());
}
pdf_flattener_->FlattenPdf(
std::move(preview_document_region),
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback), base::ReadOnlySharedMemoryRegion()));
}
void PrintSessionImpl::Close() {
web_contents_->RemoveUserData(UserDataKey());
}

@ -8,13 +8,17 @@
#include <memory>
#include "base/macros.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h"
#include "chrome/services/printing/public/mojom/pdf_flattener.mojom.h"
#include "components/arc/mojom/print_spooler.mojom.h"
#include "components/printing/common/print.mojom.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace ash {
class ArcCustomTab;
@ -50,6 +54,23 @@ class PrintSessionImpl : public mojom::PrintSessionHost,
mojom::PrintSessionHostRequest request);
friend class content::WebContentsUserData<PrintSessionImpl>;
// printing::mojom::PrintRenderer:
void CreatePreviewDocument(base::Value job_settings,
CreatePreviewDocumentCallback callback) override;
// Called once the preview document has been created by ARC. The preview
// document must be read and flattened before being returned by the
// PrintRenderer.
void OnPreviewDocumentCreated(CreatePreviewDocumentCallback callback,
mojo::ScopedHandle preview_document,
int64_t data_size);
// Called once the preview document from ARC has been read. The preview
// document must be flattened before being returned by the PrintRenderer.
void OnPreviewDocumentRead(
CreatePreviewDocumentCallback callback,
base::ReadOnlySharedMemoryRegion preview_document_region);
// Used to close the ARC Custom Tab used for printing. If the remote end
// closes the connection, the ARC Custom Tab and print preview will be closed.
// If printing has already started, this will not cancel any active print job.
@ -69,6 +90,9 @@ class PrintSessionImpl : public mojom::PrintSessionHost,
// pipe.
mojo::Binding<mojom::PrintSessionHost> session_binding_;
// Remote interface used to flatten a PDF (preview document).
mojo::Remote<printing::mojom::PdfFlattener> pdf_flattener_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
// Note: This should remain the last member so it'll be destroyed and

@ -23,6 +23,13 @@ source_set("lib") {
"//mojo/public/cpp/bindings",
]
if (is_chromeos) {
sources += [
"pdf_flattener.cc",
"pdf_flattener.h",
]
}
if (is_win) {
sources += [
"pdf_to_emf_converter.cc",

@ -0,0 +1,49 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/printing/pdf_flattener.h"
#include <utility>
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/shared_memory_mapping.h"
#include "mojo/public/cpp/base/shared_memory_utils.h"
#include "pdf/pdf.h"
namespace printing {
PdfFlattener::PdfFlattener() = default;
PdfFlattener::~PdfFlattener() = default;
void PdfFlattener::FlattenPdf(base::ReadOnlySharedMemoryRegion src_pdf_region,
FlattenPdfCallback callback) {
base::ReadOnlySharedMemoryMapping pdf_mapping = src_pdf_region.Map();
if (!pdf_mapping.IsValid()) {
std::move(callback).Run(base::ReadOnlySharedMemoryRegion());
return;
}
auto input_pdf_buffer = pdf_mapping.GetMemoryAsSpan<const uint8_t>();
std::vector<uint8_t> output_pdf_buffer =
chrome_pdf::CreateFlattenedPdf(input_pdf_buffer);
if (output_pdf_buffer.empty()) {
std::move(callback).Run(base::ReadOnlySharedMemoryRegion());
return;
}
base::MappedReadOnlyRegion region_mapping =
mojo::CreateReadOnlySharedMemoryRegion(output_pdf_buffer.size());
if (!region_mapping.IsValid()) {
std::move(callback).Run(std::move(region_mapping.region));
return;
}
memcpy(region_mapping.mapping.memory(), output_pdf_buffer.data(),
output_pdf_buffer.size());
std::move(callback).Run(std::move(region_mapping.region));
}
} // namespace printing

@ -0,0 +1,29 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_SERVICES_PRINTING_PDF_FLATTENER_H_
#define CHROME_SERVICES_PRINTING_PDF_FLATTENER_H_
#include "base/macros.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "chrome/services/printing/public/mojom/pdf_flattener.mojom.h"
namespace printing {
class PdfFlattener : public printing::mojom::PdfFlattener {
public:
PdfFlattener();
~PdfFlattener() override;
// printing::mojom::PdfFlattener:
void FlattenPdf(base::ReadOnlySharedMemoryRegion src_pdf_region,
FlattenPdfCallback callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(PdfFlattener);
};
} // namespace printing
#endif // CHROME_SERVICES_PRINTING_PDF_FLATTENER_H_

@ -9,6 +9,10 @@
#include "chrome/services/printing/pdf_to_pwg_raster_converter.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#if defined(OS_CHROMEOS)
#include "chrome/services/printing/pdf_flattener.h"
#endif
#if defined(OS_WIN)
#include "chrome/services/printing/pdf_to_emf_converter.h"
#include "chrome/services/printing/pdf_to_emf_converter_factory.h"
@ -35,6 +39,14 @@ void PrintingService::BindPdfToPwgRasterConverter(
std::move(receiver));
}
#if defined(OS_CHROMEOS)
void PrintingService::BindPdfFlattener(
mojo::PendingReceiver<mojom::PdfFlattener> receiver) {
mojo::MakeSelfOwnedReceiver(std::make_unique<printing::PdfFlattener>(),
std::move(receiver));
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
void PrintingService::BindPdfToEmfConverterFactory(
mojo::PendingReceiver<mojom::PdfToEmfConverterFactory> receiver) {

@ -25,6 +25,10 @@ class PrintingService : public mojom::PrintingService {
mojo::PendingReceiver<mojom::PdfNupConverter> receiver) override;
void BindPdfToPwgRasterConverter(
mojo::PendingReceiver<mojom::PdfToPwgRasterConverter> receiver) override;
#if defined(OS_CHROMEOS)
void BindPdfFlattener(
mojo::PendingReceiver<mojom::PdfFlattener> receiver) override;
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
void BindPdfToEmfConverterFactory(
mojo::PendingReceiver<mojom::PdfToEmfConverterFactory> receiver) override;

@ -21,6 +21,10 @@ mojom("mojom") {
"//url/mojom:url_mojom_gurl",
]
if (is_chromeos) {
sources += [ "pdf_flattener.mojom" ]
}
if (is_win) {
sources += [ "pdf_to_emf_converter.mojom" ]
}

@ -0,0 +1,18 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module printing.mojom;
import "mojo/public/mojom/base/shared_memory.mojom";
// Interface used to flatten a PDF in a printing service utility process.
// Flattening a PDF converts forms and form data to text and graphics so they
// can no longer be modified and makes most JavaScript objects useless.
interface PdfFlattener {
// Flattens a PDF.
// |src_pdf_region| contains the PDF needing to be flattened.
// |flattened_pdf_region| contains the flattened PDF.
FlattenPdf(mojo_base.mojom.ReadOnlySharedMemoryRegion src_pdf_region)
=> (mojo_base.mojom.ReadOnlySharedMemoryRegion? flattened_pdf_region);
};

@ -7,6 +7,9 @@ module printing.mojom;
import "chrome/services/printing/public/mojom/pdf_nup_converter.mojom";
import "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom";
[EnableIf=is_chromeos]
import "chrome/services/printing/public/mojom/pdf_flattener.mojom";
[EnableIf=is_win]
import "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom";
@ -20,6 +23,10 @@ interface PrintingService {
BindPdfToPwgRasterConverter(
pending_receiver<PdfToPwgRasterConverter> receiver);
// Binds an interface that can be used to flatten a PDF.
[EnableIf=is_chromeos]
BindPdfFlattener(pending_receiver<PdfFlattener> receiver);
// Binds an interface that can be used to do PDF to EMF conversion. Windows
// only.
[EnableIf=is_win]

@ -72,6 +72,11 @@ struct PrintAttributes {
PrintDuplexMode duplex_mode;
};
struct PrintDocumentRequest {
array<PrintPageRange> pages;
PrintAttributes attributes;
};
struct PrintJobRequest {
// android.printservice.PrintJob fields:
array<int8> id;

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Next MinVersion: 3
// Next MinVersion: 4
module arc.mojom;
@ -18,12 +18,17 @@ interface PrintSessionHost {
// Represents the ARC side of a print session. Used by Chrome to send
// print-related messages to ARC and request rendered print documents.
// Next method ID: 1
// Next method ID: 2
interface PrintSessionInstance {
// Called when Chrome print preview is closed.
[MinVersion=2] OnPrintPreviewClosed@0();
// TODO(jschettler): Add method to render a print document.
// Creates a |preview_document| for print preview using the provided
// |request|.
// The returned |preview_document| will be null if errors occurred while
// rendering it.
[MinVersion=3] CreatePreviewDocument@1(PrintDocumentRequest request)
=> (handle? preview_document, int64 data_size);
};
// Used by ARC to create a new print session. A print session is used to print a

@ -4,11 +4,19 @@
module printing.mojom;
import "mojo/public/mojom/base/shared_memory.mojom";
import "mojo/public/mojom/base/values.mojom";
// Interface implemented by a class that desires to render print documents for
// Chrome print preview.
interface PrintRenderer {
// TODO(jschettler): Add methods to render a print document and signal the
// close of Chrome print preview.
// Creates a preview document for print preview using the provided
// |job_settings|.
// The returned |preview_document_region| contains the preview document data
// as a flattened PDF. It will be invalid if errors occurred while rendering
// the preview document.
CreatePreviewDocument(mojo_base.mojom.DictionaryValue job_settings)
=> (mojo_base.mojom.ReadOnlySharedMemoryRegion? preview_document_region);
};
// Render process interface exposed to the browser to handle most of the

@ -19,6 +19,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/metrics/histogram_functions.h"
#include "base/process/process_handle.h"
#include "base/single_thread_task_runner.h"
@ -1333,6 +1334,11 @@ void PrintRenderFrameHelper::OnPrintPreview(
return;
}
// Save the job settings if a PrintRenderer will be used to create the preview
// document.
if (print_renderer_)
print_renderer_job_settings_ = settings.Clone();
// Set the options from document if we are previewing a pdf and send a
// message to browser.
if (print_pages_params_->params.is_first_request &&
@ -1392,12 +1398,15 @@ void PrintRenderFrameHelper::OnFramePreparedForPreviewDocument() {
PrepareFrameForPreviewDocument();
return;
}
DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW);
CreatePreviewDocumentResult result = CreatePreviewDocument();
if (result != CREATE_IN_PROGRESS)
DidFinishPrinting(result == CREATE_SUCCESS ? OK : FAIL_PREVIEW);
}
bool PrintRenderFrameHelper::CreatePreviewDocument() {
PrintRenderFrameHelper::CreatePreviewDocumentResult
PrintRenderFrameHelper::CreatePreviewDocument() {
if (!print_pages_params_ || CheckForCancel())
return false;
return CREATE_FAIL;
base::UmaHistogramEnumeration(
print_preview_context_.IsForArc() ? "Arc.PrintPreview.PreviewEvent"
@ -1410,7 +1419,7 @@ bool PrintRenderFrameHelper::CreatePreviewDocument() {
if (!print_preview_context_.CreatePreviewDocument(
std::move(prep_frame_view_), pages, print_params.printed_doc_type,
print_params.document_cookie)) {
return false;
return CREATE_FAIL;
}
PageSizeMargins default_page_layout;
@ -1448,16 +1457,27 @@ bool PrintRenderFrameHelper::CreatePreviewDocument() {
GetFitToPageScaleFactor(printable_area_in_points);
Send(new PrintHostMsg_DidStartPreview(routing_id(), params, ids));
if (CheckForCancel())
return false;
return CREATE_FAIL;
// If a PrintRenderer has been provided, use it to create the preview
// document.
if (print_renderer_) {
base::TimeTicks begin_time = base::TimeTicks::Now();
print_renderer_->CreatePreviewDocument(
print_renderer_job_settings_.Clone(),
base::BindOnce(&PrintRenderFrameHelper::OnPreviewDocumentCreated,
weak_ptr_factory_.GetWeakPtr(), begin_time));
return CREATE_IN_PROGRESS;
}
while (!print_preview_context_.IsFinalPageRendered()) {
int page_number = print_preview_context_.GetNextPageNumber();
DCHECK_GE(page_number, 0);
if (!RenderPreviewPage(page_number))
return false;
return CREATE_FAIL;
if (CheckForCancel())
return false;
return CREATE_FAIL;
// We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
// print_preview_context_.AllPagesRendered()) before calling
@ -1473,11 +1493,11 @@ bool PrintRenderFrameHelper::CreatePreviewDocument() {
DCHECK(print_preview_context_.IsModifiable() ||
print_preview_context_.IsFinalPageRendered());
if (!FinalizePrintReadyDocument())
return false;
return CREATE_FAIL;
}
}
print_preview_context_.Finished();
return true;
return CREATE_SUCCESS;
}
bool PrintRenderFrameHelper::RenderPreviewPage(int page_number) {
@ -1537,6 +1557,45 @@ bool PrintRenderFrameHelper::FinalizePrintReadyDocument() {
return true;
}
void PrintRenderFrameHelper::OnPreviewDocumentCreated(
base::TimeTicks begin_time,
base::ReadOnlySharedMemoryRegion preview_document_region) {
bool success =
ProcessPreviewDocument(begin_time, std::move(preview_document_region));
DidFinishPrinting(success ? OK : FAIL_PREVIEW);
}
bool PrintRenderFrameHelper::ProcessPreviewDocument(
base::TimeTicks begin_time,
base::ReadOnlySharedMemoryRegion preview_document_region) {
// Record the render time for the entire document.
print_preview_context_.RenderedPreviewDocument(base::TimeTicks::Now() -
begin_time);
base::ReadOnlySharedMemoryMapping preview_document_mapping =
preview_document_region.Map();
if (!preview_document_mapping.IsValid())
return false;
auto preview_document_buffer =
preview_document_mapping.GetMemoryAsSpan<const uint8_t>();
if (!print_preview_context_.metafile()->InitFromData(
preview_document_buffer.data(), preview_document_buffer.size())) {
LOG(ERROR) << "Failed to initialize PDF metafile.";
return false;
}
if (CheckForCancel())
return false;
print_preview_context_.AllPagesRendered();
if (!FinalizePrintReadyDocument())
return false;
print_preview_context_.Finished();
return true;
}
int PrintRenderFrameHelper::GetFitToPageScaleFactor(
const gfx::Rect& printable_area_in_points) {
if (print_preview_context_.IsModifiable())
@ -2411,6 +2470,12 @@ void PrintRenderFrameHelper::PrintPreviewContext::RenderedPreviewPage(
base::UmaHistogramTimes("PrintPreview.RenderPDFPageTime", page_time);
}
void PrintRenderFrameHelper::PrintPreviewContext::RenderedPreviewDocument(
const base::TimeDelta document_time) {
DCHECK_EQ(RENDERING, state_);
document_render_time_ += document_time;
}
void PrintRenderFrameHelper::PrintPreviewContext::AllPagesRendered() {
DCHECK_EQ(RENDERING, state_);
state_ = DONE;

@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
@ -150,6 +151,14 @@ class PrintRenderFrameHelper
FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintRenderFrameHelperTest, PrintWithIframe);
#endif // defined(OS_WIN) || defined(OS_MACOSX)
// CREATE_IN_PROGRESS signifies that the preview document is being rendered
// asynchronously by a PrintRenderer.
enum CreatePreviewDocumentResult {
CREATE_SUCCESS,
CREATE_IN_PROGRESS,
CREATE_FAIL,
};
enum PrintingResult {
OK,
FAIL_PRINT_INIT,
@ -247,7 +256,7 @@ class PrintRenderFrameHelper
void OnFramePreparedForPreviewDocument();
// Initialize the print preview document.
bool CreatePreviewDocument();
CreatePreviewDocumentResult CreatePreviewDocument();
// Renders a print preview page. |page_number| is 0-based.
// Returns true if print preview should continue, false on failure.
@ -256,6 +265,18 @@ class PrintRenderFrameHelper
// Finalize the print ready preview document.
bool FinalizePrintReadyDocument();
// Called after a preview document has been created by a PrintRenderer.
void OnPreviewDocumentCreated(
base::TimeTicks begin_time,
base::ReadOnlySharedMemoryRegion preview_document_region);
// Finish processing the preview document created by a PrintRenderer (record
// the render time, update the PrintPreviewContext, and finalize the print
// ready preview document).
bool ProcessPreviewDocument(
base::TimeTicks begin_time,
base::ReadOnlySharedMemoryRegion preview_document_region);
// Helper method to calculate the scale factor for fit-to-page.
int GetFitToPageScaleFactor(const gfx::Rect& printable_area_in_points);
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@ -418,6 +439,9 @@ class PrintRenderFrameHelper
// Used to check the prerendering status.
const std::unique_ptr<Delegate> delegate_;
// Settings used by a PrintRenderer to create a preview document.
base::Value print_renderer_job_settings_;
// Used to render print documents from an external source (ARC, Crostini,
// etc.).
mojom::PrintRendererAssociatedPtr print_renderer_;
@ -451,6 +475,10 @@ class PrintRenderFrameHelper
// rendering took.
void RenderedPreviewPage(const base::TimeDelta& page_time);
// Called after a preview document gets rendered by a PrintRenderer.
// |document_time| is how long the rendering took.
void RenderedPreviewDocument(const base::TimeDelta document_time);
// Updates the print preview context when the required pages are rendered.
void AllPagesRendered();

@ -34,6 +34,14 @@ class ScopedSdkInitializer {
} // namespace
#if defined(OS_CHROMEOS)
std::vector<uint8_t> CreateFlattenedPdf(
base::span<const uint8_t> input_buffer) {
ScopedSdkInitializer scoped_sdk_initializer(/*enable_v8=*/false);
return PDFEngineExports::Get()->CreateFlattenedPdf(input_buffer);
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
bool RenderPDFPageToDC(base::span<const uint8_t> pdf_buffer,
int page_number,

@ -27,6 +27,13 @@ class Size;
namespace chrome_pdf {
#if defined(OS_CHROMEOS)
// Create a flattened PDF document from an existing PDF document.
// |input_buffer| is the buffer that contains the entire PDF document to be
// flattened.
std::vector<uint8_t> CreateFlattenedPdf(base::span<const uint8_t> input_buffer);
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
// Printing modes - type to convert PDF to for printing
enum PrintingMode {

@ -474,6 +474,12 @@ class PDFEngineExports {
static PDFEngineExports* Get();
#if defined(OS_CHROMEOS)
// See the definition of CreateFlattenedPdf in pdf.cc for details.
virtual std::vector<uint8_t> CreateFlattenedPdf(
base::span<const uint8_t> input_buffer) = 0;
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
// See the definition of RenderPDFPageToDC in pdf.cc for details.
virtual bool RenderPDFPageToDC(base::span<const uint8_t> pdf_buffer,

@ -161,6 +161,16 @@ PDFiumEngineExports::PDFiumEngineExports() {}
PDFiumEngineExports::~PDFiumEngineExports() {}
#if defined(OS_CHROMEOS)
std::vector<uint8_t> PDFiumEngineExports::CreateFlattenedPdf(
base::span<const uint8_t> input_buffer) {
ScopedFPDFDocument doc = LoadPdfData(input_buffer);
if (!doc)
return std::vector<uint8_t>();
return PDFiumPrint::CreateFlattenedPdf(std::move(doc));
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
bool PDFiumEngineExports::RenderPDFPageToDC(
base::span<const uint8_t> pdf_buffer,

@ -19,6 +19,10 @@ class PDFiumEngineExports : public PDFEngineExports {
~PDFiumEngineExports() override;
// PDFEngineExports:
#if defined(OS_CHROMEOS)
std::vector<uint8_t> CreateFlattenedPdf(
base::span<const uint8_t> input_buffer) override;
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN)
bool RenderPDFPageToDC(base::span<const uint8_t> pdf_buffer,
int page_number,

@ -254,12 +254,34 @@ std::string GetPageRangeStringFromRange(
return page_number_str;
}
bool FlattenPrintData(FPDF_DOCUMENT doc) {
DCHECK(doc);
int page_count = FPDF_GetPageCount(doc);
for (int i = 0; i < page_count; ++i) {
ScopedFPDFPage page(FPDF_LoadPage(doc, i));
DCHECK(page);
if (FPDFPage_Flatten(page.get(), FLAT_PRINT) == FLATTEN_FAIL)
return false;
}
return true;
}
} // namespace
PDFiumPrint::PDFiumPrint(PDFiumEngine* engine) : engine_(engine) {}
PDFiumPrint::~PDFiumPrint() = default;
#if defined(OS_CHROMEOS)
// static
std::vector<uint8_t> PDFiumPrint::CreateFlattenedPdf(ScopedFPDFDocument doc) {
if (!FlattenPrintData(doc.get()))
return std::vector<uint8_t>();
return ConvertDocToBuffer(std::move(doc));
}
#endif // defined(OS_CHROMEOS)
// static
std::vector<uint32_t> PDFiumPrint::GetPageNumbersFromPrintPageNumberRange(
const PP_PrintPageNumberRange_Dev* page_ranges,
@ -477,17 +499,4 @@ ScopedFPDFDocument PDFiumPrint::CreateSinglePageRasterPdf(
return temp_doc;
}
bool PDFiumPrint::FlattenPrintData(FPDF_DOCUMENT doc) const {
DCHECK(doc);
int page_count = FPDF_GetPageCount(doc);
for (int i = 0; i < page_count; ++i) {
ScopedFPDFPage page(FPDF_LoadPage(doc, i));
DCHECK(page);
if (FPDFPage_Flatten(page.get(), FLAT_PRINT) == FLATTEN_FAIL)
return false;
}
return true;
}
} // namespace chrome_pdf

@ -8,6 +8,7 @@
#include <vector>
#include "base/macros.h"
#include "build/build_config.h"
#include "third_party/pdfium/public/cpp/fpdf_scopers.h"
#include "third_party/pdfium/public/fpdfview.h"
@ -29,6 +30,13 @@ class PDFiumPrint {
explicit PDFiumPrint(PDFiumEngine* engine);
~PDFiumPrint();
#if defined(OS_CHROMEOS)
// Flattens the |doc|.
// On success, returns the flattened version of |doc| as a vector.
// On failure, returns an empty vector.
static std::vector<uint8_t> CreateFlattenedPdf(ScopedFPDFDocument doc);
#endif // defined(OS_CHROMEOS)
static std::vector<uint32_t> GetPageNumbersFromPrintPageNumberRange(
const PP_PrintPageNumberRange_Dev* page_ranges,
uint32_t page_range_count);
@ -77,8 +85,6 @@ class PDFiumPrint {
FPDF_PAGE page_to_print,
const PP_PrintSettings_Dev& print_settings);
bool FlattenPrintData(FPDF_DOCUMENT doc) const;
PDFiumEngine* const engine_;
DISALLOW_COPY_AND_ASSIGN(PDFiumPrint);

@ -73,6 +73,28 @@ void SetRectToJobSettings(const std::string& json_path,
} // namespace
PageRanges GetPageRangesFromJobSettings(const base::Value& job_settings) {
PageRanges page_ranges;
const base::Value* page_range_array =
job_settings.FindListKey(kSettingPageRange);
if (page_range_array) {
for (const base::Value& page_range : page_range_array->GetList()) {
if (!page_range.is_dict())
continue;
base::Optional<int> from = page_range.FindIntKey(kSettingPageRangeFrom);
base::Optional<int> to = page_range.FindIntKey(kSettingPageRangeTo);
if (!from.has_value() || !to.has_value())
continue;
// Page numbers are 1-based in the dictionary.
// Page numbers are 0-based for the printing context.
page_ranges.push_back(PageRange{from.value() - 1, to.value() - 1});
}
}
return page_ranges;
}
bool PrintSettingsFromJobSettings(const base::Value& job_settings,
PrintSettings* settings) {
base::Optional<bool> display_header_footer =
@ -134,25 +156,7 @@ bool PrintSettingsFromJobSettings(const base::Value& job_settings,
if (margin_type == CUSTOM_MARGINS)
settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
PageRanges new_ranges;
const base::Value* page_range_array =
job_settings.FindKeyOfType(kSettingPageRange, base::Value::Type::LIST);
if (page_range_array) {
for (const base::Value& value : page_range_array->GetList()) {
if (!value.is_dict())
continue;
base::Optional<int> from = value.FindIntKey(kSettingPageRangeFrom);
base::Optional<int> to = value.FindIntKey(kSettingPageRangeTo);
if (!from.has_value() || !to.has_value())
continue;
// Page numbers are 1-based in the dictionary.
// Page numbers are 0-based for the printing context.
new_ranges.push_back(PageRange{from.value() - 1, to.value() - 1});
}
}
settings->set_ranges(new_ranges);
settings->set_ranges(GetPageRangesFromJobSettings(job_settings));
base::Optional<bool> collate = job_settings.FindBoolKey(kSettingCollate);
base::Optional<int> copies = job_settings.FindIntKey(kSettingCopies);

@ -18,6 +18,9 @@ namespace printing {
class PrintSettings;
PRINTING_EXPORT PageRanges
GetPageRangesFromJobSettings(const base::Value& job_settings);
PRINTING_EXPORT bool PrintSettingsFromJobSettings(
const base::Value& job_settings,
PrintSettings* print_settings);