added filter support to ruby module
[swftools.git] / src / pdf2swf.c
1 /* pdf2swf.c
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 <memory.h>
27 #include <unistd.h>
28 #include "../config.h"
29 #ifdef HAVE_SIGNAL_H
30 #include <signal.h>
31 #endif
32 #ifdef HAVE_DIRENT_H
33 #include <dirent.h>
34 #endif
35 #ifdef HAVE_MALLOC_H
36 #include <malloc.h>
37 #endif
38
39 #include "../lib/args.h"
40 #include "../lib/os.h"
41 #include "../lib/rfxswf.h"
42 #include "../lib/devices/swf.h"
43 #include "../lib/devices/polyops.h"
44 #include "../lib/devices/record.h"
45 #include "../lib/devices/rescale.h"
46 #include "../lib/gfxfilter.h"
47 #include "../lib/pdf/pdf.h"
48 #include "../lib/log.h"
49
50 #define SWFDIR concatPaths(getInstallationPath(), "swfs")
51
52 static gfxsource_t*driver = 0;
53 static gfxdevice_t*out = 0;
54
55 static int maxwidth=0, maxheight=0;
56
57 static char * outputname = 0;
58 static int loglevel = 3;
59 static char * pagerange = 0;
60 static char * filename = 0;
61 static char * password = 0;
62 static int zlib = 0;
63
64 static char * preloader = 0;
65 static char * viewer = 0;
66 static int xnup = 1;
67 static int ynup = 1;
68
69 static int info_only = 0;
70
71 static int max_time = 0;
72
73 static int flatten = 0;
74
75 char* fontpaths[256];
76 int fontpathpos = 0;
77
78 int move_x=0;
79 int move_y=0;
80 int custom_move = 0;
81 int clip_x1=0,clip_y1=0,clip_x2=0,clip_y2=0;
82 int custom_clip = 0;
83
84 static int system_quiet=0;
85
86 int systemf(const char* format, ...)
87 {
88     char buf[1024];
89     int ret;
90     va_list arglist;
91     va_start(arglist, format);
92     vsnprintf(buf, sizeof(buf)-1, format, arglist);
93     va_end(arglist);
94
95     if(!system_quiet) {
96         printf("%s\n", buf);
97         fflush(stdout);
98     }
99     ret = system(buf);
100     if(ret) {
101         fprintf(stderr, "system() returned %d\n", ret);
102         exit(ret);
103     }
104     return ret;
105 }
106
107 #ifdef HAVE_SIGNAL_H
108 void sigalarm(int signal)
109 {
110     msg("<fatal> Aborting rendering after %d seconds", max_time);
111 #if 0 && defined(HAVE_SYS_TIME_H) && defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRUSAGE)
112     struct rusage usage;
113     getrusage(RUSAGE_CHILDREN, &usage);
114     msg("<fatal> Memory used: %d,%d,%d", usage.ru_maxrss, usage.ru_idrss, usage.ru_isrss);
115 #endif 
116 #if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
117     struct mallinfo info = mallinfo();
118     msg("<fatal> Memory used: %d Mb (%d bytes)", info.uordblks/1048576, info.uordblks);
119 #endif
120     exit(1);
121 }
122 #endif
123
124 typedef struct _parameter {
125     struct _parameter*next;
126     const char*name;
127     const char*value;
128 } parameter_t;
129
130 static parameter_t* device_config = 0;
131 static parameter_t* device_config_next = 0;
132 static void store_parameter(const char*name, const char*value)
133 {
134     parameter_t*o = device_config;
135     while(o) {
136         if(!strcmp(name, o->name)) {
137             /* overwrite old value */
138             free((void*)o->value);
139             o->value = strdup(value);
140             return;
141         }
142         o = o->next;
143     }
144     parameter_t*p = (parameter_t*)malloc(sizeof(parameter_t));
145     p->name = strdup(name);
146     p->value = strdup(value);
147     p->next = 0;
148
149     if(device_config_next) {
150         device_config_next->next = p;
151         device_config_next = p;
152     } else {
153         device_config = p;
154         device_config_next = p;
155     }
156 }
157
158 int args_callback_option(char*name,char*val) {
159     if (!strcmp(name, "o"))
160     {
161         outputname = val;
162         return 1;
163     }
164     else if (!strcmp(name, "v"))
165     {
166         loglevel ++;
167         setConsoleLogging(loglevel);
168         return 0;
169     }
170     else if (!strcmp(name, "2"))
171     {
172         xnup = 2;
173         ynup = 1;
174         return 0;
175     }
176     else if (!strcmp(name, "4"))
177     {
178         xnup = 2;
179         ynup = 2;
180         return 0;
181     }
182     else if (!strcmp(name, "9"))
183     {
184         xnup = 3;
185         ynup = 3;
186         return 0;
187     }
188     else if (!strcmp(name, "X"))
189     {
190         maxwidth = atoi(val);
191         return 1;
192     }
193     else if (!strcmp(name, "Y"))
194     {
195         maxheight = atoi(val);
196         return 1;
197     }
198     else if (!strcmp(name, "q"))
199     {
200         loglevel --;
201         setConsoleLogging(loglevel);
202         system_quiet = 1;
203         return 0;
204     }
205     else if (name[0]=='p')
206     {
207         /* check whether the page range follows the p directly, like 
208            in -p1,2 */
209         do {
210             name++;
211         } while(*name == 32 || *name == 13 || *name == 10 || *name == '\t');
212
213         if(*name) {
214             pagerange = name;
215             return 0;
216         } 
217         pagerange = val;        
218         return 1;
219     }
220     else if (!strcmp(name, "P"))
221     {
222         password = val;
223         return 1;
224     }
225     else if (!strcmp(name, "c"))
226     {
227         char*s = strdup(val);
228         char*x1 = strtok(s, ":");
229         char*y1 = strtok(0, ":");
230         char*x2 = strtok(0, ":");
231         char*y2 = strtok(0, ":");
232         if(!(x1 && y1 && x2 && y2)) {
233             fprintf(stderr, "-c option requires four arguments, <x1>:<y1>:<x2>:<y2>\n");
234             exit(1);
235         }
236         custom_clip = 1;
237         clip_x1 = atoi(x1);
238         clip_y1 = atoi(y1);
239         clip_x2 = atoi(x2);
240         clip_y2 = atoi(y2);
241         free(s);
242         return 1;
243     }
244     else if (!strcmp(name, "m"))
245     {
246         char*s = strdup(val);
247         char*c = strchr(s, ':');
248         if(!c) {
249             fprintf(stderr, "-m option requires two arguments, <x>:<y>\n");
250             exit(1);
251         }
252         *c = 0;
253         custom_move = 1;
254         move_x = atoi(val);
255         move_y = atoi(c+1);
256         free(s);
257         return 1;
258     }
259     else if (!strcmp(name, "s"))
260     {
261         char*s = strdup(val);
262         char*c = strchr(s, '=');
263         if(c && *c && c[1])  {
264             *c = 0;
265             c++;
266             store_parameter(s,c);
267         } else if(!strcmp(s,"help")) {
268             printf("PDF Parameters:\n");
269             gfxsource_t*pdf = gfxsource_pdf_create();
270             pdf->setparameter(pdf, "help", "");
271             gfxdevice_t swf;
272             gfxdevice_swf_init(&swf);
273             printf("SWF Parameters:\n");
274             swf.setparameter(&swf, "help", "");
275             exit(0);
276         } else {
277             store_parameter(s,"1");
278         }
279         return 1;
280     }
281     else if (!strcmp(name, "S"))
282     {
283         store_parameter("drawonlyshapes", "1");
284         return 0;
285     }
286     else if (!strcmp(name, "i"))
287     {
288         store_parameter("ignoredraworder", "1");
289         return 0;
290     }
291 #ifndef WIN32
292     else if (!strcmp(name, "Q"))
293     {
294         max_time = atoi(val);
295         alarm(max_time);
296 # ifdef HAVE_SIGNAL_H
297         signal(SIGALRM, sigalarm);
298 # endif
299         return 1;
300     }
301 #endif
302     else if (!strcmp(name, "z"))
303     {
304         store_parameter("enablezlib", "1");
305         zlib = 1;
306         return 0;
307     }
308     else if (!strcmp(name, "n"))
309     {
310         store_parameter("opennewwindow", "1");
311         return 0;
312     }
313     else if (!strcmp(name, "I"))
314     {
315         info_only = 1;
316         return 0;
317     }
318     else if (!strcmp(name, "t"))
319     {
320         store_parameter("insertstop", "1");
321         return 0;
322     }
323     else if (!strcmp(name, "T"))
324     {
325         if(!strcasecmp(val, "mx"))
326             store_parameter("flashversion", "6");
327         else
328             store_parameter("flashversion", val);
329
330         return 1;
331     }
332     else if (!strcmp(name, "f"))
333     {
334         store_parameter("storeallcharacters", "1");
335         store_parameter("extrafontdata", "1");
336         return 0;
337     }
338     else if (!strcmp(name, "w"))
339     {
340         store_parameter("linksopennewwindow", "0");
341         return 0;
342     }
343     else if (!strcmp(name, "O"))
344     {
345         int level = 1;
346         int ret=0;
347         if(val&& val[0] && val[1]==0 && isdigit(val[0])) {
348             level = atoi(val);
349             ret=1;
350         }
351         if(level>=1)
352             store_parameter("poly2bitmap", "1");
353         if(level>=2)
354             store_parameter("bitmapfonts", "1");
355         if(level>=3)
356             store_parameter("ignoredraworder", "1");
357         return ret;
358     }
359     else if (!strcmp(name, "G"))
360     {
361         //store_parameter("optimize_polygons", "1");
362         flatten = 1;
363         return 0;
364     }
365     else if (!strcmp(name, "F"))
366     {
367         char *s = strdup(val);
368         int l = strlen(s);
369         while(l && s[l-1]=='/') {
370             s[l-1] = 0;
371             l--;
372         }
373         fontpaths[fontpathpos++] = s;
374         return 1;
375     }
376     else if (!strcmp(name, "l"))
377     {
378         char buf[256];
379         sprintf(buf, "%s/default_loader.swf", SWFDIR);
380         preloader = strdup(buf);
381         return 0;
382     }
383     else if (!strcmp(name, "b"))
384     {
385         char buf[256];
386         sprintf(buf, "%s/default_viewer.swf", SWFDIR);
387         viewer = strdup(buf);
388         return 0;
389     }
390     else if (!strcmp(name, "L"))
391     {
392         if(val)
393         {
394             preloader = val;
395         }
396         else
397         {
398             systemf("ls %s/*_loader.swf", SWFDIR);
399             if(!system_quiet)
400                 printf("\n");
401             exit(1);
402         }
403         return 1;
404     }
405     else if (!strcmp(name, "B"))
406     {
407         if(val)
408         {
409             viewer = val;
410         }
411         else
412         {
413             systemf("ls %s/*_viewer.swf", SWFDIR);
414             if(!system_quiet)
415                 printf("\n");
416             exit(1);
417         }
418         return 1;
419     }
420     else if (!strcmp(name, "j"))
421     {
422         if(name[1]) {
423             store_parameter("jpegquality", &name[1]);
424             return 0;
425         } else {
426             store_parameter("jpegquality", val);
427             return 1;
428         }
429     }
430     else if (!strcmp(name, "V"))
431     {   
432         printf("pdf2swf - part of %s %s\n", PACKAGE, VERSION);
433         exit(0);
434     }
435     else 
436     {
437         fprintf(stderr, "Unknown option: -%s\n", name);
438         exit(1);
439     }
440     return 0;
441 }
442
443 static struct options_t options[] = {
444 {"h", "help"},
445 {"V", "version"},
446 {"o", "output"},
447 {"p", "pages"},
448 {"P", "password"},
449 {"v", "verbose"},
450 {"z", "zlib"},
451 {"i", "ignore"},
452 {"j", "jpegquality"},
453 {"s", "set"},
454 {"w", "samewindow"},
455 {"t", "stop"},
456 {"T", "flashversion"},
457 {"F", "fontdir"},
458 {"b", "defaultviewer"},
459 {"l", "defaultloader"},
460 {"B", "viewer"},
461 {"L", "preloader"},
462 {"q", "quiet"},
463 {"S", "shapes"},
464 {"f", "fonts"},
465 {"G", "flatten"},
466 {"I", "info"},
467 {"Q", "maxtime"},
468 {"X", "width"},
469 {"Y", "height"},
470 {0,0}
471 };
472
473 int args_callback_longoption(char*name,char*val) {
474     return args_long2shortoption(options, name, val);
475 }
476
477 int args_callback_command(char*name, char*val) {
478     if (!filename) 
479         filename = name;
480     else {
481         if(outputname)
482         {
483              fprintf(stderr, "Error: Do you want the output to go to %s or to %s?", 
484                      outputname, name);
485              exit(1);
486         }
487         outputname = name;
488     }
489     return 0;
490 }
491
492 void args_callback_usage(char *name)
493 {
494     printf("\n");
495     printf("Usage: %s [-options] file.pdf -o file.swf\n", name);
496     printf("\n");
497     printf("-h , --help                    Print short help message and exit\n");
498     printf("-V , --version                 Print version info and exit\n");
499     printf("-o , --output file.swf         Direct output to file.swf. If file.swf contains '%%' (file%%.swf), then each page goes to a seperate file.\n");
500     printf("-p , --pages range             Convert only pages in range with range e.g. 1-20 or 1,4,6,9-11 or\n");
501     printf("-P , --password password       Use password for deciphering the pdf.\n");
502     printf("-v , --verbose                 Be verbose. Use more than one -v for greater effect.\n");
503     printf("-z , --zlib                    Use Flash 6 (MX) zlib compression.\n");
504     printf("-i , --ignore                  Allows pdf2swf to change the draw order of the pdf. This may make the generated\n");
505     printf("-j , --jpegquality quality     Set quality of embedded jpeg pictures to quality. 0 is worst (small), 100 is best (big). (default:85)\n");
506     printf("-s , --set param=value         Set a SWF encoder specific parameter.  See pdf2swf -s help for more information.\n");
507     printf("-w , --samewindow              When converting pdf hyperlinks, don't make the links open a new window. \n");
508     printf("-t , --stop                    Insert a stop() command in each page. \n");
509     printf("-T , --flashversion num        Set Flash Version in the SWF header to num.\n");
510     printf("-F , --fontdir directory       Add directory to the font search path.\n");
511     printf("-b , --defaultviewer           Link a standard viewer to the swf file. \n");
512     printf("-l , --defaultloader           Link a standard preloader to the swf file which will be displayed while the main swf is loading.\n");
513     printf("-B , --viewer filename         Link viewer filename to the swf file. \n");
514     printf("-L , --preloader filename      Link preloader filename to the swf file. \n");
515     printf("-q , --quiet                   Suppress normal messages.  Use -qq to suppress warnings, also.\n");
516     printf("-S , --shapes                  Don't use SWF Fonts, but store everything as shape.\n");
517     printf("-f , --fonts                   Store full fonts in SWF. (Don't reduce to used characters).\n");
518     printf("-G , --flatten                 Remove as many clip layers from file as possible. \n");
519     printf("-I , --info                    Don't do actual conversion, just display a list of all pages in the PDF.\n");
520     printf("-Q , --maxtime n               Abort conversion after n seconds. Only available on Unix.\n");
521     printf("\n");
522 }
523
524 float getRate(char*filename)
525 {
526     int fi;
527     SWF swf;
528     fi = open(filename,O_RDONLY|O_BINARY);
529     if(fi<0) { 
530         char buffer[256];
531         sprintf(buffer, "Couldn't open %s", filename);
532         perror(buffer);
533         exit(1);
534     }
535     if(swf_ReadSWF(fi,&swf) < 0)
536     { 
537         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
538         close(fi);
539         exit(1);
540     }
541     swf_FreeTags(&swf);
542     return swf.frameRate / 256.0;
543 }
544
545 void show_info(gfxsource_t*driver, char*filename)
546 {
547     gfxdocument_t* pdf = driver->open(driver, filename);
548     int pagenr;
549     FILE*fo=0;
550     if(!pdf) {
551         msg("<error> Couldn't open %s", filename);
552         exit(1);
553     }
554     if(outputname) {
555         fo = fopen(outputname, "wb");
556         if(!fo) {
557             perror(outputname);exit(1);;
558         }
559     } else {
560         fo = stdout;
561     }
562
563     for(pagenr = 1; pagenr <= pdf->num_pages; pagenr++) 
564     {
565         gfxpage_t*page = pdf->getpage(pdf,pagenr);
566         if(is_in_range(pagenr, pagerange)) {
567             fprintf(fo, "page=%d width=%.2f height=%.2f\n", pagenr, page->width, page->height);
568         }
569     }
570     pdf->destroy(pdf);
571 }
572
573
574 static gfxdevice_t swf,wrap,rescale;
575 gfxdevice_t*create_output_device()
576 {
577     gfxdevice_swf_init(&swf);
578
579     /* set up filter chain */
580         
581     out = &swf;
582     if(flatten) {
583         gfxdevice_removeclippings_init(&wrap, &swf);
584         out = &wrap;
585     }
586
587     if(maxwidth || maxheight) {
588         gfxdevice_rescale_init(&rescale, out, maxwidth, maxheight, 0);
589         out = &rescale;
590     }
591
592     /* pass global parameters to output device */
593     parameter_t*p = device_config;
594     while(p) {
595         out->setparameter(out, p->name, p->value);
596         p = p->next;
597     }
598     return out;
599 }
600
601 int main(int argn, char *argv[])
602 {
603     int ret;
604     char buf[256];
605     int numfonts = 0;
606     int t;
607     char t1searchpath[1024];
608     int nup_pos = 0;
609     int x,y;
610     int one_file_per_page = 0;
611     
612     initLog(0,-1,0,0,-1,loglevel);
613
614     /* not needed anymore since fonts are embedded
615        if(installPath) {
616         fontpaths[fontpathpos++] = concatPaths(installPath, "fonts");
617     }*/
618
619 #ifdef HAVE_SRAND48
620     srand48(time(0));
621 #else
622 #ifdef HAVE_SRAND
623     srand(time(0));
624 #endif
625 #endif
626
627     processargs(argn, argv);
628     
629     driver = gfxsource_pdf_create();
630     
631     /* pass global parameters to PDF driver*/
632     parameter_t*p = device_config;
633     while(p) {
634         driver->setparameter(driver, p->name, p->value);
635         p = p->next;
636     }
637
638     if(!filename)
639     {
640         fprintf(stderr, "Please specify an input file\n");
641         exit(1);
642     }
643
644     if (!info_only) {
645         if(!outputname)
646         {
647             if(filename) {
648                 outputname = stripFilename(filename, ".swf");
649                 msg("<notice> Output filename not given. Writing to %s", outputname);
650             } 
651         }
652             
653         if(!outputname)
654         {
655             fprintf(stderr, "Please use -o to specify an output file\n");
656             exit(1);
657         }
658     }
659
660     // test if the page range is o.k.
661     is_in_range(0x7fffffff, pagerange);
662
663     if (!filename) {
664         args_callback_usage(argv[0]);
665         exit(0);
666     }
667     
668     char fullname[256];
669     if(password && *password) {
670         sprintf(fullname, "%s|%s", filename, password);
671         filename = fullname;
672     }
673     
674     if(pagerange)
675         driver->setparameter(driver, "pages", pagerange);
676
677     /* add fonts */
678     for(t=0;t<fontpathpos;t++) {
679         driver->setparameter(driver, "fontdir", fontpaths[t]);
680     }
681
682     if(info_only) {
683         show_info(driver, filename);
684         return 0;
685     }
686
687     char*u = 0;
688     if((u = strchr(outputname, '%'))) {
689         if(strchr(u+1, '%') || 
690            strchr(outputname, '%')!=u)  {
691             msg("<error> only one %% allowed in filename\n");
692             return 1;
693         }
694         if(preloader || viewer) {
695             msg("<error> -b/-l/-B/-L not supported together with %% in filename\n");
696             return 1;
697         }
698         msg("<notice> outputting one file per page");
699         one_file_per_page = 1;
700         char*pattern = (char*)malloc(strlen(outputname)+2);
701         /* convert % to %d */
702         int l = u-outputname+1;
703         memcpy(pattern, outputname, l);
704         pattern[l]='d';
705         strcpy(pattern+l+1, outputname+l);
706         outputname = pattern;
707     }
708
709     gfxdocument_t* pdf = driver->open(driver, filename);
710     if(!pdf) {
711         msg("<error> Couldn't open %s", filename);
712         exit(1);
713     }
714     /* pass global parameters document */
715     p = device_config;
716     while(p) {
717         pdf->setparameter(pdf, p->name, p->value);
718         p = p->next;
719     }
720
721     struct mypage_t {
722         int x;
723         int y;
724         gfxpage_t*page;
725     } pages[4];
726
727     int pagenum = 0;
728     int frame = 1;
729     int pagenr;
730     
731     for(pagenr = 1; pagenr <= pdf->num_pages; pagenr++) 
732     {
733         if(is_in_range(pagenr, pagerange)) {
734             char mapping[80];
735             sprintf(mapping, "%d:%d", pagenr, frame);
736             pdf->setparameter(pdf, "pagemap", mapping);
737             pagenum++;
738         }
739         if(pagenum == xnup*ynup || (pagenr == pdf->num_pages && pagenum>1)) {
740             pagenum = 0;
741             frame++;
742         }
743     }
744     if(pagerange && !pagenum && frame==1) {
745         fprintf(stderr, "No pages in range %s", pagerange);
746         exit(1);
747     }
748
749     pagenum = 0;
750
751     gfxdevice_t*out = create_output_device();;
752     pdf->prepare(pdf, out);
753
754     for(pagenr = 1; pagenr <= pdf->num_pages; pagenr++) 
755     {
756         if(is_in_range(pagenr, pagerange)) {
757             gfxpage_t* page = pages[pagenum].page = pdf->getpage(pdf, pagenr);
758             pages[pagenum].x = 0;
759             pages[pagenum].y = 0;
760             pages[pagenum].page = page;
761             pagenum++;
762         }
763         if(pagenum == xnup*ynup || (pagenr == pdf->num_pages && pagenum>1)) {
764
765             int t;
766             int xmax[xnup], ymax[xnup];
767             int x,y;
768             int width=0, height=0;
769
770             memset(xmax, 0, xnup*sizeof(int));
771             memset(ymax, 0, ynup*sizeof(int));
772
773             for(y=0;y<ynup;y++)
774             for(x=0;x<xnup;x++) {
775                 int t = y*xnup + x;
776
777                 if(pages[t].page->width > xmax[x])
778                     xmax[x] = (int)pages[t].page->width;
779                 if(pages[t].page->height > ymax[y])
780                     ymax[y] = (int)pages[t].page->height;
781             }
782             for(x=0;x<xnup;x++) {
783                 width += xmax[x];
784                 xmax[x] = width;
785             }
786             for(y=0;y<ynup;y++) {
787                 height += ymax[y];
788                 ymax[y] = height;
789             }
790             if(custom_clip) {
791                 out->startpage(out,clip_x2 - clip_x1, clip_y2 - clip_y1);
792             } else {
793                 out->startpage(out,width,height);
794             }
795             for(t=0;t<pagenum;t++) {
796                 int x = t%xnup;
797                 int y = t/xnup;
798                 int xpos = x>0?xmax[x-1]:0;
799                 int ypos = y>0?ymax[y-1]:0;
800                 msg("<verbose> Render (%d,%d) move:%d/%d\n",
801                         (int)(pages[t].page->width + xpos),
802                         (int)(pages[t].page->height + ypos), xpos, ypos);
803                 pages[t].page->rendersection(pages[t].page, out, custom_move? move_x : xpos, 
804                                                            custom_move? move_y : ypos,
805                                                            custom_clip? clip_x1 : 0 + xpos, 
806                                                            custom_clip? clip_y1 : 0 + ypos, 
807                                                            custom_clip? clip_x2 : pages[t].page->width + xpos, 
808                                                            custom_clip? clip_y2 : pages[t].page->height + ypos);
809             }
810             out->endpage(out);
811             for(t=0;t<pagenum;t++)  {
812                 pages[t].page->destroy(pages[t].page);
813             }
814             pagenum = 0;
815
816             if(one_file_per_page) {
817                 gfxresult_t*result = out->finish(out);out=0;
818                 char buf[1024];
819                 sprintf(buf, outputname, pagenr);
820                 if(result->save(result, buf) < 0) {
821                     return 1;
822                 }
823                 result->destroy(result);result=0;
824                 out = create_output_device();;
825                 msg("<notice> Writing SWF file %s", buf);
826             }
827         }
828     }
829    
830     if(one_file_per_page) {
831         // remove empty device
832         gfxresult_t*result = out->finish(out);out=0;
833         result->destroy(result);result=0;
834     } else {
835         gfxresult_t*result = out->finish(out);
836         msg("<notice> Writing SWF file %s", outputname);
837         if(result->save(result, outputname) < 0) {
838             exit(1);
839         }
840         int width = (int)(ptroff_t)result->get(result, "width");
841         int height = (int)(ptroff_t)result->get(result, "height");
842         result->destroy(result);result=0;
843
844         if(preloader || viewer) {
845             const char*zip = "";
846             if(zlib) {
847                 zip = "-z";
848             }
849             if(!preloader && viewer) {
850                 systemf("swfcombine %s -X %d -Y %d \"%s\" viewport=\"%s\" -o \"%s\"",zip,width,height,
851                         viewer, outputname, outputname);
852                 if(!system_quiet)
853                     printf("\n");
854             }
855             if(preloader && !viewer) {
856                 msg("<warning> --preloader option without --viewer option doesn't make very much sense.");
857                 ret = systemf("swfcombine %s -Y %d -X %d %s/PreLoaderTemplate.swf loader=\"%s\" movie=\"%s\" -o \"%s\"",zip,width,height,
858                         SWFDIR, preloader, outputname, outputname);
859                 if(!system_quiet)
860                     printf("\n");
861             }
862             if(preloader && viewer) {
863 #ifdef HAVE_MKSTEMP
864                 char tmpname[] = "__swf__XXXXXX";
865                 mkstemp(tmpname);
866 #else 
867                 char*tmpname = "__tmp__.swf";
868 #endif
869                 systemf("swfcombine \"%s\" viewport=%s -o %s",
870                         viewer, outputname, tmpname);
871                 systemf("swfcombine %s -X %d -Y %d -r %f %s/PreLoaderTemplate.swf loader=%s movie=%s -o \"%s\"",zip,width,height,
872                         getRate(preloader), SWFDIR, preloader, tmpname, outputname);
873                 systemf("rm %s", tmpname);
874             }
875         }
876     }
877
878     pdf->destroy(pdf);
879     driver->destroy(driver);
880
881    
882     /* free global parameters */
883     p = device_config;
884     while(p) {
885         parameter_t*next = p->next;
886         if(p->name) free((void*)p->name);p->name = 0;
887         if(p->value) free((void*)p->value);p->value =0;
888         p->next = 0;free(p);
889         p = next;
890     }
891
892     return 0;
893 }
894