poppler: use new PDF version methods
[swftools.git] / lib / pdf / pdf.cc
1 #include <stdio.h>
2 #include <string.h>
3 #include "../gfxdevice.h"
4 #include "../gfxsource.h"
5 #include "../devices/rescale.h"
6 #include "../log.h"
7 #include "../../config.h"
8 #ifdef HAVE_POPPLER
9   #include <poppler-config.h>
10 #else
11   #include "xpdf/config.h"
12 #endif
13 #include "GlobalParams.h"
14 #include "InfoOutputDev.h"
15 #include "GFXOutputDev.h"
16 #include "FullBitmapOutputDev.h"
17 #include "BitmapOutputDev.h"
18 #include "../mem.h"
19 #include "pdf.h"
20 #define NO_ARGPARSER
21 #include "../args.h"
22
23 static double zoom = 72; /* xpdf: 86 */
24 static int zoomtowidth = 0;
25 static int multiply = 1;
26 static char* global_page_range = 0;
27
28 static int globalparams_count=0;
29
30 typedef struct _pdf_page_info
31 {
32     int xMin, yMin, xMax, yMax;
33     int width,height;
34     int number_of_images;
35     int number_of_links;
36     int number_of_fonts;
37     char has_info;
38 } pdf_page_info_t;
39
40 typedef struct _pdf_doc_internal
41 {
42     char config_bitmap_optimizing;
43     char config_full_bitmap_optimizing;
44     char config_print;
45     gfxparams_t* parameters;
46
47     int protect;
48     int nocopy;
49     int noprint;
50     
51     PDFDoc*doc;
52     Object docinfo;
53     InfoOutputDev*info;
54
55     pdf_page_info_t*pages;
56     char*filename;
57
58     /* page map */
59     int*pagemap;
60     int pagemap_size;
61     int pagemap_pos;
62
63     gfxsource_t*parent;
64 } pdf_doc_internal_t;
65
66 typedef struct _pdf_page_internal
67 {
68 } pdf_page_internal_t;
69
70 typedef struct _dev_output_internal
71 {
72     CommonOutputDev*outputDev;
73 } dev_output_internal_t;
74
75
76 typedef struct _gfxsource_internal
77 {
78     gfxparams_t* parameters;
79 } gfxsource_internal_t;
80
81
82 static const char* dirseparator()
83 {
84 #ifdef WIN32
85     return "\\";
86 #else
87     return "/";
88 #endif
89 }
90
91 void pdfpage_destroy(gfxpage_t*pdf_page)
92 {
93     pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
94     free(pdf_page->internal);pdf_page->internal = 0;
95     free(pdf_page);pdf_page=0;
96 }
97
98 static void render2(gfxpage_t*page, gfxdevice_t*dev, int x,int y, int x1,int y1,int x2,int y2)
99 {
100     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
101     gfxsource_internal_t*i = (gfxsource_internal_t*)pi->parent->internal;
102
103     if(!pi->config_print && pi->nocopy) {msg("<fatal> PDF disallows copying");exit(0);}
104     if(pi->config_print && pi->noprint) {msg("<fatal> PDF disallows printing");exit(0);}
105
106     CommonOutputDev*outputDev = 0;
107     if(pi->config_full_bitmap_optimizing) {
108         FullBitmapOutputDev*d = new FullBitmapOutputDev(pi->info, pi->doc);
109         outputDev = (CommonOutputDev*)d;
110     } else if(pi->config_bitmap_optimizing) {
111         BitmapOutputDev*d = new BitmapOutputDev(pi->info, pi->doc);
112         outputDev = (CommonOutputDev*)d;
113     } else {
114         GFXOutputDev*d = new GFXOutputDev(pi->info, pi->doc);
115         outputDev = (CommonOutputDev*)d;
116     }
117     /* pass global parameters to PDF driver*/
118     gfxparam_t*p = i->parameters->params;
119     while(p) {
120         outputDev->setParameter(p->key, p->value);
121         p = p->next;
122     }
123     p = pi->parameters->params;
124     while(p) {
125         outputDev->setParameter(p->key, p->value);
126         p = p->next;
127     }
128
129     outputDev->setPageMap(pi->pagemap, pi->pagemap_pos);
130     outputDev->setMove(x,y);
131     outputDev->setClip(x1,y1,x2,y2);
132
133     gfxdevice_t* middev=0;
134     if(multiply>1) {
135         middev = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
136         gfxdevice_rescale_init(middev, 0x00000000, 0, 0, 1.0 / multiply);
137         gfxdevice_rescale_setdevice(middev, dev);
138         middev->setparameter(middev, "protect", "1");
139         dev = middev;
140     } 
141         
142     if(!pi) {
143         msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
144         return;
145     }
146
147     if(!pi->pages[page->nr-1].has_info) {
148         msg("<fatal> pdf_page_render: page %d was previously set as not-to-render via the \"pages\" option", page->nr);
149         return;
150     }
151
152     if(pi->protect) {
153         dev->setparameter(dev, "protect", "1");
154     }
155
156     outputDev->setDevice(dev);
157     pi->doc->displayPage((OutputDev*)outputDev, page->nr, zoom*multiply, zoom*multiply, /*rotate*/0, true, true, pi->config_print);
158     pi->doc->processLinks((OutputDev*)outputDev, page->nr);
159     outputDev->finishPage();
160     outputDev->setDevice(0);
161     delete outputDev;
162
163     if(middev) {
164         gfxdevice_rescale_setdevice(middev, 0x00000000);
165         middev->finish(middev);
166     }
167
168 }
169
170     
171 void pdfpage_render(gfxpage_t*page, gfxdevice_t*output)
172 {
173     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
174     render2(page, output, 0,0, 0,0,0,0);
175 }
176
177 void pdfpage_rendersection(gfxpage_t*page, gfxdevice_t*output, gfxcoord_t x, gfxcoord_t y, gfxcoord_t _x1, gfxcoord_t _y1, gfxcoord_t _x2, gfxcoord_t _y2)
178 {
179     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
180
181     int x1=(int)_x1,y1=(int)_y1,x2=(int)_x2,y2=(int)_y2;
182     if((x1|y1|x2|y2)==0) x2++;
183
184     render2(page, output, (int)x*multiply,(int)y*multiply,
185                           (int)x1*multiply,(int)y1*multiply,(int)x2*multiply,(int)y2*multiply);
186 }
187
188 void pdf_doc_destroy(gfxdocument_t*gfx)
189 {
190     pdf_doc_internal_t*i= (pdf_doc_internal_t*)gfx->internal;
191
192     delete i->doc; i->doc=0;
193     free(i->pages); i->pages = 0;
194    
195     if(i->pagemap) {
196         free(i->pagemap);
197     }
198
199     i->docinfo.free();
200
201     if(i->filename) {
202         free(i->filename);i->filename=0;
203     }
204     
205     if(i->info) {
206         delete i->info;i->info=0;
207     }
208     if(i->parameters) {
209         gfxparams_free(i->parameters);
210         i->parameters=0;
211     }
212
213     free(gfx->internal);gfx->internal=0;
214     free(gfx);gfx=0;
215
216     if(global_page_range) {
217         free(global_page_range);
218         global_page_range = 0;
219     }
220     
221     /*globalparams_count--;
222     if(!globalparams_count) {
223         delete globalParams;
224         globalParams = 0;
225         globalparams_count = 0;
226     }*/
227 }
228
229 static void add_page_to_map(gfxdocument_t*gfx, int pdfpage, int outputpage)
230 {
231     pdf_doc_internal_t*i= (pdf_doc_internal_t*)gfx->internal;
232     if(pdfpage < 0)
233         return;
234     if(pdfpage >= i->pagemap_size) {
235         int oldlen = i->pagemap_size;
236         i->pagemap_size = oldlen + 1024;
237         if(pdfpage > i->pagemap_size)
238             i->pagemap_size = pdfpage+1;
239
240         if(i->pages) {
241             i->pagemap = (int*)malloc(i->pagemap_size*sizeof(int));
242         } else {
243             i->pagemap = (int*)realloc(i->pages, i->pagemap_size*sizeof(int));
244         }
245         memset(&i->pagemap[oldlen], -1, (i->pagemap_size-oldlen)*sizeof(int));
246     }
247     i->pagemap[pdfpage] = outputpage;
248     if(pdfpage > i->pagemap_pos)
249         i->pagemap_pos = pdfpage;
250 }
251
252 void pdf_doc_setparameter(gfxdocument_t*gfx, const char*name, const char*value)
253 {
254     pdf_doc_internal_t*i= (pdf_doc_internal_t*)gfx->internal;
255     if(!strcmp(name, "pagemap")) {
256         int pdfpage=0, outputpage=0;
257         sscanf(value,"%d:%d", &pdfpage, &outputpage);
258         add_page_to_map(gfx, pdfpage, outputpage);
259     } else if(!strcmp(name, "poly2bitmap")) {
260         i->config_bitmap_optimizing = atoi(value);
261     } else if(!strcmp(name, "bitmapfonts") || !strcmp(name, "bitmap")) {
262         i->config_full_bitmap_optimizing = atoi(value);
263     } else if(!strcmp(name, "asprint")) {
264         i->config_print = 1;
265     } else {
266         gfxparams_store(i->parameters, name, value);
267     }
268 }
269
270 gfxpage_t* pdf_doc_getpage(gfxdocument_t*doc, int page)
271 {
272     pdf_doc_internal_t*di= (pdf_doc_internal_t*)doc->internal;
273
274     if(page < 1 || page > doc->num_pages)
275         return 0;
276     
277     gfxpage_t* pdf_page = (gfxpage_t*)malloc(sizeof(gfxpage_t));
278     pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
279     memset(pi, 0, sizeof(pdf_page_internal_t));
280     pdf_page->internal = pi;
281
282     pdf_page->destroy = pdfpage_destroy;
283     pdf_page->render = pdfpage_render;
284     pdf_page->rendersection = pdfpage_rendersection;
285     pdf_page->width = di->pages[page-1].width;
286     pdf_page->height = di->pages[page-1].height;
287
288     pdf_page->parent = doc;
289     pdf_page->nr = page;
290     return pdf_page;
291 }
292
293 static char*getInfoString(Dict *infoDict, const char *key)
294 {
295     Object obj;
296     GString *s1, *s2;
297     int i;
298
299     if (infoDict && infoDict->lookup((char*)key, &obj)->isString()) {
300         s1 = obj.getString();
301         if ((s1->getChar(0) & 0xff) == 0xfe &&
302             (s1->getChar(1) & 0xff) == 0xff) {
303             s2 = new GString();
304             for (i = 2; i < obj.getString()->getLength(); i += 2) {
305               if (s1->getChar(i) == '\0') {
306                 s2->append(s1->getChar(i+1));
307               } else {
308                 delete s2;
309                 s2 = new GString("<unicode>");
310                 break;
311               }
312             }
313             char*ret = strdup(s2->getCString());
314             delete s2;
315             obj.free();
316             return ret;
317         } else {
318             char*ret = strdup(s1->getCString());
319             obj.free();
320             return ret;
321         }
322     }
323     return strdup("");
324 }
325
326 static char*getInfoDate(Dict *infoDict, const char *key) 
327 {
328     Object obj;
329     char *s;
330
331     if (infoDict && infoDict->lookup((char*)key, &obj)->isString()) {
332         s = obj.getString()->getCString();
333         if (s[0] == 'D' && s[1] == ':') {
334           s += 2;
335         }
336         char*ret = strdup(s);
337         obj.free();
338         return ret;
339     }
340     return strdup("");
341 }
342
343 char* pdf_doc_getinfo(gfxdocument_t*doc, const char*name)
344 {
345     pdf_doc_internal_t*i= (pdf_doc_internal_t*)doc->internal;
346     if(!strcmp(name, "title")) return getInfoString(i->docinfo.getDict(), "Title");
347     else if(!strcmp(name, "subject")) return getInfoString(i->docinfo.getDict(), "Subject");
348     else if(!strcmp(name, "keywords")) return getInfoString(i->docinfo.getDict(), "Keywords");
349     else if(!strcmp(name, "author")) return getInfoString(i->docinfo.getDict(), "Author");
350     else if(!strcmp(name, "creator")) return getInfoString(i->docinfo.getDict(), "Creator");
351     else if(!strcmp(name, "producer")) return getInfoString(i->docinfo.getDict(), "Producer");
352     else if(!strcmp(name, "creationdate")) return getInfoDate(i->docinfo.getDict(), "CreationDate");
353     else if(!strcmp(name, "moddate")) return getInfoDate(i->docinfo.getDict(), "ModDate");
354     else if(!strcmp(name, "linearized")) return strdup(i->doc->isLinearized() ? "yes" : "no");
355     else if(!strcmp(name, "tagged")) return strdup(i->doc->getStructTreeRoot()->isDict() ? "yes" : "no");
356     else if(!strcmp(name, "encrypted")) return strdup(i->doc->isEncrypted() ? "yes" : "no");
357     else if(!strcmp(name, "oktoprint")) return strdup(i->doc->okToPrint() ? "yes" : "no");
358     else if(!strcmp(name, "oktocopy")) return strdup(i->doc->okToCopy() ? "yes" : "no");
359     else if(!strcmp(name, "oktochange")) return strdup(i->doc->okToChange() ? "yes" : "no");
360     else if(!strcmp(name, "oktoaddnotes")) return strdup(i->doc->okToAddNotes() ? "yes" : "no");
361     else if(!strcmp(name, "version")) { 
362         char buf[32];
363 #ifdef HAVE_POPPLER
364         sprintf(buf, "%d.%d", i->doc->getPDFMajorVersion(), i->doc->getPDFMinorVersion());
365 #else
366         sprintf(buf, "%.1f", i->doc->getPDFVersion());
367 #endif
368         return strdup(buf);
369     }
370     return strdup("");
371 }
372
373
374 /* shortcut to InfoOutputDev.cc */
375 extern int config_addspace;
376 extern int config_fontquality;
377 extern int config_bigchar;
378
379 static void pdf_setparameter(gfxsource_t*src, const char*name, const char*value)
380 {
381     gfxsource_internal_t*i = (gfxsource_internal_t*)src->internal;
382         
383     gfxparams_store(i->parameters, name, value);
384
385     msg("<verbose> setting parameter %s to \"%s\"", name, value);
386     if(!strncmp(name, "fontdir", strlen("fontdir"))) {
387         addGlobalFontDir(value);
388     } else if(!strcmp(name, "addspacechars")) {
389         config_addspace = atoi(value);
390         gfxparams_store(i->parameters, "detectspaces", "0");
391     } else if(!strcmp(name, "detectspaces")) {
392         config_addspace = atoi(value);
393     } else if(!strcmp(name, "fontquality")) {
394         config_fontquality = atoi(value);
395     } else if(!strcmp(name, "bigchar")) {
396         config_bigchar = atoi(value);
397     } else if(!strcmp(name, "pages")) {
398         global_page_range = strdup(value);
399     } else if(!strncmp(name, "font", strlen("font")) && name[4]!='q') {
400         addGlobalFont(value);
401     } else if(!strncmp(name, "languagedir", strlen("languagedir"))) {
402         addGlobalLanguageDir(value);
403     } else if(!strcmp(name, "zoomtowidth")) {
404         zoomtowidth = atoi(value);
405     } else if(!strcmp(name, "zoom")) {
406         char buf[80];
407         zoom = atof(value);
408     } else if(!strcmp(name, "jpegdpi") || !strcmp(name, "ppmdpi")) {
409         msg("<error> %s not supported anymore. Please use jpegsubpixels/ppmsubpixels");
410     } else if(!strcmp(name, "multiply")) {
411         multiply = atoi(value);
412     } else if(!strcmp(name, "help")) {
413         printf("\nPDF device global parameters:\n");
414         printf("fontdir=<dir>     a directory with additional fonts\n");
415         printf("font=<filename>   an additional font filename\n");
416         printf("pages=<range>     the range of pages to convert (example: pages=1-100,210-)\n");
417         printf("zoom=<dpi>        the resultion (default: 72)\n");
418         printf("languagedir=<dir> Add an xpdf language directory\n");
419         printf("multiply=<times>  Render everything at <times> the resolution\n");
420         printf("poly2bitmap       Convert graphics to bitmaps\n");
421         printf("bitmap            Convert everything to bitmaps\n");
422     }   
423 }
424
425 void pdf_doc_prepare(gfxdocument_t*doc, gfxdevice_t*dev)
426 {
427     pdf_doc_internal_t*i= (pdf_doc_internal_t*)doc->internal;
428     i->info->dumpfonts(dev);
429 }
430
431 static gfxdocument_t*pdf_open(gfxsource_t*src, const char*filename)
432 {
433     gfxsource_internal_t*isrc = (gfxsource_internal_t*)src->internal;
434     gfxdocument_t*pdf_doc = (gfxdocument_t*)malloc(sizeof(gfxdocument_t));
435     memset(pdf_doc, 0, sizeof(gfxdocument_t));
436     pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
437     memset(i, 0, sizeof(pdf_doc_internal_t));
438     i->parent = src;
439     i->parameters = gfxparams_new();
440     pdf_doc->internal = i;
441     char*userPassword=0;
442     
443     i->filename = strdup(filename);
444
445     char*x = 0;
446     if((x = strchr((char*)filename, '|'))) {
447         *x = 0;
448         userPassword = x+1;
449     }
450     
451     GString *fileName = new GString(filename);
452     GString *userPW;
453
454     // open PDF file
455     if (userPassword && userPassword[0]) {
456       userPW = new GString(userPassword);
457     } else {
458       userPW = NULL;
459     }
460     i->doc = new PDFDoc(fileName, userPW);
461     if (userPW) {
462       delete userPW;
463     }
464     if (!i->doc->isOk()) {
465         return 0;
466     }
467
468     // get doc info
469     i->doc->getDocInfo(&i->docinfo);
470     
471     pdf_doc->num_pages = i->doc->getNumPages();
472     i->protect = 0;
473     if (i->doc->isEncrypted()) {
474           if(!i->doc->okToCopy()) {
475               i->nocopy = 1;
476           }
477           if(!i->doc->okToPrint()) {
478               i->noprint = 1;
479           }
480           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
481               i->protect = 1;
482     }
483         
484     if(zoomtowidth && i->doc->getNumPages()) {
485         Page*page = i->doc->getCatalog()->getPage(1);
486         PDFRectangle *r = page->getCropBox();
487         double width_before = r->x2 - r->x1;
488         zoom = 72.0 * zoomtowidth / width_before;
489         msg("<notice> Rendering at %f DPI. (Page width at 72 DPI: %f, target width: %d)", zoom, width_before, zoomtowidth);
490     }
491
492     i->info = new InfoOutputDev(i->doc->getXRef());
493     int t;
494     i->pages = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t)*pdf_doc->num_pages);
495     memset(i->pages,0,sizeof(pdf_page_info_t)*pdf_doc->num_pages);
496     for(t=1;t<=pdf_doc->num_pages;t++) {
497         if(!global_page_range || is_in_range(t, global_page_range)) {
498             i->doc->displayPage((OutputDev*)i->info, t, zoom, zoom, /*rotate*/0, /*usemediabox*/true, /*crop*/true, i->config_print);
499             i->doc->processLinks((OutputDev*)i->info, t);
500             i->pages[t-1].xMin = i->info->x1;
501             i->pages[t-1].yMin = i->info->y1;
502             i->pages[t-1].xMax = i->info->x2;
503             i->pages[t-1].yMax = i->info->y2;
504             i->pages[t-1].width = i->info->x2 - i->info->x1;
505             i->pages[t-1].height = i->info->y2 - i->info->y1;
506             i->pages[t-1].number_of_images = i->info->num_ppm_images + i->info->num_jpeg_images;
507             i->pages[t-1].number_of_links = i->info->num_links;
508             i->pages[t-1].number_of_fonts = i->info->num_fonts;
509             i->pages[t-1].has_info = 1;
510         }
511     }
512
513     pdf_doc->get = 0;
514     pdf_doc->destroy = pdf_doc_destroy;
515     pdf_doc->setparameter = pdf_doc_setparameter;
516     pdf_doc->getinfo = pdf_doc_getinfo;
517     pdf_doc->getpage = pdf_doc_getpage;
518     pdf_doc->prepare = pdf_doc_prepare;
519
520     /* pass global parameters to PDF driver*/
521     gfxparam_t*p = isrc->parameters->params;
522     while(p) {
523         pdf_doc->setparameter(pdf_doc, p->key, p->value);
524         p = p->next;
525     }
526     
527     return pdf_doc;
528 }
529     
530 void pdf_destroy(gfxsource_t*src)
531 {
532     if(!src->internal)
533         return;
534     gfxsource_internal_t*i = (gfxsource_internal_t*)src->internal;
535    
536     gfxparams_free(i->parameters);
537     i->parameters=0;
538     
539     free(src->internal);src->internal=0;
540
541     delete globalParams;globalParams = 0;
542     free(src);
543 }
544
545 gfxsource_t*gfxsource_pdf_create()
546 {
547     gfxsource_t*src = (gfxsource_t*)malloc(sizeof(gfxsource_t));
548     memset(src, 0, sizeof(gfxsource_t));
549     src->setparameter = pdf_setparameter;
550     src->open = pdf_open;
551     src->destroy = pdf_destroy;
552     gfxsource_internal_t*i = (gfxsource_internal_t*)rfx_calloc(sizeof(gfxsource_internal_t));
553     src->internal = (void*)i;
554     i->parameters = gfxparams_new();
555
556     if(!globalParams) {
557         globalParams = new GFXGlobalParams();
558         //globalparams_count++;
559     }
560     
561
562     return src;
563 }