1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/pdf_metafile_cg_mac.h"
7 #include <CoreGraphics/CoreGraphics.h>
14 #include "base/files/file_util.h"
15 #include "base/hash/sha1.h"
16 #include "base/path_service.h"
17 #include "printing/mojom/print.mojom.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/gfx/codec/png_codec.h"
20 #include "ui/gfx/geometry/rect.h"
26 base::FilePath GetPdfTestData(const base::FilePath::StringType& filename) {
27 base::FilePath root_path;
28 if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root_path)) {
29 return base::FilePath();
31 return root_path.Append("pdf").Append("test").Append("data").Append(filename);
34 base::FilePath GetPrintingTestData(const base::FilePath::StringType& filename) {
35 base::FilePath root_path;
36 if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root_path)) {
37 return base::FilePath();
39 return root_path.Append("printing")
46 std::unique_ptr<PdfMetafileCg> GetPdfMetafile(
47 const base::FilePath::StringType& pdf_filename) {
49 base::FilePath pdf_file = GetPdfTestData(pdf_filename);
54 if (!base::ReadFileToString(pdf_file, &pdf_data))
57 // Initialize and check metafile.
58 auto pdf_cg = std::make_unique<PdfMetafileCg>();
59 if (!pdf_cg->InitFromData(base::as_bytes(base::make_span(pdf_data))))
64 void RenderedPdfSha1(const base::FilePath::StringType& pdf_filename,
66 const gfx::Rect& expected_page_bounds,
67 const gfx::Size& dest_size,
70 base::SHA1Digest* rendered_hash) {
71 // Initialize and verify the metafile.
72 std::unique_ptr<PdfMetafileCg> pdf_cg = GetPdfMetafile(pdf_filename);
74 ASSERT_LE(page_number, pdf_cg->GetPageCount());
75 const gfx::Rect bounds = pdf_cg->GetPageBounds(page_number);
76 ASSERT_EQ(expected_page_bounds, bounds);
78 // Set up rendering context.
79 constexpr size_t kBitsPerComponent = 8;
80 constexpr size_t kBytesPerPixel = 4;
81 const size_t kStride = dest_size.width() * kBytesPerPixel;
82 std::vector<uint8_t> rendered_bitmap(dest_size.height() * kStride);
83 base::apple::ScopedCFTypeRef<CGColorSpaceRef> color_space(
84 CGColorSpaceCreateDeviceRGB());
85 base::apple::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
86 rendered_bitmap.data(), dest_size.width(), dest_size.height(),
87 kBitsPerComponent, kStride, color_space.get(),
88 uint32_t{kCGImageAlphaPremultipliedFirst} | kCGBitmapByteOrder32Little));
90 // Render using metafile and calculate the output hash.
91 ASSERT_TRUE(pdf_cg->RenderPage(page_number, context.get(),
92 gfx::Rect(dest_size).ToCGRect(), autorotate,
94 *rendered_hash = base::SHA1HashSpan(rendered_bitmap);
97 void ExpectedPngSha1(const base::FilePath::StringType& expected_png_filename,
98 const gfx::Size& expected_png_size,
99 base::SHA1Digest* expected_hash) {
100 base::FilePath expected_png_file = GetPrintingTestData(expected_png_filename);
101 ASSERT_FALSE(expected_png_file.empty());
102 std::string expected_png_data;
103 ASSERT_TRUE(base::ReadFileToString(expected_png_file, &expected_png_data));
105 // Decode expected PNG and calculate the output hash.
106 std::vector<uint8_t> expected_png_bitmap;
109 ASSERT_TRUE(gfx::PNGCodec::Decode(
110 reinterpret_cast<const uint8_t*>(expected_png_data.data()),
111 expected_png_data.size(), gfx::PNGCodec::FORMAT_BGRA,
112 &expected_png_bitmap, &png_width, &png_height));
113 ASSERT_EQ(expected_png_size.width(), png_width);
114 ASSERT_EQ(expected_png_size.height(), png_height);
115 *expected_hash = base::SHA1HashSpan(expected_png_bitmap);
118 void TestRenderPageWithTransformParams(
119 const base::FilePath::StringType& pdf_filename,
121 const gfx::Rect& expected_page_bounds,
122 const base::FilePath::StringType& expected_png_filename,
123 const gfx::Size& dest_size,
126 base::SHA1Digest rendered_hash;
127 RenderedPdfSha1(pdf_filename, page_number, expected_page_bounds, dest_size,
128 autorotate, fit_to_page, &rendered_hash);
129 base::SHA1Digest expected_hash;
130 ExpectedPngSha1(expected_png_filename, dest_size, &expected_hash);
132 // Make sure the hashes match.
133 EXPECT_EQ(expected_hash, rendered_hash);
136 void TestRenderPage(const base::FilePath::StringType& pdf_filename,
138 const gfx::Rect& expected_page_bounds,
139 const base::FilePath::StringType& expected_png_filename,
140 const gfx::Size& dest_size) {
141 TestRenderPageWithTransformParams(
142 pdf_filename, page_number, expected_page_bounds, expected_png_filename,
143 dest_size, /*autorotate=*/true, /*fit_to_page=*/false);
148 TEST(PdfMetafileCgTest, Pdf) {
149 // Test in-renderer constructor.
151 EXPECT_TRUE(pdf.Init());
152 EXPECT_TRUE(pdf.context());
155 constexpr gfx::Rect kRect1(10, 10, 520, 700);
156 constexpr gfx::Size kSize1(540, 720);
157 pdf.StartPage(kSize1, kRect1, 1.25, mojom::PageOrientation::kUpright);
161 constexpr gfx::Rect kRect2(10, 10, 520, 700);
162 constexpr gfx::Size kSize2(720, 540);
163 pdf.StartPage(kSize2, kRect2, 2.0, mojom::PageOrientation::kUpright);
166 pdf.FinishDocument();
169 const uint32_t size = pdf.GetDataSize();
172 // Get resulting data.
173 std::vector<char> buffer(size, 0);
174 pdf.GetData(&buffer.front(), size);
176 // Test browser-side constructor.
178 // TODO(thestig): Make `buffer` uint8_t and avoid the base::as_bytes() call.
179 EXPECT_TRUE(pdf2.InitFromData(base::as_bytes(base::make_span(buffer))));
181 // Get the first 4 characters from pdf2.
182 std::vector<char> buffer2(4, 0);
183 pdf2.GetData(&buffer2.front(), 4);
185 // Test that the header begins with "%PDF".
186 std::string header(&buffer2.front(), 4);
187 EXPECT_EQ(0U, header.find("%PDF", 0));
189 // Test that the PDF is correctly reconstructed.
190 EXPECT_EQ(2U, pdf2.GetPageCount());
191 gfx::Size page_size = pdf2.GetPageBounds(1).size();
192 EXPECT_EQ(540, page_size.width());
193 EXPECT_EQ(720, page_size.height());
194 page_size = pdf2.GetPageBounds(2).size();
195 EXPECT_EQ(720, page_size.width());
196 EXPECT_EQ(540, page_size.height());
199 TEST(PdfMetafileCgTest, GetPageBounds) {
201 base::FilePath pdf_file = GetPdfTestData("rectangles_multi_pages.pdf");
202 ASSERT_FALSE(pdf_file.empty());
203 std::string pdf_data;
204 ASSERT_TRUE(base::ReadFileToString(pdf_file, &pdf_data));
206 // Initialize and check metafile.
207 PdfMetafileCg pdf_cg;
208 ASSERT_TRUE(pdf_cg.InitFromData(base::as_bytes(base::make_span(pdf_data))));
209 ASSERT_EQ(5u, pdf_cg.GetPageCount());
211 // Since the input into GetPageBounds() is a 1-indexed page number, 0 and 6
212 // are out of bounds.
214 for (size_t i : {0, 6}) {
215 bounds = pdf_cg.GetPageBounds(i);
216 EXPECT_EQ(0, bounds.x());
217 EXPECT_EQ(0, bounds.y());
218 EXPECT_EQ(0, bounds.width());
219 EXPECT_EQ(0, bounds.height());
222 // Whereas 1-5 are in bounds.
223 for (size_t i = 1; i < 6; ++i) {
224 bounds = pdf_cg.GetPageBounds(i);
225 EXPECT_EQ(0, bounds.x());
226 EXPECT_EQ(0, bounds.y());
227 EXPECT_EQ(200, bounds.width());
228 EXPECT_EQ(250, bounds.height());
232 TEST(PdfMetafileCgTest, RenderPortraitRectangles) {
233 constexpr gfx::Rect kPageBounds(200, 300);
234 constexpr gfx::Size kDestinationSize(200, 300);
235 TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds,
236 "render_portrait_rectangles_expected.0.png", kDestinationSize);
239 TEST(PdfMetafileCgTest, RenderAutorotatedPortraitRectangles) {
240 constexpr gfx::Rect kPageBounds(200, 300);
241 constexpr gfx::Size kDestinationSize(300, 200);
242 TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds,
243 "render_autorotated_portrait_rectangles_expected.0.png",
247 TEST(PdfMetafileCgTest, RenderLargePortraitRectangles) {
248 constexpr gfx::Rect kPageBounds(200, 300);
249 constexpr gfx::Size kDestinationSize(100, 120);
250 TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds,
251 "render_large_portrait_rectangles_expected.0.png",
255 TEST(PdfMetafileCgTest, RenderSmallPortraitRectangles) {
256 constexpr gfx::Rect kPageBounds(200, 300);
257 constexpr gfx::Size kDestinationSize(300, 450);
258 TestRenderPage("rectangles.pdf", /*page_number=*/1, kPageBounds,
259 "render_small_portrait_rectangles_expected.0.png",
263 TEST(PdfMetafileCgTest, RenderLandscapeRectangles) {
264 constexpr gfx::Rect kPageBounds(800, 500);
265 constexpr gfx::Size kDestinationSize(400, 600);
266 TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds,
267 "render_landscape_rectangles_expected.0.png",
271 TEST(PdfMetafileCgTest, RenderRotatedRectangles) {
272 constexpr gfx::Rect kPageBounds(800, 500);
273 constexpr gfx::Size kLandscapeDestinationSize(600, 400);
274 constexpr gfx::Size kPortraitDestinationSize(400, 600);
276 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/1, kPageBounds,
277 "render_rotated_rectangles_expected.0.png",
278 kLandscapeDestinationSize);
279 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/2, kPageBounds,
280 "render_rotated_rectangles_expected.1.png",
281 kPortraitDestinationSize);
282 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/3, kPageBounds,
283 "render_rotated_rectangles_expected.2.png",
284 kLandscapeDestinationSize);
285 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/4, kPageBounds,
286 "render_rotated_rectangles_expected.3.png",
287 kPortraitDestinationSize);
289 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/1, kPageBounds,
290 "render_autorotated_rotated_rectangles_expected.0.png",
291 kPortraitDestinationSize);
292 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/2, kPageBounds,
293 "render_autorotated_rotated_rectangles_expected.1.png",
294 kLandscapeDestinationSize);
295 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/3, kPageBounds,
296 "render_autorotated_rotated_rectangles_expected.2.png",
297 kPortraitDestinationSize);
298 TestRenderPage("rotated_rectangles.pdf", /*page_number=*/4, kPageBounds,
299 "render_autorotated_rotated_rectangles_expected.3.png",
300 kLandscapeDestinationSize);
303 TEST(PdfMetafileCgTest, RenderLargeLandscapeRectangles) {
304 constexpr gfx::Rect kPageBounds(800, 500);
305 constexpr gfx::Size kDestinationSize(200, 300);
306 TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds,
307 "render_large_landscape_rectangles_expected.0.png",
311 TEST(PdfMetafileCgTest, RenderSmallLandscapeRectangles) {
312 constexpr gfx::Rect kPageBounds(800, 500);
313 constexpr gfx::Size kDestinationSize(600, 900);
314 TestRenderPage("landscape_rectangles.pdf", /*page_number=*/1, kPageBounds,
315 "render_small_landscape_rectangles_expected.0.png",
319 TEST(PdfMetafileCgTest, RenderScaledLargeLandscapeRectangles) {
320 constexpr gfx::Rect kPageBounds(800, 500);
321 constexpr gfx::Size kDestinationSize(300, 450);
322 TestRenderPageWithTransformParams(
323 "landscape_rectangles.pdf", /*page_number=*/1, kPageBounds,
324 "render_scaled_large_landscape_rectangles_expected.0.png",
326 /*autorotate=*/true, /*fit_to_page=*/true);
329 TEST(PdfMetafileCgTest, RenderScaledSmallLandscapeRectangles) {
330 constexpr gfx::Rect kPageBounds(800, 500);
331 constexpr gfx::Size kDestinationSize(600, 900);
332 TestRenderPageWithTransformParams(
333 "landscape_rectangles.pdf", /*page_number=*/1, kPageBounds,
334 "render_scaled_small_landscape_rectangles_expected.0.png",
336 /*autorotate=*/true, /*fit_to_page=*/true);
339 } // namespace printing