3706afc6feb8fb858d8b50757ebb073d51d91ea9
[swftools.git] / pdf2swf / pdf2swf.cc
1 /* pdf2swf.cc
2    main routine for pdf2swf(1)
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001,2002,2003 Matthias Kramm <kramm@quiss.org> 
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include "../config.h"
28 #ifdef HAVE_DIRENT_H
29 #include <dirent.h>
30 #endif
31 #ifdef HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34 #include "../lib/args.h"
35 #include "../lib/os.h"
36 #include "../lib/rfxswf.h"
37 #include "../lib/devices/swf.h"
38 //#include "../lib/devices/render.h"
39 #include "SWFOutputDev.h"
40 #include "log.h"
41
42 #define SWFDIR concatPaths(getInstallationPath(), "swfs")
43
44 static char * outputname = 0;
45 static int loglevel = 3;
46 static char * pagerange = 0;
47 static char * filename = 0;
48 static char * password = 0;
49 static int zlib = 0;
50
51 static char * preloader = 0;
52 static char * viewer = 0;
53 static int xnup = 1;
54 static int ynup = 1;
55
56 char* fontpaths[256];
57 int fontpathpos = 0;
58
59 int move_x=0;
60 int move_y=0;
61 int custom_move = 0;
62 int clip_x1=0,clip_y1=0,clip_x2=0,clip_y2=0;
63 int custom_clip = 0;
64
65 static int system_quiet=0;
66
67 int systemf(const char* format, ...)
68 {
69     char buf[1024];
70     int ret;
71     va_list arglist;
72     va_start(arglist, format);
73     vsprintf(buf, format, arglist);
74     va_end(arglist);
75
76     if(!system_quiet) {
77         printf("%s\n", buf);
78         fflush(stdout);
79     }
80     ret = system(buf);
81     if(ret) {
82         fprintf(stderr, "system() returned %d\n", ret);
83         exit(ret);
84     }
85     return ret;
86 }
87
88 int args_callback_option(char*name,char*val) {
89     if (!strcmp(name, "o"))
90     {
91         outputname = val;
92         return 1;
93     }
94     else if (!strcmp(name, "v"))
95     {
96         loglevel ++;
97         setConsoleLogging(loglevel);
98         return 0;
99     }
100     else if (!strcmp(name, "2"))
101     {
102         xnup = 2;
103         ynup = 1;
104         return 0;
105     }
106     else if (!strcmp(name, "4"))
107     {
108         xnup = 2;
109         ynup = 2;
110         return 0;
111     }
112     else if (!strcmp(name, "9"))
113     {
114         xnup = 3;
115         ynup = 3;
116         return 0;
117     }
118     else if (!strcmp(name, "q"))
119     {
120         loglevel --;
121         setConsoleLogging(loglevel);
122         system_quiet = 1;
123         return 0;
124     }
125     else if (name[0]=='p')
126     {
127         /* check whether the page range follows the p directly, like 
128            in -p1,2 */
129         do {
130             name++;
131         } while(*name == 32 || *name == 13 || *name == 10 || *name == '\t');
132
133         if(*name) {
134             pagerange = name;
135             return 0;
136         } 
137         pagerange = val;        
138         return 1;
139     }
140     else if (!strcmp(name, "P"))
141     {
142         password = val;
143         return 1;
144     }
145     else if (!strcmp(name, "c"))
146     {
147         char*s = strdup(val);
148         char*x1 = strtok(s, ":");
149         char*y1 = strtok(0, ":");
150         char*x2 = strtok(0, ":");
151         char*y2 = strtok(0, ":");
152         if(!(x1 && y1 && x2 && y2)) {
153             fprintf(stderr, "-m option requires four arguments, <x1>:<y1>:<x2>:<y2>\n");
154             exit(1);
155         }
156         custom_clip = 1;
157         clip_x1 = atoi(x1);
158         clip_y1 = atoi(y1);
159         clip_x2 = atoi(x2);
160         clip_y2 = atoi(y2);
161         free(s);
162         return 1;
163     }
164     else if (!strcmp(name, "m"))
165     {
166         char*s = strdup(val);
167         char*c = strchr(s, ':');
168         if(!c) {
169             fprintf(stderr, "-m option requires two arguments, <x>:<y>\n");
170             exit(1);
171         }
172         *c = 0;
173         custom_move = 1;
174         move_x = atoi(val);
175         move_y = atoi(c+1);
176         free(s);
177         return 1;
178     }
179     else if (!strcmp(name, "s"))
180     {
181         char*s = strdup(val);
182         char*c = strchr(s, '=');
183         if(c && *c && c[1])  {
184             *c = 0;
185             c++;
186             pdfswf_setparameter(s,c);
187         }
188         else
189             pdfswf_setparameter(s,"1");
190         return 1;
191     }
192     else if (!strcmp(name, "S"))
193     {
194         pdfswf_setparameter("drawonlyshapes", "1");
195         return 0;
196     }
197     else if (!strcmp(name, "i"))
198     {
199         pdfswf_setparameter("ignoredraworder", "1");
200         return 0;
201     }
202     else if (!strcmp(name, "z"))
203     {
204         pdfswf_setparameter("enablezlib", "1");
205         zlib = 1;
206         return 0;
207     }
208     else if (!strcmp(name, "n"))
209     {
210         pdfswf_setparameter("opennewwindow", "1");
211         return 0;
212     }
213     else if (!strcmp(name, "t"))
214     {
215         pdfswf_setparameter("insertstop", "1");
216         return 0;
217     }
218     else if (!strcmp(name, "T"))
219     {
220         if(!strcasecmp(val, "mx"))
221             pdfswf_setparameter("flashversion", "6");
222         else
223             pdfswf_setparameter("flashversion", val);
224
225         return 1;
226     }
227     else if (!strcmp(name, "f"))
228     {
229         pdfswf_setparameter("storeallcharacters", "1");
230         return 0;
231     }
232     else if (!strcmp(name, "w"))
233     {
234         pdfswf_setparameter("linksopennewwindow", "0");
235         return 0;
236     }
237     else if (!strcmp(name, "F"))
238     {
239         char *s = strdup(val);
240         int l = strlen(s);
241         while(l && s[l-1]=='/') {
242             s[l-1] = 0;
243             l--;
244         }
245         fontpaths[fontpathpos++] = s;
246         return 1;
247     }
248     else if (!strcmp(name, "l"))
249     {
250         char buf[256];
251         sprintf(buf, "%s/default_loader.swf", SWFDIR);
252         preloader = strdup(buf);
253         return 0;
254     }
255     else if (!strcmp(name, "b"))
256     {
257         char buf[256];
258         sprintf(buf, "%s/default_viewer.swf", SWFDIR);
259         viewer = strdup(buf);
260         return 0;
261     }
262     else if (!strcmp(name, "L"))
263     {
264         if(val)
265         {
266             preloader = val;
267         }
268         else
269         {
270             systemf("ls %s/*_loader.swf", SWFDIR);
271             if(!system_quiet)
272                 printf("\n");
273             exit(1);
274         }
275         return 1;
276     }
277     else if (!strcmp(name, "B"))
278     {
279         if(val)
280         {
281             viewer = val;
282         }
283         else
284         {
285             systemf("ls %s/*_viewer.swf", SWFDIR);
286             if(!system_quiet)
287                 printf("\n");
288             exit(1);
289         }
290         return 1;
291     }
292     else if (!strcmp(name, "j"))
293     {
294         if(name[1]) {
295             pdfswf_setparameter("jpegquality", &name[1]);
296             return 0;
297         } else {
298             pdfswf_setparameter("jpegquality", val);
299             return 1;
300         }
301     }
302     else if (!strcmp(name, "V"))
303     {   
304         printf("pdf2swf - part of %s %s\n", PACKAGE, VERSION);
305         exit(0);
306     }
307     else 
308     {
309         fprintf(stderr, "Unknown option: -%s\n", name);
310         exit(1);
311     }
312     return 0;
313 }
314
315 /*struct docoptions_t options[] =
316 {{"o","output","filename::Specify output file"},
317  {"V","version","Print program version"},
318  {"i","ignore","Ignore draw order (makes the SWF file smaller, but may produce graphic errors)"},
319  {"z","zlib","Use Flash 6 (MX) zlib compression (Needs at least Flash 6 Plugin to play)"},
320  {"s","shapes","Don't use SWF Fonts, but store everything as shape"},
321  {"j","jpegquality","Set quality of embedded jpeg pictures (default: 85)"},
322  {"p","pages","Convert only pages in range. (E.g. 3-85)"},
323  {"w","samewindow","Don't open a new browser window for links in the SWF"},
324  {"f","fonts","Stroe full fonts in SWF. (Don't reduce to used characters)"},
325  {"F","fontpath","path::Add directory to font search path"},
326  {"B","viewer","name::Link viewer \"name\" to the pdf"},
327  {"L","preloader","file.swf::Link preloader \"file.swf\" to the pdf"},
328  {"b","defaultviewer","Link default viewer to the pdf"},
329  {"l","defaultpreloader","Link default preloader to the pdf"}
330  {0,0}
331 };*/
332 struct options_t options[] =
333 {{"o","output"},
334  {"q","quiet"},
335  {"V","version"},
336  {"i","ignore"},
337  {"z","zlib"},
338  {"s","set"},
339  {"S","shapes"},
340  {"j","jpegquality"},
341  {"p","pages"},
342  {"w","samewindow"},
343  {"f","fonts"},
344  {"F","fontdir"},
345  {"B","viewer"},
346  {"L","preloader"},
347  {"b","defaultviewer"},
348  {"l","defaultpreloader"},
349  {"t","stop"},
350  {"T","flashversion"},
351  {0,0}
352 };
353
354 int args_callback_longoption(char*name,char*val) {
355     return args_long2shortoption(options, name, val);
356 }
357
358 int args_callback_command(char*name, char*val) {
359     if (!filename) 
360         filename = name;
361     else {
362         if(outputname)
363         {
364              fprintf(stderr, "Error: Do you want the output to go to %s or to %s?", 
365                      outputname, name);
366              exit(1);
367         }
368         outputname = name;
369     }
370     return 0;
371 }
372
373 void args_callback_usage(char*name)
374 {
375     printf("Usage: %s [Options] input.pdf [-o output.swf]\n", name);
376     printf("\nBasic options:\n");
377     printf("-p  --pages=range          Convert only pages in range\n");
378     printf("-P  --password=password    Use password for deciphering the pdf\n");
379     printf("-v  --verbose              Be verbose. Use more than one -v for greater effect\n");
380     printf("-q  --quiet                Suppress normal messages. Use -qq to suppress warnings, also.\n");
381 #ifdef HAVE_DIRENT_H
382     printf("-F  --fontdir directory    Add directory to font search path\n");
383 #endif
384     printf("-V  --version              Print program version\n");
385     printf("\nEnhanced conversion options:\n");
386     printf("-S  --shapes               Don't use SWF Fonts, but store everything as shape\n");
387     printf("-z  --zlib                 Use Flash 6 (MX) zlib compression (Needs at least Flash 6 Plugin to play)\n");
388     printf("-w  --samewindow           Don't open a new Browser Window for Links in the SWF\n");
389     printf("-f  --fonts                Store full fonts in SWF. (Don't reduce to used characters)\n");
390     printf("-T  --flashversion=num     Set the flash version in the header to num (default: 4)\n");
391     printf("-s insertstop              Insert a \"Stop\" Tag in every frame (don't turn pages automatically)\n");
392     printf("-s zoom=factor             Scale result, default: 72\n");
393     printf("-s jpegquality=quality     Set quality of embedded jpeg pictures (default:85)\n");
394     printf("-s caplinewidth=value      Set the minimum line width to trigger cap style handling to value. (3)\n");
395     printf("-s splinequality=value     Set the quality of spline convertion to value (0-100, default: 100).\n");
396     printf("-s fontquality=value       Set the quality of font convertion to value (0-100, default: 100).\n");
397     printf("-s ignoredraworder         Ignore draw order (makes the SWF file smaller and faster, but may produce\n"
398            "                           graphic errors)\n");
399     printf("-s filloverlap             Make intersecting shapes overlap, instead of canceling each\n"
400            "                           other out. (Needed for some Powerpoint PDFs)\n");
401     //deliberately undocumented (for now)
402     //printf("-2                         Put 2 pages into each frame.\n");
403     //printf("-4                         Put 4 pages into each frame.\n");
404     printf("Postprocessing options:\n");
405     printf("-b  --defaultviewer        Link default viewer to the pdf (%s)\n", concatPaths(SWFDIR, "default_viewer.swf"));
406     printf("-l  --defaultpreloader     Link default preloader the pdf (%s)\n", concatPaths(SWFDIR, "default_loader.swf"));
407     printf("-B  --viewer=filename      Link viewer \"name\" to the pdf (\"%s -B\" for list)\n", name);
408     printf("-L  --preloader=filename   Link preloader \"name\" to the pdf (\"%s -L\" for list)\n",name);
409 }
410
411 float getRate(char*filename)
412 {
413     int fi;
414     SWF swf;
415     fi = open(filename,O_RDONLY|O_BINARY);
416     if(fi<0) { 
417         char buffer[256];
418         sprintf(buffer, "Couldn't open %s", filename);
419         perror(buffer);
420         exit(1);
421     }
422     if(swf_ReadSWF(fi,&swf) < 0)
423     { 
424         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
425         close(fi);
426         exit(1);
427     }
428     swf_FreeTags(&swf);
429     return swf.frameRate / 256.0;
430 }
431
432 int main(int argn, char *argv[])
433 {
434     int ret;
435     char buf[256];
436     int numfonts = 0;
437     int t;
438     char t1searchpath[1024];
439     int nup_pos = 0;
440     int x,y;
441     char* installPath = getInstallationPath();
442     char* fontdir = 0;
443     
444     initLog(0,-1,0,0,-1,loglevel);
445
446 #if defined(WIN32) && defined(HAVE_STAT) && defined(HAVE_SYS_STAT_H)
447     if(installPath) {
448         fontdir = concatPaths(getInstallationPath(), "fonts");
449         FILE*test = fopen(concatPaths(fontdir,"\\d050000l.afm"), "rb");
450         if(!test) {
451             fprintf(stderr, "Couldn't find file %s - pdf2swf not installed properly? OS says:\n", concatPaths(fontdir, "\\d050000l.afm"));
452             perror("open");
453             exit(1);
454         }
455         fclose(test);
456     }
457 #endif
458
459 #ifdef HAVE_SRAND48
460     srand48(time(0));
461 #else
462 #ifdef HAVE_SRAND
463     srand(time(0));
464 #endif
465 #endif
466     processargs(argn, argv);
467
468     if(!filename)
469     {
470         fprintf(stderr, "Please specify an input file\n");
471         exit(1);
472     }
473
474     if(!outputname)
475     {
476         if(filename) {
477             outputname = stripFilename(filename, ".swf");
478             msg("<notice> Output filename not given. Writing to %s", outputname);
479         } 
480     }
481         
482     if(!outputname)
483     {
484         fprintf(stderr, "Please use -o to specify an output file\n");
485         exit(1);
486     }
487
488     // test if the page range is o.k.
489     is_in_range(0x7fffffff, pagerange);
490
491     if (!filename) {
492         args_callback_usage(argv[0]);
493         exit(0);
494     }
495
496     /* add fonts */
497     if(fontdir) {
498         pdfswf_addfontdir(fontdir);
499     }
500     for(t=0;t<fontpathpos;t++) {
501         pdfswf_addfontdir(fontpaths[t]);
502     }
503
504     pdf_doc_t* pdf = pdf_init(filename, password);
505     if(!pdf) {
506         msg("<error> Couldn't open %s", filename);
507         exit(1);
508     }
509
510     gfxdevice_t swfdev;
511     gfxdevice_swf_init(&swfdev);
512     //gfxdevice_render_init(&swfdev);
513     dev_output_t* swf = dev_output_init(&swfdev);
514
515     struct mypage_t {
516         int x;
517         int y;
518         pdf_page_t*page;
519         pdf_page_info_t*info;
520     } pages[4];
521
522     int pagenum = 0;
523     int frame = 1;
524     
525     for(int pagenr = 1; pagenr <= pdf->num_pages; pagenr++) 
526     {
527         if(is_in_range(pagenr, pagerange)) {
528             dev_output_preparepage(swf, pagenr, frame);
529             pagenum++;
530         }
531         if(pagenum == xnup*ynup || (pagenr == pdf->num_pages && pagenum>1)) {
532             pagenum = 0;
533             frame++;
534         }
535     }
536
537     pagenum = 0;
538
539     for(int pagenr = 1; pagenr <= pdf->num_pages; pagenr++) 
540     {
541         if(is_in_range(pagenr, pagerange)) {
542             pdf_page_t* page = pages[pagenum].page = pdf_getpage(pdf, pagenr);
543             pdf_page_info_t* info = pdf_page_getinfo(page);
544             pages[pagenum].x = 0;
545             pages[pagenum].y = 0;
546             pages[pagenum].info = info;
547             pages[pagenum].page = page;
548             pagenum++;
549         }
550         if(pagenum == xnup*ynup || (pagenr == pdf->num_pages && pagenum>1)) {
551
552             int t;
553             int xmax[xnup], ymax[xnup];
554             int x,y;
555             int width=0, height=0;
556
557             memset(xmax, 0, xnup*sizeof(int));
558             memset(ymax, 0, ynup*sizeof(int));
559
560             for(y=0;y<ynup;y++)
561             for(x=0;x<xnup;x++) {
562                 int t = y*xnup + x;
563
564                 if(pages[t].info->xMax-pages[t].info->xMin > xmax[x])
565                     xmax[x] = pages[t].info->xMax-pages[t].info->xMin;
566                 if(pages[t].info->yMax-pages[t].info->yMin > ymax[y])
567                     ymax[y] = pages[t].info->yMax-pages[t].info->yMin;
568             }
569             for(x=0;x<xnup;x++) {
570                 width += xmax[x];
571                 xmax[x] = width;
572             }
573             for(y=0;y<ynup;y++) {
574                 height += ymax[y];
575                 ymax[y] = height;
576             }
577             if(custom_clip) {
578                 dev_output_startframe(swf, clip_x2 - clip_x1, clip_y2 - clip_y1);
579             } else {
580                 dev_output_startframe(swf, width, height);
581             }
582             for(t=0;t<pagenum;t++) {
583                 int x = t%xnup;
584                 int y = t/xnup;
585                 int xpos = x>0?xmax[x-1]:0;
586                 int ypos = y>0?ymax[y-1]:0;
587                 pdf_page_info_t*info = pages[t].info;
588                 xpos -= info->xMin;
589                 ypos -= info->yMin;
590                 msg("<verbose> Render (%d,%d)-(%d,%d) move:%d/%d\n",
591                         info->xMin + xpos,
592                         info->yMin + ypos,
593                         info->xMax + xpos,
594                         info->yMax + ypos, xpos, ypos);
595                 pdf_page_rendersection(pages[t].page, swf, custom_move? move_x : xpos, 
596                                                            custom_move? move_y : ypos,
597                                                            custom_clip? clip_x1 : info->xMin + xpos, 
598                                                            custom_clip? clip_y1 : info->yMin + ypos, 
599                                                            custom_clip? clip_x2 : info->xMax + xpos, 
600                                                            custom_clip? clip_y2 : info->yMax + ypos);
601             }
602             dev_output_endframe(swf);
603             for(t=0;t<pagenum;t++)  {
604                 pdf_page_info_destroy(pages[t].info);
605                 pdf_page_destroy(pages[t].page);
606             }
607             pagenum = 0;
608         }
609     }
610     dev_output_finish(swf);
611
612     gfxresult_t*result = swfdev.finish(&swfdev);
613     if(result->save(result, outputname) < 0) {
614         exit(1);
615     }
616
617     int width = (int)result->get(result, "width");
618     int height = (int)result->get(result, "height");
619     msg("<notice> SWF written");
620     
621     result->destroy(result);
622
623     dev_output_destroy(swf);
624
625     pdf_destroy(pdf);
626
627     char*zip = "";
628     if(zlib)
629         zip = "-z";
630 #undef SYSTEM_BACKTICKS
631     if(viewer && !preloader) {
632 #ifdef SYSTEM_BACKTICKS
633         systemf("swfcombine %s `swfdump -XY \"%s\"` \"%s\" viewport=\"%s\" -o \"%s\"",zip,
634                 viewer, outputname, outputname);
635 #else
636         systemf("swfcombine %s -X %d -Y %d \"%s\" viewport=\"%s\" -o \"%s\"",zip,width,height,
637                 viewer, outputname, outputname);
638 #endif
639         if(!system_quiet)
640             printf("\n");
641     }
642     if(preloader && !viewer) {
643         msg("<warning> --preloader option without --viewer option doesn't make very much sense.");
644 #ifdef SYSTEM_BACKTICKS
645         ret = systemf("swfcombine %s `swfdump -r \"%s\"` %s/PreLoaderTemplate.swf loader=\"%s\" movie=\"%s\" -o \"%s\"",zip,
646                 preloader, SWFDIR, preloader, outputname, outputname);
647 #else
648         ret = systemf("swfcombine %s -Y %d -X %d %s/PreLoaderTemplate.swf loader=\"%s\" movie=\"%s\" -o \"%s\"",zip,width,height,
649                 SWFDIR, preloader, outputname, outputname);
650 #endif
651         if(!system_quiet)
652             printf("\n");
653     }
654     if(preloader && viewer) {
655         systemf("swfcombine \"%s\" viewport=%s -o __tmp__.swf",
656                 viewer, outputname, outputname);
657 #ifdef SYSTEM_BACKTICKS
658         systemf("swfcombine %s `swfdump -XY \"%s\"` `swfdump -r \"%s\"` %s/PreLoaderTemplate.swf loader=%s movie=__tmp__.swf -o \"%s\"",zip,
659                 outputname, preloader, SWFDIR, preloader, outputname);
660 #else
661         /* TODO: read out rate */
662         systemf("swfcombine %s -X %d -Y %d -r %f %s/PreLoaderTemplate.swf loader=%s movie=__tmp__.swf -o \"%s\"",zip,width,height,
663                 getRate(preloader), preloader, SWFDIR, preloader, outputname);
664 #endif
665         systemf("rm __tmp__.swf");
666     }
667
668     return 0;
669 }
670