0

Move page layout rectangles into DocumentLayout

This change moves primary ownership of a page's "layout rectangle" from
individual PDFiumPage objects to the DocumentLayout as a whole.

Conceptually, a page's rectangle in the layout really is a property of
the layout, and not of the PDFium-specific page data structure: A
PDFiumPage should be a client of the layout, not vice versa.

This change also allows multiple layouts to exist simultaneously: The
shared, per engine PDFiumPage-based layout rectangles currently prevent
calculating different layouts independently.

To minimize the size of this refactoring, for now the PDFiumPage retains
a secondary copy of the layout rectangle for use during painting and
coordinate transformations. This copy should only be updated when
PDFiumEngine::ApplyCurrentLayoutToPages() is called, after a layout
change.

Bug: 885110
Change-Id: I8aac622e80f0779c556b8873ab085c6106f92c6e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1746084
Commit-Queue: K Moon <kmoon@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686933}
This commit is contained in:
K Moon
2019-08-14 19:19:56 +00:00
committed by Commit Bot
parent 2db2d2878b
commit ff7ec676a7
5 changed files with 99 additions and 112 deletions

@ -44,11 +44,11 @@ DocumentLayout::DocumentLayout() = default;
DocumentLayout::~DocumentLayout() = default;
std::vector<pp::Rect> DocumentLayout::GetSingleViewLayout(
void DocumentLayout::ComputeSingleViewLayout(
const std::vector<pp::Size>& page_sizes) {
set_size({GetWidestPageWidth(page_sizes), 0});
std::vector<pp::Rect> formatted_rects(page_sizes.size());
page_rects_.resize(page_sizes.size());
for (size_t i = 0; i < page_sizes.size(); ++i) {
if (i != 0) {
// Add space for bottom separator.
@ -56,19 +56,17 @@ std::vector<pp::Rect> DocumentLayout::GetSingleViewLayout(
}
const pp::Size& page_size = page_sizes[i];
formatted_rects[i] =
page_rects_[i] =
draw_utils::GetRectForSingleView(page_size, size_, kSingleViewInsets);
AppendPageRect(page_size);
draw_utils::ExpandDocumentSize(page_size, &size_);
}
return formatted_rects;
}
std::vector<pp::Rect> DocumentLayout::GetTwoUpViewLayout(
void DocumentLayout::ComputeTwoUpViewLayout(
const std::vector<pp::Size>& page_sizes) {
set_size({GetWidestPageWidth(page_sizes), 0});
std::vector<pp::Rect> formatted_rects(page_sizes.size());
page_rects_.resize(page_sizes.size());
for (size_t i = 0; i < page_sizes.size(); ++i) {
draw_utils::PageInsetSizes page_insets =
draw_utils::GetPageInsetsForTwoUpView(
@ -76,10 +74,10 @@ std::vector<pp::Rect> DocumentLayout::GetTwoUpViewLayout(
const pp::Size& page_size = page_sizes[i];
if (i % 2 == 0) {
formatted_rects[i] = draw_utils::GetLeftRectForTwoUpView(
page_rects_[i] = draw_utils::GetLeftRectForTwoUpView(
page_size, {size_.width(), size_.height()}, page_insets);
} else {
formatted_rects[i] = draw_utils::GetRightRectForTwoUpView(
page_rects_[i] = draw_utils::GetRightRectForTwoUpView(
page_size, {size_.width(), size_.height()}, page_insets);
EnlargeHeight(std::max(page_size.height(), page_sizes[i - 1].height()));
}
@ -90,8 +88,6 @@ std::vector<pp::Rect> DocumentLayout::GetTwoUpViewLayout(
}
size_.set_width(2 * size_.width());
return formatted_rects;
}
void DocumentLayout::EnlargeHeight(int height) {
@ -99,9 +95,4 @@ void DocumentLayout::EnlargeHeight(int height) {
size_.Enlarge(0, height);
}
void DocumentLayout::AppendPageRect(const pp::Size& page_rect) {
// TODO(kmoon): Inline draw_utils::ExpandDocumentSize().
draw_utils::ExpandDocumentSize(page_rect, &size_);
}
} // namespace chrome_pdf

@ -5,8 +5,10 @@
#ifndef PDF_DOCUMENT_LAYOUT_H_
#define PDF_DOCUMENT_LAYOUT_H_
#include <cstddef>
#include <vector>
#include "base/logging.h"
#include "pdf/draw_utils/coordinates.h"
#include "pdf/page_orientation.h"
#include "ppapi/cpp/rect.h"
@ -68,33 +70,41 @@ class DocumentLayout final {
const pp::Size& size() const { return size_; }
// Sets the layout's total size.
//
// TODO(kmoon): Get rid of this method.
void set_size(const pp::Size& size) { size_ = size; }
// Given |page_sizes|, return pp::Rects that represent |page_sizes|
// formatted for single view and update the layout's size to the size of
// the new single view layout.
std::vector<pp::Rect> GetSingleViewLayout(
const std::vector<pp::Size>& page_sizes);
size_t page_count() const { return page_rects_.size(); }
// Given |page_sizes|, return pp::Rects that represent |page_sizes|
// formatted for two-up view and update the layout's size to the size of
// the new two-up view layout.
std::vector<pp::Rect> GetTwoUpViewLayout(
const std::vector<pp::Size>& page_sizes);
// Gets the layout rectangle for a page. Only valid after computing a layout.
const pp::Rect& page_rect(size_t page_index) const {
DCHECK_LT(page_index, page_count());
return page_rects_[page_index];
}
// Computes layout that represent |page_sizes| formatted for single view.
//
// TODO(kmoon): Control layout type using an option.
void ComputeSingleViewLayout(const std::vector<pp::Size>& page_sizes);
// Computes layout that represent |page_sizes| formatted for two-up view.
//
// TODO(kmoon): Control layout type using an option.
void ComputeTwoUpViewLayout(const std::vector<pp::Size>& page_sizes);
// Increases the layout's total height by |height|.
//
// TODO(kmoon): Delete or make this private.
void EnlargeHeight(int height);
// Appends a rectangle of size |page_rect| to the layout. This will increase
// the layout's height by the page's height, and increase the layout's width
// to at least the page's width.
void AppendPageRect(const pp::Size& page_rect);
private:
Options options_;
// Layout's total size.
pp::Size size_;
// Page layout rectangles.
std::vector<pp::Rect> page_rects_;
};
} // namespace chrome_pdf

@ -96,6 +96,7 @@ TEST_F(DocumentLayoutTest, DefaultConstructor) {
EXPECT_EQ(layout_.options().default_page_orientation(),
PageOrientation::kOriginal);
EXPECT_PRED2(PpSizeEq, layout_.size(), pp::Size(0, 0));
EXPECT_EQ(layout_.page_count(), 0u);
}
TEST_F(DocumentLayoutTest, SetOptionsDoesNotRecomputeLayout) {
@ -117,64 +118,59 @@ TEST_F(DocumentLayoutTest, EnlargeHeight) {
EXPECT_PRED2(PpSizeEq, layout_.size(), pp::Size(0, 16));
}
TEST_F(DocumentLayoutTest, GetSingleViewLayout) {
std::vector<pp::Rect> single_view_layout;
TEST_F(DocumentLayoutTest, ComputeSingleViewLayout) {
std::vector<pp::Size> page_sizes{
{300, 400}, {400, 500}, {300, 400}, {200, 300}};
single_view_layout = layout_.GetSingleViewLayout(page_sizes);
ASSERT_EQ(4u, single_view_layout.size());
EXPECT_PRED2(PpRectEq, pp::Rect(55, 3, 290, 390), single_view_layout[0]);
EXPECT_PRED2(PpRectEq, pp::Rect(5, 407, 390, 490), single_view_layout[1]);
EXPECT_PRED2(PpRectEq, pp::Rect(55, 911, 290, 390), single_view_layout[2]);
EXPECT_PRED2(PpRectEq, pp::Rect(105, 1315, 190, 290), single_view_layout[3]);
layout_.ComputeSingleViewLayout(page_sizes);
ASSERT_EQ(4u, layout_.page_count());
EXPECT_PRED2(PpRectEq, pp::Rect(55, 3, 290, 390), layout_.page_rect(0));
EXPECT_PRED2(PpRectEq, pp::Rect(5, 407, 390, 490), layout_.page_rect(1));
EXPECT_PRED2(PpRectEq, pp::Rect(55, 911, 290, 390), layout_.page_rect(2));
EXPECT_PRED2(PpRectEq, pp::Rect(105, 1315, 190, 290), layout_.page_rect(3));
EXPECT_PRED2(PpSizeEq, pp::Size(400, 1612), layout_.size());
page_sizes = {{240, 300}, {320, 400}, {250, 360}, {300, 600}, {270, 555}};
single_view_layout = layout_.GetSingleViewLayout(page_sizes);
ASSERT_EQ(5u, single_view_layout.size());
EXPECT_PRED2(PpRectEq, pp::Rect(45, 3, 230, 290), single_view_layout[0]);
EXPECT_PRED2(PpRectEq, pp::Rect(5, 307, 310, 390), single_view_layout[1]);
EXPECT_PRED2(PpRectEq, pp::Rect(40, 711, 240, 350), single_view_layout[2]);
EXPECT_PRED2(PpRectEq, pp::Rect(15, 1075, 290, 590), single_view_layout[3]);
EXPECT_PRED2(PpRectEq, pp::Rect(30, 1679, 260, 545), single_view_layout[4]);
layout_.ComputeSingleViewLayout(page_sizes);
ASSERT_EQ(5u, layout_.page_count());
EXPECT_PRED2(PpRectEq, pp::Rect(45, 3, 230, 290), layout_.page_rect(0));
EXPECT_PRED2(PpRectEq, pp::Rect(5, 307, 310, 390), layout_.page_rect(1));
EXPECT_PRED2(PpRectEq, pp::Rect(40, 711, 240, 350), layout_.page_rect(2));
EXPECT_PRED2(PpRectEq, pp::Rect(15, 1075, 290, 590), layout_.page_rect(3));
EXPECT_PRED2(PpRectEq, pp::Rect(30, 1679, 260, 545), layout_.page_rect(4));
EXPECT_PRED2(PpSizeEq, pp::Size(320, 2231), layout_.size());
}
TEST_F(DocumentLayoutTest, GetTwoUpViewLayout) {
std::vector<pp::Rect> two_up_view_layout;
TEST_F(DocumentLayoutTest, ComputeTwoUpViewLayout) {
// Test case where the widest page is on the right.
std::vector<pp::Size> page_sizes{
{826, 1066}, {1066, 826}, {826, 1066}, {826, 900}};
two_up_view_layout = layout_.GetTwoUpViewLayout(page_sizes);
ASSERT_EQ(4u, two_up_view_layout.size());
EXPECT_PRED2(PpRectEq, pp::Rect(245, 3, 820, 1056), two_up_view_layout[0]);
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 3, 1060, 816), two_up_view_layout[1]);
EXPECT_PRED2(PpRectEq, pp::Rect(245, 1069, 820, 1056), two_up_view_layout[2]);
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 1069, 820, 890), two_up_view_layout[3]);
layout_.ComputeTwoUpViewLayout(page_sizes);
ASSERT_EQ(4u, layout_.page_count());
EXPECT_PRED2(PpRectEq, pp::Rect(245, 3, 820, 1056), layout_.page_rect(0));
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 3, 1060, 816), layout_.page_rect(1));
EXPECT_PRED2(PpRectEq, pp::Rect(245, 1069, 820, 1056), layout_.page_rect(2));
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 1069, 820, 890), layout_.page_rect(3));
EXPECT_PRED2(PpSizeEq, pp::Size(2132, 2132), layout_.size());
// Test case where the widest page is on the left.
page_sizes = {{1066, 826}, {820, 1056}, {820, 890}, {826, 1066}};
two_up_view_layout = layout_.GetTwoUpViewLayout(page_sizes);
ASSERT_EQ(4u, two_up_view_layout.size());
EXPECT_PRED2(PpRectEq, pp::Rect(5, 3, 1060, 816), two_up_view_layout[0]);
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 3, 814, 1046), two_up_view_layout[1]);
EXPECT_PRED2(PpRectEq, pp::Rect(251, 1059, 814, 880), two_up_view_layout[2]);
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 1059, 820, 1056),
two_up_view_layout[3]);
layout_.ComputeTwoUpViewLayout(page_sizes);
ASSERT_EQ(4u, layout_.page_count());
EXPECT_PRED2(PpRectEq, pp::Rect(5, 3, 1060, 816), layout_.page_rect(0));
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 3, 814, 1046), layout_.page_rect(1));
EXPECT_PRED2(PpRectEq, pp::Rect(251, 1059, 814, 880), layout_.page_rect(2));
EXPECT_PRED2(PpRectEq, pp::Rect(1067, 1059, 820, 1056), layout_.page_rect(3));
EXPECT_PRED2(PpSizeEq, pp::Size(2132, 2122), layout_.size());
// Test case where there's an odd # of pages.
page_sizes = {{200, 300}, {400, 200}, {300, 600}, {250, 500}, {300, 400}};
two_up_view_layout = layout_.GetTwoUpViewLayout(page_sizes);
ASSERT_EQ(5u, two_up_view_layout.size());
EXPECT_PRED2(PpRectEq, pp::Rect(205, 3, 194, 290), two_up_view_layout[0]);
EXPECT_PRED2(PpRectEq, pp::Rect(401, 3, 394, 190), two_up_view_layout[1]);
EXPECT_PRED2(PpRectEq, pp::Rect(105, 303, 294, 590), two_up_view_layout[2]);
EXPECT_PRED2(PpRectEq, pp::Rect(401, 303, 244, 490), two_up_view_layout[3]);
EXPECT_PRED2(PpRectEq, pp::Rect(105, 903, 290, 390), two_up_view_layout[4]);
layout_.ComputeTwoUpViewLayout(page_sizes);
ASSERT_EQ(5u, layout_.page_count());
EXPECT_PRED2(PpRectEq, pp::Rect(205, 3, 194, 290), layout_.page_rect(0));
EXPECT_PRED2(PpRectEq, pp::Rect(401, 3, 394, 190), layout_.page_rect(1));
EXPECT_PRED2(PpRectEq, pp::Rect(105, 303, 294, 590), layout_.page_rect(2));
EXPECT_PRED2(PpRectEq, pp::Rect(401, 303, 244, 490), layout_.page_rect(3));
EXPECT_PRED2(PpRectEq, pp::Rect(105, 903, 290, 390), layout_.page_rect(4));
EXPECT_PRED2(PpSizeEq, pp::Size(800, 1300), layout_.size());
}
@ -182,17 +178,6 @@ TEST_F(DocumentLayoutDeathTest, EnlargeHeightNegativeIncrement) {
EXPECT_DCHECK_DEATH(layout_.EnlargeHeight(-5));
}
TEST_F(DocumentLayoutTest, AppendPageRect) {
layout_.AppendPageRect(pp::Size(3, 5));
EXPECT_PRED2(PpSizeEq, layout_.size(), pp::Size(3, 5));
layout_.AppendPageRect(pp::Size(7, 11));
EXPECT_PRED2(PpSizeEq, layout_.size(), pp::Size(7, 16));
layout_.AppendPageRect(pp::Size(5, 11));
EXPECT_PRED2(PpSizeEq, layout_.size(), pp::Size(7, 27));
}
} // namespace
} // namespace chrome_pdf

@ -2229,6 +2229,7 @@ bool PDFiumEngine::GetPageSizeAndUniformity(pp::Size* size) {
return true;
}
// TODO(kmoon): Rewrite this to use DocumentLayout properly.
void PDFiumEngine::AppendBlankPages(int num_pages) {
DCHECK_NE(num_pages, 0);
@ -2388,36 +2389,33 @@ void PDFiumEngine::ContinueLoadingDocument(const std::string& password) {
FinishLoadingDocument();
}
void PDFiumEngine::AppendPageRectToPages(const pp::Rect& page_rect,
size_t page_index,
bool reload) {
if (!reload) {
// The page is marked as not being available even if |doc_complete| is
// true because FPDFAvail_IsPageAvail() still has to be called for this
// page, which will be done in FinishLoadingDocument().
pages_.push_back(
std::make_unique<PDFiumPage>(this, page_index, page_rect, false));
} else if (page_index < pages_.size()) {
pages_[page_index]->set_rect(page_rect);
} else {
bool available =
FPDFAvail_IsPageAvail(fpdf_availability(), page_index, nullptr);
pages_.push_back(
std::make_unique<PDFiumPage>(this, page_index, page_rect, available));
}
}
void PDFiumEngine::LoadPagesInCurrentLayout(std::vector<pp::Size> page_sizes,
bool reload) {
std::vector<pp::Rect> formatted_pages;
if (two_up_view_) {
formatted_pages = layout_.GetTwoUpViewLayout(page_sizes);
layout_.ComputeTwoUpViewLayout(page_sizes);
} else {
formatted_pages = layout_.GetSingleViewLayout(page_sizes);
layout_.ComputeSingleViewLayout(page_sizes);
}
for (size_t i = 0; i < formatted_pages.size(); ++i) {
AppendPageRectToPages(formatted_pages[i], i, reload);
ApplyCurrentLayoutToPages(reload);
}
// TODO(kmoon): This should be the only method that sets |PDFiumPage::rect_|.
void PDFiumEngine::ApplyCurrentLayoutToPages(bool reload) {
for (size_t i = 0; i < layout_.page_count(); ++i) {
const pp::Rect& page_rect = layout_.page_rect(i);
if (!reload) {
// The page is marked as not being available even if |doc_complete| is
// true because FPDFAvail_IsPageAvail() still has to be called for this
// page, which will be done in FinishLoadingDocument().
pages_.push_back(std::make_unique<PDFiumPage>(this, i, page_rect, false));
} else if (i < pages_.size()) {
pages_[i]->set_rect(page_rect);
} else {
bool available = FPDFAvail_IsPageAvail(fpdf_availability(), i, nullptr);
pages_.push_back(
std::make_unique<PDFiumPage>(this, i, page_rect, available));
}
}
}

@ -235,16 +235,19 @@ class PDFiumEngine : public PDFEngine,
// If this has been run once, it will not notify the client again.
void FinishLoadingDocument();
// Appends a PDFiumPage with |page_rect| to |pages_| or sets existing
// PDFiumPage for |page_index| to |page_rect|. |page_index| is 0-based.
void AppendPageRectToPages(const pp::Rect& page_rect,
size_t page_index,
bool reload);
// Formats the pages of |page_sizes| and appends them to |pages_|. Formats to
// two-up view if |two_up_view_| is true, else formats to single-view.
void LoadPagesInCurrentLayout(std::vector<pp::Size> page_sizes, bool reload);
// Applies the current layout to the PDFiumPage objects. This primarily
// involves updating the PDFiumPage rectangles from the corresponding layout
// page rectangles.
//
// TODO(kmoon): Conceivably, the PDFiumPages wouldn't need to store page
// rectangles at all, and we could get rid of this step. This is a pretty
// involved change, however.
void ApplyCurrentLayoutToPages(bool reload);
// Loads information about the pages in the document and calculate the
// document size.
void LoadPageInfo(bool reload);