XFA: merge patch from issue 801913002 and 804463003
[pdfium.git] / samples / pdfium_test.cc
1 // 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 #include <wchar.h>
10
11 #include <list>
12 #include <string>
13 #include <utility>
14
15 #include "../fpdfsdk/include/fpdf_dataavail.h"
16 #include "../fpdfsdk/include/fpdf_ext.h"
17 #include "../fpdfsdk/include/fpdfformfill.h"
18 #include "../fpdfsdk/include/fpdftext.h"
19 #include "../fpdfsdk/include/fpdfview.h"
20 #include "../core/include/fxcrt/fx_system.h"
21 #include "v8/include/v8.h"
22
23 #ifdef _WIN32
24   #define snprintf _snprintf
25 #endif
26
27 enum OutputFormat {
28   OUTPUT_NONE,
29   OUTPUT_PPM,
30 #ifdef _WIN32
31   OUTPUT_BMP,
32   OUTPUT_EMF,
33 #endif
34 };
35
36 static void WritePpm(const char* pdf_name, int num, const void* buffer_void,
37                      int stride, int width, int height) {
38   const char* buffer = reinterpret_cast<const char*>(buffer_void);
39
40   if (stride < 0 || width < 0 || height < 0)
41     return;
42   if (height > 0 && width > INT_MAX / height)
43     return;
44   int out_len = width * height;
45   if (out_len > INT_MAX / 3)
46     return;
47   out_len *= 3;
48
49   char filename[256];
50   snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
51   FILE* fp = fopen(filename, "wb");
52   if (!fp)
53     return;
54   fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
55   // Source data is B, G, R, unused.
56   // Dest data is R, G, B.
57   char* result = new char[out_len];
58   if (result) {
59     for (int h = 0; h < height; ++h) {
60       const char* src_line = buffer + (stride * h);
61       char* dest_line = result + (width * h * 3);
62       for (int w = 0; w < width; ++w) {
63         // R
64         dest_line[w * 3] = src_line[(w * 4) + 2];
65         // G
66         dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
67         // B
68         dest_line[(w * 3) + 2] = src_line[w * 4];
69       }
70     }
71     fwrite(result, out_len, 1, fp);
72     delete [] result;
73   }
74   fclose(fp);
75 }
76
77 #ifdef _WIN32
78 static void WriteBmp(const char* pdf_name, int num, const void* buffer,
79                      int stride, int width, int height) {
80   if (stride < 0 || width < 0 || height < 0)
81     return;
82   if (height > 0 && width > INT_MAX / height)
83     return;
84   int out_len = stride * height;
85   if (out_len > INT_MAX / 3)
86     return;
87
88   char filename[256];
89   snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
90   FILE* fp = fopen(filename, "wb");
91   if (!fp)
92     return;
93
94   BITMAPINFO bmi = {0};
95   bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
96   bmi.bmiHeader.biWidth = width;
97   bmi.bmiHeader.biHeight = -height;  // top-down image
98   bmi.bmiHeader.biPlanes = 1;
99   bmi.bmiHeader.biBitCount = 32;
100   bmi.bmiHeader.biCompression = BI_RGB;
101   bmi.bmiHeader.biSizeImage = 0;
102
103   BITMAPFILEHEADER file_header = {0};
104   file_header.bfType = 0x4d42;
105   file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
106   file_header.bfOffBits = file_header.bfSize - out_len;
107
108   fwrite(&file_header, sizeof(file_header), 1, fp);
109   fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
110   fwrite(buffer, out_len, 1, fp);
111   fclose(fp);
112 }
113
114 void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
115   int width = static_cast<int>(FPDF_GetPageWidth(page));
116   int height = static_cast<int>(FPDF_GetPageHeight(page));
117
118   char filename[256];
119   snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
120
121   HDC dc = CreateEnhMetaFileA(NULL, filename, NULL, NULL);
122   
123   HRGN rgn = CreateRectRgn(0, 0, width, height); 
124   SelectClipRgn(dc, rgn); 
125   DeleteObject(rgn);
126
127   SelectObject(dc, GetStockObject(NULL_PEN));
128   SelectObject(dc, GetStockObject(WHITE_BRUSH));
129   // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
130   Rectangle(dc, 0, 0, width + 1, height + 1);
131
132   FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
133                   FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
134
135   DeleteEnhMetaFile(CloseEnhMetaFile(dc));
136 }
137 #endif
138
139 int Form_Alert(IPDF_JSPLATFORM*, FPDF_WIDESTRING msg, FPDF_WIDESTRING,
140                int, int) {
141   // Deal with differences between UTF16LE and wchar_t on this platform.
142   size_t characters = 0;
143   while (msg[characters]) {
144     ++characters;
145   }
146   wchar_t* platform_string =
147       (wchar_t*)malloc((characters + 1) * sizeof(wchar_t));
148   for (size_t i = 0; i < characters + 1; ++i) {
149     unsigned char* ptr = (unsigned char*)&msg[i];
150     platform_string[i] = ptr[0] + 256 * ptr[1];
151   }
152   printf("Alert: %ls\n", platform_string);
153   free(platform_string);
154   return 0;
155 }
156
157 void Unsupported_Handler(UNSUPPORT_INFO*, int type) {
158   std::string feature = "Unknown";
159   switch (type) {
160     case FPDF_UNSP_DOC_XFAFORM:
161       feature = "XFA";
162       break;
163     case FPDF_UNSP_DOC_PORTABLECOLLECTION:
164       feature = "Portfolios_Packages";
165       break;
166     case FPDF_UNSP_DOC_ATTACHMENT:
167     case FPDF_UNSP_ANNOT_ATTACHMENT:
168       feature = "Attachment";
169       break;
170     case FPDF_UNSP_DOC_SECURITY:
171       feature = "Rights_Management";
172       break;
173     case FPDF_UNSP_DOC_SHAREDREVIEW:
174       feature = "Shared_Review";
175       break;
176     case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
177     case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
178     case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
179       feature = "Shared_Form";
180       break;
181     case FPDF_UNSP_ANNOT_3DANNOT:
182       feature = "3D";
183       break;
184     case FPDF_UNSP_ANNOT_MOVIE:
185       feature = "Movie";
186       break;
187     case FPDF_UNSP_ANNOT_SOUND:
188       feature = "Sound";
189       break;
190     case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
191     case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
192       feature = "Screen";
193       break;
194     case FPDF_UNSP_ANNOT_SIG:
195       feature = "Digital_Signature";
196       break;
197   }
198   printf("Unsupported feature: %s.\n", feature.c_str());
199 }
200
201 bool ParseCommandLine(int argc, const char* argv[], OutputFormat* output_format,
202                       std::list<const char*>* files) {
203   *output_format = OUTPUT_NONE;
204   files->clear();
205
206   int cur_arg = 1;
207   for (; cur_arg < argc; ++cur_arg) {
208     if (strcmp(argv[cur_arg], "--ppm") == 0)
209       *output_format = OUTPUT_PPM;
210 #ifdef _WIN32
211     else if (strcmp(argv[cur_arg], "--emf") == 0)
212       *output_format = OUTPUT_EMF;
213     else if (strcmp(argv[cur_arg], "--bmp") == 0)
214       *output_format = OUTPUT_BMP;
215 #endif
216     else
217       break;
218   }
219
220   if (cur_arg > 2)  // Multiple options.
221     return false;
222
223   if (cur_arg >= argc)  // No input files.
224     return false;
225
226   for (int i = cur_arg; i < argc; i++)
227     files->push_back(argv[i]);
228
229   return true;
230 }
231
232 class TestLoader {
233  public:
234   TestLoader(const char* pBuf, size_t len);
235
236   const char* m_pBuf;
237   size_t m_Len;
238 };
239
240 TestLoader::TestLoader(const char* pBuf, size_t len)
241     : m_pBuf(pBuf), m_Len(len) {
242 }
243
244 int Get_Block(void* param, unsigned long pos, unsigned char* pBuf,
245               unsigned long size) {
246   TestLoader* pLoader = (TestLoader*) param;
247   if (pos + size < pos || pos + size > pLoader->m_Len) return 0;
248   memcpy(pBuf, pLoader->m_pBuf + pos, size);
249   return 1;
250 }
251
252 bool Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
253   return true;
254 }
255
256 void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
257 }
258
259 void RenderPdf(const char* name, const char* pBuf, size_t len,
260                OutputFormat format) {
261   printf("Rendering PDF file %s.\n", name);
262
263   IPDF_JSPLATFORM platform_callbacks;
264   memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
265   platform_callbacks.version = 1;
266   platform_callbacks.app_alert = Form_Alert;
267
268   FPDF_FORMFILLINFO form_callbacks;
269   memset(&form_callbacks, '\0', sizeof(form_callbacks));
270   form_callbacks.version = 2;
271   form_callbacks.m_pJsPlatform = &platform_callbacks;
272
273   TestLoader loader(pBuf, len);
274
275   FPDF_FILEACCESS file_access;
276   memset(&file_access, '\0', sizeof(file_access));
277   file_access.m_FileLen = static_cast<unsigned long>(len);
278   file_access.m_GetBlock = Get_Block;
279   file_access.m_Param = &loader;
280
281   FX_FILEAVAIL file_avail;
282   memset(&file_avail, '\0', sizeof(file_avail));
283   file_avail.version = 1;
284   file_avail.IsDataAvail = Is_Data_Avail;
285
286   FX_DOWNLOADHINTS hints;
287   memset(&hints, '\0', sizeof(hints));
288   hints.version = 1;
289   hints.AddSegment = Add_Segment;
290
291   FPDF_DOCUMENT doc;
292   FPDF_AVAIL pdf_avail = FPDFAvail_Create(&file_avail, &file_access);
293
294   (void) FPDFAvail_IsDocAvail(pdf_avail, &hints);
295
296   if (!FPDFAvail_IsLinearized(pdf_avail)) {
297     printf("Non-linearized path...\n");
298     doc = FPDF_LoadCustomDocument(&file_access, NULL);
299   } else {
300     printf("Linearized path...\n");
301     doc = FPDFAvail_GetDocument(pdf_avail, NULL);
302   }
303
304   (void) FPDF_GetDocPermissions(doc);
305   (void) FPDFAvail_IsFormAvail(pdf_avail, &hints);
306
307   FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(doc, &form_callbacks);
308   if (!FPDF_LoadXFA(doc)) {
309     printf("LoadXFA unsuccessful, continuing anyway.\n");
310   }
311   FPDF_SetFormFieldHighlightColor(form, 0, 0xFFE4DD);
312   FPDF_SetFormFieldHighlightAlpha(form, 100);
313
314   int first_page = FPDFAvail_GetFirstPageNum(doc);
315   (void) FPDFAvail_IsPageAvail(pdf_avail, first_page, &hints);
316
317   int page_count = FPDF_GetPageCount(doc);
318   for (int i = 0; i < page_count; ++i) {
319     (void) FPDFAvail_IsPageAvail(pdf_avail, i, &hints);
320   }
321
322   FORM_DoDocumentJSAction(form);
323   FORM_DoDocumentOpenAction(form);
324
325   size_t rendered_pages = 0;
326   size_t bad_pages = 0;
327   for (int i = 0; i < page_count; ++i) {
328     FPDF_PAGE page = FPDF_LoadPage(doc, i);
329     if (!page) {
330         bad_pages ++;
331         continue;
332     }
333     FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
334     FORM_OnAfterLoadPage(page, form);
335     FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_OPEN);
336
337     int width = static_cast<int>(FPDF_GetPageWidth(page));
338     int height = static_cast<int>(FPDF_GetPageHeight(page));
339     FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, 0);
340     FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF);
341
342     FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
343     rendered_pages ++;
344
345     FPDF_FFLDraw(form, bitmap, page, 0, 0, width, height, 0, 0);
346     int stride = FPDFBitmap_GetStride(bitmap);
347     const char* buffer =
348         reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap));
349
350     switch (format) {
351 #ifdef _WIN32
352       case OUTPUT_BMP:
353         WriteBmp(name, i, buffer, stride, width, height);
354         break;
355
356       case OUTPUT_EMF:
357         WriteEmf(page, name, i);
358         break;
359 #endif
360       case OUTPUT_PPM:
361         WritePpm(name, i, buffer, stride, width, height);
362         break;
363       default:
364         break;
365     }
366
367     FPDFBitmap_Destroy(bitmap);
368
369     FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
370     FORM_OnBeforeClosePage(page, form);
371     FPDFText_ClosePage(text_page);
372     FPDF_ClosePage(page);
373   }
374
375   FORM_DoDocumentAAction(form, FPDFDOC_AACTION_WC);
376   FPDF_CloseDocument(doc);
377   FPDFDOC_ExitFormFillEnvironment(form);
378   FPDFAvail_Destroy(pdf_avail);
379
380   printf("Loaded, parsed and rendered %" PRIuS " pages.\n", rendered_pages);
381   printf("Skipped %" PRIuS " bad pages.\n", bad_pages);
382 }
383
384 int main(int argc, const char* argv[]) {
385   v8::V8::InitializeICU();
386   OutputFormat format = OUTPUT_NONE;
387   std::list<const char*> files;
388   if (!ParseCommandLine(argc, argv, &format, &files)) {
389     printf("Usage: pdfium_test [OPTION] [FILE]...\n");
390     printf("--ppm    write page images <pdf-name>.<page-number>.ppm\n");
391 #ifdef _WIN32
392     printf("--bmp    write page images <pdf-name>.<page-number>.bmp\n");
393     printf("--emf    write page meta files <pdf-name>.<page-number>.emf\n");
394 #endif
395     return 1;
396   }
397
398   FPDF_InitLibrary();
399
400   UNSUPPORT_INFO unsuppored_info;
401   memset(&unsuppored_info, '\0', sizeof(unsuppored_info));
402   unsuppored_info.version = 1;
403   unsuppored_info.FSDK_UnSupport_Handler = Unsupported_Handler;
404
405   FSDK_SetUnSpObjProcessHandler(&unsuppored_info);
406
407   while (!files.empty()) {
408     const char* filename = files.front();
409     files.pop_front();
410     FILE* file = fopen(filename, "rb");
411     if (!file) {
412       fprintf(stderr, "Failed to open: %s\n", filename);
413       continue;
414     }
415     (void) fseek(file, 0, SEEK_END);
416     size_t len = ftell(file);
417     (void) fseek(file, 0, SEEK_SET);
418     char* pBuf = (char*) malloc(len);
419     size_t ret = fread(pBuf, 1, len, file);
420     (void) fclose(file);
421     if (ret != len) {
422       fprintf(stderr, "Failed to read: %s\n", filename);
423     } else {
424       RenderPdf(filename, pBuf, len, format);
425     }
426     free(pBuf);
427   }
428
429   FPDF_DestroyLibrary();
430
431   return 0;
432 }