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}


@ -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
|
||||
|
After ![]() (image error) Size: 338 B |
After ![]() (image error) Size: 345 B |
After ![]() (image error) Size: 272 B |
After ![]() (image error) Size: 380 B |
After ![]() (image error) Size: 394 B |
After ![]() (image error) Size: 372 B |
After ![]() (image error) Size: 441 B |
After ![]() (image error) Size: 519 B |
After ![]() (image error) Size: 538 B |
After ![]() (image error) Size: 522 B |
After ![]() (image error) Size: 435 B |
After ![]() (image error) Size: 471 B |
After ![]() (image error) Size: 372 B |
After ![]() (image error) Size: 441 B |
12
pdf/test/data/thumbnail/README.md
Normal file
@ -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.
|
88
pdf/test/data/variable_page_sizes.in
Normal file
@ -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
|
105
pdf/test/data/variable_page_sizes.pdf
Normal file
@ -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
|