blob: c15a1a864829844569ffdc57ba6f445edf03dd50 [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
tonikitoo3e981582016-08-26 08:37:10 -070010#include <map>
Tom Sepezdaa2e842015-01-29 15:44:37 -080011#include <sstream>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070012#include <string>
13#include <utility>
Tom Sepez5ee12d72014-12-17 16:24:01 -080014#include <vector>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070015
Cary Clark399be5b2016-03-14 16:51:29 -040016#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
17#define _SKIA_SUPPORT_
18#endif
19
stephanafa05e972017-01-02 06:19:41 -080020#include "core/fdrm/crypto/fx_crypt.h"
Tom Sepez1d17a042017-03-16 13:22:47 -070021#include "public/cpp/fpdf_deleters.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080022#include "public/fpdf_dataavail.h"
Lei Zhang453d96b2015-12-31 13:13:10 -080023#include "public/fpdf_edit.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080024#include "public/fpdf_ext.h"
25#include "public/fpdf_formfill.h"
26#include "public/fpdf_text.h"
27#include "public/fpdfview.h"
Dan Sinclairefbc1912016-02-17 16:54:43 -050028#include "samples/image_diff_png.h"
Lei Zhangbde53d22015-11-12 22:21:30 -080029#include "testing/test_support.h"
Tom Sepezd831dc72015-10-19 16:04:22 -070030
thestigf2b940c2016-10-13 06:48:47 -070031#ifdef _WIN32
32#include <io.h>
33#else
34#include <unistd.h>
35#endif
36
Tom Sepez452b4f32015-10-13 09:27:27 -070037#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -080038#include "v8/include/libplatform/libplatform.h"
Tom Sepez1ed8a212015-05-11 15:25:39 -070039#include "v8/include/v8.h"
Lei Zhang8241df72015-11-06 14:38:48 -080040#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070041
Cary Clark399be5b2016-03-14 16:51:29 -040042#ifdef PDF_ENABLE_SKIA
43#include "third_party/skia/include/core/SkPictureRecorder.h"
44#include "third_party/skia/include/core/SkStream.h"
45#endif
46
thestigf2b940c2016-10-13 06:48:47 -070047#ifdef _WIN32
48#define access _access
49#define snprintf _snprintf
50#define R_OK 4
51#endif
52
Vitaly Buka9e0177a2014-07-22 18:15:42 -070053enum OutputFormat {
54 OUTPUT_NONE,
dsinclairb63068f2016-06-16 07:58:09 -070055 OUTPUT_TEXT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070056 OUTPUT_PPM,
Tom Sepezaf18cb32015-02-05 15:06:01 -080057 OUTPUT_PNG,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070058#ifdef _WIN32
59 OUTPUT_BMP,
60 OUTPUT_EMF,
61#endif
Cary Clark399be5b2016-03-14 16:51:29 -040062#ifdef PDF_ENABLE_SKIA
63 OUTPUT_SKP,
64#endif
Vitaly Buka9e0177a2014-07-22 18:15:42 -070065};
66
Tom Sepez5ee12d72014-12-17 16:24:01 -080067struct Options {
tsepezf09bdfa2016-04-18 16:08:26 -070068 Options()
npme3c73152016-11-14 09:14:52 -080069 : show_config(false),
70 send_events(false),
71 pages(false),
stephanafa05e972017-01-02 06:19:41 -080072 md5(false),
npme3c73152016-11-14 09:14:52 -080073 output_format(OUTPUT_NONE) {}
Tom Sepez5ee12d72014-12-17 16:24:01 -080074
Tom Sepez2991d8d2016-01-15 16:02:48 -080075 bool show_config;
tsepezf09bdfa2016-04-18 16:08:26 -070076 bool send_events;
npme3c73152016-11-14 09:14:52 -080077 bool pages;
stephanafa05e972017-01-02 06:19:41 -080078 bool md5;
Tom Sepez5ee12d72014-12-17 16:24:01 -080079 OutputFormat output_format;
Tom Sepezdaa2e842015-01-29 15:44:37 -080080 std::string scale_factor_as_string;
Tom Sepez5ee12d72014-12-17 16:24:01 -080081 std::string exe_path;
82 std::string bin_directory;
Lei Zhang6f62d532015-09-23 15:31:44 -070083 std::string font_directory;
npmfa20cd52016-11-14 13:33:40 -080084 // 0-based page numbers to be rendered.
85 int first_page;
86 int last_page;
Tom Sepez5ee12d72014-12-17 16:24:01 -080087};
88
tonikitoo81d92f82016-09-21 12:44:56 -070089struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
90 // Hold a map of the currently loaded pages in order to avoid them
91 // to get loaded twice.
npmfa20cd52016-11-14 13:33:40 -080092 std::map<int, FPDF_PAGE> loaded_pages;
tonikitoo81d92f82016-09-21 12:44:56 -070093
94 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
95 // make use of it.
npmfa20cd52016-11-14 13:33:40 -080096 FPDF_FORMHANDLE form_handle;
tonikitoo81d92f82016-09-21 12:44:56 -070097};
98
99static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
npmfa20cd52016-11-14 13:33:40 -0800100 FPDF_FORMFILLINFO* form_fill_info) {
101 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
tonikitoo81d92f82016-09-21 12:44:56 -0700102}
103
Tom Sepezaf18cb32015-02-05 15:06:01 -0800104static bool CheckDimensions(int stride, int width, int height) {
105 if (stride < 0 || width < 0 || height < 0)
106 return false;
107 if (height > 0 && width > INT_MAX / height)
108 return false;
109 return true;
110}
111
stephanafa05e972017-01-02 06:19:41 -0800112static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
113 // Get the MD5 hash and write it to stdout.
114 uint8_t digest[16];
115 CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(buffer), len, digest);
116 printf("MD5:%s:", file_name);
117 for (int i = 0; i < 16; i++)
118 printf("%02x", digest[i]);
119 printf("\n");
120}
121
122static std::string WritePpm(const char* pdf_name,
123 int num,
124 const void* buffer_void,
125 int stride,
126 int width,
127 int height) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700128 const char* buffer = reinterpret_cast<const char*>(buffer_void);
129
Tom Sepezaf18cb32015-02-05 15:06:01 -0800130 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800131 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800132
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700133 int out_len = width * height;
134 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800135 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700136 out_len *= 3;
137
138 char filename[256];
139 snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
John Abd-El-Maleka548d302014-06-26 10:18:11 -0700140 FILE* fp = fopen(filename, "wb");
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700141 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800142 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700143 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
144 // Source data is B, G, R, unused.
145 // Dest data is R, G, B.
thestig514e8c92016-07-15 17:57:54 -0700146 std::vector<char> result(out_len);
Lei Zhange00660b2015-08-13 15:40:18 -0700147 for (int h = 0; h < height; ++h) {
148 const char* src_line = buffer + (stride * h);
thestig514e8c92016-07-15 17:57:54 -0700149 char* dest_line = result.data() + (width * h * 3);
Lei Zhange00660b2015-08-13 15:40:18 -0700150 for (int w = 0; w < width; ++w) {
151 // R
152 dest_line[w * 3] = src_line[(w * 4) + 2];
153 // G
154 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
155 // B
156 dest_line[(w * 3) + 2] = src_line[w * 4];
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700157 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700158 }
thestig514e8c92016-07-15 17:57:54 -0700159 fwrite(result.data(), out_len, 1, fp);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700160 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800161 return std::string(filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700162}
163
dsinclairb63068f2016-06-16 07:58:09 -0700164void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
165 char filename[256];
166 int chars_formatted =
167 snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
168 if (chars_formatted < 0 ||
169 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
170 fprintf(stderr, "Filename %s is too long\n", filename);
171 return;
172 }
173
174 FILE* fp = fopen(filename, "w");
175 if (!fp) {
176 fprintf(stderr, "Failed to open %s for output\n", filename);
177 return;
178 }
179
180 // Output in UTF32-LE.
181 uint32_t bom = 0x0000FEFF;
182 fwrite(&bom, sizeof(bom), 1, fp);
183
Tom Sepez1d17a042017-03-16 13:22:47 -0700184 std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
185 for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
186 uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
dsinclairb63068f2016-06-16 07:58:09 -0700187 fwrite(&c, sizeof(c), 1, fp);
188 }
dsinclairb63068f2016-06-16 07:58:09 -0700189 (void)fclose(fp);
190}
191
stephanafa05e972017-01-02 06:19:41 -0800192static std::string WritePng(const char* pdf_name,
193 int num,
194 const void* buffer_void,
195 int stride,
196 int width,
197 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800198 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800199 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800200
201 std::vector<unsigned char> png_encoding;
202 const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
203 if (!image_diff_png::EncodeBGRAPNG(
204 buffer, width, height, stride, false, &png_encoding)) {
205 fprintf(stderr, "Failed to convert bitmap to PNG\n");
stephanafa05e972017-01-02 06:19:41 -0800206 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800207 }
208
209 char filename[256];
210 int chars_formatted = snprintf(
211 filename, sizeof(filename), "%s.%d.png", pdf_name, num);
212 if (chars_formatted < 0 ||
213 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
Cary Clark399be5b2016-03-14 16:51:29 -0400214 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800215 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800216 }
217
218 FILE* fp = fopen(filename, "wb");
219 if (!fp) {
220 fprintf(stderr, "Failed to open %s for output\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800221 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800222 }
223
224 size_t bytes_written = fwrite(
225 &png_encoding.front(), 1, png_encoding.size(), fp);
226 if (bytes_written != png_encoding.size())
227 fprintf(stderr, "Failed to write to %s\n", filename);
228
Lei Zhang5377ebf2015-09-23 14:52:53 -0700229 (void)fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800230 return std::string(filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800231}
232
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700233#ifdef _WIN32
stephanafa05e972017-01-02 06:19:41 -0800234static std::string WriteBmp(const char* pdf_name,
235 int num,
236 const void* buffer,
237 int stride,
238 int width,
239 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800240 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800241 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800242
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700243 int out_len = stride * height;
244 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800245 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700246
247 char filename[256];
248 snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
249 FILE* fp = fopen(filename, "wb");
250 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800251 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700252
Nico Weber2827bdd2015-07-01 14:08:08 -0700253 BITMAPINFO bmi = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700254 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
255 bmi.bmiHeader.biWidth = width;
256 bmi.bmiHeader.biHeight = -height; // top-down image
257 bmi.bmiHeader.biPlanes = 1;
258 bmi.bmiHeader.biBitCount = 32;
259 bmi.bmiHeader.biCompression = BI_RGB;
260 bmi.bmiHeader.biSizeImage = 0;
261
Nico Weber2827bdd2015-07-01 14:08:08 -0700262 BITMAPFILEHEADER file_header = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700263 file_header.bfType = 0x4d42;
264 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
265 file_header.bfOffBits = file_header.bfSize - out_len;
266
267 fwrite(&file_header, sizeof(file_header), 1, fp);
268 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
269 fwrite(buffer, out_len, 1, fp);
270 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800271 return std::string(filename);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700272}
273
274void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
275 int width = static_cast<int>(FPDF_GetPageWidth(page));
276 int height = static_cast<int>(FPDF_GetPageHeight(page));
277
278 char filename[256];
279 snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
280
Lei Zhang5377ebf2015-09-23 14:52:53 -0700281 HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800282
283 HRGN rgn = CreateRectRgn(0, 0, width, height);
284 SelectClipRgn(dc, rgn);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700285 DeleteObject(rgn);
286
287 SelectObject(dc, GetStockObject(NULL_PEN));
288 SelectObject(dc, GetStockObject(WHITE_BRUSH));
289 // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
290 Rectangle(dc, 0, 0, width + 1, height + 1);
291
292 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
293 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
294
295 DeleteEnhMetaFile(CloseEnhMetaFile(dc));
296}
297#endif
298
Cary Clark399be5b2016-03-14 16:51:29 -0400299#ifdef PDF_ENABLE_SKIA
stephanafa05e972017-01-02 06:19:41 -0800300static std::string WriteSkp(const char* pdf_name,
301 int num,
302 SkPictureRecorder* recorder) {
Cary Clark399be5b2016-03-14 16:51:29 -0400303 char filename[256];
304 int chars_formatted =
305 snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
306
307 if (chars_formatted < 0 ||
308 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
309 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800310 return "";
Cary Clark399be5b2016-03-14 16:51:29 -0400311 }
312
thestigb97e07e2016-09-28 22:16:40 -0700313 sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
Cary Clark399be5b2016-03-14 16:51:29 -0400314 SkFILEWStream wStream(filename);
315 picture->serialize(&wStream);
stephanafa05e972017-01-02 06:19:41 -0800316 return std::string(filename);
Cary Clark399be5b2016-03-14 16:51:29 -0400317}
318#endif
319
Tom Sepez58fb36a2016-02-01 10:32:14 -0800320// These example JS platform callback handlers are entirely optional,
321// and exist here to show the flow of information from a document back
322// to the embedder.
Tom Sepezbd932572016-01-29 09:10:41 -0800323int ExampleAppAlert(IPDF_JSPLATFORM*,
324 FPDF_WIDESTRING msg,
325 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800326 int type,
327 int icon) {
Tom Sepezbd932572016-01-29 09:10:41 -0800328 printf("%ls", GetPlatformWString(title).c_str());
npmfa20cd52016-11-14 13:33:40 -0800329 if (icon || type)
330 printf("[icon=%d,type=%d]", icon, type);
Tom Sepezbd932572016-01-29 09:10:41 -0800331 printf(": %ls\n", GetPlatformWString(msg).c_str());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700332 return 0;
333}
334
Tom Sepez58fb36a2016-02-01 10:32:14 -0800335int ExampleAppResponse(IPDF_JSPLATFORM*,
336 FPDF_WIDESTRING question,
337 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800338 FPDF_WIDESTRING default_value,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800339 FPDF_WIDESTRING label,
npmfa20cd52016-11-14 13:33:40 -0800340 FPDF_BOOL is_password,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800341 void* response,
342 int length) {
343 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
344 GetPlatformWString(title).c_str(),
345 GetPlatformWString(question).c_str(),
npmfa20cd52016-11-14 13:33:40 -0800346 GetPlatformWString(default_value).c_str(),
347 GetPlatformWString(label).c_str(), is_password, length);
Tom Sepez58fb36a2016-02-01 10:32:14 -0800348
349 // UTF-16, always LE regardless of platform.
350 uint8_t* ptr = static_cast<uint8_t*>(response);
351 ptr[0] = 'N';
352 ptr[1] = 0;
353 ptr[2] = 'o';
354 ptr[3] = 0;
355 return 4;
356}
357
npmfa20cd52016-11-14 13:33:40 -0800358void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
359 printf("Goto Page: %d\n", page_number);
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800360}
361
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800362void ExampleDocMail(IPDF_JSPLATFORM*,
363 void* mailData,
364 int length,
npmfa20cd52016-11-14 13:33:40 -0800365 FPDF_BOOL UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800366 FPDF_WIDESTRING To,
367 FPDF_WIDESTRING Subject,
368 FPDF_WIDESTRING CC,
369 FPDF_WIDESTRING BCC,
370 FPDF_WIDESTRING Msg) {
npmfa20cd52016-11-14 13:33:40 -0800371 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800372 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
373 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
374 GetPlatformWString(Msg).c_str());
375}
376
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800377void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700378 std::string feature = "Unknown";
379 switch (type) {
380 case FPDF_UNSP_DOC_XFAFORM:
381 feature = "XFA";
382 break;
383 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
384 feature = "Portfolios_Packages";
385 break;
386 case FPDF_UNSP_DOC_ATTACHMENT:
387 case FPDF_UNSP_ANNOT_ATTACHMENT:
388 feature = "Attachment";
389 break;
390 case FPDF_UNSP_DOC_SECURITY:
391 feature = "Rights_Management";
392 break;
393 case FPDF_UNSP_DOC_SHAREDREVIEW:
394 feature = "Shared_Review";
395 break;
396 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
397 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
398 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
399 feature = "Shared_Form";
400 break;
401 case FPDF_UNSP_ANNOT_3DANNOT:
402 feature = "3D";
403 break;
404 case FPDF_UNSP_ANNOT_MOVIE:
405 feature = "Movie";
406 break;
407 case FPDF_UNSP_ANNOT_SOUND:
408 feature = "Sound";
409 break;
410 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
411 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
412 feature = "Screen";
413 break;
414 case FPDF_UNSP_ANNOT_SIG:
415 feature = "Digital_Signature";
416 break;
417 }
418 printf("Unsupported feature: %s.\n", feature.c_str());
419}
420
Tom Sepez5ee12d72014-12-17 16:24:01 -0800421bool ParseCommandLine(const std::vector<std::string>& args,
thestig514e8c92016-07-15 17:57:54 -0700422 Options* options,
423 std::vector<std::string>* files) {
424 if (args.empty())
Tom Sepez5ee12d72014-12-17 16:24:01 -0800425 return false;
thestig514e8c92016-07-15 17:57:54 -0700426
Tom Sepez5ee12d72014-12-17 16:24:01 -0800427 options->exe_path = args[0];
Bo Xud44e3922014-12-19 02:27:25 -0800428 size_t cur_idx = 1;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800429 for (; cur_idx < args.size(); ++cur_idx) {
430 const std::string& cur_arg = args[cur_idx];
Tom Sepez2991d8d2016-01-15 16:02:48 -0800431 if (cur_arg == "--show-config") {
432 options->show_config = true;
tsepezf09bdfa2016-04-18 16:08:26 -0700433 } else if (cur_arg == "--send-events") {
434 options->send_events = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800435 } else if (cur_arg == "--ppm") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800436 if (options->output_format != OUTPUT_NONE) {
437 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
438 return false;
439 }
440 options->output_format = OUTPUT_PPM;
Tom Sepezaf18cb32015-02-05 15:06:01 -0800441 } else if (cur_arg == "--png") {
442 if (options->output_format != OUTPUT_NONE) {
443 fprintf(stderr, "Duplicate or conflicting --png argument\n");
444 return false;
445 }
446 options->output_format = OUTPUT_PNG;
dsinclairb63068f2016-06-16 07:58:09 -0700447 } else if (cur_arg == "--txt") {
448 if (options->output_format != OUTPUT_NONE) {
449 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
450 return false;
451 }
452 options->output_format = OUTPUT_TEXT;
Cary Clark399be5b2016-03-14 16:51:29 -0400453#ifdef PDF_ENABLE_SKIA
454 } else if (cur_arg == "--skp") {
455 if (options->output_format != OUTPUT_NONE) {
456 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
457 return false;
458 }
459 options->output_format = OUTPUT_SKP;
460#endif
Lei Zhang6f62d532015-09-23 15:31:44 -0700461 } else if (cur_arg.size() > 11 &&
462 cur_arg.compare(0, 11, "--font-dir=") == 0) {
463 if (!options->font_directory.empty()) {
464 fprintf(stderr, "Duplicate --font-dir argument\n");
465 return false;
466 }
467 options->font_directory = cur_arg.substr(11);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700468#ifdef _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500469 } else if (cur_arg == "--emf") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800470 if (options->output_format != OUTPUT_NONE) {
471 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
472 return false;
473 }
474 options->output_format = OUTPUT_EMF;
Dan Sinclair50cce602016-02-24 09:51:16 -0500475 } else if (cur_arg == "--bmp") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800476 if (options->output_format != OUTPUT_NONE) {
477 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
478 return false;
479 }
480 options->output_format = OUTPUT_BMP;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800481#endif // _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500482
Tom Sepez452b4f32015-10-13 09:27:27 -0700483#ifdef PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800484#ifdef V8_USE_EXTERNAL_STARTUP_DATA
Dan Sinclair738b08c2016-03-01 14:45:20 -0500485 } else if (cur_arg.size() > 10 &&
486 cur_arg.compare(0, 10, "--bin-dir=") == 0) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800487 if (!options->bin_directory.empty()) {
488 fprintf(stderr, "Duplicate --bin-dir argument\n");
489 return false;
490 }
491 options->bin_directory = cur_arg.substr(10);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800492#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700493#endif // PDF_ENABLE_V8
Dan Sinclair738b08c2016-03-01 14:45:20 -0500494
495 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
Tom Sepezdaa2e842015-01-29 15:44:37 -0800496 if (!options->scale_factor_as_string.empty()) {
497 fprintf(stderr, "Duplicate --scale argument\n");
498 return false;
499 }
500 options->scale_factor_as_string = cur_arg.substr(8);
npme3c73152016-11-14 09:14:52 -0800501 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
502 if (options->pages) {
503 fprintf(stderr, "Duplicate --pages argument\n");
504 return false;
505 }
506 options->pages = true;
npmfa20cd52016-11-14 13:33:40 -0800507 const std::string pages_string = cur_arg.substr(8);
508 size_t first_dash = pages_string.find("-");
509 if (first_dash == std::string::npos) {
510 std::stringstream(pages_string) >> options->first_page;
511 options->last_page = options->first_page;
npme3c73152016-11-14 09:14:52 -0800512 } else {
npmfa20cd52016-11-14 13:33:40 -0800513 std::stringstream(pages_string.substr(0, first_dash)) >>
514 options->first_page;
515 std::stringstream(pages_string.substr(first_dash + 1)) >>
516 options->last_page;
npme3c73152016-11-14 09:14:52 -0800517 }
stephanafa05e972017-01-02 06:19:41 -0800518 } else if (cur_arg == "--md5") {
519 options->md5 = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800520 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
521 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
522 return false;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500523 } else {
Vitaly Buka8f2c3dc2014-08-20 10:32:36 -0700524 break;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500525 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700526 }
thestig514e8c92016-07-15 17:57:54 -0700527 for (size_t i = cur_idx; i < args.size(); i++)
Tom Sepez5ee12d72014-12-17 16:24:01 -0800528 files->push_back(args[i]);
thestig514e8c92016-07-15 17:57:54 -0700529
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700530 return true;
531}
532
npmfa20cd52016-11-14 13:33:40 -0800533FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700534 return true;
535}
536
npmfa20cd52016-11-14 13:33:40 -0800537void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700538
tsepezf09bdfa2016-04-18 16:08:26 -0700539void SendPageEvents(const FPDF_FORMHANDLE& form,
540 const FPDF_PAGE& page,
541 const std::string& events) {
542 auto lines = StringSplit(events, '\n');
543 for (auto line : lines) {
544 auto command = StringSplit(line, '#');
545 if (command[0].empty())
546 continue;
547 auto tokens = StringSplit(command[0], ',');
548 if (tokens[0] == "keycode") {
549 if (tokens.size() == 2) {
550 int keycode = atoi(tokens[1].c_str());
551 FORM_OnKeyDown(form, page, keycode, 0);
552 FORM_OnKeyUp(form, page, keycode, 0);
553 } else {
554 fprintf(stderr, "keycode: bad args\n");
555 }
556 } else if (tokens[0] == "mousedown") {
557 if (tokens.size() == 4) {
558 int x = atoi(tokens[2].c_str());
559 int y = atoi(tokens[3].c_str());
560 if (tokens[1] == "left")
561 FORM_OnLButtonDown(form, page, 0, x, y);
562#ifdef PDF_ENABLE_XFA
563 else if (tokens[1] == "right")
564 FORM_OnRButtonDown(form, page, 0, x, y);
565#endif
566 else
567 fprintf(stderr, "mousedown: bad button name\n");
568 } else {
569 fprintf(stderr, "mousedown: bad args\n");
570 }
571 } else if (tokens[0] == "mouseup") {
572 if (tokens.size() == 4) {
573 int x = atoi(tokens[2].c_str());
574 int y = atoi(tokens[3].c_str());
575 if (tokens[1] == "left")
576 FORM_OnLButtonUp(form, page, 0, x, y);
577#ifdef PDF_ENABLE_XFA
578 else if (tokens[1] == "right")
579 FORM_OnRButtonUp(form, page, 0, x, y);
580#endif
581 else
582 fprintf(stderr, "mouseup: bad button name\n");
583 } else {
584 fprintf(stderr, "mouseup: bad args\n");
585 }
586 } else if (tokens[0] == "mousemove") {
587 if (tokens.size() == 3) {
588 int x = atoi(tokens[1].c_str());
589 int y = atoi(tokens[2].c_str());
590 FORM_OnMouseMove(form, page, 0, x, y);
591 } else {
592 fprintf(stderr, "mousemove: bad args\n");
593 }
594 } else {
595 fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
596 }
597 }
598}
599
tonikitoo3e981582016-08-26 08:37:10 -0700600FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
601 FPDF_DOCUMENT doc,
602 int index) {
npmfa20cd52016-11-14 13:33:40 -0800603 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
604 ToPDFiumTestFormFillInfo(param);
605 auto& loaded_pages = form_fill_info->loaded_pages;
npmfa20cd52016-11-14 13:33:40 -0800606 auto iter = loaded_pages.find(index);
607 if (iter != loaded_pages.end())
tonikitoo3e981582016-08-26 08:37:10 -0700608 return iter->second;
609
610 FPDF_PAGE page = FPDF_LoadPage(doc, index);
611 if (!page)
612 return nullptr;
613
npmfa20cd52016-11-14 13:33:40 -0800614 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
npmfa20cd52016-11-14 13:33:40 -0800615 FORM_OnAfterLoadPage(page, form_handle);
616 FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
npmfa20cd52016-11-14 13:33:40 -0800617 loaded_pages[index] = page;
tonikitoo3e981582016-08-26 08:37:10 -0700618 return page;
619}
620
Jun Fangb553bcb2015-11-10 18:49:04 +0800621bool RenderPage(const std::string& name,
tonikitoo3e981582016-08-26 08:37:10 -0700622 FPDF_DOCUMENT doc,
Tom Sepez1d17a042017-03-16 13:22:47 -0700623 FPDF_FORMHANDLE form,
npmfa20cd52016-11-14 13:33:40 -0800624 FPDF_FORMFILLINFO_PDFiumTest& form_fill_info,
Jun Fangb553bcb2015-11-10 18:49:04 +0800625 const int page_index,
tsepezf09bdfa2016-04-18 16:08:26 -0700626 const Options& options,
627 const std::string& events) {
Tom Sepez1d17a042017-03-16 13:22:47 -0700628 std::unique_ptr<void, FPDFPageDeleter> page(
629 GetPageForIndex(&form_fill_info, doc, page_index));
630 if (!page.get())
Jun Fangb553bcb2015-11-10 18:49:04 +0800631 return false;
thestig514e8c92016-07-15 17:57:54 -0700632
Tom Sepez1d17a042017-03-16 13:22:47 -0700633 std::unique_ptr<void, FPDFTextPageDeleter> text_page(
634 FPDFText_LoadPage(page.get()));
tsepezf09bdfa2016-04-18 16:08:26 -0700635 if (options.send_events)
Tom Sepez1d17a042017-03-16 13:22:47 -0700636 SendPageEvents(form, page.get(), events);
tsepezf09bdfa2016-04-18 16:08:26 -0700637
Jun Fangdf7f3662015-11-10 18:29:18 +0800638 double scale = 1.0;
thestig514e8c92016-07-15 17:57:54 -0700639 if (!options.scale_factor_as_string.empty())
Jun Fangdf7f3662015-11-10 18:29:18 +0800640 std::stringstream(options.scale_factor_as_string) >> scale;
thestig514e8c92016-07-15 17:57:54 -0700641
Tom Sepez1d17a042017-03-16 13:22:47 -0700642 int width = static_cast<int>(FPDF_GetPageWidth(page.get()) * scale);
643 int height = static_cast<int>(FPDF_GetPageHeight(page.get()) * scale);
644 int alpha = FPDFPage_HasTransparency(page.get()) ? 1 : 0;
645 std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
646 FPDFBitmap_Create(width, height, alpha));
647
thestige97ea032016-05-19 10:59:15 -0700648 if (bitmap) {
649 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700650 FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
651 FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
652 FPDF_ANNOT);
Jun Fangdf7f3662015-11-10 18:29:18 +0800653
Tom Sepez1d17a042017-03-16 13:22:47 -0700654 FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
655 FPDF_ANNOT);
656 int stride = FPDFBitmap_GetStride(bitmap.get());
thestige97ea032016-05-19 10:59:15 -0700657 const char* buffer =
Tom Sepez1d17a042017-03-16 13:22:47 -0700658 reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
Jun Fangdf7f3662015-11-10 18:29:18 +0800659
stephanafa05e972017-01-02 06:19:41 -0800660 std::string&& image_file_name = "";
thestige97ea032016-05-19 10:59:15 -0700661 switch (options.output_format) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800662#ifdef _WIN32
thestige97ea032016-05-19 10:59:15 -0700663 case OUTPUT_BMP:
stephanafa05e972017-01-02 06:19:41 -0800664 image_file_name =
665 WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700666 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800667
thestige97ea032016-05-19 10:59:15 -0700668 case OUTPUT_EMF:
Tom Sepez1d17a042017-03-16 13:22:47 -0700669 WriteEmf(page.get(), name.c_str(), page_index);
thestige97ea032016-05-19 10:59:15 -0700670 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800671#endif
dsinclairb63068f2016-06-16 07:58:09 -0700672 case OUTPUT_TEXT:
Tom Sepez1d17a042017-03-16 13:22:47 -0700673 WriteText(page.get(), name.c_str(), page_index);
dsinclairb63068f2016-06-16 07:58:09 -0700674 break;
675
thestige97ea032016-05-19 10:59:15 -0700676 case OUTPUT_PNG:
stephanafa05e972017-01-02 06:19:41 -0800677 image_file_name =
678 WritePng(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700679 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800680
thestige97ea032016-05-19 10:59:15 -0700681 case OUTPUT_PPM:
stephanafa05e972017-01-02 06:19:41 -0800682 image_file_name =
683 WritePpm(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700684 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800685
Cary Clark399be5b2016-03-14 16:51:29 -0400686#ifdef PDF_ENABLE_SKIA
thestige97ea032016-05-19 10:59:15 -0700687 case OUTPUT_SKP: {
688 std::unique_ptr<SkPictureRecorder> recorder(
thestigb97e07e2016-09-28 22:16:40 -0700689 reinterpret_cast<SkPictureRecorder*>(
Tom Sepez1d17a042017-03-16 13:22:47 -0700690 FPDF_RenderPageSkp(page.get(), width, height)));
691 FPDF_FFLRecord(form, recorder.get(), page.get(), 0, 0, width, height, 0,
692 0);
stephanafa05e972017-01-02 06:19:41 -0800693 image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
thestige97ea032016-05-19 10:59:15 -0700694 } break;
Cary Clark399be5b2016-03-14 16:51:29 -0400695#endif
thestige97ea032016-05-19 10:59:15 -0700696 default:
697 break;
698 }
Jun Fangdf7f3662015-11-10 18:29:18 +0800699
stephanafa05e972017-01-02 06:19:41 -0800700 // Write the filename and the MD5 of the buffer to stdout if we wrote a
701 // file.
702 if (options.md5 && image_file_name != "")
703 OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
thestige97ea032016-05-19 10:59:15 -0700704 } else {
705 fprintf(stderr, "Page was too large to be rendered.\n");
706 }
tonikitoo3e981582016-08-26 08:37:10 -0700707
npmfa20cd52016-11-14 13:33:40 -0800708 form_fill_info.loaded_pages.erase(page_index);
Tom Sepez1d17a042017-03-16 13:22:47 -0700709 FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
710 FORM_OnBeforeClosePage(page.get(), form);
thestige97ea032016-05-19 10:59:15 -0700711 return !!bitmap;
Jun Fangdf7f3662015-11-10 18:29:18 +0800712}
713
tsepezf09bdfa2016-04-18 16:08:26 -0700714void RenderPdf(const std::string& name,
715 const char* pBuf,
716 size_t len,
717 const Options& options,
718 const std::string& events) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700719 IPDF_JSPLATFORM platform_callbacks;
720 memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
Tom Sepeza72e8e22015-10-07 10:17:53 -0700721 platform_callbacks.version = 3;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800722 platform_callbacks.app_alert = ExampleAppAlert;
Tom Sepez58fb36a2016-02-01 10:32:14 -0800723 platform_callbacks.app_response = ExampleAppResponse;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800724 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800725 platform_callbacks.Doc_mail = ExampleDocMail;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700726
tonikitoo81d92f82016-09-21 12:44:56 -0700727 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
Tom Sepezc46d0002015-11-30 15:46:36 -0800728#ifdef PDF_ENABLE_XFA
Tom Sepezed631382014-11-18 14:10:25 -0800729 form_callbacks.version = 2;
Tom Sepezc46d0002015-11-30 15:46:36 -0800730#else // PDF_ENABLE_XFA
731 form_callbacks.version = 1;
732#endif // PDF_ENABLE_XFA
tonikitoo3e981582016-08-26 08:37:10 -0700733 form_callbacks.FFI_GetPage = GetPageForIndex;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700734 form_callbacks.m_pJsPlatform = &platform_callbacks;
735
736 TestLoader loader(pBuf, len);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700737 FPDF_FILEACCESS file_access;
738 memset(&file_access, '\0', sizeof(file_access));
John Abd-El-Malek7dc51722014-05-26 12:54:31 -0700739 file_access.m_FileLen = static_cast<unsigned long>(len);
Tom Sepezd831dc72015-10-19 16:04:22 -0700740 file_access.m_GetBlock = TestLoader::GetBlock;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700741 file_access.m_Param = &loader;
742
743 FX_FILEAVAIL file_avail;
744 memset(&file_avail, '\0', sizeof(file_avail));
745 file_avail.version = 1;
746 file_avail.IsDataAvail = Is_Data_Avail;
747
748 FX_DOWNLOADHINTS hints;
749 memset(&hints, '\0', sizeof(hints));
750 hints.version = 1;
751 hints.AddSegment = Add_Segment;
752
Jun Fangdf7f3662015-11-10 18:29:18 +0800753 int nRet = PDF_DATA_NOTAVAIL;
Jun Fangb553bcb2015-11-10 18:49:04 +0800754 bool bIsLinearized = false;
Tom Sepez1d17a042017-03-16 13:22:47 -0700755 std::unique_ptr<void, FPDFDocumentDeleter> doc;
756 std::unique_ptr<void, FPDFAvailDeleter> pdf_avail(
757 FPDFAvail_Create(&file_avail, &file_access));
Tom Sepezc98895c2015-11-24 15:30:36 -0800758
Tom Sepez1d17a042017-03-16 13:22:47 -0700759 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
760 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800761 if (doc) {
thestig514e8c92016-07-15 17:57:54 -0700762 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700763 nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
thestig514e8c92016-07-15 17:57:54 -0700764
Jun Fangdf7f3662015-11-10 18:29:18 +0800765 if (nRet == PDF_DATA_ERROR) {
766 fprintf(stderr, "Unknown error in checking if doc was available.\n");
767 return;
768 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700769 nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
Jun Fangdf7f3662015-11-10 18:29:18 +0800770 if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
771 fprintf(stderr,
772 "Error %d was returned in checking if form was available.\n",
773 nRet);
774 return;
775 }
Jun Fangb553bcb2015-11-10 18:49:04 +0800776 bIsLinearized = true;
Jun Fangdf7f3662015-11-10 18:29:18 +0800777 }
778 } else {
Tom Sepez1d17a042017-03-16 13:22:47 -0700779 doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800780 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700781
Lei Zhang5377ebf2015-09-23 14:52:53 -0700782 if (!doc) {
Dan Sinclaireb815bf2015-10-27 13:08:41 -0400783 unsigned long err = FPDF_GetLastError();
784 fprintf(stderr, "Load pdf docs unsuccessful: ");
785 switch (err) {
786 case FPDF_ERR_SUCCESS:
787 fprintf(stderr, "Success");
788 break;
789 case FPDF_ERR_UNKNOWN:
790 fprintf(stderr, "Unknown error");
791 break;
792 case FPDF_ERR_FILE:
793 fprintf(stderr, "File not found or could not be opened");
794 break;
795 case FPDF_ERR_FORMAT:
796 fprintf(stderr, "File not in PDF format or corrupted");
797 break;
798 case FPDF_ERR_PASSWORD:
799 fprintf(stderr, "Password required or incorrect password");
800 break;
801 case FPDF_ERR_SECURITY:
802 fprintf(stderr, "Unsupported security scheme");
803 break;
804 case FPDF_ERR_PAGE:
805 fprintf(stderr, "Page not found or content error");
806 break;
807 default:
808 fprintf(stderr, "Unknown error %ld", err);
809 }
810 fprintf(stderr, ".\n");
JUN FANG827a1722015-03-05 13:39:21 -0800811 return;
812 }
813
Tom Sepez1d17a042017-03-16 13:22:47 -0700814 (void)FPDF_GetDocPermissions(doc.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700815
Tom Sepez1d17a042017-03-16 13:22:47 -0700816 std::unique_ptr<void, FPDFFormHandleDeleter> form(
817 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
818 form_callbacks.form_handle = form.get();
tonikitoo81d92f82016-09-21 12:44:56 -0700819
Tom Sepezc46d0002015-11-30 15:46:36 -0800820#ifdef PDF_ENABLE_XFA
thestigb97e07e2016-09-28 22:16:40 -0700821 int doc_type = DOCTYPE_PDF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700822 if (FPDF_HasXFAField(doc.get(), &doc_type) && doc_type != DOCTYPE_PDF &&
823 !FPDF_LoadXFA(doc.get())) {
Lei Zhang5377ebf2015-09-23 14:52:53 -0700824 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
Tom Sepez56451382014-12-05 13:30:51 -0800825 }
Tom Sepezc46d0002015-11-30 15:46:36 -0800826#endif // PDF_ENABLE_XFA
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700827
Tom Sepez1d17a042017-03-16 13:22:47 -0700828 FPDF_SetFormFieldHighlightColor(form.get(), 0, 0xFFE4DD);
829 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
830 FORM_DoDocumentJSAction(form.get());
831 FORM_DoDocumentOpenAction(form.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700832
Tom Sepez1d17a042017-03-16 13:22:47 -0700833 int page_count = FPDF_GetPageCount(doc.get());
Tom Sepez1ed8a212015-05-11 15:25:39 -0700834 int rendered_pages = 0;
835 int bad_pages = 0;
npmfa20cd52016-11-14 13:33:40 -0800836 int first_page = options.pages ? options.first_page : 0;
837 int last_page = options.pages ? options.last_page + 1 : page_count;
838 for (int i = first_page; i < last_page; ++i) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800839 if (bIsLinearized) {
840 nRet = PDF_DATA_NOTAVAIL;
thestig514e8c92016-07-15 17:57:54 -0700841 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700842 nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
thestig514e8c92016-07-15 17:57:54 -0700843
Jun Fangdf7f3662015-11-10 18:29:18 +0800844 if (nRet == PDF_DATA_ERROR) {
845 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
846 i);
847 return;
848 }
849 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700850 if (RenderPage(name, doc.get(), form.get(), form_callbacks, i, options,
851 events))
Jun Fangdf7f3662015-11-10 18:29:18 +0800852 ++rendered_pages;
thestig514e8c92016-07-15 17:57:54 -0700853 else
Lei Zhang5377ebf2015-09-23 14:52:53 -0700854 ++bad_pages;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700855 }
856
Tom Sepez1d17a042017-03-16 13:22:47 -0700857 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
Tom Sepez1ed8a212015-05-11 15:25:39 -0700858 fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
tsepez10b01bf2016-05-04 12:52:42 -0700859 if (bad_pages)
860 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700861}
862
Tom Sepez2991d8d2016-01-15 16:02:48 -0800863static void ShowConfig() {
864 std::string config;
865 std::string maybe_comma;
866#if PDF_ENABLE_V8
867 config.append(maybe_comma);
868 config.append("V8");
869 maybe_comma = ",";
870#endif // PDF_ENABLE_V8
871#ifdef V8_USE_EXTERNAL_STARTUP_DATA
872 config.append(maybe_comma);
873 config.append("V8_EXTERNAL");
874 maybe_comma = ",";
875#endif // V8_USE_EXTERNAL_STARTUP_DATA
876#ifdef PDF_ENABLE_XFA
877 config.append(maybe_comma);
878 config.append("XFA");
879 maybe_comma = ",";
880#endif // PDF_ENABLE_XFA
dan sinclair00d40642017-01-30 19:48:54 -0800881#ifdef PDF_ENABLE_ASAN
882 config.append(maybe_comma);
883 config.append("ASAN");
884 maybe_comma = ",";
885#endif // PDF_ENABLE_ASAN
Tom Sepez2991d8d2016-01-15 16:02:48 -0800886 printf("%s\n", config.c_str());
887}
888
thestig514e8c92016-07-15 17:57:54 -0700889static const char kUsageString[] =
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800890 "Usage: pdfium_test [OPTION] [FILE]...\n"
Tom Sepez2991d8d2016-01-15 16:02:48 -0800891 " --show-config - print build options and exit\n"
tsepez10b01bf2016-05-04 12:52:42 -0700892 " --send-events - send input described by .evt file\n"
Lei Zhang6f62d532015-09-23 15:31:44 -0700893 " --bin-dir=<path> - override path to v8 external data\n"
894 " --font-dir=<path> - override path to external fonts\n"
895 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
npmfa20cd52016-11-14 13:33:40 -0800896 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800897#ifdef _WIN32
898 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
899 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
Tom Sepezc46d0002015-11-30 15:46:36 -0800900#endif // _WIN32
thestig514e8c92016-07-15 17:57:54 -0700901 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800902 " --png - write page images <pdf-name>.<page-number>.png\n"
thestig514e8c92016-07-15 17:57:54 -0700903 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
Cary Clark399be5b2016-03-14 16:51:29 -0400904#ifdef PDF_ENABLE_SKIA
905 " --skp - write page images <pdf-name>.<page-number>.skp\n"
906#endif
stephanafa05e972017-01-02 06:19:41 -0800907 " --md5 - write output image paths and their md5 hashes to stdout.\n"
Cary Clark399be5b2016-03-14 16:51:29 -0400908 "";
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800909
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700910int main(int argc, const char* argv[]) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800911 std::vector<std::string> args(argv, argv + argc);
912 Options options;
thestig514e8c92016-07-15 17:57:54 -0700913 std::vector<std::string> files;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800914 if (!ParseCommandLine(args, &options, &files)) {
thestig514e8c92016-07-15 17:57:54 -0700915 fprintf(stderr, "%s", kUsageString);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700916 return 1;
917 }
918
Tom Sepez2991d8d2016-01-15 16:02:48 -0800919 if (options.show_config) {
920 ShowConfig();
921 return 0;
922 }
923
924 if (files.empty()) {
925 fprintf(stderr, "No input files.\n");
926 return 1;
927 }
928
Tom Sepez452b4f32015-10-13 09:27:27 -0700929#ifdef PDF_ENABLE_V8
Tom Sepezd831dc72015-10-19 16:04:22 -0700930 v8::Platform* platform;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800931#ifdef V8_USE_EXTERNAL_STARTUP_DATA
932 v8::StartupData natives;
933 v8::StartupData snapshot;
Tom Sepezd831dc72015-10-19 16:04:22 -0700934 InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
935 &snapshot, &platform);
936#else // V8_USE_EXTERNAL_STARTUP_DATA
jochen9e077d22016-06-09 02:51:13 -0700937 InitializeV8ForPDFium(options.exe_path, &platform);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800938#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700939#endif // PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800940
Tom Sepeza72e8e22015-10-07 10:17:53 -0700941 FPDF_LIBRARY_CONFIG config;
942 config.version = 2;
943 config.m_pUserFontPaths = nullptr;
944 config.m_pIsolate = nullptr;
945 config.m_v8EmbedderSlot = 0;
946
947 const char* path_array[2];
948 if (!options.font_directory.empty()) {
Lei Zhang6f62d532015-09-23 15:31:44 -0700949 path_array[0] = options.font_directory.c_str();
950 path_array[1] = nullptr;
Lei Zhang6f62d532015-09-23 15:31:44 -0700951 config.m_pUserFontPaths = path_array;
Lei Zhang6f62d532015-09-23 15:31:44 -0700952 }
Tom Sepeza72e8e22015-10-07 10:17:53 -0700953 FPDF_InitLibraryWithConfig(&config);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700954
npmfa20cd52016-11-14 13:33:40 -0800955 UNSUPPORT_INFO unsupported_info;
956 memset(&unsupported_info, '\0', sizeof(unsupported_info));
957 unsupported_info.version = 1;
958 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700959
npmfa20cd52016-11-14 13:33:40 -0800960 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700961
thestig514e8c92016-07-15 17:57:54 -0700962 for (const std::string& filename : files) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800963 size_t file_length = 0;
Tom Sepez0aa35312016-01-06 10:16:32 -0800964 std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
965 GetFileContents(filename.c_str(), &file_length);
tsepezf09bdfa2016-04-18 16:08:26 -0700966 if (!file_contents)
967 continue;
tsepez10b01bf2016-05-04 12:52:42 -0700968 fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
tsepezf09bdfa2016-04-18 16:08:26 -0700969 std::string events;
970 if (options.send_events) {
971 std::string event_filename = filename;
972 size_t event_length = 0;
973 size_t extension_pos = event_filename.find(".pdf");
974 if (extension_pos != std::string::npos) {
975 event_filename.replace(extension_pos, 4, ".evt");
thestigf2b940c2016-10-13 06:48:47 -0700976 if (access(event_filename.c_str(), R_OK) == 0) {
977 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
978 std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
979 GetFileContents(event_filename.c_str(), &event_length);
980 if (event_contents) {
981 fprintf(stderr, "Sending events from: %s\n",
982 event_filename.c_str());
983 events = std::string(event_contents.get(), event_length);
984 }
tsepezf09bdfa2016-04-18 16:08:26 -0700985 }
986 }
987 }
988 RenderPdf(filename, file_contents.get(), file_length, options, events);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700989 }
990
991 FPDF_DestroyLibrary();
Tom Sepez452b4f32015-10-13 09:27:27 -0700992#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -0800993 v8::V8::ShutdownPlatform();
994 delete platform;
thestigc08cd7a2016-06-27 09:47:59 -0700995
996#ifdef V8_USE_EXTERNAL_STARTUP_DATA
997 free(const_cast<char*>(natives.data));
998 free(const_cast<char*>(snapshot.data));
999#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001000#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001001
1002 return 0;
1003}