blob: 054d3f01d1ac8a5da7e99db5e4b40c9f8a8a96f9 [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"
Dan Sinclairddcb6e72017-04-05 10:30:33 -040026#include "public/fpdf_structtree.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080027#include "public/fpdf_text.h"
28#include "public/fpdfview.h"
Dan Sinclairefbc1912016-02-17 16:54:43 -050029#include "samples/image_diff_png.h"
Lei Zhangbde53d22015-11-12 22:21:30 -080030#include "testing/test_support.h"
Tom Sepezd831dc72015-10-19 16:04:22 -070031
thestigf2b940c2016-10-13 06:48:47 -070032#ifdef _WIN32
33#include <io.h>
34#else
35#include <unistd.h>
36#endif
37
Tom Sepez452b4f32015-10-13 09:27:27 -070038#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -080039#include "v8/include/libplatform/libplatform.h"
Tom Sepez1ed8a212015-05-11 15:25:39 -070040#include "v8/include/v8.h"
Lei Zhang8241df72015-11-06 14:38:48 -080041#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070042
Cary Clark399be5b2016-03-14 16:51:29 -040043#ifdef PDF_ENABLE_SKIA
44#include "third_party/skia/include/core/SkPictureRecorder.h"
45#include "third_party/skia/include/core/SkStream.h"
46#endif
47
thestigf2b940c2016-10-13 06:48:47 -070048#ifdef _WIN32
49#define access _access
50#define snprintf _snprintf
51#define R_OK 4
52#endif
53
Vitaly Buka9e0177a2014-07-22 18:15:42 -070054enum OutputFormat {
55 OUTPUT_NONE,
dsinclairb63068f2016-06-16 07:58:09 -070056 OUTPUT_TEXT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070057 OUTPUT_PPM,
Tom Sepezaf18cb32015-02-05 15:06:01 -080058 OUTPUT_PNG,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070059#ifdef _WIN32
60 OUTPUT_BMP,
61 OUTPUT_EMF,
62#endif
Cary Clark399be5b2016-03-14 16:51:29 -040063#ifdef PDF_ENABLE_SKIA
64 OUTPUT_SKP,
65#endif
Vitaly Buka9e0177a2014-07-22 18:15:42 -070066};
67
Tom Sepez5ee12d72014-12-17 16:24:01 -080068struct Options {
tsepezf09bdfa2016-04-18 16:08:26 -070069 Options()
Dan Sinclairddcb6e72017-04-05 10:30:33 -040070 : show_structure(false),
71 show_config(false),
npme3c73152016-11-14 09:14:52 -080072 send_events(false),
73 pages(false),
stephanafa05e972017-01-02 06:19:41 -080074 md5(false),
npme3c73152016-11-14 09:14:52 -080075 output_format(OUTPUT_NONE) {}
Tom Sepez5ee12d72014-12-17 16:24:01 -080076
Dan Sinclairddcb6e72017-04-05 10:30:33 -040077 bool show_structure;
Tom Sepez2991d8d2016-01-15 16:02:48 -080078 bool show_config;
tsepezf09bdfa2016-04-18 16:08:26 -070079 bool send_events;
npme3c73152016-11-14 09:14:52 -080080 bool pages;
stephanafa05e972017-01-02 06:19:41 -080081 bool md5;
Tom Sepez5ee12d72014-12-17 16:24:01 -080082 OutputFormat output_format;
Tom Sepezdaa2e842015-01-29 15:44:37 -080083 std::string scale_factor_as_string;
Tom Sepez5ee12d72014-12-17 16:24:01 -080084 std::string exe_path;
85 std::string bin_directory;
Lei Zhang6f62d532015-09-23 15:31:44 -070086 std::string font_directory;
npmfa20cd52016-11-14 13:33:40 -080087 // 0-based page numbers to be rendered.
88 int first_page;
89 int last_page;
Tom Sepez5ee12d72014-12-17 16:24:01 -080090};
91
tonikitoo81d92f82016-09-21 12:44:56 -070092struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
93 // Hold a map of the currently loaded pages in order to avoid them
94 // to get loaded twice.
npmfa20cd52016-11-14 13:33:40 -080095 std::map<int, FPDF_PAGE> loaded_pages;
tonikitoo81d92f82016-09-21 12:44:56 -070096
97 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
98 // make use of it.
npmfa20cd52016-11-14 13:33:40 -080099 FPDF_FORMHANDLE form_handle;
tonikitoo81d92f82016-09-21 12:44:56 -0700100};
101
102static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
npmfa20cd52016-11-14 13:33:40 -0800103 FPDF_FORMFILLINFO* form_fill_info) {
104 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
tonikitoo81d92f82016-09-21 12:44:56 -0700105}
106
Tom Sepezaf18cb32015-02-05 15:06:01 -0800107static bool CheckDimensions(int stride, int width, int height) {
108 if (stride < 0 || width < 0 || height < 0)
109 return false;
110 if (height > 0 && width > INT_MAX / height)
111 return false;
112 return true;
113}
114
stephanafa05e972017-01-02 06:19:41 -0800115static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
116 // Get the MD5 hash and write it to stdout.
117 uint8_t digest[16];
118 CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(buffer), len, digest);
119 printf("MD5:%s:", file_name);
120 for (int i = 0; i < 16; i++)
121 printf("%02x", digest[i]);
122 printf("\n");
123}
124
125static std::string WritePpm(const char* pdf_name,
126 int num,
127 const void* buffer_void,
128 int stride,
129 int width,
130 int height) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700131 const char* buffer = reinterpret_cast<const char*>(buffer_void);
132
Tom Sepezaf18cb32015-02-05 15:06:01 -0800133 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800134 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800135
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700136 int out_len = width * height;
137 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800138 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700139 out_len *= 3;
140
141 char filename[256];
142 snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
John Abd-El-Maleka548d302014-06-26 10:18:11 -0700143 FILE* fp = fopen(filename, "wb");
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700144 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800145 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700146 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
147 // Source data is B, G, R, unused.
148 // Dest data is R, G, B.
thestig514e8c92016-07-15 17:57:54 -0700149 std::vector<char> result(out_len);
Lei Zhange00660b2015-08-13 15:40:18 -0700150 for (int h = 0; h < height; ++h) {
151 const char* src_line = buffer + (stride * h);
thestig514e8c92016-07-15 17:57:54 -0700152 char* dest_line = result.data() + (width * h * 3);
Lei Zhange00660b2015-08-13 15:40:18 -0700153 for (int w = 0; w < width; ++w) {
154 // R
155 dest_line[w * 3] = src_line[(w * 4) + 2];
156 // G
157 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
158 // B
159 dest_line[(w * 3) + 2] = src_line[w * 4];
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700160 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700161 }
thestig514e8c92016-07-15 17:57:54 -0700162 fwrite(result.data(), out_len, 1, fp);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700163 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800164 return std::string(filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700165}
166
dsinclairb63068f2016-06-16 07:58:09 -0700167void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
168 char filename[256];
169 int chars_formatted =
170 snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
171 if (chars_formatted < 0 ||
172 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
173 fprintf(stderr, "Filename %s is too long\n", filename);
174 return;
175 }
176
177 FILE* fp = fopen(filename, "w");
178 if (!fp) {
179 fprintf(stderr, "Failed to open %s for output\n", filename);
180 return;
181 }
182
183 // Output in UTF32-LE.
184 uint32_t bom = 0x0000FEFF;
185 fwrite(&bom, sizeof(bom), 1, fp);
186
Tom Sepez1d17a042017-03-16 13:22:47 -0700187 std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
188 for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
189 uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
dsinclairb63068f2016-06-16 07:58:09 -0700190 fwrite(&c, sizeof(c), 1, fp);
191 }
dsinclairb63068f2016-06-16 07:58:09 -0700192 (void)fclose(fp);
193}
194
stephanafa05e972017-01-02 06:19:41 -0800195static std::string WritePng(const char* pdf_name,
196 int num,
197 const void* buffer_void,
198 int stride,
199 int width,
200 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800201 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800202 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800203
204 std::vector<unsigned char> png_encoding;
205 const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
206 if (!image_diff_png::EncodeBGRAPNG(
207 buffer, width, height, stride, false, &png_encoding)) {
208 fprintf(stderr, "Failed to convert bitmap to PNG\n");
stephanafa05e972017-01-02 06:19:41 -0800209 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800210 }
211
212 char filename[256];
213 int chars_formatted = snprintf(
214 filename, sizeof(filename), "%s.%d.png", pdf_name, num);
215 if (chars_formatted < 0 ||
216 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
Cary Clark399be5b2016-03-14 16:51:29 -0400217 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800218 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800219 }
220
221 FILE* fp = fopen(filename, "wb");
222 if (!fp) {
223 fprintf(stderr, "Failed to open %s for output\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800224 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800225 }
226
227 size_t bytes_written = fwrite(
228 &png_encoding.front(), 1, png_encoding.size(), fp);
229 if (bytes_written != png_encoding.size())
230 fprintf(stderr, "Failed to write to %s\n", filename);
231
Lei Zhang5377ebf2015-09-23 14:52:53 -0700232 (void)fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800233 return std::string(filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800234}
235
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700236#ifdef _WIN32
stephanafa05e972017-01-02 06:19:41 -0800237static std::string WriteBmp(const char* pdf_name,
238 int num,
239 const void* buffer,
240 int stride,
241 int width,
242 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800243 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800244 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800245
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700246 int out_len = stride * height;
247 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800248 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700249
250 char filename[256];
251 snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
252 FILE* fp = fopen(filename, "wb");
253 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800254 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700255
Nico Weber2827bdd2015-07-01 14:08:08 -0700256 BITMAPINFO bmi = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700257 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
258 bmi.bmiHeader.biWidth = width;
259 bmi.bmiHeader.biHeight = -height; // top-down image
260 bmi.bmiHeader.biPlanes = 1;
261 bmi.bmiHeader.biBitCount = 32;
262 bmi.bmiHeader.biCompression = BI_RGB;
263 bmi.bmiHeader.biSizeImage = 0;
264
Nico Weber2827bdd2015-07-01 14:08:08 -0700265 BITMAPFILEHEADER file_header = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700266 file_header.bfType = 0x4d42;
267 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
268 file_header.bfOffBits = file_header.bfSize - out_len;
269
270 fwrite(&file_header, sizeof(file_header), 1, fp);
271 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
272 fwrite(buffer, out_len, 1, fp);
273 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800274 return std::string(filename);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700275}
276
277void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
278 int width = static_cast<int>(FPDF_GetPageWidth(page));
279 int height = static_cast<int>(FPDF_GetPageHeight(page));
280
281 char filename[256];
282 snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
283
Lei Zhang5377ebf2015-09-23 14:52:53 -0700284 HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800285
286 HRGN rgn = CreateRectRgn(0, 0, width, height);
287 SelectClipRgn(dc, rgn);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700288 DeleteObject(rgn);
289
290 SelectObject(dc, GetStockObject(NULL_PEN));
291 SelectObject(dc, GetStockObject(WHITE_BRUSH));
292 // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
293 Rectangle(dc, 0, 0, width + 1, height + 1);
294
295 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
296 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
297
298 DeleteEnhMetaFile(CloseEnhMetaFile(dc));
299}
300#endif
301
Cary Clark399be5b2016-03-14 16:51:29 -0400302#ifdef PDF_ENABLE_SKIA
stephanafa05e972017-01-02 06:19:41 -0800303static std::string WriteSkp(const char* pdf_name,
304 int num,
305 SkPictureRecorder* recorder) {
Cary Clark399be5b2016-03-14 16:51:29 -0400306 char filename[256];
307 int chars_formatted =
308 snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
309
310 if (chars_formatted < 0 ||
311 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
312 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800313 return "";
Cary Clark399be5b2016-03-14 16:51:29 -0400314 }
315
thestigb97e07e2016-09-28 22:16:40 -0700316 sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
Cary Clark399be5b2016-03-14 16:51:29 -0400317 SkFILEWStream wStream(filename);
318 picture->serialize(&wStream);
stephanafa05e972017-01-02 06:19:41 -0800319 return std::string(filename);
Cary Clark399be5b2016-03-14 16:51:29 -0400320}
321#endif
322
Tom Sepez58fb36a2016-02-01 10:32:14 -0800323// These example JS platform callback handlers are entirely optional,
324// and exist here to show the flow of information from a document back
325// to the embedder.
Tom Sepezbd932572016-01-29 09:10:41 -0800326int ExampleAppAlert(IPDF_JSPLATFORM*,
327 FPDF_WIDESTRING msg,
328 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800329 int type,
330 int icon) {
Tom Sepezbd932572016-01-29 09:10:41 -0800331 printf("%ls", GetPlatformWString(title).c_str());
npmfa20cd52016-11-14 13:33:40 -0800332 if (icon || type)
333 printf("[icon=%d,type=%d]", icon, type);
Tom Sepezbd932572016-01-29 09:10:41 -0800334 printf(": %ls\n", GetPlatformWString(msg).c_str());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700335 return 0;
336}
337
Tom Sepez58fb36a2016-02-01 10:32:14 -0800338int ExampleAppResponse(IPDF_JSPLATFORM*,
339 FPDF_WIDESTRING question,
340 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800341 FPDF_WIDESTRING default_value,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800342 FPDF_WIDESTRING label,
npmfa20cd52016-11-14 13:33:40 -0800343 FPDF_BOOL is_password,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800344 void* response,
345 int length) {
346 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
347 GetPlatformWString(title).c_str(),
348 GetPlatformWString(question).c_str(),
npmfa20cd52016-11-14 13:33:40 -0800349 GetPlatformWString(default_value).c_str(),
350 GetPlatformWString(label).c_str(), is_password, length);
Tom Sepez58fb36a2016-02-01 10:32:14 -0800351
352 // UTF-16, always LE regardless of platform.
353 uint8_t* ptr = static_cast<uint8_t*>(response);
354 ptr[0] = 'N';
355 ptr[1] = 0;
356 ptr[2] = 'o';
357 ptr[3] = 0;
358 return 4;
359}
360
npmfa20cd52016-11-14 13:33:40 -0800361void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
362 printf("Goto Page: %d\n", page_number);
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800363}
364
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800365void ExampleDocMail(IPDF_JSPLATFORM*,
366 void* mailData,
367 int length,
npmfa20cd52016-11-14 13:33:40 -0800368 FPDF_BOOL UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800369 FPDF_WIDESTRING To,
370 FPDF_WIDESTRING Subject,
371 FPDF_WIDESTRING CC,
372 FPDF_WIDESTRING BCC,
373 FPDF_WIDESTRING Msg) {
npmfa20cd52016-11-14 13:33:40 -0800374 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800375 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
376 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
377 GetPlatformWString(Msg).c_str());
378}
379
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800380void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700381 std::string feature = "Unknown";
382 switch (type) {
383 case FPDF_UNSP_DOC_XFAFORM:
384 feature = "XFA";
385 break;
386 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
387 feature = "Portfolios_Packages";
388 break;
389 case FPDF_UNSP_DOC_ATTACHMENT:
390 case FPDF_UNSP_ANNOT_ATTACHMENT:
391 feature = "Attachment";
392 break;
393 case FPDF_UNSP_DOC_SECURITY:
394 feature = "Rights_Management";
395 break;
396 case FPDF_UNSP_DOC_SHAREDREVIEW:
397 feature = "Shared_Review";
398 break;
399 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
400 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
401 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
402 feature = "Shared_Form";
403 break;
404 case FPDF_UNSP_ANNOT_3DANNOT:
405 feature = "3D";
406 break;
407 case FPDF_UNSP_ANNOT_MOVIE:
408 feature = "Movie";
409 break;
410 case FPDF_UNSP_ANNOT_SOUND:
411 feature = "Sound";
412 break;
413 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
414 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
415 feature = "Screen";
416 break;
417 case FPDF_UNSP_ANNOT_SIG:
418 feature = "Digital_Signature";
419 break;
420 }
421 printf("Unsupported feature: %s.\n", feature.c_str());
422}
423
Tom Sepez5ee12d72014-12-17 16:24:01 -0800424bool ParseCommandLine(const std::vector<std::string>& args,
thestig514e8c92016-07-15 17:57:54 -0700425 Options* options,
426 std::vector<std::string>* files) {
427 if (args.empty())
Tom Sepez5ee12d72014-12-17 16:24:01 -0800428 return false;
thestig514e8c92016-07-15 17:57:54 -0700429
Tom Sepez5ee12d72014-12-17 16:24:01 -0800430 options->exe_path = args[0];
Bo Xud44e3922014-12-19 02:27:25 -0800431 size_t cur_idx = 1;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800432 for (; cur_idx < args.size(); ++cur_idx) {
433 const std::string& cur_arg = args[cur_idx];
Tom Sepez2991d8d2016-01-15 16:02:48 -0800434 if (cur_arg == "--show-config") {
435 options->show_config = true;
tsepezf09bdfa2016-04-18 16:08:26 -0700436 } else if (cur_arg == "--send-events") {
437 options->send_events = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800438 } else if (cur_arg == "--ppm") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800439 if (options->output_format != OUTPUT_NONE) {
440 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
441 return false;
442 }
443 options->output_format = OUTPUT_PPM;
Tom Sepezaf18cb32015-02-05 15:06:01 -0800444 } else if (cur_arg == "--png") {
445 if (options->output_format != OUTPUT_NONE) {
446 fprintf(stderr, "Duplicate or conflicting --png argument\n");
447 return false;
448 }
449 options->output_format = OUTPUT_PNG;
dsinclairb63068f2016-06-16 07:58:09 -0700450 } else if (cur_arg == "--txt") {
451 if (options->output_format != OUTPUT_NONE) {
452 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
453 return false;
454 }
455 options->output_format = OUTPUT_TEXT;
Cary Clark399be5b2016-03-14 16:51:29 -0400456#ifdef PDF_ENABLE_SKIA
457 } else if (cur_arg == "--skp") {
458 if (options->output_format != OUTPUT_NONE) {
459 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
460 return false;
461 }
462 options->output_format = OUTPUT_SKP;
463#endif
Lei Zhang6f62d532015-09-23 15:31:44 -0700464 } else if (cur_arg.size() > 11 &&
465 cur_arg.compare(0, 11, "--font-dir=") == 0) {
466 if (!options->font_directory.empty()) {
467 fprintf(stderr, "Duplicate --font-dir argument\n");
468 return false;
469 }
470 options->font_directory = cur_arg.substr(11);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700471#ifdef _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500472 } else if (cur_arg == "--emf") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800473 if (options->output_format != OUTPUT_NONE) {
474 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
475 return false;
476 }
477 options->output_format = OUTPUT_EMF;
Dan Sinclair50cce602016-02-24 09:51:16 -0500478 } else if (cur_arg == "--bmp") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800479 if (options->output_format != OUTPUT_NONE) {
480 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
481 return false;
482 }
483 options->output_format = OUTPUT_BMP;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800484#endif // _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500485
Tom Sepez452b4f32015-10-13 09:27:27 -0700486#ifdef PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800487#ifdef V8_USE_EXTERNAL_STARTUP_DATA
Dan Sinclair738b08c2016-03-01 14:45:20 -0500488 } else if (cur_arg.size() > 10 &&
489 cur_arg.compare(0, 10, "--bin-dir=") == 0) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800490 if (!options->bin_directory.empty()) {
491 fprintf(stderr, "Duplicate --bin-dir argument\n");
492 return false;
493 }
494 options->bin_directory = cur_arg.substr(10);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800495#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700496#endif // PDF_ENABLE_V8
Dan Sinclair738b08c2016-03-01 14:45:20 -0500497
498 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
Tom Sepezdaa2e842015-01-29 15:44:37 -0800499 if (!options->scale_factor_as_string.empty()) {
500 fprintf(stderr, "Duplicate --scale argument\n");
501 return false;
502 }
503 options->scale_factor_as_string = cur_arg.substr(8);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400504 } else if (cur_arg == "--show-structure") {
505 options->show_structure = true;
npme3c73152016-11-14 09:14:52 -0800506 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
507 if (options->pages) {
508 fprintf(stderr, "Duplicate --pages argument\n");
509 return false;
510 }
511 options->pages = true;
npmfa20cd52016-11-14 13:33:40 -0800512 const std::string pages_string = cur_arg.substr(8);
513 size_t first_dash = pages_string.find("-");
514 if (first_dash == std::string::npos) {
515 std::stringstream(pages_string) >> options->first_page;
516 options->last_page = options->first_page;
npme3c73152016-11-14 09:14:52 -0800517 } else {
npmfa20cd52016-11-14 13:33:40 -0800518 std::stringstream(pages_string.substr(0, first_dash)) >>
519 options->first_page;
520 std::stringstream(pages_string.substr(first_dash + 1)) >>
521 options->last_page;
npme3c73152016-11-14 09:14:52 -0800522 }
stephanafa05e972017-01-02 06:19:41 -0800523 } else if (cur_arg == "--md5") {
524 options->md5 = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800525 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
526 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
527 return false;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500528 } else {
Vitaly Buka8f2c3dc2014-08-20 10:32:36 -0700529 break;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500530 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700531 }
thestig514e8c92016-07-15 17:57:54 -0700532 for (size_t i = cur_idx; i < args.size(); i++)
Tom Sepez5ee12d72014-12-17 16:24:01 -0800533 files->push_back(args[i]);
thestig514e8c92016-07-15 17:57:54 -0700534
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700535 return true;
536}
537
npmfa20cd52016-11-14 13:33:40 -0800538FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700539 return true;
540}
541
npmfa20cd52016-11-14 13:33:40 -0800542void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700543
tsepezf09bdfa2016-04-18 16:08:26 -0700544void SendPageEvents(const FPDF_FORMHANDLE& form,
545 const FPDF_PAGE& page,
546 const std::string& events) {
547 auto lines = StringSplit(events, '\n');
548 for (auto line : lines) {
549 auto command = StringSplit(line, '#');
550 if (command[0].empty())
551 continue;
552 auto tokens = StringSplit(command[0], ',');
553 if (tokens[0] == "keycode") {
554 if (tokens.size() == 2) {
555 int keycode = atoi(tokens[1].c_str());
556 FORM_OnKeyDown(form, page, keycode, 0);
557 FORM_OnKeyUp(form, page, keycode, 0);
558 } else {
559 fprintf(stderr, "keycode: bad args\n");
560 }
561 } else if (tokens[0] == "mousedown") {
562 if (tokens.size() == 4) {
563 int x = atoi(tokens[2].c_str());
564 int y = atoi(tokens[3].c_str());
565 if (tokens[1] == "left")
566 FORM_OnLButtonDown(form, page, 0, x, y);
567#ifdef PDF_ENABLE_XFA
568 else if (tokens[1] == "right")
569 FORM_OnRButtonDown(form, page, 0, x, y);
570#endif
571 else
572 fprintf(stderr, "mousedown: bad button name\n");
573 } else {
574 fprintf(stderr, "mousedown: bad args\n");
575 }
576 } else if (tokens[0] == "mouseup") {
577 if (tokens.size() == 4) {
578 int x = atoi(tokens[2].c_str());
579 int y = atoi(tokens[3].c_str());
580 if (tokens[1] == "left")
581 FORM_OnLButtonUp(form, page, 0, x, y);
582#ifdef PDF_ENABLE_XFA
583 else if (tokens[1] == "right")
584 FORM_OnRButtonUp(form, page, 0, x, y);
585#endif
586 else
587 fprintf(stderr, "mouseup: bad button name\n");
588 } else {
589 fprintf(stderr, "mouseup: bad args\n");
590 }
591 } else if (tokens[0] == "mousemove") {
592 if (tokens.size() == 3) {
593 int x = atoi(tokens[1].c_str());
594 int y = atoi(tokens[2].c_str());
595 FORM_OnMouseMove(form, page, 0, x, y);
596 } else {
597 fprintf(stderr, "mousemove: bad args\n");
598 }
599 } else {
600 fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
601 }
602 }
603}
604
tonikitoo3e981582016-08-26 08:37:10 -0700605FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
606 FPDF_DOCUMENT doc,
607 int index) {
npmfa20cd52016-11-14 13:33:40 -0800608 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
609 ToPDFiumTestFormFillInfo(param);
610 auto& loaded_pages = form_fill_info->loaded_pages;
npmfa20cd52016-11-14 13:33:40 -0800611 auto iter = loaded_pages.find(index);
612 if (iter != loaded_pages.end())
tonikitoo3e981582016-08-26 08:37:10 -0700613 return iter->second;
614
615 FPDF_PAGE page = FPDF_LoadPage(doc, index);
616 if (!page)
617 return nullptr;
618
npmfa20cd52016-11-14 13:33:40 -0800619 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
npmfa20cd52016-11-14 13:33:40 -0800620 FORM_OnAfterLoadPage(page, form_handle);
621 FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
npmfa20cd52016-11-14 13:33:40 -0800622 loaded_pages[index] = page;
tonikitoo3e981582016-08-26 08:37:10 -0700623 return page;
624}
625
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400626static const size_t kBufSize = 1024;
627std::wstring ConvertToWString(const unsigned short* buf,
628 unsigned long buf_size) {
629 wchar_t new_buf[kBufSize];
630 for (unsigned long i = 0; i < buf_size; ++i)
631 new_buf[i] = buf[i];
632 return std::wstring(new_buf);
633}
634
635void DumpChildStructure(FPDF_STRUCTELEMENT child, const std::string& indent) {
636 unsigned short buf[kBufSize];
637 unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
638 printf("%s%ls", indent.c_str(), ConvertToWString(buf, len).c_str());
639
640 memset(buf, 0, sizeof(buf));
641 len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
642 if (len > 0)
643 printf(" (%ls)", ConvertToWString(buf, len).c_str());
644 printf("\n");
645
646 for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
647 FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
648 // If the child is not an Element then this will return null. This can
649 // happen if the element is things like an object reference or a stream.
650 if (!sub_child)
651 continue;
652
653 DumpChildStructure(sub_child, indent + " ");
654 }
655}
656
657void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
658 std::unique_ptr<void, FPDFStructTreeDeleter> tree(
659 FPDF_StructTree_GetForPage(page));
660 if (!tree) {
661 printf("Failed to load struct tree for page %d\n", page_idx);
662 return;
663 }
664
665 printf("Structure Tree for Page %d\n", page_idx);
666 for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
667 FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
668 if (!child) {
669 printf("Failed to load child %d for page %d\n", i, page_idx);
670 continue;
671 }
672 DumpChildStructure(child, "");
673 }
674 printf("\n\n");
675}
676
Jun Fangb553bcb2015-11-10 18:49:04 +0800677bool RenderPage(const std::string& name,
tonikitoo3e981582016-08-26 08:37:10 -0700678 FPDF_DOCUMENT doc,
Tom Sepez1d17a042017-03-16 13:22:47 -0700679 FPDF_FORMHANDLE form,
npmfa20cd52016-11-14 13:33:40 -0800680 FPDF_FORMFILLINFO_PDFiumTest& form_fill_info,
Jun Fangb553bcb2015-11-10 18:49:04 +0800681 const int page_index,
tsepezf09bdfa2016-04-18 16:08:26 -0700682 const Options& options,
683 const std::string& events) {
Tom Sepez1d17a042017-03-16 13:22:47 -0700684 std::unique_ptr<void, FPDFPageDeleter> page(
685 GetPageForIndex(&form_fill_info, doc, page_index));
686 if (!page.get())
Jun Fangb553bcb2015-11-10 18:49:04 +0800687 return false;
thestig514e8c92016-07-15 17:57:54 -0700688
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400689 if (options.show_structure) {
690 DumpPageStructure(page.get(), page_index);
691 return true;
692 }
693
Tom Sepez1d17a042017-03-16 13:22:47 -0700694 std::unique_ptr<void, FPDFTextPageDeleter> text_page(
695 FPDFText_LoadPage(page.get()));
tsepezf09bdfa2016-04-18 16:08:26 -0700696 if (options.send_events)
Tom Sepez1d17a042017-03-16 13:22:47 -0700697 SendPageEvents(form, page.get(), events);
tsepezf09bdfa2016-04-18 16:08:26 -0700698
Jun Fangdf7f3662015-11-10 18:29:18 +0800699 double scale = 1.0;
thestig514e8c92016-07-15 17:57:54 -0700700 if (!options.scale_factor_as_string.empty())
Jun Fangdf7f3662015-11-10 18:29:18 +0800701 std::stringstream(options.scale_factor_as_string) >> scale;
thestig514e8c92016-07-15 17:57:54 -0700702
Tom Sepez1d17a042017-03-16 13:22:47 -0700703 int width = static_cast<int>(FPDF_GetPageWidth(page.get()) * scale);
704 int height = static_cast<int>(FPDF_GetPageHeight(page.get()) * scale);
705 int alpha = FPDFPage_HasTransparency(page.get()) ? 1 : 0;
706 std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
707 FPDFBitmap_Create(width, height, alpha));
708
thestige97ea032016-05-19 10:59:15 -0700709 if (bitmap) {
710 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700711 FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
712 FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
713 FPDF_ANNOT);
Jun Fangdf7f3662015-11-10 18:29:18 +0800714
Tom Sepez1d17a042017-03-16 13:22:47 -0700715 FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
716 FPDF_ANNOT);
717 int stride = FPDFBitmap_GetStride(bitmap.get());
thestige97ea032016-05-19 10:59:15 -0700718 const char* buffer =
Tom Sepez1d17a042017-03-16 13:22:47 -0700719 reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
Jun Fangdf7f3662015-11-10 18:29:18 +0800720
stephanafa05e972017-01-02 06:19:41 -0800721 std::string&& image_file_name = "";
thestige97ea032016-05-19 10:59:15 -0700722 switch (options.output_format) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800723#ifdef _WIN32
thestige97ea032016-05-19 10:59:15 -0700724 case OUTPUT_BMP:
stephanafa05e972017-01-02 06:19:41 -0800725 image_file_name =
726 WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700727 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800728
thestige97ea032016-05-19 10:59:15 -0700729 case OUTPUT_EMF:
Tom Sepez1d17a042017-03-16 13:22:47 -0700730 WriteEmf(page.get(), name.c_str(), page_index);
thestige97ea032016-05-19 10:59:15 -0700731 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800732#endif
dsinclairb63068f2016-06-16 07:58:09 -0700733 case OUTPUT_TEXT:
Tom Sepez1d17a042017-03-16 13:22:47 -0700734 WriteText(page.get(), name.c_str(), page_index);
dsinclairb63068f2016-06-16 07:58:09 -0700735 break;
736
thestige97ea032016-05-19 10:59:15 -0700737 case OUTPUT_PNG:
stephanafa05e972017-01-02 06:19:41 -0800738 image_file_name =
739 WritePng(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700740 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800741
thestige97ea032016-05-19 10:59:15 -0700742 case OUTPUT_PPM:
stephanafa05e972017-01-02 06:19:41 -0800743 image_file_name =
744 WritePpm(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700745 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800746
Cary Clark399be5b2016-03-14 16:51:29 -0400747#ifdef PDF_ENABLE_SKIA
thestige97ea032016-05-19 10:59:15 -0700748 case OUTPUT_SKP: {
749 std::unique_ptr<SkPictureRecorder> recorder(
thestigb97e07e2016-09-28 22:16:40 -0700750 reinterpret_cast<SkPictureRecorder*>(
Tom Sepez1d17a042017-03-16 13:22:47 -0700751 FPDF_RenderPageSkp(page.get(), width, height)));
752 FPDF_FFLRecord(form, recorder.get(), page.get(), 0, 0, width, height, 0,
753 0);
stephanafa05e972017-01-02 06:19:41 -0800754 image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
thestige97ea032016-05-19 10:59:15 -0700755 } break;
Cary Clark399be5b2016-03-14 16:51:29 -0400756#endif
thestige97ea032016-05-19 10:59:15 -0700757 default:
758 break;
759 }
Jun Fangdf7f3662015-11-10 18:29:18 +0800760
stephanafa05e972017-01-02 06:19:41 -0800761 // Write the filename and the MD5 of the buffer to stdout if we wrote a
762 // file.
763 if (options.md5 && image_file_name != "")
764 OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
thestige97ea032016-05-19 10:59:15 -0700765 } else {
766 fprintf(stderr, "Page was too large to be rendered.\n");
767 }
tonikitoo3e981582016-08-26 08:37:10 -0700768
npmfa20cd52016-11-14 13:33:40 -0800769 form_fill_info.loaded_pages.erase(page_index);
Tom Sepez1d17a042017-03-16 13:22:47 -0700770 FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
771 FORM_OnBeforeClosePage(page.get(), form);
thestige97ea032016-05-19 10:59:15 -0700772 return !!bitmap;
Jun Fangdf7f3662015-11-10 18:29:18 +0800773}
774
tsepezf09bdfa2016-04-18 16:08:26 -0700775void RenderPdf(const std::string& name,
776 const char* pBuf,
777 size_t len,
778 const Options& options,
779 const std::string& events) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700780 IPDF_JSPLATFORM platform_callbacks;
781 memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
Tom Sepeza72e8e22015-10-07 10:17:53 -0700782 platform_callbacks.version = 3;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800783 platform_callbacks.app_alert = ExampleAppAlert;
Tom Sepez58fb36a2016-02-01 10:32:14 -0800784 platform_callbacks.app_response = ExampleAppResponse;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800785 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800786 platform_callbacks.Doc_mail = ExampleDocMail;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700787
tonikitoo81d92f82016-09-21 12:44:56 -0700788 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
Tom Sepezc46d0002015-11-30 15:46:36 -0800789#ifdef PDF_ENABLE_XFA
Tom Sepezed631382014-11-18 14:10:25 -0800790 form_callbacks.version = 2;
Tom Sepezc46d0002015-11-30 15:46:36 -0800791#else // PDF_ENABLE_XFA
792 form_callbacks.version = 1;
793#endif // PDF_ENABLE_XFA
tonikitoo3e981582016-08-26 08:37:10 -0700794 form_callbacks.FFI_GetPage = GetPageForIndex;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700795 form_callbacks.m_pJsPlatform = &platform_callbacks;
796
797 TestLoader loader(pBuf, len);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700798 FPDF_FILEACCESS file_access;
799 memset(&file_access, '\0', sizeof(file_access));
John Abd-El-Malek7dc51722014-05-26 12:54:31 -0700800 file_access.m_FileLen = static_cast<unsigned long>(len);
Tom Sepezd831dc72015-10-19 16:04:22 -0700801 file_access.m_GetBlock = TestLoader::GetBlock;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700802 file_access.m_Param = &loader;
803
804 FX_FILEAVAIL file_avail;
805 memset(&file_avail, '\0', sizeof(file_avail));
806 file_avail.version = 1;
807 file_avail.IsDataAvail = Is_Data_Avail;
808
809 FX_DOWNLOADHINTS hints;
810 memset(&hints, '\0', sizeof(hints));
811 hints.version = 1;
812 hints.AddSegment = Add_Segment;
813
Jun Fangdf7f3662015-11-10 18:29:18 +0800814 int nRet = PDF_DATA_NOTAVAIL;
Jun Fangb553bcb2015-11-10 18:49:04 +0800815 bool bIsLinearized = false;
Tom Sepez1d17a042017-03-16 13:22:47 -0700816 std::unique_ptr<void, FPDFDocumentDeleter> doc;
817 std::unique_ptr<void, FPDFAvailDeleter> pdf_avail(
818 FPDFAvail_Create(&file_avail, &file_access));
Tom Sepezc98895c2015-11-24 15:30:36 -0800819
Tom Sepez1d17a042017-03-16 13:22:47 -0700820 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
821 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800822 if (doc) {
thestig514e8c92016-07-15 17:57:54 -0700823 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700824 nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
thestig514e8c92016-07-15 17:57:54 -0700825
Jun Fangdf7f3662015-11-10 18:29:18 +0800826 if (nRet == PDF_DATA_ERROR) {
827 fprintf(stderr, "Unknown error in checking if doc was available.\n");
828 return;
829 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700830 nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
Jun Fangdf7f3662015-11-10 18:29:18 +0800831 if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
832 fprintf(stderr,
833 "Error %d was returned in checking if form was available.\n",
834 nRet);
835 return;
836 }
Jun Fangb553bcb2015-11-10 18:49:04 +0800837 bIsLinearized = true;
Jun Fangdf7f3662015-11-10 18:29:18 +0800838 }
839 } else {
Tom Sepez1d17a042017-03-16 13:22:47 -0700840 doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800841 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700842
Lei Zhang5377ebf2015-09-23 14:52:53 -0700843 if (!doc) {
Dan Sinclaireb815bf2015-10-27 13:08:41 -0400844 unsigned long err = FPDF_GetLastError();
845 fprintf(stderr, "Load pdf docs unsuccessful: ");
846 switch (err) {
847 case FPDF_ERR_SUCCESS:
848 fprintf(stderr, "Success");
849 break;
850 case FPDF_ERR_UNKNOWN:
851 fprintf(stderr, "Unknown error");
852 break;
853 case FPDF_ERR_FILE:
854 fprintf(stderr, "File not found or could not be opened");
855 break;
856 case FPDF_ERR_FORMAT:
857 fprintf(stderr, "File not in PDF format or corrupted");
858 break;
859 case FPDF_ERR_PASSWORD:
860 fprintf(stderr, "Password required or incorrect password");
861 break;
862 case FPDF_ERR_SECURITY:
863 fprintf(stderr, "Unsupported security scheme");
864 break;
865 case FPDF_ERR_PAGE:
866 fprintf(stderr, "Page not found or content error");
867 break;
868 default:
869 fprintf(stderr, "Unknown error %ld", err);
870 }
871 fprintf(stderr, ".\n");
JUN FANG827a1722015-03-05 13:39:21 -0800872 return;
873 }
874
Tom Sepez1d17a042017-03-16 13:22:47 -0700875 (void)FPDF_GetDocPermissions(doc.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700876
Tom Sepez1d17a042017-03-16 13:22:47 -0700877 std::unique_ptr<void, FPDFFormHandleDeleter> form(
878 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
879 form_callbacks.form_handle = form.get();
tonikitoo81d92f82016-09-21 12:44:56 -0700880
Tom Sepezc46d0002015-11-30 15:46:36 -0800881#ifdef PDF_ENABLE_XFA
thestigb97e07e2016-09-28 22:16:40 -0700882 int doc_type = DOCTYPE_PDF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700883 if (FPDF_HasXFAField(doc.get(), &doc_type) && doc_type != DOCTYPE_PDF &&
884 !FPDF_LoadXFA(doc.get())) {
Lei Zhang5377ebf2015-09-23 14:52:53 -0700885 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
Tom Sepez56451382014-12-05 13:30:51 -0800886 }
Tom Sepezc46d0002015-11-30 15:46:36 -0800887#endif // PDF_ENABLE_XFA
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700888
Tom Sepez1d17a042017-03-16 13:22:47 -0700889 FPDF_SetFormFieldHighlightColor(form.get(), 0, 0xFFE4DD);
890 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
891 FORM_DoDocumentJSAction(form.get());
892 FORM_DoDocumentOpenAction(form.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700893
Tom Sepez1d17a042017-03-16 13:22:47 -0700894 int page_count = FPDF_GetPageCount(doc.get());
Tom Sepez1ed8a212015-05-11 15:25:39 -0700895 int rendered_pages = 0;
896 int bad_pages = 0;
npmfa20cd52016-11-14 13:33:40 -0800897 int first_page = options.pages ? options.first_page : 0;
898 int last_page = options.pages ? options.last_page + 1 : page_count;
899 for (int i = first_page; i < last_page; ++i) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800900 if (bIsLinearized) {
901 nRet = PDF_DATA_NOTAVAIL;
thestig514e8c92016-07-15 17:57:54 -0700902 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700903 nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
thestig514e8c92016-07-15 17:57:54 -0700904
Jun Fangdf7f3662015-11-10 18:29:18 +0800905 if (nRet == PDF_DATA_ERROR) {
906 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
907 i);
908 return;
909 }
910 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700911 if (RenderPage(name, doc.get(), form.get(), form_callbacks, i, options,
912 events))
Jun Fangdf7f3662015-11-10 18:29:18 +0800913 ++rendered_pages;
thestig514e8c92016-07-15 17:57:54 -0700914 else
Lei Zhang5377ebf2015-09-23 14:52:53 -0700915 ++bad_pages;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700916 }
917
Tom Sepez1d17a042017-03-16 13:22:47 -0700918 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
Tom Sepez1ed8a212015-05-11 15:25:39 -0700919 fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
tsepez10b01bf2016-05-04 12:52:42 -0700920 if (bad_pages)
921 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700922}
923
Tom Sepez2991d8d2016-01-15 16:02:48 -0800924static void ShowConfig() {
925 std::string config;
926 std::string maybe_comma;
927#if PDF_ENABLE_V8
928 config.append(maybe_comma);
929 config.append("V8");
930 maybe_comma = ",";
931#endif // PDF_ENABLE_V8
932#ifdef V8_USE_EXTERNAL_STARTUP_DATA
933 config.append(maybe_comma);
934 config.append("V8_EXTERNAL");
935 maybe_comma = ",";
936#endif // V8_USE_EXTERNAL_STARTUP_DATA
937#ifdef PDF_ENABLE_XFA
938 config.append(maybe_comma);
939 config.append("XFA");
940 maybe_comma = ",";
941#endif // PDF_ENABLE_XFA
dan sinclair00d40642017-01-30 19:48:54 -0800942#ifdef PDF_ENABLE_ASAN
943 config.append(maybe_comma);
944 config.append("ASAN");
945 maybe_comma = ",";
946#endif // PDF_ENABLE_ASAN
Tom Sepez2991d8d2016-01-15 16:02:48 -0800947 printf("%s\n", config.c_str());
948}
949
thestig514e8c92016-07-15 17:57:54 -0700950static const char kUsageString[] =
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800951 "Usage: pdfium_test [OPTION] [FILE]...\n"
Tom Sepez2991d8d2016-01-15 16:02:48 -0800952 " --show-config - print build options and exit\n"
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400953 " --show-structure - print the structure elements from the document\n"
tsepez10b01bf2016-05-04 12:52:42 -0700954 " --send-events - send input described by .evt file\n"
Lei Zhang6f62d532015-09-23 15:31:44 -0700955 " --bin-dir=<path> - override path to v8 external data\n"
956 " --font-dir=<path> - override path to external fonts\n"
957 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
npmfa20cd52016-11-14 13:33:40 -0800958 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800959#ifdef _WIN32
960 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
961 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
Tom Sepezc46d0002015-11-30 15:46:36 -0800962#endif // _WIN32
thestig514e8c92016-07-15 17:57:54 -0700963 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800964 " --png - write page images <pdf-name>.<page-number>.png\n"
thestig514e8c92016-07-15 17:57:54 -0700965 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
Cary Clark399be5b2016-03-14 16:51:29 -0400966#ifdef PDF_ENABLE_SKIA
967 " --skp - write page images <pdf-name>.<page-number>.skp\n"
968#endif
stephanafa05e972017-01-02 06:19:41 -0800969 " --md5 - write output image paths and their md5 hashes to stdout.\n"
Cary Clark399be5b2016-03-14 16:51:29 -0400970 "";
Tom Sepez23b4e3f2015-02-06 16:05:23 -0800971
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700972int main(int argc, const char* argv[]) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800973 std::vector<std::string> args(argv, argv + argc);
974 Options options;
thestig514e8c92016-07-15 17:57:54 -0700975 std::vector<std::string> files;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800976 if (!ParseCommandLine(args, &options, &files)) {
thestig514e8c92016-07-15 17:57:54 -0700977 fprintf(stderr, "%s", kUsageString);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700978 return 1;
979 }
980
Tom Sepez2991d8d2016-01-15 16:02:48 -0800981 if (options.show_config) {
982 ShowConfig();
983 return 0;
984 }
985
986 if (files.empty()) {
987 fprintf(stderr, "No input files.\n");
988 return 1;
989 }
990
Tom Sepez452b4f32015-10-13 09:27:27 -0700991#ifdef PDF_ENABLE_V8
Tom Sepezd831dc72015-10-19 16:04:22 -0700992 v8::Platform* platform;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800993#ifdef V8_USE_EXTERNAL_STARTUP_DATA
994 v8::StartupData natives;
995 v8::StartupData snapshot;
Tom Sepezd831dc72015-10-19 16:04:22 -0700996 InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
997 &snapshot, &platform);
998#else // V8_USE_EXTERNAL_STARTUP_DATA
jochen9e077d22016-06-09 02:51:13 -0700999 InitializeV8ForPDFium(options.exe_path, &platform);
Tom Sepez5ee12d72014-12-17 16:24:01 -08001000#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001001#endif // PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -08001002
Tom Sepeza72e8e22015-10-07 10:17:53 -07001003 FPDF_LIBRARY_CONFIG config;
1004 config.version = 2;
1005 config.m_pUserFontPaths = nullptr;
1006 config.m_pIsolate = nullptr;
1007 config.m_v8EmbedderSlot = 0;
1008
1009 const char* path_array[2];
1010 if (!options.font_directory.empty()) {
Lei Zhang6f62d532015-09-23 15:31:44 -07001011 path_array[0] = options.font_directory.c_str();
1012 path_array[1] = nullptr;
Lei Zhang6f62d532015-09-23 15:31:44 -07001013 config.m_pUserFontPaths = path_array;
Lei Zhang6f62d532015-09-23 15:31:44 -07001014 }
Tom Sepeza72e8e22015-10-07 10:17:53 -07001015 FPDF_InitLibraryWithConfig(&config);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001016
npmfa20cd52016-11-14 13:33:40 -08001017 UNSUPPORT_INFO unsupported_info;
1018 memset(&unsupported_info, '\0', sizeof(unsupported_info));
1019 unsupported_info.version = 1;
1020 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001021
npmfa20cd52016-11-14 13:33:40 -08001022 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001023
thestig514e8c92016-07-15 17:57:54 -07001024 for (const std::string& filename : files) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001025 size_t file_length = 0;
Tom Sepez0aa35312016-01-06 10:16:32 -08001026 std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
1027 GetFileContents(filename.c_str(), &file_length);
tsepezf09bdfa2016-04-18 16:08:26 -07001028 if (!file_contents)
1029 continue;
tsepez10b01bf2016-05-04 12:52:42 -07001030 fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
tsepezf09bdfa2016-04-18 16:08:26 -07001031 std::string events;
1032 if (options.send_events) {
1033 std::string event_filename = filename;
1034 size_t event_length = 0;
1035 size_t extension_pos = event_filename.find(".pdf");
1036 if (extension_pos != std::string::npos) {
1037 event_filename.replace(extension_pos, 4, ".evt");
thestigf2b940c2016-10-13 06:48:47 -07001038 if (access(event_filename.c_str(), R_OK) == 0) {
1039 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1040 std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
1041 GetFileContents(event_filename.c_str(), &event_length);
1042 if (event_contents) {
1043 fprintf(stderr, "Sending events from: %s\n",
1044 event_filename.c_str());
1045 events = std::string(event_contents.get(), event_length);
1046 }
tsepezf09bdfa2016-04-18 16:08:26 -07001047 }
1048 }
1049 }
1050 RenderPdf(filename, file_contents.get(), file_length, options, events);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001051 }
1052
1053 FPDF_DestroyLibrary();
Tom Sepez452b4f32015-10-13 09:27:27 -07001054#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -08001055 v8::V8::ShutdownPlatform();
1056 delete platform;
thestigc08cd7a2016-06-27 09:47:59 -07001057
1058#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1059 free(const_cast<char*>(natives.data));
1060 free(const_cast<char*>(snapshot.data));
1061#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001062#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001063
1064 return 0;
1065}