• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
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 <limits.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include <bitset>
11 #include <iterator>
12 #include <map>
13 #include <memory>
14 #include <sstream>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 #if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
20 #define _SKIA_SUPPORT_
21 #endif
22 
23 #include "public/cpp/fpdf_deleters.h"
24 #include "public/fpdf_annot.h"
25 #include "public/fpdf_attachment.h"
26 #include "public/fpdf_dataavail.h"
27 #include "public/fpdf_edit.h"
28 #include "public/fpdf_ext.h"
29 #include "public/fpdf_formfill.h"
30 #include "public/fpdf_progressive.h"
31 #include "public/fpdf_structtree.h"
32 #include "public/fpdf_text.h"
33 #include "public/fpdfview.h"
34 #include "testing/image_diff/image_diff_png.h"
35 #include "testing/test_support.h"
36 #include "third_party/base/logging.h"
37 
38 #ifdef _WIN32
39 #include <io.h>
40 #else
41 #include <unistd.h>
42 #endif
43 
44 #ifdef ENABLE_CALLGRIND
45 #include <valgrind/callgrind.h>
46 #endif  // ENABLE_CALLGRIND
47 
48 #ifdef PDF_ENABLE_V8
49 #include "v8/include/libplatform/libplatform.h"
50 #include "v8/include/v8.h"
51 #endif  // PDF_ENABLE_V8
52 
53 #ifdef PDF_ENABLE_SKIA
54 #include "third_party/skia/include/core/SkPictureRecorder.h"
55 #include "third_party/skia/include/core/SkStream.h"
56 #endif
57 
58 #ifdef _WIN32
59 #define access _access
60 #define snprintf _snprintf
61 #define R_OK 4
62 #endif
63 
64 enum OutputFormat {
65   OUTPUT_NONE,
66   OUTPUT_STRUCTURE,
67   OUTPUT_TEXT,
68   OUTPUT_PPM,
69   OUTPUT_PNG,
70   OUTPUT_ANNOT,
71 #ifdef _WIN32
72   OUTPUT_BMP,
73   OUTPUT_EMF,
74   OUTPUT_PS2,
75   OUTPUT_PS3,
76 #endif
77 #ifdef PDF_ENABLE_SKIA
78   OUTPUT_SKP,
79 #endif
80 };
81 
82 namespace {
83 
84 struct Options {
Options__anon5a3ded8a0111::Options85   Options()
86       : show_config(false),
87         show_metadata(false),
88         send_events(false),
89         render_oneshot(false),
90         save_attachments(false),
91         save_images(false),
92 #ifdef ENABLE_CALLGRIND
93         callgrind_delimiters(false),
94 #endif  // ENABLE_CALLGRIND
95         pages(false),
96         md5(false),
97         output_format(OUTPUT_NONE) {
98   }
99 
100   bool show_config;
101   bool show_metadata;
102   bool send_events;
103   bool render_oneshot;
104   bool save_attachments;
105   bool save_images;
106 #ifdef ENABLE_CALLGRIND
107   bool callgrind_delimiters;
108 #endif  // ENABLE_CALLGRIND
109   bool pages;
110   bool md5;
111   OutputFormat output_format;
112   std::string scale_factor_as_string;
113   std::string exe_path;
114   std::string bin_directory;
115   std::string font_directory;
116   // 0-based page numbers to be rendered.
117   int first_page;
118   int last_page;
119 };
120 
121 struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
122   // Hold a map of the currently loaded pages in order to avoid them
123   // to get loaded twice.
124   std::map<int, std::unique_ptr<void, FPDFPageDeleter>> loaded_pages;
125 
126   // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
127   // make use of it.
128   FPDF_FORMHANDLE form_handle;
129 };
130 
ToPDFiumTestFormFillInfo(FPDF_FORMFILLINFO * form_fill_info)131 FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
132     FPDF_FORMFILLINFO* form_fill_info) {
133   return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
134 }
135 
CheckDimensions(int stride,int width,int height)136 bool CheckDimensions(int stride, int width, int height) {
137   if (stride < 0 || width < 0 || height < 0)
138     return false;
139   if (height > 0 && width > INT_MAX / height)
140     return false;
141   return true;
142 }
143 
OutputMD5Hash(const char * file_name,const char * buffer,int len)144 void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
145   // Get the MD5 hash and write it to stdout.
146   std::string hash =
147       GenerateMD5Base16(reinterpret_cast<const uint8_t*>(buffer), len);
148   printf("MD5:%s:%s\n", file_name, hash.c_str());
149 }
150 
WritePpm(const char * pdf_name,int num,const void * buffer_void,int stride,int width,int height)151 std::string WritePpm(const char* pdf_name,
152                      int num,
153                      const void* buffer_void,
154                      int stride,
155                      int width,
156                      int height) {
157   const auto* buffer = reinterpret_cast<const char*>(buffer_void);
158 
159   if (!CheckDimensions(stride, width, height))
160     return "";
161 
162   int out_len = width * height;
163   if (out_len > INT_MAX / 3)
164     return "";
165   out_len *= 3;
166 
167   char filename[256];
168   snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
169   FILE* fp = fopen(filename, "wb");
170   if (!fp)
171     return "";
172   fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
173   // Source data is B, G, R, unused.
174   // Dest data is R, G, B.
175   std::vector<char> result(out_len);
176   for (int h = 0; h < height; ++h) {
177     const char* src_line = buffer + (stride * h);
178     char* dest_line = result.data() + (width * h * 3);
179     for (int w = 0; w < width; ++w) {
180       // R
181       dest_line[w * 3] = src_line[(w * 4) + 2];
182       // G
183       dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
184       // B
185       dest_line[(w * 3) + 2] = src_line[w * 4];
186     }
187   }
188   if (fwrite(result.data(), out_len, 1, fp) != 1)
189     fprintf(stderr, "Failed to write to %s\n", filename);
190   fclose(fp);
191   return std::string(filename);
192 }
193 
WriteText(FPDF_PAGE page,const char * pdf_name,int num)194 void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
195   char filename[256];
196   int chars_formatted =
197       snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
198   if (chars_formatted < 0 ||
199       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
200     fprintf(stderr, "Filename %s is too long\n", filename);
201     return;
202   }
203 
204   FILE* fp = fopen(filename, "w");
205   if (!fp) {
206     fprintf(stderr, "Failed to open %s for output\n", filename);
207     return;
208   }
209 
210   // Output in UTF32-LE.
211   uint32_t bom = 0x0000FEFF;
212   if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
213     fprintf(stderr, "Failed to write to %s\n", filename);
214     (void)fclose(fp);
215     return;
216   }
217 
218   std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
219   for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
220     uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
221     if (fwrite(&c, sizeof(c), 1, fp) != 1) {
222       fprintf(stderr, "Failed to write to %s\n", filename);
223       break;
224     }
225   }
226   (void)fclose(fp);
227 }
228 
AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype)229 const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
230   if (subtype == FPDF_ANNOT_TEXT)
231     return "Text";
232   if (subtype == FPDF_ANNOT_LINK)
233     return "Link";
234   if (subtype == FPDF_ANNOT_FREETEXT)
235     return "FreeText";
236   if (subtype == FPDF_ANNOT_LINE)
237     return "Line";
238   if (subtype == FPDF_ANNOT_SQUARE)
239     return "Square";
240   if (subtype == FPDF_ANNOT_CIRCLE)
241     return "Circle";
242   if (subtype == FPDF_ANNOT_POLYGON)
243     return "Polygon";
244   if (subtype == FPDF_ANNOT_POLYLINE)
245     return "PolyLine";
246   if (subtype == FPDF_ANNOT_HIGHLIGHT)
247     return "Highlight";
248   if (subtype == FPDF_ANNOT_UNDERLINE)
249     return "Underline";
250   if (subtype == FPDF_ANNOT_SQUIGGLY)
251     return "Squiggly";
252   if (subtype == FPDF_ANNOT_STRIKEOUT)
253     return "StrikeOut";
254   if (subtype == FPDF_ANNOT_STAMP)
255     return "Stamp";
256   if (subtype == FPDF_ANNOT_CARET)
257     return "Caret";
258   if (subtype == FPDF_ANNOT_INK)
259     return "Ink";
260   if (subtype == FPDF_ANNOT_POPUP)
261     return "Popup";
262   if (subtype == FPDF_ANNOT_FILEATTACHMENT)
263     return "FileAttachment";
264   if (subtype == FPDF_ANNOT_SOUND)
265     return "Sound";
266   if (subtype == FPDF_ANNOT_MOVIE)
267     return "Movie";
268   if (subtype == FPDF_ANNOT_WIDGET)
269     return "Widget";
270   if (subtype == FPDF_ANNOT_SCREEN)
271     return "Screen";
272   if (subtype == FPDF_ANNOT_PRINTERMARK)
273     return "PrinterMark";
274   if (subtype == FPDF_ANNOT_TRAPNET)
275     return "TrapNet";
276   if (subtype == FPDF_ANNOT_WATERMARK)
277     return "Watermark";
278   if (subtype == FPDF_ANNOT_THREED)
279     return "3D";
280   if (subtype == FPDF_ANNOT_RICHMEDIA)
281     return "RichMedia";
282   if (subtype == FPDF_ANNOT_XFAWIDGET)
283     return "XFAWidget";
284   NOTREACHED();
285   return "";
286 }
287 
AppendFlagString(const char * flag,std::string * output)288 void AppendFlagString(const char* flag, std::string* output) {
289   if (!output->empty())
290     *output += ", ";
291   *output += flag;
292 }
293 
AnnotFlagsToString(int flags)294 std::string AnnotFlagsToString(int flags) {
295   std::string str;
296   if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
297     AppendFlagString("Invisible", &str);
298   if (flags & FPDF_ANNOT_FLAG_HIDDEN)
299     AppendFlagString("Hidden", &str);
300   if (flags & FPDF_ANNOT_FLAG_PRINT)
301     AppendFlagString("Print", &str);
302   if (flags & FPDF_ANNOT_FLAG_NOZOOM)
303     AppendFlagString("NoZoom", &str);
304   if (flags & FPDF_ANNOT_FLAG_NOROTATE)
305     AppendFlagString("NoRotate", &str);
306   if (flags & FPDF_ANNOT_FLAG_NOVIEW)
307     AppendFlagString("NoView", &str);
308   if (flags & FPDF_ANNOT_FLAG_READONLY)
309     AppendFlagString("ReadOnly", &str);
310   if (flags & FPDF_ANNOT_FLAG_LOCKED)
311     AppendFlagString("Locked", &str);
312   if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
313     AppendFlagString("ToggleNoView", &str);
314   return str;
315 }
316 
PageObjectTypeToCString(int type)317 const char* PageObjectTypeToCString(int type) {
318   if (type == FPDF_PAGEOBJ_TEXT)
319     return "Text";
320   if (type == FPDF_PAGEOBJ_PATH)
321     return "Path";
322   if (type == FPDF_PAGEOBJ_IMAGE)
323     return "Image";
324   if (type == FPDF_PAGEOBJ_SHADING)
325     return "Shading";
326   if (type == FPDF_PAGEOBJ_FORM)
327     return "Form";
328   NOTREACHED();
329   return "";
330 }
331 
WriteAnnot(FPDF_PAGE page,const char * pdf_name,int num)332 void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
333   // Open the output text file.
334   char filename[256];
335   int chars_formatted =
336       snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
337   if (chars_formatted < 0 ||
338       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
339     fprintf(stderr, "Filename %s is too long\n", filename);
340     return;
341   }
342   FILE* fp = fopen(filename, "w");
343   if (!fp) {
344     fprintf(stderr, "Failed to open %s for output\n", filename);
345     return;
346   }
347 
348   int annot_count = FPDFPage_GetAnnotCount(page);
349   fprintf(fp, "Number of annotations: %d\n\n", annot_count);
350 
351   // Iterate through all annotations on this page.
352   for (int i = 0; i < annot_count; ++i) {
353     // Retrieve the annotation object and its subtype.
354     fprintf(fp, "Annotation #%d:\n", i + 1);
355     FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, i);
356     if (!annot) {
357       fprintf(fp, "Failed to retrieve annotation!\n\n");
358       continue;
359     }
360     FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
361     fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
362 
363     // Retrieve the annotation flags.
364     fprintf(fp, "Flags set: %s\n",
365             AnnotFlagsToString(FPDFAnnot_GetFlags(annot)).c_str());
366 
367     // Retrieve the annotation's object count and object types.
368     const int obj_count = FPDFAnnot_GetObjectCount(annot);
369     fprintf(fp, "Number of objects: %d\n", obj_count);
370     if (obj_count > 0) {
371       fprintf(fp, "Object types: ");
372       for (int j = 0; j < obj_count; ++j) {
373         const char* type = PageObjectTypeToCString(
374             FPDFPageObj_GetType(FPDFAnnot_GetObject(annot, j)));
375         fprintf(fp, "%s  ", type);
376       }
377       fprintf(fp, "\n");
378     }
379 
380     // Retrieve the annotation's color and interior color.
381     unsigned int R;
382     unsigned int G;
383     unsigned int B;
384     unsigned int A;
385     if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A)) {
386       fprintf(fp, "Failed to retrieve color.\n");
387     } else {
388       fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
389     }
390     if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_InteriorColor, &R, &G,
391                             &B, &A)) {
392       fprintf(fp, "Failed to retrieve interior color.\n");
393     } else {
394       fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
395     }
396 
397     // Retrieve the annotation's contents and author.
398     static constexpr char kContentsKey[] = "Contents";
399     static constexpr char kAuthorKey[] = "T";
400     unsigned long len =
401         FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
402     std::vector<char> buf(len);
403     FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len);
404     fprintf(fp, "Content: %ls\n",
405             GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
406                 .c_str());
407     len = FPDFAnnot_GetStringValue(annot, kAuthorKey, nullptr, 0);
408     buf.clear();
409     buf.resize(len);
410     FPDFAnnot_GetStringValue(annot, kAuthorKey, buf.data(), len);
411     fprintf(fp, "Author: %ls\n",
412             GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
413                 .c_str());
414 
415     // Retrieve the annotation's quadpoints if it is a markup annotation.
416     if (FPDFAnnot_HasAttachmentPoints(annot)) {
417       FS_QUADPOINTSF quadpoints;
418       if (FPDFAnnot_GetAttachmentPoints(annot, &quadpoints)) {
419         fprintf(fp,
420                 "Quadpoints: (%.3f, %.3f), (%.3f, %.3f), (%.3f, %.3f), (%.3f, "
421                 "%.3f)\n",
422                 quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2,
423                 quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4);
424       } else {
425         fprintf(fp, "Failed to retrieve quadpoints.\n");
426       }
427     }
428 
429     // Retrieve the annotation's rectangle coordinates.
430     FS_RECTF rect;
431     if (FPDFAnnot_GetRect(annot, &rect)) {
432       fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
433               rect.left, rect.bottom, rect.right, rect.top);
434     } else {
435       fprintf(fp, "Failed to retrieve annotation rectangle.\n");
436     }
437 
438     FPDFPage_CloseAnnot(annot);
439   }
440 
441   (void)fclose(fp);
442 }
443 
WritePng(const char * pdf_name,int num,const void * buffer_void,int stride,int width,int height)444 std::string WritePng(const char* pdf_name,
445                      int num,
446                      const void* buffer_void,
447                      int stride,
448                      int width,
449                      int height) {
450   if (!CheckDimensions(stride, width, height))
451     return "";
452 
453   std::vector<unsigned char> png_encoding;
454   const auto* buffer = static_cast<const unsigned char*>(buffer_void);
455   if (!image_diff_png::EncodeBGRAPNG(
456           buffer, width, height, stride, false, &png_encoding)) {
457     fprintf(stderr, "Failed to convert bitmap to PNG\n");
458     return "";
459   }
460 
461   char filename[256];
462   int chars_formatted = snprintf(
463       filename, sizeof(filename), "%s.%d.png", pdf_name, num);
464   if (chars_formatted < 0 ||
465       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
466     fprintf(stderr, "Filename %s is too long\n", filename);
467     return "";
468   }
469 
470   FILE* fp = fopen(filename, "wb");
471   if (!fp) {
472     fprintf(stderr, "Failed to open %s for output\n", filename);
473     return "";
474   }
475 
476   size_t bytes_written = fwrite(
477       &png_encoding.front(), 1, png_encoding.size(), fp);
478   if (bytes_written != png_encoding.size())
479     fprintf(stderr, "Failed to write to %s\n", filename);
480 
481   (void)fclose(fp);
482   return std::string(filename);
483 }
484 
485 #ifdef _WIN32
WriteBmp(const char * pdf_name,int num,const void * buffer,int stride,int width,int height)486 std::string WriteBmp(const char* pdf_name,
487                      int num,
488                      const void* buffer,
489                      int stride,
490                      int width,
491                      int height) {
492   if (!CheckDimensions(stride, width, height))
493     return "";
494 
495   int out_len = stride * height;
496   if (out_len > INT_MAX / 3)
497     return "";
498 
499   char filename[256];
500   snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
501   FILE* fp = fopen(filename, "wb");
502   if (!fp)
503     return "";
504 
505   BITMAPINFO bmi = {};
506   bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
507   bmi.bmiHeader.biWidth = width;
508   bmi.bmiHeader.biHeight = -height;  // top-down image
509   bmi.bmiHeader.biPlanes = 1;
510   bmi.bmiHeader.biBitCount = 32;
511   bmi.bmiHeader.biCompression = BI_RGB;
512   bmi.bmiHeader.biSizeImage = 0;
513 
514   BITMAPFILEHEADER file_header = {};
515   file_header.bfType = 0x4d42;
516   file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
517   file_header.bfOffBits = file_header.bfSize - out_len;
518 
519   if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
520       fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
521       fwrite(buffer, out_len, 1, fp) != 1) {
522     fprintf(stderr, "Failed to write to %s\n", filename);
523   }
524   fclose(fp);
525   return std::string(filename);
526 }
527 
WriteEmf(FPDF_PAGE page,const char * pdf_name,int num)528 void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
529   char filename[256];
530   snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
531 
532   HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
533 
534   int width = static_cast<int>(FPDF_GetPageWidth(page));
535   int height = static_cast<int>(FPDF_GetPageHeight(page));
536   HRGN rgn = CreateRectRgn(0, 0, width, height);
537   SelectClipRgn(dc, rgn);
538   DeleteObject(rgn);
539 
540   SelectObject(dc, GetStockObject(NULL_PEN));
541   SelectObject(dc, GetStockObject(WHITE_BRUSH));
542   // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
543   Rectangle(dc, 0, 0, width + 1, height + 1);
544 
545   FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
546                   FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
547 
548   DeleteEnhMetaFile(CloseEnhMetaFile(dc));
549 }
550 
EnhMetaFileProc(HDC hdc,HANDLETABLE * handle_table,const ENHMETARECORD * record,int objects_count,LPARAM param)551 int CALLBACK EnhMetaFileProc(HDC hdc,
552                              HANDLETABLE* handle_table,
553                              const ENHMETARECORD* record,
554                              int objects_count,
555                              LPARAM param) {
556   std::vector<const ENHMETARECORD*>& items =
557       *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
558   items.push_back(record);
559   return 1;
560 }
561 
WritePS(FPDF_PAGE page,const char * pdf_name,int num)562 void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
563   char filename[256];
564   snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
565   FILE* fp = fopen(filename, "wb");
566   if (!fp)
567     return;
568 
569   HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
570 
571   int width = static_cast<int>(FPDF_GetPageWidth(page));
572   int height = static_cast<int>(FPDF_GetPageHeight(page));
573   FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
574                   FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
575 
576   HENHMETAFILE emf = CloseEnhMetaFile(dc);
577   std::vector<const ENHMETARECORD*> items;
578   EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
579   for (const ENHMETARECORD* record : items) {
580     if (record->iType != EMR_GDICOMMENT)
581       continue;
582 
583     const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
584     const char* data = reinterpret_cast<const char*>(comment->Data);
585     uint16_t size = *reinterpret_cast<const uint16_t*>(data);
586     if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
587       fprintf(stderr, "Failed to write to %s\n", filename);
588       break;
589     }
590   }
591   fclose(fp);
592   DeleteEnhMetaFile(emf);
593 }
594 #endif  // _WIN32
595 
596 #ifdef PDF_ENABLE_SKIA
WriteSkp(const char * pdf_name,int num,SkPictureRecorder * recorder)597 std::string WriteSkp(const char* pdf_name,
598                      int num,
599                      SkPictureRecorder* recorder) {
600   char filename[256];
601   int chars_formatted =
602       snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
603 
604   if (chars_formatted < 0 ||
605       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
606     fprintf(stderr, "Filename %s is too long\n", filename);
607     return "";
608   }
609 
610   sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
611   SkFILEWStream wStream(filename);
612   picture->serialize(&wStream);
613   return std::string(filename);
614 }
615 #endif
616 
617 // These example JS platform callback handlers are entirely optional,
618 // and exist here to show the flow of information from a document back
619 // to the embedder.
ExampleAppAlert(IPDF_JSPLATFORM *,FPDF_WIDESTRING msg,FPDF_WIDESTRING title,int type,int icon)620 int ExampleAppAlert(IPDF_JSPLATFORM*,
621                     FPDF_WIDESTRING msg,
622                     FPDF_WIDESTRING title,
623                     int type,
624                     int icon) {
625   printf("%ls", GetPlatformWString(title).c_str());
626   if (icon || type)
627     printf("[icon=%d,type=%d]", icon, type);
628   printf(": %ls\n", GetPlatformWString(msg).c_str());
629   return 0;
630 }
631 
ExampleAppResponse(IPDF_JSPLATFORM *,FPDF_WIDESTRING question,FPDF_WIDESTRING title,FPDF_WIDESTRING default_value,FPDF_WIDESTRING label,FPDF_BOOL is_password,void * response,int length)632 int ExampleAppResponse(IPDF_JSPLATFORM*,
633                        FPDF_WIDESTRING question,
634                        FPDF_WIDESTRING title,
635                        FPDF_WIDESTRING default_value,
636                        FPDF_WIDESTRING label,
637                        FPDF_BOOL is_password,
638                        void* response,
639                        int length) {
640   printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
641          GetPlatformWString(title).c_str(),
642          GetPlatformWString(question).c_str(),
643          GetPlatformWString(default_value).c_str(),
644          GetPlatformWString(label).c_str(), is_password, length);
645 
646   // UTF-16, always LE regardless of platform.
647   auto* ptr = static_cast<uint8_t*>(response);
648   ptr[0] = 'N';
649   ptr[1] = 0;
650   ptr[2] = 'o';
651   ptr[3] = 0;
652   return 4;
653 }
654 
ExampleDocGotoPage(IPDF_JSPLATFORM *,int page_number)655 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
656   printf("Goto Page: %d\n", page_number);
657 }
658 
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)659 void ExampleDocMail(IPDF_JSPLATFORM*,
660                     void* mailData,
661                     int length,
662                     FPDF_BOOL UI,
663                     FPDF_WIDESTRING To,
664                     FPDF_WIDESTRING Subject,
665                     FPDF_WIDESTRING CC,
666                     FPDF_WIDESTRING BCC,
667                     FPDF_WIDESTRING Msg) {
668   printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
669          GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
670          GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
671          GetPlatformWString(Msg).c_str());
672 }
673 
ExampleUnsupportedHandler(UNSUPPORT_INFO *,int type)674 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
675   std::string feature = "Unknown";
676   switch (type) {
677     case FPDF_UNSP_DOC_XFAFORM:
678       feature = "XFA";
679       break;
680     case FPDF_UNSP_DOC_PORTABLECOLLECTION:
681       feature = "Portfolios_Packages";
682       break;
683     case FPDF_UNSP_DOC_ATTACHMENT:
684     case FPDF_UNSP_ANNOT_ATTACHMENT:
685       feature = "Attachment";
686       break;
687     case FPDF_UNSP_DOC_SECURITY:
688       feature = "Rights_Management";
689       break;
690     case FPDF_UNSP_DOC_SHAREDREVIEW:
691       feature = "Shared_Review";
692       break;
693     case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
694     case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
695     case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
696       feature = "Shared_Form";
697       break;
698     case FPDF_UNSP_ANNOT_3DANNOT:
699       feature = "3D";
700       break;
701     case FPDF_UNSP_ANNOT_MOVIE:
702       feature = "Movie";
703       break;
704     case FPDF_UNSP_ANNOT_SOUND:
705       feature = "Sound";
706       break;
707     case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
708     case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
709       feature = "Screen";
710       break;
711     case FPDF_UNSP_ANNOT_SIG:
712       feature = "Digital_Signature";
713       break;
714   }
715   printf("Unsupported feature: %s.\n", feature.c_str());
716 }
717 
ParseCommandLine(const std::vector<std::string> & args,Options * options,std::vector<std::string> * files)718 bool ParseCommandLine(const std::vector<std::string>& args,
719                       Options* options,
720                       std::vector<std::string>* files) {
721   if (args.empty())
722     return false;
723 
724   options->exe_path = args[0];
725   size_t cur_idx = 1;
726   for (; cur_idx < args.size(); ++cur_idx) {
727     const std::string& cur_arg = args[cur_idx];
728     if (cur_arg == "--show-config") {
729       options->show_config = true;
730     } else if (cur_arg == "--show-metadata") {
731       options->show_metadata = true;
732     } else if (cur_arg == "--send-events") {
733       options->send_events = true;
734     } else if (cur_arg == "--render-oneshot") {
735       options->render_oneshot = true;
736     } else if (cur_arg == "--save-attachments") {
737       options->save_attachments = true;
738     } else if (cur_arg == "--save-images") {
739       options->save_images = true;
740 #ifdef ENABLE_CALLGRIND
741     } else if (cur_arg == "--callgrind-delim") {
742       options->callgrind_delimiters = true;
743 #endif  // ENABLE_CALLGRIND
744     } else if (cur_arg == "--ppm") {
745       if (options->output_format != OUTPUT_NONE) {
746         fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
747         return false;
748       }
749       options->output_format = OUTPUT_PPM;
750     } else if (cur_arg == "--png") {
751       if (options->output_format != OUTPUT_NONE) {
752         fprintf(stderr, "Duplicate or conflicting --png argument\n");
753         return false;
754       }
755       options->output_format = OUTPUT_PNG;
756     } else if (cur_arg == "--txt") {
757       if (options->output_format != OUTPUT_NONE) {
758         fprintf(stderr, "Duplicate or conflicting --txt argument\n");
759         return false;
760       }
761       options->output_format = OUTPUT_TEXT;
762     } else if (cur_arg == "--annot") {
763       if (options->output_format != OUTPUT_NONE) {
764         fprintf(stderr, "Duplicate or conflicting --annot argument\n");
765         return false;
766       }
767       options->output_format = OUTPUT_ANNOT;
768 #ifdef PDF_ENABLE_SKIA
769     } else if (cur_arg == "--skp") {
770       if (options->output_format != OUTPUT_NONE) {
771         fprintf(stderr, "Duplicate or conflicting --skp argument\n");
772         return false;
773       }
774       options->output_format = OUTPUT_SKP;
775 #endif
776     } else if (cur_arg.size() > 11 &&
777                cur_arg.compare(0, 11, "--font-dir=") == 0) {
778       if (!options->font_directory.empty()) {
779         fprintf(stderr, "Duplicate --font-dir argument\n");
780         return false;
781       }
782       options->font_directory = cur_arg.substr(11);
783 #ifdef _WIN32
784     } else if (cur_arg == "--emf") {
785       if (options->output_format != OUTPUT_NONE) {
786         fprintf(stderr, "Duplicate or conflicting --emf argument\n");
787         return false;
788       }
789       options->output_format = OUTPUT_EMF;
790     } else if (cur_arg == "--ps2") {
791       if (options->output_format != OUTPUT_NONE) {
792         fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
793         return false;
794       }
795       options->output_format = OUTPUT_PS2;
796     } else if (cur_arg == "--ps3") {
797       if (options->output_format != OUTPUT_NONE) {
798         fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
799         return false;
800       }
801       options->output_format = OUTPUT_PS3;
802     } else if (cur_arg == "--bmp") {
803       if (options->output_format != OUTPUT_NONE) {
804         fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
805         return false;
806       }
807       options->output_format = OUTPUT_BMP;
808 #endif  // _WIN32
809 
810 #ifdef PDF_ENABLE_V8
811 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
812     } else if (cur_arg.size() > 10 &&
813                cur_arg.compare(0, 10, "--bin-dir=") == 0) {
814       if (!options->bin_directory.empty()) {
815         fprintf(stderr, "Duplicate --bin-dir argument\n");
816         return false;
817       }
818       options->bin_directory = cur_arg.substr(10);
819 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
820 #endif  // PDF_ENABLE_V8
821 
822     } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
823       if (!options->scale_factor_as_string.empty()) {
824         fprintf(stderr, "Duplicate --scale argument\n");
825         return false;
826       }
827       options->scale_factor_as_string = cur_arg.substr(8);
828     } else if (cur_arg == "--show-structure") {
829       if (options->output_format != OUTPUT_NONE) {
830         fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
831         return false;
832       }
833       options->output_format = OUTPUT_STRUCTURE;
834     } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
835       if (options->pages) {
836         fprintf(stderr, "Duplicate --pages argument\n");
837         return false;
838       }
839       options->pages = true;
840       const std::string pages_string = cur_arg.substr(8);
841       size_t first_dash = pages_string.find("-");
842       if (first_dash == std::string::npos) {
843         std::stringstream(pages_string) >> options->first_page;
844         options->last_page = options->first_page;
845       } else {
846         std::stringstream(pages_string.substr(0, first_dash)) >>
847             options->first_page;
848         std::stringstream(pages_string.substr(first_dash + 1)) >>
849             options->last_page;
850       }
851     } else if (cur_arg == "--md5") {
852       options->md5 = true;
853     } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
854       fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
855       return false;
856     } else {
857       break;
858     }
859   }
860   for (size_t i = cur_idx; i < args.size(); i++)
861     files->push_back(args[i]);
862 
863   return true;
864 }
865 
PrintLastError()866 void PrintLastError() {
867   unsigned long err = FPDF_GetLastError();
868   fprintf(stderr, "Load pdf docs unsuccessful: ");
869   switch (err) {
870     case FPDF_ERR_SUCCESS:
871       fprintf(stderr, "Success");
872       break;
873     case FPDF_ERR_UNKNOWN:
874       fprintf(stderr, "Unknown error");
875       break;
876     case FPDF_ERR_FILE:
877       fprintf(stderr, "File not found or could not be opened");
878       break;
879     case FPDF_ERR_FORMAT:
880       fprintf(stderr, "File not in PDF format or corrupted");
881       break;
882     case FPDF_ERR_PASSWORD:
883       fprintf(stderr, "Password required or incorrect password");
884       break;
885     case FPDF_ERR_SECURITY:
886       fprintf(stderr, "Unsupported security scheme");
887       break;
888     case FPDF_ERR_PAGE:
889       fprintf(stderr, "Page not found or content error");
890       break;
891     default:
892       fprintf(stderr, "Unknown error %ld", err);
893   }
894   fprintf(stderr, ".\n");
895   return;
896 }
897 
Is_Data_Avail(FX_FILEAVAIL * avail,size_t offset,size_t size)898 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
899   return true;
900 }
901 
Add_Segment(FX_DOWNLOADHINTS * hints,size_t offset,size_t size)902 void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
903 
SendPageEvents(FPDF_FORMHANDLE form,FPDF_PAGE page,const std::string & events)904 void SendPageEvents(FPDF_FORMHANDLE form,
905                     FPDF_PAGE page,
906                     const std::string& events) {
907   auto lines = StringSplit(events, '\n');
908   for (auto line : lines) {
909     auto command = StringSplit(line, '#');
910     if (command[0].empty())
911       continue;
912     auto tokens = StringSplit(command[0], ',');
913     if (tokens[0] == "charcode") {
914       if (tokens.size() == 2) {
915         int keycode = atoi(tokens[1].c_str());
916         FORM_OnChar(form, page, keycode, 0);
917       } else {
918         fprintf(stderr, "charcode: bad args\n");
919       }
920     } else if (tokens[0] == "keycode") {
921       if (tokens.size() == 2) {
922         int keycode = atoi(tokens[1].c_str());
923         FORM_OnKeyDown(form, page, keycode, 0);
924         FORM_OnKeyUp(form, page, keycode, 0);
925       } else {
926         fprintf(stderr, "keycode: bad args\n");
927       }
928     } else if (tokens[0] == "mousedown") {
929       if (tokens.size() == 4) {
930         int x = atoi(tokens[2].c_str());
931         int y = atoi(tokens[3].c_str());
932         if (tokens[1] == "left")
933           FORM_OnLButtonDown(form, page, 0, x, y);
934 #ifdef PDF_ENABLE_XFA
935         else if (tokens[1] == "right")
936           FORM_OnRButtonDown(form, page, 0, x, y);
937 #endif
938         else
939           fprintf(stderr, "mousedown: bad button name\n");
940       } else {
941         fprintf(stderr, "mousedown: bad args\n");
942       }
943     } else if (tokens[0] == "mouseup") {
944       if (tokens.size() == 4) {
945         int x = atoi(tokens[2].c_str());
946         int y = atoi(tokens[3].c_str());
947         if (tokens[1] == "left")
948           FORM_OnLButtonUp(form, page, 0, x, y);
949 #ifdef PDF_ENABLE_XFA
950         else if (tokens[1] == "right")
951           FORM_OnRButtonUp(form, page, 0, x, y);
952 #endif
953         else
954           fprintf(stderr, "mouseup: bad button name\n");
955       } else {
956         fprintf(stderr, "mouseup: bad args\n");
957       }
958     } else if (tokens[0] == "mousemove") {
959       if (tokens.size() == 3) {
960         int x = atoi(tokens[1].c_str());
961         int y = atoi(tokens[2].c_str());
962         FORM_OnMouseMove(form, page, 0, x, y);
963       } else {
964         fprintf(stderr, "mousemove: bad args\n");
965       }
966     } else if (tokens[0] == "focus") {
967       if (tokens.size() == 3) {
968         int x = atoi(tokens[1].c_str());
969         int y = atoi(tokens[2].c_str());
970         FORM_OnFocus(form, page, 0, x, y);
971       } else {
972         fprintf(stderr, "focus: bad args\n");
973       }
974     } else {
975       fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
976     }
977   }
978 }
979 
GetPageForIndex(FPDF_FORMFILLINFO * param,FPDF_DOCUMENT doc,int index)980 FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
981                           FPDF_DOCUMENT doc,
982                           int index) {
983   FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
984       ToPDFiumTestFormFillInfo(param);
985   auto& loaded_pages = form_fill_info->loaded_pages;
986   auto iter = loaded_pages.find(index);
987   if (iter != loaded_pages.end())
988     return iter->second.get();
989 
990   FPDF_PAGE page = FPDF_LoadPage(doc, index);
991   if (!page)
992     return nullptr;
993 
994   FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
995   FORM_OnAfterLoadPage(page, form_handle);
996   FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
997   loaded_pages[index].reset(page);
998   return page;
999 }
1000 
ConvertToWString(const unsigned short * buf,unsigned long buf_size)1001 std::wstring ConvertToWString(const unsigned short* buf,
1002                               unsigned long buf_size) {
1003   std::wstring result;
1004   result.reserve(buf_size);
1005   std::copy(buf, buf + buf_size, std::back_inserter(result));
1006   return result;
1007 }
1008 
DumpChildStructure(FPDF_STRUCTELEMENT child,int indent)1009 void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
1010   static const size_t kBufSize = 1024;
1011   unsigned short buf[kBufSize];
1012   unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
1013   printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
1014 
1015   memset(buf, 0, sizeof(buf));
1016   len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
1017   if (len > 0)
1018     printf(": '%ls'", ConvertToWString(buf, len).c_str());
1019 
1020   memset(buf, 0, sizeof(buf));
1021   len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
1022   if (len > 0)
1023     printf(" (%ls)", ConvertToWString(buf, len).c_str());
1024   printf("\n");
1025 
1026   for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
1027     FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
1028     // If the child is not an Element then this will return null. This can
1029     // happen if the element is things like an object reference or a stream.
1030     if (!sub_child)
1031       continue;
1032 
1033     DumpChildStructure(sub_child, indent + 1);
1034   }
1035 }
1036 
DumpPageStructure(FPDF_PAGE page,const int page_idx)1037 void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
1038   std::unique_ptr<void, FPDFStructTreeDeleter> tree(
1039       FPDF_StructTree_GetForPage(page));
1040   if (!tree) {
1041     fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
1042     return;
1043   }
1044 
1045   printf("Structure Tree for Page %d\n", page_idx);
1046   for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
1047     FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
1048     if (!child) {
1049       fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
1050       continue;
1051     }
1052     DumpChildStructure(child, 0);
1053   }
1054   printf("\n\n");
1055 }
1056 
DumpMetaData(FPDF_DOCUMENT doc)1057 void DumpMetaData(FPDF_DOCUMENT doc) {
1058   constexpr const char* meta_tags[] = {"Title",        "Author",  "Subject",
1059                                        "Keywords",     "Creator", "Producer",
1060                                        "CreationDate", "ModDate"};
1061   for (const char* meta_tag : meta_tags) {
1062     char meta_buffer[4096];
1063     unsigned long len =
1064         FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
1065     if (!len)
1066       continue;
1067 
1068     auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
1069     printf("%-12s = %ls (%lu bytes)\n", meta_tag,
1070            GetPlatformWString(meta_string).c_str(), len);
1071   }
1072 }
1073 
SaveAttachments(FPDF_DOCUMENT doc,const std::string & name)1074 void SaveAttachments(FPDF_DOCUMENT doc, const std::string& name) {
1075   for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
1076     FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
1077 
1078     // Retrieve the attachment file name.
1079     std::string attachment_name;
1080     unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
1081     if (len) {
1082       std::vector<char> buf(len);
1083       unsigned long actual_len =
1084           FPDFAttachment_GetName(attachment, buf.data(), len);
1085       if (actual_len == len) {
1086         attachment_name =
1087             GetPlatformString(reinterpret_cast<unsigned short*>(buf.data()));
1088       }
1089     }
1090     if (attachment_name.empty()) {
1091       fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
1092       continue;
1093     }
1094 
1095     // Calculate the full attachment file name.
1096     char save_name[256];
1097     int chars_formatted =
1098         snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
1099                  attachment_name.c_str());
1100     if (chars_formatted < 0 ||
1101         static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
1102       fprintf(stderr, "Filename %s is too long\n", save_name);
1103       continue;
1104     }
1105 
1106     // Retrieve the attachment.
1107     len = FPDFAttachment_GetFile(attachment, nullptr, 0);
1108     std::vector<char> data_buf(len);
1109     if (len) {
1110       unsigned long actual_len =
1111           FPDFAttachment_GetFile(attachment, data_buf.data(), len);
1112       if (actual_len != len)
1113         data_buf.clear();
1114     }
1115     if (data_buf.empty()) {
1116       fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str());
1117       continue;
1118     }
1119 
1120     // Write the attachment file.
1121     FILE* fp = fopen(save_name, "wb");
1122     if (!fp) {
1123       fprintf(stderr, "Failed to open %s for saving attachment.\n", save_name);
1124       continue;
1125     }
1126 
1127     size_t written_len = fwrite(data_buf.data(), 1, len, fp);
1128     if (written_len == len) {
1129       fprintf(stderr, "Saved attachment \"%s\" as: %s.\n",
1130               attachment_name.c_str(), save_name);
1131     } else {
1132       fprintf(stderr, "Failed to write to %s\n", save_name);
1133     }
1134     fclose(fp);
1135   }
1136 }
1137 
SaveImages(FPDF_PAGE page,const char * pdf_name,int page_num)1138 void SaveImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
1139   for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
1140     FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
1141     if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE)
1142       continue;
1143 
1144     std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
1145         FPDFImageObj_GetBitmap(obj));
1146     if (!bitmap) {
1147       fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
1148               i + 1, page_num + 1);
1149       continue;
1150     }
1151 
1152     int format = FPDFBitmap_GetFormat(bitmap.get());
1153     if (format == FPDFBitmap_Unknown) {
1154       fprintf(stderr,
1155               "Image object #%d on page #%d has a bitmap of unknown format.\n",
1156               i + 1, page_num + 1);
1157       continue;
1158     }
1159 
1160     std::vector<unsigned char> png_encoding;
1161     const unsigned char* buffer =
1162         static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap.get()));
1163     int width = FPDFBitmap_GetWidth(bitmap.get());
1164     int height = FPDFBitmap_GetHeight(bitmap.get());
1165     int stride = FPDFBitmap_GetStride(bitmap.get());
1166     bool ret = false;
1167     switch (format) {
1168       case FPDFBitmap_Gray:
1169         ret = image_diff_png::EncodeGrayPNG(buffer, width, height, stride,
1170                                             &png_encoding);
1171         break;
1172       case FPDFBitmap_BGR:
1173         ret = image_diff_png::EncodeBGRPNG(buffer, width, height, stride,
1174                                            &png_encoding);
1175         break;
1176       case FPDFBitmap_BGRx:
1177         ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride, true,
1178                                             &png_encoding);
1179         break;
1180       case FPDFBitmap_BGRA:
1181         ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride,
1182                                             false, &png_encoding);
1183         break;
1184       default:
1185         NOTREACHED();
1186     }
1187     if (!ret) {
1188       fprintf(stderr,
1189               "Failed to convert image object #%d on page #%d to png.\n", i + 1,
1190               page_num + 1);
1191       continue;
1192     }
1193 
1194     char filename[256];
1195     int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png",
1196                                    pdf_name, page_num, i);
1197     if (chars_formatted < 0 ||
1198         static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
1199       fprintf(stderr, "Filename %s for saving image is too long\n", filename);
1200       continue;
1201     }
1202 
1203     FILE* fp = fopen(filename, "wb");
1204     if (!fp) {
1205       fprintf(stderr, "Failed to open %s for saving image.\n", filename);
1206       continue;
1207     }
1208 
1209     size_t bytes_written =
1210         fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
1211     if (bytes_written != png_encoding.size())
1212       fprintf(stderr, "Failed to write to %s.\n", filename);
1213     else
1214       fprintf(stderr, "Successfully wrote embedded image %s.\n", filename);
1215 
1216     (void)fclose(fp);
1217   }
1218 }
1219 
1220 // Note, for a client using progressive rendering you'd want to determine if you
1221 // need the rendering to pause instead of always saying |true|. This is for
1222 // testing to force the renderer to break whenever possible.
NeedToPauseNow(IFSDK_PAUSE * p)1223 FPDF_BOOL NeedToPauseNow(IFSDK_PAUSE* p) {
1224   return true;
1225 }
1226 
RenderPage(const std::string & name,FPDF_DOCUMENT doc,FPDF_FORMHANDLE form,FPDF_FORMFILLINFO_PDFiumTest * form_fill_info,const int page_index,const Options & options,const std::string & events)1227 bool RenderPage(const std::string& name,
1228                 FPDF_DOCUMENT doc,
1229                 FPDF_FORMHANDLE form,
1230                 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info,
1231                 const int page_index,
1232                 const Options& options,
1233                 const std::string& events) {
1234   FPDF_PAGE page = GetPageForIndex(form_fill_info, doc, page_index);
1235   if (!page)
1236     return false;
1237   if (options.send_events)
1238     SendPageEvents(form, page, events);
1239   if (options.save_images)
1240     SaveImages(page, name.c_str(), page_index);
1241   if (options.output_format == OUTPUT_STRUCTURE) {
1242     DumpPageStructure(page, page_index);
1243     return true;
1244   }
1245 
1246   std::unique_ptr<void, FPDFTextPageDeleter> text_page(FPDFText_LoadPage(page));
1247 
1248   double scale = 1.0;
1249   if (!options.scale_factor_as_string.empty())
1250     std::stringstream(options.scale_factor_as_string) >> scale;
1251 
1252   auto width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
1253   auto height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
1254   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
1255   std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
1256       FPDFBitmap_Create(width, height, alpha));
1257 
1258   if (bitmap) {
1259     FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
1260     FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
1261 
1262     if (options.render_oneshot) {
1263       // Note, client programs probably want to use this method instead of the
1264       // progressive calls. The progressive calls are if you need to pause the
1265       // rendering to update the UI, the PDF renderer will break when possible.
1266       FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0,
1267                             FPDF_ANNOT);
1268     } else {
1269       IFSDK_PAUSE pause;
1270       pause.version = 1;
1271       pause.NeedToPauseNow = &NeedToPauseNow;
1272 
1273       int rv = FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, width,
1274                                            height, 0, FPDF_ANNOT, &pause);
1275       while (rv == FPDF_RENDER_TOBECOUNTINUED)
1276         rv = FPDF_RenderPage_Continue(page, &pause);
1277     }
1278 
1279     FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, FPDF_ANNOT);
1280 
1281     if (!options.render_oneshot)
1282       FPDF_RenderPage_Close(page);
1283 
1284     int stride = FPDFBitmap_GetStride(bitmap.get());
1285     const char* buffer =
1286         reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
1287 
1288     std::string image_file_name;
1289     switch (options.output_format) {
1290 #ifdef _WIN32
1291       case OUTPUT_BMP:
1292         image_file_name =
1293             WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
1294         break;
1295 
1296       case OUTPUT_EMF:
1297         WriteEmf(page, name.c_str(), page_index);
1298         break;
1299 
1300       case OUTPUT_PS2:
1301       case OUTPUT_PS3:
1302         WritePS(page, name.c_str(), page_index);
1303         break;
1304 #endif
1305       case OUTPUT_TEXT:
1306         WriteText(page, name.c_str(), page_index);
1307         break;
1308 
1309       case OUTPUT_ANNOT:
1310         WriteAnnot(page, name.c_str(), page_index);
1311         break;
1312 
1313       case OUTPUT_PNG:
1314         image_file_name =
1315             WritePng(name.c_str(), page_index, buffer, stride, width, height);
1316         break;
1317 
1318       case OUTPUT_PPM:
1319         image_file_name =
1320             WritePpm(name.c_str(), page_index, buffer, stride, width, height);
1321         break;
1322 
1323 #ifdef PDF_ENABLE_SKIA
1324       case OUTPUT_SKP: {
1325         std::unique_ptr<SkPictureRecorder> recorder(
1326             reinterpret_cast<SkPictureRecorder*>(
1327                 FPDF_RenderPageSkp(page, width, height)));
1328         FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0);
1329         image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
1330       } break;
1331 #endif
1332       default:
1333         break;
1334     }
1335 
1336     // Write the filename and the MD5 of the buffer to stdout if we wrote a
1337     // file.
1338     if (options.md5 && !image_file_name.empty())
1339       OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
1340   } else {
1341     fprintf(stderr, "Page was too large to be rendered.\n");
1342   }
1343 
1344   FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
1345   FORM_OnBeforeClosePage(page, form);
1346   return !!bitmap;
1347 }
1348 
RenderPdf(const std::string & name,const char * pBuf,size_t len,const Options & options,const std::string & events)1349 void RenderPdf(const std::string& name,
1350                const char* pBuf,
1351                size_t len,
1352                const Options& options,
1353                const std::string& events) {
1354   IPDF_JSPLATFORM platform_callbacks = {};
1355   platform_callbacks.version = 3;
1356   platform_callbacks.app_alert = ExampleAppAlert;
1357   platform_callbacks.app_response = ExampleAppResponse;
1358   platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
1359   platform_callbacks.Doc_mail = ExampleDocMail;
1360 
1361   // The pdf_avail must outlive doc.
1362   std::unique_ptr<void, FPDFAvailDeleter> pdf_avail;
1363   // The document must outlive |form_callbacks.loaded_pages|.
1364   std::unique_ptr<void, FPDFDocumentDeleter> doc;
1365   FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
1366 #ifdef PDF_ENABLE_XFA
1367   form_callbacks.version = 2;
1368 #else   // PDF_ENABLE_XFA
1369   form_callbacks.version = 1;
1370 #endif  // PDF_ENABLE_XFA
1371   form_callbacks.FFI_GetPage = GetPageForIndex;
1372   form_callbacks.m_pJsPlatform = &platform_callbacks;
1373 
1374   TestLoader loader(pBuf, len);
1375   FPDF_FILEACCESS file_access = {};
1376   file_access.m_FileLen = static_cast<unsigned long>(len);
1377   file_access.m_GetBlock = TestLoader::GetBlock;
1378   file_access.m_Param = &loader;
1379 
1380   FX_FILEAVAIL file_avail = {};
1381   file_avail.version = 1;
1382   file_avail.IsDataAvail = Is_Data_Avail;
1383 
1384   FX_DOWNLOADHINTS hints = {};
1385   hints.version = 1;
1386   hints.AddSegment = Add_Segment;
1387 
1388   int nRet = PDF_DATA_NOTAVAIL;
1389   bool bIsLinearized = false;
1390   pdf_avail.reset(FPDFAvail_Create(&file_avail, &file_access));
1391 
1392   if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
1393     doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
1394     if (doc) {
1395       while (nRet == PDF_DATA_NOTAVAIL)
1396         nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
1397 
1398       if (nRet == PDF_DATA_ERROR) {
1399         fprintf(stderr, "Unknown error in checking if doc was available.\n");
1400         return;
1401       }
1402       nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
1403       if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
1404         fprintf(stderr,
1405                 "Error %d was returned in checking if form was available.\n",
1406                 nRet);
1407         return;
1408       }
1409       bIsLinearized = true;
1410     }
1411   } else {
1412     doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
1413   }
1414 
1415   if (!doc) {
1416     PrintLastError();
1417     return;
1418   }
1419 
1420   (void)FPDF_GetDocPermissions(doc.get());
1421 
1422   if (options.show_metadata)
1423     DumpMetaData(doc.get());
1424 
1425   if (options.save_attachments)
1426     SaveAttachments(doc.get(), name);
1427 
1428   std::unique_ptr<void, FPDFFormHandleDeleter> form(
1429       FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
1430   form_callbacks.form_handle = form.get();
1431 
1432 #ifdef PDF_ENABLE_XFA
1433   int doc_type = FPDF_GetFormType(doc.get());
1434   if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
1435     if (!FPDF_LoadXFA(doc.get()))
1436       fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
1437   }
1438 #endif  // PDF_ENABLE_XFA
1439 
1440   FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
1441   FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
1442   FORM_DoDocumentJSAction(form.get());
1443   FORM_DoDocumentOpenAction(form.get());
1444 
1445 #if _WIN32
1446   if (options.output_format == OUTPUT_PS2)
1447     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
1448   else if (options.output_format == OUTPUT_PS3)
1449     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
1450 #endif
1451 
1452   int page_count = FPDF_GetPageCount(doc.get());
1453   int rendered_pages = 0;
1454   int bad_pages = 0;
1455   int first_page = options.pages ? options.first_page : 0;
1456   int last_page = options.pages ? options.last_page + 1 : page_count;
1457   for (int i = first_page; i < last_page; ++i) {
1458     if (bIsLinearized) {
1459       nRet = PDF_DATA_NOTAVAIL;
1460       while (nRet == PDF_DATA_NOTAVAIL)
1461         nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
1462 
1463       if (nRet == PDF_DATA_ERROR) {
1464         fprintf(stderr, "Unknown error in checking if page %d is available.\n",
1465                 i);
1466         return;
1467       }
1468     }
1469     if (RenderPage(name, doc.get(), form.get(), &form_callbacks, i, options,
1470                    events)) {
1471       ++rendered_pages;
1472     } else {
1473       ++bad_pages;
1474     }
1475   }
1476 
1477   FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
1478   fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
1479   if (bad_pages)
1480     fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
1481 }
1482 
ShowConfig()1483 void ShowConfig() {
1484   std::string config;
1485   std::string maybe_comma;
1486 #if PDF_ENABLE_V8
1487   config.append(maybe_comma);
1488   config.append("V8");
1489   maybe_comma = ",";
1490 #endif  // PDF_ENABLE_V8
1491 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1492   config.append(maybe_comma);
1493   config.append("V8_EXTERNAL");
1494   maybe_comma = ",";
1495 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
1496 #ifdef PDF_ENABLE_XFA
1497   config.append(maybe_comma);
1498   config.append("XFA");
1499   maybe_comma = ",";
1500 #endif  // PDF_ENABLE_XFA
1501 #ifdef PDF_ENABLE_ASAN
1502   config.append(maybe_comma);
1503   config.append("ASAN");
1504   maybe_comma = ",";
1505 #endif  // PDF_ENABLE_ASAN
1506   printf("%s\n", config.c_str());
1507 }
1508 
1509 constexpr char kUsageString[] =
1510     "Usage: pdfium_test [OPTION] [FILE]...\n"
1511     "  --show-config       - print build options and exit\n"
1512     "  --show-metadata     - print the file metadata\n"
1513     "  --show-structure    - print the structure elements from the document\n"
1514     "  --send-events       - send input described by .evt file\n"
1515     "  --render-oneshot    - render image without using progressive renderer\n"
1516     "  --save-attachments  - write embedded attachments "
1517     "<pdf-name>.attachment.<attachment-name>\n"
1518     "  --save-images       - write embedded images "
1519     "<pdf-name>.<page-number>.<object-number>.png\n"
1520 #ifdef ENABLE_CALLGRIND
1521     "  --callgrind-delim   - delimit interesting section when using callgrind\n"
1522 #endif  // ENABLE_CALLGRIND
1523     "  --bin-dir=<path>    - override path to v8 external data\n"
1524     "  --font-dir=<path>   - override path to external fonts\n"
1525     "  --scale=<number>    - scale output size by number (e.g. 0.5)\n"
1526     "  --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
1527 #ifdef _WIN32
1528     "  --bmp   - write page images <pdf-name>.<page-number>.bmp\n"
1529     "  --emf   - write page meta files <pdf-name>.<page-number>.emf\n"
1530     "  --ps2   - write page raw PostScript (Lvl 2) "
1531     "<pdf-name>.<page-number>.ps\n"
1532     "  --ps3   - write page raw PostScript (Lvl 3) "
1533     "<pdf-name>.<page-number>.ps\n"
1534 #endif  // _WIN32
1535     "  --txt   - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
1536     "  --png   - write page images <pdf-name>.<page-number>.png\n"
1537     "  --ppm   - write page images <pdf-name>.<page-number>.ppm\n"
1538     "  --annot - write annotation info <pdf-name>.<page-number>.annot.txt\n"
1539 #ifdef PDF_ENABLE_SKIA
1540     "  --skp   - write page images <pdf-name>.<page-number>.skp\n"
1541 #endif
1542     "  --md5   - write output image paths and their md5 hashes to stdout.\n"
1543     "";
1544 
1545 }  // namespace
1546 
main(int argc,const char * argv[])1547 int main(int argc, const char* argv[]) {
1548   std::vector<std::string> args(argv, argv + argc);
1549   Options options;
1550   std::vector<std::string> files;
1551   if (!ParseCommandLine(args, &options, &files)) {
1552     fprintf(stderr, "%s", kUsageString);
1553     return 1;
1554   }
1555 
1556   if (options.show_config) {
1557     ShowConfig();
1558     return 0;
1559   }
1560 
1561   if (files.empty()) {
1562     fprintf(stderr, "No input files.\n");
1563     return 1;
1564   }
1565 
1566 #ifdef PDF_ENABLE_V8
1567   v8::Platform* platform;
1568 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1569   v8::StartupData natives;
1570   v8::StartupData snapshot;
1571   InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
1572                         &snapshot, &platform);
1573 #else   // V8_USE_EXTERNAL_STARTUP_DATA
1574   InitializeV8ForPDFium(options.exe_path, &platform);
1575 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
1576 #endif  // PDF_ENABLE_V8
1577 
1578   FPDF_LIBRARY_CONFIG config;
1579   config.version = 2;
1580   config.m_pUserFontPaths = nullptr;
1581   config.m_pIsolate = nullptr;
1582   config.m_v8EmbedderSlot = 0;
1583 
1584   const char* path_array[2];
1585   if (!options.font_directory.empty()) {
1586     path_array[0] = options.font_directory.c_str();
1587     path_array[1] = nullptr;
1588     config.m_pUserFontPaths = path_array;
1589   }
1590   FPDF_InitLibraryWithConfig(&config);
1591 
1592   UNSUPPORT_INFO unsupported_info = {};
1593   unsupported_info.version = 1;
1594   unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
1595 
1596   FSDK_SetUnSpObjProcessHandler(&unsupported_info);
1597 
1598   for (const std::string& filename : files) {
1599     size_t file_length = 0;
1600     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
1601         GetFileContents(filename.c_str(), &file_length);
1602     if (!file_contents)
1603       continue;
1604     fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
1605 
1606 #ifdef ENABLE_CALLGRIND
1607     if (options.callgrind_delimiters)
1608       CALLGRIND_START_INSTRUMENTATION;
1609 #endif  // ENABLE_CALLGRIND
1610 
1611     std::string events;
1612     if (options.send_events) {
1613       std::string event_filename = filename;
1614       size_t event_length = 0;
1615       size_t extension_pos = event_filename.find(".pdf");
1616       if (extension_pos != std::string::npos) {
1617         event_filename.replace(extension_pos, 4, ".evt");
1618         if (access(event_filename.c_str(), R_OK) == 0) {
1619           fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1620           std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
1621               GetFileContents(event_filename.c_str(), &event_length);
1622           if (event_contents) {
1623             fprintf(stderr, "Sending events from: %s\n",
1624                     event_filename.c_str());
1625             events = std::string(event_contents.get(), event_length);
1626           }
1627         }
1628       }
1629     }
1630     RenderPdf(filename, file_contents.get(), file_length, options, events);
1631 
1632 #ifdef ENABLE_CALLGRIND
1633     if (options.callgrind_delimiters)
1634       CALLGRIND_STOP_INSTRUMENTATION;
1635 #endif  // ENABLE_CALLGRIND
1636   }
1637 
1638   FPDF_DestroyLibrary();
1639 #ifdef PDF_ENABLE_V8
1640   v8::V8::ShutdownPlatform();
1641   delete platform;
1642 
1643 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
1644   free(const_cast<char*>(natives.data));
1645   free(const_cast<char*>(snapshot.data));
1646 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
1647 #endif  // PDF_ENABLE_V8
1648 
1649   return 0;
1650 }
1651