1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "pdf/document_layout.h"
9 #include "base/check_op.h"
10 #include "base/values.h"
11 #include "ui/gfx/geometry/point.h"
12 #include "ui/gfx/geometry/rect.h"
13 #include "ui/gfx/geometry/size.h"
15 namespace chrome_pdf {
19 constexpr char kDirection[] = "direction";
20 constexpr char kDefaultPageOrientation[] = "defaultPageOrientation";
21 constexpr char kTwoUpViewEnabled[] = "twoUpViewEnabled";
23 int GetWidestPageWidth(const std::vector<gfx::Size>& page_sizes) {
24 int widest_page_width = 0;
25 for (const auto& page_size : page_sizes) {
26 widest_page_width = std::max(widest_page_width, page_size.width());
29 return widest_page_width;
32 gfx::Rect InsetRect(const gfx::Rect& rect,
33 const draw_utils::PageInsetSizes& inset_sizes) {
34 gfx::Rect inset_rect(rect);
35 inset_rect.Inset(gfx::Insets::TLBR(inset_sizes.top, inset_sizes.left,
36 inset_sizes.bottom, inset_sizes.right));
42 const draw_utils::PageInsetSizes DocumentLayout::kSingleViewInsets{
43 /*left=*/5, /*top=*/3, /*right=*/5, /*bottom=*/7};
45 DocumentLayout::Options::Options() = default;
47 DocumentLayout::Options::Options(const Options& other) = default;
48 DocumentLayout::Options& DocumentLayout::Options::operator=(
49 const Options& other) = default;
51 DocumentLayout::Options::~Options() = default;
53 base::Value::Dict DocumentLayout::Options::ToValue() const {
54 base::Value::Dict dictionary;
55 dictionary.Set(kDirection, direction_);
56 dictionary.Set(kDefaultPageOrientation,
57 static_cast<int>(default_page_orientation_));
58 dictionary.Set(kTwoUpViewEnabled, page_spread_ == PageSpread::kTwoUpOdd);
62 void DocumentLayout::Options::FromValue(const base::Value::Dict& value) {
63 int32_t direction = value.FindInt(kDirection).value();
64 DCHECK_GE(direction, base::i18n::UNKNOWN_DIRECTION);
65 DCHECK_LE(direction, base::i18n::TEXT_DIRECTION_MAX);
66 direction_ = static_cast<base::i18n::TextDirection>(direction);
68 int32_t default_page_orientation =
69 value.FindInt(kDefaultPageOrientation).value();
70 DCHECK_GE(default_page_orientation,
71 static_cast<int32_t>(PageOrientation::kOriginal));
72 DCHECK_LE(default_page_orientation,
73 static_cast<int32_t>(PageOrientation::kLast));
74 default_page_orientation_ =
75 static_cast<PageOrientation>(default_page_orientation);
77 page_spread_ = value.FindBool(kTwoUpViewEnabled).value()
78 ? PageSpread::kTwoUpOdd
82 void DocumentLayout::Options::RotatePagesClockwise() {
83 default_page_orientation_ = RotateClockwise(default_page_orientation_);
86 void DocumentLayout::Options::RotatePagesCounterclockwise() {
87 default_page_orientation_ = RotateCounterclockwise(default_page_orientation_);
90 DocumentLayout::DocumentLayout() = default;
92 DocumentLayout::~DocumentLayout() = default;
94 void DocumentLayout::SetOptions(const Options& options) {
95 // To be conservative, we want to consider the layout dirty for any layout
96 // option changes, even if the page rects don't necessarily change when
97 // layout options change.
99 // We also probably don't want layout changes to actually kick in until
100 // the next call to ComputeLayout(). (In practice, we'll call ComputeLayout()
101 // shortly after calling SetOptions().)
102 if (options_ != options) {
108 void DocumentLayout::ComputeLayout(const std::vector<gfx::Size>& page_sizes) {
109 switch (options_.page_spread()) {
110 case PageSpread::kOneUp:
111 return ComputeOneUpLayout(page_sizes);
112 case PageSpread::kTwoUpOdd:
113 return ComputeTwoUpOddLayout(page_sizes);
117 void DocumentLayout::ComputeOneUpLayout(
118 const std::vector<gfx::Size>& page_sizes) {
119 gfx::Size document_size(GetWidestPageWidth(page_sizes), 0);
121 if (page_layouts_.size() != page_sizes.size()) {
122 // TODO(kmoon): May want to do less work when shrinking a layout.
123 page_layouts_.resize(page_sizes.size());
127 for (size_t i = 0; i < page_sizes.size(); ++i) {
129 // Add space for bottom separator.
130 document_size.Enlarge(0, kBottomSeparator);
133 const gfx::Size& page_size = page_sizes[i];
134 gfx::Rect page_rect =
135 draw_utils::GetRectForSingleView(page_size, document_size);
136 CopyRectIfModified(page_rect, page_layouts_[i].outer_rect);
137 CopyRectIfModified(InsetRect(page_rect, kSingleViewInsets),
138 page_layouts_[i].inner_rect);
140 draw_utils::ExpandDocumentSize(page_size, &document_size);
143 if (size_ != document_size) {
144 size_ = document_size;
149 void DocumentLayout::ComputeTwoUpOddLayout(
150 const std::vector<gfx::Size>& page_sizes) {
151 gfx::Size document_size(GetWidestPageWidth(page_sizes), 0);
153 if (page_layouts_.size() != page_sizes.size()) {
154 // TODO(kmoon): May want to do less work when shrinking a layout.
155 page_layouts_.resize(page_sizes.size());
159 for (size_t i = 0; i < page_sizes.size(); ++i) {
160 draw_utils::PageInsetSizes page_insets =
161 draw_utils::GetPageInsetsForTwoUpView(
162 i, page_sizes.size(), kSingleViewInsets, kHorizontalSeparator);
163 const gfx::Size& page_size = page_sizes[i];
167 page_rect = draw_utils::GetLeftRectForTwoUpView(
168 page_size, {document_size.width(), document_size.height()});
170 page_rect = draw_utils::GetRightRectForTwoUpView(
171 page_size, {document_size.width(), document_size.height()});
172 document_size.Enlarge(
173 0, std::max(page_size.height(), page_sizes[i - 1].height()));
175 CopyRectIfModified(page_rect, page_layouts_[i].outer_rect);
176 CopyRectIfModified(InsetRect(page_rect, page_insets),
177 page_layouts_[i].inner_rect);
180 if (page_sizes.size() % 2 == 1) {
181 document_size.Enlarge(0, page_sizes.back().height());
184 document_size.set_width(2 * document_size.width());
186 if (size_ != document_size) {
187 size_ = document_size;
192 void DocumentLayout::CopyRectIfModified(const gfx::Rect& source_rect,
193 gfx::Rect& destination_rect) {
194 if (destination_rect != source_rect) {
195 destination_rect = source_rect;
200 } // namespace chrome_pdf