blob: fa57d65b9ed7121b99ee60872eb506cf2af6a81b [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,
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040056 OUTPUT_STRUCTURE,
dsinclairb63068f2016-06-16 07:58:09 -070057 OUTPUT_TEXT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070058 OUTPUT_PPM,
Tom Sepezaf18cb32015-02-05 15:06:01 -080059 OUTPUT_PNG,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070060#ifdef _WIN32
61 OUTPUT_BMP,
62 OUTPUT_EMF,
Lei Zhangd1c9b452017-05-05 17:21:36 -070063 OUTPUT_PS2,
64 OUTPUT_PS3,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070065#endif
Cary Clark399be5b2016-03-14 16:51:29 -040066#ifdef PDF_ENABLE_SKIA
67 OUTPUT_SKP,
68#endif
Vitaly Buka9e0177a2014-07-22 18:15:42 -070069};
70
Tom Sepez5ee12d72014-12-17 16:24:01 -080071struct Options {
tsepezf09bdfa2016-04-18 16:08:26 -070072 Options()
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040073 : show_config(false),
npme3c73152016-11-14 09:14:52 -080074 send_events(false),
75 pages(false),
stephanafa05e972017-01-02 06:19:41 -080076 md5(false),
npme3c73152016-11-14 09:14:52 -080077 output_format(OUTPUT_NONE) {}
Tom Sepez5ee12d72014-12-17 16:24:01 -080078
Tom Sepez2991d8d2016-01-15 16:02:48 -080079 bool show_config;
tsepezf09bdfa2016-04-18 16:08:26 -070080 bool send_events;
npme3c73152016-11-14 09:14:52 -080081 bool pages;
stephanafa05e972017-01-02 06:19:41 -080082 bool md5;
Tom Sepez5ee12d72014-12-17 16:24:01 -080083 OutputFormat output_format;
Tom Sepezdaa2e842015-01-29 15:44:37 -080084 std::string scale_factor_as_string;
Tom Sepez5ee12d72014-12-17 16:24:01 -080085 std::string exe_path;
86 std::string bin_directory;
Lei Zhang6f62d532015-09-23 15:31:44 -070087 std::string font_directory;
npmfa20cd52016-11-14 13:33:40 -080088 // 0-based page numbers to be rendered.
89 int first_page;
90 int last_page;
Tom Sepez5ee12d72014-12-17 16:24:01 -080091};
92
tonikitoo81d92f82016-09-21 12:44:56 -070093struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
94 // Hold a map of the currently loaded pages in order to avoid them
95 // to get loaded twice.
npmfa20cd52016-11-14 13:33:40 -080096 std::map<int, FPDF_PAGE> loaded_pages;
tonikitoo81d92f82016-09-21 12:44:56 -070097
98 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
99 // make use of it.
npmfa20cd52016-11-14 13:33:40 -0800100 FPDF_FORMHANDLE form_handle;
tonikitoo81d92f82016-09-21 12:44:56 -0700101};
102
103static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
npmfa20cd52016-11-14 13:33:40 -0800104 FPDF_FORMFILLINFO* form_fill_info) {
105 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
tonikitoo81d92f82016-09-21 12:44:56 -0700106}
107
Tom Sepezaf18cb32015-02-05 15:06:01 -0800108static bool CheckDimensions(int stride, int width, int height) {
109 if (stride < 0 || width < 0 || height < 0)
110 return false;
111 if (height > 0 && width > INT_MAX / height)
112 return false;
113 return true;
114}
115
stephanafa05e972017-01-02 06:19:41 -0800116static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
117 // Get the MD5 hash and write it to stdout.
118 uint8_t digest[16];
119 CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(buffer), len, digest);
120 printf("MD5:%s:", file_name);
121 for (int i = 0; i < 16; i++)
122 printf("%02x", digest[i]);
123 printf("\n");
124}
125
126static std::string WritePpm(const char* pdf_name,
127 int num,
128 const void* buffer_void,
129 int stride,
130 int width,
131 int height) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700132 const char* buffer = reinterpret_cast<const char*>(buffer_void);
133
Tom Sepezaf18cb32015-02-05 15:06:01 -0800134 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800135 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800136
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700137 int out_len = width * height;
138 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800139 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700140 out_len *= 3;
141
142 char filename[256];
143 snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
John Abd-El-Maleka548d302014-06-26 10:18:11 -0700144 FILE* fp = fopen(filename, "wb");
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700145 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800146 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700147 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
148 // Source data is B, G, R, unused.
149 // Dest data is R, G, B.
thestig514e8c92016-07-15 17:57:54 -0700150 std::vector<char> result(out_len);
Lei Zhange00660b2015-08-13 15:40:18 -0700151 for (int h = 0; h < height; ++h) {
152 const char* src_line = buffer + (stride * h);
thestig514e8c92016-07-15 17:57:54 -0700153 char* dest_line = result.data() + (width * h * 3);
Lei Zhange00660b2015-08-13 15:40:18 -0700154 for (int w = 0; w < width; ++w) {
155 // R
156 dest_line[w * 3] = src_line[(w * 4) + 2];
157 // G
158 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
159 // B
160 dest_line[(w * 3) + 2] = src_line[w * 4];
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700161 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700162 }
thestig514e8c92016-07-15 17:57:54 -0700163 fwrite(result.data(), out_len, 1, fp);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700164 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800165 return std::string(filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700166}
167
dsinclairb63068f2016-06-16 07:58:09 -0700168void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
169 char filename[256];
170 int chars_formatted =
171 snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
172 if (chars_formatted < 0 ||
173 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
174 fprintf(stderr, "Filename %s is too long\n", filename);
175 return;
176 }
177
178 FILE* fp = fopen(filename, "w");
179 if (!fp) {
180 fprintf(stderr, "Failed to open %s for output\n", filename);
181 return;
182 }
183
184 // Output in UTF32-LE.
185 uint32_t bom = 0x0000FEFF;
186 fwrite(&bom, sizeof(bom), 1, fp);
187
Tom Sepez1d17a042017-03-16 13:22:47 -0700188 std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
189 for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
190 uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
dsinclairb63068f2016-06-16 07:58:09 -0700191 fwrite(&c, sizeof(c), 1, fp);
192 }
dsinclairb63068f2016-06-16 07:58:09 -0700193 (void)fclose(fp);
194}
195
stephanafa05e972017-01-02 06:19:41 -0800196static std::string WritePng(const char* pdf_name,
197 int num,
198 const void* buffer_void,
199 int stride,
200 int width,
201 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800202 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800203 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800204
205 std::vector<unsigned char> png_encoding;
206 const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
207 if (!image_diff_png::EncodeBGRAPNG(
208 buffer, width, height, stride, false, &png_encoding)) {
209 fprintf(stderr, "Failed to convert bitmap to PNG\n");
stephanafa05e972017-01-02 06:19:41 -0800210 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800211 }
212
213 char filename[256];
214 int chars_formatted = snprintf(
215 filename, sizeof(filename), "%s.%d.png", pdf_name, num);
216 if (chars_formatted < 0 ||
217 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
Cary Clark399be5b2016-03-14 16:51:29 -0400218 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800219 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800220 }
221
222 FILE* fp = fopen(filename, "wb");
223 if (!fp) {
224 fprintf(stderr, "Failed to open %s for output\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800225 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800226 }
227
228 size_t bytes_written = fwrite(
229 &png_encoding.front(), 1, png_encoding.size(), fp);
230 if (bytes_written != png_encoding.size())
231 fprintf(stderr, "Failed to write to %s\n", filename);
232
Lei Zhang5377ebf2015-09-23 14:52:53 -0700233 (void)fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800234 return std::string(filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800235}
236
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700237#ifdef _WIN32
stephanafa05e972017-01-02 06:19:41 -0800238static std::string WriteBmp(const char* pdf_name,
239 int num,
240 const void* buffer,
241 int stride,
242 int width,
243 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800244 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800245 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800246
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700247 int out_len = stride * height;
248 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800249 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700250
251 char filename[256];
252 snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
253 FILE* fp = fopen(filename, "wb");
254 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800255 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700256
Nico Weber2827bdd2015-07-01 14:08:08 -0700257 BITMAPINFO bmi = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700258 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
259 bmi.bmiHeader.biWidth = width;
260 bmi.bmiHeader.biHeight = -height; // top-down image
261 bmi.bmiHeader.biPlanes = 1;
262 bmi.bmiHeader.biBitCount = 32;
263 bmi.bmiHeader.biCompression = BI_RGB;
264 bmi.bmiHeader.biSizeImage = 0;
265
Nico Weber2827bdd2015-07-01 14:08:08 -0700266 BITMAPFILEHEADER file_header = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700267 file_header.bfType = 0x4d42;
268 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
269 file_header.bfOffBits = file_header.bfSize - out_len;
270
271 fwrite(&file_header, sizeof(file_header), 1, fp);
272 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
273 fwrite(buffer, out_len, 1, fp);
274 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800275 return std::string(filename);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700276}
277
278void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700279 char filename[256];
280 snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
281
Lei Zhang5377ebf2015-09-23 14:52:53 -0700282 HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800283
Lei Zhangd1c9b452017-05-05 17:21:36 -0700284 int width = static_cast<int>(FPDF_GetPageWidth(page));
285 int height = static_cast<int>(FPDF_GetPageHeight(page));
Tom Sepezaf18cb32015-02-05 15:06:01 -0800286 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}
Lei Zhangd1c9b452017-05-05 17:21:36 -0700300
301int CALLBACK EnhMetaFileProc(HDC hdc,
302 HANDLETABLE* handle_table,
303 const ENHMETARECORD* record,
304 int objects_count,
305 LPARAM param) {
306 std::vector<const ENHMETARECORD*>& items =
307 *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
308 items.push_back(record);
309 return 1;
310}
311
312void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
313 char filename[256];
314 snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
315 FILE* fp = fopen(filename, "wb");
316 if (!fp)
317 return;
318
319 HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
320
321 int width = static_cast<int>(FPDF_GetPageWidth(page));
322 int height = static_cast<int>(FPDF_GetPageHeight(page));
323 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
324 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
325
326 HENHMETAFILE emf = CloseEnhMetaFile(dc);
327 std::vector<const ENHMETARECORD*> items;
328 EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
329 for (const ENHMETARECORD* record : items) {
330 if (record->iType != EMR_GDICOMMENT)
331 continue;
332
333 const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
334 const char* data = reinterpret_cast<const char*>(comment->Data);
335 uint16_t size = *reinterpret_cast<const uint16_t*>(data);
336 fwrite(data + sizeof(uint16_t), size, 1, fp);
337 }
338 fclose(fp);
339 DeleteEnhMetaFile(emf);
340}
341#endif // _WIN32
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700342
Cary Clark399be5b2016-03-14 16:51:29 -0400343#ifdef PDF_ENABLE_SKIA
stephanafa05e972017-01-02 06:19:41 -0800344static std::string WriteSkp(const char* pdf_name,
345 int num,
346 SkPictureRecorder* recorder) {
Cary Clark399be5b2016-03-14 16:51:29 -0400347 char filename[256];
348 int chars_formatted =
349 snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
350
351 if (chars_formatted < 0 ||
352 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
353 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800354 return "";
Cary Clark399be5b2016-03-14 16:51:29 -0400355 }
356
thestigb97e07e2016-09-28 22:16:40 -0700357 sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
Cary Clark399be5b2016-03-14 16:51:29 -0400358 SkFILEWStream wStream(filename);
359 picture->serialize(&wStream);
stephanafa05e972017-01-02 06:19:41 -0800360 return std::string(filename);
Cary Clark399be5b2016-03-14 16:51:29 -0400361}
362#endif
363
Tom Sepez58fb36a2016-02-01 10:32:14 -0800364// These example JS platform callback handlers are entirely optional,
365// and exist here to show the flow of information from a document back
366// to the embedder.
Tom Sepezbd932572016-01-29 09:10:41 -0800367int ExampleAppAlert(IPDF_JSPLATFORM*,
368 FPDF_WIDESTRING msg,
369 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800370 int type,
371 int icon) {
Tom Sepezbd932572016-01-29 09:10:41 -0800372 printf("%ls", GetPlatformWString(title).c_str());
npmfa20cd52016-11-14 13:33:40 -0800373 if (icon || type)
374 printf("[icon=%d,type=%d]", icon, type);
Tom Sepezbd932572016-01-29 09:10:41 -0800375 printf(": %ls\n", GetPlatformWString(msg).c_str());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700376 return 0;
377}
378
Tom Sepez58fb36a2016-02-01 10:32:14 -0800379int ExampleAppResponse(IPDF_JSPLATFORM*,
380 FPDF_WIDESTRING question,
381 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800382 FPDF_WIDESTRING default_value,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800383 FPDF_WIDESTRING label,
npmfa20cd52016-11-14 13:33:40 -0800384 FPDF_BOOL is_password,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800385 void* response,
386 int length) {
387 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
388 GetPlatformWString(title).c_str(),
389 GetPlatformWString(question).c_str(),
npmfa20cd52016-11-14 13:33:40 -0800390 GetPlatformWString(default_value).c_str(),
391 GetPlatformWString(label).c_str(), is_password, length);
Tom Sepez58fb36a2016-02-01 10:32:14 -0800392
393 // UTF-16, always LE regardless of platform.
394 uint8_t* ptr = static_cast<uint8_t*>(response);
395 ptr[0] = 'N';
396 ptr[1] = 0;
397 ptr[2] = 'o';
398 ptr[3] = 0;
399 return 4;
400}
401
npmfa20cd52016-11-14 13:33:40 -0800402void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
403 printf("Goto Page: %d\n", page_number);
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800404}
405
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800406void ExampleDocMail(IPDF_JSPLATFORM*,
407 void* mailData,
408 int length,
npmfa20cd52016-11-14 13:33:40 -0800409 FPDF_BOOL UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800410 FPDF_WIDESTRING To,
411 FPDF_WIDESTRING Subject,
412 FPDF_WIDESTRING CC,
413 FPDF_WIDESTRING BCC,
414 FPDF_WIDESTRING Msg) {
npmfa20cd52016-11-14 13:33:40 -0800415 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800416 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
417 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
418 GetPlatformWString(Msg).c_str());
419}
420
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800421void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700422 std::string feature = "Unknown";
423 switch (type) {
424 case FPDF_UNSP_DOC_XFAFORM:
425 feature = "XFA";
426 break;
427 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
428 feature = "Portfolios_Packages";
429 break;
430 case FPDF_UNSP_DOC_ATTACHMENT:
431 case FPDF_UNSP_ANNOT_ATTACHMENT:
432 feature = "Attachment";
433 break;
434 case FPDF_UNSP_DOC_SECURITY:
435 feature = "Rights_Management";
436 break;
437 case FPDF_UNSP_DOC_SHAREDREVIEW:
438 feature = "Shared_Review";
439 break;
440 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
441 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
442 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
443 feature = "Shared_Form";
444 break;
445 case FPDF_UNSP_ANNOT_3DANNOT:
446 feature = "3D";
447 break;
448 case FPDF_UNSP_ANNOT_MOVIE:
449 feature = "Movie";
450 break;
451 case FPDF_UNSP_ANNOT_SOUND:
452 feature = "Sound";
453 break;
454 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
455 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
456 feature = "Screen";
457 break;
458 case FPDF_UNSP_ANNOT_SIG:
459 feature = "Digital_Signature";
460 break;
461 }
462 printf("Unsupported feature: %s.\n", feature.c_str());
463}
464
Tom Sepez5ee12d72014-12-17 16:24:01 -0800465bool ParseCommandLine(const std::vector<std::string>& args,
thestig514e8c92016-07-15 17:57:54 -0700466 Options* options,
467 std::vector<std::string>* files) {
468 if (args.empty())
Tom Sepez5ee12d72014-12-17 16:24:01 -0800469 return false;
thestig514e8c92016-07-15 17:57:54 -0700470
Tom Sepez5ee12d72014-12-17 16:24:01 -0800471 options->exe_path = args[0];
Bo Xud44e3922014-12-19 02:27:25 -0800472 size_t cur_idx = 1;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800473 for (; cur_idx < args.size(); ++cur_idx) {
474 const std::string& cur_arg = args[cur_idx];
Tom Sepez2991d8d2016-01-15 16:02:48 -0800475 if (cur_arg == "--show-config") {
476 options->show_config = true;
tsepezf09bdfa2016-04-18 16:08:26 -0700477 } else if (cur_arg == "--send-events") {
478 options->send_events = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800479 } else if (cur_arg == "--ppm") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800480 if (options->output_format != OUTPUT_NONE) {
481 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
482 return false;
483 }
484 options->output_format = OUTPUT_PPM;
Tom Sepezaf18cb32015-02-05 15:06:01 -0800485 } else if (cur_arg == "--png") {
486 if (options->output_format != OUTPUT_NONE) {
487 fprintf(stderr, "Duplicate or conflicting --png argument\n");
488 return false;
489 }
490 options->output_format = OUTPUT_PNG;
dsinclairb63068f2016-06-16 07:58:09 -0700491 } else if (cur_arg == "--txt") {
492 if (options->output_format != OUTPUT_NONE) {
493 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
494 return false;
495 }
496 options->output_format = OUTPUT_TEXT;
Cary Clark399be5b2016-03-14 16:51:29 -0400497#ifdef PDF_ENABLE_SKIA
498 } else if (cur_arg == "--skp") {
499 if (options->output_format != OUTPUT_NONE) {
500 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
501 return false;
502 }
503 options->output_format = OUTPUT_SKP;
504#endif
Lei Zhang6f62d532015-09-23 15:31:44 -0700505 } else if (cur_arg.size() > 11 &&
506 cur_arg.compare(0, 11, "--font-dir=") == 0) {
507 if (!options->font_directory.empty()) {
508 fprintf(stderr, "Duplicate --font-dir argument\n");
509 return false;
510 }
511 options->font_directory = cur_arg.substr(11);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700512#ifdef _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500513 } else if (cur_arg == "--emf") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800514 if (options->output_format != OUTPUT_NONE) {
515 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
516 return false;
517 }
518 options->output_format = OUTPUT_EMF;
Lei Zhangd1c9b452017-05-05 17:21:36 -0700519 } else if (cur_arg == "--ps2") {
520 if (options->output_format != OUTPUT_NONE) {
521 fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
522 return false;
523 }
524 options->output_format = OUTPUT_PS2;
525 } else if (cur_arg == "--ps3") {
526 if (options->output_format != OUTPUT_NONE) {
527 fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
528 return false;
529 }
530 options->output_format = OUTPUT_PS3;
Dan Sinclair50cce602016-02-24 09:51:16 -0500531 } else if (cur_arg == "--bmp") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800532 if (options->output_format != OUTPUT_NONE) {
533 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
534 return false;
535 }
536 options->output_format = OUTPUT_BMP;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800537#endif // _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500538
Tom Sepez452b4f32015-10-13 09:27:27 -0700539#ifdef PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800540#ifdef V8_USE_EXTERNAL_STARTUP_DATA
Dan Sinclair738b08c2016-03-01 14:45:20 -0500541 } else if (cur_arg.size() > 10 &&
542 cur_arg.compare(0, 10, "--bin-dir=") == 0) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800543 if (!options->bin_directory.empty()) {
544 fprintf(stderr, "Duplicate --bin-dir argument\n");
545 return false;
546 }
547 options->bin_directory = cur_arg.substr(10);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800548#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700549#endif // PDF_ENABLE_V8
Dan Sinclair738b08c2016-03-01 14:45:20 -0500550
551 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
Tom Sepezdaa2e842015-01-29 15:44:37 -0800552 if (!options->scale_factor_as_string.empty()) {
553 fprintf(stderr, "Duplicate --scale argument\n");
554 return false;
555 }
556 options->scale_factor_as_string = cur_arg.substr(8);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400557 } else if (cur_arg == "--show-structure") {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400558 if (options->output_format != OUTPUT_NONE) {
559 fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
560 return false;
561 }
562 options->output_format = OUTPUT_STRUCTURE;
npme3c73152016-11-14 09:14:52 -0800563 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
564 if (options->pages) {
565 fprintf(stderr, "Duplicate --pages argument\n");
566 return false;
567 }
568 options->pages = true;
npmfa20cd52016-11-14 13:33:40 -0800569 const std::string pages_string = cur_arg.substr(8);
570 size_t first_dash = pages_string.find("-");
571 if (first_dash == std::string::npos) {
572 std::stringstream(pages_string) >> options->first_page;
573 options->last_page = options->first_page;
npme3c73152016-11-14 09:14:52 -0800574 } else {
npmfa20cd52016-11-14 13:33:40 -0800575 std::stringstream(pages_string.substr(0, first_dash)) >>
576 options->first_page;
577 std::stringstream(pages_string.substr(first_dash + 1)) >>
578 options->last_page;
npme3c73152016-11-14 09:14:52 -0800579 }
stephanafa05e972017-01-02 06:19:41 -0800580 } else if (cur_arg == "--md5") {
581 options->md5 = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800582 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
583 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
584 return false;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500585 } else {
Vitaly Buka8f2c3dc2014-08-20 10:32:36 -0700586 break;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500587 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700588 }
thestig514e8c92016-07-15 17:57:54 -0700589 for (size_t i = cur_idx; i < args.size(); i++)
Tom Sepez5ee12d72014-12-17 16:24:01 -0800590 files->push_back(args[i]);
thestig514e8c92016-07-15 17:57:54 -0700591
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700592 return true;
593}
594
npmfa20cd52016-11-14 13:33:40 -0800595FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700596 return true;
597}
598
npmfa20cd52016-11-14 13:33:40 -0800599void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700600
tsepezf09bdfa2016-04-18 16:08:26 -0700601void SendPageEvents(const FPDF_FORMHANDLE& form,
602 const FPDF_PAGE& page,
603 const std::string& events) {
604 auto lines = StringSplit(events, '\n');
605 for (auto line : lines) {
606 auto command = StringSplit(line, '#');
607 if (command[0].empty())
608 continue;
609 auto tokens = StringSplit(command[0], ',');
610 if (tokens[0] == "keycode") {
611 if (tokens.size() == 2) {
612 int keycode = atoi(tokens[1].c_str());
613 FORM_OnKeyDown(form, page, keycode, 0);
614 FORM_OnKeyUp(form, page, keycode, 0);
615 } else {
616 fprintf(stderr, "keycode: bad args\n");
617 }
618 } else if (tokens[0] == "mousedown") {
619 if (tokens.size() == 4) {
620 int x = atoi(tokens[2].c_str());
621 int y = atoi(tokens[3].c_str());
622 if (tokens[1] == "left")
623 FORM_OnLButtonDown(form, page, 0, x, y);
624#ifdef PDF_ENABLE_XFA
625 else if (tokens[1] == "right")
626 FORM_OnRButtonDown(form, page, 0, x, y);
627#endif
628 else
629 fprintf(stderr, "mousedown: bad button name\n");
630 } else {
631 fprintf(stderr, "mousedown: bad args\n");
632 }
633 } else if (tokens[0] == "mouseup") {
634 if (tokens.size() == 4) {
635 int x = atoi(tokens[2].c_str());
636 int y = atoi(tokens[3].c_str());
637 if (tokens[1] == "left")
638 FORM_OnLButtonUp(form, page, 0, x, y);
639#ifdef PDF_ENABLE_XFA
640 else if (tokens[1] == "right")
641 FORM_OnRButtonUp(form, page, 0, x, y);
642#endif
643 else
644 fprintf(stderr, "mouseup: bad button name\n");
645 } else {
646 fprintf(stderr, "mouseup: bad args\n");
647 }
648 } else if (tokens[0] == "mousemove") {
649 if (tokens.size() == 3) {
650 int x = atoi(tokens[1].c_str());
651 int y = atoi(tokens[2].c_str());
652 FORM_OnMouseMove(form, page, 0, x, y);
653 } else {
654 fprintf(stderr, "mousemove: bad args\n");
655 }
656 } else {
657 fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
658 }
659 }
660}
661
tonikitoo3e981582016-08-26 08:37:10 -0700662FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
663 FPDF_DOCUMENT doc,
664 int index) {
npmfa20cd52016-11-14 13:33:40 -0800665 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
666 ToPDFiumTestFormFillInfo(param);
667 auto& loaded_pages = form_fill_info->loaded_pages;
npmfa20cd52016-11-14 13:33:40 -0800668 auto iter = loaded_pages.find(index);
669 if (iter != loaded_pages.end())
tonikitoo3e981582016-08-26 08:37:10 -0700670 return iter->second;
671
672 FPDF_PAGE page = FPDF_LoadPage(doc, index);
673 if (!page)
674 return nullptr;
675
npmfa20cd52016-11-14 13:33:40 -0800676 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
npmfa20cd52016-11-14 13:33:40 -0800677 FORM_OnAfterLoadPage(page, form_handle);
678 FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
npmfa20cd52016-11-14 13:33:40 -0800679 loaded_pages[index] = page;
tonikitoo3e981582016-08-26 08:37:10 -0700680 return page;
681}
682
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400683std::wstring ConvertToWString(const unsigned short* buf,
684 unsigned long buf_size) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400685 std::wstring result;
686 result.reserve(buf_size);
687 std::copy(buf, buf + buf_size, std::back_inserter(result));
688 return result;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400689}
690
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400691void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
692 static const size_t kBufSize = 1024;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400693 unsigned short buf[kBufSize];
694 unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400695 printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400696
697 memset(buf, 0, sizeof(buf));
dan sinclaird9dad3a2017-04-06 14:44:02 -0400698 len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
699 if (len > 0)
700 printf(": '%ls'", ConvertToWString(buf, len).c_str());
701
702 memset(buf, 0, sizeof(buf));
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400703 len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
704 if (len > 0)
705 printf(" (%ls)", ConvertToWString(buf, len).c_str());
706 printf("\n");
707
708 for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
709 FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
710 // If the child is not an Element then this will return null. This can
711 // happen if the element is things like an object reference or a stream.
712 if (!sub_child)
713 continue;
714
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400715 DumpChildStructure(sub_child, indent + 1);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400716 }
717}
718
719void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
720 std::unique_ptr<void, FPDFStructTreeDeleter> tree(
721 FPDF_StructTree_GetForPage(page));
722 if (!tree) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400723 fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400724 return;
725 }
726
727 printf("Structure Tree for Page %d\n", page_idx);
728 for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
729 FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
730 if (!child) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400731 fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400732 continue;
733 }
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400734 DumpChildStructure(child, 0);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400735 }
736 printf("\n\n");
737}
738
Jun Fangb553bcb2015-11-10 18:49:04 +0800739bool RenderPage(const std::string& name,
tonikitoo3e981582016-08-26 08:37:10 -0700740 FPDF_DOCUMENT doc,
Tom Sepez1d17a042017-03-16 13:22:47 -0700741 FPDF_FORMHANDLE form,
npmfa20cd52016-11-14 13:33:40 -0800742 FPDF_FORMFILLINFO_PDFiumTest& form_fill_info,
Jun Fangb553bcb2015-11-10 18:49:04 +0800743 const int page_index,
tsepezf09bdfa2016-04-18 16:08:26 -0700744 const Options& options,
745 const std::string& events) {
Tom Sepez1d17a042017-03-16 13:22:47 -0700746 std::unique_ptr<void, FPDFPageDeleter> page(
747 GetPageForIndex(&form_fill_info, doc, page_index));
748 if (!page.get())
Jun Fangb553bcb2015-11-10 18:49:04 +0800749 return false;
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400750 if (options.send_events)
751 SendPageEvents(form, page.get(), events);
752 if (options.output_format == OUTPUT_STRUCTURE) {
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400753 DumpPageStructure(page.get(), page_index);
754 return true;
755 }
756
Tom Sepez1d17a042017-03-16 13:22:47 -0700757 std::unique_ptr<void, FPDFTextPageDeleter> text_page(
758 FPDFText_LoadPage(page.get()));
tsepezf09bdfa2016-04-18 16:08:26 -0700759
Jun Fangdf7f3662015-11-10 18:29:18 +0800760 double scale = 1.0;
thestig514e8c92016-07-15 17:57:54 -0700761 if (!options.scale_factor_as_string.empty())
Jun Fangdf7f3662015-11-10 18:29:18 +0800762 std::stringstream(options.scale_factor_as_string) >> scale;
thestig514e8c92016-07-15 17:57:54 -0700763
Tom Sepez1d17a042017-03-16 13:22:47 -0700764 int width = static_cast<int>(FPDF_GetPageWidth(page.get()) * scale);
765 int height = static_cast<int>(FPDF_GetPageHeight(page.get()) * scale);
766 int alpha = FPDFPage_HasTransparency(page.get()) ? 1 : 0;
767 std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
768 FPDFBitmap_Create(width, height, alpha));
769
thestige97ea032016-05-19 10:59:15 -0700770 if (bitmap) {
771 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700772 FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
773 FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
774 FPDF_ANNOT);
Jun Fangdf7f3662015-11-10 18:29:18 +0800775
Tom Sepez1d17a042017-03-16 13:22:47 -0700776 FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
777 FPDF_ANNOT);
778 int stride = FPDFBitmap_GetStride(bitmap.get());
thestige97ea032016-05-19 10:59:15 -0700779 const char* buffer =
Tom Sepez1d17a042017-03-16 13:22:47 -0700780 reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
Jun Fangdf7f3662015-11-10 18:29:18 +0800781
stephanafa05e972017-01-02 06:19:41 -0800782 std::string&& image_file_name = "";
thestige97ea032016-05-19 10:59:15 -0700783 switch (options.output_format) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800784#ifdef _WIN32
thestige97ea032016-05-19 10:59:15 -0700785 case OUTPUT_BMP:
stephanafa05e972017-01-02 06:19:41 -0800786 image_file_name =
787 WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700788 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800789
thestige97ea032016-05-19 10:59:15 -0700790 case OUTPUT_EMF:
Tom Sepez1d17a042017-03-16 13:22:47 -0700791 WriteEmf(page.get(), name.c_str(), page_index);
thestige97ea032016-05-19 10:59:15 -0700792 break;
Lei Zhangd1c9b452017-05-05 17:21:36 -0700793
794 case OUTPUT_PS2:
795 case OUTPUT_PS3:
796 WritePS(page.get(), name.c_str(), page_index);
797 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800798#endif
dsinclairb63068f2016-06-16 07:58:09 -0700799 case OUTPUT_TEXT:
Tom Sepez1d17a042017-03-16 13:22:47 -0700800 WriteText(page.get(), name.c_str(), page_index);
dsinclairb63068f2016-06-16 07:58:09 -0700801 break;
802
thestige97ea032016-05-19 10:59:15 -0700803 case OUTPUT_PNG:
stephanafa05e972017-01-02 06:19:41 -0800804 image_file_name =
805 WritePng(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700806 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800807
thestige97ea032016-05-19 10:59:15 -0700808 case OUTPUT_PPM:
stephanafa05e972017-01-02 06:19:41 -0800809 image_file_name =
810 WritePpm(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700811 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800812
Cary Clark399be5b2016-03-14 16:51:29 -0400813#ifdef PDF_ENABLE_SKIA
thestige97ea032016-05-19 10:59:15 -0700814 case OUTPUT_SKP: {
815 std::unique_ptr<SkPictureRecorder> recorder(
thestigb97e07e2016-09-28 22:16:40 -0700816 reinterpret_cast<SkPictureRecorder*>(
Tom Sepez1d17a042017-03-16 13:22:47 -0700817 FPDF_RenderPageSkp(page.get(), width, height)));
818 FPDF_FFLRecord(form, recorder.get(), page.get(), 0, 0, width, height, 0,
819 0);
stephanafa05e972017-01-02 06:19:41 -0800820 image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
thestige97ea032016-05-19 10:59:15 -0700821 } break;
Cary Clark399be5b2016-03-14 16:51:29 -0400822#endif
thestige97ea032016-05-19 10:59:15 -0700823 default:
824 break;
825 }
Jun Fangdf7f3662015-11-10 18:29:18 +0800826
stephanafa05e972017-01-02 06:19:41 -0800827 // Write the filename and the MD5 of the buffer to stdout if we wrote a
828 // file.
829 if (options.md5 && image_file_name != "")
830 OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
thestige97ea032016-05-19 10:59:15 -0700831 } else {
832 fprintf(stderr, "Page was too large to be rendered.\n");
833 }
tonikitoo3e981582016-08-26 08:37:10 -0700834
npmfa20cd52016-11-14 13:33:40 -0800835 form_fill_info.loaded_pages.erase(page_index);
Tom Sepez1d17a042017-03-16 13:22:47 -0700836 FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
837 FORM_OnBeforeClosePage(page.get(), form);
thestige97ea032016-05-19 10:59:15 -0700838 return !!bitmap;
Jun Fangdf7f3662015-11-10 18:29:18 +0800839}
840
tsepezf09bdfa2016-04-18 16:08:26 -0700841void RenderPdf(const std::string& name,
842 const char* pBuf,
843 size_t len,
844 const Options& options,
845 const std::string& events) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700846 IPDF_JSPLATFORM platform_callbacks;
847 memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
Tom Sepeza72e8e22015-10-07 10:17:53 -0700848 platform_callbacks.version = 3;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800849 platform_callbacks.app_alert = ExampleAppAlert;
Tom Sepez58fb36a2016-02-01 10:32:14 -0800850 platform_callbacks.app_response = ExampleAppResponse;
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800851 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800852 platform_callbacks.Doc_mail = ExampleDocMail;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700853
tonikitoo81d92f82016-09-21 12:44:56 -0700854 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
Tom Sepezc46d0002015-11-30 15:46:36 -0800855#ifdef PDF_ENABLE_XFA
Tom Sepezed631382014-11-18 14:10:25 -0800856 form_callbacks.version = 2;
Tom Sepezc46d0002015-11-30 15:46:36 -0800857#else // PDF_ENABLE_XFA
858 form_callbacks.version = 1;
859#endif // PDF_ENABLE_XFA
tonikitoo3e981582016-08-26 08:37:10 -0700860 form_callbacks.FFI_GetPage = GetPageForIndex;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700861 form_callbacks.m_pJsPlatform = &platform_callbacks;
862
863 TestLoader loader(pBuf, len);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700864 FPDF_FILEACCESS file_access;
865 memset(&file_access, '\0', sizeof(file_access));
John Abd-El-Malek7dc51722014-05-26 12:54:31 -0700866 file_access.m_FileLen = static_cast<unsigned long>(len);
Tom Sepezd831dc72015-10-19 16:04:22 -0700867 file_access.m_GetBlock = TestLoader::GetBlock;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700868 file_access.m_Param = &loader;
869
870 FX_FILEAVAIL file_avail;
871 memset(&file_avail, '\0', sizeof(file_avail));
872 file_avail.version = 1;
873 file_avail.IsDataAvail = Is_Data_Avail;
874
875 FX_DOWNLOADHINTS hints;
876 memset(&hints, '\0', sizeof(hints));
877 hints.version = 1;
878 hints.AddSegment = Add_Segment;
879
Jun Fangdf7f3662015-11-10 18:29:18 +0800880 int nRet = PDF_DATA_NOTAVAIL;
Jun Fangb553bcb2015-11-10 18:49:04 +0800881 bool bIsLinearized = false;
Tom Sepez1d17a042017-03-16 13:22:47 -0700882 std::unique_ptr<void, FPDFDocumentDeleter> doc;
883 std::unique_ptr<void, FPDFAvailDeleter> pdf_avail(
884 FPDFAvail_Create(&file_avail, &file_access));
Tom Sepezc98895c2015-11-24 15:30:36 -0800885
Tom Sepez1d17a042017-03-16 13:22:47 -0700886 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
887 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800888 if (doc) {
thestig514e8c92016-07-15 17:57:54 -0700889 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700890 nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
thestig514e8c92016-07-15 17:57:54 -0700891
Jun Fangdf7f3662015-11-10 18:29:18 +0800892 if (nRet == PDF_DATA_ERROR) {
893 fprintf(stderr, "Unknown error in checking if doc was available.\n");
894 return;
895 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700896 nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
Jun Fangdf7f3662015-11-10 18:29:18 +0800897 if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
898 fprintf(stderr,
899 "Error %d was returned in checking if form was available.\n",
900 nRet);
901 return;
902 }
Jun Fangb553bcb2015-11-10 18:49:04 +0800903 bIsLinearized = true;
Jun Fangdf7f3662015-11-10 18:29:18 +0800904 }
905 } else {
Tom Sepez1d17a042017-03-16 13:22:47 -0700906 doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +0800907 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700908
Lei Zhang5377ebf2015-09-23 14:52:53 -0700909 if (!doc) {
Dan Sinclaireb815bf2015-10-27 13:08:41 -0400910 unsigned long err = FPDF_GetLastError();
911 fprintf(stderr, "Load pdf docs unsuccessful: ");
912 switch (err) {
913 case FPDF_ERR_SUCCESS:
914 fprintf(stderr, "Success");
915 break;
916 case FPDF_ERR_UNKNOWN:
917 fprintf(stderr, "Unknown error");
918 break;
919 case FPDF_ERR_FILE:
920 fprintf(stderr, "File not found or could not be opened");
921 break;
922 case FPDF_ERR_FORMAT:
923 fprintf(stderr, "File not in PDF format or corrupted");
924 break;
925 case FPDF_ERR_PASSWORD:
926 fprintf(stderr, "Password required or incorrect password");
927 break;
928 case FPDF_ERR_SECURITY:
929 fprintf(stderr, "Unsupported security scheme");
930 break;
931 case FPDF_ERR_PAGE:
932 fprintf(stderr, "Page not found or content error");
933 break;
934 default:
935 fprintf(stderr, "Unknown error %ld", err);
936 }
937 fprintf(stderr, ".\n");
JUN FANG827a1722015-03-05 13:39:21 -0800938 return;
939 }
940
Tom Sepez1d17a042017-03-16 13:22:47 -0700941 (void)FPDF_GetDocPermissions(doc.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700942
Tom Sepez1d17a042017-03-16 13:22:47 -0700943 std::unique_ptr<void, FPDFFormHandleDeleter> form(
944 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
945 form_callbacks.form_handle = form.get();
tonikitoo81d92f82016-09-21 12:44:56 -0700946
Tom Sepezc46d0002015-11-30 15:46:36 -0800947#ifdef PDF_ENABLE_XFA
thestigb97e07e2016-09-28 22:16:40 -0700948 int doc_type = DOCTYPE_PDF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700949 if (FPDF_HasXFAField(doc.get(), &doc_type) && doc_type != DOCTYPE_PDF &&
950 !FPDF_LoadXFA(doc.get())) {
Lei Zhang5377ebf2015-09-23 14:52:53 -0700951 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
Tom Sepez56451382014-12-05 13:30:51 -0800952 }
Tom Sepezc46d0002015-11-30 15:46:36 -0800953#endif // PDF_ENABLE_XFA
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700954
Tom Sepez1d17a042017-03-16 13:22:47 -0700955 FPDF_SetFormFieldHighlightColor(form.get(), 0, 0xFFE4DD);
956 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
957 FORM_DoDocumentJSAction(form.get());
958 FORM_DoDocumentOpenAction(form.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700959
Lei Zhangd1c9b452017-05-05 17:21:36 -0700960#if _WIN32
961 if (options.output_format == OUTPUT_PS2)
962 FPDF_SetPrintPostscriptLevel(2);
963 else if (options.output_format == OUTPUT_PS3)
964 FPDF_SetPrintPostscriptLevel(3);
965#endif
966
Tom Sepez1d17a042017-03-16 13:22:47 -0700967 int page_count = FPDF_GetPageCount(doc.get());
Tom Sepez1ed8a212015-05-11 15:25:39 -0700968 int rendered_pages = 0;
969 int bad_pages = 0;
npmfa20cd52016-11-14 13:33:40 -0800970 int first_page = options.pages ? options.first_page : 0;
971 int last_page = options.pages ? options.last_page + 1 : page_count;
972 for (int i = first_page; i < last_page; ++i) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800973 if (bIsLinearized) {
974 nRet = PDF_DATA_NOTAVAIL;
thestig514e8c92016-07-15 17:57:54 -0700975 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -0700976 nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
thestig514e8c92016-07-15 17:57:54 -0700977
Jun Fangdf7f3662015-11-10 18:29:18 +0800978 if (nRet == PDF_DATA_ERROR) {
979 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
980 i);
981 return;
982 }
983 }
Tom Sepez1d17a042017-03-16 13:22:47 -0700984 if (RenderPage(name, doc.get(), form.get(), form_callbacks, i, options,
985 events))
Jun Fangdf7f3662015-11-10 18:29:18 +0800986 ++rendered_pages;
thestig514e8c92016-07-15 17:57:54 -0700987 else
Lei Zhang5377ebf2015-09-23 14:52:53 -0700988 ++bad_pages;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700989 }
990
Tom Sepez1d17a042017-03-16 13:22:47 -0700991 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
Tom Sepez1ed8a212015-05-11 15:25:39 -0700992 fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
tsepez10b01bf2016-05-04 12:52:42 -0700993 if (bad_pages)
994 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700995}
996
Tom Sepez2991d8d2016-01-15 16:02:48 -0800997static void ShowConfig() {
998 std::string config;
999 std::string maybe_comma;
1000#if PDF_ENABLE_V8
1001 config.append(maybe_comma);
1002 config.append("V8");
1003 maybe_comma = ",";
1004#endif // PDF_ENABLE_V8
1005#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1006 config.append(maybe_comma);
1007 config.append("V8_EXTERNAL");
1008 maybe_comma = ",";
1009#endif // V8_USE_EXTERNAL_STARTUP_DATA
1010#ifdef PDF_ENABLE_XFA
1011 config.append(maybe_comma);
1012 config.append("XFA");
1013 maybe_comma = ",";
1014#endif // PDF_ENABLE_XFA
dan sinclair00d40642017-01-30 19:48:54 -08001015#ifdef PDF_ENABLE_ASAN
1016 config.append(maybe_comma);
1017 config.append("ASAN");
1018 maybe_comma = ",";
1019#endif // PDF_ENABLE_ASAN
Tom Sepez2991d8d2016-01-15 16:02:48 -08001020 printf("%s\n", config.c_str());
1021}
1022
thestig514e8c92016-07-15 17:57:54 -07001023static const char kUsageString[] =
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001024 "Usage: pdfium_test [OPTION] [FILE]...\n"
Tom Sepez2991d8d2016-01-15 16:02:48 -08001025 " --show-config - print build options and exit\n"
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001026 " --show-structure - print the structure elements from the document\n"
tsepez10b01bf2016-05-04 12:52:42 -07001027 " --send-events - send input described by .evt file\n"
Lei Zhang6f62d532015-09-23 15:31:44 -07001028 " --bin-dir=<path> - override path to v8 external data\n"
1029 " --font-dir=<path> - override path to external fonts\n"
1030 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
npmfa20cd52016-11-14 13:33:40 -08001031 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001032#ifdef _WIN32
1033 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
1034 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
Lei Zhangd1c9b452017-05-05 17:21:36 -07001035 " --ps2 - write page raw PostScript (Lvl 2) <pdf-name>.<page-number>.ps\n"
1036 " --ps3 - write page raw PostScript (Lvl 3) <pdf-name>.<page-number>.ps\n"
Tom Sepezc46d0002015-11-30 15:46:36 -08001037#endif // _WIN32
thestig514e8c92016-07-15 17:57:54 -07001038 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001039 " --png - write page images <pdf-name>.<page-number>.png\n"
thestig514e8c92016-07-15 17:57:54 -07001040 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001041#ifdef PDF_ENABLE_SKIA
1042 " --skp - write page images <pdf-name>.<page-number>.skp\n"
1043#endif
stephanafa05e972017-01-02 06:19:41 -08001044 " --md5 - write output image paths and their md5 hashes to stdout.\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001045 "";
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001046
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001047int main(int argc, const char* argv[]) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001048 std::vector<std::string> args(argv, argv + argc);
1049 Options options;
thestig514e8c92016-07-15 17:57:54 -07001050 std::vector<std::string> files;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001051 if (!ParseCommandLine(args, &options, &files)) {
thestig514e8c92016-07-15 17:57:54 -07001052 fprintf(stderr, "%s", kUsageString);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001053 return 1;
1054 }
1055
Tom Sepez2991d8d2016-01-15 16:02:48 -08001056 if (options.show_config) {
1057 ShowConfig();
1058 return 0;
1059 }
1060
1061 if (files.empty()) {
1062 fprintf(stderr, "No input files.\n");
1063 return 1;
1064 }
1065
Tom Sepez452b4f32015-10-13 09:27:27 -07001066#ifdef PDF_ENABLE_V8
Tom Sepezd831dc72015-10-19 16:04:22 -07001067 v8::Platform* platform;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001068#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1069 v8::StartupData natives;
1070 v8::StartupData snapshot;
Tom Sepezd831dc72015-10-19 16:04:22 -07001071 InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
1072 &snapshot, &platform);
1073#else // V8_USE_EXTERNAL_STARTUP_DATA
jochen9e077d22016-06-09 02:51:13 -07001074 InitializeV8ForPDFium(options.exe_path, &platform);
Tom Sepez5ee12d72014-12-17 16:24:01 -08001075#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001076#endif // PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -08001077
Tom Sepeza72e8e22015-10-07 10:17:53 -07001078 FPDF_LIBRARY_CONFIG config;
1079 config.version = 2;
1080 config.m_pUserFontPaths = nullptr;
1081 config.m_pIsolate = nullptr;
1082 config.m_v8EmbedderSlot = 0;
1083
1084 const char* path_array[2];
1085 if (!options.font_directory.empty()) {
Lei Zhang6f62d532015-09-23 15:31:44 -07001086 path_array[0] = options.font_directory.c_str();
1087 path_array[1] = nullptr;
Lei Zhang6f62d532015-09-23 15:31:44 -07001088 config.m_pUserFontPaths = path_array;
Lei Zhang6f62d532015-09-23 15:31:44 -07001089 }
Tom Sepeza72e8e22015-10-07 10:17:53 -07001090 FPDF_InitLibraryWithConfig(&config);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001091
npmfa20cd52016-11-14 13:33:40 -08001092 UNSUPPORT_INFO unsupported_info;
1093 memset(&unsupported_info, '\0', sizeof(unsupported_info));
1094 unsupported_info.version = 1;
1095 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001096
npmfa20cd52016-11-14 13:33:40 -08001097 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001098
thestig514e8c92016-07-15 17:57:54 -07001099 for (const std::string& filename : files) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001100 size_t file_length = 0;
Tom Sepez0aa35312016-01-06 10:16:32 -08001101 std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
1102 GetFileContents(filename.c_str(), &file_length);
tsepezf09bdfa2016-04-18 16:08:26 -07001103 if (!file_contents)
1104 continue;
tsepez10b01bf2016-05-04 12:52:42 -07001105 fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
tsepezf09bdfa2016-04-18 16:08:26 -07001106 std::string events;
1107 if (options.send_events) {
1108 std::string event_filename = filename;
1109 size_t event_length = 0;
1110 size_t extension_pos = event_filename.find(".pdf");
1111 if (extension_pos != std::string::npos) {
1112 event_filename.replace(extension_pos, 4, ".evt");
thestigf2b940c2016-10-13 06:48:47 -07001113 if (access(event_filename.c_str(), R_OK) == 0) {
1114 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1115 std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
1116 GetFileContents(event_filename.c_str(), &event_length);
1117 if (event_contents) {
1118 fprintf(stderr, "Sending events from: %s\n",
1119 event_filename.c_str());
1120 events = std::string(event_contents.get(), event_length);
1121 }
tsepezf09bdfa2016-04-18 16:08:26 -07001122 }
1123 }
1124 }
1125 RenderPdf(filename, file_contents.get(), file_length, options, events);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001126 }
1127
1128 FPDF_DestroyLibrary();
Tom Sepez452b4f32015-10-13 09:27:27 -07001129#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -08001130 v8::V8::ShutdownPlatform();
1131 delete platform;
thestigc08cd7a2016-06-27 09:47:59 -07001132
1133#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1134 free(const_cast<char*>(natives.data));
1135 free(const_cast<char*>(snapshot.data));
1136#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001137#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001138
1139 return 0;
1140}