Commit 4222f88d authored by Daniel Hosseinian's avatar Daniel Hosseinian Committed by Commit Bot

Implement PDFiumPage::GenerateThumbnail()

Generate the thumbnail onto the SkBitmap of a Thumbnail object using
PDFium's FPDF_RenderPageBitmap().

Add a test PDF and expectation PNGs of its thumbnails at different
resolutions. Compare the rendered thumbnails to the expectation PNGs.

Bug: 652400
Change-Id: Id4c5e7f7a93822c6c38efdd63657e3e8002cd9cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2382687Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarK. Moon <kmoon@chromium.org>
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805961}
parent b732b883
......@@ -332,6 +332,7 @@ if (enable_pdf) {
":ppapi_migration",
"//base",
"//base/test:test_support",
"//cc:test_support",
"//gin",
"//ppapi/c",
"//ppapi/cpp:objects",
......
include_rules = [
"+cc/test/pixel_comparator.h",
"+cc/test/pixel_test_utils.h",
"+gin/array_buffer.h",
"+gin/public",
"+gin/v8_initializer.h",
......
......@@ -23,11 +23,13 @@
#include "pdf/pdfium/pdfium_engine.h"
#include "pdf/pdfium/pdfium_unsupported_features.h"
#include "pdf/ppapi_migration/geometry_conversions.h"
#include "pdf/thumbnail.h"
#include "ppapi/c/private/ppb_pdf.h"
#include "printing/units.h"
#include "third_party/pdfium/public/cpp/fpdf_scopers.h"
#include "third_party/pdfium/public/fpdf_annot.h"
#include "third_party/pdfium/public/fpdf_catalog.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
......@@ -1457,6 +1459,34 @@ gfx::Rect PDFiumPage::PageToScreen(const gfx::Point& page_point,
new_size_y.ValueOrDie());
}
Thumbnail PDFiumPage::GenerateThumbnail(float device_pixel_ratio) {
DCHECK(available());
FPDF_PAGE page = GetPage();
gfx::Size page_size(base::saturated_cast<int>(FPDF_GetPageWidthF(page)),
base::saturated_cast<int>(FPDF_GetPageHeightF(page)));
Thumbnail thumbnail(page_size, device_pixel_ratio);
SkBitmap& sk_bitmap = thumbnail.bitmap();
ScopedFPDFBitmap fpdf_bitmap(FPDFBitmap_CreateEx(
sk_bitmap.width(), sk_bitmap.height(), FPDFBitmap_BGRA,
sk_bitmap.getPixels(), sk_bitmap.rowBytes()));
// Clear the bitmap.
FPDFBitmap_FillRect(fpdf_bitmap.get(), /*left=*/0, /*top=*/0,
sk_bitmap.width(), sk_bitmap.height(),
/*color=*/0xFFFFFFFF);
// The combination of the |FPDF_REVERSE_BYTE_ORDER| rendering flag and the
// |FPDFBitmap_BGRA| format when initializing |fpdf_bitmap| results in an RGBA
// rendering, which is the format required by HTML <canvas>.
FPDF_RenderPageBitmap(fpdf_bitmap.get(), GetPage(), /*start_x=*/0,
/*start_y=*/0, sk_bitmap.width(), sk_bitmap.height(),
/*rotate=*/0, FPDF_ANNOT | FPDF_REVERSE_BYTE_ORDER);
return thumbnail;
}
PDFiumPage::ScopedUnloadPreventer::ScopedUnloadPreventer(PDFiumPage* page)
: page_(page) {
page_->preventing_unload_count_++;
......
......@@ -30,6 +30,7 @@ class Point;
namespace chrome_pdf {
class PDFiumEngine;
class Thumbnail;
// Wrapper around a page from the document.
class PDFiumPage {
......@@ -157,6 +158,9 @@ class PDFiumPage {
double bottom,
PageOrientation orientation) const;
// Generate a page thumbnail accommodating a specific |device_pixel_ratio|.
Thumbnail GenerateThumbnail(float device_pixel_ratio);
int index() const { return index_; }
const gfx::Rect& rect() const { return rect_; }
......
......@@ -8,16 +8,23 @@
#include <vector>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/gtest_util.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
#include "pdf/pdfium/pdfium_engine.h"
#include "pdf/pdfium/pdfium_test_base.h"
#include "pdf/test/test_client.h"
#include "pdf/test/test_utils.h"
#include "pdf/thumbnail.h"
#include "ppapi/c/private/ppb_pdf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/pdfium/public/fpdf_formfill.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/range/range.h"
......@@ -73,6 +80,23 @@ void PopulateTextObjects(const std::vector<gfx::Range>& ranges,
}
}
base::FilePath GetThumbnailTestData(const std::string& expectation_file_prefix,
size_t page_index,
float device_pixel_ratio) {
std::string file_dir = base::StringPrintf("%.1fx", device_pixel_ratio);
std::string file_name = base::StringPrintf(
"%s_expected.pdf.%zu.png", expectation_file_prefix.c_str(), page_index);
base::FilePath root_path;
if (!base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path))
return base::FilePath();
return root_path.Append(FILE_PATH_LITERAL("pdf"))
.Append(FILE_PATH_LITERAL("test"))
.Append(FILE_PATH_LITERAL("data"))
.Append(FILE_PATH_LITERAL("thumbnail"))
.AppendASCII(file_dir)
.AppendASCII(file_name);
}
} // namespace
using PDFiumPageTest = PDFiumTestBase;
......@@ -706,4 +730,64 @@ TEST_F(PDFiumPageOverlappingTest, CountCompleteOverlaps) {
ASSERT_EQ(12u, PDFiumPage::CountLinkHighlightOverlaps(links, highlights));
}
class PDFiumPageThumbnailTest : public PDFiumTestBase {
public:
PDFiumPageThumbnailTest() = default;
PDFiumPageThumbnailTest(const PDFiumPageThumbnailTest&) = delete;
PDFiumPageThumbnailTest& operator=(const PDFiumPageThumbnailTest&) = delete;
~PDFiumPageThumbnailTest() override = default;
void TestGenerateThumbnail(PDFiumEngine& engine,
size_t page_index,
float device_pixel_ratio,
const gfx::Size& expected_thumbnail_size,
const std::string& expectation_file_prefix) {
PDFiumPage& page = GetPDFiumPageForTest(engine, page_index);
Thumbnail thumbnail = page.GenerateThumbnail(device_pixel_ratio);
EXPECT_EQ(expected_thumbnail_size, gfx::Size(thumbnail.bitmap().width(),
thumbnail.bitmap().height()));
EXPECT_EQ(device_pixel_ratio, thumbnail.device_pixel_ratio());
base::FilePath expectation_png_file_path = GetThumbnailTestData(
expectation_file_prefix, page_index, device_pixel_ratio);
cc::MatchesPNGFile(thumbnail.bitmap(), expectation_png_file_path,
cc::ExactPixelComparator(/*discard_alpha=*/false));
}
};
TEST_F(PDFiumPageThumbnailTest, GenerateThumbnail) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("variable_page_sizes.pdf"));
ASSERT_EQ(7, engine->GetNumberOfPages());
static constexpr struct {
size_t page_index;
float device_pixel_ratio;
gfx::Size expected_thumbnail_size;
} kGenerateThumbnailTestParams[] = {
{0, 1, {108, 140}}, // ANSI Letter
{1, 1, {108, 152}}, // ISO 216 A4
{2, 1, {140, 140}}, // Square
{3, 1, {540, 108}}, // Wide
{4, 1, {108, 540}}, // Tall
{5, 1, {1399, 46}}, // Super wide
{6, 1, {46, 1399}}, // Super tall
{0, 2, {216, 280}}, // ANSI Letter
{1, 2, {214, 303}}, // ISO 216 A4
{2, 2, {255, 255}}, // Square
{3, 2, {571, 114}}, // Wide
{4, 2, {114, 571}}, // Tall
{5, 2, {1399, 46}}, // Super wide
{6, 2, {46, 1399}}, // Super tall
};
for (const auto& params : kGenerateThumbnailTestParams) {
TestGenerateThumbnail(*engine, params.page_index, params.device_pixel_ratio,
params.expected_thumbnail_size,
"variable_page_sizes");
}
}
} // namespace chrome_pdf
# PDF thumbnail test expectations
The PNG files in this directory are the thumbnail rendering outputs for PDFs in
`//pdf/test/data/`. They are generated from raw bitmaps using
`gfx::PNGCodec::Encode()` using the `gfx::PNGCodec::FORMAT_RGBA` format. The
PNGs are further optimized with `optipng`.
Each PNG file is named using the PDF file name and zero-based page number, and
is located in a directory corresponding to the device to pixel ratio. For
example, the file located at `2.0x/variable_page_sizes_expected.pdf.3.png` is
the thumbnail rendering of the fourth page of `variable_page_sizes.pdf` with a
device to pixel ratio of 2.0.
{{header}}
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
{{object 2 0}} <<
/Type /Pages
/Count 7
/Kids [3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R]
>>
endobj
{{object 3 0}} <<
% ANSI Letter
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Contents 10 0 R
>>
endobj
{{object 4 0}} <<
% ISO 216 A4
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 595 842]
/Contents 10 0 R
>>
endobj
{{object 5 0}} <<
% Square
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 200 200]
/Contents 10 0 R
>>
endobj
{{object 6 0}} <<
% Wide
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 1000 200]
/Contents 10 0 R
>>
endobj
{{object 7 0}} <<
% Tall
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 200 1000]
/Contents 10 0 R
>>
endobj
{{object 8 0}} <<
% Super wide
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 1500 50]
/Contents 10 0 R
>>
endobj
{{object 9 0}} <<
% Super tall
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 50 1500]
/Contents 10 0 R
>>
endobj
{{object 10 0}} <<
{{streamlen}}
>>
stream
q
0 0 1 rg
0 0 25 25 re B*
1 1 0 rg
25 0 25 25 re B*
1 0 0 rg
0 25 25 25 re B*
0 1 0 rg
25 25 25 25 re B*
Q
endstream
endobj
{{xref}}
{{trailer}}
{{startxref}}
%%EOF
This diff was suppressed by a .gitattributes entry.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment