document_layout.cc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "pdf/document_layout.h"
  5. #include <algorithm>
  6. #include "base/check_op.h"
  7. #include "base/values.h"
  8. #include "ui/gfx/geometry/point.h"
  9. #include "ui/gfx/geometry/rect.h"
  10. #include "ui/gfx/geometry/size.h"
  11. namespace chrome_pdf {
  12. namespace {
  13. constexpr char kDirection[] = "direction";
  14. constexpr char kDefaultPageOrientation[] = "defaultPageOrientation";
  15. constexpr char kTwoUpViewEnabled[] = "twoUpViewEnabled";
  16. int GetWidestPageWidth(const std::vector<gfx::Size>& page_sizes) {
  17. int widest_page_width = 0;
  18. for (const auto& page_size : page_sizes) {
  19. widest_page_width = std::max(widest_page_width, page_size.width());
  20. }
  21. return widest_page_width;
  22. }
  23. gfx::Rect InsetRect(const gfx::Rect& rect,
  24. const draw_utils::PageInsetSizes& inset_sizes) {
  25. gfx::Rect inset_rect(rect);
  26. inset_rect.Inset(gfx::Insets::TLBR(inset_sizes.top, inset_sizes.left,
  27. inset_sizes.bottom, inset_sizes.right));
  28. return inset_rect;
  29. }
  30. } // namespace
  31. const draw_utils::PageInsetSizes DocumentLayout::kSingleViewInsets{
  32. /*left=*/5, /*top=*/3, /*right=*/5, /*bottom=*/7};
  33. DocumentLayout::Options::Options() = default;
  34. DocumentLayout::Options::Options(const Options& other) = default;
  35. DocumentLayout::Options& DocumentLayout::Options::operator=(
  36. const Options& other) = default;
  37. DocumentLayout::Options::~Options() = default;
  38. base::Value::Dict DocumentLayout::Options::ToValue() const {
  39. base::Value::Dict dictionary;
  40. dictionary.Set(kDirection, direction_);
  41. dictionary.Set(kDefaultPageOrientation,
  42. static_cast<int>(default_page_orientation_));
  43. dictionary.Set(kTwoUpViewEnabled, page_spread_ == PageSpread::kTwoUpOdd);
  44. return dictionary;
  45. }
  46. void DocumentLayout::Options::FromValue(const base::Value::Dict& value) {
  47. int32_t direction = value.FindInt(kDirection).value();
  48. DCHECK_GE(direction, base::i18n::UNKNOWN_DIRECTION);
  49. DCHECK_LE(direction, base::i18n::TEXT_DIRECTION_MAX);
  50. direction_ = static_cast<base::i18n::TextDirection>(direction);
  51. int32_t default_page_orientation =
  52. value.FindInt(kDefaultPageOrientation).value();
  53. DCHECK_GE(default_page_orientation,
  54. static_cast<int32_t>(PageOrientation::kOriginal));
  55. DCHECK_LE(default_page_orientation,
  56. static_cast<int32_t>(PageOrientation::kLast));
  57. default_page_orientation_ =
  58. static_cast<PageOrientation>(default_page_orientation);
  59. page_spread_ = value.FindBool(kTwoUpViewEnabled).value()
  60. ? PageSpread::kTwoUpOdd
  61. : PageSpread::kOneUp;
  62. }
  63. void DocumentLayout::Options::RotatePagesClockwise() {
  64. default_page_orientation_ = RotateClockwise(default_page_orientation_);
  65. }
  66. void DocumentLayout::Options::RotatePagesCounterclockwise() {
  67. default_page_orientation_ = RotateCounterclockwise(default_page_orientation_);
  68. }
  69. DocumentLayout::DocumentLayout() = default;
  70. DocumentLayout::~DocumentLayout() = default;
  71. void DocumentLayout::SetOptions(const Options& options) {
  72. // To be conservative, we want to consider the layout dirty for any layout
  73. // option changes, even if the page rects don't necessarily change when
  74. // layout options change.
  75. //
  76. // We also probably don't want layout changes to actually kick in until
  77. // the next call to ComputeLayout(). (In practice, we'll call ComputeLayout()
  78. // shortly after calling SetOptions().)
  79. if (options_ != options) {
  80. dirty_ = true;
  81. }
  82. options_ = options;
  83. }
  84. void DocumentLayout::ComputeLayout(const std::vector<gfx::Size>& page_sizes) {
  85. switch (options_.page_spread()) {
  86. case PageSpread::kOneUp:
  87. return ComputeOneUpLayout(page_sizes);
  88. case PageSpread::kTwoUpOdd:
  89. return ComputeTwoUpOddLayout(page_sizes);
  90. }
  91. }
  92. void DocumentLayout::ComputeOneUpLayout(
  93. const std::vector<gfx::Size>& page_sizes) {
  94. gfx::Size document_size(GetWidestPageWidth(page_sizes), 0);
  95. if (page_layouts_.size() != page_sizes.size()) {
  96. // TODO(kmoon): May want to do less work when shrinking a layout.
  97. page_layouts_.resize(page_sizes.size());
  98. dirty_ = true;
  99. }
  100. for (size_t i = 0; i < page_sizes.size(); ++i) {
  101. if (i != 0) {
  102. // Add space for bottom separator.
  103. document_size.Enlarge(0, kBottomSeparator);
  104. }
  105. const gfx::Size& page_size = page_sizes[i];
  106. gfx::Rect page_rect =
  107. draw_utils::GetRectForSingleView(page_size, document_size);
  108. CopyRectIfModified(page_rect, page_layouts_[i].outer_rect);
  109. CopyRectIfModified(InsetRect(page_rect, kSingleViewInsets),
  110. page_layouts_[i].inner_rect);
  111. draw_utils::ExpandDocumentSize(page_size, &document_size);
  112. }
  113. if (size_ != document_size) {
  114. size_ = document_size;
  115. dirty_ = true;
  116. }
  117. }
  118. void DocumentLayout::ComputeTwoUpOddLayout(
  119. const std::vector<gfx::Size>& page_sizes) {
  120. 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());
  124. dirty_ = true;
  125. }
  126. for (size_t i = 0; i < page_sizes.size(); ++i) {
  127. draw_utils::PageInsetSizes page_insets =
  128. draw_utils::GetPageInsetsForTwoUpView(
  129. i, page_sizes.size(), kSingleViewInsets, kHorizontalSeparator);
  130. const gfx::Size& page_size = page_sizes[i];
  131. gfx::Rect page_rect;
  132. if (i % 2 == 0) {
  133. page_rect = draw_utils::GetLeftRectForTwoUpView(
  134. page_size, {document_size.width(), document_size.height()});
  135. } else {
  136. page_rect = draw_utils::GetRightRectForTwoUpView(
  137. page_size, {document_size.width(), document_size.height()});
  138. document_size.Enlarge(
  139. 0, std::max(page_size.height(), page_sizes[i - 1].height()));
  140. }
  141. CopyRectIfModified(page_rect, page_layouts_[i].outer_rect);
  142. CopyRectIfModified(InsetRect(page_rect, page_insets),
  143. page_layouts_[i].inner_rect);
  144. }
  145. if (page_sizes.size() % 2 == 1) {
  146. document_size.Enlarge(0, page_sizes.back().height());
  147. }
  148. document_size.set_width(2 * document_size.width());
  149. if (size_ != document_size) {
  150. size_ = document_size;
  151. dirty_ = true;
  152. }
  153. }
  154. void DocumentLayout::CopyRectIfModified(const gfx::Rect& source_rect,
  155. gfx::Rect& destination_rect) {
  156. if (destination_rect != source_rect) {
  157. destination_rect = source_rect;
  158. dirty_ = true;
  159. }
  160. }
  161. } // namespace chrome_pdf