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