blob: ee95e2f3c1a1ed5c4616c68504f9f54dc9ad4a45 [file] [log] [blame]
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001// 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
Jane Liu57f228d2017-07-13 18:10:30 -040010#include <bitset>
Lei Zhang9a25ede2017-06-22 00:05:12 -070011#include <iterator>
tonikitoo3e981582016-08-26 08:37:10 -070012#include <map>
Tom Sepezbfa2a972017-07-24 11:38:31 -070013#include <memory>
Tom Sepezdaa2e842015-01-29 15:44:37 -080014#include <sstream>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070015#include <string>
16#include <utility>
Tom Sepez5ee12d72014-12-17 16:24:01 -080017#include <vector>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070018
Cary Clark399be5b2016-03-14 16:51:29 -040019#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
20#define _SKIA_SUPPORT_
21#endif
22
Tom Sepez1d17a042017-03-16 13:22:47 -070023#include "public/cpp/fpdf_deleters.h"
Jane Liu4fd9a472017-06-01 18:56:09 -040024#include "public/fpdf_annot.h"
Jane Liu4442d452017-07-19 10:19:42 -040025#include "public/fpdf_attachment.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080026#include "public/fpdf_dataavail.h"
Lei Zhang453d96b2015-12-31 13:13:10 -080027#include "public/fpdf_edit.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080028#include "public/fpdf_ext.h"
29#include "public/fpdf_formfill.h"
Dan Sinclairaeadad12017-07-18 16:43:41 -040030#include "public/fpdf_progressive.h"
Dan Sinclairddcb6e72017-04-05 10:30:33 -040031#include "public/fpdf_structtree.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080032#include "public/fpdf_text.h"
33#include "public/fpdfview.h"
Lei Zhang143959d2017-06-22 12:20:58 -070034#include "testing/image_diff/image_diff_png.h"
Lei Zhangbde53d22015-11-12 22:21:30 -080035#include "testing/test_support.h"
Jane Liu4fd9a472017-06-01 18:56:09 -040036#include "third_party/base/logging.h"
Tom Sepezd831dc72015-10-19 16:04:22 -070037
thestigf2b940c2016-10-13 06:48:47 -070038#ifdef _WIN32
39#include <io.h>
40#else
41#include <unistd.h>
42#endif
43
Henrique Nakashima95ea7782017-07-11 16:42:43 -040044#ifdef ENABLE_CALLGRIND
45#include <valgrind/callgrind.h>
46#endif // ENABLE_CALLGRIND
47
Tom Sepez452b4f32015-10-13 09:27:27 -070048#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -080049#include "v8/include/libplatform/libplatform.h"
Tom Sepez1ed8a212015-05-11 15:25:39 -070050#include "v8/include/v8.h"
Lei Zhang8241df72015-11-06 14:38:48 -080051#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070052
Cary Clark399be5b2016-03-14 16:51:29 -040053#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
thestigf2b940c2016-10-13 06:48:47 -070058#ifdef _WIN32
59#define access _access
60#define snprintf _snprintf
61#define R_OK 4
62#endif
63
Vitaly Buka9e0177a2014-07-22 18:15:42 -070064enum OutputFormat {
65 OUTPUT_NONE,
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040066 OUTPUT_STRUCTURE,
dsinclairb63068f2016-06-16 07:58:09 -070067 OUTPUT_TEXT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070068 OUTPUT_PPM,
Tom Sepezaf18cb32015-02-05 15:06:01 -080069 OUTPUT_PNG,
Jane Liu4fd9a472017-06-01 18:56:09 -040070 OUTPUT_ANNOT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070071#ifdef _WIN32
72 OUTPUT_BMP,
73 OUTPUT_EMF,
Lei Zhangd1c9b452017-05-05 17:21:36 -070074 OUTPUT_PS2,
75 OUTPUT_PS3,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070076#endif
Cary Clark399be5b2016-03-14 16:51:29 -040077#ifdef PDF_ENABLE_SKIA
78 OUTPUT_SKP,
79#endif
Vitaly Buka9e0177a2014-07-22 18:15:42 -070080};
81
Lei Zhangd2be6462017-07-21 14:31:21 -070082namespace {
83
Tom Sepez5ee12d72014-12-17 16:24:01 -080084struct Options {
tsepezf09bdfa2016-04-18 16:08:26 -070085 Options()
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040086 : show_config(false),
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -040087 show_metadata(false),
npme3c73152016-11-14 09:14:52 -080088 send_events(false),
Dan Sinclairaeadad12017-07-18 16:43:41 -040089 render_oneshot(false),
Jane Liu4442d452017-07-19 10:19:42 -040090 save_attachments(false),
Henrique Nakashima95ea7782017-07-11 16:42:43 -040091#ifdef ENABLE_CALLGRIND
92 callgrind_delimiters(false),
93#endif // ENABLE_CALLGRIND
npme3c73152016-11-14 09:14:52 -080094 pages(false),
stephanafa05e972017-01-02 06:19:41 -080095 md5(false),
Henrique Nakashima95ea7782017-07-11 16:42:43 -040096 output_format(OUTPUT_NONE) {
97 }
Tom Sepez5ee12d72014-12-17 16:24:01 -080098
Tom Sepez2991d8d2016-01-15 16:02:48 -080099 bool show_config;
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -0400100 bool show_metadata;
tsepezf09bdfa2016-04-18 16:08:26 -0700101 bool send_events;
Dan Sinclairaeadad12017-07-18 16:43:41 -0400102 bool render_oneshot;
Jane Liu4442d452017-07-19 10:19:42 -0400103 bool save_attachments;
Henrique Nakashima95ea7782017-07-11 16:42:43 -0400104#ifdef ENABLE_CALLGRIND
105 bool callgrind_delimiters;
106#endif // ENABLE_CALLGRIND
npme3c73152016-11-14 09:14:52 -0800107 bool pages;
stephanafa05e972017-01-02 06:19:41 -0800108 bool md5;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800109 OutputFormat output_format;
Tom Sepezdaa2e842015-01-29 15:44:37 -0800110 std::string scale_factor_as_string;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800111 std::string exe_path;
112 std::string bin_directory;
Lei Zhang6f62d532015-09-23 15:31:44 -0700113 std::string font_directory;
npmfa20cd52016-11-14 13:33:40 -0800114 // 0-based page numbers to be rendered.
115 int first_page;
116 int last_page;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800117};
118
tonikitoo81d92f82016-09-21 12:44:56 -0700119struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
120 // Hold a map of the currently loaded pages in order to avoid them
121 // to get loaded twice.
Tom Sepezbfa2a972017-07-24 11:38:31 -0700122 std::map<int, std::unique_ptr<void, FPDFPageDeleter>> loaded_pages;
tonikitoo81d92f82016-09-21 12:44:56 -0700123
124 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
125 // make use of it.
npmfa20cd52016-11-14 13:33:40 -0800126 FPDF_FORMHANDLE form_handle;
tonikitoo81d92f82016-09-21 12:44:56 -0700127};
128
Lei Zhangd2be6462017-07-21 14:31:21 -0700129FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
npmfa20cd52016-11-14 13:33:40 -0800130 FPDF_FORMFILLINFO* form_fill_info) {
131 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
tonikitoo81d92f82016-09-21 12:44:56 -0700132}
133
Lei Zhangd2be6462017-07-21 14:31:21 -0700134bool CheckDimensions(int stride, int width, int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800135 if (stride < 0 || width < 0 || height < 0)
136 return false;
137 if (height > 0 && width > INT_MAX / height)
138 return false;
139 return true;
140}
141
Lei Zhangd2be6462017-07-21 14:31:21 -0700142void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
stephanafa05e972017-01-02 06:19:41 -0800143 // Get the MD5 hash and write it to stdout.
Lei Zhang143959d2017-06-22 12:20:58 -0700144 std::string hash =
145 GenerateMD5Base16(reinterpret_cast<const uint8_t*>(buffer), len);
146 printf("MD5:%s:%s\n", file_name, hash.c_str());
stephanafa05e972017-01-02 06:19:41 -0800147}
148
Lei Zhangd2be6462017-07-21 14:31:21 -0700149std::string WritePpm(const char* pdf_name,
150 int num,
151 const void* buffer_void,
152 int stride,
153 int width,
154 int height) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700155 const char* buffer = reinterpret_cast<const char*>(buffer_void);
156
Tom Sepezaf18cb32015-02-05 15:06:01 -0800157 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800158 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800159
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700160 int out_len = width * height;
161 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800162 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700163 out_len *= 3;
164
165 char filename[256];
166 snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
John Abd-El-Maleka548d302014-06-26 10:18:11 -0700167 FILE* fp = fopen(filename, "wb");
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700168 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800169 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700170 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
171 // Source data is B, G, R, unused.
172 // Dest data is R, G, B.
thestig514e8c92016-07-15 17:57:54 -0700173 std::vector<char> result(out_len);
Lei Zhange00660b2015-08-13 15:40:18 -0700174 for (int h = 0; h < height; ++h) {
175 const char* src_line = buffer + (stride * h);
thestig514e8c92016-07-15 17:57:54 -0700176 char* dest_line = result.data() + (width * h * 3);
Lei Zhange00660b2015-08-13 15:40:18 -0700177 for (int w = 0; w < width; ++w) {
178 // R
179 dest_line[w * 3] = src_line[(w * 4) + 2];
180 // G
181 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
182 // B
183 dest_line[(w * 3) + 2] = src_line[w * 4];
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700184 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700185 }
Lei Zhangd2be6462017-07-21 14:31:21 -0700186 if (fwrite(result.data(), out_len, 1, fp) != 1)
187 fprintf(stderr, "Failed to write to %s\n", filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700188 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800189 return std::string(filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700190}
191
dsinclairb63068f2016-06-16 07:58:09 -0700192void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
193 char filename[256];
194 int chars_formatted =
195 snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
196 if (chars_formatted < 0 ||
197 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
198 fprintf(stderr, "Filename %s is too long\n", filename);
199 return;
200 }
201
202 FILE* fp = fopen(filename, "w");
203 if (!fp) {
204 fprintf(stderr, "Failed to open %s for output\n", filename);
205 return;
206 }
207
208 // Output in UTF32-LE.
209 uint32_t bom = 0x0000FEFF;
Lei Zhangd2be6462017-07-21 14:31:21 -0700210 if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
211 fprintf(stderr, "Failed to write to %s\n", filename);
212 (void)fclose(fp);
213 return;
214 }
dsinclairb63068f2016-06-16 07:58:09 -0700215
Tom Sepez1d17a042017-03-16 13:22:47 -0700216 std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
217 for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
218 uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
Lei Zhangd2be6462017-07-21 14:31:21 -0700219 if (fwrite(&c, sizeof(c), 1, fp) != 1) {
220 fprintf(stderr, "Failed to write to %s\n", filename);
221 break;
222 }
dsinclairb63068f2016-06-16 07:58:09 -0700223 }
dsinclairb63068f2016-06-16 07:58:09 -0700224 (void)fclose(fp);
225}
226
Lei Zhangd2be6462017-07-21 14:31:21 -0700227const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
Jane Liu4fd9a472017-06-01 18:56:09 -0400228 if (subtype == FPDF_ANNOT_TEXT)
229 return "Text";
230 if (subtype == FPDF_ANNOT_LINK)
231 return "Link";
232 if (subtype == FPDF_ANNOT_FREETEXT)
233 return "FreeText";
234 if (subtype == FPDF_ANNOT_LINE)
235 return "Line";
236 if (subtype == FPDF_ANNOT_SQUARE)
237 return "Square";
238 if (subtype == FPDF_ANNOT_CIRCLE)
239 return "Circle";
240 if (subtype == FPDF_ANNOT_POLYGON)
241 return "Polygon";
242 if (subtype == FPDF_ANNOT_POLYLINE)
243 return "PolyLine";
244 if (subtype == FPDF_ANNOT_HIGHLIGHT)
245 return "Highlight";
246 if (subtype == FPDF_ANNOT_UNDERLINE)
247 return "Underline";
248 if (subtype == FPDF_ANNOT_SQUIGGLY)
249 return "Squiggly";
250 if (subtype == FPDF_ANNOT_STRIKEOUT)
251 return "StrikeOut";
252 if (subtype == FPDF_ANNOT_STAMP)
253 return "Stamp";
254 if (subtype == FPDF_ANNOT_CARET)
255 return "Caret";
256 if (subtype == FPDF_ANNOT_INK)
257 return "Ink";
258 if (subtype == FPDF_ANNOT_POPUP)
259 return "Popup";
260 if (subtype == FPDF_ANNOT_FILEATTACHMENT)
261 return "FileAttachment";
262 if (subtype == FPDF_ANNOT_SOUND)
263 return "Sound";
264 if (subtype == FPDF_ANNOT_MOVIE)
265 return "Movie";
266 if (subtype == FPDF_ANNOT_WIDGET)
267 return "Widget";
268 if (subtype == FPDF_ANNOT_SCREEN)
269 return "Screen";
270 if (subtype == FPDF_ANNOT_PRINTERMARK)
271 return "PrinterMark";
272 if (subtype == FPDF_ANNOT_TRAPNET)
273 return "TrapNet";
274 if (subtype == FPDF_ANNOT_WATERMARK)
275 return "Watermark";
276 if (subtype == FPDF_ANNOT_THREED)
277 return "3D";
278 if (subtype == FPDF_ANNOT_RICHMEDIA)
279 return "RichMedia";
280 if (subtype == FPDF_ANNOT_XFAWIDGET)
281 return "XFAWidget";
282 NOTREACHED();
283 return "";
284}
285
Lei Zhangd2be6462017-07-21 14:31:21 -0700286void AppendFlagString(const char* flag, std::string* output) {
287 if (!output->empty())
288 *output += ", ";
289 *output += flag;
290}
291
Jane Liu57f228d2017-07-13 18:10:30 -0400292std::string AnnotFlagsToString(int flags) {
Lei Zhangd2be6462017-07-21 14:31:21 -0700293 std::string str;
Jane Liu57f228d2017-07-13 18:10:30 -0400294 if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
Lei Zhangd2be6462017-07-21 14:31:21 -0700295 AppendFlagString("Invisible", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400296 if (flags & FPDF_ANNOT_FLAG_HIDDEN)
Lei Zhangd2be6462017-07-21 14:31:21 -0700297 AppendFlagString("Hidden", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400298 if (flags & FPDF_ANNOT_FLAG_PRINT)
Lei Zhangd2be6462017-07-21 14:31:21 -0700299 AppendFlagString("Print", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400300 if (flags & FPDF_ANNOT_FLAG_NOZOOM)
Lei Zhangd2be6462017-07-21 14:31:21 -0700301 AppendFlagString("NoZoom", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400302 if (flags & FPDF_ANNOT_FLAG_NOROTATE)
Lei Zhangd2be6462017-07-21 14:31:21 -0700303 AppendFlagString("NoRotate", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400304 if (flags & FPDF_ANNOT_FLAG_NOVIEW)
Lei Zhangd2be6462017-07-21 14:31:21 -0700305 AppendFlagString("NoView", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400306 if (flags & FPDF_ANNOT_FLAG_READONLY)
Lei Zhangd2be6462017-07-21 14:31:21 -0700307 AppendFlagString("ReadOnly", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400308 if (flags & FPDF_ANNOT_FLAG_LOCKED)
Lei Zhangd2be6462017-07-21 14:31:21 -0700309 AppendFlagString("Locked", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400310 if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
Lei Zhangd2be6462017-07-21 14:31:21 -0700311 AppendFlagString("ToggleNoView", &str);
Jane Liu57f228d2017-07-13 18:10:30 -0400312 return str;
313}
314
Lei Zhangd2be6462017-07-21 14:31:21 -0700315const char* PageObjectTypeToCString(int type) {
Jane Liu57f228d2017-07-13 18:10:30 -0400316 if (type == FPDF_PAGEOBJ_TEXT)
317 return "Text";
318 if (type == FPDF_PAGEOBJ_PATH)
319 return "Path";
320 if (type == FPDF_PAGEOBJ_IMAGE)
321 return "Image";
322 if (type == FPDF_PAGEOBJ_SHADING)
323 return "Shading";
324 if (type == FPDF_PAGEOBJ_FORM)
325 return "Form";
326 NOTREACHED();
327 return "";
328}
329
Jane Liu4fd9a472017-06-01 18:56:09 -0400330void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
331 // Open the output text file.
332 char filename[256];
333 int chars_formatted =
334 snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
335 if (chars_formatted < 0 ||
336 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
337 fprintf(stderr, "Filename %s is too long\n", filename);
338 return;
339 }
340 FILE* fp = fopen(filename, "w");
341 if (!fp) {
342 fprintf(stderr, "Failed to open %s for output\n", filename);
343 return;
344 }
345
346 int annot_count = FPDFPage_GetAnnotCount(page);
347 fprintf(fp, "Number of annotations: %d\n\n", annot_count);
348
349 // Iterate through all annotations on this page.
Jane Liu57f228d2017-07-13 18:10:30 -0400350 for (int i = 0; i < annot_count; ++i) {
Jane Liu4fd9a472017-06-01 18:56:09 -0400351 // Retrieve the annotation object and its subtype.
352 fprintf(fp, "Annotation #%d:\n", i + 1);
Jane Liud60e9ad2017-06-26 11:28:36 -0400353 FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, i);
354 if (!annot) {
Jane Liu4fd9a472017-06-01 18:56:09 -0400355 fprintf(fp, "Failed to retrieve annotation!\n\n");
356 continue;
357 }
358 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
Lei Zhangd2be6462017-07-21 14:31:21 -0700359 fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
Jane Liu4fd9a472017-06-01 18:56:09 -0400360
Jane Liu57f228d2017-07-13 18:10:30 -0400361 // Retrieve the annotation flags.
362 fprintf(fp, "Flags set: %s\n",
363 AnnotFlagsToString(FPDFAnnot_GetFlags(annot)).c_str());
364
365 // Retrieve the annotation's object count and object types.
366 const int obj_count = FPDFAnnot_GetObjectCount(annot);
367 fprintf(fp, "Number of objects: %d\n", obj_count);
368 if (obj_count > 0) {
369 fprintf(fp, "Object types: ");
370 for (int j = 0; j < obj_count; ++j) {
Lei Zhangd2be6462017-07-21 14:31:21 -0700371 const char* type = PageObjectTypeToCString(
372 FPDFPageObj_GetType(FPDFAnnot_GetObject(annot, j)));
373 fprintf(fp, "%s ", type);
Jane Liu57f228d2017-07-13 18:10:30 -0400374 }
375 fprintf(fp, "\n");
376 }
377
Jane Liu4fd9a472017-06-01 18:56:09 -0400378 // Retrieve the annotation's color and interior color.
379 unsigned int R;
380 unsigned int G;
381 unsigned int B;
382 unsigned int A;
383 if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A)) {
384 fprintf(fp, "Failed to retrieve color.\n");
385 } else {
386 fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
387 }
388 if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_InteriorColor, &R, &G,
389 &B, &A)) {
390 fprintf(fp, "Failed to retrieve interior color.\n");
391 } else {
392 fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
393 }
394
395 // Retrieve the annotation's contents and author.
Jane Liu2e1a32b2017-07-06 12:01:25 -0400396 std::unique_ptr<unsigned short, pdfium::FreeDeleter> contents_key =
397 GetFPDFWideString(L"Contents");
Jane Liu4fd9a472017-06-01 18:56:09 -0400398 unsigned long len =
Jane Liu2e1a32b2017-07-06 12:01:25 -0400399 FPDFAnnot_GetStringValue(annot, contents_key.get(), nullptr, 0);
Jane Liu4fd9a472017-06-01 18:56:09 -0400400 std::vector<char> buf(len);
Jane Liu2e1a32b2017-07-06 12:01:25 -0400401 FPDFAnnot_GetStringValue(annot, contents_key.get(), buf.data(), len);
Jane Liu4fd9a472017-06-01 18:56:09 -0400402 fprintf(fp, "Content: %ls\n",
403 GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
404 .c_str());
Jane Liu2e1a32b2017-07-06 12:01:25 -0400405 std::unique_ptr<unsigned short, pdfium::FreeDeleter> author_key =
406 GetFPDFWideString(L"T");
407 len = FPDFAnnot_GetStringValue(annot, author_key.get(), nullptr, 0);
Jane Liu4fd9a472017-06-01 18:56:09 -0400408 buf.clear();
409 buf.resize(len);
Jane Liu2e1a32b2017-07-06 12:01:25 -0400410 FPDFAnnot_GetStringValue(annot, author_key.get(), buf.data(), len);
Jane Liu4fd9a472017-06-01 18:56:09 -0400411 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.
Jane Liu4fd9a472017-06-01 18:56:09 -0400416 if (FPDFAnnot_HasAttachmentPoints(annot)) {
Jane Liud60e9ad2017-06-26 11:28:36 -0400417 FS_QUADPOINTSF quadpoints = FPDFAnnot_GetAttachmentPoints(annot);
Jane Liu57f228d2017-07-13 18:10:30 -0400418 fprintf(fp,
419 "Quadpoints: (%.3f, %.3f), (%.3f, %.3f), (%.3f, %.3f), (%.3f, "
420 "%.3f)\n",
Jane Liud60e9ad2017-06-26 11:28:36 -0400421 quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2,
422 quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4);
Jane Liu4fd9a472017-06-01 18:56:09 -0400423 }
424
425 // Retrieve the annotation's rectangle coordinates.
Jane Liud60e9ad2017-06-26 11:28:36 -0400426 FS_RECTF rect = FPDFAnnot_GetRect(annot);
Jane Liu57f228d2017-07-13 18:10:30 -0400427 fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
428 rect.left, rect.bottom, rect.right, rect.top);
429
430 FPDFPage_CloseAnnot(annot);
Jane Liu4fd9a472017-06-01 18:56:09 -0400431 }
432
433 (void)fclose(fp);
434}
435
Lei Zhangd2be6462017-07-21 14:31:21 -0700436std::string WritePng(const char* pdf_name,
437 int num,
438 const void* buffer_void,
439 int stride,
440 int width,
441 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800442 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800443 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800444
445 std::vector<unsigned char> png_encoding;
446 const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
447 if (!image_diff_png::EncodeBGRAPNG(
448 buffer, width, height, stride, false, &png_encoding)) {
449 fprintf(stderr, "Failed to convert bitmap to PNG\n");
stephanafa05e972017-01-02 06:19:41 -0800450 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800451 }
452
453 char filename[256];
454 int chars_formatted = snprintf(
455 filename, sizeof(filename), "%s.%d.png", pdf_name, num);
456 if (chars_formatted < 0 ||
457 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
Cary Clark399be5b2016-03-14 16:51:29 -0400458 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800459 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800460 }
461
462 FILE* fp = fopen(filename, "wb");
463 if (!fp) {
464 fprintf(stderr, "Failed to open %s for output\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800465 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800466 }
467
468 size_t bytes_written = fwrite(
469 &png_encoding.front(), 1, png_encoding.size(), fp);
470 if (bytes_written != png_encoding.size())
Lei Zhangd2be6462017-07-21 14:31:21 -0700471 fprintf(stderr, "Failed to write to %s\n", filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800472
Lei Zhang5377ebf2015-09-23 14:52:53 -0700473 (void)fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800474 return std::string(filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800475}
476
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700477#ifdef _WIN32
Lei Zhangd2be6462017-07-21 14:31:21 -0700478std::string WriteBmp(const char* pdf_name,
479 int num,
480 const void* buffer,
481 int stride,
482 int width,
483 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800484 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800485 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800486
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700487 int out_len = stride * height;
488 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800489 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700490
491 char filename[256];
492 snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
493 FILE* fp = fopen(filename, "wb");
494 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800495 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700496
Nico Weber2827bdd2015-07-01 14:08:08 -0700497 BITMAPINFO bmi = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700498 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
499 bmi.bmiHeader.biWidth = width;
500 bmi.bmiHeader.biHeight = -height; // top-down image
501 bmi.bmiHeader.biPlanes = 1;
502 bmi.bmiHeader.biBitCount = 32;
503 bmi.bmiHeader.biCompression = BI_RGB;
504 bmi.bmiHeader.biSizeImage = 0;
505
Nico Weber2827bdd2015-07-01 14:08:08 -0700506 BITMAPFILEHEADER file_header = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700507 file_header.bfType = 0x4d42;
508 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
509 file_header.bfOffBits = file_header.bfSize - out_len;
510
Lei Zhangd2be6462017-07-21 14:31:21 -0700511 if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
512 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
513 fwrite(buffer, out_len, 1, fp) != 1) {
514 fprintf(stderr, "Failed to write to %s\n", filename);
515 }
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700516 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800517 return std::string(filename);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700518}
519
520void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700521 char filename[256];
522 snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
523
Lei Zhang5377ebf2015-09-23 14:52:53 -0700524 HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800525
Lei Zhangd1c9b452017-05-05 17:21:36 -0700526 int width = static_cast<int>(FPDF_GetPageWidth(page));
527 int height = static_cast<int>(FPDF_GetPageHeight(page));
Tom Sepezaf18cb32015-02-05 15:06:01 -0800528 HRGN rgn = CreateRectRgn(0, 0, width, height);
529 SelectClipRgn(dc, rgn);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700530 DeleteObject(rgn);
531
532 SelectObject(dc, GetStockObject(NULL_PEN));
533 SelectObject(dc, GetStockObject(WHITE_BRUSH));
534 // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
535 Rectangle(dc, 0, 0, width + 1, height + 1);
536
537 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
538 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
539
540 DeleteEnhMetaFile(CloseEnhMetaFile(dc));
541}
Lei Zhangd1c9b452017-05-05 17:21:36 -0700542
543int CALLBACK EnhMetaFileProc(HDC hdc,
544 HANDLETABLE* handle_table,
545 const ENHMETARECORD* record,
546 int objects_count,
547 LPARAM param) {
548 std::vector<const ENHMETARECORD*>& items =
549 *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
550 items.push_back(record);
551 return 1;
552}
553
554void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
555 char filename[256];
556 snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
557 FILE* fp = fopen(filename, "wb");
558 if (!fp)
559 return;
560
561 HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
562
563 int width = static_cast<int>(FPDF_GetPageWidth(page));
564 int height = static_cast<int>(FPDF_GetPageHeight(page));
565 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
566 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
567
568 HENHMETAFILE emf = CloseEnhMetaFile(dc);
569 std::vector<const ENHMETARECORD*> items;
570 EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
571 for (const ENHMETARECORD* record : items) {
572 if (record->iType != EMR_GDICOMMENT)
573 continue;
574
575 const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
576 const char* data = reinterpret_cast<const char*>(comment->Data);
577 uint16_t size = *reinterpret_cast<const uint16_t*>(data);
Lei Zhangd2be6462017-07-21 14:31:21 -0700578 if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
579 fprintf(stderr, "Failed to write to %s\n", filename);
580 break;
581 }
Lei Zhangd1c9b452017-05-05 17:21:36 -0700582 }
583 fclose(fp);
584 DeleteEnhMetaFile(emf);
585}
586#endif // _WIN32
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700587
Cary Clark399be5b2016-03-14 16:51:29 -0400588#ifdef PDF_ENABLE_SKIA
Lei Zhangd2be6462017-07-21 14:31:21 -0700589std::string WriteSkp(const char* pdf_name,
590 int num,
591 SkPictureRecorder* recorder) {
Cary Clark399be5b2016-03-14 16:51:29 -0400592 char filename[256];
593 int chars_formatted =
594 snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
595
596 if (chars_formatted < 0 ||
597 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
598 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800599 return "";
Cary Clark399be5b2016-03-14 16:51:29 -0400600 }
601
thestigb97e07e2016-09-28 22:16:40 -0700602 sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
Cary Clark399be5b2016-03-14 16:51:29 -0400603 SkFILEWStream wStream(filename);
604 picture->serialize(&wStream);
stephanafa05e972017-01-02 06:19:41 -0800605 return std::string(filename);
Cary Clark399be5b2016-03-14 16:51:29 -0400606}
607#endif
608
Tom Sepez58fb36a2016-02-01 10:32:14 -0800609// These example JS platform callback handlers are entirely optional,
610// and exist here to show the flow of information from a document back
611// to the embedder.
Tom Sepezbd932572016-01-29 09:10:41 -0800612int ExampleAppAlert(IPDF_JSPLATFORM*,
613 FPDF_WIDESTRING msg,
614 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800615 int type,
616 int icon) {
Tom Sepezbd932572016-01-29 09:10:41 -0800617 printf("%ls", GetPlatformWString(title).c_str());
npmfa20cd52016-11-14 13:33:40 -0800618 if (icon || type)
619 printf("[icon=%d,type=%d]", icon, type);
Tom Sepezbd932572016-01-29 09:10:41 -0800620 printf(": %ls\n", GetPlatformWString(msg).c_str());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700621 return 0;
622}
623
Tom Sepez58fb36a2016-02-01 10:32:14 -0800624int ExampleAppResponse(IPDF_JSPLATFORM*,
625 FPDF_WIDESTRING question,
626 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800627 FPDF_WIDESTRING default_value,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800628 FPDF_WIDESTRING label,
npmfa20cd52016-11-14 13:33:40 -0800629 FPDF_BOOL is_password,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800630 void* response,
631 int length) {
632 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
633 GetPlatformWString(title).c_str(),
634 GetPlatformWString(question).c_str(),
npmfa20cd52016-11-14 13:33:40 -0800635 GetPlatformWString(default_value).c_str(),
636 GetPlatformWString(label).c_str(), is_password, length);
Tom Sepez58fb36a2016-02-01 10:32:14 -0800637
638 // UTF-16, always LE regardless of platform.
639 uint8_t* ptr = static_cast<uint8_t*>(response);
640 ptr[0] = 'N';
641 ptr[1] = 0;
642 ptr[2] = 'o';
643 ptr[3] = 0;
644 return 4;
645}
646
npmfa20cd52016-11-14 13:33:40 -0800647void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
648 printf("Goto Page: %d\n", page_number);
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800649}
650
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800651void ExampleDocMail(IPDF_JSPLATFORM*,
652 void* mailData,
653 int length,
npmfa20cd52016-11-14 13:33:40 -0800654 FPDF_BOOL UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800655 FPDF_WIDESTRING To,
656 FPDF_WIDESTRING Subject,
657 FPDF_WIDESTRING CC,
658 FPDF_WIDESTRING BCC,
659 FPDF_WIDESTRING Msg) {
npmfa20cd52016-11-14 13:33:40 -0800660 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800661 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
662 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
663 GetPlatformWString(Msg).c_str());
664}
665
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800666void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700667 std::string feature = "Unknown";
668 switch (type) {
669 case FPDF_UNSP_DOC_XFAFORM:
670 feature = "XFA";
671 break;
672 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
673 feature = "Portfolios_Packages";
674 break;
675 case FPDF_UNSP_DOC_ATTACHMENT:
676 case FPDF_UNSP_ANNOT_ATTACHMENT:
677 feature = "Attachment";
678 break;
679 case FPDF_UNSP_DOC_SECURITY:
680 feature = "Rights_Management";
681 break;
682 case FPDF_UNSP_DOC_SHAREDREVIEW:
683 feature = "Shared_Review";
684 break;
685 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
686 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
687 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
688 feature = "Shared_Form";
689 break;
690 case FPDF_UNSP_ANNOT_3DANNOT:
691 feature = "3D";
692 break;
693 case FPDF_UNSP_ANNOT_MOVIE:
694 feature = "Movie";
695 break;
696 case FPDF_UNSP_ANNOT_SOUND:
697 feature = "Sound";
698 break;
699 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
700 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
701 feature = "Screen";
702 break;
703 case FPDF_UNSP_ANNOT_SIG:
704 feature = "Digital_Signature";
705 break;
706 }
707 printf("Unsupported feature: %s.\n", feature.c_str());
708}
709
Tom Sepez5ee12d72014-12-17 16:24:01 -0800710bool ParseCommandLine(const std::vector<std::string>& args,
thestig514e8c92016-07-15 17:57:54 -0700711 Options* options,
712 std::vector<std::string>* files) {
713 if (args.empty())
Tom Sepez5ee12d72014-12-17 16:24:01 -0800714 return false;
thestig514e8c92016-07-15 17:57:54 -0700715
Tom Sepez5ee12d72014-12-17 16:24:01 -0800716 options->exe_path = args[0];
Bo Xud44e3922014-12-19 02:27:25 -0800717 size_t cur_idx = 1;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800718 for (; cur_idx < args.size(); ++cur_idx) {
719 const std::string& cur_arg = args[cur_idx];
Tom Sepez2991d8d2016-01-15 16:02:48 -0800720 if (cur_arg == "--show-config") {
721 options->show_config = true;
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -0400722 } else if (cur_arg == "--show-metadata") {
723 options->show_metadata = true;
tsepezf09bdfa2016-04-18 16:08:26 -0700724 } else if (cur_arg == "--send-events") {
725 options->send_events = true;
Dan Sinclairaeadad12017-07-18 16:43:41 -0400726 } else if (cur_arg == "--render-oneshot") {
727 options->render_oneshot = true;
Jane Liu4442d452017-07-19 10:19:42 -0400728 } else if (cur_arg == "--save-attachments") {
729 options->save_attachments = true;
Henrique Nakashima95ea7782017-07-11 16:42:43 -0400730#ifdef ENABLE_CALLGRIND
731 } else if (cur_arg == "--callgrind-delim") {
732 options->callgrind_delimiters = true;
733#endif // ENABLE_CALLGRIND
Tom Sepez2991d8d2016-01-15 16:02:48 -0800734 } else if (cur_arg == "--ppm") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800735 if (options->output_format != OUTPUT_NONE) {
736 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
737 return false;
738 }
739 options->output_format = OUTPUT_PPM;
Tom Sepezaf18cb32015-02-05 15:06:01 -0800740 } else if (cur_arg == "--png") {
741 if (options->output_format != OUTPUT_NONE) {
742 fprintf(stderr, "Duplicate or conflicting --png argument\n");
743 return false;
744 }
745 options->output_format = OUTPUT_PNG;
dsinclairb63068f2016-06-16 07:58:09 -0700746 } else if (cur_arg == "--txt") {
747 if (options->output_format != OUTPUT_NONE) {
748 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
749 return false;
750 }
751 options->output_format = OUTPUT_TEXT;
Jane Liu4fd9a472017-06-01 18:56:09 -0400752 } else if (cur_arg == "--annot") {
753 if (options->output_format != OUTPUT_NONE) {
754 fprintf(stderr, "Duplicate or conflicting --annot argument\n");
755 return false;
756 }
757 options->output_format = OUTPUT_ANNOT;
Cary Clark399be5b2016-03-14 16:51:29 -0400758#ifdef PDF_ENABLE_SKIA
759 } else if (cur_arg == "--skp") {
760 if (options->output_format != OUTPUT_NONE) {
761 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
762 return false;
763 }
764 options->output_format = OUTPUT_SKP;
765#endif
Lei Zhang6f62d532015-09-23 15:31:44 -0700766 } else if (cur_arg.size() > 11 &&
767 cur_arg.compare(0, 11, "--font-dir=") == 0) {
768 if (!options->font_directory.empty()) {
769 fprintf(stderr, "Duplicate --font-dir argument\n");
770 return false;
771 }
772 options->font_directory = cur_arg.substr(11);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700773#ifdef _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500774 } else if (cur_arg == "--emf") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800775 if (options->output_format != OUTPUT_NONE) {
776 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
777 return false;
778 }
779 options->output_format = OUTPUT_EMF;
Lei Zhangd1c9b452017-05-05 17:21:36 -0700780 } else if (cur_arg == "--ps2") {
781 if (options->output_format != OUTPUT_NONE) {
782 fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
783 return false;
784 }
785 options->output_format = OUTPUT_PS2;
786 } else if (cur_arg == "--ps3") {
787 if (options->output_format != OUTPUT_NONE) {
788 fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
789 return false;
790 }
791 options->output_format = OUTPUT_PS3;
Dan Sinclair50cce602016-02-24 09:51:16 -0500792 } else if (cur_arg == "--bmp") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800793 if (options->output_format != OUTPUT_NONE) {
794 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
795 return false;
796 }
797 options->output_format = OUTPUT_BMP;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800798#endif // _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500799
Tom Sepez452b4f32015-10-13 09:27:27 -0700800#ifdef PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800801#ifdef V8_USE_EXTERNAL_STARTUP_DATA
Dan Sinclair738b08c2016-03-01 14:45:20 -0500802 } else if (cur_arg.size() > 10 &&
803 cur_arg.compare(0, 10, "--bin-dir=") == 0) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800804 if (!options->bin_directory.empty()) {
805 fprintf(stderr, "Duplicate --bin-dir argument\n");
806 return false;
807 }
808 options->bin_directory = cur_arg.substr(10);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800809#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700810#endif // PDF_ENABLE_V8
Dan Sinclair738b08c2016-03-01 14:45:20 -0500811
812 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
Tom Sepezdaa2e842015-01-29 15:44:37 -0800813 if (!options->scale_factor_as_string.empty()) {
814 fprintf(stderr, "Duplicate --scale argument\n");
815 return false;
816 }
817 options->scale_factor_as_string = cur_arg.substr(8);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400818 } else if (cur_arg == "--show-structure") {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400819 if (options->output_format != OUTPUT_NONE) {
820 fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
821 return false;
822 }
823 options->output_format = OUTPUT_STRUCTURE;
npme3c73152016-11-14 09:14:52 -0800824 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
825 if (options->pages) {
826 fprintf(stderr, "Duplicate --pages argument\n");
827 return false;
828 }
829 options->pages = true;
npmfa20cd52016-11-14 13:33:40 -0800830 const std::string pages_string = cur_arg.substr(8);
831 size_t first_dash = pages_string.find("-");
832 if (first_dash == std::string::npos) {
833 std::stringstream(pages_string) >> options->first_page;
834 options->last_page = options->first_page;
npme3c73152016-11-14 09:14:52 -0800835 } else {
npmfa20cd52016-11-14 13:33:40 -0800836 std::stringstream(pages_string.substr(0, first_dash)) >>
837 options->first_page;
838 std::stringstream(pages_string.substr(first_dash + 1)) >>
839 options->last_page;
npme3c73152016-11-14 09:14:52 -0800840 }
stephanafa05e972017-01-02 06:19:41 -0800841 } else if (cur_arg == "--md5") {
842 options->md5 = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800843 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
844 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
845 return false;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500846 } else {
Vitaly Buka8f2c3dc2014-08-20 10:32:36 -0700847 break;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500848 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700849 }
thestig514e8c92016-07-15 17:57:54 -0700850 for (size_t i = cur_idx; i < args.size(); i++)
Tom Sepez5ee12d72014-12-17 16:24:01 -0800851 files->push_back(args[i]);
thestig514e8c92016-07-15 17:57:54 -0700852
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700853 return true;
854}
855
Lei Zhangd2be6462017-07-21 14:31:21 -0700856void PrintLastError() {
857 unsigned long err = FPDF_GetLastError();
858 fprintf(stderr, "Load pdf docs unsuccessful: ");
859 switch (err) {
860 case FPDF_ERR_SUCCESS:
861 fprintf(stderr, "Success");
862 break;
863 case FPDF_ERR_UNKNOWN:
864 fprintf(stderr, "Unknown error");
865 break;
866 case FPDF_ERR_FILE:
867 fprintf(stderr, "File not found or could not be opened");
868 break;
869 case FPDF_ERR_FORMAT:
870 fprintf(stderr, "File not in PDF format or corrupted");
871 break;
872 case FPDF_ERR_PASSWORD:
873 fprintf(stderr, "Password required or incorrect password");
874 break;
875 case FPDF_ERR_SECURITY:
876 fprintf(stderr, "Unsupported security scheme");
877 break;
878 case FPDF_ERR_PAGE:
879 fprintf(stderr, "Page not found or content error");
880 break;
881 default:
882 fprintf(stderr, "Unknown error %ld", err);
883 }
884 fprintf(stderr, ".\n");
885 return;
886}
887
npmfa20cd52016-11-14 13:33:40 -0800888FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700889 return true;
890}
891
npmfa20cd52016-11-14 13:33:40 -0800892void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700893
Lei Zhang54d91ec2017-06-16 19:08:02 -0700894void SendPageEvents(FPDF_FORMHANDLE form,
895 FPDF_PAGE page,
tsepezf09bdfa2016-04-18 16:08:26 -0700896 const std::string& events) {
897 auto lines = StringSplit(events, '\n');
898 for (auto line : lines) {
899 auto command = StringSplit(line, '#');
900 if (command[0].empty())
901 continue;
902 auto tokens = StringSplit(command[0], ',');
Nicolas Penae30f07a2017-05-18 18:37:46 -0400903 if (tokens[0] == "charcode") {
904 if (tokens.size() == 2) {
905 int keycode = atoi(tokens[1].c_str());
906 FORM_OnChar(form, page, keycode, 0);
907 } else {
908 fprintf(stderr, "charcode: bad args\n");
909 }
910 } else if (tokens[0] == "keycode") {
tsepezf09bdfa2016-04-18 16:08:26 -0700911 if (tokens.size() == 2) {
912 int keycode = atoi(tokens[1].c_str());
913 FORM_OnKeyDown(form, page, keycode, 0);
914 FORM_OnKeyUp(form, page, keycode, 0);
915 } else {
916 fprintf(stderr, "keycode: bad args\n");
917 }
918 } else if (tokens[0] == "mousedown") {
919 if (tokens.size() == 4) {
920 int x = atoi(tokens[2].c_str());
921 int y = atoi(tokens[3].c_str());
922 if (tokens[1] == "left")
923 FORM_OnLButtonDown(form, page, 0, x, y);
924#ifdef PDF_ENABLE_XFA
925 else if (tokens[1] == "right")
926 FORM_OnRButtonDown(form, page, 0, x, y);
927#endif
928 else
929 fprintf(stderr, "mousedown: bad button name\n");
930 } else {
931 fprintf(stderr, "mousedown: bad args\n");
932 }
933 } else if (tokens[0] == "mouseup") {
934 if (tokens.size() == 4) {
935 int x = atoi(tokens[2].c_str());
936 int y = atoi(tokens[3].c_str());
937 if (tokens[1] == "left")
938 FORM_OnLButtonUp(form, page, 0, x, y);
939#ifdef PDF_ENABLE_XFA
940 else if (tokens[1] == "right")
941 FORM_OnRButtonUp(form, page, 0, x, y);
942#endif
943 else
944 fprintf(stderr, "mouseup: bad button name\n");
945 } else {
946 fprintf(stderr, "mouseup: bad args\n");
947 }
948 } else if (tokens[0] == "mousemove") {
949 if (tokens.size() == 3) {
950 int x = atoi(tokens[1].c_str());
951 int y = atoi(tokens[2].c_str());
952 FORM_OnMouseMove(form, page, 0, x, y);
953 } else {
954 fprintf(stderr, "mousemove: bad args\n");
955 }
956 } else {
957 fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
958 }
959 }
960}
961
tonikitoo3e981582016-08-26 08:37:10 -0700962FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
963 FPDF_DOCUMENT doc,
964 int index) {
npmfa20cd52016-11-14 13:33:40 -0800965 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
966 ToPDFiumTestFormFillInfo(param);
967 auto& loaded_pages = form_fill_info->loaded_pages;
npmfa20cd52016-11-14 13:33:40 -0800968 auto iter = loaded_pages.find(index);
969 if (iter != loaded_pages.end())
Tom Sepezbfa2a972017-07-24 11:38:31 -0700970 return iter->second.get();
tonikitoo3e981582016-08-26 08:37:10 -0700971
972 FPDF_PAGE page = FPDF_LoadPage(doc, index);
973 if (!page)
974 return nullptr;
975
npmfa20cd52016-11-14 13:33:40 -0800976 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
npmfa20cd52016-11-14 13:33:40 -0800977 FORM_OnAfterLoadPage(page, form_handle);
978 FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
Tom Sepezbfa2a972017-07-24 11:38:31 -0700979 loaded_pages[index].reset(page);
tonikitoo3e981582016-08-26 08:37:10 -0700980 return page;
981}
982
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400983std::wstring ConvertToWString(const unsigned short* buf,
984 unsigned long buf_size) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400985 std::wstring result;
986 result.reserve(buf_size);
987 std::copy(buf, buf + buf_size, std::back_inserter(result));
988 return result;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400989}
990
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400991void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
992 static const size_t kBufSize = 1024;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400993 unsigned short buf[kBufSize];
994 unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400995 printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400996
997 memset(buf, 0, sizeof(buf));
dan sinclaird9dad3a2017-04-06 14:44:02 -0400998 len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
999 if (len > 0)
1000 printf(": '%ls'", ConvertToWString(buf, len).c_str());
1001
1002 memset(buf, 0, sizeof(buf));
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001003 len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
1004 if (len > 0)
1005 printf(" (%ls)", ConvertToWString(buf, len).c_str());
1006 printf("\n");
1007
1008 for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
1009 FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
1010 // If the child is not an Element then this will return null. This can
1011 // happen if the element is things like an object reference or a stream.
1012 if (!sub_child)
1013 continue;
1014
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001015 DumpChildStructure(sub_child, indent + 1);
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001016 }
1017}
1018
1019void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
1020 std::unique_ptr<void, FPDFStructTreeDeleter> tree(
1021 FPDF_StructTree_GetForPage(page));
1022 if (!tree) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001023 fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001024 return;
1025 }
1026
1027 printf("Structure Tree for Page %d\n", page_idx);
1028 for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
1029 FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
1030 if (!child) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001031 fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001032 continue;
1033 }
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001034 DumpChildStructure(child, 0);
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001035 }
1036 printf("\n\n");
1037}
1038
Lei Zhangd2be6462017-07-21 14:31:21 -07001039void DumpMetaData(FPDF_DOCUMENT doc) {
1040 constexpr const char* meta_tags[] = {"Title", "Author", "Subject",
1041 "Keywords", "Creator", "Producer",
1042 "CreationDate", "ModDate"};
1043 for (const char* meta_tag : meta_tags) {
1044 char meta_buffer[4096];
1045 unsigned long len =
1046 FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
1047 if (!len)
1048 continue;
1049
1050 auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
1051 printf("%-12s = %ls (%lu bytes)\n", meta_tag,
1052 GetPlatformWString(meta_string).c_str(), len);
1053 }
1054}
1055
1056void SaveAttachments(FPDF_DOCUMENT doc, const std::string& name) {
1057 for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
1058 FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
1059
1060 // Retrieve the attachment file name.
1061 std::string attachment_name;
1062 unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
1063 if (len) {
1064 std::vector<char> buf(len);
1065 unsigned long actual_len =
1066 FPDFAttachment_GetName(attachment, buf.data(), len);
1067 if (actual_len == len) {
1068 attachment_name =
1069 GetPlatformString(reinterpret_cast<unsigned short*>(buf.data()));
1070 }
1071 }
1072 if (attachment_name.empty()) {
1073 fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
1074 continue;
1075 }
1076
1077 // Calculate the full attachment file name.
1078 char save_name[256];
1079 int chars_formatted =
1080 snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
1081 attachment_name.c_str());
1082 if (chars_formatted < 0 ||
1083 static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
1084 fprintf(stderr, "Filename %s is too long\n", save_name);
1085 continue;
1086 }
1087
1088 // Retrieve the attachment.
1089 len = FPDFAttachment_GetFile(attachment, nullptr, 0);
1090 std::vector<char> data_buf(len);
1091 if (len) {
1092 unsigned long actual_len =
1093 FPDFAttachment_GetFile(attachment, data_buf.data(), len);
1094 if (actual_len != len)
1095 data_buf.clear();
1096 }
1097 if (data_buf.empty()) {
1098 fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str());
1099 continue;
1100 }
1101
1102 // Write the attachment file.
1103 FILE* fp = fopen(save_name, "wb");
1104 if (!fp) {
1105 fprintf(stderr, "Failed to open %s for saving attachment.\n", save_name);
1106 continue;
1107 }
1108
1109 size_t written_len = fwrite(data_buf.data(), 1, len, fp);
1110 if (written_len == len) {
1111 fprintf(stderr, "Saved attachment \"%s\" as: %s.\n",
1112 attachment_name.c_str(), save_name);
1113 } else {
1114 fprintf(stderr, "Failed to write to %s\n", save_name);
1115 }
1116 fclose(fp);
1117 }
1118}
1119
Dan Sinclairaeadad12017-07-18 16:43:41 -04001120// Note, for a client using progressive rendering you'd want to determine if you
1121// need the rendering to pause instead of always saying |true|. This is for
1122// testing to force the renderer to break whenever possible.
1123FPDF_BOOL NeedToPauseNow(IFSDK_PAUSE* p) {
1124 return true;
1125}
1126
Jun Fangb553bcb2015-11-10 18:49:04 +08001127bool RenderPage(const std::string& name,
tonikitoo3e981582016-08-26 08:37:10 -07001128 FPDF_DOCUMENT doc,
Tom Sepez1d17a042017-03-16 13:22:47 -07001129 FPDF_FORMHANDLE form,
Lei Zhangd2be6462017-07-21 14:31:21 -07001130 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info,
Jun Fangb553bcb2015-11-10 18:49:04 +08001131 const int page_index,
tsepezf09bdfa2016-04-18 16:08:26 -07001132 const Options& options,
1133 const std::string& events) {
Tom Sepezbfa2a972017-07-24 11:38:31 -07001134 FPDF_PAGE page = GetPageForIndex(form_fill_info, doc, page_index);
1135 if (!page)
Jun Fangb553bcb2015-11-10 18:49:04 +08001136 return false;
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001137 if (options.send_events)
Tom Sepezbfa2a972017-07-24 11:38:31 -07001138 SendPageEvents(form, page, events);
Dan Sinclair3c67fbd2017-04-05 16:07:50 -04001139 if (options.output_format == OUTPUT_STRUCTURE) {
Tom Sepezbfa2a972017-07-24 11:38:31 -07001140 DumpPageStructure(page, page_index);
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001141 return true;
1142 }
1143
Tom Sepezbfa2a972017-07-24 11:38:31 -07001144 std::unique_ptr<void, FPDFTextPageDeleter> text_page(FPDFText_LoadPage(page));
tsepezf09bdfa2016-04-18 16:08:26 -07001145
Jun Fangdf7f3662015-11-10 18:29:18 +08001146 double scale = 1.0;
thestig514e8c92016-07-15 17:57:54 -07001147 if (!options.scale_factor_as_string.empty())
Jun Fangdf7f3662015-11-10 18:29:18 +08001148 std::stringstream(options.scale_factor_as_string) >> scale;
thestig514e8c92016-07-15 17:57:54 -07001149
Tom Sepezbfa2a972017-07-24 11:38:31 -07001150 int width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
1151 int height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
1152 int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
Tom Sepez1d17a042017-03-16 13:22:47 -07001153 std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
1154 FPDFBitmap_Create(width, height, alpha));
1155
thestige97ea032016-05-19 10:59:15 -07001156 if (bitmap) {
1157 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
Tom Sepez1d17a042017-03-16 13:22:47 -07001158 FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
Dan Sinclairaeadad12017-07-18 16:43:41 -04001159
1160 if (options.render_oneshot) {
1161 // Note, client programs probably want to use this method instead of the
1162 // progressive calls. The progressive calls are if you need to pause the
1163 // rendering to update the UI, the PDF renderer will break when possible.
Tom Sepezbfa2a972017-07-24 11:38:31 -07001164 FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0,
Dan Sinclairaeadad12017-07-18 16:43:41 -04001165 FPDF_ANNOT);
1166 } else {
1167 IFSDK_PAUSE pause;
1168 pause.version = 1;
1169 pause.NeedToPauseNow = &NeedToPauseNow;
1170
Tom Sepezbfa2a972017-07-24 11:38:31 -07001171 int rv = FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, width,
1172 height, 0, FPDF_ANNOT, &pause);
Dan Sinclairaeadad12017-07-18 16:43:41 -04001173 while (rv == FPDF_RENDER_TOBECOUNTINUED)
Tom Sepezbfa2a972017-07-24 11:38:31 -07001174 rv = FPDF_RenderPage_Continue(page, &pause);
Dan Sinclairaeadad12017-07-18 16:43:41 -04001175 }
Jun Fangdf7f3662015-11-10 18:29:18 +08001176
Tom Sepezbfa2a972017-07-24 11:38:31 -07001177 FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, FPDF_ANNOT);
Dan Sinclairaeadad12017-07-18 16:43:41 -04001178
1179 if (!options.render_oneshot)
Tom Sepezbfa2a972017-07-24 11:38:31 -07001180 FPDF_RenderPage_Close(page);
Dan Sinclairaeadad12017-07-18 16:43:41 -04001181
Tom Sepez1d17a042017-03-16 13:22:47 -07001182 int stride = FPDFBitmap_GetStride(bitmap.get());
thestige97ea032016-05-19 10:59:15 -07001183 const char* buffer =
Tom Sepez1d17a042017-03-16 13:22:47 -07001184 reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
Jun Fangdf7f3662015-11-10 18:29:18 +08001185
stephanafa05e972017-01-02 06:19:41 -08001186 std::string&& image_file_name = "";
thestige97ea032016-05-19 10:59:15 -07001187 switch (options.output_format) {
Jun Fangdf7f3662015-11-10 18:29:18 +08001188#ifdef _WIN32
thestige97ea032016-05-19 10:59:15 -07001189 case OUTPUT_BMP:
stephanafa05e972017-01-02 06:19:41 -08001190 image_file_name =
1191 WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -07001192 break;
Jun Fangdf7f3662015-11-10 18:29:18 +08001193
thestige97ea032016-05-19 10:59:15 -07001194 case OUTPUT_EMF:
Tom Sepezbfa2a972017-07-24 11:38:31 -07001195 WriteEmf(page, name.c_str(), page_index);
thestige97ea032016-05-19 10:59:15 -07001196 break;
Lei Zhangd1c9b452017-05-05 17:21:36 -07001197
1198 case OUTPUT_PS2:
1199 case OUTPUT_PS3:
Tom Sepezbfa2a972017-07-24 11:38:31 -07001200 WritePS(page, name.c_str(), page_index);
Lei Zhangd1c9b452017-05-05 17:21:36 -07001201 break;
Jun Fangdf7f3662015-11-10 18:29:18 +08001202#endif
dsinclairb63068f2016-06-16 07:58:09 -07001203 case OUTPUT_TEXT:
Tom Sepezbfa2a972017-07-24 11:38:31 -07001204 WriteText(page, name.c_str(), page_index);
dsinclairb63068f2016-06-16 07:58:09 -07001205 break;
1206
Jane Liu4fd9a472017-06-01 18:56:09 -04001207 case OUTPUT_ANNOT:
Tom Sepezbfa2a972017-07-24 11:38:31 -07001208 WriteAnnot(page, name.c_str(), page_index);
Jane Liu4fd9a472017-06-01 18:56:09 -04001209 break;
1210
thestige97ea032016-05-19 10:59:15 -07001211 case OUTPUT_PNG:
stephanafa05e972017-01-02 06:19:41 -08001212 image_file_name =
1213 WritePng(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -07001214 break;
Jun Fangdf7f3662015-11-10 18:29:18 +08001215
thestige97ea032016-05-19 10:59:15 -07001216 case OUTPUT_PPM:
stephanafa05e972017-01-02 06:19:41 -08001217 image_file_name =
1218 WritePpm(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -07001219 break;
Jun Fangdf7f3662015-11-10 18:29:18 +08001220
Cary Clark399be5b2016-03-14 16:51:29 -04001221#ifdef PDF_ENABLE_SKIA
thestige97ea032016-05-19 10:59:15 -07001222 case OUTPUT_SKP: {
1223 std::unique_ptr<SkPictureRecorder> recorder(
thestigb97e07e2016-09-28 22:16:40 -07001224 reinterpret_cast<SkPictureRecorder*>(
Tom Sepezbfa2a972017-07-24 11:38:31 -07001225 FPDF_RenderPageSkp(page, width, height)));
1226 FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0);
stephanafa05e972017-01-02 06:19:41 -08001227 image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
thestige97ea032016-05-19 10:59:15 -07001228 } break;
Cary Clark399be5b2016-03-14 16:51:29 -04001229#endif
thestige97ea032016-05-19 10:59:15 -07001230 default:
1231 break;
1232 }
Jun Fangdf7f3662015-11-10 18:29:18 +08001233
stephanafa05e972017-01-02 06:19:41 -08001234 // Write the filename and the MD5 of the buffer to stdout if we wrote a
1235 // file.
1236 if (options.md5 && image_file_name != "")
1237 OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
thestige97ea032016-05-19 10:59:15 -07001238 } else {
1239 fprintf(stderr, "Page was too large to be rendered.\n");
1240 }
tonikitoo3e981582016-08-26 08:37:10 -07001241
Tom Sepezbfa2a972017-07-24 11:38:31 -07001242 FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
1243 FORM_OnBeforeClosePage(page, form);
thestige97ea032016-05-19 10:59:15 -07001244 return !!bitmap;
Jun Fangdf7f3662015-11-10 18:29:18 +08001245}
1246
tsepezf09bdfa2016-04-18 16:08:26 -07001247void RenderPdf(const std::string& name,
1248 const char* pBuf,
1249 size_t len,
1250 const Options& options,
1251 const std::string& events) {
Lei Zhangd2be6462017-07-21 14:31:21 -07001252 IPDF_JSPLATFORM platform_callbacks = {};
Tom Sepeza72e8e22015-10-07 10:17:53 -07001253 platform_callbacks.version = 3;
Tom Sepezb7cb36a2015-02-13 16:54:48 -08001254 platform_callbacks.app_alert = ExampleAppAlert;
Tom Sepez58fb36a2016-02-01 10:32:14 -08001255 platform_callbacks.app_response = ExampleAppResponse;
Tom Sepezb7cb36a2015-02-13 16:54:48 -08001256 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
Tom Sepeze5fbd7a2016-01-29 17:05:08 -08001257 platform_callbacks.Doc_mail = ExampleDocMail;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001258
Artem Strygin834ebec2017-07-27 14:01:32 +03001259 // The pdf_avail must outlive doc.
1260 std::unique_ptr<void, FPDFAvailDeleter> pdf_avail;
Tom Sepezbfa2a972017-07-24 11:38:31 -07001261 // The document must outlive |form_callbacks.loaded_pages|.
1262 std::unique_ptr<void, FPDFDocumentDeleter> doc;
tonikitoo81d92f82016-09-21 12:44:56 -07001263 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
Tom Sepezc46d0002015-11-30 15:46:36 -08001264#ifdef PDF_ENABLE_XFA
Tom Sepezed631382014-11-18 14:10:25 -08001265 form_callbacks.version = 2;
Tom Sepezc46d0002015-11-30 15:46:36 -08001266#else // PDF_ENABLE_XFA
1267 form_callbacks.version = 1;
1268#endif // PDF_ENABLE_XFA
tonikitoo3e981582016-08-26 08:37:10 -07001269 form_callbacks.FFI_GetPage = GetPageForIndex;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001270 form_callbacks.m_pJsPlatform = &platform_callbacks;
1271
1272 TestLoader loader(pBuf, len);
Lei Zhangd2be6462017-07-21 14:31:21 -07001273 FPDF_FILEACCESS file_access = {};
John Abd-El-Malek7dc51722014-05-26 12:54:31 -07001274 file_access.m_FileLen = static_cast<unsigned long>(len);
Tom Sepezd831dc72015-10-19 16:04:22 -07001275 file_access.m_GetBlock = TestLoader::GetBlock;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001276 file_access.m_Param = &loader;
1277
Lei Zhangd2be6462017-07-21 14:31:21 -07001278 FX_FILEAVAIL file_avail = {};
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001279 file_avail.version = 1;
1280 file_avail.IsDataAvail = Is_Data_Avail;
1281
Lei Zhangd2be6462017-07-21 14:31:21 -07001282 FX_DOWNLOADHINTS hints = {};
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001283 hints.version = 1;
1284 hints.AddSegment = Add_Segment;
1285
Jun Fangdf7f3662015-11-10 18:29:18 +08001286 int nRet = PDF_DATA_NOTAVAIL;
Jun Fangb553bcb2015-11-10 18:49:04 +08001287 bool bIsLinearized = false;
Artem Strygin834ebec2017-07-27 14:01:32 +03001288 pdf_avail.reset(FPDFAvail_Create(&file_avail, &file_access));
Tom Sepezc98895c2015-11-24 15:30:36 -08001289
Tom Sepez1d17a042017-03-16 13:22:47 -07001290 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
1291 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +08001292 if (doc) {
thestig514e8c92016-07-15 17:57:54 -07001293 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -07001294 nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
thestig514e8c92016-07-15 17:57:54 -07001295
Jun Fangdf7f3662015-11-10 18:29:18 +08001296 if (nRet == PDF_DATA_ERROR) {
1297 fprintf(stderr, "Unknown error in checking if doc was available.\n");
1298 return;
1299 }
Tom Sepez1d17a042017-03-16 13:22:47 -07001300 nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
Jun Fangdf7f3662015-11-10 18:29:18 +08001301 if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
1302 fprintf(stderr,
1303 "Error %d was returned in checking if form was available.\n",
1304 nRet);
1305 return;
1306 }
Jun Fangb553bcb2015-11-10 18:49:04 +08001307 bIsLinearized = true;
Jun Fangdf7f3662015-11-10 18:29:18 +08001308 }
1309 } else {
Tom Sepez1d17a042017-03-16 13:22:47 -07001310 doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +08001311 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001312
Lei Zhang5377ebf2015-09-23 14:52:53 -07001313 if (!doc) {
Lei Zhangd2be6462017-07-21 14:31:21 -07001314 PrintLastError();
JUN FANG827a1722015-03-05 13:39:21 -08001315 return;
1316 }
1317
Tom Sepez1d17a042017-03-16 13:22:47 -07001318 (void)FPDF_GetDocPermissions(doc.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001319
Lei Zhangd2be6462017-07-21 14:31:21 -07001320 if (options.show_metadata)
1321 DumpMetaData(doc.get());
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -04001322
Lei Zhangd2be6462017-07-21 14:31:21 -07001323 if (options.save_attachments)
1324 SaveAttachments(doc.get(), name);
Jane Liu4442d452017-07-19 10:19:42 -04001325
Tom Sepez1d17a042017-03-16 13:22:47 -07001326 std::unique_ptr<void, FPDFFormHandleDeleter> form(
1327 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
1328 form_callbacks.form_handle = form.get();
tonikitoo81d92f82016-09-21 12:44:56 -07001329
Tom Sepezc46d0002015-11-30 15:46:36 -08001330#ifdef PDF_ENABLE_XFA
thestigb97e07e2016-09-28 22:16:40 -07001331 int doc_type = DOCTYPE_PDF;
Tom Sepez1d17a042017-03-16 13:22:47 -07001332 if (FPDF_HasXFAField(doc.get(), &doc_type) && doc_type != DOCTYPE_PDF &&
1333 !FPDF_LoadXFA(doc.get())) {
Lei Zhang5377ebf2015-09-23 14:52:53 -07001334 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
Tom Sepez56451382014-12-05 13:30:51 -08001335 }
Tom Sepezc46d0002015-11-30 15:46:36 -08001336#endif // PDF_ENABLE_XFA
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001337
Tom Sepez1d17a042017-03-16 13:22:47 -07001338 FPDF_SetFormFieldHighlightColor(form.get(), 0, 0xFFE4DD);
1339 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
1340 FORM_DoDocumentJSAction(form.get());
1341 FORM_DoDocumentOpenAction(form.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001342
Lei Zhangd1c9b452017-05-05 17:21:36 -07001343#if _WIN32
1344 if (options.output_format == OUTPUT_PS2)
Lei Zhangd2be6462017-07-21 14:31:21 -07001345 FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
Lei Zhangd1c9b452017-05-05 17:21:36 -07001346 else if (options.output_format == OUTPUT_PS3)
Lei Zhangd2be6462017-07-21 14:31:21 -07001347 FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
Lei Zhangd1c9b452017-05-05 17:21:36 -07001348#endif
1349
Tom Sepez1d17a042017-03-16 13:22:47 -07001350 int page_count = FPDF_GetPageCount(doc.get());
Tom Sepez1ed8a212015-05-11 15:25:39 -07001351 int rendered_pages = 0;
1352 int bad_pages = 0;
npmfa20cd52016-11-14 13:33:40 -08001353 int first_page = options.pages ? options.first_page : 0;
1354 int last_page = options.pages ? options.last_page + 1 : page_count;
1355 for (int i = first_page; i < last_page; ++i) {
Jun Fangdf7f3662015-11-10 18:29:18 +08001356 if (bIsLinearized) {
1357 nRet = PDF_DATA_NOTAVAIL;
thestig514e8c92016-07-15 17:57:54 -07001358 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -07001359 nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
thestig514e8c92016-07-15 17:57:54 -07001360
Jun Fangdf7f3662015-11-10 18:29:18 +08001361 if (nRet == PDF_DATA_ERROR) {
1362 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
1363 i);
1364 return;
1365 }
1366 }
Lei Zhangd2be6462017-07-21 14:31:21 -07001367 if (RenderPage(name, doc.get(), form.get(), &form_callbacks, i, options,
1368 events)) {
Jun Fangdf7f3662015-11-10 18:29:18 +08001369 ++rendered_pages;
Lei Zhangd2be6462017-07-21 14:31:21 -07001370 } else {
Lei Zhang5377ebf2015-09-23 14:52:53 -07001371 ++bad_pages;
Lei Zhangd2be6462017-07-21 14:31:21 -07001372 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001373 }
1374
Tom Sepez1d17a042017-03-16 13:22:47 -07001375 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
Tom Sepez1ed8a212015-05-11 15:25:39 -07001376 fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
tsepez10b01bf2016-05-04 12:52:42 -07001377 if (bad_pages)
1378 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001379}
1380
Lei Zhangd2be6462017-07-21 14:31:21 -07001381void ShowConfig() {
Tom Sepez2991d8d2016-01-15 16:02:48 -08001382 std::string config;
1383 std::string maybe_comma;
1384#if PDF_ENABLE_V8
1385 config.append(maybe_comma);
1386 config.append("V8");
1387 maybe_comma = ",";
1388#endif // PDF_ENABLE_V8
1389#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1390 config.append(maybe_comma);
1391 config.append("V8_EXTERNAL");
1392 maybe_comma = ",";
1393#endif // V8_USE_EXTERNAL_STARTUP_DATA
1394#ifdef PDF_ENABLE_XFA
1395 config.append(maybe_comma);
1396 config.append("XFA");
1397 maybe_comma = ",";
1398#endif // PDF_ENABLE_XFA
dan sinclair00d40642017-01-30 19:48:54 -08001399#ifdef PDF_ENABLE_ASAN
1400 config.append(maybe_comma);
1401 config.append("ASAN");
1402 maybe_comma = ",";
1403#endif // PDF_ENABLE_ASAN
Tom Sepez2991d8d2016-01-15 16:02:48 -08001404 printf("%s\n", config.c_str());
1405}
1406
Lei Zhangd2be6462017-07-21 14:31:21 -07001407constexpr char kUsageString[] =
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001408 "Usage: pdfium_test [OPTION] [FILE]...\n"
Jane Liu4442d452017-07-19 10:19:42 -04001409 " --show-config - print build options and exit\n"
1410 " --show-metadata - print the file metadata\n"
1411 " --show-structure - print the structure elements from the document\n"
1412 " --send-events - send input described by .evt file\n"
1413 " --render-oneshot - render image without using progressive renderer\n"
1414 " --save-attachments - write embedded attachments "
1415 "<pdf-name>.attachment.<attachment-name>\n"
Henrique Nakashima95ea7782017-07-11 16:42:43 -04001416#ifdef ENABLE_CALLGRIND
Jane Liu4442d452017-07-19 10:19:42 -04001417 " --callgrind-delim - delimit interesting section when using callgrind\n"
Henrique Nakashima95ea7782017-07-11 16:42:43 -04001418#endif // ENABLE_CALLGRIND
Jane Liu4442d452017-07-19 10:19:42 -04001419 " --bin-dir=<path> - override path to v8 external data\n"
1420 " --font-dir=<path> - override path to external fonts\n"
1421 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
npmfa20cd52016-11-14 13:33:40 -08001422 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001423#ifdef _WIN32
Jane Liu57f228d2017-07-13 18:10:30 -04001424 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
1425 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
1426 " --ps2 - write page raw PostScript (Lvl 2) "
1427 "<pdf-name>.<page-number>.ps\n"
1428 " --ps3 - write page raw PostScript (Lvl 3) "
1429 "<pdf-name>.<page-number>.ps\n"
Tom Sepezc46d0002015-11-30 15:46:36 -08001430#endif // _WIN32
Jane Liu57f228d2017-07-13 18:10:30 -04001431 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
1432 " --png - write page images <pdf-name>.<page-number>.png\n"
1433 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
1434 " --annot - write annotation info <pdf-name>.<page-number>.annot.txt\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001435#ifdef PDF_ENABLE_SKIA
Jane Liu57f228d2017-07-13 18:10:30 -04001436 " --skp - write page images <pdf-name>.<page-number>.skp\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001437#endif
Jane Liu57f228d2017-07-13 18:10:30 -04001438 " --md5 - write output image paths and their md5 hashes to stdout.\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001439 "";
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001440
Lei Zhangd2be6462017-07-21 14:31:21 -07001441} // namespace
1442
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001443int main(int argc, const char* argv[]) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001444 std::vector<std::string> args(argv, argv + argc);
1445 Options options;
thestig514e8c92016-07-15 17:57:54 -07001446 std::vector<std::string> files;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001447 if (!ParseCommandLine(args, &options, &files)) {
thestig514e8c92016-07-15 17:57:54 -07001448 fprintf(stderr, "%s", kUsageString);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001449 return 1;
1450 }
1451
Tom Sepez2991d8d2016-01-15 16:02:48 -08001452 if (options.show_config) {
1453 ShowConfig();
1454 return 0;
1455 }
1456
1457 if (files.empty()) {
1458 fprintf(stderr, "No input files.\n");
1459 return 1;
1460 }
1461
Tom Sepez452b4f32015-10-13 09:27:27 -07001462#ifdef PDF_ENABLE_V8
Tom Sepezd831dc72015-10-19 16:04:22 -07001463 v8::Platform* platform;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001464#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1465 v8::StartupData natives;
1466 v8::StartupData snapshot;
Tom Sepezd831dc72015-10-19 16:04:22 -07001467 InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
1468 &snapshot, &platform);
1469#else // V8_USE_EXTERNAL_STARTUP_DATA
jochen9e077d22016-06-09 02:51:13 -07001470 InitializeV8ForPDFium(options.exe_path, &platform);
Tom Sepez5ee12d72014-12-17 16:24:01 -08001471#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001472#endif // PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -08001473
Tom Sepeza72e8e22015-10-07 10:17:53 -07001474 FPDF_LIBRARY_CONFIG config;
1475 config.version = 2;
1476 config.m_pUserFontPaths = nullptr;
1477 config.m_pIsolate = nullptr;
1478 config.m_v8EmbedderSlot = 0;
1479
1480 const char* path_array[2];
1481 if (!options.font_directory.empty()) {
Lei Zhang6f62d532015-09-23 15:31:44 -07001482 path_array[0] = options.font_directory.c_str();
1483 path_array[1] = nullptr;
Lei Zhang6f62d532015-09-23 15:31:44 -07001484 config.m_pUserFontPaths = path_array;
Lei Zhang6f62d532015-09-23 15:31:44 -07001485 }
Tom Sepeza72e8e22015-10-07 10:17:53 -07001486 FPDF_InitLibraryWithConfig(&config);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001487
Lei Zhangd2be6462017-07-21 14:31:21 -07001488 UNSUPPORT_INFO unsupported_info = {};
npmfa20cd52016-11-14 13:33:40 -08001489 unsupported_info.version = 1;
1490 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001491
npmfa20cd52016-11-14 13:33:40 -08001492 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001493
thestig514e8c92016-07-15 17:57:54 -07001494 for (const std::string& filename : files) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001495 size_t file_length = 0;
Tom Sepez0aa35312016-01-06 10:16:32 -08001496 std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
1497 GetFileContents(filename.c_str(), &file_length);
tsepezf09bdfa2016-04-18 16:08:26 -07001498 if (!file_contents)
1499 continue;
tsepez10b01bf2016-05-04 12:52:42 -07001500 fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
Henrique Nakashima95ea7782017-07-11 16:42:43 -04001501
1502#ifdef ENABLE_CALLGRIND
1503 if (options.callgrind_delimiters)
1504 CALLGRIND_START_INSTRUMENTATION;
1505#endif // ENABLE_CALLGRIND
1506
tsepezf09bdfa2016-04-18 16:08:26 -07001507 std::string events;
1508 if (options.send_events) {
1509 std::string event_filename = filename;
1510 size_t event_length = 0;
1511 size_t extension_pos = event_filename.find(".pdf");
1512 if (extension_pos != std::string::npos) {
1513 event_filename.replace(extension_pos, 4, ".evt");
thestigf2b940c2016-10-13 06:48:47 -07001514 if (access(event_filename.c_str(), R_OK) == 0) {
1515 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1516 std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
1517 GetFileContents(event_filename.c_str(), &event_length);
1518 if (event_contents) {
1519 fprintf(stderr, "Sending events from: %s\n",
1520 event_filename.c_str());
1521 events = std::string(event_contents.get(), event_length);
1522 }
tsepezf09bdfa2016-04-18 16:08:26 -07001523 }
1524 }
1525 }
1526 RenderPdf(filename, file_contents.get(), file_length, options, events);
Henrique Nakashima95ea7782017-07-11 16:42:43 -04001527
1528#ifdef ENABLE_CALLGRIND
1529 if (options.callgrind_delimiters)
1530 CALLGRIND_STOP_INSTRUMENTATION;
1531#endif // ENABLE_CALLGRIND
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001532 }
1533
1534 FPDF_DestroyLibrary();
Tom Sepez452b4f32015-10-13 09:27:27 -07001535#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -08001536 v8::V8::ShutdownPlatform();
1537 delete platform;
thestigc08cd7a2016-06-27 09:47:59 -07001538
1539#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1540 free(const_cast<char*>(natives.data));
1541 free(const_cast<char*>(snapshot.data));
1542#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001543#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001544
1545 return 0;
1546}