Skip to content

Commit

Permalink
Handle CSS @page margins correctly when scaling.
Browse files Browse the repository at this point in the history
Let Blink handle the scale factor set in PrintParams. The PDF plug-in
already does this, but Blink didn't. Therefore there was logic in
PrintRenderFrameHelper to determine whether the scale factor should be
applied or not. This can now be removed, since everyone (i.e. PDF and
Blink) is now able to honor blink::WebPrintParams::scale_factor.

The only scaling that PrintRenderFrameHelper now needs to deal with, is
the fit-to-page scale factor, i.e. when the page size specified in CSS
is larger than actual paper.

Add a bunch of browser tests for this. Test coverage was/is really low!
Also test all three scaling mechanisms, when combined: PrintParams
scale, Blink scaling (to prevent inline overflow), and
PrintRenderFrameHelper fit-to-page scaling.

Bug: 1480270
Change-Id: I163486eb0aa0fa2d3d630827394b19a2f97cdaf1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4853339
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1195404}
  • Loading branch information
mstensho authored and Chromium LUCI CQ committed Sep 12, 2023
1 parent ec2f7f0 commit 70c7b2b
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 119 deletions.
157 changes: 51 additions & 106 deletions components/printing/renderer/print_render_frame_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,10 @@ double FitPrintParamsToPage(const mojom::PrintParams& page_params,
}

mojom::PageSizeMarginsPtr CalculatePageLayoutFromPrintParams(
const mojom::PrintParams& params,
double scale_factor) {
bool fit_to_page = IsPrintScalingOptionFitToPage(params);
float content_width = params.content_size.width();
float content_height = params.content_size.height();
// Scale the content to its normal size for purpose of computing page layout.
// Otherwise we will get negative margins.
bool scale = fit_to_page || params.print_to_pdf;
if (scale && scale_factor >= PrintRenderFrameHelper::kEpsilon) {
content_width = std::round(params.content_size.width() * scale_factor);
content_height = std::round(params.content_size.height() * scale_factor);
}
const mojom::PrintParams& params) {
// TODO(crbug.com/1480958): Consider not rounding the values.
float content_width = std::round(params.content_size.width());
float content_height = std::round(params.content_size.height());

float margin_bottom =
params.page_size.height() - content_height - params.margin_top;
Expand Down Expand Up @@ -359,14 +351,9 @@ blink::WebPrintParams ComputeWebKitPrintParamsInDesiredDpi(
blink::WebPrintParams webkit_print_params;
int dpi = GetDPI(print_params);
webkit_print_params.printer_dpi = dpi;
if (source_is_pdf) {
// The |scale_factor| in print_params comes from the |scale_factor| in
// PrintSettings, which converts an integer percentage between 10 and 200
// to a float in PrintSettingsFromJobSettings. As a result, it can be
// converted back safely for the integer |scale_factor| in WebPrintParams.
webkit_print_params.scale_factor =
static_cast<int>(print_params.scale_factor * 100);
webkit_print_params.scale_factor = print_params.scale_factor;

if (source_is_pdf) {
#if BUILDFLAG(IS_APPLE)
// For Mac, GetDPI() returns a value that avoids DPI-based scaling. This is
// correct except when rastering PDFs, which uses |printer_dpi|, and the
Expand Down Expand Up @@ -556,47 +543,35 @@ mojom::PrintScalingOption GetPrintScalingOption(
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)

// Helper functions to inverse-scale float values with a double valued scaling.
float InverseScaleDouble(float value, double scaling) {
return static_cast<double>(value) / scaling;
}
gfx::SizeF InverseScaleDouble(gfx::SizeF original, double scaling) {
return gfx::SizeF(InverseScaleDouble(original.width(), scaling),
InverseScaleDouble(original.height(), scaling));
}
template <typename Param>
struct ParamWithFitToPageScale {
Param param;
double fit_to_page_scale_factor = 1.0f;
};

mojom::PrintParamsPtr CalculatePrintParamsForCss(
ParamWithFitToPageScale<mojom::PrintParamsPtr> CalculatePrintParamsForCss(
blink::WebLocalFrame* frame,
uint32_t page_index,
const mojom::PrintParams& page_params,
bool ignore_css_margins,
bool fit_to_page,
double* scale_factor) {
bool fit_to_page) {
mojom::PrintParamsPtr css_params = GetCssPrintParams(
frame, page_index, page_params, ignore_css_margins, fit_to_page);

mojom::PrintParamsPtr params = page_params.Clone();
EnsureOrientationMatches(*css_params, params.get());
params->content_size.SetSize(params->content_size.width() / *scale_factor,
params->content_size.height() / *scale_factor);
if (ignore_css_margins && fit_to_page) {
return params;
return {std::move(params)};
}

mojom::PrintParamsPtr result_params = std::move(css_params);
// If not printing a pdf or fitting to page, scale the page size.
bool scale = !params->print_to_pdf;
double page_scaling = scale ? *scale_factor : 1.0f;
if (!fit_to_page) {
result_params->page_size =
InverseScaleDouble(result_params->page_size, page_scaling);
}
if (ignore_css_margins) {
// Since not fitting to page, scale the page size and margins.
params->margin_left = InverseScaleDouble(params->margin_left, page_scaling);
params->margin_top = InverseScaleDouble(params->margin_top, page_scaling);
params->page_size = InverseScaleDouble(params->page_size, page_scaling);
double fit_to_page_scale_factor = 1.0f;

if (ignore_css_margins) {
// TODO(crbug.com/1477190): Copying from `params` shouldn't be necessary
// anymore, since Blink no longer modifies the input margins / size if told
// to leave them alone. There are some further complication here, due to
// EnsureOrientationMatches(), though...
result_params->margin_top = params->margin_top;
result_params->margin_left = params->margin_left;

Expand All @@ -614,39 +589,27 @@ mojom::PrintParamsPtr CalculatePrintParamsForCss(
result_params->margin_left - default_margin_right,
result_params->page_size.height() -
result_params->margin_top - default_margin_bottom);
} else {
// Using the CSS parameters. Scale CSS content size.
result_params->content_size =
gfx::SizeF(result_params->content_size.width() / *scale_factor,
result_params->content_size.height() / *scale_factor);
if (fit_to_page) {
double factor = FitPrintParamsToPage(*params, result_params.get());
*scale_factor *= factor;
} else {
// Already scaled the page, need to also scale the CSS margins since they
// are begin applied
result_params->margin_left =
InverseScaleDouble(result_params->margin_left, page_scaling);
result_params->margin_top =
InverseScaleDouble(result_params->margin_top, page_scaling);
}
} else if (fit_to_page) {
fit_to_page_scale_factor =
FitPrintParamsToPage(*params, result_params.get());
}

return result_params;
return {std::move(result_params), fit_to_page_scale_factor};
}

// Get page layout and fit to page if needed. The layout is in device pixels.
mojom::PageSizeMarginsPtr ComputePageLayoutForCss(
ParamWithFitToPageScale<mojom::PageSizeMarginsPtr> ComputePageLayoutForCss(
blink::WebLocalFrame* frame,
uint32_t page_index,
const mojom::PrintParams& page_params,
bool ignore_css_margins,
double* scale_factor) {
double input_scale_factor = *scale_factor;
mojom::PrintParamsPtr params = CalculatePrintParamsForCss(
frame, page_index, page_params, ignore_css_margins,
IsPrintScalingOptionFitToPage(page_params), scale_factor);
return CalculatePageLayoutFromPrintParams(*params, input_scale_factor);
bool ignore_css_margins) {
ParamWithFitToPageScale<mojom::PrintParamsPtr> result =
CalculatePrintParamsForCss(frame, page_index, page_params,
ignore_css_margins,
IsPrintScalingOptionFitToPage(page_params));
mojom::PageSizeMarginsPtr page_size_margins =
CalculatePageLayoutFromPrintParams(*result.param);
return {std::move(page_size_margins), result.fit_to_page_scale_factor};
}

bool CopyMetafileDataToReadOnlySharedMem(
Expand Down Expand Up @@ -683,12 +646,6 @@ bool CopyMetafileDataToDidPrintContentParams(
return true;
}

double GetScaleFactor(double input_scale_factor, bool is_pdf) {
if (input_scale_factor >= PrintRenderFrameHelper::kEpsilon && !is_pdf)
return input_scale_factor;
return 1.0f;
}

// Given the |device| and |canvas| to draw on, prints the appropriate headers
// and footers using strings from |header_footer_info| on to the canvas.
void PrintHeaderAndFooter(cc::PaintCanvas* canvas,
Expand Down Expand Up @@ -1121,10 +1078,10 @@ void PrepareFrameAndViewForPrint::ComputeScalingAndPrintParams(
web_print_params_ = ComputeWebKitPrintParamsInDesiredDpi(
*print_params, is_pdf, ignore_css_margins, fit_to_page);
frame->PrintBegin(web_print_params_, node_to_print_);
double scale_factor = GetScaleFactor(print_params->scale_factor, is_pdf);
print_params = CalculatePrintParamsForCss(frame, /*page_index=*/0,
*print_params, ignore_css_margins,
fit_to_page, &scale_factor);
print_params =
CalculatePrintParamsForCss(frame, /*page_index=*/0, *print_params,
ignore_css_margins, fit_to_page)
.param;
if (selection)
*selection = frame->SelectionAsMarkup().Utf8();
frame->PrintEnd();
Expand Down Expand Up @@ -1718,10 +1675,8 @@ void PrintRenderFrameHelper::SnapshotForContentAnalysis(
print_pages_params.params->document_cookie, page_count);

for (size_t page_index = 0; page_index < page_count; ++page_index) {
PrintPageInternal(
*print_pages_params.params, page_index, page_count,
GetScaleFactor(print_pages_params.params->scale_factor, is_pdf), frame,
metafile.get());
PrintPageInternal(*print_pages_params.params, page_index, page_count, frame,
metafile.get());
}
frame->PrintEnd();
metafile->FinishDocument();
Expand Down Expand Up @@ -1840,13 +1795,10 @@ PrintRenderFrameHelper::CreatePreviewDocument() {
if (delegate_->ShouldGenerateTaggedPDF())
snapshotter_ = render_frame()->CreateAXTreeSnapshotter(ui::AXMode::kPDF);

double scale_factor =
GetScaleFactor(print_params.scale_factor,
/*is_pdf=*/!print_preview_context_.IsModifiable());

mojom::PageSizeMarginsPtr default_page_layout =
ComputePageLayoutForCss(print_preview_context_.prepared_frame(), 0,
print_params, ignore_css_margins_, &scale_factor);
print_params, ignore_css_margins_)
.param;
int dpi = GetDPI(print_params);
// Convert to points.
default_page_layout =
Expand Down Expand Up @@ -1965,11 +1917,8 @@ bool PrintRenderFrameHelper::RenderPreviewPage(uint32_t page_index) {
render_metafile->UtilizeTypefaceContext(
print_preview_context_.typeface_content_info());
base::TimeTicks begin_time = base::TimeTicks::Now();
double scale_factor =
GetScaleFactor(print_params.scale_factor,
/*is_pdf=*/!print_preview_context_.IsModifiable());
PrintPageInternal(print_params, page_index,
print_preview_context_.total_page_count(), scale_factor,
print_preview_context_.total_page_count(),
print_preview_context_.prepared_frame(), render_metafile);
print_preview_context_.RenderedPreviewPage(base::TimeTicks::Now() -
begin_time);
Expand Down Expand Up @@ -2384,12 +2333,8 @@ bool PrintRenderFrameHelper::PrintPagesNative(
page_params->content = mojom::DidPrintContentParams::New();
page_params->page_size = ToFlooredSize(print_params.page_size);
page_params->content_area = gfx::Rect(page_params->page_size);
bool is_pdf =
IsPrintingPdfFrame(prep_frame_view_->frame(), prep_frame_view_->node());
for (uint32_t printed_page : printed_pages) {
PrintPageInternal(print_params, printed_page, page_count,
GetScaleFactor(print_params.scale_factor, is_pdf), frame,
&metafile);
PrintPageInternal(print_params, printed_page, page_count, frame, &metafile);
}

// blink::printEnd() for PDF should be called before metafile is closed.
Expand Down Expand Up @@ -2615,12 +2560,11 @@ bool PrintRenderFrameHelper::RenderPagesForPrint(blink::WebLocalFrame* frame,
void PrintRenderFrameHelper::PrintPageInternal(const mojom::PrintParams& params,
uint32_t page_index,
uint32_t page_count,
double scale_factor,
blink::WebLocalFrame* frame,
MetafileSkia* metafile) {
mojom::PageSizeMarginsPtr page_layout_in_device_pixels =
ComputePageLayoutForCss(frame, page_index, params, ignore_css_margins_,
&scale_factor);
ParamWithFitToPageScale<mojom::PageSizeMarginsPtr> layout =
ComputePageLayoutForCss(frame, page_index, params, ignore_css_margins_);
auto& page_layout_in_device_pixels = layout.param;
mojom::PageSizeMarginsPtr page_layout_in_css_pixels =
ConvertedPageSizeMargins(page_layout_in_device_pixels, GetDPI(params),
kPixelsPerInch);
Expand Down Expand Up @@ -2650,7 +2594,7 @@ void PrintRenderFrameHelper::PrintPageInternal(const mojom::PrintParams& params,
params, *page_layout_in_points, &page_size_in_points,
&content_area_ignored, &canvas_area_in_points);

double scale_factor_for_points = scale_factor *
double scale_factor_for_points = layout.fit_to_page_scale_factor *
static_cast<double>(kPointsPerInch) /
static_cast<double>(kPixelsPerInch);

Expand All @@ -2664,12 +2608,13 @@ void PrintRenderFrameHelper::PrintPageInternal(const mojom::PrintParams& params,
canvas->SetPrintingMetafile(metafile);

if (params.display_header_footer) {
PrintHeaderAndFooter(canvas, page_index, page_count, *frame, scale_factor,
PrintHeaderAndFooter(canvas, page_index, page_count, *frame,
layout.fit_to_page_scale_factor,
*page_layout_in_css_pixels, params);
}

RenderPageContent(frame, page_index, canvas_area, content_area, scale_factor,
canvas);
RenderPageContent(frame, page_index, canvas_area, content_area,
layout.fit_to_page_scale_factor, canvas);

// Done printing. Close the canvas to retrieve the compiled metafile.
bool ret = metafile->FinishPage();
Expand Down
1 change: 0 additions & 1 deletion components/printing/renderer/print_render_frame_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ class PrintRenderFrameHelper
void PrintPageInternal(const mojom::PrintParams& params,
uint32_t page_index,
uint32_t page_count,
double scale_factor,
blink::WebLocalFrame* frame,
MetafileSkia* metafile);

Expand Down
Loading

0 comments on commit 70c7b2b

Please sign in to comment.