| // 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 <algorithm> |
| |
| #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 kDefaultPageOrientation[] = "defaultPageOrientation"; |
| constexpr char kTwoUpViewEnabled[] = "twoUpViewEnabled"; |
| |
| int GetWidestPageWidth(const std::vector<gfx::Size>& 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(inset_sizes.left, inset_sizes.top, inset_sizes.right, |
| inset_sizes.bottom); |
| 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 DocumentLayout::Options::ToValue() const { |
| base::Value dictionary(base::Value::Type::DICTIONARY); |
| dictionary.SetIntKey(kDefaultPageOrientation, |
| static_cast<int32_t>(default_page_orientation_)); |
| dictionary.SetBoolKey(kTwoUpViewEnabled, |
| page_spread_ == PageSpread::kTwoUpOdd); |
| return dictionary; |
| } |
| |
| void DocumentLayout::Options::FromValue(const base::Value& value) { |
| DCHECK(value.is_dict()); |
| |
| int32_t default_page_orientation = |
| value.FindIntKey(kDefaultPageOrientation).value(); |
| DCHECK_GE(default_page_orientation, |
| static_cast<int32_t>(PageOrientation::kOriginal)); |
| DCHECK_LE(default_page_orientation, |
| static_cast<int32_t>(PageOrientation::kLast)); |
| default_page_orientation_ = |
| static_cast<PageOrientation>(default_page_orientation); |
| |
| page_spread_ = value.FindBoolKey(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<gfx::Size>& 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<gfx::Size>& 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<gfx::Size>& 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 |