// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "printing/pdf_metafile_cg_mac.h" #import #include #include #include #include #include "base/files/file_util.h" #include "base/hash/sha1.h" #include "base/path_service.h" #include "printing/mojom/print.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/rect.h" namespace printing { namespace { base::FilePath GetPdfTestData(const base::FilePath::StringType& filename) { base::FilePath root_path; if (!base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)) return base::FilePath(); return root_path.Append("pdf").Append("test").Append("data").Append(filename); } base::FilePath GetPrintingTestData(const base::FilePath::StringType& filename) { base::FilePath root_path; if (!base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)) return base::FilePath(); return root_path.Append("printing") .Append("test") .Append("data") .Append("pdf_cg") .Append(filename); } std::unique_ptr GetPdfMetafile( const base::FilePath::StringType& pdf_filename) { // Get test data. base::FilePath pdf_file = GetPdfTestData(pdf_filename); if (pdf_file.empty()) return nullptr; std::string pdf_data; if (!base::ReadFileToString(pdf_file, &pdf_data)) return nullptr; // Initialize and check metafile. auto pdf_cg = std::make_unique(); if (!pdf_cg->InitFromData(base::as_bytes(base::make_span(pdf_data)))) return nullptr; return pdf_cg; } void RenderedPdfSha1(const base::FilePath::StringType& pdf_filename, size_t page_number, const gfx::Rect& expected_page_bounds, const gfx::Size& dest_size, bool autorotate, bool fit_to_page, base::SHA1Digest* rendered_hash) { // Initialize and verify the metafile. std::unique_ptr pdf_cg = GetPdfMetafile(pdf_filename); ASSERT_TRUE(pdf_cg); ASSERT_LE(page_number, pdf_cg->GetPageCount()); const gfx::Rect bounds = pdf_cg->GetPageBounds(page_number); ASSERT_EQ(expected_page_bounds, bounds); // Set up rendering context. constexpr size_t kBitsPerComponent = 8; constexpr size_t kBytesPerPixel = 4; const size_t kStride = dest_size.width() * kBytesPerPixel; std::vector rendered_bitmap(dest_size.height() * kStride); base::ScopedCFTypeRef color_space( CGColorSpaceCreateDeviceRGB()); base::ScopedCFTypeRef context(CGBitmapContextCreate( rendered_bitmap.data(), dest_size.width(), dest_size.height(), kBitsPerComponent, kStride, color_space, uint32_t{kCGImageAlphaPremultipliedFirst} | kCGBitmapByteOrder32Little)); // Render using metafile and calculate the output hash. ASSERT_TRUE(pdf_cg->RenderPage(page_number, context, gfx::Rect(dest_size).ToCGRect(), autorotate, fit_to_page)); *rendered_hash = base::SHA1HashSpan(rendered_bitmap); } void ExpectedPngSha1(const base::FilePath::StringType& expected_png_filename, const gfx::Size& expected_png_size, base::SHA1Digest* expected_hash) { base::FilePath expected_png_file = GetPrintingTestData(expected_png_filename); ASSERT_FALSE(expected_png_file.empty()); std::string expected_png_data; ASSERT_TRUE(base::ReadFileToString(expected_png_file, &expected_png_data)); // Decode expected PNG and calculate the output hash. std::vector expected_png_bitmap; int png_width; int png_height; ASSERT_TRUE(gfx::PNGCodec::Decode( reinterpret_cast(expected_png_data.data()), expected_png_data.size(), gfx::PNGCodec::FORMAT_BGRA, &expected_png_bitmap, &png_width, &png_height)); ASSERT_EQ(expected_png_size.width(), png_width); ASSERT_EQ(expected_png_size.height(), png_height); *expected_hash = base::SHA1HashSpan(expected_png_bitmap); } void TestRenderPageWithTransformParams( const base::FilePath::StringType& pdf_filename, size_t page_number, const gfx::Rect& expected_page_bounds, const base::FilePath::StringType& expected_png_filename, const gfx::Size& dest_size, bool autorotate, bool fit_to_page) { base::SHA1Digest rendered_hash; RenderedPdfSha1(pdf_filename, page_number, expected_page_bounds, dest_size, autorotate, fit_to_page, &rendered_hash); base::SHA1Digest expected_hash; ExpectedPngSha1(expected_png_filename, dest_size, &expected_hash); // Make sure the hashes match. EXPECT_EQ(expected_hash, rendered_hash); } void TestRenderPage(const base::FilePath::StringType& pdf_filename, size_t page_number, const gfx::Rect& expected_page_bounds, const base::FilePath::StringType& expected_png_filename, const gfx::Size& dest_size) { TestRenderPageWithTransformParams( pdf_filename, page_number, expected_page_bounds, expected_png_filename, dest_size, /*autorotate=*/true, /*fit_to_page=*/false); } } // namespace TEST(PdfMetafileCgTest, Pdf) { // Test in-renderer constructor. PdfMetafileCg pdf; EXPECT_TRUE(pdf.Init()); EXPECT_TRUE(pdf.context()); // Render page 1. constexpr gfx::Rect kRect1(10, 10, 520, 700); constexpr gfx::Size kSize1(540, 720); pdf.StartPage(kSize1, kRect1, 1.25, mojom::PageOrientation::kUpright); pdf.FinishPage(); // Render page 2. constexpr gfx::Rect kRect2(10, 10, 520, 700); constexpr gfx::Size kSize2(720, 540); pdf.StartPage(kSize2, kRect2, 2.0, mojom::PageOrientation::kUpright); pdf.FinishPage(); pdf.FinishDocument(); // Check data size. const uint32_t size = pdf.GetDataSize(); EXPECT_GT(size, 0U); // Get resulting data. std::vector buffer(size, 0); pdf.GetData(&buffer.front(), size); // Test browser-side constructor. PdfMetafileCg pdf2; // TODO(thestig): Make `buffer` uint8_t and avoid the base::as_bytes() call. EXPECT_TRUE(pdf2.InitFromData(base::as_bytes(base::make_span(buffer)))); // Get the first 4 characters from pdf2. std::vector buffer2(4, 0); pdf2.GetData(&buffer2.front(), 4); // Test that the header begins with "%PDF". std::string header(&buffer2.front(), 4); EXPECT_EQ(0U, header.find("%PDF", 0)); // Test that the PDF is correctly reconstructed. EXPECT_EQ(2U, pdf2.GetPageCount()); gfx::Size page_size = pdf2.GetPageBounds(1).size(); EXPECT_EQ(540, page_size.width()); EXPECT_EQ(720, page_size.height()); page_size = pdf2.GetPageBounds(2).size(); EXPECT_EQ(720, page_size.width()); EXPECT_EQ(540, page_size.height()); } TEST(PdfMetafileCgTest, GetPageBounds) { // Get test data. base::FilePath pdf_file = GetPdfTestData("rectangles_multi_pages.pdf"); ASSERT_FALSE(pdf_file.empty()); std::string pdf_data; ASSERT_TRUE(base::ReadFileToString(pdf_file, &pdf_data)); // Initialize and check metafile. PdfMetafileCg pdf_cg; ASSERT_TRUE(pdf_cg.InitFromData(base::as_bytes(base::make_span(pdf_data)))); ASSERT_EQ(5u, pdf_cg.GetPageCount()); // Since the input into GetPageBounds() is a 1-indexed page number, 0 and 6 // are out of bounds. gfx::Rect bounds; for (size_t i : {0, 6}) { bounds = pdf_cg.GetPageBounds(i); EXPECT_EQ(0, bounds.x()); EXPECT_EQ(0, bounds.y()); EXPECT_EQ(0, bounds.width()); EXPECT_EQ(0, bounds.height()); } // Whereas 1-5 are in bounds. for (size_t i = 1; i < 6; ++i) { bounds = pdf_cg.GetPageBounds(i); EXPECT_EQ(0, bounds.x()); EXPECT_EQ(0, bounds.y()); EXPECT_EQ(200, bounds.width()); EXPECT_EQ(250, bounds.height()); } } TEST(PdfMetafileCgTest, RenderPortraitRectangles) { constexpr gfx::Rect kPageBounds(200, 300); constexpr gfx::Size kDestinationSize(200, 300); TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds, "render_portrait_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderAutorotatedPortraitRectangles) { constexpr gfx::Rect kPageBounds(200, 300); constexpr gfx::Size kDestinationSize(300, 200); TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds, "render_autorotated_portrait_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderLargePortraitRectangles) { constexpr gfx::Rect kPageBounds(200, 300); constexpr gfx::Size kDestinationSize(100, 120); TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds, "render_large_portrait_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderSmallPortraitRectangles) { constexpr gfx::Rect kPageBounds(200, 300); constexpr gfx::Size kDestinationSize(300, 450); TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds, "render_small_portrait_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderLandscapeRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kDestinationSize(400, 600); TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_landscape_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderRotatedRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kLandscapeDestinationSize(600, 400); constexpr gfx::Size kPortraitDestinationSize(400, 600); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_rotated_rectangles_expected.0.png", kLandscapeDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/2, kPageBounds, "render_rotated_rectangles_expected.1.png", kPortraitDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/3, kPageBounds, "render_rotated_rectangles_expected.2.png", kLandscapeDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/4, kPageBounds, "render_rotated_rectangles_expected.3.png", kPortraitDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_autorotated_rotated_rectangles_expected.0.png", kPortraitDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/2, kPageBounds, "render_autorotated_rotated_rectangles_expected.1.png", kLandscapeDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/3, kPageBounds, "render_autorotated_rotated_rectangles_expected.2.png", kPortraitDestinationSize); TestRenderPage("rotated_rectangles.pdf", /*page_number=*/4, kPageBounds, "render_autorotated_rotated_rectangles_expected.3.png", kLandscapeDestinationSize); } TEST(PdfMetafileCgTest, RenderLargeLandscapeRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kDestinationSize(200, 300); TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_large_landscape_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderSmallLandscapeRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kDestinationSize(600, 900); TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_small_landscape_rectangles_expected.0.png", kDestinationSize); } TEST(PdfMetafileCgTest, RenderScaledLargeLandscapeRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kDestinationSize(300, 450); TestRenderPageWithTransformParams( "landscape_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_scaled_large_landscape_rectangles_expected.0.png", kDestinationSize, /*autorotate=*/true, /*fit_to_page=*/true); } TEST(PdfMetafileCgTest, RenderScaledSmallLandscapeRectangles) { constexpr gfx::Rect kPageBounds(800, 500); constexpr gfx::Size kDestinationSize(600, 900); TestRenderPageWithTransformParams( "landscape_rectangles.pdf", /*page_number=*/1, kPageBounds, "render_scaled_small_landscape_rectangles_expected.0.png", kDestinationSize, /*autorotate=*/true, /*fit_to_page=*/true); } } // namespace printing