1 // Copyright 2019 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdint.h>
6
7 #include <utility>
8
9 #include "build/build_config.h"
10 #include "core/fxge/cfx_defaultrenderdevice.h"
11 #include "core/fxge/dib/fx_dib.h"
12 #include "public/fpdf_progressive.h"
13 #include "testing/embedder_test.h"
14 #include "testing/embedder_test_constants.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/base/check.h"
17
18 namespace {
19
20 constexpr FX_ARGB kBlack = 0xFF000000;
21 constexpr FX_ARGB kBlue = 0xFF0000FF;
22 constexpr FX_ARGB kGreen = 0xFF00FF00;
23 constexpr FX_ARGB kRed = 0xFFFF0000;
24 constexpr FX_ARGB kWhite = 0xFFFFFFFF;
25
AnnotationStampWithApBaseContentChecksum()26 const char* AnnotationStampWithApBaseContentChecksum() {
27 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
28 return "4fedc838daa6762cf7eee180986a0f1b";
29 }
30 #if BUILDFLAG(IS_APPLE)
31 return "243f3d6267d9db09198fed9f8c4957fd";
32 #else
33 return "e31414933c9ff3950773981e5bf61678";
34 #endif
35 }
36
37 } // namespace
38
39 class FPDFProgressiveRenderEmbedderTest : public EmbedderTest {
40 public:
41 class FakePause : public IFSDK_PAUSE {
42 public:
FakePause(bool should_pause)43 explicit FakePause(bool should_pause) : should_pause_(should_pause) {
44 IFSDK_PAUSE::version = 1;
45 IFSDK_PAUSE::user = nullptr;
46 IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
47 }
48 ~FakePause() = default;
Pause_NeedToPauseNow(IFSDK_PAUSE * param)49 static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) {
50 return static_cast<FakePause*>(param)->should_pause_;
51 }
52
53 private:
54 const bool should_pause_;
55 };
56
57 // StartRenderPageWithFlags() with no flags.
58 // The call returns true if the rendering is complete.
59 bool StartRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
60
61 // Start rendering of |page| into a bitmap with the ability to |pause| the
62 // rendering with the specified rendering |flags|.
63 // The call returns true if the rendering is complete.
64 //
65 // See public/fpdfview.h for a list of page rendering flags.
66 bool StartRenderPageWithFlags(FPDF_PAGE page, IFSDK_PAUSE* pause, int flags);
67
68 // Start rendering of |page| into a bitmap with the ability to pause the
69 // rendering with the specified rendering |flags| and the specified
70 // |color_scheme|. This also takes in the |background_color| for the bitmap.
71 // The call returns true if the rendering is complete.
72 //
73 // See public/fpdfview.h for the list of page rendering flags and
74 // the list of colors in the scheme.
75 bool StartRenderPageWithColorSchemeAndBackground(
76 FPDF_PAGE page,
77 IFSDK_PAUSE* pause,
78 int flags,
79 const FPDF_COLORSCHEME* color_scheme,
80 uint32_t background_color);
81
82 // Continue rendering of |page| into the bitmap created in
83 // StartRenderPageWithFlags().
84 // The call returns true if the rendering is complete.
85 bool ContinueRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
86
87 // Simplified form of FinishRenderPageWithForms() with no form handle.
88 ScopedFPDFBitmap FinishRenderPage(FPDF_PAGE page);
89
90 // Finish rendering of |page| into the bitmap created in
91 // StartRenderPageWithFlags(). This also renders the forms associated with
92 // the page. The form handle associated with |page| should be passed in via
93 // |handle|. If |handle| is nullptr, then forms on the page will not be
94 // rendered.
95 // This returns the bitmap generated by the progressive render calls.
96 ScopedFPDFBitmap FinishRenderPageWithForms(FPDF_PAGE page,
97 FPDF_FORMHANDLE handle);
98
99 // Convert the |page| into a bitmap with a |background_color|, using the
100 // color scheme render API with the specific |flags| and |color_scheme|.
101 // The form handle associated with |page| should be passed in via |handle|.
102 // If |handle| is nullptr, then forms on the page will not be rendered.
103 // This returns the bitmap generated by the progressive render calls.
104 //
105 // See public/fpdfview.h for a list of page rendering flags and
106 // the color scheme that can be applied for rendering.
107 ScopedFPDFBitmap RenderPageWithForcedColorScheme(
108 FPDF_PAGE page,
109 FPDF_FORMHANDLE handle,
110 int flags,
111 const FPDF_COLORSCHEME* color_scheme,
112 FX_ARGB background_color);
113
114 protected:
115 // Utility method to render the |page_num| of the currently loaded Pdf
116 // using RenderPageWithForcedColorScheme() passing in the render options
117 // and expected values for bitmap verification.
118 void VerifyRenderingWithColorScheme(int page_num,
119 int flags,
120 const FPDF_COLORSCHEME* color_scheme,
121 FX_ARGB background_color,
122 int bitmap_width,
123 int bitmap_height,
124 const char* md5);
125
126 private:
127 // Keeps the bitmap used for progressive rendering alive until
128 // FPDF_RenderPage_Close() is called after which the bitmap is returned
129 // to the caller.
130 ScopedFPDFBitmap progressive_render_bitmap_;
131 int progressive_render_flags_ = 0;
132 };
133
StartRenderPage(FPDF_PAGE page,IFSDK_PAUSE * pause)134 bool FPDFProgressiveRenderEmbedderTest::StartRenderPage(FPDF_PAGE page,
135 IFSDK_PAUSE* pause) {
136 return StartRenderPageWithFlags(page, pause, 0);
137 }
138
StartRenderPageWithFlags(FPDF_PAGE page,IFSDK_PAUSE * pause,int flags)139 bool FPDFProgressiveRenderEmbedderTest::StartRenderPageWithFlags(
140 FPDF_PAGE page,
141 IFSDK_PAUSE* pause,
142 int flags) {
143 int width = static_cast<int>(FPDF_GetPageWidth(page));
144 int height = static_cast<int>(FPDF_GetPageHeight(page));
145 progressive_render_flags_ = flags;
146 int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
147 progressive_render_bitmap_ =
148 ScopedFPDFBitmap(FPDFBitmap_Create(width, height, alpha));
149 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
150 FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height,
151 fill_color);
152 int rv = FPDF_RenderPageBitmap_Start(progressive_render_bitmap_.get(), page,
153 0, 0, width, height, 0,
154 progressive_render_flags_, pause);
155 return rv != FPDF_RENDER_TOBECONTINUED;
156 }
157
158 bool FPDFProgressiveRenderEmbedderTest::
StartRenderPageWithColorSchemeAndBackground(FPDF_PAGE page,IFSDK_PAUSE * pause,int flags,const FPDF_COLORSCHEME * color_scheme,uint32_t background_color)159 StartRenderPageWithColorSchemeAndBackground(
160 FPDF_PAGE page,
161 IFSDK_PAUSE* pause,
162 int flags,
163 const FPDF_COLORSCHEME* color_scheme,
164 uint32_t background_color) {
165 int width = static_cast<int>(FPDF_GetPageWidth(page));
166 int height = static_cast<int>(FPDF_GetPageHeight(page));
167 progressive_render_flags_ = flags;
168 int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
169 progressive_render_bitmap_ =
170 ScopedFPDFBitmap(FPDFBitmap_Create(width, height, alpha));
171 DCHECK(progressive_render_bitmap_);
172 FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height,
173 background_color);
174 int rv = FPDF_RenderPageBitmapWithColorScheme_Start(
175 progressive_render_bitmap_.get(), page, 0, 0, width, height, 0,
176 progressive_render_flags_, color_scheme, pause);
177 return rv != FPDF_RENDER_TOBECONTINUED;
178 }
179
ContinueRenderPage(FPDF_PAGE page,IFSDK_PAUSE * pause)180 bool FPDFProgressiveRenderEmbedderTest::ContinueRenderPage(FPDF_PAGE page,
181 IFSDK_PAUSE* pause) {
182 DCHECK(progressive_render_bitmap_);
183
184 int rv = FPDF_RenderPage_Continue(page, pause);
185 return rv != FPDF_RENDER_TOBECONTINUED;
186 }
187
FinishRenderPage(FPDF_PAGE page)188 ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPage(
189 FPDF_PAGE page) {
190 return FinishRenderPageWithForms(page, /*handle=*/nullptr);
191 }
192
FinishRenderPageWithForms(FPDF_PAGE page,FPDF_FORMHANDLE handle)193 ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms(
194 FPDF_PAGE page,
195 FPDF_FORMHANDLE handle) {
196 DCHECK(progressive_render_bitmap_);
197
198 int width = static_cast<int>(FPDF_GetPageWidth(page));
199 int height = static_cast<int>(FPDF_GetPageHeight(page));
200 FPDF_FFLDraw(handle, progressive_render_bitmap_.get(), page, 0, 0, width,
201 height, 0, progressive_render_flags_);
202 FPDF_RenderPage_Close(page);
203 return std::move(progressive_render_bitmap_);
204 }
205
206 ScopedFPDFBitmap
RenderPageWithForcedColorScheme(FPDF_PAGE page,FPDF_FORMHANDLE handle,int flags,const FPDF_COLORSCHEME * color_scheme,FX_ARGB background_color)207 FPDFProgressiveRenderEmbedderTest::RenderPageWithForcedColorScheme(
208 FPDF_PAGE page,
209 FPDF_FORMHANDLE handle,
210 int flags,
211 const FPDF_COLORSCHEME* color_scheme,
212 FX_ARGB background_color) {
213 FakePause pause(true);
214 bool render_done = StartRenderPageWithColorSchemeAndBackground(
215 page, &pause, flags, color_scheme, background_color) ==
216 FPDF_RENDER_TOBECONTINUED;
217 EXPECT_FALSE(render_done);
218
219 while (!render_done) {
220 render_done = ContinueRenderPage(page, &pause);
221 }
222 return FinishRenderPageWithForms(page, form_handle());
223 }
224
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderWithoutPause)225 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithoutPause) {
226 // Test rendering of page content using progressive render APIs
227 // without pausing the rendering.
228 ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
229 FPDF_PAGE page = LoadPage(0);
230 ASSERT_TRUE(page);
231 FakePause pause(false);
232 EXPECT_TRUE(StartRenderPage(page, &pause));
233 ScopedFPDFBitmap bitmap = FinishRenderPage(page);
234 CompareBitmap(bitmap.get(), 595, 842,
235 AnnotationStampWithApBaseContentChecksum());
236 UnloadPage(page);
237 }
238
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderWithPause)239 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithPause) {
240 // Test rendering of page content using progressive render APIs
241 // with pause in rendering.
242 ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
243 FPDF_PAGE page = LoadPage(0);
244 ASSERT_TRUE(page);
245 FakePause pause(true);
246 bool render_done = StartRenderPage(page, &pause);
247 EXPECT_FALSE(render_done);
248
249 while (!render_done) {
250 render_done = ContinueRenderPage(page, &pause);
251 }
252 ScopedFPDFBitmap bitmap = FinishRenderPage(page);
253 CompareBitmap(bitmap.get(), 595, 842,
254 AnnotationStampWithApBaseContentChecksum());
255 UnloadPage(page);
256 }
257
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderAnnotWithPause)258 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderAnnotWithPause) {
259 // Test rendering of the page with annotations using progressive render APIs
260 // with pause in rendering.
261 ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
262 FPDF_PAGE page = LoadPage(0);
263 ASSERT_TRUE(page);
264 FakePause pause(true);
265 bool render_done = StartRenderPageWithFlags(page, &pause, FPDF_ANNOT);
266 EXPECT_FALSE(render_done);
267
268 while (!render_done) {
269 render_done = ContinueRenderPage(page, &pause);
270 }
271 ScopedFPDFBitmap bitmap = FinishRenderPage(page);
272 CompareBitmap(bitmap.get(), 595, 842,
273 pdfium::AnnotationStampWithApChecksum());
274 UnloadPage(page);
275 }
276
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderFormsWithPause)277 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormsWithPause) {
278 // Test rendering of the page with forms using progressive render APIs
279 // with pause in rendering.
280 ASSERT_TRUE(OpenDocument("text_form.pdf"));
281 FPDF_PAGE page = LoadPage(0);
282 ASSERT_TRUE(page);
283 FakePause pause(true);
284 bool render_done = StartRenderPage(page, &pause);
285 EXPECT_FALSE(render_done);
286
287 while (!render_done) {
288 render_done = ContinueRenderPage(page, &pause);
289 }
290 ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle());
291 CompareBitmap(bitmap.get(), 300, 300, pdfium::TextFormChecksum());
292 UnloadPage(page);
293 }
294
VerifyRenderingWithColorScheme(int page_num,int flags,const FPDF_COLORSCHEME * color_scheme,FX_ARGB background_color,int bitmap_width,int bitmap_height,const char * md5)295 void FPDFProgressiveRenderEmbedderTest::VerifyRenderingWithColorScheme(
296 int page_num,
297 int flags,
298 const FPDF_COLORSCHEME* color_scheme,
299 FX_ARGB background_color,
300 int bitmap_width,
301 int bitmap_height,
302 const char* md5) {
303 ASSERT_TRUE(document());
304
305 FPDF_PAGE page = LoadPage(page_num);
306 ASSERT_TRUE(page);
307
308 ScopedFPDFBitmap bitmap = RenderPageWithForcedColorScheme(
309 page, form_handle(), flags, color_scheme, background_color);
310 ASSERT_TRUE(bitmap);
311 CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, md5);
312 UnloadPage(page);
313 }
314
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderTextWithColorScheme)315 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderTextWithColorScheme) {
316 // Test rendering of text with forced color scheme on.
317 const char* content_with_text_checksum = []() {
318 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
319 return "edd919ec8b59fab1f16b5f2adb1175f3";
320 #if BUILDFLAG(IS_APPLE)
321 return "ee4ec12f54ce8d117a73bd9b85a8954d";
322 #else
323 return "704db63ed2bf77254ecaa8035b85f21a";
324 #endif // BUILDFLAG(IS_APPLE)
325 }();
326
327 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
328
329 FPDF_COLORSCHEME color_scheme{kBlack, kWhite, kWhite, kWhite};
330 VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
331 kBlack, 200, 200, content_with_text_checksum);
332 }
333
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderPathWithColorScheme)334 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderPathWithColorScheme) {
335 // Test rendering of paths with forced color scheme on.
336 const char* rectangles_checksum = []() {
337 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
338 return "4b0f850a94698d07b6cd2814d1b4ccb7";
339 return "249f59b0d066c4f6bd89782a80822219";
340 }();
341
342 ASSERT_TRUE(OpenDocument("rectangles.pdf"));
343
344 FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
345 VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
346 kBlack, 200, 300, rectangles_checksum);
347 }
348
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderPathWithColorSchemeAndConvertFillToStroke)349 TEST_F(FPDFProgressiveRenderEmbedderTest,
350 RenderPathWithColorSchemeAndConvertFillToStroke) {
351 // Test rendering of paths with forced color scheme on and conversion from
352 // fill to stroke enabled. The fill paths should be rendered as stroke.
353 const char* rectangles_checksum = []() {
354 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
355 return "c1cbbd2ce6921f608a3c55140592419b";
356 return "0ebcc11e617635eca1fa9ce475383a80";
357 }();
358
359 ASSERT_TRUE(OpenDocument("rectangles.pdf"));
360
361 FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
362 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_CONVERT_FILL_TO_STROKE,
363 &color_scheme, kBlack, 200, 300,
364 rectangles_checksum);
365 }
366
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderHighlightWithColorScheme)367 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderHighlightWithColorScheme) {
368 // Test rendering of highlight with forced color scheme on.
369 //
370 // Note: The fill color rendered for highlight is different from the normal
371 // path since highlights have Multiply blend mode, while the other path has
372 // Normal blend mode.
373 const char* content_with_highlight_fill_checksum = []() {
374 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
375 return "49dcfcfdc38d200bb3d57a2ca3086034";
376 #if BUILDFLAG(IS_APPLE)
377 return "a820afec9b99d3d3f2e9e9382bbad7c1";
378 #else
379 return "a08a0639f89446f66f3689ee8e08b9fe";
380 #endif // BUILDFLAG(IS_APPLE)
381 }();
382
383 ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
384
385 FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
386 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
387 kBlue, 612, 792,
388 content_with_highlight_fill_checksum);
389 }
390
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderHighlightWithColorSchemeAndConvertFillToStroke)391 TEST_F(FPDFProgressiveRenderEmbedderTest,
392 RenderHighlightWithColorSchemeAndConvertFillToStroke) {
393 // Test rendering of highlight with forced color and converting fill to
394 // stroke. The highlight should be rendered as a stroke of the rect.
395 //
396 // Note: The stroke color rendered for highlight is different from the normal
397 // path since highlights have Multiply blend mode, while the other path has
398 // Normal blend mode.
399
400 const char* md5_content_with_highlight = []() {
401 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
402 return "c609e8810fba2f12db8f8a2b043d97bd";
403 #if BUILDFLAG(IS_APPLE)
404 return "8837bea0b3520164b1784e513c882a2d";
405 #else
406 return "3dd8c02f5c06bac85e0d2c8bf37d1dc4";
407 #endif // BUILDFLAG(IS_APPLE)
408 }();
409
410 ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
411
412 FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
413 VerifyRenderingWithColorScheme(
414 /*page_num=*/0, FPDF_ANNOT | FPDF_CONVERT_FILL_TO_STROKE, &color_scheme,
415 kBlue, 612, 792, md5_content_with_highlight);
416 }
417
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderInkWithColorScheme)418 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderInkWithColorScheme) {
419 // Test rendering of multiple ink with forced color scheme on.
420 const char* content_with_ink_checksum = []() {
421 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
422 #if BUILDFLAG(IS_APPLE)
423 return "5108aa537b6ecc37b3f0a35b76c1b379";
424 #else
425 return "b39d9f68ff71963d82c43eb20caa8f4d";
426 #endif
427 }
428 return "797bce7dc6c50ee86b095405df9fe5aa";
429 }();
430
431 ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
432
433 FPDF_COLORSCHEME color_scheme{kBlack, kGreen, kRed, kRed};
434 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
435 kBlack, 612, 792, content_with_ink_checksum);
436 }
437
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderStampWithColorScheme)438 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderStampWithColorScheme) {
439 // Test rendering of static annotation with forced color scheme on.
440 const char* content_with_stamp_checksum = []() {
441 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
442 return "6e028012a4854ebfd9ee92da862bf679";
443 #if BUILDFLAG(IS_APPLE)
444 return "8170c539e95f22f14eb8f266a5f1bbed";
445 #else
446 return "d1fd087e59d4dcebf47b56570bdb8c22";
447 #endif
448 }();
449
450 ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
451
452 FPDF_COLORSCHEME color_scheme{kBlue, kGreen, kRed, kRed};
453 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
454 kWhite, 595, 842, content_with_stamp_checksum);
455 }
456
TEST_F(FPDFProgressiveRenderEmbedderTest,RenderFormWithColorScheme)457 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormWithColorScheme) {
458 // Test rendering of form does not change with forced color scheme on.
459 const char* content_with_form_checksum = []() {
460 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
461 return "9f75d98afc6d6313bd87e6562ea6df15";
462 return "080f7a4381606659301440e1b14dca35";
463 }();
464
465 ASSERT_TRUE(OpenDocument("annotiter.pdf"));
466
467 FPDF_COLORSCHEME color_scheme{kGreen, kGreen, kRed, kRed};
468 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
469 kWhite, 612, 792, content_with_form_checksum);
470
471 // Verify that the MD5 hash matches when rendered without |color_scheme|.
472 VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT,
473 /*color_scheme=*/nullptr, kWhite, 612, 792,
474 content_with_form_checksum);
475 }
476