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