0

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/+/2382687
Reviewed-by: Khushal <khushalsagar@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: K. Moon <kmoon@chromium.org>
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805961}
This commit is contained in:
Daniel Hosseinian
2020-09-10 23:12:38 +00:00
committed by Commit Bot
parent b732b8835b
commit 4222f88dd4
22 changed files with 326 additions and 0 deletions

@ -332,6 +332,7 @@ if (enable_pdf) {
":ppapi_migration",
"//base",
"//base/test:test_support",
"//cc:test_support",
"//gin",
"//ppapi/c",
"//ppapi/cpp:objects",

@ -1,4 +1,6 @@
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

Binary file not shown.

After

(image error) Size: 338 B

Binary file not shown.

After

(image error) Size: 345 B

Binary file not shown.

After

(image error) Size: 272 B

Binary file not shown.

After

(image error) Size: 380 B

Binary file not shown.

After

(image error) Size: 394 B

Binary file not shown.

After

(image error) Size: 372 B

Binary file not shown.

After

(image error) Size: 441 B

Binary file not shown.

After

(image error) Size: 519 B

Binary file not shown.

After

(image error) Size: 538 B

Binary file not shown.

After

(image error) Size: 522 B

Binary file not shown.

After

(image error) Size: 435 B

Binary file not shown.

After

(image error) Size: 471 B

Binary file not shown.

After

(image error) Size: 372 B

Binary file not shown.

After

(image error) Size: 441 B

@ -0,0 +1,12 @@
# 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.

@ -0,0 +1,88 @@
{{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

@ -0,0 +1,105 @@
%PDF-1.7
%<25><><EFBFBD><EFBFBD>
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj <<
/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
3 0 obj <<
% ANSI Letter
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Contents 10 0 R
>>
endobj
4 0 obj <<
% ISO 216 A4
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 595 842]
/Contents 10 0 R
>>
endobj
5 0 obj <<
% Square
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 200 200]
/Contents 10 0 R
>>
endobj
6 0 obj <<
% Wide
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 1000 200]
/Contents 10 0 R
>>
endobj
7 0 obj <<
% Tall
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 200 1000]
/Contents 10 0 R
>>
endobj
8 0 obj <<
% Super wide
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 1500 50]
/Contents 10 0 R
>>
endobj
9 0 obj <<
% Super tall
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 50 1500]
/Contents 10 0 R
>>
endobj
10 0 obj <<
/Length 108
>>
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
0 11
0000000000 65535 f
0000000015 00000 n
0000000068 00000 n
0000000167 00000 n
0000000279 00000 n
0000000390 00000 n
0000000497 00000 n
0000000603 00000 n
0000000709 00000 n
0000000820 00000 n
0000000931 00000 n
trailer <<
/Root 1 0 R
/Size 11
>>
startxref
1092
%%EOF