1 // Copyright 2010 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 <locale.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <algorithm>
13 #include <functional>
14 #include <iterator>
15 #include <map>
16 #include <memory>
17 #include <optional>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21
22 #if defined(PDF_ENABLE_SKIA) && !defined(PDF_USE_SKIA)
23 #define PDF_USE_SKIA
24 #endif
25
26 #include "core/fxcrt/check_op.h"
27 #include "core/fxcrt/compiler_specific.h"
28 #include "core/fxcrt/span.h"
29 #include "public/cpp/fpdf_scopers.h"
30 #include "public/fpdf_annot.h"
31 #include "public/fpdf_attachment.h"
32 #include "public/fpdf_dataavail.h"
33 #include "public/fpdf_edit.h"
34 #include "public/fpdf_ext.h"
35 #include "public/fpdf_formfill.h"
36 #include "public/fpdf_progressive.h"
37 #include "public/fpdf_structtree.h"
38 #include "public/fpdf_text.h"
39 #include "public/fpdfview.h"
40 #include "testing/command_line_helpers.h"
41 #include "testing/font_renamer.h"
42 #include "testing/fx_string_testhelpers.h"
43 #include "testing/helpers/dump.h"
44 #include "testing/helpers/event.h"
45 #include "testing/helpers/page_renderer.h"
46 #include "testing/helpers/write.h"
47 #include "testing/test_loader.h"
48 #include "testing/utils/file_util.h"
49 #include "testing/utils/hash.h"
50 #include "testing/utils/path_service.h"
51
52 #ifdef _WIN32
53 #include <crtdbg.h>
54 #include <errhandlingapi.h>
55 #include <io.h>
56 #include <wingdi.h>
57
58 #include "testing/helpers/win32/com_factory.h"
59 #else
60 #include <unistd.h>
61 #endif // _WIN32
62
63 #ifdef ENABLE_CALLGRIND
64 #include <valgrind/callgrind.h>
65 #endif // ENABLE_CALLGRIND
66
67 #if defined(PDF_USE_PARTITION_ALLOC)
68 #include "testing/allocator_shim_config.h"
69 #endif
70
71 #ifdef PDF_ENABLE_SKIA
72 #include "third_party/skia/include/core/SkCanvas.h" // nogncheck
73 #include "third_party/skia/include/core/SkColor.h" // nogncheck
74 #include "third_party/skia/include/core/SkDocument.h" // nogncheck
75 #include "third_party/skia/include/core/SkPicture.h" // nogncheck
76 #include "third_party/skia/include/core/SkPictureRecorder.h" // nogncheck
77 #include "third_party/skia/include/core/SkPixmap.h" // nogncheck
78 #include "third_party/skia/include/core/SkRefCnt.h" // nogncheck
79 #include "third_party/skia/include/core/SkStream.h" // nogncheck
80 #include "third_party/skia/include/core/SkSurface.h" // nogncheck
81
82 #ifdef _WIN32
83 #include "third_party/skia/include/docs/SkXPSDocument.h" // nogncheck
84 #endif
85
86 #ifdef BUILD_WITH_CHROMIUM
87 #include "testing/chromium_support/discardable_memory_allocator.h" // nogncheck
88 #endif
89 #endif // PDF_ENABLE_SKIA
90
91 #ifdef PDF_ENABLE_V8
92 #include "testing/v8_initializer.h"
93 #include "v8/include/libplatform/libplatform.h"
94 #include "v8/include/v8-array-buffer.h"
95 #include "v8/include/v8-isolate.h"
96 #include "v8/include/v8-platform.h"
97 #include "v8/include/v8-snapshot.h"
98 #endif // PDF_ENABLE_V8
99
100 #ifdef _WIN32
101 #define access _access
102 #define snprintf _snprintf
103 #define R_OK 4
104 #endif
105
106 // wordexp is a POSIX function that is only available on macOS and non-Android
107 // Linux platforms.
108 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
109 #define WORDEXP_AVAILABLE
110 #endif
111
112 #ifdef WORDEXP_AVAILABLE
113 #include <wordexp.h>
114 #endif // WORDEXP_AVAILABLE
115
116 namespace {
117
118 enum class RendererType {
119 kDefault,
120 kAgg,
121 #ifdef _WIN32
122 kGdi,
123 #endif // _WIN32
124 #if defined(PDF_ENABLE_SKIA)
125 kSkia,
126 #endif // defined(PDF_ENABLE_SKIA)
127 };
128
129 enum class OutputFormat {
130 kNone,
131 kPageInfo,
132 kStructure,
133 kText,
134 kPpm,
135 kPng,
136 kAnnot,
137 #ifdef _WIN32
138 kBmp,
139 kEmf,
140 kPs2,
141 kPs3,
142 kPs3Type42,
143 #endif
144 #ifdef PDF_ENABLE_SKIA
145 kSkp,
146 #ifdef _WIN32
147 kXps,
148 #endif // _WIN32
149 #endif // PDF_ENABLE_SKIA
150 };
151
152 struct Options {
153 Options() = default;
154
155 bool show_config = false;
156 bool show_metadata = false;
157 bool send_events = false;
158 bool use_load_mem_document = false;
159 bool render_oneshot = false;
160 bool lcd_text = false;
161 bool no_nativetext = false;
162 bool grayscale = false;
163 bool forced_color = false;
164 bool fill_to_stroke = false;
165 bool limit_cache = false;
166 bool force_halftone = false;
167 bool printing = false;
168 bool no_smoothtext = false;
169 bool no_smoothimage = false;
170 bool no_smoothpath = false;
171 bool reverse_byte_order = false;
172 bool save_attachments = false;
173 bool save_images = false;
174 bool save_rendered_images = false;
175 bool save_thumbnails = false;
176 bool save_thumbnails_decoded = false;
177 bool save_thumbnails_raw = false;
178 RendererType use_renderer_type = RendererType::kDefault;
179 #ifdef PDF_ENABLE_V8
180 bool disable_javascript = false;
181 std::string js_flags; // Extra flags to pass to v8 init.
182 #ifdef PDF_ENABLE_XFA
183 bool disable_xfa = false;
184 #endif // PDF_ENABLE_XFA
185 #endif // PDF_ENABLE_V8
186 bool pages = false;
187 bool md5 = false;
188 #ifdef ENABLE_CALLGRIND
189 bool callgrind_delimiters = false;
190 #endif
191 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
192 bool linux_no_system_fonts = false;
193 #endif
194 bool croscore_font_names = false;
195 OutputFormat output_format = OutputFormat::kNone;
196 std::string password;
197 std::string scale_factor_as_string;
198 std::string exe_path;
199 std::string bin_directory;
200 std::string font_directory;
201 int first_page = 0; // First 0-based page number to renderer.
202 int last_page = 0; // Last 0-based page number to renderer.
203 time_t time = -1;
204 };
205
PageRenderFlagsFromOptions(const Options & options)206 int PageRenderFlagsFromOptions(const Options& options) {
207 int flags = FPDF_ANNOT;
208 if (options.lcd_text)
209 flags |= FPDF_LCD_TEXT;
210 if (options.no_nativetext)
211 flags |= FPDF_NO_NATIVETEXT;
212 if (options.grayscale)
213 flags |= FPDF_GRAYSCALE;
214 if (options.fill_to_stroke)
215 flags |= FPDF_CONVERT_FILL_TO_STROKE;
216 if (options.limit_cache)
217 flags |= FPDF_RENDER_LIMITEDIMAGECACHE;
218 if (options.force_halftone)
219 flags |= FPDF_RENDER_FORCEHALFTONE;
220 if (options.printing)
221 flags |= FPDF_PRINTING;
222 if (options.no_smoothtext)
223 flags |= FPDF_RENDER_NO_SMOOTHTEXT;
224 if (options.no_smoothimage)
225 flags |= FPDF_RENDER_NO_SMOOTHIMAGE;
226 if (options.no_smoothpath)
227 flags |= FPDF_RENDER_NO_SMOOTHPATH;
228 if (options.reverse_byte_order)
229 flags |= FPDF_REVERSE_BYTE_ORDER;
230 return flags;
231 }
232
ExpandDirectoryPath(const std::string & path)233 std::optional<std::string> ExpandDirectoryPath(const std::string& path) {
234 #if defined(WORDEXP_AVAILABLE)
235 wordexp_t expansion;
236 if (wordexp(path.c_str(), &expansion, 0) != 0 || expansion.we_wordc < 1) {
237 wordfree(&expansion);
238 return {};
239 }
240 // Need to contruct the return value before hand, since wordfree will
241 // deallocate |expansion|.
242 std::optional<std::string> ret_val = {expansion.we_wordv[0]};
243 wordfree(&expansion);
244 return ret_val;
245 #else
246 return {path};
247 #endif // WORDEXP_AVAILABLE
248 }
249
GetCustomFontPath(const Options & options)250 std::optional<const char*> GetCustomFontPath(const Options& options) {
251 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
252 // Set custom font path to an empty path. This avoids the fallback to default
253 // font paths.
254 if (options.linux_no_system_fonts)
255 return nullptr;
256 #endif
257
258 // No custom font path. Use default.
259 if (options.font_directory.empty())
260 return std::nullopt;
261
262 // Set custom font path to |options.font_directory|.
263 return options.font_directory.c_str();
264 }
265
266 struct FPDF_FORMFILLINFO_PDFiumTest final : public FPDF_FORMFILLINFO {
267 // Hold a map of the currently loaded pages in order to avoid them
268 // to get loaded twice.
269 std::map<int, ScopedFPDFPage> loaded_pages;
270
271 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
272 // make use of it.
273 FPDF_FORMHANDLE form_handle;
274 };
275
ToPDFiumTestFormFillInfo(FPDF_FORMFILLINFO * form_fill_info)276 FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
277 FPDF_FORMFILLINFO* form_fill_info) {
278 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
279 }
280
OutputMD5Hash(const char * file_name,pdfium::span<const uint8_t> output)281 void OutputMD5Hash(const char* file_name, pdfium::span<const uint8_t> output) {
282 // Get the MD5 hash and write it to stdout.
283 std::string hash = GenerateMD5Base16(output);
284 printf("MD5:%s:%s\n", file_name, hash.c_str());
285 }
286
287 #ifdef PDF_ENABLE_V8
288
289 struct V8IsolateDeleter {
operator ()__anonad4446c00111::V8IsolateDeleter290 inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); }
291 };
292
293 // These example JS platform callback handlers are entirely optional,
294 // and exist here to show the flow of information from a document back
295 // to the embedder.
ExampleAppAlert(IPDF_JSPLATFORM *,FPDF_WIDESTRING msg,FPDF_WIDESTRING title,int type,int icon)296 int ExampleAppAlert(IPDF_JSPLATFORM*,
297 FPDF_WIDESTRING msg,
298 FPDF_WIDESTRING title,
299 int type,
300 int icon) {
301 printf("%ls", GetPlatformWString(title).c_str());
302 if (icon || type)
303 printf("[icon=%d,type=%d]", icon, type);
304 printf(": %ls\n", GetPlatformWString(msg).c_str());
305 return 0;
306 }
307
ExampleAppBeep(IPDF_JSPLATFORM *,int type)308 void ExampleAppBeep(IPDF_JSPLATFORM*, int type) {
309 printf("BEEP!!! %d\n", type);
310 }
311
ExampleAppResponse(IPDF_JSPLATFORM *,FPDF_WIDESTRING question,FPDF_WIDESTRING title,FPDF_WIDESTRING default_value,FPDF_WIDESTRING label,FPDF_BOOL is_password,void * response,int length)312 int ExampleAppResponse(IPDF_JSPLATFORM*,
313 FPDF_WIDESTRING question,
314 FPDF_WIDESTRING title,
315 FPDF_WIDESTRING default_value,
316 FPDF_WIDESTRING label,
317 FPDF_BOOL is_password,
318 void* response,
319 int length) {
320 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
321 GetPlatformWString(title).c_str(),
322 GetPlatformWString(question).c_str(),
323 GetPlatformWString(default_value).c_str(),
324 GetPlatformWString(label).c_str(), is_password, length);
325
326 // UTF-16, always LE regardless of platform.
327 auto* ptr = static_cast<uint8_t*>(response);
328 ptr[0] = 'N';
329 ptr[1] = 0;
330 ptr[2] = 'o';
331 ptr[3] = 0;
332 return 4;
333 }
334
ExampleDocGetFilePath(IPDF_JSPLATFORM *,void * file_path,int length)335 int ExampleDocGetFilePath(IPDF_JSPLATFORM*, void* file_path, int length) {
336 static const char kPath[] = "myfile.pdf";
337 constexpr int kRequired = static_cast<int>(sizeof(kPath));
338 if (file_path && length >= kRequired)
339 memcpy(file_path, kPath, kRequired);
340 return kRequired;
341 }
342
ExampleDocMail(IPDF_JSPLATFORM *,void * mailData,int length,FPDF_BOOL UI,FPDF_WIDESTRING To,FPDF_WIDESTRING Subject,FPDF_WIDESTRING CC,FPDF_WIDESTRING BCC,FPDF_WIDESTRING Msg)343 void ExampleDocMail(IPDF_JSPLATFORM*,
344 void* mailData,
345 int length,
346 FPDF_BOOL UI,
347 FPDF_WIDESTRING To,
348 FPDF_WIDESTRING Subject,
349 FPDF_WIDESTRING CC,
350 FPDF_WIDESTRING BCC,
351 FPDF_WIDESTRING Msg) {
352 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
353 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
354 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
355 GetPlatformWString(Msg).c_str());
356 }
357
ExampleDocPrint(IPDF_JSPLATFORM *,FPDF_BOOL bUI,int nStart,int nEnd,FPDF_BOOL bSilent,FPDF_BOOL bShrinkToFit,FPDF_BOOL bPrintAsImage,FPDF_BOOL bReverse,FPDF_BOOL bAnnotations)358 void ExampleDocPrint(IPDF_JSPLATFORM*,
359 FPDF_BOOL bUI,
360 int nStart,
361 int nEnd,
362 FPDF_BOOL bSilent,
363 FPDF_BOOL bShrinkToFit,
364 FPDF_BOOL bPrintAsImage,
365 FPDF_BOOL bReverse,
366 FPDF_BOOL bAnnotations) {
367 printf("Doc Print: %d, %d, %d, %d, %d, %d, %d, %d\n", bUI, nStart, nEnd,
368 bSilent, bShrinkToFit, bPrintAsImage, bReverse, bAnnotations);
369 }
370
ExampleDocSubmitForm(IPDF_JSPLATFORM *,void * formData,int length,FPDF_WIDESTRING url)371 void ExampleDocSubmitForm(IPDF_JSPLATFORM*,
372 void* formData,
373 int length,
374 FPDF_WIDESTRING url) {
375 printf("Doc Submit Form: url=%ls + %d data bytes:\n",
376 GetPlatformWString(url).c_str(), length);
377 uint8_t* ptr = reinterpret_cast<uint8_t*>(formData);
378 for (int i = 0; i < length; ++i)
379 printf(" %02x", ptr[i]);
380 printf("\n");
381 }
382
ExampleDocGotoPage(IPDF_JSPLATFORM *,int page_number)383 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
384 printf("Goto Page: %d\n", page_number);
385 }
386
ExampleFieldBrowse(IPDF_JSPLATFORM *,void * file_path,int length)387 int ExampleFieldBrowse(IPDF_JSPLATFORM*, void* file_path, int length) {
388 static const char kPath[] = "selected.txt";
389 constexpr int kRequired = static_cast<int>(sizeof(kPath));
390 if (file_path && length >= kRequired)
391 memcpy(file_path, kPath, kRequired);
392 return kRequired;
393 }
394 #endif // PDF_ENABLE_V8
395
396 #ifdef PDF_ENABLE_XFA
ExamplePopupMenu(FPDF_FORMFILLINFO * pInfo,FPDF_PAGE page,FPDF_WIDGET always_null,int flags,float x,float y)397 FPDF_BOOL ExamplePopupMenu(FPDF_FORMFILLINFO* pInfo,
398 FPDF_PAGE page,
399 FPDF_WIDGET always_null,
400 int flags,
401 float x,
402 float y) {
403 printf("Popup: x=%2.1f, y=%2.1f, flags=0x%x\n", x, y, flags);
404 return true;
405 }
406 #endif // PDF_ENABLE_XFA
407
ExampleNamedAction(FPDF_FORMFILLINFO * pInfo,FPDF_BYTESTRING name)408 void ExampleNamedAction(FPDF_FORMFILLINFO* pInfo, FPDF_BYTESTRING name) {
409 printf("Execute named action: %s\n", name);
410 }
411
ExampleUnsupportedHandler(UNSUPPORT_INFO *,int type)412 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
413 std::string feature = "Unknown";
414 switch (type) {
415 case FPDF_UNSP_DOC_XFAFORM:
416 feature = "XFA";
417 break;
418 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
419 feature = "Portfolios_Packages";
420 break;
421 case FPDF_UNSP_DOC_ATTACHMENT:
422 case FPDF_UNSP_ANNOT_ATTACHMENT:
423 feature = "Attachment";
424 break;
425 case FPDF_UNSP_DOC_SECURITY:
426 feature = "Rights_Management";
427 break;
428 case FPDF_UNSP_DOC_SHAREDREVIEW:
429 feature = "Shared_Review";
430 break;
431 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
432 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
433 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
434 feature = "Shared_Form";
435 break;
436 case FPDF_UNSP_ANNOT_3DANNOT:
437 feature = "3D";
438 break;
439 case FPDF_UNSP_ANNOT_MOVIE:
440 feature = "Movie";
441 break;
442 case FPDF_UNSP_ANNOT_SOUND:
443 feature = "Sound";
444 break;
445 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
446 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
447 feature = "Screen";
448 break;
449 case FPDF_UNSP_ANNOT_SIG:
450 feature = "Digital_Signature";
451 break;
452 }
453 printf("Unsupported feature: %s.\n", feature.c_str());
454 }
455
ParseCommandLine(const std::vector<std::string> & args,Options * options,std::vector<std::string> * files)456 bool ParseCommandLine(const std::vector<std::string>& args,
457 Options* options,
458 std::vector<std::string>* files) {
459 if (args.empty())
460 return false;
461
462 options->exe_path = args[0];
463 size_t cur_idx = 1;
464 std::string value;
465 for (; cur_idx < args.size(); ++cur_idx) {
466 const std::string& cur_arg = args[cur_idx];
467 if (cur_arg == "--show-config") {
468 options->show_config = true;
469 } else if (cur_arg == "--show-metadata") {
470 options->show_metadata = true;
471 } else if (cur_arg == "--send-events") {
472 options->send_events = true;
473 } else if (cur_arg == "--mem-document") {
474 options->use_load_mem_document = true;
475 } else if (cur_arg == "--render-oneshot") {
476 options->render_oneshot = true;
477 } else if (cur_arg == "--lcd-text") {
478 options->lcd_text = true;
479 } else if (cur_arg == "--no-nativetext") {
480 options->no_nativetext = true;
481 } else if (cur_arg == "--grayscale") {
482 options->grayscale = true;
483 } else if (cur_arg == "--forced-color") {
484 options->forced_color = true;
485 } else if (cur_arg == "--fill-to-stroke") {
486 options->fill_to_stroke = true;
487 } else if (cur_arg == "--limit-cache") {
488 options->limit_cache = true;
489 } else if (cur_arg == "--force-halftone") {
490 options->force_halftone = true;
491 } else if (cur_arg == "--printing") {
492 options->printing = true;
493 } else if (cur_arg == "--no-smoothtext") {
494 options->no_smoothtext = true;
495 } else if (cur_arg == "--no-smoothimage") {
496 options->no_smoothimage = true;
497 } else if (cur_arg == "--no-smoothpath") {
498 options->no_smoothpath = true;
499 } else if (cur_arg == "--reverse-byte-order") {
500 options->reverse_byte_order = true;
501 } else if (cur_arg == "--save-attachments") {
502 options->save_attachments = true;
503 } else if (cur_arg == "--save-images") {
504 if (options->save_rendered_images) {
505 fprintf(stderr,
506 "--save-rendered-images conflicts with --save-images\n");
507 return false;
508 }
509 options->save_images = true;
510 } else if (cur_arg == "--save-rendered-images") {
511 if (options->save_images) {
512 fprintf(stderr,
513 "--save-images conflicts with --save-rendered-images\n");
514 return false;
515 }
516 options->save_rendered_images = true;
517 } else if (cur_arg == "--save-thumbs") {
518 options->save_thumbnails = true;
519 } else if (cur_arg == "--save-thumbs-dec") {
520 options->save_thumbnails_decoded = true;
521 } else if (cur_arg == "--save-thumbs-raw") {
522 options->save_thumbnails_raw = true;
523 } else if (ParseSwitchKeyValue(cur_arg, "--use-renderer=", &value)) {
524 if (options->use_renderer_type != RendererType::kDefault) {
525 fprintf(stderr, "Duplicate --use-renderer argument\n");
526 return false;
527 }
528 if (value == "agg") {
529 options->use_renderer_type = RendererType::kAgg;
530 #ifdef _WIN32
531 } else if (value == "gdi") {
532 options->use_renderer_type = RendererType::kGdi;
533 #endif // _WIN32
534 #if defined(PDF_ENABLE_SKIA)
535 } else if (value == "skia") {
536 options->use_renderer_type = RendererType::kSkia;
537 #endif // defined(PDF_ENABLE_SKIA)
538 } else {
539 fprintf(stderr, "Invalid --use-renderer argument\n");
540 return false;
541 }
542 #ifdef PDF_ENABLE_V8
543 } else if (cur_arg == "--disable-javascript") {
544 options->disable_javascript = true;
545 } else if (ParseSwitchKeyValue(cur_arg, "--js-flags=", &value)) {
546 if (!options->js_flags.empty()) {
547 fprintf(stderr, "Duplicate --js-flags argument\n");
548 return false;
549 }
550 options->js_flags = value;
551 #ifdef PDF_ENABLE_XFA
552 } else if (cur_arg == "--disable-xfa") {
553 options->disable_xfa = true;
554 #endif // PDF_ENABLE_XFA
555 #endif // PDF_ENABLE_V8
556 #ifdef ENABLE_CALLGRIND
557 } else if (cur_arg == "--callgrind-delim") {
558 options->callgrind_delimiters = true;
559 #endif
560 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
561 } else if (cur_arg == "--no-system-fonts") {
562 options->linux_no_system_fonts = true;
563 #endif
564 } else if (cur_arg == "--croscore-font-names") {
565 options->croscore_font_names = true;
566 } else if (cur_arg == "--ppm") {
567 if (options->output_format != OutputFormat::kNone) {
568 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
569 return false;
570 }
571 options->output_format = OutputFormat::kPpm;
572 } else if (cur_arg == "--png") {
573 if (options->output_format != OutputFormat::kNone) {
574 fprintf(stderr, "Duplicate or conflicting --png argument\n");
575 return false;
576 }
577 options->output_format = OutputFormat::kPng;
578 } else if (cur_arg == "--txt") {
579 if (options->output_format != OutputFormat::kNone) {
580 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
581 return false;
582 }
583 options->output_format = OutputFormat::kText;
584 } else if (cur_arg == "--annot") {
585 if (options->output_format != OutputFormat::kNone) {
586 fprintf(stderr, "Duplicate or conflicting --annot argument\n");
587 return false;
588 }
589 options->output_format = OutputFormat::kAnnot;
590 #ifdef PDF_ENABLE_SKIA
591 } else if (cur_arg == "--skp") {
592 if (options->output_format != OutputFormat::kNone) {
593 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
594 return false;
595 }
596 options->output_format = OutputFormat::kSkp;
597 #ifdef _WIN32
598 } else if (cur_arg == "--xps") {
599 if (options->output_format != OutputFormat::kNone) {
600 fprintf(stderr, "Duplicate or conflicting --xps argument\n");
601 return false;
602 }
603 options->output_format = OutputFormat::kXps;
604 #endif // _WIN32
605 #endif // PDF_ENABLE_SKIA
606 } else if (ParseSwitchKeyValue(cur_arg, "--font-dir=", &value)) {
607 if (!options->font_directory.empty()) {
608 fprintf(stderr, "Duplicate --font-dir argument\n");
609 return false;
610 }
611 std::string path = value;
612 std::optional<std::string> expanded_path = ExpandDirectoryPath(path);
613 if (!expanded_path.has_value()) {
614 fprintf(stderr, "Failed to expand --font-dir, %s\n", path.c_str());
615 return false;
616 }
617
618 if (!PathService::DirectoryExists(expanded_path.value())) {
619 fprintf(stderr, "--font-dir, %s, appears to not be a directory\n",
620 path.c_str());
621 return false;
622 }
623
624 options->font_directory = expanded_path.value();
625
626 #ifdef _WIN32
627 } else if (cur_arg == "--emf") {
628 if (options->output_format != OutputFormat::kNone) {
629 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
630 return false;
631 }
632 options->output_format = OutputFormat::kEmf;
633 } else if (cur_arg == "--ps2") {
634 if (options->output_format != OutputFormat::kNone) {
635 fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
636 return false;
637 }
638 options->output_format = OutputFormat::kPs2;
639 } else if (cur_arg == "--ps3") {
640 if (options->output_format != OutputFormat::kNone) {
641 fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
642 return false;
643 }
644 options->output_format = OutputFormat::kPs3;
645 } else if (cur_arg == "--ps3-type42") {
646 if (options->output_format != OutputFormat::kNone) {
647 fprintf(stderr, "Duplicate or conflicting --ps3-type42 argument\n");
648 return false;
649 }
650 options->output_format = OutputFormat::kPs3Type42;
651 } else if (cur_arg == "--bmp") {
652 if (options->output_format != OutputFormat::kNone) {
653 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
654 return false;
655 }
656 options->output_format = OutputFormat::kBmp;
657 #endif // _WIN32
658
659 #ifdef PDF_ENABLE_V8
660 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
661 } else if (ParseSwitchKeyValue(cur_arg, "--bin-dir=", &value)) {
662 if (!options->bin_directory.empty()) {
663 fprintf(stderr, "Duplicate --bin-dir argument\n");
664 return false;
665 }
666 std::string path = value;
667 std::optional<std::string> expanded_path = ExpandDirectoryPath(path);
668 if (!expanded_path.has_value()) {
669 fprintf(stderr, "Failed to expand --bin-dir, %s\n", path.c_str());
670 return false;
671 }
672 options->bin_directory = expanded_path.value();
673 #endif // V8_USE_EXTERNAL_STARTUP_DATA
674 #endif // PDF_ENABLE_V8
675
676 } else if (ParseSwitchKeyValue(cur_arg, "--password=", &value)) {
677 if (!options->password.empty()) {
678 fprintf(stderr, "Duplicate --password argument\n");
679 return false;
680 }
681 options->password = value;
682 } else if (ParseSwitchKeyValue(cur_arg, "--scale=", &value)) {
683 if (!options->scale_factor_as_string.empty()) {
684 fprintf(stderr, "Duplicate --scale argument\n");
685 return false;
686 }
687 options->scale_factor_as_string = value;
688 } else if (cur_arg == "--show-pageinfo") {
689 if (options->output_format != OutputFormat::kNone) {
690 fprintf(stderr, "Duplicate or conflicting --show-pageinfo argument\n");
691 return false;
692 }
693 options->output_format = OutputFormat::kPageInfo;
694 } else if (cur_arg == "--show-structure") {
695 if (options->output_format != OutputFormat::kNone) {
696 fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
697 return false;
698 }
699 options->output_format = OutputFormat::kStructure;
700 } else if (ParseSwitchKeyValue(cur_arg, "--pages=", &value)) {
701 if (options->pages) {
702 fprintf(stderr, "Duplicate --pages argument\n");
703 return false;
704 }
705 options->pages = true;
706 const std::string pages_string = value;
707 size_t first_dash = pages_string.find('-');
708 if (first_dash == std::string::npos) {
709 std::stringstream(pages_string) >> options->first_page;
710 options->last_page = options->first_page;
711 } else {
712 std::stringstream(pages_string.substr(0, first_dash)) >>
713 options->first_page;
714 std::stringstream(pages_string.substr(first_dash + 1)) >>
715 options->last_page;
716 }
717 } else if (cur_arg == "--md5") {
718 options->md5 = true;
719 } else if (ParseSwitchKeyValue(cur_arg, "--time=", &value)) {
720 if (options->time > -1) {
721 fprintf(stderr, "Duplicate --time argument\n");
722 return false;
723 }
724 const std::string time_string = value;
725 std::stringstream(time_string) >> options->time;
726 if (options->time < 0) {
727 fprintf(stderr, "Invalid --time argument, must be non-negative\n");
728 return false;
729 }
730 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
731 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
732 return false;
733 } else {
734 break;
735 }
736 }
737 for (size_t i = cur_idx; i < args.size(); i++)
738 files->push_back(args[i]);
739
740 return true;
741 }
742
PrintLastError()743 void PrintLastError() {
744 unsigned long err = FPDF_GetLastError();
745 fprintf(stderr, "Load pdf docs unsuccessful: ");
746 switch (err) {
747 case FPDF_ERR_SUCCESS:
748 fprintf(stderr, "Success");
749 break;
750 case FPDF_ERR_UNKNOWN:
751 fprintf(stderr, "Unknown error");
752 break;
753 case FPDF_ERR_FILE:
754 fprintf(stderr, "File not found or could not be opened");
755 break;
756 case FPDF_ERR_FORMAT:
757 fprintf(stderr, "File not in PDF format or corrupted");
758 break;
759 case FPDF_ERR_PASSWORD:
760 fprintf(stderr, "Password required or incorrect password");
761 break;
762 case FPDF_ERR_SECURITY:
763 fprintf(stderr, "Unsupported security scheme");
764 break;
765 case FPDF_ERR_PAGE:
766 fprintf(stderr, "Page not found or content error");
767 break;
768 default:
769 fprintf(stderr, "Unknown error %ld", err);
770 }
771 fprintf(stderr, ".\n");
772 }
773
Is_Data_Avail(FX_FILEAVAIL * avail,size_t offset,size_t size)774 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
775 return true;
776 }
777
Add_Segment(FX_DOWNLOADHINTS * hints,size_t offset,size_t size)778 void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
779
GetPageForIndex(FPDF_FORMFILLINFO * param,FPDF_DOCUMENT doc,int index)780 FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
781 FPDF_DOCUMENT doc,
782 int index) {
783 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
784 ToPDFiumTestFormFillInfo(param);
785 auto& loaded_pages = form_fill_info->loaded_pages;
786 auto iter = loaded_pages.find(index);
787 if (iter != loaded_pages.end())
788 return iter->second.get();
789
790 ScopedFPDFPage page(FPDF_LoadPage(doc, index));
791 if (!page)
792 return nullptr;
793
794 // Mark the page as loaded first to prevent infinite recursion.
795 FPDF_PAGE page_ptr = page.get();
796 loaded_pages[index] = std::move(page);
797
798 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
799 FORM_OnAfterLoadPage(page_ptr, form_handle);
800 FORM_DoPageAAction(page_ptr, form_handle, FPDFPAGE_AACTION_OPEN);
801 return page_ptr;
802 }
803
804 // Note, for a client using progressive rendering you'd want to determine if you
805 // need the rendering to pause instead of always saying |true|. This is for
806 // testing to force the renderer to break whenever possible.
NeedToPauseNow(IFSDK_PAUSE * p)807 FPDF_BOOL NeedToPauseNow(IFSDK_PAUSE* p) {
808 return true;
809 }
810
811 class Processor final {
812 public:
Processor(const Options * options,const std::function<void ()> * idler)813 Processor(const Options* options, const std::function<void()>* idler)
814 : options_(options), idler_(idler) {
815 DCHECK(options_);
816 DCHECK(idler_);
817 }
818
options() const819 const Options& options() const { return *options_; }
idler() const820 const std::function<void()>& idler() const { return *idler_; }
821
822 #ifdef _WIN32
com_factory()823 ComFactory& com_factory() { return com_factory_; }
824 #endif // _WIN32
825
826 // Invokes `idler()`.
Idle() const827 void Idle() const { idler()(); }
828
829 void ProcessPdf(const std::string& name,
830 pdfium::span<const uint8_t> data,
831 const std::string& events);
832
833 private:
834 const Options* options_;
835 const std::function<void()>* idler_;
836
837 #ifdef _WIN32
838 ComFactory com_factory_;
839 #endif // _WIN32
840 };
841
842 class PdfProcessor final {
843 public:
PdfProcessor(Processor * processor,const std::string * name,const std::string * events,FPDF_DOCUMENT doc,FPDF_FORMHANDLE form,FPDF_FORMFILLINFO_PDFiumTest * form_fill_info)844 PdfProcessor(Processor* processor,
845 const std::string* name,
846 const std::string* events,
847 FPDF_DOCUMENT doc,
848 FPDF_FORMHANDLE form,
849 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info)
850 : processor_(processor),
851 name_(name),
852 events_(events),
853 doc_(doc),
854 form_(form),
855 form_fill_info_(form_fill_info) {
856 DCHECK(processor_);
857 DCHECK(name_);
858 DCHECK(events_);
859 DCHECK(doc_);
860 DCHECK(form_);
861 DCHECK(form_fill_info_);
862 }
863
864 bool ProcessPage(int page_index);
865
866 private:
867 // Per processor state.
options() const868 const Options& options() const { return processor_->options(); }
idler() const869 const std::function<void()>& idler() const { return processor_->idler(); }
870
871 #ifdef _WIN32
com_factory()872 ComFactory& com_factory() { return processor_->com_factory(); }
873 #endif // _WIN32
874
875 // Per PDF state.
name() const876 const std::string& name() const { return *name_; }
events() const877 const std::string& events() const { return *events_; }
doc() const878 FPDF_DOCUMENT doc() const { return doc_; }
form() const879 FPDF_FORMHANDLE form() const { return form_; }
880
881 // Invokes `idler()`.
Idle() const882 void Idle() const { idler()(); }
883
GetPage(int page_index) const884 FPDF_PAGE GetPage(int page_index) const {
885 return GetPageForIndex(form_fill_info_, doc_, page_index);
886 }
887
888 Processor* processor_;
889 const std::string* name_;
890 const std::string* events_;
891 FPDF_DOCUMENT doc_;
892 FPDF_FORMHANDLE form_;
893 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info_;
894 };
895
896 // Page renderer with bitmap output.
897 class BitmapPageRenderer : public PageRenderer {
898 public:
899 // Function type that writes rendered output to a file, returning `false` on
900 // failure.
901 //
902 // Intended to wrap functions from `pdfium_test_write_helper.h`.
903 using PageWriter = std::function<bool(BitmapPageRenderer& renderer,
904 const std::string& name,
905 int page_index,
906 bool md5)>;
907
908 // Wraps a `PageWriter` around a function pointer that writes the text page.
WrapPageWriter(void (* text_page_writer)(FPDF_TEXTPAGE text_page,const char * pdf_name,int num))909 static PageWriter WrapPageWriter(
910 void (*text_page_writer)(FPDF_TEXTPAGE text_page,
911 const char* pdf_name,
912 int num)) {
913 return [text_page_writer](BitmapPageRenderer& renderer,
914 const std::string& name, int page_index,
915 bool /*md5*/) {
916 ScopedFPDFTextPage text_page(FPDFText_LoadPage(renderer.page()));
917 if (!text_page) {
918 return false;
919 }
920
921 text_page_writer(text_page.get(), name.c_str(), page_index);
922 return true;
923 };
924 }
925
926 // Wraps a `PageWriter` around a function pointer that writes the page.
WrapPageWriter(void (* page_writer)(FPDF_PAGE page,const char * pdf_name,int num))927 static PageWriter WrapPageWriter(void (*page_writer)(FPDF_PAGE page,
928 const char* pdf_name,
929 int num)) {
930 return [page_writer](BitmapPageRenderer& renderer, const std::string& name,
931 int page_index, bool /*md5*/) {
932 page_writer(renderer.page(), name.c_str(), page_index);
933 return true;
934 };
935 }
936
937 // Wraps a `PageWriter` around a function pointer that writes the rasterized
938 // bitmap to an image file.
WrapPageWriter(std::string (* bitmap_writer)(const char * pdf_name,int num,void * buffer,int stride,int width,int height))939 static PageWriter WrapPageWriter(
940 std::string (*bitmap_writer)(const char* pdf_name,
941 int num,
942 void* buffer,
943 int stride,
944 int width,
945 int height)) {
946 return [bitmap_writer](BitmapPageRenderer& renderer,
947 const std::string& name, int page_index, bool md5) {
948 int stride = FPDFBitmap_GetStride(renderer.bitmap());
949 void* buffer = FPDFBitmap_GetBuffer(renderer.bitmap());
950 std::string image_file_name = bitmap_writer(
951 name.c_str(), page_index, buffer, /*stride=*/stride,
952 /*width=*/renderer.width(), /*height=*/renderer.height());
953 if (image_file_name.empty()) {
954 return false;
955 }
956
957 if (md5) {
958 // Write the filename and the MD5 of the buffer to stdout.
959 OutputMD5Hash(image_file_name.c_str(),
960 UNSAFE_TODO(pdfium::make_span(
961 static_cast<const uint8_t*>(buffer),
962 static_cast<size_t>(stride) * renderer.height())));
963 }
964 return true;
965 };
966 }
967
HasOutput() const968 bool HasOutput() const override { return !!bitmap_; }
969
Finish(FPDF_FORMHANDLE form)970 void Finish(FPDF_FORMHANDLE form) override {
971 FPDF_FFLDraw(form, bitmap(), page(), /*start_x=*/0, /*start_y=*/0,
972 /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
973 /*flags=*/flags());
974 Idle();
975 }
976
Write(const std::string & name,int page_index,bool md5)977 bool Write(const std::string& name, int page_index, bool md5) override {
978 return writer_ && writer_(*this, name, page_index, md5);
979 }
980
981 protected:
BitmapPageRenderer(FPDF_PAGE page,int width,int height,int flags,const std::function<void ()> & idler,PageWriter writer)982 BitmapPageRenderer(FPDF_PAGE page,
983 int width,
984 int height,
985 int flags,
986 const std::function<void()>& idler,
987 PageWriter writer)
988 : PageRenderer(page, /*width=*/width, /*height=*/height, /*flags=*/flags),
989 idler_(idler),
990 writer_(std::move(writer)) {}
991
InitializeBitmap(void * first_scan)992 bool InitializeBitmap(void* first_scan) {
993 bool alpha = FPDFPage_HasTransparency(page());
994 bitmap_.reset(FPDFBitmap_CreateEx(
995 /*width=*/width(), /*height=*/height(),
996 /*format=*/alpha ? FPDFBitmap_BGRA : FPDFBitmap_BGRx, first_scan,
997 /*stride=*/width() * sizeof(uint32_t)));
998 if (!bitmap()) {
999 return false;
1000 }
1001
1002 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
1003 return FPDFBitmap_FillRect(bitmap(), /*left=*/0, /*top=*/0,
1004 /*width=*/width(),
1005 /*height=*/height(), /*color=*/fill_color);
1006 }
1007
ResetBitmap()1008 void ResetBitmap() { bitmap_.reset(); }
1009
Idle() const1010 void Idle() const { idler_(); }
bitmap()1011 FPDF_BITMAP bitmap() { return bitmap_.get(); }
1012
1013 private:
1014 const std::function<void()>& idler_;
1015 PageWriter writer_;
1016 ScopedFPDFBitmap bitmap_;
1017 };
1018
1019 // Bitmap page renderer completing in a single operation.
1020 class OneShotBitmapPageRenderer : public BitmapPageRenderer {
1021 public:
OneShotBitmapPageRenderer(FPDF_PAGE page,int width,int height,int flags,const std::function<void ()> & idler,PageWriter writer)1022 OneShotBitmapPageRenderer(FPDF_PAGE page,
1023 int width,
1024 int height,
1025 int flags,
1026 const std::function<void()>& idler,
1027 PageWriter writer)
1028 : BitmapPageRenderer(page,
1029 /*width=*/width,
1030 /*height=*/height,
1031 /*flags=*/flags,
1032 idler,
1033 std::move(writer)) {}
1034
Start()1035 bool Start() override {
1036 if (!InitializeBitmap(/*first_scan=*/nullptr)) {
1037 return false;
1038 }
1039
1040 // Note, client programs probably want to use this method instead of the
1041 // progressive calls. The progressive calls are if you need to pause the
1042 // rendering to update the UI, the PDF renderer will break when possible.
1043 FPDF_RenderPageBitmap(bitmap(), page(), /*start_x=*/0, /*start_y=*/0,
1044 /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
1045 /*flags=*/flags());
1046 return true;
1047 }
1048 };
1049
1050 // Bitmap page renderer completing over multiple operations.
1051 class ProgressiveBitmapPageRenderer : public BitmapPageRenderer {
1052 public:
ProgressiveBitmapPageRenderer(FPDF_PAGE page,int width,int height,int flags,const std::function<void ()> & idler,PageWriter writer,const FPDF_COLORSCHEME * color_scheme)1053 ProgressiveBitmapPageRenderer(FPDF_PAGE page,
1054 int width,
1055 int height,
1056 int flags,
1057 const std::function<void()>& idler,
1058 PageWriter writer,
1059 const FPDF_COLORSCHEME* color_scheme)
1060 : BitmapPageRenderer(page,
1061 /*width=*/width,
1062 /*height=*/height,
1063 /*flags=*/flags,
1064 idler,
1065 std::move(writer)),
1066 color_scheme_(color_scheme) {
1067 pause_.version = 1;
1068 pause_.NeedToPauseNow = &NeedToPauseNow;
1069 }
1070
Start()1071 bool Start() override {
1072 if (!InitializeBitmap(/*first_scan=*/nullptr)) {
1073 return false;
1074 }
1075
1076 if (FPDF_RenderPageBitmapWithColorScheme_Start(
1077 bitmap(), page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(),
1078 /*size_y=*/height(), /*rotate=*/0, /*flags=*/flags(), color_scheme_,
1079 &pause_) == FPDF_RENDER_TOBECONTINUED) {
1080 to_be_continued_ = true;
1081 }
1082 return true;
1083 }
1084
Continue()1085 bool Continue() override {
1086 if (to_be_continued_) {
1087 to_be_continued_ = (FPDF_RenderPage_Continue(page(), &pause_) ==
1088 FPDF_RENDER_TOBECONTINUED);
1089 }
1090 return to_be_continued_;
1091 }
1092
Finish(FPDF_FORMHANDLE form)1093 void Finish(FPDF_FORMHANDLE form) override {
1094 BitmapPageRenderer::Finish(form);
1095 FPDF_RenderPage_Close(page());
1096 Idle();
1097 }
1098
1099 private:
1100 const FPDF_COLORSCHEME* color_scheme_;
1101 IFSDK_PAUSE pause_;
1102 bool to_be_continued_ = false;
1103 };
1104
1105 #ifdef _WIN32
1106 class ScopedGdiDc final {
1107 public:
~ScopedGdiDc()1108 ~ScopedGdiDc() { Reset(nullptr); }
1109
Reset(HDC dc)1110 void Reset(HDC dc) {
1111 if (dc_) {
1112 [[maybe_unused]] BOOL success = DeleteDC(dc_);
1113 DCHECK(success);
1114 }
1115 dc_ = dc;
1116 }
1117
Get() const1118 HDC Get() const { return dc_; }
1119
1120 private:
1121 HDC dc_ = nullptr;
1122 };
1123
1124 class ScopedGdiObject final {
1125 public:
~ScopedGdiObject()1126 ~ScopedGdiObject() { Reset(nullptr); }
1127
Reset(HGDIOBJ object)1128 void Reset(HGDIOBJ object) {
1129 if (object_) {
1130 [[maybe_unused]] BOOL success = DeleteObject(object_);
1131 DCHECK(success);
1132 }
1133 object_ = object;
1134 }
1135
Get() const1136 HGDIOBJ Get() const { return object_; }
1137
1138 private:
1139 HGDIOBJ object_ = nullptr;
1140 };
1141
1142 class GdiDisplayPageRenderer : public BitmapPageRenderer {
1143 public:
GdiDisplayPageRenderer(FPDF_PAGE page,int width,int height,int flags,const std::function<void ()> & idler,PageWriter writer)1144 GdiDisplayPageRenderer(FPDF_PAGE page,
1145 int width,
1146 int height,
1147 int flags,
1148 const std::function<void()>& idler,
1149 PageWriter writer)
1150 : BitmapPageRenderer(page,
1151 /*width=*/width,
1152 /*height=*/height,
1153 /*flags=*/flags,
1154 idler,
1155 std::move(writer)) {}
1156
~GdiDisplayPageRenderer()1157 ~GdiDisplayPageRenderer() override {
1158 // Need to free `bitmap()` first, in case it points at `dib_` memory.
1159 ResetBitmap();
1160 }
1161
Start()1162 bool Start() override {
1163 // Create an in-memory DC compatible with the display.
1164 dc_.Reset(CreateCompatibleDC(/*hdc=*/nullptr));
1165 if (!dc_.Get()) {
1166 return false;
1167 }
1168
1169 // Create a BGRA DIB and select it into the in-memory DC.
1170 BITMAPINFO dib_info;
1171 memset(&dib_info, 0, sizeof(BITMAPINFO));
1172 dib_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1173 dib_info.bmiHeader.biWidth = width();
1174 dib_info.bmiHeader.biHeight = -height(); // top-down
1175 dib_info.bmiHeader.biPlanes = 1;
1176 dib_info.bmiHeader.biBitCount = 32;
1177 dib_info.bmiHeader.biCompression = BI_RGB;
1178
1179 VOID* dib_pixels;
1180 dib_.Reset(CreateDIBSection(dc_.Get(), &dib_info, DIB_RGB_COLORS,
1181 &dib_pixels, /*hSection=*/nullptr,
1182 /*offset=*/0));
1183 if (!dib_.Get() || !InitializeBitmap(dib_pixels)) {
1184 return false;
1185 }
1186
1187 HGDIOBJ old_obj = SelectObject(dc_.Get(), dib_.Get());
1188 CHECK(old_obj);
1189 CHECK_NE(old_obj, HGDI_ERROR);
1190
1191 // Render into the in-memory DC.
1192 FPDF_BOOL render_result =
1193 FPDF_RenderPage(dc_.Get(), page(), /*start_x=*/0, /*start_y=*/0,
1194 /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
1195 /*flags=*/flags());
1196 if (!render_result) {
1197 return false;
1198 }
1199
1200 bool result = !!GdiFlush();
1201 HGDIOBJ dib_obj = SelectObject(dc_.Get(), old_obj);
1202 CHECK((GetObjectType(old_obj) != OBJ_REGION && dib_obj) ||
1203 (GetObjectType(old_obj) == OBJ_REGION && dib_obj != HGDI_ERROR));
1204 return result;
1205 }
1206
Finish(FPDF_FORMHANDLE)1207 void Finish(FPDF_FORMHANDLE /*form*/) override {
1208 // Note that `fpdf_formfill.h` does not support GDI.
1209
1210 // The GDI backend doesn't support alpha and clears the alpha component to
1211 // transparent, so clear the alpha component back to opaque.
1212 const int stride = FPDFBitmap_GetStride(bitmap());
1213 DCHECK_EQ(width() * sizeof(uint32_t), static_cast<size_t>(stride));
1214 const int pixel_stride = stride / sizeof(uint32_t);
1215
1216 uint32_t* scanline =
1217 reinterpret_cast<uint32_t*>(FPDFBitmap_GetBuffer(bitmap()));
1218 for (int row = 0; row < height(); ++row) {
1219 for (int column = 0; column < width(); ++column) {
1220 scanline[column] |= 0xFF000000;
1221 }
1222 scanline += pixel_stride;
1223 }
1224 }
1225
1226 private:
1227 ScopedGdiDc dc_;
1228 ScopedGdiObject dib_;
1229 };
1230 #endif // _WIN32
1231
1232 #ifdef PDF_ENABLE_SKIA
1233 class SkCanvasPageRenderer : public PageRenderer {
1234 public:
Start()1235 bool Start() override {
1236 FPDF_RenderPageSkia(reinterpret_cast<FPDF_SKIA_CANVAS>(canvas()), page(),
1237 width(), height());
1238 return true;
1239 }
1240
Finish(FPDF_FORMHANDLE form)1241 void Finish(FPDF_FORMHANDLE form) override {
1242 FPDF_FFLDrawSkia(form, reinterpret_cast<FPDF_SKIA_CANVAS>(canvas()), page(),
1243 /*start_x=*/0, /*start_y=*/0, width(), height(),
1244 /*rotate=*/0, flags());
1245 }
1246
1247 protected:
SkCanvasPageRenderer(FPDF_PAGE page,int width,int height,int flags)1248 SkCanvasPageRenderer(FPDF_PAGE page, int width, int height, int flags)
1249 : PageRenderer(page, width, height, flags) {}
1250
1251 virtual SkCanvas* canvas() = 0;
1252 };
1253
1254 class SkPicturePageRenderer final : public SkCanvasPageRenderer {
1255 public:
SkPicturePageRenderer(FPDF_PAGE page,int width,int height,int flags)1256 SkPicturePageRenderer(FPDF_PAGE page, int width, int height, int flags)
1257 : SkCanvasPageRenderer(page, width, height, flags) {}
1258
HasOutput() const1259 bool HasOutput() const override { return !!picture_; }
1260
Start()1261 bool Start() override {
1262 recorder_ = std::make_unique<SkPictureRecorder>();
1263 recorder_->beginRecording(width(), height());
1264 return SkCanvasPageRenderer::Start();
1265 }
1266
Finish(FPDF_FORMHANDLE form)1267 void Finish(FPDF_FORMHANDLE form) override {
1268 SkCanvasPageRenderer::Finish(form);
1269 picture_ = recorder_->finishRecordingAsPicture();
1270 recorder_.reset();
1271 }
1272
Write(const std::string & name,int page_index,bool md5)1273 bool Write(const std::string& name, int page_index, bool md5) override {
1274 std::string image_file_name = WriteSkp(name.c_str(), page_index, *picture_);
1275 if (image_file_name.empty())
1276 return false;
1277
1278 if (md5) {
1279 // Play back the `SkPicture` so we can take a hash of the result.
1280 sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(
1281 /*width=*/width(), /*height=*/height()));
1282 if (!surface)
1283 return false;
1284
1285 // Must clear to white before replay to match initial `CFX_DIBitmap`.
1286 surface->getCanvas()->clear(SK_ColorWHITE);
1287 surface->getCanvas()->drawPicture(picture_);
1288
1289 // Write the filename and the MD5 of the buffer to stdout.
1290 SkPixmap pixmap;
1291 if (!surface->peekPixels(&pixmap))
1292 return false;
1293
1294 OutputMD5Hash(image_file_name.c_str(),
1295 UNSAFE_TODO(pdfium::make_span(
1296 static_cast<const uint8_t*>(pixmap.addr()),
1297 pixmap.computeByteSize())));
1298 }
1299 return true;
1300 }
1301
1302 protected:
canvas()1303 SkCanvas* canvas() override { return recorder_->getRecordingCanvas(); }
1304
1305 private:
1306 std::unique_ptr<SkPictureRecorder> recorder_;
1307 sk_sp<SkPicture> picture_;
1308 };
1309
1310 class SkDocumentPageRenderer final : public SkCanvasPageRenderer {
1311 public:
SkDocumentPageRenderer(std::unique_ptr<SkWStream> stream,sk_sp<SkDocument> document,FPDF_PAGE page,int width,int height,int flags)1312 SkDocumentPageRenderer(std::unique_ptr<SkWStream> stream,
1313 sk_sp<SkDocument> document,
1314 FPDF_PAGE page,
1315 int width,
1316 int height,
1317 int flags)
1318 : SkCanvasPageRenderer(page, width, height, flags),
1319 stream_(std::move(stream)),
1320 document_(std::move(document)) {
1321 DCHECK(stream_);
1322 DCHECK(document_);
1323 }
1324
HasOutput() const1325 bool HasOutput() const override { return has_output_; }
1326
Start()1327 bool Start() override {
1328 if (!document_) {
1329 return false;
1330 }
1331
1332 DCHECK(!canvas_);
1333 canvas_ = document_->beginPage(width(), height());
1334 if (!canvas_) {
1335 return false;
1336 }
1337
1338 return SkCanvasPageRenderer::Start();
1339 }
1340
Finish(FPDF_FORMHANDLE form)1341 void Finish(FPDF_FORMHANDLE form) override {
1342 SkCanvasPageRenderer::Finish(form);
1343
1344 DCHECK(canvas_);
1345 canvas_ = nullptr;
1346 document_->endPage();
1347
1348 has_output_ = true;
1349 }
1350
Write(const std::string &,int,bool)1351 bool Write(const std::string& /*name*/,
1352 int /*page_index*/,
1353 bool /*md5*/) override {
1354 bool success = HasOutput();
1355 if (success) {
1356 document_->close();
1357 } else {
1358 document_->abort();
1359 }
1360
1361 document_.reset();
1362 stream_.reset();
1363 return success;
1364 }
1365
1366 protected:
canvas()1367 SkCanvas* canvas() override { return canvas_; }
1368
1369 private:
1370 std::unique_ptr<SkWStream> stream_;
1371 sk_sp<SkDocument> document_;
1372
1373 SkCanvas* canvas_ = nullptr;
1374 bool has_output_ = false;
1375 };
1376 #endif // PDF_ENABLE_SKIA
1377
ProcessPage(const int page_index)1378 bool PdfProcessor::ProcessPage(const int page_index) {
1379 FPDF_PAGE page = GetPage(page_index);
1380 if (!page) {
1381 return false;
1382 }
1383
1384 if (options().send_events) {
1385 SendPageEvents(form(), page, events(), idler());
1386 }
1387 if (options().save_images) {
1388 WriteImages(page, name().c_str(), page_index);
1389 }
1390 if (options().save_rendered_images) {
1391 WriteRenderedImages(doc(), page, name().c_str(), page_index);
1392 }
1393 if (options().save_thumbnails) {
1394 WriteThumbnail(page, name().c_str(), page_index);
1395 }
1396 if (options().save_thumbnails_decoded) {
1397 WriteDecodedThumbnailStream(page, name().c_str(), page_index);
1398 }
1399 if (options().save_thumbnails_raw) {
1400 WriteRawThumbnailStream(page, name().c_str(), page_index);
1401 }
1402 if (options().output_format == OutputFormat::kPageInfo) {
1403 DumpPageInfo(page, page_index);
1404 return true;
1405 }
1406 if (options().output_format == OutputFormat::kStructure) {
1407 DumpPageStructure(page, page_index);
1408 return true;
1409 }
1410
1411 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
1412 double scale = 1.0;
1413 if (!options().scale_factor_as_string.empty()) {
1414 std::stringstream(options().scale_factor_as_string) >> scale;
1415 }
1416
1417 int width = static_cast<int>(FPDF_GetPageWidthF(page) * scale);
1418 int height = static_cast<int>(FPDF_GetPageHeightF(page) * scale);
1419 int flags = PageRenderFlagsFromOptions(options());
1420
1421 std::unique_ptr<PageRenderer> renderer;
1422 BitmapPageRenderer::PageWriter writer;
1423 switch (options().output_format) {
1424 case OutputFormat::kText:
1425 writer = BitmapPageRenderer::WrapPageWriter(WriteText);
1426 break;
1427
1428 case OutputFormat::kAnnot:
1429 writer = BitmapPageRenderer::WrapPageWriter(WriteAnnot);
1430 break;
1431
1432 case OutputFormat::kPpm:
1433 writer = BitmapPageRenderer::WrapPageWriter(WritePpm);
1434 break;
1435
1436 case OutputFormat::kPng:
1437 writer = BitmapPageRenderer::WrapPageWriter(WritePng);
1438 break;
1439
1440 #ifdef _WIN32
1441 case OutputFormat::kBmp:
1442 writer = BitmapPageRenderer::WrapPageWriter(WriteBmp);
1443 break;
1444
1445 case OutputFormat::kEmf:
1446 // TODO(crbug.com/pdfium/2054): Render directly to DC.
1447 writer = BitmapPageRenderer::WrapPageWriter(WriteEmf);
1448 break;
1449
1450 case OutputFormat::kPs2:
1451 case OutputFormat::kPs3:
1452 // TODO(crbug.com/pdfium/2054): Render directly to DC.
1453 writer = BitmapPageRenderer::WrapPageWriter(WritePS);
1454 break;
1455 #endif // _WIN32
1456
1457 #ifdef PDF_ENABLE_SKIA
1458 case OutputFormat::kSkp:
1459 renderer = std::make_unique<SkPicturePageRenderer>(
1460 page, /*width=*/width, /*height=*/height, /*flags=*/flags);
1461 break;
1462
1463 #ifdef _WIN32
1464 case OutputFormat::kXps: {
1465 IXpsOMObjectFactory* xps_factory = com_factory().GetXpsOMObjectFactory();
1466 if (!xps_factory) {
1467 break;
1468 }
1469
1470 std::unique_ptr<SkWStream> stream =
1471 WriteToSkWStream(name(), page_index, "xps");
1472 if (!stream) {
1473 break;
1474 }
1475
1476 sk_sp<SkDocument> document =
1477 SkXPS::MakeDocument(stream.get(), xps_factory);
1478 if (!document) {
1479 break;
1480 }
1481
1482 renderer = std::make_unique<SkDocumentPageRenderer>(
1483 std::move(stream), std::move(document), page, width, height, flags);
1484 break;
1485 }
1486 #endif // _WIN32
1487 #endif // PDF_ENABLE_SKIA
1488
1489 default:
1490 // Other formats won't write the output to a file, but still rasterize.
1491 break;
1492 }
1493
1494 #ifdef _WIN32
1495 if (!renderer && options().use_renderer_type == RendererType::kGdi) {
1496 renderer = std::make_unique<GdiDisplayPageRenderer>(
1497 page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
1498 std::move(writer));
1499 }
1500 #endif // _WIN32
1501
1502 if (!renderer) {
1503 // Use a rasterizing page renderer by default.
1504 if (options().render_oneshot) {
1505 renderer = std::make_unique<OneShotBitmapPageRenderer>(
1506 page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
1507 std::move(writer));
1508 } else {
1509 // Client programs will be setting these values when rendering.
1510 // This is a sample color scheme with distinct colors.
1511 // Used only when `options().forced_color` is true.
1512 FPDF_COLORSCHEME color_scheme;
1513 color_scheme.path_fill_color = 0xFFFF0000;
1514 color_scheme.path_stroke_color = 0xFF00FF00;
1515 color_scheme.text_fill_color = 0xFF0000FF;
1516 color_scheme.text_stroke_color = 0xFF00FFFF;
1517
1518 renderer = std::make_unique<ProgressiveBitmapPageRenderer>(
1519 page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
1520 std::move(writer), options().forced_color ? &color_scheme : nullptr);
1521 }
1522 }
1523
1524 if (renderer->Start()) {
1525 while (renderer->Continue())
1526 continue;
1527 renderer->Finish(form());
1528 renderer->Write(name(), page_index, /*md5=*/options().md5);
1529 } else {
1530 fprintf(stderr, "Page was too large to be rendered.\n");
1531 }
1532
1533 FORM_DoPageAAction(page, form(), FPDFPAGE_AACTION_CLOSE);
1534 Idle();
1535
1536 FORM_OnBeforeClosePage(page, form());
1537 Idle();
1538
1539 return renderer->HasOutput();
1540 }
1541
ProcessPdf(const std::string & name,pdfium::span<const uint8_t> data,const std::string & events)1542 void Processor::ProcessPdf(const std::string& name,
1543 pdfium::span<const uint8_t> data,
1544 const std::string& events) {
1545 TestLoader loader(data);
1546
1547 FPDF_FILEACCESS file_access = {};
1548 file_access.m_FileLen = static_cast<unsigned long>(data.size());
1549 file_access.m_GetBlock = TestLoader::GetBlock;
1550 file_access.m_Param = &loader;
1551
1552 FX_FILEAVAIL file_avail = {};
1553 file_avail.version = 1;
1554 file_avail.IsDataAvail = Is_Data_Avail;
1555
1556 FX_DOWNLOADHINTS hints = {};
1557 hints.version = 1;
1558 hints.AddSegment = Add_Segment;
1559
1560 // |pdf_avail| must outlive |doc|.
1561 ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access));
1562
1563 // |doc| must outlive |form_callbacks.loaded_pages|.
1564 ScopedFPDFDocument doc;
1565
1566 const char* password =
1567 options().password.empty() ? nullptr : options().password.c_str();
1568 bool is_linearized = false;
1569 if (options().use_load_mem_document) {
1570 doc.reset(FPDF_LoadMemDocument(data.data(), data.size(), password));
1571 } else {
1572 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
1573 int avail_status = PDF_DATA_NOTAVAIL;
1574 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), password));
1575 if (doc) {
1576 while (avail_status == PDF_DATA_NOTAVAIL)
1577 avail_status = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
1578
1579 if (avail_status == PDF_DATA_ERROR) {
1580 fprintf(stderr, "Unknown error in checking if doc was available.\n");
1581 return;
1582 }
1583 avail_status = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
1584 if (avail_status == PDF_FORM_ERROR ||
1585 avail_status == PDF_FORM_NOTAVAIL) {
1586 fprintf(stderr,
1587 "Error %d was returned in checking if form was available.\n",
1588 avail_status);
1589 return;
1590 }
1591 is_linearized = true;
1592 }
1593 } else {
1594 doc.reset(FPDF_LoadCustomDocument(&file_access, password));
1595 }
1596 }
1597
1598 if (!doc) {
1599 PrintLastError();
1600 return;
1601 }
1602
1603 if (!FPDF_DocumentHasValidCrossReferenceTable(doc.get()))
1604 fprintf(stderr, "Document has invalid cross reference table\n");
1605
1606 if (options().show_metadata) {
1607 DumpMetaData(doc.get());
1608 }
1609
1610 if (options().save_attachments) {
1611 WriteAttachments(doc.get(), name);
1612 }
1613
1614 #ifdef PDF_ENABLE_V8
1615 IPDF_JSPLATFORM platform_callbacks = {};
1616 platform_callbacks.version = 3;
1617 platform_callbacks.app_alert = ExampleAppAlert;
1618 platform_callbacks.app_beep = ExampleAppBeep;
1619 platform_callbacks.app_response = ExampleAppResponse;
1620 platform_callbacks.Doc_getFilePath = ExampleDocGetFilePath;
1621 platform_callbacks.Doc_mail = ExampleDocMail;
1622 platform_callbacks.Doc_print = ExampleDocPrint;
1623 platform_callbacks.Doc_submitForm = ExampleDocSubmitForm;
1624 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
1625 platform_callbacks.Field_browse = ExampleFieldBrowse;
1626 #endif // PDF_ENABLE_V8
1627
1628 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
1629 #ifdef PDF_ENABLE_XFA
1630 form_callbacks.version = 2;
1631 form_callbacks.xfa_disabled =
1632 options().disable_xfa || options().disable_javascript;
1633 form_callbacks.FFI_PopupMenu = ExamplePopupMenu;
1634 #else // PDF_ENABLE_XFA
1635 form_callbacks.version = 1;
1636 #endif // PDF_ENABLE_XFA
1637 form_callbacks.FFI_ExecuteNamedAction = ExampleNamedAction;
1638 form_callbacks.FFI_GetPage = GetPageForIndex;
1639
1640 #ifdef PDF_ENABLE_V8
1641 if (!options().disable_javascript) {
1642 form_callbacks.m_pJsPlatform = &platform_callbacks;
1643 }
1644 #endif // PDF_ENABLE_V8
1645
1646 ScopedFPDFFormHandle form(
1647 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
1648 form_callbacks.form_handle = form.get();
1649
1650 #ifdef PDF_ENABLE_XFA
1651 if (!options().disable_xfa && !options().disable_javascript) {
1652 int doc_type = FPDF_GetFormType(doc.get());
1653 if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
1654 if (!FPDF_LoadXFA(doc.get()))
1655 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
1656 }
1657 }
1658 #endif // PDF_ENABLE_XFA
1659
1660 FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
1661 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
1662 FORM_DoDocumentJSAction(form.get());
1663 FORM_DoDocumentOpenAction(form.get());
1664
1665 #if _WIN32
1666 if (options().output_format == OutputFormat::kPs2) {
1667 FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
1668 } else if (options().output_format == OutputFormat::kPs3) {
1669 FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
1670 } else if (options().output_format == OutputFormat::kPs3Type42) {
1671 FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3_TYPE42);
1672 }
1673 #endif
1674
1675 int page_count = FPDF_GetPageCount(doc.get());
1676 int processed_pages = 0;
1677 int bad_pages = 0;
1678 int first_page = options().pages ? options().first_page : 0;
1679 int last_page = options().pages ? options().last_page + 1 : page_count;
1680 PdfProcessor pdf_processor(this, &name, &events, doc.get(), form.get(),
1681 &form_callbacks);
1682 for (int i = first_page; i < last_page; ++i) {
1683 if (is_linearized) {
1684 int avail_status = PDF_DATA_NOTAVAIL;
1685 while (avail_status == PDF_DATA_NOTAVAIL)
1686 avail_status = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
1687
1688 if (avail_status == PDF_DATA_ERROR) {
1689 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
1690 i);
1691 return;
1692 }
1693 }
1694 if (pdf_processor.ProcessPage(i)) {
1695 ++processed_pages;
1696 } else {
1697 ++bad_pages;
1698 }
1699 Idle();
1700 }
1701
1702 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
1703 Idle();
1704
1705 fprintf(stderr, "Processed %d pages.\n", processed_pages);
1706 if (bad_pages)
1707 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
1708 }
1709
ShowConfig()1710 void ShowConfig() {
1711 std::string config;
1712 [[maybe_unused]] auto append_config = [&config](const char* name) {
1713 if (!config.empty())
1714 config += ',';
1715 config += name;
1716 };
1717
1718 #ifdef PDF_ENABLE_V8
1719 append_config("V8");
1720 #endif
1721 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1722 append_config("V8_EXTERNAL");
1723 #endif
1724 #ifdef PDF_ENABLE_XFA
1725 append_config("XFA");
1726 #endif
1727 #ifdef PDF_ENABLE_ASAN
1728 append_config("ASAN");
1729 #endif
1730 #ifdef PDF_ENABLE_SKIA
1731 append_config("SKIA");
1732 #endif
1733 #ifdef _WIN32
1734 append_config("GDI");
1735 #endif
1736 printf("%s\n", config.c_str());
1737 }
1738
1739 constexpr char kUsageString[] =
1740 "Usage: pdfium_test [OPTION] [FILE]...\n"
1741 " --show-config - print build options and exit\n"
1742 " --show-metadata - print the file metadata\n"
1743 " --show-pageinfo - print information about pages\n"
1744 " --show-structure - print the structure elements from the "
1745 "document\n"
1746 " --send-events - send input described by .evt file\n"
1747 " --mem-document - load document with FPDF_LoadMemDocument()\n"
1748 " --render-oneshot - render image without using progressive "
1749 "renderer\n"
1750 " --lcd-text - render text optimized for LCD displays\n"
1751 " --no-nativetext - render without using the native text output\n"
1752 " --grayscale - render grayscale output\n"
1753 " --forced-color - render in forced color mode\n"
1754 " --fill-to-stroke - render fill as stroke in forced color mode\n"
1755 " --limit-cache - render limiting image cache size\n"
1756 " --force-halftone - render forcing halftone\n"
1757 " --printing - render as if for printing\n"
1758 " --no-smoothtext - render disabling text anti-aliasing\n"
1759 " --no-smoothimage - render disabling image anti-alisasing\n"
1760 " --no-smoothpath - render disabling path anti-aliasing\n"
1761 " --reverse-byte-order - render to BGRA, if supported by the output "
1762 "format\n"
1763 " --save-attachments - write embedded attachments "
1764 "<pdf-name>.attachment.<attachment-name>\n"
1765 " --save-images - write raw embedded images "
1766 "<pdf-name>.<page-number>.<object-number>.png\n"
1767 " --save-rendered-images - write embedded images as rendered on the page "
1768 "<pdf-name>.<page-number>.<object-number>.png\n"
1769 " --save-thumbs - write page thumbnails "
1770 "<pdf-name>.thumbnail.<page-number>.png\n"
1771 " --save-thumbs-dec - write page thumbnails' decoded stream data"
1772 "<pdf-name>.thumbnail.decoded.<page-number>.png\n"
1773 " --save-thumbs-raw - write page thumbnails' raw stream data"
1774 "<pdf-name>.thumbnail.raw.<page-number>.png\n"
1775
1776 #if defined(PDF_ENABLE_SKIA)
1777 #ifdef _WIN32
1778 " --use-renderer - renderer to use, one of [agg | gdi | skia]\n"
1779 #else
1780 " --use-renderer - renderer to use, one of [agg | skia]\n"
1781 #endif // _WIN32
1782 #else
1783 #ifdef _WIN32
1784 " --use-renderer - renderer to use, one of [agg | gdi]\n"
1785 #else
1786 " --use-renderer - renderer to use, one of [agg]\n"
1787 #endif // _WIN32
1788 #endif // defined(PDF_ENABLE_SKIA)
1789
1790 #ifdef PDF_ENABLE_V8
1791 " --disable-javascript - do not execute JS in PDF files\n"
1792 " --js-flags=<flags> - additional flags to pass to V8\n"
1793 #ifdef PDF_ENABLE_XFA
1794 " --disable-xfa - do not process XFA forms\n"
1795 #endif // PDF_ENABLE_XFA
1796 #endif // PDF_ENABLE_V8
1797 #ifdef ENABLE_CALLGRIND
1798 " --callgrind-delim - delimit interesting section when using "
1799 "callgrind\n"
1800 #endif
1801 #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
1802 " --no-system-fonts - do not use system fonts, overrides --font-dir\n"
1803 #endif
1804 " --croscore-font-names - use Croscore font names\n"
1805 " --bin-dir=<path> - override path to v8 external data\n"
1806 " --font-dir=<path> - override path to external fonts\n"
1807 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
1808 " --password=<secret> - password to decrypt the PDF with\n"
1809 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
1810 #ifdef _WIN32
1811 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
1812 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
1813 " --ps2 - write page raw PostScript (Lvl 2) "
1814 "<pdf-name>.<page-number>.ps\n"
1815 " --ps3 - write page raw PostScript (Lvl 3) "
1816 "<pdf-name>.<page-number>.ps\n"
1817 " --ps3-type42 - write page raw PostScript (Lvl 3 with Type 42 fonts) "
1818 "<pdf-name>.<page-number>.ps\n"
1819 #endif
1820 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
1821 " --png - write page images <pdf-name>.<page-number>.png\n"
1822 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
1823 " --annot - write annotation info <pdf-name>.<page-number>.annot.txt\n"
1824 #ifdef PDF_ENABLE_SKIA
1825 " --skp - write page images <pdf-name>.<page-number>.skp\n"
1826 #ifdef _WIN32
1827 " --xps - write page images <pdf-name>.<page-number>.xps\n"
1828 #endif // _WIN32
1829 #endif // PDF_ENABLE_SKIA
1830 " --md5 - write output image paths and their md5 hashes to stdout.\n"
1831 " --time=<number> - Seconds since the epoch to set system time.\n"
1832 "";
1833
SetUpErrorHandling()1834 void SetUpErrorHandling() {
1835 #ifdef _WIN32
1836 // Suppress various Windows error reporting mechanisms that can pop up dialog
1837 // boxes and cause the program to hang.
1838 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
1839 SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
1840 _set_error_mode(_OUT_TO_STDERR);
1841 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
1842 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
1843 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
1844 #endif // _WIN32
1845 }
1846
1847 } // namespace
1848
main(int argc,const char * argv[])1849 int main(int argc, const char* argv[]) {
1850 #if defined(PDF_USE_PARTITION_ALLOC)
1851 pdfium::ConfigurePartitionAllocShimPartitionForTest();
1852 #endif
1853
1854 SetUpErrorHandling();
1855 setlocale(LC_CTYPE, "en_US.UTF-8"); // For printf() of high-characters.
1856
1857 std::vector<std::string> args(argv, argv + argc);
1858 Options options;
1859 std::vector<std::string> files;
1860 if (!ParseCommandLine(args, &options, &files)) {
1861 fprintf(stderr, "%s", kUsageString);
1862 return 1;
1863 }
1864
1865 if (options.show_config) {
1866 ShowConfig();
1867 return 0;
1868 }
1869
1870 if (files.empty()) {
1871 fprintf(stderr, "No input files.\n");
1872 return 1;
1873 }
1874
1875 FPDF_LIBRARY_CONFIG config;
1876 config.version = 4;
1877 config.m_pUserFontPaths = nullptr;
1878 config.m_pIsolate = nullptr;
1879 config.m_v8EmbedderSlot = 0;
1880 config.m_pPlatform = nullptr;
1881
1882 switch (options.use_renderer_type) {
1883 case RendererType::kDefault:
1884 config.m_RendererType = GetDefaultRendererType();
1885 break;
1886
1887 case RendererType::kAgg:
1888 config.m_RendererType = FPDF_RENDERERTYPE_AGG;
1889 break;
1890
1891 #ifdef _WIN32
1892 case RendererType::kGdi:
1893 // GDI renderer uses `FPDF_RenderPage()`, rather than a renderer type.
1894 config.m_RendererType = GetDefaultRendererType();
1895 break;
1896 #endif // _WIN32
1897
1898 #if defined(PDF_ENABLE_SKIA)
1899 case RendererType::kSkia:
1900 #if defined(BUILD_WITH_CHROMIUM)
1901 // Needed to support Chromium's copy of Skia, which uses a
1902 // `DiscardableMemoryAllocator`.
1903 chromium_support::InitializeDiscardableMemoryAllocator();
1904 #endif // defined(BUILD_WITH_CHROMIUM)
1905 config.m_RendererType = FPDF_RENDERERTYPE_SKIA;
1906 break;
1907 #endif // defined(PDF_ENABLE_SKIA)
1908 }
1909
1910 std::function<void()> idler = []() {};
1911 #ifdef PDF_ENABLE_V8
1912 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1913 v8::StartupData snapshot;
1914 #endif // V8_USE_EXTERNAL_STARTUP_DATA
1915 std::unique_ptr<v8::Platform> platform;
1916 std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate;
1917 if (!options.disable_javascript) {
1918 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1919 platform = InitializeV8ForPDFiumWithStartupData(
1920 options.exe_path, options.js_flags, options.bin_directory, &snapshot);
1921 #else // V8_USE_EXTERNAL_STARTUP_DATA
1922 platform = InitializeV8ForPDFium(options.exe_path, options.js_flags);
1923 #endif // V8_USE_EXTERNAL_STARTUP_DATA
1924 if (!platform) {
1925 fprintf(stderr, "V8 initialization failed.\n");
1926 return 1;
1927 }
1928 config.m_pPlatform = platform.get();
1929
1930 v8::Isolate::CreateParams params;
1931 params.array_buffer_allocator = static_cast<v8::ArrayBuffer::Allocator*>(
1932 FPDF_GetArrayBufferAllocatorSharedInstance());
1933 isolate.reset(v8::Isolate::New(params));
1934 config.m_pIsolate = isolate.get();
1935
1936 idler = [&platform, &isolate]() {
1937 int task_count = 0;
1938 while (v8::platform::PumpMessageLoop(platform.get(), isolate.get()))
1939 ++task_count;
1940 if (task_count)
1941 fprintf(stderr, "Pumped %d tasks\n", task_count);
1942 };
1943 }
1944 #endif // PDF_ENABLE_V8
1945
1946 const char* path_array[2] = {nullptr, nullptr};
1947 std::optional<const char*> custom_font_path = GetCustomFontPath(options);
1948 if (custom_font_path.has_value()) {
1949 path_array[0] = custom_font_path.value();
1950 config.m_pUserFontPaths = path_array;
1951 }
1952
1953 FPDF_InitLibraryWithConfig(&config);
1954
1955 {
1956 std::unique_ptr<FontRenamer> font_renamer;
1957 if (options.croscore_font_names) {
1958 // Must be destroyed before FPDF_DestroyLibrary().
1959 font_renamer = std::make_unique<FontRenamer>();
1960 }
1961
1962 UNSUPPORT_INFO unsupported_info = {};
1963 unsupported_info.version = 1;
1964 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
1965
1966 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
1967
1968 if (options.time > -1) {
1969 // This must be a static var to avoid explicit capture, so the lambda can
1970 // be converted to a function ptr.
1971 static time_t time_ret = options.time;
1972 FSDK_SetTimeFunction([]() { return time_ret; });
1973 FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
1974 }
1975
1976 Processor processor(&options, &idler);
1977 for (const std::string& filename : files) {
1978 std::vector<uint8_t> file_contents = GetFileContents(filename.c_str());
1979 if (file_contents.empty()) {
1980 continue;
1981 }
1982 fprintf(stderr, "Processing PDF file %s.\n", filename.c_str());
1983
1984 #ifdef ENABLE_CALLGRIND
1985 if (options.callgrind_delimiters) {
1986 CALLGRIND_START_INSTRUMENTATION;
1987 }
1988 #endif // ENABLE_CALLGRIND
1989
1990 std::string events;
1991 if (options.send_events) {
1992 std::string event_filename = filename;
1993 size_t extension_pos = event_filename.find(".pdf");
1994 if (extension_pos != std::string::npos) {
1995 event_filename.replace(extension_pos, 4, ".evt");
1996 if (access(event_filename.c_str(), R_OK) == 0) {
1997 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1998 std::vector<uint8_t> event_contents =
1999 GetFileContents(event_filename.c_str());
2000 if (!event_contents.empty()) {
2001 fprintf(stderr, "Sending events from: %s\n",
2002 event_filename.c_str());
2003 std::copy(event_contents.begin(), event_contents.end(),
2004 std::back_inserter(events));
2005 }
2006 }
2007 }
2008 }
2009
2010 processor.ProcessPdf(filename, file_contents, events);
2011
2012 #ifdef ENABLE_CALLGRIND
2013 if (options.callgrind_delimiters) {
2014 CALLGRIND_STOP_INSTRUMENTATION;
2015 }
2016 #endif // ENABLE_CALLGRIND
2017 }
2018 }
2019
2020 FPDF_DestroyLibrary();
2021
2022 #ifdef PDF_ENABLE_V8
2023 if (!options.disable_javascript) {
2024 isolate.reset();
2025 ShutdownV8ForPDFium();
2026 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2027 free(const_cast<char*>(snapshot.data));
2028 #endif // V8_USE_EXTERNAL_STARTUP_DATA
2029 }
2030 #endif // PDF_ENABLE_V8
2031
2032 return 0;
2033 }
2034