910e62b5创建于 1月15日历史提交
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/page_setup.h"

#include <algorithm>
#include <tuple>

#include "base/check_op.h"
#include "base/strings/stringprintf.h"

namespace printing {

namespace {

// Checks whether `printable_area` can be used to form a valid symmetrical
// printable area, so that margin_left equals margin_right, and margin_top
// equals margin_bottom.  For example if
// printable_area.x() * 2 >= page_size.width(), then the
// content_width = page_size.width() - 2 * printable_area.x() would be zero or
// negative, which is invalid.
// `page_size` is the physical page size that includes margins.
bool IsValidPrintableArea(const gfx::Size& page_size,
                          const gfx::Rect& printable_area) {
  return !printable_area.IsEmpty() && printable_area.x() >= 0 &&
         printable_area.y() >= 0 &&
         printable_area.right() <= page_size.width() &&
         printable_area.bottom() <= page_size.height() &&
         printable_area.x() * 2 < page_size.width() &&
         printable_area.y() * 2 < page_size.height() &&
         printable_area.right() * 2 > page_size.width() &&
         printable_area.bottom() * 2 > page_size.height();
}

}  // namespace

PageMargins::PageMargins()
    : header(0), footer(0), left(0), right(0), top(0), bottom(0) {}

PageMargins::PageMargins(int header,
                         int footer,
                         int left,
                         int right,
                         int top,
                         int bottom)
    : header(header),
      footer(footer),
      left(left),
      right(right),
      top(top),
      bottom(bottom) {}

bool PageMargins::operator==(const PageMargins& other) const {
  return std::tie(header, footer, left, right, top, bottom) ==
         std::tie(other.header, other.footer, other.left, other.right,
                  other.top, other.bottom);
}

void PageMargins::Clear() {
  header = 0;
  footer = 0;
  left = 0;
  right = 0;
  top = 0;
  bottom = 0;
}

std::string PageMargins::ToString() const {
  return base::StringPrintf(
      "header=%d, footer=%d, left=%d, right=%d, top=%d, bottom=%d)", header,
      footer, left, right, top, bottom);
}

bool PageMargins::IsEmpty() const {
  return header == 0 && footer == 0 && left == 0 && right == 0 && top == 0 &&
         bottom == 0;
}

PageSetup::PageSetup() {
  Clear();
}

PageSetup::PageSetup(const gfx::Size& physical_size,
                     const gfx::Rect& printable_area,
                     const PageMargins& requested_margins,
                     bool forced_margins,
                     int text_height)
    : requested_margins_(requested_margins), forced_margins_(forced_margins) {
  Init(physical_size, printable_area, text_height);
}

PageSetup::PageSetup(const PageSetup& other) = default;

PageSetup::~PageSetup() = default;

bool PageSetup::operator==(const PageSetup& other) const {
  return std::tie(physical_size_, printable_area_, overlay_area_, content_area_,
                  effective_margins_, requested_margins_, forced_margins_,
                  text_height_) ==
         std::tie(other.physical_size_, other.printable_area_,
                  other.overlay_area_, other.content_area_,
                  other.effective_margins_, other.requested_margins_,
                  other.forced_margins_, other.text_height_);
}

// static
gfx::Rect PageSetup::GetSymmetricalPrintableArea(
    const gfx::Size& page_size,
    const gfx::Rect& printable_area) {
  if (!IsValidPrintableArea(page_size, printable_area))
    return gfx::Rect();

  int left_right_margin =
      std::max(printable_area.x(), page_size.width() - printable_area.right());
  int top_bottom_margin = std::max(
      printable_area.y(), page_size.height() - printable_area.bottom());
  int width = page_size.width() - 2 * left_right_margin;
  int height = page_size.height() - 2 * top_bottom_margin;

  gfx::Rect symmetrical_printable_area = gfx::Rect(page_size);
  symmetrical_printable_area.ClampToCenteredSize(gfx::Size(width, height));

  return symmetrical_printable_area;
}

void PageSetup::Clear() {
  physical_size_.SetSize(0, 0);
  printable_area_.SetRect(0, 0, 0, 0);
  overlay_area_.SetRect(0, 0, 0, 0);
  content_area_.SetRect(0, 0, 0, 0);
  effective_margins_.Clear();
  text_height_ = 0;
  forced_margins_ = false;
}

void PageSetup::Init(const gfx::Size& physical_size,
                     const gfx::Rect& printable_area,
                     int text_height) {
  DCHECK_LE(printable_area.right(), physical_size.width());
  // I've seen this assert triggers on Canon GP160PF PCL 5e and HP LaserJet 5.
  // Since we don't know the dpi here, just disable the check.
  // DCHECK_LE(printable_area.bottom(), physical_size.height());
  DCHECK_GE(printable_area.x(), 0);
  DCHECK_GE(printable_area.y(), 0);
  DCHECK_GE(text_height, 0);
  physical_size_ = physical_size;
  printable_area_ = printable_area;
  text_height_ = text_height;

  SetRequestedMarginsAndCalculateSizes(requested_margins_);
}

void PageSetup::SetRequestedMargins(const PageMargins& requested_margins) {
  forced_margins_ = false;
  SetRequestedMarginsAndCalculateSizes(requested_margins);
}

void PageSetup::ForceRequestedMargins(const PageMargins& requested_margins) {
  forced_margins_ = true;
  SetRequestedMarginsAndCalculateSizes(requested_margins);
}

void PageSetup::FlipOrientation() {
  if (physical_size_.width() && physical_size_.height()) {
    gfx::Size new_size(physical_size_.height(), physical_size_.width());
    int new_y = physical_size_.width() -
                (printable_area_.width() + printable_area_.x());
    gfx::Rect new_printable_area(printable_area_.y(), new_y,
                                 printable_area_.height(),
                                 printable_area_.width());
    Init(new_size, new_printable_area, text_height_);
  }
}

void PageSetup::SetRequestedMarginsAndCalculateSizes(
    const PageMargins& requested_margins) {
  requested_margins_ = requested_margins;
  if (physical_size_.width() && physical_size_.height()) {
    if (forced_margins_)
      CalculateSizesWithinRect(gfx::Rect(physical_size_), 0);
    else
      CalculateSizesWithinRect(printable_area_, text_height_);
  }
}

void PageSetup::CalculateSizesWithinRect(const gfx::Rect& bounds,
                                         int text_height) {
  // Calculate the effective margins. The tricky part.
  effective_margins_.header = std::max(requested_margins_.header, bounds.y());
  effective_margins_.footer = std::max(
      requested_margins_.footer, physical_size_.height() - bounds.bottom());
  effective_margins_.left = std::max(requested_margins_.left, bounds.x());
  effective_margins_.top = std::max({requested_margins_.top, bounds.y(),
                                     effective_margins_.header + text_height});
  effective_margins_.right = std::max(requested_margins_.right,
                                      physical_size_.width() - bounds.right());
  effective_margins_.bottom = std::max(
      {requested_margins_.bottom, physical_size_.height() - bounds.bottom(),
       effective_margins_.footer + text_height});

  // Calculate the overlay area. If the margins are excessive, the overlay_area
  // size will be (0, 0).
  overlay_area_.set_x(effective_margins_.left);
  overlay_area_.set_y(effective_margins_.header);
  overlay_area_.set_width(std::max(
      0,
      physical_size_.width() - effective_margins_.right - overlay_area_.x()));
  overlay_area_.set_height(std::max(
      0,
      physical_size_.height() - effective_margins_.footer - overlay_area_.y()));

  // Calculate the content area. If the margins are excessive, the content_area
  // size will be (0, 0).
  content_area_.set_x(effective_margins_.left);
  content_area_.set_y(effective_margins_.top);
  content_area_.set_width(std::max(
      0,
      physical_size_.width() - effective_margins_.right - content_area_.x()));
  content_area_.set_height(std::max(
      0,
      physical_size_.height() - effective_margins_.bottom - content_area_.y()));
}

}  // namespace printing