// 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 "pdf/document_layout.h" #include #include "base/check_op.h" #include "base/values.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" namespace chrome_pdf { namespace { constexpr char kDirection[] = "direction"; constexpr char kDefaultPageOrientation[] = "defaultPageOrientation"; constexpr char kTwoUpViewEnabled[] = "twoUpViewEnabled"; int GetWidestPageWidth(const std::vector& page_sizes) { int widest_page_width = 0; for (const auto& page_size : page_sizes) { widest_page_width = std::max(widest_page_width, page_size.width()); } return widest_page_width; } gfx::Rect InsetRect(const gfx::Rect& rect, const draw_utils::PageInsetSizes& inset_sizes) { gfx::Rect inset_rect(rect); inset_rect.Inset(gfx::Insets::TLBR(inset_sizes.top, inset_sizes.left, inset_sizes.bottom, inset_sizes.right)); return inset_rect; } } // namespace const draw_utils::PageInsetSizes DocumentLayout::kSingleViewInsets{ /*left=*/5, /*top=*/3, /*right=*/5, /*bottom=*/7}; DocumentLayout::Options::Options() = default; DocumentLayout::Options::Options(const Options& other) = default; DocumentLayout::Options& DocumentLayout::Options::operator=( const Options& other) = default; DocumentLayout::Options::~Options() = default; base::Value::Dict DocumentLayout::Options::ToValue() const { base::Value::Dict dictionary; dictionary.Set(kDirection, direction_); dictionary.Set(kDefaultPageOrientation, static_cast(default_page_orientation_)); dictionary.Set(kTwoUpViewEnabled, page_spread_ == PageSpread::kTwoUpOdd); return dictionary; } void DocumentLayout::Options::FromValue(const base::Value::Dict& value) { int32_t direction = value.FindInt(kDirection).value(); DCHECK_GE(direction, base::i18n::UNKNOWN_DIRECTION); DCHECK_LE(direction, base::i18n::TEXT_DIRECTION_MAX); direction_ = static_cast(direction); int32_t default_page_orientation = value.FindInt(kDefaultPageOrientation).value(); DCHECK_GE(default_page_orientation, static_cast(PageOrientation::kOriginal)); DCHECK_LE(default_page_orientation, static_cast(PageOrientation::kLast)); default_page_orientation_ = static_cast(default_page_orientation); page_spread_ = value.FindBool(kTwoUpViewEnabled).value() ? PageSpread::kTwoUpOdd : PageSpread::kOneUp; } void DocumentLayout::Options::RotatePagesClockwise() { default_page_orientation_ = RotateClockwise(default_page_orientation_); } void DocumentLayout::Options::RotatePagesCounterclockwise() { default_page_orientation_ = RotateCounterclockwise(default_page_orientation_); } DocumentLayout::DocumentLayout() = default; DocumentLayout::~DocumentLayout() = default; void DocumentLayout::SetOptions(const Options& options) { // To be conservative, we want to consider the layout dirty for any layout // option changes, even if the page rects don't necessarily change when // layout options change. // // We also probably don't want layout changes to actually kick in until // the next call to ComputeLayout(). (In practice, we'll call ComputeLayout() // shortly after calling SetOptions().) if (options_ != options) { dirty_ = true; } options_ = options; } void DocumentLayout::ComputeLayout(const std::vector& page_sizes) { switch (options_.page_spread()) { case PageSpread::kOneUp: return ComputeOneUpLayout(page_sizes); case PageSpread::kTwoUpOdd: return ComputeTwoUpOddLayout(page_sizes); } } void DocumentLayout::ComputeOneUpLayout( const std::vector& page_sizes) { gfx::Size document_size(GetWidestPageWidth(page_sizes), 0); if (page_layouts_.size() != page_sizes.size()) { // TODO(kmoon): May want to do less work when shrinking a layout. page_layouts_.resize(page_sizes.size()); dirty_ = true; } for (size_t i = 0; i < page_sizes.size(); ++i) { if (i != 0) { // Add space for bottom separator. document_size.Enlarge(0, kBottomSeparator); } const gfx::Size& page_size = page_sizes[i]; gfx::Rect page_rect = draw_utils::GetRectForSingleView(page_size, document_size); CopyRectIfModified(page_rect, page_layouts_[i].outer_rect); CopyRectIfModified(InsetRect(page_rect, kSingleViewInsets), page_layouts_[i].inner_rect); draw_utils::ExpandDocumentSize(page_size, &document_size); } if (size_ != document_size) { size_ = document_size; dirty_ = true; } } void DocumentLayout::ComputeTwoUpOddLayout( const std::vector& page_sizes) { gfx::Size document_size(GetWidestPageWidth(page_sizes), 0); if (page_layouts_.size() != page_sizes.size()) { // TODO(kmoon): May want to do less work when shrinking a layout. page_layouts_.resize(page_sizes.size()); dirty_ = true; } for (size_t i = 0; i < page_sizes.size(); ++i) { draw_utils::PageInsetSizes page_insets = draw_utils::GetPageInsetsForTwoUpView( i, page_sizes.size(), kSingleViewInsets, kHorizontalSeparator); const gfx::Size& page_size = page_sizes[i]; gfx::Rect page_rect; if (i % 2 == 0) { page_rect = draw_utils::GetLeftRectForTwoUpView( page_size, {document_size.width(), document_size.height()}); } else { page_rect = draw_utils::GetRightRectForTwoUpView( page_size, {document_size.width(), document_size.height()}); document_size.Enlarge( 0, std::max(page_size.height(), page_sizes[i - 1].height())); } CopyRectIfModified(page_rect, page_layouts_[i].outer_rect); CopyRectIfModified(InsetRect(page_rect, page_insets), page_layouts_[i].inner_rect); } if (page_sizes.size() % 2 == 1) { document_size.Enlarge(0, page_sizes.back().height()); } document_size.set_width(2 * document_size.width()); if (size_ != document_size) { size_ = document_size; dirty_ = true; } } void DocumentLayout::CopyRectIfModified(const gfx::Rect& source_rect, gfx::Rect& destination_rect) { if (destination_rect != source_rect) { destination_rect = source_rect; dirty_ = true; } } } // namespace chrome_pdf