blob: 5eccf2255434203426cb321a611886d95d102a2b [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
Lei Zhang9a25ede2017-06-22 00:05:12 -070010#include <iterator>
tonikitoo3e981582016-08-26 08:37:10 -070011#include <map>
Tom Sepezdaa2e842015-01-29 15:44:37 -080012#include <sstream>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070013#include <string>
14#include <utility>
Tom Sepez5ee12d72014-12-17 16:24:01 -080015#include <vector>
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070016
Cary Clark399be5b2016-03-14 16:51:29 -040017#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
18#define _SKIA_SUPPORT_
19#endif
20
Tom Sepez1d17a042017-03-16 13:22:47 -070021#include "public/cpp/fpdf_deleters.h"
Jane Liu4fd9a472017-06-01 18:56:09 -040022#include "public/fpdf_annot.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080023#include "public/fpdf_dataavail.h"
Lei Zhang453d96b2015-12-31 13:13:10 -080024#include "public/fpdf_edit.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080025#include "public/fpdf_ext.h"
26#include "public/fpdf_formfill.h"
Dan Sinclairddcb6e72017-04-05 10:30:33 -040027#include "public/fpdf_structtree.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080028#include "public/fpdf_text.h"
29#include "public/fpdfview.h"
Lei Zhang143959d2017-06-22 12:20:58 -070030#include "testing/image_diff/image_diff_png.h"
Lei Zhangbde53d22015-11-12 22:21:30 -080031#include "testing/test_support.h"
Jane Liu4fd9a472017-06-01 18:56:09 -040032#include "third_party/base/logging.h"
Tom Sepezd831dc72015-10-19 16:04:22 -070033
thestigf2b940c2016-10-13 06:48:47 -070034#ifdef _WIN32
35#include <io.h>
36#else
37#include <unistd.h>
38#endif
39
Tom Sepez452b4f32015-10-13 09:27:27 -070040#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -080041#include "v8/include/libplatform/libplatform.h"
Tom Sepez1ed8a212015-05-11 15:25:39 -070042#include "v8/include/v8.h"
Lei Zhang8241df72015-11-06 14:38:48 -080043#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070044
Cary Clark399be5b2016-03-14 16:51:29 -040045#ifdef PDF_ENABLE_SKIA
46#include "third_party/skia/include/core/SkPictureRecorder.h"
47#include "third_party/skia/include/core/SkStream.h"
48#endif
49
thestigf2b940c2016-10-13 06:48:47 -070050#ifdef _WIN32
51#define access _access
52#define snprintf _snprintf
53#define R_OK 4
54#endif
55
Vitaly Buka9e0177a2014-07-22 18:15:42 -070056enum OutputFormat {
57 OUTPUT_NONE,
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040058 OUTPUT_STRUCTURE,
dsinclairb63068f2016-06-16 07:58:09 -070059 OUTPUT_TEXT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070060 OUTPUT_PPM,
Tom Sepezaf18cb32015-02-05 15:06:01 -080061 OUTPUT_PNG,
Jane Liu4fd9a472017-06-01 18:56:09 -040062 OUTPUT_ANNOT,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070063#ifdef _WIN32
64 OUTPUT_BMP,
65 OUTPUT_EMF,
Lei Zhangd1c9b452017-05-05 17:21:36 -070066 OUTPUT_PS2,
67 OUTPUT_PS3,
Vitaly Buka9e0177a2014-07-22 18:15:42 -070068#endif
Cary Clark399be5b2016-03-14 16:51:29 -040069#ifdef PDF_ENABLE_SKIA
70 OUTPUT_SKP,
71#endif
Vitaly Buka9e0177a2014-07-22 18:15:42 -070072};
73
Tom Sepez5ee12d72014-12-17 16:24:01 -080074struct Options {
tsepezf09bdfa2016-04-18 16:08:26 -070075 Options()
Dan Sinclair3c67fbd2017-04-05 16:07:50 -040076 : show_config(false),
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -040077 show_metadata(false),
npme3c73152016-11-14 09:14:52 -080078 send_events(false),
79 pages(false),
stephanafa05e972017-01-02 06:19:41 -080080 md5(false),
npme3c73152016-11-14 09:14:52 -080081 output_format(OUTPUT_NONE) {}
Tom Sepez5ee12d72014-12-17 16:24:01 -080082
Tom Sepez2991d8d2016-01-15 16:02:48 -080083 bool show_config;
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -040084 bool show_metadata;
tsepezf09bdfa2016-04-18 16:08:26 -070085 bool send_events;
npme3c73152016-11-14 09:14:52 -080086 bool pages;
stephanafa05e972017-01-02 06:19:41 -080087 bool md5;
Tom Sepez5ee12d72014-12-17 16:24:01 -080088 OutputFormat output_format;
Tom Sepezdaa2e842015-01-29 15:44:37 -080089 std::string scale_factor_as_string;
Tom Sepez5ee12d72014-12-17 16:24:01 -080090 std::string exe_path;
91 std::string bin_directory;
Lei Zhang6f62d532015-09-23 15:31:44 -070092 std::string font_directory;
npmfa20cd52016-11-14 13:33:40 -080093 // 0-based page numbers to be rendered.
94 int first_page;
95 int last_page;
Tom Sepez5ee12d72014-12-17 16:24:01 -080096};
97
tonikitoo81d92f82016-09-21 12:44:56 -070098struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
99 // Hold a map of the currently loaded pages in order to avoid them
100 // to get loaded twice.
npmfa20cd52016-11-14 13:33:40 -0800101 std::map<int, FPDF_PAGE> loaded_pages;
tonikitoo81d92f82016-09-21 12:44:56 -0700102
103 // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
104 // make use of it.
npmfa20cd52016-11-14 13:33:40 -0800105 FPDF_FORMHANDLE form_handle;
tonikitoo81d92f82016-09-21 12:44:56 -0700106};
107
108static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
npmfa20cd52016-11-14 13:33:40 -0800109 FPDF_FORMFILLINFO* form_fill_info) {
110 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
tonikitoo81d92f82016-09-21 12:44:56 -0700111}
112
Tom Sepezaf18cb32015-02-05 15:06:01 -0800113static bool CheckDimensions(int stride, int width, int height) {
114 if (stride < 0 || width < 0 || height < 0)
115 return false;
116 if (height > 0 && width > INT_MAX / height)
117 return false;
118 return true;
119}
120
stephanafa05e972017-01-02 06:19:41 -0800121static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
122 // Get the MD5 hash and write it to stdout.
Lei Zhang143959d2017-06-22 12:20:58 -0700123 std::string hash =
124 GenerateMD5Base16(reinterpret_cast<const uint8_t*>(buffer), len);
125 printf("MD5:%s:%s\n", file_name, hash.c_str());
stephanafa05e972017-01-02 06:19:41 -0800126}
127
128static std::string WritePpm(const char* pdf_name,
129 int num,
130 const void* buffer_void,
131 int stride,
132 int width,
133 int height) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700134 const char* buffer = reinterpret_cast<const char*>(buffer_void);
135
Tom Sepezaf18cb32015-02-05 15:06:01 -0800136 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800137 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800138
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700139 int out_len = width * height;
140 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800141 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700142 out_len *= 3;
143
144 char filename[256];
145 snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
John Abd-El-Maleka548d302014-06-26 10:18:11 -0700146 FILE* fp = fopen(filename, "wb");
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700147 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800148 return "";
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700149 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
150 // Source data is B, G, R, unused.
151 // Dest data is R, G, B.
thestig514e8c92016-07-15 17:57:54 -0700152 std::vector<char> result(out_len);
Lei Zhange00660b2015-08-13 15:40:18 -0700153 for (int h = 0; h < height; ++h) {
154 const char* src_line = buffer + (stride * h);
thestig514e8c92016-07-15 17:57:54 -0700155 char* dest_line = result.data() + (width * h * 3);
Lei Zhange00660b2015-08-13 15:40:18 -0700156 for (int w = 0; w < width; ++w) {
157 // R
158 dest_line[w * 3] = src_line[(w * 4) + 2];
159 // G
160 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
161 // B
162 dest_line[(w * 3) + 2] = src_line[w * 4];
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700163 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700164 }
thestig514e8c92016-07-15 17:57:54 -0700165 fwrite(result.data(), out_len, 1, fp);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700166 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800167 return std::string(filename);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700168}
169
dsinclairb63068f2016-06-16 07:58:09 -0700170void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
171 char filename[256];
172 int chars_formatted =
173 snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
174 if (chars_formatted < 0 ||
175 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
176 fprintf(stderr, "Filename %s is too long\n", filename);
177 return;
178 }
179
180 FILE* fp = fopen(filename, "w");
181 if (!fp) {
182 fprintf(stderr, "Failed to open %s for output\n", filename);
183 return;
184 }
185
186 // Output in UTF32-LE.
187 uint32_t bom = 0x0000FEFF;
188 fwrite(&bom, sizeof(bom), 1, fp);
189
Tom Sepez1d17a042017-03-16 13:22:47 -0700190 std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
191 for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
192 uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
dsinclairb63068f2016-06-16 07:58:09 -0700193 fwrite(&c, sizeof(c), 1, fp);
194 }
dsinclairb63068f2016-06-16 07:58:09 -0700195 (void)fclose(fp);
196}
197
Jane Liu4fd9a472017-06-01 18:56:09 -0400198std::string AnnotSubtypeToString(FPDF_ANNOTATION_SUBTYPE subtype) {
199 if (subtype == FPDF_ANNOT_TEXT)
200 return "Text";
201 if (subtype == FPDF_ANNOT_LINK)
202 return "Link";
203 if (subtype == FPDF_ANNOT_FREETEXT)
204 return "FreeText";
205 if (subtype == FPDF_ANNOT_LINE)
206 return "Line";
207 if (subtype == FPDF_ANNOT_SQUARE)
208 return "Square";
209 if (subtype == FPDF_ANNOT_CIRCLE)
210 return "Circle";
211 if (subtype == FPDF_ANNOT_POLYGON)
212 return "Polygon";
213 if (subtype == FPDF_ANNOT_POLYLINE)
214 return "PolyLine";
215 if (subtype == FPDF_ANNOT_HIGHLIGHT)
216 return "Highlight";
217 if (subtype == FPDF_ANNOT_UNDERLINE)
218 return "Underline";
219 if (subtype == FPDF_ANNOT_SQUIGGLY)
220 return "Squiggly";
221 if (subtype == FPDF_ANNOT_STRIKEOUT)
222 return "StrikeOut";
223 if (subtype == FPDF_ANNOT_STAMP)
224 return "Stamp";
225 if (subtype == FPDF_ANNOT_CARET)
226 return "Caret";
227 if (subtype == FPDF_ANNOT_INK)
228 return "Ink";
229 if (subtype == FPDF_ANNOT_POPUP)
230 return "Popup";
231 if (subtype == FPDF_ANNOT_FILEATTACHMENT)
232 return "FileAttachment";
233 if (subtype == FPDF_ANNOT_SOUND)
234 return "Sound";
235 if (subtype == FPDF_ANNOT_MOVIE)
236 return "Movie";
237 if (subtype == FPDF_ANNOT_WIDGET)
238 return "Widget";
239 if (subtype == FPDF_ANNOT_SCREEN)
240 return "Screen";
241 if (subtype == FPDF_ANNOT_PRINTERMARK)
242 return "PrinterMark";
243 if (subtype == FPDF_ANNOT_TRAPNET)
244 return "TrapNet";
245 if (subtype == FPDF_ANNOT_WATERMARK)
246 return "Watermark";
247 if (subtype == FPDF_ANNOT_THREED)
248 return "3D";
249 if (subtype == FPDF_ANNOT_RICHMEDIA)
250 return "RichMedia";
251 if (subtype == FPDF_ANNOT_XFAWIDGET)
252 return "XFAWidget";
253 NOTREACHED();
254 return "";
255}
256
257void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
258 // Open the output text file.
259 char filename[256];
260 int chars_formatted =
261 snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
262 if (chars_formatted < 0 ||
263 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
264 fprintf(stderr, "Filename %s is too long\n", filename);
265 return;
266 }
267 FILE* fp = fopen(filename, "w");
268 if (!fp) {
269 fprintf(stderr, "Failed to open %s for output\n", filename);
270 return;
271 }
272
273 int annot_count = FPDFPage_GetAnnotCount(page);
274 fprintf(fp, "Number of annotations: %d\n\n", annot_count);
275
276 // Iterate through all annotations on this page.
277 for (int i = 0; i < annot_count; i++) {
278 // Retrieve the annotation object and its subtype.
279 fprintf(fp, "Annotation #%d:\n", i + 1);
Jane Liud60e9ad2017-06-26 11:28:36 -0400280 FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, i);
281 if (!annot) {
Jane Liu4fd9a472017-06-01 18:56:09 -0400282 fprintf(fp, "Failed to retrieve annotation!\n\n");
283 continue;
284 }
285 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
286 fprintf(fp, "Subtype: %s\n", AnnotSubtypeToString(subtype).c_str());
287
288 // Retrieve the annotation's color and interior color.
289 unsigned int R;
290 unsigned int G;
291 unsigned int B;
292 unsigned int A;
293 if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A)) {
294 fprintf(fp, "Failed to retrieve color.\n");
295 } else {
296 fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
297 }
298 if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_InteriorColor, &R, &G,
299 &B, &A)) {
300 fprintf(fp, "Failed to retrieve interior color.\n");
301 } else {
302 fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
303 }
304
305 // Retrieve the annotation's contents and author.
Jane Liu2e1a32b2017-07-06 12:01:25 -0400306 std::unique_ptr<unsigned short, pdfium::FreeDeleter> contents_key =
307 GetFPDFWideString(L"Contents");
Jane Liu4fd9a472017-06-01 18:56:09 -0400308 unsigned long len =
Jane Liu2e1a32b2017-07-06 12:01:25 -0400309 FPDFAnnot_GetStringValue(annot, contents_key.get(), nullptr, 0);
Jane Liu4fd9a472017-06-01 18:56:09 -0400310 std::vector<char> buf(len);
Jane Liu2e1a32b2017-07-06 12:01:25 -0400311 FPDFAnnot_GetStringValue(annot, contents_key.get(), buf.data(), len);
Jane Liu4fd9a472017-06-01 18:56:09 -0400312 fprintf(fp, "Content: %ls\n",
313 GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
314 .c_str());
Jane Liu2e1a32b2017-07-06 12:01:25 -0400315 std::unique_ptr<unsigned short, pdfium::FreeDeleter> author_key =
316 GetFPDFWideString(L"T");
317 len = FPDFAnnot_GetStringValue(annot, author_key.get(), nullptr, 0);
Jane Liu4fd9a472017-06-01 18:56:09 -0400318 buf.clear();
319 buf.resize(len);
Jane Liu2e1a32b2017-07-06 12:01:25 -0400320 FPDFAnnot_GetStringValue(annot, author_key.get(), buf.data(), len);
Jane Liu4fd9a472017-06-01 18:56:09 -0400321 fprintf(fp, "Author: %ls\n",
322 GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
323 .c_str());
324
325 // Retrieve the annotation's quadpoints if it is a markup annotation.
Jane Liu4fd9a472017-06-01 18:56:09 -0400326 if (FPDFAnnot_HasAttachmentPoints(annot)) {
Jane Liud60e9ad2017-06-26 11:28:36 -0400327 FS_QUADPOINTSF quadpoints = FPDFAnnot_GetAttachmentPoints(annot);
328 fprintf(fp, "Quadpoints: (%f, %f), (%f, %f), (%f, %f), (%f, %f)\n",
329 quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2,
330 quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4);
Jane Liu4fd9a472017-06-01 18:56:09 -0400331 }
332
333 // Retrieve the annotation's rectangle coordinates.
Jane Liud60e9ad2017-06-26 11:28:36 -0400334 FS_RECTF rect = FPDFAnnot_GetRect(annot);
335 fprintf(fp, "Rectangle: l - %f, b - %f, r - %f, t - %f\n\n", rect.left,
336 rect.bottom, rect.right, rect.top);
Jane Liu4fd9a472017-06-01 18:56:09 -0400337 }
338
339 (void)fclose(fp);
340}
341
stephanafa05e972017-01-02 06:19:41 -0800342static std::string WritePng(const char* pdf_name,
343 int num,
344 const void* buffer_void,
345 int stride,
346 int width,
347 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800348 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800349 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800350
351 std::vector<unsigned char> png_encoding;
352 const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
353 if (!image_diff_png::EncodeBGRAPNG(
354 buffer, width, height, stride, false, &png_encoding)) {
355 fprintf(stderr, "Failed to convert bitmap to PNG\n");
stephanafa05e972017-01-02 06:19:41 -0800356 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800357 }
358
359 char filename[256];
360 int chars_formatted = snprintf(
361 filename, sizeof(filename), "%s.%d.png", pdf_name, num);
362 if (chars_formatted < 0 ||
363 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
Cary Clark399be5b2016-03-14 16:51:29 -0400364 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800365 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800366 }
367
368 FILE* fp = fopen(filename, "wb");
369 if (!fp) {
370 fprintf(stderr, "Failed to open %s for output\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800371 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800372 }
373
374 size_t bytes_written = fwrite(
375 &png_encoding.front(), 1, png_encoding.size(), fp);
376 if (bytes_written != png_encoding.size())
377 fprintf(stderr, "Failed to write to %s\n", filename);
378
Lei Zhang5377ebf2015-09-23 14:52:53 -0700379 (void)fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800380 return std::string(filename);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800381}
382
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700383#ifdef _WIN32
stephanafa05e972017-01-02 06:19:41 -0800384static std::string WriteBmp(const char* pdf_name,
385 int num,
386 const void* buffer,
387 int stride,
388 int width,
389 int height) {
Tom Sepezaf18cb32015-02-05 15:06:01 -0800390 if (!CheckDimensions(stride, width, height))
stephanafa05e972017-01-02 06:19:41 -0800391 return "";
Tom Sepezaf18cb32015-02-05 15:06:01 -0800392
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700393 int out_len = stride * height;
394 if (out_len > INT_MAX / 3)
stephanafa05e972017-01-02 06:19:41 -0800395 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700396
397 char filename[256];
398 snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
399 FILE* fp = fopen(filename, "wb");
400 if (!fp)
stephanafa05e972017-01-02 06:19:41 -0800401 return "";
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700402
Nico Weber2827bdd2015-07-01 14:08:08 -0700403 BITMAPINFO bmi = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700404 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
405 bmi.bmiHeader.biWidth = width;
406 bmi.bmiHeader.biHeight = -height; // top-down image
407 bmi.bmiHeader.biPlanes = 1;
408 bmi.bmiHeader.biBitCount = 32;
409 bmi.bmiHeader.biCompression = BI_RGB;
410 bmi.bmiHeader.biSizeImage = 0;
411
Nico Weber2827bdd2015-07-01 14:08:08 -0700412 BITMAPFILEHEADER file_header = {};
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700413 file_header.bfType = 0x4d42;
414 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
415 file_header.bfOffBits = file_header.bfSize - out_len;
416
417 fwrite(&file_header, sizeof(file_header), 1, fp);
418 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
419 fwrite(buffer, out_len, 1, fp);
420 fclose(fp);
stephanafa05e972017-01-02 06:19:41 -0800421 return std::string(filename);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700422}
423
424void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700425 char filename[256];
426 snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
427
Lei Zhang5377ebf2015-09-23 14:52:53 -0700428 HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
Tom Sepezaf18cb32015-02-05 15:06:01 -0800429
Lei Zhangd1c9b452017-05-05 17:21:36 -0700430 int width = static_cast<int>(FPDF_GetPageWidth(page));
431 int height = static_cast<int>(FPDF_GetPageHeight(page));
Tom Sepezaf18cb32015-02-05 15:06:01 -0800432 HRGN rgn = CreateRectRgn(0, 0, width, height);
433 SelectClipRgn(dc, rgn);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700434 DeleteObject(rgn);
435
436 SelectObject(dc, GetStockObject(NULL_PEN));
437 SelectObject(dc, GetStockObject(WHITE_BRUSH));
438 // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
439 Rectangle(dc, 0, 0, width + 1, height + 1);
440
441 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
442 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
443
444 DeleteEnhMetaFile(CloseEnhMetaFile(dc));
445}
Lei Zhangd1c9b452017-05-05 17:21:36 -0700446
447int CALLBACK EnhMetaFileProc(HDC hdc,
448 HANDLETABLE* handle_table,
449 const ENHMETARECORD* record,
450 int objects_count,
451 LPARAM param) {
452 std::vector<const ENHMETARECORD*>& items =
453 *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
454 items.push_back(record);
455 return 1;
456}
457
458void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
459 char filename[256];
460 snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
461 FILE* fp = fopen(filename, "wb");
462 if (!fp)
463 return;
464
465 HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
466
467 int width = static_cast<int>(FPDF_GetPageWidth(page));
468 int height = static_cast<int>(FPDF_GetPageHeight(page));
469 FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
470 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
471
472 HENHMETAFILE emf = CloseEnhMetaFile(dc);
473 std::vector<const ENHMETARECORD*> items;
474 EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
475 for (const ENHMETARECORD* record : items) {
476 if (record->iType != EMR_GDICOMMENT)
477 continue;
478
479 const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
480 const char* data = reinterpret_cast<const char*>(comment->Data);
481 uint16_t size = *reinterpret_cast<const uint16_t*>(data);
482 fwrite(data + sizeof(uint16_t), size, 1, fp);
483 }
484 fclose(fp);
485 DeleteEnhMetaFile(emf);
486}
487#endif // _WIN32
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700488
Cary Clark399be5b2016-03-14 16:51:29 -0400489#ifdef PDF_ENABLE_SKIA
stephanafa05e972017-01-02 06:19:41 -0800490static std::string WriteSkp(const char* pdf_name,
491 int num,
492 SkPictureRecorder* recorder) {
Cary Clark399be5b2016-03-14 16:51:29 -0400493 char filename[256];
494 int chars_formatted =
495 snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
496
497 if (chars_formatted < 0 ||
498 static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
499 fprintf(stderr, "Filename %s is too long\n", filename);
stephanafa05e972017-01-02 06:19:41 -0800500 return "";
Cary Clark399be5b2016-03-14 16:51:29 -0400501 }
502
thestigb97e07e2016-09-28 22:16:40 -0700503 sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
Cary Clark399be5b2016-03-14 16:51:29 -0400504 SkFILEWStream wStream(filename);
505 picture->serialize(&wStream);
stephanafa05e972017-01-02 06:19:41 -0800506 return std::string(filename);
Cary Clark399be5b2016-03-14 16:51:29 -0400507}
508#endif
509
Tom Sepez58fb36a2016-02-01 10:32:14 -0800510// These example JS platform callback handlers are entirely optional,
511// and exist here to show the flow of information from a document back
512// to the embedder.
Tom Sepezbd932572016-01-29 09:10:41 -0800513int ExampleAppAlert(IPDF_JSPLATFORM*,
514 FPDF_WIDESTRING msg,
515 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800516 int type,
517 int icon) {
Tom Sepezbd932572016-01-29 09:10:41 -0800518 printf("%ls", GetPlatformWString(title).c_str());
npmfa20cd52016-11-14 13:33:40 -0800519 if (icon || type)
520 printf("[icon=%d,type=%d]", icon, type);
Tom Sepezbd932572016-01-29 09:10:41 -0800521 printf(": %ls\n", GetPlatformWString(msg).c_str());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700522 return 0;
523}
524
Tom Sepez58fb36a2016-02-01 10:32:14 -0800525int ExampleAppResponse(IPDF_JSPLATFORM*,
526 FPDF_WIDESTRING question,
527 FPDF_WIDESTRING title,
npmfa20cd52016-11-14 13:33:40 -0800528 FPDF_WIDESTRING default_value,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800529 FPDF_WIDESTRING label,
npmfa20cd52016-11-14 13:33:40 -0800530 FPDF_BOOL is_password,
Tom Sepez58fb36a2016-02-01 10:32:14 -0800531 void* response,
532 int length) {
533 printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
534 GetPlatformWString(title).c_str(),
535 GetPlatformWString(question).c_str(),
npmfa20cd52016-11-14 13:33:40 -0800536 GetPlatformWString(default_value).c_str(),
537 GetPlatformWString(label).c_str(), is_password, length);
Tom Sepez58fb36a2016-02-01 10:32:14 -0800538
539 // UTF-16, always LE regardless of platform.
540 uint8_t* ptr = static_cast<uint8_t*>(response);
541 ptr[0] = 'N';
542 ptr[1] = 0;
543 ptr[2] = 'o';
544 ptr[3] = 0;
545 return 4;
546}
547
npmfa20cd52016-11-14 13:33:40 -0800548void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
549 printf("Goto Page: %d\n", page_number);
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800550}
551
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800552void ExampleDocMail(IPDF_JSPLATFORM*,
553 void* mailData,
554 int length,
npmfa20cd52016-11-14 13:33:40 -0800555 FPDF_BOOL UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800556 FPDF_WIDESTRING To,
557 FPDF_WIDESTRING Subject,
558 FPDF_WIDESTRING CC,
559 FPDF_WIDESTRING BCC,
560 FPDF_WIDESTRING Msg) {
npmfa20cd52016-11-14 13:33:40 -0800561 printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
Tom Sepeze5fbd7a2016-01-29 17:05:08 -0800562 GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
563 GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
564 GetPlatformWString(Msg).c_str());
565}
566
Tom Sepezb7cb36a2015-02-13 16:54:48 -0800567void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700568 std::string feature = "Unknown";
569 switch (type) {
570 case FPDF_UNSP_DOC_XFAFORM:
571 feature = "XFA";
572 break;
573 case FPDF_UNSP_DOC_PORTABLECOLLECTION:
574 feature = "Portfolios_Packages";
575 break;
576 case FPDF_UNSP_DOC_ATTACHMENT:
577 case FPDF_UNSP_ANNOT_ATTACHMENT:
578 feature = "Attachment";
579 break;
580 case FPDF_UNSP_DOC_SECURITY:
581 feature = "Rights_Management";
582 break;
583 case FPDF_UNSP_DOC_SHAREDREVIEW:
584 feature = "Shared_Review";
585 break;
586 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
587 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
588 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
589 feature = "Shared_Form";
590 break;
591 case FPDF_UNSP_ANNOT_3DANNOT:
592 feature = "3D";
593 break;
594 case FPDF_UNSP_ANNOT_MOVIE:
595 feature = "Movie";
596 break;
597 case FPDF_UNSP_ANNOT_SOUND:
598 feature = "Sound";
599 break;
600 case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
601 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
602 feature = "Screen";
603 break;
604 case FPDF_UNSP_ANNOT_SIG:
605 feature = "Digital_Signature";
606 break;
607 }
608 printf("Unsupported feature: %s.\n", feature.c_str());
609}
610
Tom Sepez5ee12d72014-12-17 16:24:01 -0800611bool ParseCommandLine(const std::vector<std::string>& args,
thestig514e8c92016-07-15 17:57:54 -0700612 Options* options,
613 std::vector<std::string>* files) {
614 if (args.empty())
Tom Sepez5ee12d72014-12-17 16:24:01 -0800615 return false;
thestig514e8c92016-07-15 17:57:54 -0700616
Tom Sepez5ee12d72014-12-17 16:24:01 -0800617 options->exe_path = args[0];
Bo Xud44e3922014-12-19 02:27:25 -0800618 size_t cur_idx = 1;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800619 for (; cur_idx < args.size(); ++cur_idx) {
620 const std::string& cur_arg = args[cur_idx];
Tom Sepez2991d8d2016-01-15 16:02:48 -0800621 if (cur_arg == "--show-config") {
622 options->show_config = true;
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -0400623 } else if (cur_arg == "--show-metadata") {
624 options->show_metadata = true;
tsepezf09bdfa2016-04-18 16:08:26 -0700625 } else if (cur_arg == "--send-events") {
626 options->send_events = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800627 } else if (cur_arg == "--ppm") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800628 if (options->output_format != OUTPUT_NONE) {
629 fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
630 return false;
631 }
632 options->output_format = OUTPUT_PPM;
Tom Sepezaf18cb32015-02-05 15:06:01 -0800633 } else if (cur_arg == "--png") {
634 if (options->output_format != OUTPUT_NONE) {
635 fprintf(stderr, "Duplicate or conflicting --png argument\n");
636 return false;
637 }
638 options->output_format = OUTPUT_PNG;
dsinclairb63068f2016-06-16 07:58:09 -0700639 } else if (cur_arg == "--txt") {
640 if (options->output_format != OUTPUT_NONE) {
641 fprintf(stderr, "Duplicate or conflicting --txt argument\n");
642 return false;
643 }
644 options->output_format = OUTPUT_TEXT;
Jane Liu4fd9a472017-06-01 18:56:09 -0400645 } else if (cur_arg == "--annot") {
646 if (options->output_format != OUTPUT_NONE) {
647 fprintf(stderr, "Duplicate or conflicting --annot argument\n");
648 return false;
649 }
650 options->output_format = OUTPUT_ANNOT;
Cary Clark399be5b2016-03-14 16:51:29 -0400651#ifdef PDF_ENABLE_SKIA
652 } else if (cur_arg == "--skp") {
653 if (options->output_format != OUTPUT_NONE) {
654 fprintf(stderr, "Duplicate or conflicting --skp argument\n");
655 return false;
656 }
657 options->output_format = OUTPUT_SKP;
658#endif
Lei Zhang6f62d532015-09-23 15:31:44 -0700659 } else if (cur_arg.size() > 11 &&
660 cur_arg.compare(0, 11, "--font-dir=") == 0) {
661 if (!options->font_directory.empty()) {
662 fprintf(stderr, "Duplicate --font-dir argument\n");
663 return false;
664 }
665 options->font_directory = cur_arg.substr(11);
Vitaly Buka9e0177a2014-07-22 18:15:42 -0700666#ifdef _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500667 } else if (cur_arg == "--emf") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800668 if (options->output_format != OUTPUT_NONE) {
669 fprintf(stderr, "Duplicate or conflicting --emf argument\n");
670 return false;
671 }
672 options->output_format = OUTPUT_EMF;
Lei Zhangd1c9b452017-05-05 17:21:36 -0700673 } else if (cur_arg == "--ps2") {
674 if (options->output_format != OUTPUT_NONE) {
675 fprintf(stderr, "Duplicate or conflicting --ps2 argument\n");
676 return false;
677 }
678 options->output_format = OUTPUT_PS2;
679 } else if (cur_arg == "--ps3") {
680 if (options->output_format != OUTPUT_NONE) {
681 fprintf(stderr, "Duplicate or conflicting --ps3 argument\n");
682 return false;
683 }
684 options->output_format = OUTPUT_PS3;
Dan Sinclair50cce602016-02-24 09:51:16 -0500685 } else if (cur_arg == "--bmp") {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800686 if (options->output_format != OUTPUT_NONE) {
687 fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
688 return false;
689 }
690 options->output_format = OUTPUT_BMP;
Tom Sepez5ee12d72014-12-17 16:24:01 -0800691#endif // _WIN32
Dan Sinclair738b08c2016-03-01 14:45:20 -0500692
Tom Sepez452b4f32015-10-13 09:27:27 -0700693#ifdef PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -0800694#ifdef V8_USE_EXTERNAL_STARTUP_DATA
Dan Sinclair738b08c2016-03-01 14:45:20 -0500695 } else if (cur_arg.size() > 10 &&
696 cur_arg.compare(0, 10, "--bin-dir=") == 0) {
Tom Sepez5ee12d72014-12-17 16:24:01 -0800697 if (!options->bin_directory.empty()) {
698 fprintf(stderr, "Duplicate --bin-dir argument\n");
699 return false;
700 }
701 options->bin_directory = cur_arg.substr(10);
Tom Sepez5ee12d72014-12-17 16:24:01 -0800702#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -0700703#endif // PDF_ENABLE_V8
Dan Sinclair738b08c2016-03-01 14:45:20 -0500704
705 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
Tom Sepezdaa2e842015-01-29 15:44:37 -0800706 if (!options->scale_factor_as_string.empty()) {
707 fprintf(stderr, "Duplicate --scale argument\n");
708 return false;
709 }
710 options->scale_factor_as_string = cur_arg.substr(8);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400711 } else if (cur_arg == "--show-structure") {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400712 if (options->output_format != OUTPUT_NONE) {
713 fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
714 return false;
715 }
716 options->output_format = OUTPUT_STRUCTURE;
npme3c73152016-11-14 09:14:52 -0800717 } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
718 if (options->pages) {
719 fprintf(stderr, "Duplicate --pages argument\n");
720 return false;
721 }
722 options->pages = true;
npmfa20cd52016-11-14 13:33:40 -0800723 const std::string pages_string = cur_arg.substr(8);
724 size_t first_dash = pages_string.find("-");
725 if (first_dash == std::string::npos) {
726 std::stringstream(pages_string) >> options->first_page;
727 options->last_page = options->first_page;
npme3c73152016-11-14 09:14:52 -0800728 } else {
npmfa20cd52016-11-14 13:33:40 -0800729 std::stringstream(pages_string.substr(0, first_dash)) >>
730 options->first_page;
731 std::stringstream(pages_string.substr(first_dash + 1)) >>
732 options->last_page;
npme3c73152016-11-14 09:14:52 -0800733 }
stephanafa05e972017-01-02 06:19:41 -0800734 } else if (cur_arg == "--md5") {
735 options->md5 = true;
Tom Sepez2991d8d2016-01-15 16:02:48 -0800736 } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
737 fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
738 return false;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500739 } else {
Vitaly Buka8f2c3dc2014-08-20 10:32:36 -0700740 break;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500741 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700742 }
thestig514e8c92016-07-15 17:57:54 -0700743 for (size_t i = cur_idx; i < args.size(); i++)
Tom Sepez5ee12d72014-12-17 16:24:01 -0800744 files->push_back(args[i]);
thestig514e8c92016-07-15 17:57:54 -0700745
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700746 return true;
747}
748
npmfa20cd52016-11-14 13:33:40 -0800749FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700750 return true;
751}
752
npmfa20cd52016-11-14 13:33:40 -0800753void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700754
Lei Zhang54d91ec2017-06-16 19:08:02 -0700755void SendPageEvents(FPDF_FORMHANDLE form,
756 FPDF_PAGE page,
tsepezf09bdfa2016-04-18 16:08:26 -0700757 const std::string& events) {
758 auto lines = StringSplit(events, '\n');
759 for (auto line : lines) {
760 auto command = StringSplit(line, '#');
761 if (command[0].empty())
762 continue;
763 auto tokens = StringSplit(command[0], ',');
Nicolas Penae30f07a2017-05-18 18:37:46 -0400764 if (tokens[0] == "charcode") {
765 if (tokens.size() == 2) {
766 int keycode = atoi(tokens[1].c_str());
767 FORM_OnChar(form, page, keycode, 0);
768 } else {
769 fprintf(stderr, "charcode: bad args\n");
770 }
771 } else if (tokens[0] == "keycode") {
tsepezf09bdfa2016-04-18 16:08:26 -0700772 if (tokens.size() == 2) {
773 int keycode = atoi(tokens[1].c_str());
774 FORM_OnKeyDown(form, page, keycode, 0);
775 FORM_OnKeyUp(form, page, keycode, 0);
776 } else {
777 fprintf(stderr, "keycode: bad args\n");
778 }
779 } else if (tokens[0] == "mousedown") {
780 if (tokens.size() == 4) {
781 int x = atoi(tokens[2].c_str());
782 int y = atoi(tokens[3].c_str());
783 if (tokens[1] == "left")
784 FORM_OnLButtonDown(form, page, 0, x, y);
785#ifdef PDF_ENABLE_XFA
786 else if (tokens[1] == "right")
787 FORM_OnRButtonDown(form, page, 0, x, y);
788#endif
789 else
790 fprintf(stderr, "mousedown: bad button name\n");
791 } else {
792 fprintf(stderr, "mousedown: bad args\n");
793 }
794 } else if (tokens[0] == "mouseup") {
795 if (tokens.size() == 4) {
796 int x = atoi(tokens[2].c_str());
797 int y = atoi(tokens[3].c_str());
798 if (tokens[1] == "left")
799 FORM_OnLButtonUp(form, page, 0, x, y);
800#ifdef PDF_ENABLE_XFA
801 else if (tokens[1] == "right")
802 FORM_OnRButtonUp(form, page, 0, x, y);
803#endif
804 else
805 fprintf(stderr, "mouseup: bad button name\n");
806 } else {
807 fprintf(stderr, "mouseup: bad args\n");
808 }
809 } else if (tokens[0] == "mousemove") {
810 if (tokens.size() == 3) {
811 int x = atoi(tokens[1].c_str());
812 int y = atoi(tokens[2].c_str());
813 FORM_OnMouseMove(form, page, 0, x, y);
814 } else {
815 fprintf(stderr, "mousemove: bad args\n");
816 }
817 } else {
818 fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
819 }
820 }
821}
822
tonikitoo3e981582016-08-26 08:37:10 -0700823FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
824 FPDF_DOCUMENT doc,
825 int index) {
npmfa20cd52016-11-14 13:33:40 -0800826 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
827 ToPDFiumTestFormFillInfo(param);
828 auto& loaded_pages = form_fill_info->loaded_pages;
npmfa20cd52016-11-14 13:33:40 -0800829 auto iter = loaded_pages.find(index);
830 if (iter != loaded_pages.end())
tonikitoo3e981582016-08-26 08:37:10 -0700831 return iter->second;
832
833 FPDF_PAGE page = FPDF_LoadPage(doc, index);
834 if (!page)
835 return nullptr;
836
npmfa20cd52016-11-14 13:33:40 -0800837 FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
npmfa20cd52016-11-14 13:33:40 -0800838 FORM_OnAfterLoadPage(page, form_handle);
839 FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
npmfa20cd52016-11-14 13:33:40 -0800840 loaded_pages[index] = page;
tonikitoo3e981582016-08-26 08:37:10 -0700841 return page;
842}
843
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400844std::wstring ConvertToWString(const unsigned short* buf,
845 unsigned long buf_size) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400846 std::wstring result;
847 result.reserve(buf_size);
848 std::copy(buf, buf + buf_size, std::back_inserter(result));
849 return result;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400850}
851
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400852void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
853 static const size_t kBufSize = 1024;
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400854 unsigned short buf[kBufSize];
855 unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400856 printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400857
858 memset(buf, 0, sizeof(buf));
dan sinclaird9dad3a2017-04-06 14:44:02 -0400859 len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
860 if (len > 0)
861 printf(": '%ls'", ConvertToWString(buf, len).c_str());
862
863 memset(buf, 0, sizeof(buf));
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400864 len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
865 if (len > 0)
866 printf(" (%ls)", ConvertToWString(buf, len).c_str());
867 printf("\n");
868
869 for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
870 FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
871 // If the child is not an Element then this will return null. This can
872 // happen if the element is things like an object reference or a stream.
873 if (!sub_child)
874 continue;
875
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400876 DumpChildStructure(sub_child, indent + 1);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400877 }
878}
879
880void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
881 std::unique_ptr<void, FPDFStructTreeDeleter> tree(
882 FPDF_StructTree_GetForPage(page));
883 if (!tree) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400884 fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400885 return;
886 }
887
888 printf("Structure Tree for Page %d\n", page_idx);
889 for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
890 FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
891 if (!child) {
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400892 fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400893 continue;
894 }
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400895 DumpChildStructure(child, 0);
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400896 }
897 printf("\n\n");
898}
899
Jun Fangb553bcb2015-11-10 18:49:04 +0800900bool RenderPage(const std::string& name,
tonikitoo3e981582016-08-26 08:37:10 -0700901 FPDF_DOCUMENT doc,
Tom Sepez1d17a042017-03-16 13:22:47 -0700902 FPDF_FORMHANDLE form,
npmfa20cd52016-11-14 13:33:40 -0800903 FPDF_FORMFILLINFO_PDFiumTest& form_fill_info,
Jun Fangb553bcb2015-11-10 18:49:04 +0800904 const int page_index,
tsepezf09bdfa2016-04-18 16:08:26 -0700905 const Options& options,
906 const std::string& events) {
Tom Sepez1d17a042017-03-16 13:22:47 -0700907 std::unique_ptr<void, FPDFPageDeleter> page(
908 GetPageForIndex(&form_fill_info, doc, page_index));
909 if (!page.get())
Jun Fangb553bcb2015-11-10 18:49:04 +0800910 return false;
Dan Sinclair3c67fbd2017-04-05 16:07:50 -0400911 if (options.send_events)
912 SendPageEvents(form, page.get(), events);
913 if (options.output_format == OUTPUT_STRUCTURE) {
Dan Sinclairddcb6e72017-04-05 10:30:33 -0400914 DumpPageStructure(page.get(), page_index);
915 return true;
916 }
917
Tom Sepez1d17a042017-03-16 13:22:47 -0700918 std::unique_ptr<void, FPDFTextPageDeleter> text_page(
919 FPDFText_LoadPage(page.get()));
tsepezf09bdfa2016-04-18 16:08:26 -0700920
Jun Fangdf7f3662015-11-10 18:29:18 +0800921 double scale = 1.0;
thestig514e8c92016-07-15 17:57:54 -0700922 if (!options.scale_factor_as_string.empty())
Jun Fangdf7f3662015-11-10 18:29:18 +0800923 std::stringstream(options.scale_factor_as_string) >> scale;
thestig514e8c92016-07-15 17:57:54 -0700924
Tom Sepez1d17a042017-03-16 13:22:47 -0700925 int width = static_cast<int>(FPDF_GetPageWidth(page.get()) * scale);
926 int height = static_cast<int>(FPDF_GetPageHeight(page.get()) * scale);
927 int alpha = FPDFPage_HasTransparency(page.get()) ? 1 : 0;
928 std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
929 FPDFBitmap_Create(width, height, alpha));
930
thestige97ea032016-05-19 10:59:15 -0700931 if (bitmap) {
932 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
Tom Sepez1d17a042017-03-16 13:22:47 -0700933 FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
934 FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
935 FPDF_ANNOT);
Jun Fangdf7f3662015-11-10 18:29:18 +0800936
Tom Sepez1d17a042017-03-16 13:22:47 -0700937 FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
938 FPDF_ANNOT);
939 int stride = FPDFBitmap_GetStride(bitmap.get());
thestige97ea032016-05-19 10:59:15 -0700940 const char* buffer =
Tom Sepez1d17a042017-03-16 13:22:47 -0700941 reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
Jun Fangdf7f3662015-11-10 18:29:18 +0800942
stephanafa05e972017-01-02 06:19:41 -0800943 std::string&& image_file_name = "";
thestige97ea032016-05-19 10:59:15 -0700944 switch (options.output_format) {
Jun Fangdf7f3662015-11-10 18:29:18 +0800945#ifdef _WIN32
thestige97ea032016-05-19 10:59:15 -0700946 case OUTPUT_BMP:
stephanafa05e972017-01-02 06:19:41 -0800947 image_file_name =
948 WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700949 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800950
thestige97ea032016-05-19 10:59:15 -0700951 case OUTPUT_EMF:
Tom Sepez1d17a042017-03-16 13:22:47 -0700952 WriteEmf(page.get(), name.c_str(), page_index);
thestige97ea032016-05-19 10:59:15 -0700953 break;
Lei Zhangd1c9b452017-05-05 17:21:36 -0700954
955 case OUTPUT_PS2:
956 case OUTPUT_PS3:
957 WritePS(page.get(), name.c_str(), page_index);
958 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800959#endif
dsinclairb63068f2016-06-16 07:58:09 -0700960 case OUTPUT_TEXT:
Tom Sepez1d17a042017-03-16 13:22:47 -0700961 WriteText(page.get(), name.c_str(), page_index);
dsinclairb63068f2016-06-16 07:58:09 -0700962 break;
963
Jane Liu4fd9a472017-06-01 18:56:09 -0400964 case OUTPUT_ANNOT:
965 WriteAnnot(page.get(), name.c_str(), page_index);
966 break;
967
thestige97ea032016-05-19 10:59:15 -0700968 case OUTPUT_PNG:
stephanafa05e972017-01-02 06:19:41 -0800969 image_file_name =
970 WritePng(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700971 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800972
thestige97ea032016-05-19 10:59:15 -0700973 case OUTPUT_PPM:
stephanafa05e972017-01-02 06:19:41 -0800974 image_file_name =
975 WritePpm(name.c_str(), page_index, buffer, stride, width, height);
thestige97ea032016-05-19 10:59:15 -0700976 break;
Jun Fangdf7f3662015-11-10 18:29:18 +0800977
Cary Clark399be5b2016-03-14 16:51:29 -0400978#ifdef PDF_ENABLE_SKIA
thestige97ea032016-05-19 10:59:15 -0700979 case OUTPUT_SKP: {
980 std::unique_ptr<SkPictureRecorder> recorder(
thestigb97e07e2016-09-28 22:16:40 -0700981 reinterpret_cast<SkPictureRecorder*>(
Tom Sepez1d17a042017-03-16 13:22:47 -0700982 FPDF_RenderPageSkp(page.get(), width, height)));
983 FPDF_FFLRecord(form, recorder.get(), page.get(), 0, 0, width, height, 0,
984 0);
stephanafa05e972017-01-02 06:19:41 -0800985 image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
thestige97ea032016-05-19 10:59:15 -0700986 } break;
Cary Clark399be5b2016-03-14 16:51:29 -0400987#endif
thestige97ea032016-05-19 10:59:15 -0700988 default:
989 break;
990 }
Jun Fangdf7f3662015-11-10 18:29:18 +0800991
stephanafa05e972017-01-02 06:19:41 -0800992 // Write the filename and the MD5 of the buffer to stdout if we wrote a
993 // file.
994 if (options.md5 && image_file_name != "")
995 OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
thestige97ea032016-05-19 10:59:15 -0700996 } else {
997 fprintf(stderr, "Page was too large to be rendered.\n");
998 }
tonikitoo3e981582016-08-26 08:37:10 -0700999
npmfa20cd52016-11-14 13:33:40 -08001000 form_fill_info.loaded_pages.erase(page_index);
Tom Sepez1d17a042017-03-16 13:22:47 -07001001 FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
1002 FORM_OnBeforeClosePage(page.get(), form);
thestige97ea032016-05-19 10:59:15 -07001003 return !!bitmap;
Jun Fangdf7f3662015-11-10 18:29:18 +08001004}
1005
tsepezf09bdfa2016-04-18 16:08:26 -07001006void RenderPdf(const std::string& name,
1007 const char* pBuf,
1008 size_t len,
1009 const Options& options,
1010 const std::string& events) {
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001011 IPDF_JSPLATFORM platform_callbacks;
1012 memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
Tom Sepeza72e8e22015-10-07 10:17:53 -07001013 platform_callbacks.version = 3;
Tom Sepezb7cb36a2015-02-13 16:54:48 -08001014 platform_callbacks.app_alert = ExampleAppAlert;
Tom Sepez58fb36a2016-02-01 10:32:14 -08001015 platform_callbacks.app_response = ExampleAppResponse;
Tom Sepezb7cb36a2015-02-13 16:54:48 -08001016 platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
Tom Sepeze5fbd7a2016-01-29 17:05:08 -08001017 platform_callbacks.Doc_mail = ExampleDocMail;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001018
tonikitoo81d92f82016-09-21 12:44:56 -07001019 FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
Tom Sepezc46d0002015-11-30 15:46:36 -08001020#ifdef PDF_ENABLE_XFA
Tom Sepezed631382014-11-18 14:10:25 -08001021 form_callbacks.version = 2;
Tom Sepezc46d0002015-11-30 15:46:36 -08001022#else // PDF_ENABLE_XFA
1023 form_callbacks.version = 1;
1024#endif // PDF_ENABLE_XFA
tonikitoo3e981582016-08-26 08:37:10 -07001025 form_callbacks.FFI_GetPage = GetPageForIndex;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001026 form_callbacks.m_pJsPlatform = &platform_callbacks;
1027
1028 TestLoader loader(pBuf, len);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001029 FPDF_FILEACCESS file_access;
1030 memset(&file_access, '\0', sizeof(file_access));
John Abd-El-Malek7dc51722014-05-26 12:54:31 -07001031 file_access.m_FileLen = static_cast<unsigned long>(len);
Tom Sepezd831dc72015-10-19 16:04:22 -07001032 file_access.m_GetBlock = TestLoader::GetBlock;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001033 file_access.m_Param = &loader;
1034
1035 FX_FILEAVAIL file_avail;
1036 memset(&file_avail, '\0', sizeof(file_avail));
1037 file_avail.version = 1;
1038 file_avail.IsDataAvail = Is_Data_Avail;
1039
1040 FX_DOWNLOADHINTS hints;
1041 memset(&hints, '\0', sizeof(hints));
1042 hints.version = 1;
1043 hints.AddSegment = Add_Segment;
1044
Jun Fangdf7f3662015-11-10 18:29:18 +08001045 int nRet = PDF_DATA_NOTAVAIL;
Jun Fangb553bcb2015-11-10 18:49:04 +08001046 bool bIsLinearized = false;
Tom Sepez1d17a042017-03-16 13:22:47 -07001047 std::unique_ptr<void, FPDFDocumentDeleter> doc;
1048 std::unique_ptr<void, FPDFAvailDeleter> pdf_avail(
1049 FPDFAvail_Create(&file_avail, &file_access));
Tom Sepezc98895c2015-11-24 15:30:36 -08001050
Tom Sepez1d17a042017-03-16 13:22:47 -07001051 if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
1052 doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +08001053 if (doc) {
thestig514e8c92016-07-15 17:57:54 -07001054 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -07001055 nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
thestig514e8c92016-07-15 17:57:54 -07001056
Jun Fangdf7f3662015-11-10 18:29:18 +08001057 if (nRet == PDF_DATA_ERROR) {
1058 fprintf(stderr, "Unknown error in checking if doc was available.\n");
1059 return;
1060 }
Tom Sepez1d17a042017-03-16 13:22:47 -07001061 nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
Jun Fangdf7f3662015-11-10 18:29:18 +08001062 if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
1063 fprintf(stderr,
1064 "Error %d was returned in checking if form was available.\n",
1065 nRet);
1066 return;
1067 }
Jun Fangb553bcb2015-11-10 18:49:04 +08001068 bIsLinearized = true;
Jun Fangdf7f3662015-11-10 18:29:18 +08001069 }
1070 } else {
Tom Sepez1d17a042017-03-16 13:22:47 -07001071 doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
Jun Fangdf7f3662015-11-10 18:29:18 +08001072 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001073
Lei Zhang5377ebf2015-09-23 14:52:53 -07001074 if (!doc) {
Dan Sinclaireb815bf2015-10-27 13:08:41 -04001075 unsigned long err = FPDF_GetLastError();
1076 fprintf(stderr, "Load pdf docs unsuccessful: ");
1077 switch (err) {
1078 case FPDF_ERR_SUCCESS:
1079 fprintf(stderr, "Success");
1080 break;
1081 case FPDF_ERR_UNKNOWN:
1082 fprintf(stderr, "Unknown error");
1083 break;
1084 case FPDF_ERR_FILE:
1085 fprintf(stderr, "File not found or could not be opened");
1086 break;
1087 case FPDF_ERR_FORMAT:
1088 fprintf(stderr, "File not in PDF format or corrupted");
1089 break;
1090 case FPDF_ERR_PASSWORD:
1091 fprintf(stderr, "Password required or incorrect password");
1092 break;
1093 case FPDF_ERR_SECURITY:
1094 fprintf(stderr, "Unsupported security scheme");
1095 break;
1096 case FPDF_ERR_PAGE:
1097 fprintf(stderr, "Page not found or content error");
1098 break;
1099 default:
1100 fprintf(stderr, "Unknown error %ld", err);
1101 }
1102 fprintf(stderr, ".\n");
JUN FANG827a1722015-03-05 13:39:21 -08001103 return;
1104 }
1105
Tom Sepez1d17a042017-03-16 13:22:47 -07001106 (void)FPDF_GetDocPermissions(doc.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001107
Henrique Nakashimab73ce7b2017-06-19 16:04:34 -04001108 if (options.show_metadata) {
1109 const char* metaTags[] = {"Title", "Author", "Subject", "Keywords",
1110 "Creator", "Producer", "CreationDate", "ModDate"};
1111 for (const char* metaTag : metaTags) {
1112 char metaBuffer[4096];
1113 int len = FPDF_GetMetaText(doc.get(), metaTag, metaBuffer, 4096);
1114 printf("%-12s = %ls (%d bytes)\n", metaTag,
1115 GetPlatformWString(reinterpret_cast<unsigned short*>(metaBuffer))
1116 .c_str(),
1117 len);
1118 }
1119 }
1120
Tom Sepez1d17a042017-03-16 13:22:47 -07001121 std::unique_ptr<void, FPDFFormHandleDeleter> form(
1122 FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
1123 form_callbacks.form_handle = form.get();
tonikitoo81d92f82016-09-21 12:44:56 -07001124
Tom Sepezc46d0002015-11-30 15:46:36 -08001125#ifdef PDF_ENABLE_XFA
thestigb97e07e2016-09-28 22:16:40 -07001126 int doc_type = DOCTYPE_PDF;
Tom Sepez1d17a042017-03-16 13:22:47 -07001127 if (FPDF_HasXFAField(doc.get(), &doc_type) && doc_type != DOCTYPE_PDF &&
1128 !FPDF_LoadXFA(doc.get())) {
Lei Zhang5377ebf2015-09-23 14:52:53 -07001129 fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
Tom Sepez56451382014-12-05 13:30:51 -08001130 }
Tom Sepezc46d0002015-11-30 15:46:36 -08001131#endif // PDF_ENABLE_XFA
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001132
Tom Sepez1d17a042017-03-16 13:22:47 -07001133 FPDF_SetFormFieldHighlightColor(form.get(), 0, 0xFFE4DD);
1134 FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
1135 FORM_DoDocumentJSAction(form.get());
1136 FORM_DoDocumentOpenAction(form.get());
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001137
Lei Zhangd1c9b452017-05-05 17:21:36 -07001138#if _WIN32
1139 if (options.output_format == OUTPUT_PS2)
1140 FPDF_SetPrintPostscriptLevel(2);
1141 else if (options.output_format == OUTPUT_PS3)
1142 FPDF_SetPrintPostscriptLevel(3);
1143#endif
1144
Tom Sepez1d17a042017-03-16 13:22:47 -07001145 int page_count = FPDF_GetPageCount(doc.get());
Tom Sepez1ed8a212015-05-11 15:25:39 -07001146 int rendered_pages = 0;
1147 int bad_pages = 0;
npmfa20cd52016-11-14 13:33:40 -08001148 int first_page = options.pages ? options.first_page : 0;
1149 int last_page = options.pages ? options.last_page + 1 : page_count;
1150 for (int i = first_page; i < last_page; ++i) {
Jun Fangdf7f3662015-11-10 18:29:18 +08001151 if (bIsLinearized) {
1152 nRet = PDF_DATA_NOTAVAIL;
thestig514e8c92016-07-15 17:57:54 -07001153 while (nRet == PDF_DATA_NOTAVAIL)
Tom Sepez1d17a042017-03-16 13:22:47 -07001154 nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
thestig514e8c92016-07-15 17:57:54 -07001155
Jun Fangdf7f3662015-11-10 18:29:18 +08001156 if (nRet == PDF_DATA_ERROR) {
1157 fprintf(stderr, "Unknown error in checking if page %d is available.\n",
1158 i);
1159 return;
1160 }
1161 }
Tom Sepez1d17a042017-03-16 13:22:47 -07001162 if (RenderPage(name, doc.get(), form.get(), form_callbacks, i, options,
1163 events))
Jun Fangdf7f3662015-11-10 18:29:18 +08001164 ++rendered_pages;
thestig514e8c92016-07-15 17:57:54 -07001165 else
Lei Zhang5377ebf2015-09-23 14:52:53 -07001166 ++bad_pages;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001167 }
1168
Tom Sepez1d17a042017-03-16 13:22:47 -07001169 FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
Tom Sepez1ed8a212015-05-11 15:25:39 -07001170 fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
tsepez10b01bf2016-05-04 12:52:42 -07001171 if (bad_pages)
1172 fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001173}
1174
Tom Sepez2991d8d2016-01-15 16:02:48 -08001175static void ShowConfig() {
1176 std::string config;
1177 std::string maybe_comma;
1178#if PDF_ENABLE_V8
1179 config.append(maybe_comma);
1180 config.append("V8");
1181 maybe_comma = ",";
1182#endif // PDF_ENABLE_V8
1183#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1184 config.append(maybe_comma);
1185 config.append("V8_EXTERNAL");
1186 maybe_comma = ",";
1187#endif // V8_USE_EXTERNAL_STARTUP_DATA
1188#ifdef PDF_ENABLE_XFA
1189 config.append(maybe_comma);
1190 config.append("XFA");
1191 maybe_comma = ",";
1192#endif // PDF_ENABLE_XFA
dan sinclair00d40642017-01-30 19:48:54 -08001193#ifdef PDF_ENABLE_ASAN
1194 config.append(maybe_comma);
1195 config.append("ASAN");
1196 maybe_comma = ",";
1197#endif // PDF_ENABLE_ASAN
Tom Sepez2991d8d2016-01-15 16:02:48 -08001198 printf("%s\n", config.c_str());
1199}
1200
thestig514e8c92016-07-15 17:57:54 -07001201static const char kUsageString[] =
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001202 "Usage: pdfium_test [OPTION] [FILE]...\n"
Tom Sepez2991d8d2016-01-15 16:02:48 -08001203 " --show-config - print build options and exit\n"
Dan Sinclairddcb6e72017-04-05 10:30:33 -04001204 " --show-structure - print the structure elements from the document\n"
tsepez10b01bf2016-05-04 12:52:42 -07001205 " --send-events - send input described by .evt file\n"
Lei Zhang6f62d532015-09-23 15:31:44 -07001206 " --bin-dir=<path> - override path to v8 external data\n"
1207 " --font-dir=<path> - override path to external fonts\n"
1208 " --scale=<number> - scale output size by number (e.g. 0.5)\n"
npmfa20cd52016-11-14 13:33:40 -08001209 " --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001210#ifdef _WIN32
1211 " --bmp - write page images <pdf-name>.<page-number>.bmp\n"
1212 " --emf - write page meta files <pdf-name>.<page-number>.emf\n"
Lei Zhangd1c9b452017-05-05 17:21:36 -07001213 " --ps2 - write page raw PostScript (Lvl 2) <pdf-name>.<page-number>.ps\n"
1214 " --ps3 - write page raw PostScript (Lvl 3) <pdf-name>.<page-number>.ps\n"
Tom Sepezc46d0002015-11-30 15:46:36 -08001215#endif // _WIN32
thestig514e8c92016-07-15 17:57:54 -07001216 " --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001217 " --png - write page images <pdf-name>.<page-number>.png\n"
thestig514e8c92016-07-15 17:57:54 -07001218 " --ppm - write page images <pdf-name>.<page-number>.ppm\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001219#ifdef PDF_ENABLE_SKIA
1220 " --skp - write page images <pdf-name>.<page-number>.skp\n"
1221#endif
stephanafa05e972017-01-02 06:19:41 -08001222 " --md5 - write output image paths and their md5 hashes to stdout.\n"
Cary Clark399be5b2016-03-14 16:51:29 -04001223 "";
Tom Sepez23b4e3f2015-02-06 16:05:23 -08001224
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001225int main(int argc, const char* argv[]) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001226 std::vector<std::string> args(argv, argv + argc);
1227 Options options;
thestig514e8c92016-07-15 17:57:54 -07001228 std::vector<std::string> files;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001229 if (!ParseCommandLine(args, &options, &files)) {
thestig514e8c92016-07-15 17:57:54 -07001230 fprintf(stderr, "%s", kUsageString);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001231 return 1;
1232 }
1233
Tom Sepez2991d8d2016-01-15 16:02:48 -08001234 if (options.show_config) {
1235 ShowConfig();
1236 return 0;
1237 }
1238
1239 if (files.empty()) {
1240 fprintf(stderr, "No input files.\n");
1241 return 1;
1242 }
1243
Tom Sepez452b4f32015-10-13 09:27:27 -07001244#ifdef PDF_ENABLE_V8
Tom Sepezd831dc72015-10-19 16:04:22 -07001245 v8::Platform* platform;
Tom Sepez5ee12d72014-12-17 16:24:01 -08001246#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1247 v8::StartupData natives;
1248 v8::StartupData snapshot;
Tom Sepezd831dc72015-10-19 16:04:22 -07001249 InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
1250 &snapshot, &platform);
1251#else // V8_USE_EXTERNAL_STARTUP_DATA
jochen9e077d22016-06-09 02:51:13 -07001252 InitializeV8ForPDFium(options.exe_path, &platform);
Tom Sepez5ee12d72014-12-17 16:24:01 -08001253#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001254#endif // PDF_ENABLE_V8
Tom Sepez5ee12d72014-12-17 16:24:01 -08001255
Tom Sepeza72e8e22015-10-07 10:17:53 -07001256 FPDF_LIBRARY_CONFIG config;
1257 config.version = 2;
1258 config.m_pUserFontPaths = nullptr;
1259 config.m_pIsolate = nullptr;
1260 config.m_v8EmbedderSlot = 0;
1261
1262 const char* path_array[2];
1263 if (!options.font_directory.empty()) {
Lei Zhang6f62d532015-09-23 15:31:44 -07001264 path_array[0] = options.font_directory.c_str();
1265 path_array[1] = nullptr;
Lei Zhang6f62d532015-09-23 15:31:44 -07001266 config.m_pUserFontPaths = path_array;
Lei Zhang6f62d532015-09-23 15:31:44 -07001267 }
Tom Sepeza72e8e22015-10-07 10:17:53 -07001268 FPDF_InitLibraryWithConfig(&config);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001269
npmfa20cd52016-11-14 13:33:40 -08001270 UNSUPPORT_INFO unsupported_info;
1271 memset(&unsupported_info, '\0', sizeof(unsupported_info));
1272 unsupported_info.version = 1;
1273 unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001274
npmfa20cd52016-11-14 13:33:40 -08001275 FSDK_SetUnSpObjProcessHandler(&unsupported_info);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001276
thestig514e8c92016-07-15 17:57:54 -07001277 for (const std::string& filename : files) {
Tom Sepez5ee12d72014-12-17 16:24:01 -08001278 size_t file_length = 0;
Tom Sepez0aa35312016-01-06 10:16:32 -08001279 std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
1280 GetFileContents(filename.c_str(), &file_length);
tsepezf09bdfa2016-04-18 16:08:26 -07001281 if (!file_contents)
1282 continue;
tsepez10b01bf2016-05-04 12:52:42 -07001283 fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
tsepezf09bdfa2016-04-18 16:08:26 -07001284 std::string events;
1285 if (options.send_events) {
1286 std::string event_filename = filename;
1287 size_t event_length = 0;
1288 size_t extension_pos = event_filename.find(".pdf");
1289 if (extension_pos != std::string::npos) {
1290 event_filename.replace(extension_pos, 4, ".evt");
thestigf2b940c2016-10-13 06:48:47 -07001291 if (access(event_filename.c_str(), R_OK) == 0) {
1292 fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
1293 std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
1294 GetFileContents(event_filename.c_str(), &event_length);
1295 if (event_contents) {
1296 fprintf(stderr, "Sending events from: %s\n",
1297 event_filename.c_str());
1298 events = std::string(event_contents.get(), event_length);
1299 }
tsepezf09bdfa2016-04-18 16:08:26 -07001300 }
1301 }
1302 }
1303 RenderPdf(filename, file_contents.get(), file_length, options, events);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001304 }
1305
1306 FPDF_DestroyLibrary();
Tom Sepez452b4f32015-10-13 09:27:27 -07001307#ifdef PDF_ENABLE_V8
John Abd-El-Malekb045ed22015-02-10 09:15:12 -08001308 v8::V8::ShutdownPlatform();
1309 delete platform;
thestigc08cd7a2016-06-27 09:47:59 -07001310
1311#ifdef V8_USE_EXTERNAL_STARTUP_DATA
1312 free(const_cast<char*>(natives.data));
1313 free(const_cast<char*>(snapshot.data));
1314#endif // V8_USE_EXTERNAL_STARTUP_DATA
Tom Sepez452b4f32015-10-13 09:27:27 -07001315#endif // PDF_ENABLE_V8
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001316
1317 return 0;
1318}