blob: 7922579efbd878ae7a10b379b4e207ee7bc82e7f [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/pdfium/pdfium_page.h"
#include <optional>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "build/build_config.h"
#include "pdf/accessibility_structs.h"
#include "pdf/pdfium/pdfium_engine.h"
#include "pdf/pdfium/pdfium_test_base.h"
#include "pdf/test/test_client.h"
#include "pdf/test/test_helpers.h"
#include "pdf/ui/thumbnail.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/pdfium/public/fpdf_formfill.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/geometry/test/geometry_util.h"
#include "ui/gfx/range/range.h"
namespace chrome_pdf {
namespace {
TEST(PDFiumPageHelperTest, ToPDFiumRotation) {
EXPECT_EQ(ToPDFiumRotation(PageOrientation::kOriginal), 0);
EXPECT_EQ(ToPDFiumRotation(PageOrientation::kClockwise90), 1);
EXPECT_EQ(ToPDFiumRotation(PageOrientation::kClockwise180), 2);
EXPECT_EQ(ToPDFiumRotation(PageOrientation::kClockwise270), 3);
}
TEST(PDFiumPageHelperTest, ScopedUnloadPreventer) {
// Should not DCHECK in its dtor due to ScopedUnloadPreventer usage.
PDFiumPage page1(/*engine=*/nullptr, 1);
PDFiumPage page2(/*engine=*/nullptr, 2);
PDFiumPage::ScopedUnloadPreventer prevent_unload1(&page1);
PDFiumPage::ScopedUnloadPreventer prevent_unload2(&page2);
PDFiumPage::ScopedUnloadPreventer prevent_unload3(prevent_unload2);
PDFiumPage::ScopedUnloadPreventer prevent_unload4(&page2);
prevent_unload2 = prevent_unload1;
prevent_unload1 = prevent_unload2;
prevent_unload1 = prevent_unload4;
prevent_unload4 = prevent_unload1;
prevent_unload3 = prevent_unload4;
}
void CompareTextRuns(const AccessibilityTextRunInfo& expected_text_run,
const AccessibilityTextRunInfo& actual_text_run) {
EXPECT_EQ(expected_text_run.len, actual_text_run.len);
EXPECT_RECTF_EQ(expected_text_run.bounds, actual_text_run.bounds);
EXPECT_EQ(expected_text_run.direction, actual_text_run.direction);
const AccessibilityTextStyleInfo& expected_style = expected_text_run.style;
const AccessibilityTextStyleInfo& actual_style = actual_text_run.style;
EXPECT_EQ(expected_style.font_name, actual_style.font_name);
EXPECT_EQ(expected_style.font_weight, actual_style.font_weight);
EXPECT_EQ(expected_style.render_mode, actual_style.render_mode);
EXPECT_FLOAT_EQ(expected_style.font_size, actual_style.font_size);
EXPECT_EQ(expected_style.fill_color, actual_style.fill_color);
EXPECT_EQ(expected_style.stroke_color, actual_style.stroke_color);
EXPECT_EQ(expected_style.is_italic, actual_style.is_italic);
EXPECT_EQ(expected_style.is_bold, actual_style.is_bold);
}
template <typename T>
void PopulateTextObjects(const std::vector<gfx::Range>& ranges,
std::vector<T>* text_objects) {
text_objects->resize(ranges.size());
for (size_t i = 0; i < ranges.size(); ++i) {
(*text_objects)[i].start_char_index = ranges[i].start();
(*text_objects)[i].char_count = ranges[i].end() - ranges[i].start();
}
}
// Returns the page size for a `PDFiumPage`. The caller must make sure that
// `pdfium_page` is available.
gfx::SizeF GetPageSizeHelper(PDFiumPage& pdfium_page) {
FPDF_PAGE page = pdfium_page.GetPage();
return gfx::SizeF(FPDF_GetPageWidthF(page), FPDF_GetPageHeightF(page));
}
base::FilePath GetThumbnailTestData(const std::string& expectation_file_prefix,
size_t page_index,
float device_pixel_ratio,
bool use_skia) {
std::string file_dir = base::StringPrintf("%.1fx", device_pixel_ratio);
std::string file_name = base::StringPrintf(
"%s_expected%s.pdf.%zu.png", expectation_file_prefix.c_str(),
use_skia ? "_skia" : "", page_index);
return base::FilePath(FILE_PATH_LITERAL("thumbnail"))
.AppendASCII(file_dir)
.AppendASCII(file_name);
}
} // namespace
using PDFiumPageTest = PDFiumTestBase;
TEST_P(PDFiumPageTest, Constructor) {
PDFiumPage page(/*engine=*/nullptr, 2);
EXPECT_EQ(page.index(), 2);
EXPECT_TRUE(page.rect().IsEmpty());
EXPECT_FALSE(page.available());
}
TEST_P(PDFiumPageTest, IsCharInPageBounds) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("hello_world_cropped.pdf"));
ASSERT_TRUE(engine);
PDFiumPage page(engine.get(), 0);
EXPECT_FALSE(page.available());
EXPECT_EQ(page.GetCharCount(), 0);
page.MarkAvailable();
EXPECT_TRUE(page.available());
EXPECT_EQ(page.GetCharCount(), 30);
const gfx::RectF page_bounds = page.GetCroppedRect();
EXPECT_EQ(page_bounds, gfx::RectF(193.33333f, 129.33333f));
EXPECT_EQ(page.GetCharAtIndex(0), 'H');
EXPECT_FALSE(page.IsCharInPageBounds(0, page_bounds));
EXPECT_EQ(page.GetCharAtIndex(12), '!');
EXPECT_TRUE(page.IsCharInPageBounds(12, page_bounds));
EXPECT_EQ(page.GetCharAtIndex(13), '\r');
EXPECT_TRUE(page.IsCharInPageBounds(13, page_bounds));
EXPECT_EQ(page.GetCharAtIndex(14), '\n');
EXPECT_TRUE(page.IsCharInPageBounds(14, page_bounds));
EXPECT_EQ(page.GetCharAtIndex(15), 'G');
EXPECT_FALSE(page.IsCharInPageBounds(15, page_bounds));
EXPECT_EQ(page.GetCharAtIndex(29), '!');
EXPECT_FALSE(page.IsCharInPageBounds(29, page_bounds));
}
TEST_P(PDFiumPageTest, GetBoundingBoxRotatedMultipage) {
// Check getting bounding box for multiple rotated pages.
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("rotated_multi_page.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(4, engine->GetNumberOfPages());
// Rotation 0 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(0.0f, bounding_box.x());
EXPECT_FLOAT_EQ(266.66669f, bounding_box.y());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.width());
EXPECT_FLOAT_EQ(400.0f, bounding_box.height());
}
// Rotation 90 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 1);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(266.66669f, bounding_box.x());
EXPECT_FLOAT_EQ(666.66669f, bounding_box.y());
EXPECT_FLOAT_EQ(400.0f, bounding_box.width());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.height());
}
// Rotation 180 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 2);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(666.66669f, bounding_box.x());
EXPECT_FLOAT_EQ(933.33337f, bounding_box.y());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.width());
EXPECT_FLOAT_EQ(400.0f, bounding_box.height());
}
// Rotation 270 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 3);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(933.33337f, bounding_box.x());
EXPECT_FLOAT_EQ(0.0f, bounding_box.y());
EXPECT_FLOAT_EQ(400.0f, bounding_box.width());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.height());
}
}
TEST_P(PDFiumPageTest, GetBoundingBoxAnnotations) {
// Check getting the bounding box for annotations.
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(92.0f, bounding_box.x());
EXPECT_FLOAT_EQ(450.66669, bounding_box.y());
EXPECT_FLOAT_EQ(201.33334f, bounding_box.width());
EXPECT_FLOAT_EQ(469.33334f, bounding_box.height());
}
TEST_P(PDFiumPageTest, GetBoundingBoxBlankPage) {
// Check getting the bounding box for a blank page. The bounding box should be
// the crop box scaled to page pixels.
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("blank.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
// The crop box is 200x200 in points.
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(0.0f, bounding_box.x());
EXPECT_FLOAT_EQ(0.0f, bounding_box.y());
EXPECT_FLOAT_EQ(266.66669f, bounding_box.width());
EXPECT_FLOAT_EQ(266.66669f, bounding_box.height());
}
TEST_P(PDFiumPageTest, GetBoundingBoxCropped) {
// Check getting the bounding box for a page with a crop box different than
// the media box.
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("landscape_rectangles.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(0.0f, bounding_box.x());
EXPECT_FLOAT_EQ(0.0f, bounding_box.y());
EXPECT_FLOAT_EQ(800.0f, bounding_box.width());
EXPECT_FLOAT_EQ(533.33337f, bounding_box.height());
}
TEST_P(PDFiumPageTest, GetBoundingBoxRotatedMultipageCropped) {
// Check getting the bounding box for a multiple rotated pages with a crop
// box.
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("rotated_multi_page_cropped.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(4, engine->GetNumberOfPages());
// Rotation 0 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(0.0f, bounding_box.x());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.y());
EXPECT_FLOAT_EQ(66.666672f, bounding_box.width());
EXPECT_FLOAT_EQ(400.0f, bounding_box.height());
}
// Rotation 90 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 1);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(133.33334f, bounding_box.x());
EXPECT_FLOAT_EQ(400.0f, bounding_box.y());
EXPECT_FLOAT_EQ(400.0f, bounding_box.width());
EXPECT_FLOAT_EQ(66.666672f, bounding_box.height());
}
// Rotation 180 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 2);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(400.0f, bounding_box.x());
EXPECT_FLOAT_EQ(133.33334f, bounding_box.y());
EXPECT_FLOAT_EQ(66.666672f, bounding_box.width());
EXPECT_FLOAT_EQ(400.0f, bounding_box.height());
}
// Rotation 270 degrees clockwise.
{
PDFiumPage& page = GetPDFiumPageForTest(*engine, 3);
const gfx::RectF bounding_box = page.GetBoundingBox();
EXPECT_FLOAT_EQ(133.33334f, bounding_box.x());
EXPECT_FLOAT_EQ(0.0f, bounding_box.y());
EXPECT_FLOAT_EQ(400.0f, bounding_box.width());
EXPECT_FLOAT_EQ(66.666672f, bounding_box.height());
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageTest, testing::Bool());
class PDFiumPageLinkTest : public PDFiumTestBase {
public:
PDFiumPageLinkTest() = default;
PDFiumPageLinkTest(const PDFiumPageLinkTest&) = delete;
PDFiumPageLinkTest& operator=(const PDFiumPageLinkTest&) = delete;
~PDFiumPageLinkTest() override = default;
const std::vector<PDFiumPage::Link>& GetLinks(PDFiumEngine& engine,
int page_index) {
PDFiumPage& page = GetPDFiumPageForTest(engine, page_index);
page.CalculateLinks();
return page.links_;
}
};
TEST_P(PDFiumPageLinkTest, LinkGeneration) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("weblinks.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
bool using_test_fonts = UsingTestFonts();
const std::vector<PDFiumPage::Link>& links = GetLinks(*engine, 0);
ASSERT_EQ(3u, links.size());
const PDFiumPage::Link& link = links[0];
EXPECT_EQ("http://yahoo.com", link.target.url);
EXPECT_EQ(7, link.start_char_index);
EXPECT_EQ(16, link.char_count);
ASSERT_EQ(1u, link.bounding_rects.size());
if (using_test_fonts) {
EXPECT_EQ(gfx::Rect(75, 192, 110, 15), link.bounding_rects[0]);
} else {
EXPECT_EQ(gfx::Rect(75, 191, 110, 16), link.bounding_rects[0]);
}
const PDFiumPage::Link& second_link = links[1];
EXPECT_EQ("http://bing.com", second_link.target.url);
EXPECT_EQ(52, second_link.start_char_index);
EXPECT_EQ(15, second_link.char_count);
ASSERT_EQ(1u, second_link.bounding_rects.size());
if (using_test_fonts) {
EXPECT_EQ(gfx::Rect(131, 120, 138, 22), second_link.bounding_rects[0]);
} else {
EXPECT_EQ(gfx::Rect(131, 121, 138, 20), second_link.bounding_rects[0]);
}
const PDFiumPage::Link& third_link = links[2];
EXPECT_EQ("http://google.com", third_link.target.url);
EXPECT_EQ(92, third_link.start_char_index);
EXPECT_EQ(17, third_link.char_count);
ASSERT_EQ(1u, third_link.bounding_rects.size());
EXPECT_EQ(gfx::Rect(82, 67, 161, 21), third_link.bounding_rects[0]);
}
TEST_P(PDFiumPageLinkTest, AnnotLinkGeneration) {
struct ExpectedLink {
int32_t start_char_index;
int32_t char_count;
std::vector<gfx::Rect> bounding_rects;
std::string url;
int page;
float y_in_pixels;
};
static ExpectedLink expected_links[] = {
{144, 38, {{99, 436, 236, 13}}, "https://pdfium.googlesource.com/pdfium"},
{27, 38, {{112, 215, 617, 28}}, "", 1, 89.333336},
{65, 27, {{93, 334, 174, 21}}, "https://www.adobe.com"},
{253,
18,
{{242, 455, 1, 18}, {242, 472, 1, 15}},
"https://cs.chromium.org"},
{-1, 0, {{58, 926, 28, 27}}, "https://www.google.com"}};
if (UsingTestFonts()) {
expected_links[0].bounding_rects[0] = {99, 436, 236, 14};
}
static constexpr size_t kExpectedLinkCount = std::size(expected_links);
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("link_annots.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(2, engine->GetNumberOfPages());
const std::vector<PDFiumPage::Link>& links = GetLinks(*engine, 0);
ASSERT_EQ(kExpectedLinkCount, links.size());
for (size_t i = 0; i < kExpectedLinkCount; ++i) {
const PDFiumPage::Link& actual_current_link = links[i];
const ExpectedLink& expected_current_link = expected_links[i];
EXPECT_EQ(expected_current_link.start_char_index,
actual_current_link.start_char_index);
EXPECT_EQ(expected_current_link.char_count, actual_current_link.char_count);
size_t bounds_size = actual_current_link.bounding_rects.size();
ASSERT_EQ(expected_current_link.bounding_rects.size(), bounds_size);
for (size_t bounds_index = 0; bounds_index < bounds_size; ++bounds_index) {
EXPECT_EQ(expected_current_link.bounding_rects[bounds_index],
actual_current_link.bounding_rects[bounds_index]);
}
EXPECT_EQ(expected_current_link.url, actual_current_link.target.url);
if (actual_current_link.target.url.empty()) {
EXPECT_EQ(expected_current_link.page, actual_current_link.target.page);
ASSERT_TRUE(actual_current_link.target.y_in_pixels.has_value());
EXPECT_FLOAT_EQ(expected_current_link.y_in_pixels,
actual_current_link.target.y_in_pixels.value());
}
}
}
TEST_P(PDFiumPageLinkTest, GetLinkTarget) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("in_doc_link_with_various_page_sizes.pdf"));
ASSERT_EQ(3, engine->GetNumberOfPages());
const std::vector<PDFiumPage::Link>& links = GetLinks(*engine, 0);
ASSERT_EQ(1u, links.size());
// Get the destination link that exists in the first page.
PDFiumPage& first_page = GetPDFiumPageForTest(*engine, 0);
FPDF_LINK link = FPDFLink_GetLinkAtPoint(first_page.GetPage(), 70, 740);
ASSERT_TRUE(link);
FPDF_DEST dest_link = FPDFLink_GetDest(engine->doc(), link);
ASSERT_TRUE(dest_link);
PDFiumPage::LinkTarget target;
PDFiumPage::Area area = first_page.GetLinkTarget(link, &target);
EXPECT_EQ(PDFiumPage::Area::DOCLINK_AREA, area);
EXPECT_TRUE(target.url.empty());
ASSERT_EQ(1, target.page);
// Make sure the target page's size is different from the first page's. This
// guarantees that the in-screen coordinates are calculated based on the
// target page's dimension.
PDFiumPage& target_page = GetPDFiumPageForTest(*engine, target.page);
ASSERT_TRUE(target_page.available());
ASSERT_TRUE(first_page.available());
EXPECT_NE(GetPageSizeHelper(first_page), GetPageSizeHelper(target_page));
ASSERT_TRUE(target.x_in_pixels.has_value());
ASSERT_TRUE(target.y_in_pixels.has_value());
EXPECT_FLOAT_EQ(74.666664f, target.x_in_pixels.value());
EXPECT_FLOAT_EQ(120.f, target.y_in_pixels.value());
EXPECT_FALSE(target.zoom.has_value());
}
// Regression test for crbug.com/1396248
TEST_P(PDFiumPageLinkTest, GetUTF8LinkTarget) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("uri_action_utf8.pdf"));
ASSERT_EQ(1, engine->GetNumberOfPages());
const std::vector<PDFiumPage::Link>& links = GetLinks(*engine, 0);
ASSERT_EQ(1u, links.size());
// Get the only link in the document.
PDFiumPage& first_page = GetPDFiumPageForTest(*engine, 0);
FPDF_LINK link = FPDFLink_GetLinkAtPoint(first_page.GetPage(), 100, 100);
ASSERT_TRUE(link);
FPDF_DEST dest_link = FPDFLink_GetDest(engine->doc(), link);
EXPECT_FALSE(dest_link);
PDFiumPage::LinkTarget target;
PDFiumPage::Area area = first_page.GetLinkTarget(link, &target);
EXPECT_EQ(PDFiumPage::Area::WEBLINK_AREA, area);
EXPECT_EQ("https://site.test/hello_你好.html", target.url);
EXPECT_EQ(-1, target.page);
EXPECT_FALSE(target.x_in_pixels.has_value());
EXPECT_FALSE(target.y_in_pixels.has_value());
EXPECT_FALSE(target.zoom.has_value());
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageLinkTest, testing::Bool());
using PDFiumPageImageTest = PDFiumTestBase;
TEST_P(PDFiumPageImageTest, CalculateImages) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("image_alt_text.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.CalculateImages();
ASSERT_EQ(3u, page.images_.size());
EXPECT_EQ(gfx::Rect(380, 78, 67, 68), page.images_[0].bounding_rect);
EXPECT_EQ("Image 1", page.images_[0].alt_text);
EXPECT_EQ(gfx::Rect(380, 385, 27, 28), page.images_[1].bounding_rect);
EXPECT_EQ("Image 2", page.images_[1].alt_text);
EXPECT_EQ(gfx::Rect(380, 678, 1, 1), page.images_[2].bounding_rect);
EXPECT_EQ("Image 3", page.images_[2].alt_text);
}
TEST_P(PDFiumPageImageTest, ImageAltText) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("text_with_image.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.CalculateImages();
ASSERT_EQ(3u, page.images_.size());
EXPECT_EQ(gfx::Rect(380, 78, 67, 68), page.images_[0].bounding_rect);
EXPECT_EQ("Image 1", page.images_[0].alt_text);
EXPECT_EQ(gfx::Rect(380, 385, 27, 28), page.images_[1].bounding_rect);
EXPECT_EQ("", page.images_[1].alt_text);
EXPECT_EQ(gfx::Rect(380, 678, 1, 1), page.images_[2].bounding_rect);
EXPECT_EQ("", page.images_[2].alt_text);
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageImageTest, testing::Bool());
class PDFiumPageImageDataTest : public PDFiumPageImageTest {
public:
PDFiumPageImageDataTest() : enable_pdf_ocr_({features::kPdfOcr}) {}
PDFiumPageImageDataTest(const PDFiumPageImageDataTest&) = delete;
PDFiumPageImageDataTest& operator=(const PDFiumPageImageDataTest&) = delete;
~PDFiumPageImageDataTest() override = default;
void SetUp() override {
PDFiumPageImageTest::SetUp();
base::DiscardableMemoryAllocator::SetInstance(
&discardable_memory_allocator_);
}
void TearDown() override {
base::DiscardableMemoryAllocator::SetInstance(nullptr);
PDFiumPageImageTest::TearDown();
}
private:
base::test::ScopedFeatureList enable_pdf_ocr_;
base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
};
TEST_P(PDFiumPageImageDataTest, ImageData) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("text_with_image.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.CalculateImages();
ASSERT_EQ(3u, page.images_.size());
ASSERT_FALSE(page.images_[0].alt_text.empty());
SkBitmap image_bitmap = engine->GetImageForOcr(
/*page_index=*/0, page.images_[0].page_object_index);
EXPECT_FALSE(image_bitmap.drawsNothing());
EXPECT_EQ(image_bitmap.width(), 50);
EXPECT_EQ(image_bitmap.height(), 50);
ASSERT_TRUE(page.images_[1].alt_text.empty());
image_bitmap = engine->GetImageForOcr(/*page_index=*/0,
page.images_[1].page_object_index);
EXPECT_FALSE(image_bitmap.drawsNothing());
// While the scaled image size is 20x20, `image_data` has the same size as
// the image in the PDF file, which is 50x50, and is not scaled.
EXPECT_EQ(image_bitmap.width(), 50);
EXPECT_EQ(image_bitmap.height(), 50);
}
TEST_P(PDFiumPageImageDataTest, RotatedPageImageData) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("rotated_page.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.CalculateImages();
ASSERT_EQ(1u, page.images_.size());
// This page is rotated, therefore the extracted image size is 25x100 while
// the stored image is 100x25.
SkBitmap image_bitmap = engine->GetImageForOcr(
/*page_index=*/0, page.images_[0].page_object_index);
EXPECT_EQ(image_bitmap.width(), 25);
EXPECT_EQ(image_bitmap.height(), 100);
}
TEST_P(PDFiumPageImageDataTest, ImageDataForNonImage) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("text_with_image.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.CalculateImages();
ASSERT_EQ(3u, page.images_.size());
ASSERT_EQ(1, page.images_[0].page_object_index);
// Existing non-image object.
SkBitmap image_bitmap = engine->GetImageForOcr(
/*page_index=*/0, /*image_index=*/0);
EXPECT_TRUE(image_bitmap.drawsNothing());
EXPECT_EQ(image_bitmap.width(), 0);
EXPECT_EQ(image_bitmap.height(), 0);
// Out of range.
image_bitmap = engine->GetImageForOcr(
/*page_index=*/0, /*image_index=*/1000);
EXPECT_TRUE(image_bitmap.drawsNothing());
EXPECT_EQ(image_bitmap.width(), 0);
EXPECT_EQ(image_bitmap.height(), 0);
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageImageDataTest, testing::Bool());
using PDFiumPageTextTest = PDFiumTestBase;
TEST_P(PDFiumPageTextTest, TextRunBounds) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("leading_trailing_spaces_per_text_run.pdf"));
ASSERT_TRUE(engine);
constexpr int kFirstRunStartIndex = 0;
constexpr int kFirstRunEndIndex = 20;
constexpr int kPageIndex = 0;
std::optional<AccessibilityTextRunInfo> text_run_info_1 =
engine->GetTextRunInfo(kPageIndex, kFirstRunStartIndex);
ASSERT_TRUE(text_run_info_1.has_value());
const auto& actual_text_run_1 = text_run_info_1.value();
EXPECT_EQ(21u, actual_text_run_1.len);
EXPECT_TRUE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kFirstRunStartIndex)));
gfx::RectF text_run_bounds = actual_text_run_1.bounds;
EXPECT_TRUE(text_run_bounds.Contains(
engine->GetCharBounds(kPageIndex, kFirstRunStartIndex)));
// Last non-space character should fall in the bounding box of the text run.
// Text run looks like this:
// " Hello, world! \r\n "<17 characters><first Tj>
// " \r\n "<4 characters><second Tj>
// " "<1 character><third Tj starting spaces>
// Finally generated text run: " Hello, world! \r\n \r\n "
constexpr int kFirstRunLastNonSpaceCharIndex = 13;
EXPECT_FALSE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kFirstRunLastNonSpaceCharIndex)));
EXPECT_TRUE(text_run_bounds.Contains(
engine->GetCharBounds(kPageIndex, kFirstRunLastNonSpaceCharIndex)));
EXPECT_TRUE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kFirstRunEndIndex)));
gfx::RectF end_char_rect =
engine->GetCharBounds(kPageIndex, kFirstRunEndIndex);
EXPECT_FALSE(text_run_bounds.Contains(end_char_rect));
// Equals to the length of the previous text run.
constexpr int kSecondRunStartIndex = 21;
constexpr int kSecondRunEndIndex = 36;
// Test the properties of second text run.
// Note: The leading spaces in second text run are accounted for in the end
// of first text run. Hence we won't see a space leading the second text run.
std::optional<AccessibilityTextRunInfo> text_run_info_2 =
engine->GetTextRunInfo(kPageIndex, kSecondRunStartIndex);
ASSERT_TRUE(text_run_info_2.has_value());
const auto& actual_text_run_2 = text_run_info_2.value();
EXPECT_EQ(16u, actual_text_run_2.len);
EXPECT_FALSE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kSecondRunStartIndex)));
text_run_bounds = actual_text_run_2.bounds;
EXPECT_TRUE(text_run_bounds.Contains(
engine->GetCharBounds(kPageIndex, kSecondRunStartIndex)));
// Last non-space character should fall in the bounding box of the text run.
// Text run looks like this:
// "Goodbye, world! "<19 characters><first Tj>
// Finally generated text run: "Goodbye, world! "
constexpr int kSecondRunLastNonSpaceCharIndex = 35;
EXPECT_FALSE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kSecondRunLastNonSpaceCharIndex)));
EXPECT_TRUE(text_run_bounds.Contains(
engine->GetCharBounds(kPageIndex, kSecondRunLastNonSpaceCharIndex)));
EXPECT_TRUE(base::IsUnicodeWhitespace(
engine->GetCharUnicode(kPageIndex, kSecondRunEndIndex)));
EXPECT_FALSE(text_run_bounds.Contains(
engine->GetCharBounds(kPageIndex, kSecondRunEndIndex)));
}
TEST_P(PDFiumPageTextTest, GetTextRunInfo) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("weblinks.pdf"));
ASSERT_TRUE(engine);
int current_char_index = 0;
AccessibilityTextStyleInfo expected_style_1 = {
"Times-Roman",
0,
AccessibilityTextRenderMode::kFill,
12,
0xff000000,
0xff000000,
false,
false};
AccessibilityTextStyleInfo expected_style_2 = {
"Helvetica", 0, AccessibilityTextRenderMode::kFill,
16, 0xff000000, 0xff000000,
false, false};
// The links span from [7, 22], [52, 66] and [92, 108] with 16, 15 and 17
// text run lengths respectively. There are text runs preceding and
// succeeding them.
AccessibilityTextRunInfo expected_text_runs[] = {
{7, gfx::RectF(26.666666f, 189.333333f, 38.666672f, 13.333344f),
AccessibilityTextDirection::kLeftToRight, expected_style_1},
{16, gfx::RectF(70.666664f, 189.333333f, 108.0f, 14.666672f),
AccessibilityTextDirection::kLeftToRight, expected_style_1},
{20, gfx::RectF(181.333333f, 189.333333f, 117.333333f, 14.666672f),
AccessibilityTextDirection::kLeftToRight, expected_style_1},
{9, gfx::RectF(28.0f, 117.33334f, 89.333328f, 20.0f),
AccessibilityTextDirection::kLeftToRight, expected_style_2},
{15, gfx::RectF(126.66666f, 117.33334f, 137.33334f, 20.0f),
AccessibilityTextDirection::kLeftToRight, expected_style_2},
{20, gfx::RectF(266.66666f, 118.66666f, 169.33334f, 18.666664f),
AccessibilityTextDirection::kLeftToRight, expected_style_2},
{5, gfx::RectF(28.0f, 65.333336f, 40.0f, 18.666664f),
AccessibilityTextDirection::kLeftToRight, expected_style_2},
{17, gfx::RectF(77.333336f, 64.0f, 160.0f, 20.0f),
AccessibilityTextDirection::kLeftToRight, expected_style_2}};
if (UsingTestFonts()) {
expected_text_runs[4].bounds =
gfx::RectF(126.66666f, 117.33334f, 137.33334f, 21.33334f);
expected_text_runs[5].bounds =
gfx::RectF(266.66666f, 118.66666f, 170.66666f, 20.0f);
expected_text_runs[7].bounds =
gfx::RectF(77.333336f, 64.0f, 160.0f, 21.33333f);
}
// Test negative char index returns nullopt
std::optional<AccessibilityTextRunInfo> text_run_info_result =
engine->GetTextRunInfo(0, -1);
ASSERT_FALSE(text_run_info_result.has_value());
// Test valid char index returns expected text run info and expected text
// style info
for (const auto& expected_text_run : expected_text_runs) {
text_run_info_result = engine->GetTextRunInfo(0, current_char_index);
ASSERT_TRUE(text_run_info_result.has_value());
const auto& actual_text_run = text_run_info_result.value();
CompareTextRuns(expected_text_run, actual_text_run);
current_char_index += actual_text_run.len;
}
// Test char index outside char range returns nullopt
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
EXPECT_EQ(page.GetCharCount(), current_char_index);
text_run_info_result = engine->GetTextRunInfo(0, current_char_index);
ASSERT_FALSE(text_run_info_result.has_value());
}
TEST_P(PDFiumPageTextTest, HighlightTextRunInfo) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("highlights.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
// Highlights span across text run indices 0, 2 and 3.
static const AccessibilityTextStyleInfo kExpectedStyle = {
"Helvetica", 0, AccessibilityTextRenderMode::kFill,
16, 0xff000000, 0xff000000,
false, false};
AccessibilityTextRunInfo expected_text_runs[] = {
{5, gfx::RectF(1.3333334f, 198.66667f, 46.666668f, 14.666672f),
AccessibilityTextDirection::kLeftToRight, kExpectedStyle},
{7, gfx::RectF(50.666668f, 198.66667f, 47.999996f, 17.333328f),
AccessibilityTextDirection::kLeftToRight, kExpectedStyle},
{7, gfx::RectF(106.66666f, 198.66667f, 73.333336f, 18.666672f),
AccessibilityTextDirection::kLeftToRight, kExpectedStyle},
{2, gfx::RectF(181.33333f, 202.66667f, 16.0f, 14.66667f),
AccessibilityTextDirection::kNone, kExpectedStyle},
{2, gfx::RectF(198.66667f, 202.66667f, 21.333328f, 10.666672f),
AccessibilityTextDirection::kLeftToRight, kExpectedStyle}};
if (UsingTestFonts()) {
expected_text_runs[2].bounds =
gfx::RectF(106.66666f, 198.66667f, 73.333336f, 19.999985f);
expected_text_runs[4].bounds =
gfx::RectF(198.66667f, 201.33333f, 21.333328f, 12.000015f);
}
int current_char_index = 0;
for (const auto& expected_text_run : expected_text_runs) {
std::optional<AccessibilityTextRunInfo> text_run_info_result =
engine->GetTextRunInfo(0, current_char_index);
ASSERT_TRUE(text_run_info_result.has_value());
const auto& actual_text_run = text_run_info_result.value();
CompareTextRuns(expected_text_run, actual_text_run);
current_char_index += actual_text_run.len;
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageTextTest, testing::Bool());
using PDFiumPageHighlightTest = PDFiumTestBase;
TEST_P(PDFiumPageHighlightTest, PopulateHighlights) {
struct ExpectedHighlight {
int32_t start_char_index;
int32_t char_count;
gfx::Rect bounding_rect;
uint32_t color;
};
constexpr uint32_t kHighlightDefaultColor = MakeARGB(255, 255, 255, 0);
constexpr uint32_t kHighlightRedColor = MakeARGB(102, 230, 0, 0);
constexpr uint32_t kHighlightNoColor = MakeARGB(0, 0, 0, 0);
static const ExpectedHighlight kExpectedHighlights[] = {
{0, 5, {5, 196, 49, 26}, kHighlightDefaultColor},
{12, 7, {110, 196, 77, 26}, kHighlightRedColor},
{20, 1, {192, 196, 13, 26}, kHighlightNoColor}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("highlights.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.PopulateAnnotations();
ASSERT_EQ(std::size(kExpectedHighlights), page.highlights_.size());
for (size_t i = 0; i < page.highlights_.size(); ++i) {
ASSERT_EQ(kExpectedHighlights[i].start_char_index,
page.highlights_[i].start_char_index);
ASSERT_EQ(kExpectedHighlights[i].char_count,
page.highlights_[i].char_count);
EXPECT_EQ(kExpectedHighlights[i].bounding_rect,
page.highlights_[i].bounding_rect);
ASSERT_EQ(kExpectedHighlights[i].color, page.highlights_[i].color);
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageHighlightTest, testing::Bool());
using PDFiumPageTextFieldTest = PDFiumTestBase;
TEST_P(PDFiumPageTextFieldTest, PopulateTextFields) {
struct ExpectedTextField {
const char* name;
const char* value;
gfx::Rect bounding_rect;
int flags;
};
static const ExpectedTextField kExpectedTextFields[] = {
{"Text Box", "Text", {138, 230, 135, 41}, 0},
{"ReadOnly", "Elephant", {138, 163, 135, 41}, 1},
{"Required", "Required Field", {138, 303, 135, 34}, 2},
{"Password", "", {138, 356, 135, 35}, 8192}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("form_text_fields.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.PopulateAnnotations();
size_t text_fields_count = page.text_fields_.size();
ASSERT_EQ(std::size(kExpectedTextFields), text_fields_count);
for (size_t i = 0; i < text_fields_count; ++i) {
EXPECT_EQ(kExpectedTextFields[i].name, page.text_fields_[i].name);
EXPECT_EQ(kExpectedTextFields[i].value, page.text_fields_[i].value);
EXPECT_EQ(kExpectedTextFields[i].bounding_rect,
page.text_fields_[i].bounding_rect);
EXPECT_EQ(kExpectedTextFields[i].flags, page.text_fields_[i].flags);
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageTextFieldTest, testing::Bool());
using PDFiumPageChoiceFieldTest = PDFiumTestBase;
TEST_P(PDFiumPageChoiceFieldTest, PopulateChoiceFields) {
struct ExpectedChoiceFieldOption {
const char* name;
bool is_selected;
};
struct ExpectedChoiceField {
const char* name;
std::vector<struct ExpectedChoiceFieldOption> options;
gfx::Rect bounding_rect;
int flags;
};
static const ExpectedChoiceField kExpectedChoiceFields[] = {
{"Listbox_SingleSelect",
{{"Foo", false}, {"Bar", false}, {"Qux", false}},
{138, 296, 135, 41},
0},
{"Combo1",
{{"Apple", false}, {"Banana", true}, {"Cherry", false}},
{138, 230, 135, 41},
131072},
{"Listbox_ReadOnly",
{{"Dog", false}, {"Elephant", false}, {"Frog", false}},
{138, 96, 135, 41},
1},
{"Listbox_MultiSelectMultipleIndices",
{
{"Albania", false},
{"Belgium", true},
{"Croatia", false},
{"Denmark", true},
{"Estonia", false},
},
{138, 430, 135, 41},
2097152},
{"Listbox_MultiSelectMultipleValues",
{
{"Alpha", false},
{"Beta", false},
{"Gamma", true},
{"Delta", false},
{"Epsilon", true},
},
{138, 496, 135, 41},
2097152},
{"Listbox_MultiSelectMultipleMismatch",
{
{"Alligator", true},
{"Bear", false},
{"Cougar", true},
{"Deer", false},
{"Echidna", false},
},
{138, 563, 135, 41},
2097152}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("form_choice_fields.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.PopulateAnnotations();
size_t choice_fields_count = page.choice_fields_.size();
ASSERT_EQ(std::size(kExpectedChoiceFields), choice_fields_count);
for (size_t i = 0; i < choice_fields_count; ++i) {
EXPECT_EQ(kExpectedChoiceFields[i].name, page.choice_fields_[i].name);
size_t choice_field_options_count = page.choice_fields_[i].options.size();
ASSERT_EQ(std::size(kExpectedChoiceFields[i].options),
choice_field_options_count);
for (size_t j = 0; j < choice_field_options_count; ++j) {
EXPECT_EQ(kExpectedChoiceFields[i].options[j].name,
page.choice_fields_[i].options[j].name);
EXPECT_EQ(kExpectedChoiceFields[i].options[j].is_selected,
page.choice_fields_[i].options[j].is_selected);
}
EXPECT_EQ(kExpectedChoiceFields[i].bounding_rect,
page.choice_fields_[i].bounding_rect);
EXPECT_EQ(kExpectedChoiceFields[i].flags, page.choice_fields_[i].flags);
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageChoiceFieldTest, testing::Bool());
using PDFiumPageButtonTest = PDFiumTestBase;
TEST_P(PDFiumPageButtonTest, PopulateButtons) {
struct ExpectedButton {
const char* name;
const char* value;
int type;
int flags;
bool is_checked;
uint32_t control_count;
int control_index;
gfx::Rect bounding_rect;
};
static const ExpectedButton kExpectedButtons[] = {{"readOnlyCheckbox",
"Yes",
FPDF_FORMFIELD_CHECKBOX,
1,
true,
1,
0,
{185, 43, 28, 28}},
{"checkbox",
"Yes",
FPDF_FORMFIELD_CHECKBOX,
2,
false,
1,
0,
{185, 96, 28, 28}},
{"RadioButton",
"value1",
FPDF_FORMFIELD_RADIOBUTTON,
49154,
false,
2,
0,
{185, 243, 28, 28}},
{"RadioButton",
"value2",
FPDF_FORMFIELD_RADIOBUTTON,
49154,
true,
2,
1,
{252, 243, 27, 28}},
{"PushButton",
"",
FPDF_FORMFIELD_PUSHBUTTON,
65536,
false,
0,
-1,
{118, 270, 55, 67}}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("form_buttons.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage& page = GetPDFiumPageForTest(*engine, 0);
page.PopulateAnnotations();
size_t buttons_count = page.buttons_.size();
ASSERT_EQ(std::size(kExpectedButtons), buttons_count);
for (size_t i = 0; i < buttons_count; ++i) {
EXPECT_EQ(kExpectedButtons[i].name, page.buttons_[i].name);
EXPECT_EQ(kExpectedButtons[i].value, page.buttons_[i].value);
EXPECT_EQ(kExpectedButtons[i].type, page.buttons_[i].type);
EXPECT_EQ(kExpectedButtons[i].flags, page.buttons_[i].flags);
EXPECT_EQ(kExpectedButtons[i].is_checked, page.buttons_[i].is_checked);
EXPECT_EQ(kExpectedButtons[i].control_count,
page.buttons_[i].control_count);
EXPECT_EQ(kExpectedButtons[i].control_index,
page.buttons_[i].control_index);
EXPECT_EQ(kExpectedButtons[i].bounding_rect,
page.buttons_[i].bounding_rect);
}
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageButtonTest, testing::Bool());
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, thumbnail.image_size());
EXPECT_EQ(device_pixel_ratio, thumbnail.device_pixel_ratio());
auto image_info =
SkImageInfo::Make(gfx::SizeToSkISize(thumbnail.image_size()),
kRGBA_8888_SkColorType, kPremul_SkAlphaType);
int stride = thumbnail.stride();
ASSERT_GT(stride, 0);
ASSERT_EQ(image_info.minRowBytes(), static_cast<size_t>(stride));
std::vector<uint8_t> data = thumbnail.TakeData();
sk_sp<SkImage> image = SkImages::RasterFromPixmapCopy(
SkPixmap(image_info, data.data(), image_info.minRowBytes()));
ASSERT_TRUE(image);
base::FilePath expectation_png_file_path =
GetThumbnailTestData(expectation_file_prefix, page_index,
device_pixel_ratio, /*use_skia=*/GetParam());
EXPECT_TRUE(MatchesPngFile(image.get(), expectation_png_file_path));
}
};
TEST_P(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
};
#if defined(ARCH_CPU_ARM64)
std::string file_name =
GetParam() ? "variable_page_sizes_arm64" : "variable_page_sizes";
#else
std::string file_name = "variable_page_sizes";
#endif
for (const auto& params : kGenerateThumbnailTestParams) {
TestGenerateThumbnail(*engine, params.page_index, params.device_pixel_ratio,
params.expected_thumbnail_size, file_name);
}
}
// For crbug.com/1248455
TEST_P(PDFiumPageThumbnailTest, GenerateThumbnailForAnnotation) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("signature_widget.pdf"));
TestGenerateThumbnail(*engine, /*page_index=*/0, /*device_pixel_ratio=*/1,
/*expected_thumbnail_size=*/{140, 140},
"signature_widget");
TestGenerateThumbnail(*engine, /*page_index=*/0, /*device_pixel_ratio=*/2,
/*expected_thumbnail_size=*/{255, 255},
"signature_widget");
}
INSTANTIATE_TEST_SUITE_P(All, PDFiumPageThumbnailTest, testing::Bool());
} // namespace chrome_pdf