ef2dde37044a460cef5fd392a68b37a557e174c5
[swftools.git] / lib / pdf / GFXOutputDev.cc
1 /* GFXOutputDev.cc
2    implements a pdf output device (OutputDev).
3
4    This file is part of swftools.
5
6    Swftools is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    Swftools is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with swftools; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stddef.h>
24 #include <string.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include "../../config.h"
29 #include "../os.h"
30 #ifdef HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
38 #endif
39 //xpdf header files
40 #include "config.h"
41 #ifdef HAVE_POPPLER
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
44 #else
45 #include "gfile.h"
46 #include "GString.h"
47 #endif
48 #include "Object.h"
49 #include "Stream.h"
50 #include "Array.h"
51 #include "Dict.h"
52 #include "XRef.h"
53 #include "Catalog.h"
54 #include "Page.h"
55 #include "PDFDoc.h"
56 #include "Error.h"
57 #include "Link.h"
58 #include "OutputDev.h"
59 #include "GfxFont.h"
60 #include "GfxState.h"
61 //#include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
64
65 //  swftools header files
66 #include "../log.h"
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
75
76 #include "../png.h"
77 #include "fonts.h"
78
79 #include <math.h>
80
81 #define SQRT2 1.41421356237309504880
82
83 typedef struct _fontfile
84 {
85     const char*filename;
86     int len; // basename length
87     int used;
88     struct _fontfile*next;
89 } fontfile_t;
90
91 // for pdfswf_addfont
92
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
95
96 static int fontnum = 0;
97
98 /* config */
99
100 struct fontentry {
101     const char*pdffont;
102     const char*filename;
103     char*afm;
104     int afmlen;
105     char*pfb;
106     int pfblen;
107     char*fullfilename;
108     DisplayFontParam *dfp;
109 } pdf2t1map[] ={
110 {"Times-Roman",           "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
111 {"Times-Italic",          "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
112 {"Times-Bold",            "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
113 {"Times-BoldItalic",      "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
114 {"Helvetica",             "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
115 {"Helvetica-Oblique",     "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
116 {"Helvetica-Bold",        "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
117 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
118 {"Courier",               "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
119 {"Courier-Oblique",       "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
120 {"Courier-Bold",          "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
121 {"Courier-BoldOblique",   "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
122 {"Symbol",                "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
123 {"ZapfDingbats",          "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
124
125
126 static int verbose = 0;
127 static int dbgindent = 1;
128 static void dbg(const char*format, ...)
129 {
130     char buf[1024];
131     int l;
132     va_list arglist;
133     if(!verbose)
134         return;
135     va_start(arglist, format);
136     vsnprintf(buf, sizeof(buf)-1, format, arglist);
137     va_end(arglist);
138     l = strlen(buf);
139     while(l && buf[l-1]=='\n') {
140         buf[l-1] = 0;
141         l--;
142     }
143     printf("(pdf) ");
144     int indent = dbgindent;
145     while(indent) {
146         printf(" ");
147         indent--;
148     }
149     printf("%s\n", buf);
150     fflush(stdout);
151 }
152
153 GFXOutputGlobals*gfxglobals=0;
154
155 GFXOutputGlobals::GFXOutputGlobals()
156 {
157     this->featurewarnings = 0;
158     this->jpeginfo = 0;
159     this->textmodeinfo = 0;
160     this->linkinfo = 0;
161     this->pbminfo = 0;
162 }
163 GFXOutputGlobals::~GFXOutputGlobals()
164 {
165     feature_t*f = this->featurewarnings;
166     while(f) {
167         feature_t*next = f->next;
168         if(f->string) {
169             free(f->string);f->string =0;
170         }
171         f->next = 0;
172         free(f);
173         f = next;
174     }
175     this->featurewarnings = 0;
176 }
177
178 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
179 {
180     feature_t*f = gfxglobals->featurewarnings;
181     while(f) {
182         if(!strcmp(feature, f->string))
183             return;
184         f = f->next;
185     }
186     f = (feature_t*)malloc(sizeof(feature_t));
187     f->string = strdup(feature);
188     f->next = gfxglobals->featurewarnings;
189     gfxglobals->featurewarnings = f;
190     if(warn) {
191         msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
192         if(this->config_break_on_warning) {
193             msg("<fatal> Aborting conversion due to unsupported feature");
194             exit(1);
195         }
196     } else {
197         msg("<notice> File contains %s",feature);
198     }
199 }
200 void GFXOutputDev::warnfeature(const char*feature,char fully)
201 {
202     showfeature(feature,fully,1);
203 }
204 void GFXOutputDev::infofeature(const char*feature)
205 {
206     showfeature(feature,0,0);
207 }
208
209 GFXOutputState::GFXOutputState() {
210     this->clipping = 0;
211     this->createsoftmask = 0;
212     this->transparencygroup = 0;
213     this->softmask = 0;
214     this->grouprecording = 0;
215     this->isolated = 0;
216 }
217
218 GBool GFXOutputDev::interpretType3Chars() 
219 {
220     return gTrue;
221 }
222
223 typedef struct _drawnchar
224 {
225     gfxcoord_t x,y;
226     int charid;
227     gfxcolor_t color;
228 } drawnchar_t;
229
230 class CharBuffer
231 {
232     drawnchar_t * chars;
233     int buf_size;
234     int num_chars;
235
236 public:
237
238     CharBuffer()
239     {
240         buf_size = 32;
241         chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
242         memset(chars, 0, sizeof(drawnchar_t)*buf_size);
243         num_chars = 0;
244     }
245     ~CharBuffer()
246     {
247         free(chars);chars = 0;
248     }
249
250     void grow(int size)
251     {
252         if(size>=buf_size) {
253             buf_size += 32;
254             chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
255         }
256     }
257
258     void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
259     {
260         grow(num_chars);
261         chars[num_chars].x = x;
262         chars[num_chars].y = y;
263         chars[num_chars].color = color;
264         chars[num_chars].charid = charid;
265     }
266 };
267     
268 char* writeOutStdFont(fontentry* f)
269 {
270     FILE*fi;
271     char namebuf1[512];
272     char namebuf2[512];
273     char* tmpFileName = mktmpname(namebuf1);
274
275     sprintf(namebuf2, "%s.afm", tmpFileName);
276     fi = fopen(namebuf2, "wb");
277     if(!fi)
278         return 0;
279     fwrite(f->afm, 1, f->afmlen, fi);
280     fclose(fi);
281
282     sprintf(namebuf2, "%s.pfb", tmpFileName);
283     fi = fopen(namebuf2, "wb");
284     if(!fi)
285         return 0;
286     fwrite(f->pfb, 1, f->pfblen, fi);
287     fclose(fi);
288     return strdup(namebuf2);
289 }
290 void unlinkfont(char* filename)
291 {
292     int l;
293     if(!filename)
294         return;
295     msg("<verbose> Removing temporary font file %s", filename);
296     l=strlen(filename);
297     unlink(filename);
298     if(!strncmp(&filename[l-4],".afm",4)) {
299         memcpy(&filename[l-4],".pfb",4); unlink(filename);
300         memcpy(&filename[l-4],".pfa",4); unlink(filename);
301         memcpy(&filename[l-4],".afm",4);
302         return;
303     } else 
304     if(!strncmp(&filename[l-4],".pfa",4)) {
305         memcpy(&filename[l-4],".afm",4); unlink(filename);
306         memcpy(&filename[l-4],".pfa",4);
307         return;
308     } else 
309     if(!strncmp(&filename[l-4],".pfb",4)) {
310         memcpy(&filename[l-4],".afm",4); unlink(filename);
311         memcpy(&filename[l-4],".pfb",4);
312         return;
313     }
314 }
315
316 static int config_use_fontconfig = 1;
317 static int fcinitcalled = 0; 
318
319 GFXGlobalParams::GFXGlobalParams()
320 : GlobalParams((char*)"")
321 {
322     //setupBaseFonts(char *dir); //not tested yet
323 }
324 GFXGlobalParams::~GFXGlobalParams()
325 {
326     msg("<verbose> Performing cleanups");
327     int t;
328     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
329         if(pdf2t1map[t].fullfilename) {
330             unlinkfont(pdf2t1map[t].fullfilename);
331         }
332     }
333 #ifdef HAVE_FONTCONFIG
334     if(config_use_fontconfig && fcinitcalled)
335         FcFini();
336 #endif
337 }
338 #ifdef HAVE_FONTCONFIG
339 static char stralphacmp(const char*s1, const char*s2)
340 {
341     while(*s1 && *s2) {
342         /* skip over space, minus, comma etc. */
343         while(*s1>=32 && *s1<=63) s1++;
344         while(*s2>=32 && *s2<=63) s2++;
345         if(*s1!=*s2)
346             break;
347         s1++;s2++;
348     }
349     return *s1 - *s2;
350 }
351
352 static char fc_ismatch(FcPattern*match, char*family, char*style)
353 {
354     char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
355     FcBool scalable=FcFalse, outline=FcFalse;
356     FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
357     FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
358     FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
359     FcPatternGetBool(match, "outline", 0, &outline);
360     FcPatternGetBool(match, "scalable", 0, &scalable);
361
362     if(scalable!=FcTrue || outline!=FcTrue)
363         return 0;
364
365     if (!stralphacmp(fcfamily, family)) {
366         msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
367         return 1;
368     } else {
369         //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
370         return 0;
371     }
372 }
373 #endif
374
375 static inline char islowercase(char c) 
376 {
377     return (c>='a' && c<='z');
378 }
379
380 char* fontconfig_searchForFont(char*name)
381 {
382 #ifdef HAVE_FONTCONFIG
383     if(!config_use_fontconfig)
384         return 0;
385     
386     // call init ony once
387     if (!fcinitcalled) {
388         fcinitcalled = 1;
389
390         // check whether we have a config file
391         char* configfile = (char*)FcConfigFilename(0);
392         int configexists = 0;
393         FILE*fi = fopen(configfile, "rb");
394         if(fi) {
395             configexists = 1;fclose(fi);
396             msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
397         } else {
398             msg("<debug> Initializing FontConfig (no configfile)");
399         }
400
401         if(!configexists) {
402             /* A fontconfig instance which didn't find a configfile is unbelievably
403                cranky, so let's just write out a small xml file and make fontconfig
404                happy */
405             FcConfig*c = FcConfigCreate();
406             char namebuf[512];
407             char* tmpFileName = mktmpname(namebuf);
408             FILE*fi = fopen(tmpFileName, "wb");
409             fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
410 #ifdef WIN32
411             fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
412 #endif
413             fprintf(fi, "<dir>~/.fonts</dir>\n");
414 #ifdef WIN32
415             fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
416 #endif
417             fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
418             fprintf(fi, "</fontconfig>\n");
419             fclose(fi);
420             FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
421             FcConfigBuildFonts(c);
422             FcConfigSetCurrent(c);
423         }
424
425         if(!FcInit()) {
426             msg("<debug> FontConfig Initialization failed. Disabling.");
427             config_use_fontconfig = 0;
428             return 0;
429         }
430         FcConfig * config = FcConfigGetCurrent();
431         if(!config) {
432             msg("<debug> FontConfig Config Initialization failed. Disabling.");
433             config_use_fontconfig = 0;
434             return 0;
435         }
436
437         /* add external fonts to fontconfig's config, too. */
438         fontfile_t*fd = global_fonts;
439         while(fd) {
440             FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
441             msg("<debug> Adding font %s to fontconfig", fd->filename);
442             fd = fd->next;
443         }
444
445         FcFontSet * set =  FcConfigGetFonts(config, FcSetSystem);
446         msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
447         if(!set || !set->nfont) {
448             msg("<debug> FontConfig has zero fonts. Disabling.");
449             config_use_fontconfig = 0;
450             return 0;
451         }
452
453         if(getLogLevel() >= LOGLEVEL_TRACE) {
454             int t;
455             int p;
456             for(p=0;p<2;p++) {
457                 if(set) {
458                     for(t=0;t<set->nfont;t++) {
459                         char*fcfamily=0,*fcstyle=0,*filename=0;
460                         FcBool scalable=FcFalse, outline=FcFalse;
461                         FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
462                         FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
463                         FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
464                         FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
465                         FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
466                         if(scalable && outline) {
467                             msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
468                         }
469                     }
470                 }
471                 set =  FcConfigGetFonts(config, FcSetApplication);
472             }
473         }
474     }
475
476     char*family = strdup(name);
477     int len = strlen(family);
478
479     char*styles[] = {"Medium", "Regular", "Bold", "Italic", "Black", "Narrow"};
480     char*style = 0;
481     int t;
482     for(t=0;t<sizeof(styles)/sizeof(styles[0]);t++) {
483         int l = strlen(styles[t]);
484         if(len>l+1 && !strcmp(family+len-l, styles[t]) && islowercase(family[len-l-1])) {
485             style = styles[t];
486             family[len-l]=0;
487             break;
488         }
489     }
490     if(!style) {
491         char*dash = strchr(family, '-');
492         if(!dash) dash = strchr(family, ',');
493         if(dash) {
494             *dash = 0;
495             style = dash+1;
496         }
497     }
498     FcPattern*pattern = 0;
499     if(style) {
500         msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
501         pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
502     } else {
503         msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
504         pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
505     }
506     pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
507
508     FcResult result;
509     FcConfigSubstitute(0, pattern, FcMatchPattern); 
510     FcDefaultSubstitute(pattern);
511
512     FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
513     if(set) {
514         int t;
515         for(t=0;t<set->nfont;t++) {
516             FcPattern*match = set->fonts[t];
517             //FcPattern*match = FcFontMatch(0, pattern, &result);
518             if(fc_ismatch(match, family, style)) {
519                 char*filename=0;
520                 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
521                     msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
522                     filename=0;
523                 }
524                 //FcPatternDestroy(match);
525                 msg("<debug> fontconfig: returning filename %s", filename);
526                 free(family);
527                 FcPatternDestroy(pattern);
528                 FcFontSetDestroy(set);
529                 return filename?strdup(filename):0;
530             }
531         }
532     }
533     free(family);
534     FcPatternDestroy(pattern);
535     FcFontSetDestroy(set);
536     return 0;
537 #else
538     return 0;
539 #endif
540 }
541
542 static DisplayFontParamKind detectFontType(const char*filename)
543 {
544     if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
545         return displayFontTT;
546     if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
547         return displayFontT1;
548     return displayFontTT;
549 }
550
551 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
552 {
553     msg("<verbose> looking for font %s", fontName->getCString());
554
555     char*name = fontName->getCString();
556     
557     /* see if it is a pdf standard font */
558     int t;
559     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
560         if(!strcmp(name, pdf2t1map[t].pdffont)) {
561             if(!pdf2t1map[t].fullfilename) {
562                 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
563                 if(!pdf2t1map[t].fullfilename) {
564                     msg("<error> Couldn't save default font- is the Temp Directory writable?");
565                 } else {
566                     msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
567                 }
568                 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
569                 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
570                 pdf2t1map[t].dfp = dfp;
571             }
572             return pdf2t1map[t].dfp;
573         }
574     }
575     
576     int bestlen = 0x7fffffff;
577     const char*bestfilename = 0;
578    
579 #ifndef HAVE_FONTCONFIG
580     /* if we don't have fontconfig, try a simple filename-comparison approach */
581     fontfile_t*f = global_fonts;
582     while(f) {
583         if(strstr(f->filename, name)) {
584             if(f->len < bestlen) {
585                 bestlen = f->len;
586                 bestfilename = f->filename;
587             }
588         }
589         f = f->next;
590     }
591 #endif
592
593     /* if we didn't find anything up to now, try looking for the
594        font via fontconfig */
595     char*filename = 0;
596     if(!bestfilename) {
597         filename = fontconfig_searchForFont(name);
598     } else {
599         filename = strdup(bestfilename);
600     }
601
602     if(filename) {
603         msg("<verbose> Font %s maps to %s\n", name, filename);
604         DisplayFontParamKind kind = detectFontType(filename);
605         DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
606         if(kind == displayFontTT) {
607             dfp->tt.fileName = new GString(filename);
608         } else {
609             dfp->t1.fileName = new GString(filename);
610         }
611         free(filename);
612         return dfp;
613     } else {
614         msg("<verbose> Font %s not found\n", name);
615         return GlobalParams::getDisplayFont(fontName);
616     }
617 }
618
619 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
620 {
621     if(!gfxglobals)
622         gfxglobals = new GFXOutputGlobals();
623
624     this->info = info;
625     this->doc = doc;
626     this->xref = doc->getXRef();
627     
628     this->type3active = 0;
629     this->statepos = 0;
630     this->xref = 0;
631     this->user_movex = 0;
632     this->user_movey = 0;
633     this->clipmovex = 0;
634     this->clipmovey = 0;
635     this->user_clipx1 = 0;
636     this->user_clipy1 = 0;
637     this->user_clipx2 = 0;
638     this->user_clipy2 = 0;
639     this->current_gfxfont = 0;
640     this->current_fontinfo = 0;
641     this->current_text_stroke = 0;
642     this->current_text_clip = 0;
643     this->outer_clip_box = 0;
644     this->config_bigchar=0;
645     this->config_convertgradients=1;
646     this->config_break_on_warning=0;
647     this->config_remapunicode=0;
648     this->config_transparent=0;
649     this->config_extrafontdata = 0;
650     this->config_drawonlyshapes = 0;
651     this->config_disable_polygon_conversion = 0;
652     this->config_multiply = 1;
653     this->config_detectspaces = 1;
654     this->config_linkdatafile = 0;
655     this->page2page = 0;
656     this->num_pages = 0;
657   
658     memset(states, 0, sizeof(states));
659 };
660
661 void GFXOutputDev::setParameter(const char*key, const char*value)
662 {
663     if(!strcmp(key,"breakonwarning")) {
664         this->config_break_on_warning = atoi(value);
665     } else if(!strcmp(key,"remapunicode")) {
666         this->config_remapunicode = atoi(value);
667     } else if(!strcmp(key,"transparent")) {
668         this->config_transparent = atoi(value);
669     } else if(!strcmp(key,"drawonlyshapes")) {
670         this->config_drawonlyshapes = atoi(value);
671     } else if(!strcmp(key,"detectspaces")) {
672         this->config_detectspaces = atoi(value);
673     } else if(!strcmp(key,"extrafontdata")) {
674         this->config_extrafontdata = atoi(value);
675     } else if(!strcmp(key,"linkdatafile")) {
676         this->config_linkdatafile = strdup(value);
677     } else if(!strcmp(key,"convertgradients")) {
678         this->config_convertgradients = atoi(value);
679     } else if(!strcmp(key,"multiply")) {
680         this->config_multiply = atoi(value);
681         if(this->config_multiply<1) 
682             this->config_multiply=1;
683     } else if(!strcmp(key,"disable_polygon_conversion")) {
684         this->config_disable_polygon_conversion = atoi(value);
685     }
686 }
687   
688 void GFXOutputDev::setDevice(gfxdevice_t*dev)
689 {
690     this->device = dev;
691 }
692   
693 void GFXOutputDev::setMove(int x,int y)
694 {
695     this->user_movex = x;
696     this->user_movey = y;
697 }
698
699 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
700 {
701     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
702     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
703
704     this->user_clipx1 = x1;
705     this->user_clipy1 = y1;
706     this->user_clipx2 = x2;
707     this->user_clipy2 = y2;
708 }
709
710 static char*getFontName(GfxFont*font)
711 {
712     char*fontid;
713     GString*gstr = font->getName();
714     char* fname = gstr==0?0:gstr->getCString();
715     if(fname==0) {
716         char buf[32];
717         Ref*r=font->getID();
718         sprintf(buf, "UFONT%d", r->num);
719         fontid = strdup(buf);
720     } else {
721         fontid = strdup(fname);
722     }
723
724     char*fontname= 0;
725     char* plus = strchr(fontid, '+');
726     if(plus && plus < &fontid[strlen(fontid)-1]) {
727         fontname = strdup(plus+1);
728     } else {
729         fontname = strdup(fontid);
730     }
731     free(fontid);
732     return fontname;
733 }
734
735 static void dumpFontInfo(const char*loglevel, GfxFont*font);
736 static int lastdumps[1024];
737 static int lastdumppos = 0;
738 /* nr = 0  unknown
739    nr = 1  substituting
740    nr = 2  type 3
741  */
742 static void showFontError(GfxFont*font, int nr) 
743 {  
744     Ref*r=font->getID();
745     int t;
746     for(t=0;t<lastdumppos;t++)
747         if(lastdumps[t] == r->num)
748             break;
749     if(t < lastdumppos)
750       return;
751     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
752     lastdumps[lastdumppos++] = r->num;
753     if(nr == 0)
754       msg("<warning> The following font caused problems:");
755     else if(nr == 1)
756       msg("<warning> The following font caused problems (substituting):");
757     else if(nr == 2)
758       msg("<warning> The following Type 3 Font will be rendered as graphics:");
759     dumpFontInfo("<warning>", font);
760 }
761
762 static void dumpFontInfo(const char*loglevel, GfxFont*font)
763 {
764   char* id = getFontID(font);
765   char* name = getFontName(font);
766   Ref* r=font->getID();
767   msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
768
769   GString*gstr  = font->getTag();
770    
771   msg("%s| Tag: %s", loglevel, id);
772   
773   if(font->isCIDFont()) msg("%s| is CID font", loglevel);
774
775   GfxFontType type=font->getType();
776   switch(type) {
777     case fontUnknownType:
778      msg("%s| Type: unknown",loglevel);
779     break;
780     case fontType1:
781      msg("%s| Type: 1",loglevel);
782     break;
783     case fontType1C:
784      msg("%s| Type: 1C",loglevel);
785     break;
786     case fontType3:
787      msg("%s| Type: 3",loglevel);
788     break;
789     case fontTrueType:
790      msg("%s| Type: TrueType",loglevel);
791     break;
792     case fontCIDType0:
793      msg("%s| Type: CIDType0",loglevel);
794     break;
795     case fontCIDType0C:
796      msg("%s| Type: CIDType0C",loglevel);
797     break;
798     case fontCIDType2:
799      msg("%s| Type: CIDType2",loglevel);
800     break;
801   }
802   
803   Ref embRef;
804   GBool embedded = font->getEmbeddedFontID(&embRef);
805   char*embeddedName=0;
806   if(font->getEmbeddedFontName()) {
807     embeddedName = font->getEmbeddedFontName()->getCString();
808   }
809   if(embedded)
810    msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
811
812   gstr = font->getExtFontFile();
813   if(gstr)
814    msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
815
816   // Get font descriptor flags.
817   if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
818   if(font->isSerif()) msg("%s| is serif", loglevel);
819   if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
820   if(font->isItalic()) msg("%s| is italic", loglevel);
821   if(font->isBold()) msg("%s| is bold", loglevel);
822
823   free(id);
824   free(name);
825 }
826
827 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
828 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
829
830 void dump_outline(gfxline_t*line)
831 {
832     /*gfxbbox_t*r = gfxline_isrectangle(line);
833     if(!r)
834         printf("is not a rectangle\n");
835     else
836         printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
837     */
838
839     while(line) {
840         if(line->type == gfx_moveTo) {
841             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
842         } else if(line->type == gfx_lineTo) {
843             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
844         } else if(line->type == gfx_splineTo) {
845             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
846         }
847         line = line->next;
848     }
849 }
850
851 void gfxPath_dump(GfxPath*path)
852 {
853     int num = path->getNumSubpaths();
854     int t;
855     int cpos=0;
856     for(t = 0; t < num; t++) {
857         GfxSubpath *subpath = path->getSubpath(t);
858         int subnum = subpath->getNumPoints();
859         int s;  
860         for(s=0;s<subnum;s++) {
861            double x=subpath->getX(s);
862            double y=subpath->getY(s);
863            if(s==0 && !subpath->getCurve(s)) {
864                 printf("M %f %f\n", x, y);
865            } else if(s==0 && subpath->getCurve(s)) {
866                 printf("E %f %f\n", x, y);
867            } else if(subpath->getCurve(s)) {
868                 printf("C %f %f\n", x, y);
869            } else {
870                 printf("T %f %f\n", x, y);
871            }
872         }
873     }
874 }
875
876 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
877 {
878     int num = path->getNumSubpaths();
879     int s,t;
880     int cpos = 0;
881     double lastx=0,lasty=0,posx=0,posy=0;
882     int needsfix=0;
883     if(!num) {
884         msg("<warning> empty path");
885         return 0;
886     }
887     gfxdrawer_t draw;
888     gfxdrawer_target_gfxline(&draw);
889
890     for(t = 0; t < num; t++) {
891         GfxSubpath *subpath = path->getSubpath(t);
892         int subnum = subpath->getNumPoints();
893         double bx=0,by=0,cx=0,cy=0;
894
895         for(s=0;s<subnum;s++) {
896            double x,y;
897            
898            this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
899
900            if(s==0) {
901                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
902                     draw.lineTo(&draw, lastx, lasty);
903                 }
904                 draw.moveTo(&draw, x,y);
905                 posx = lastx = x; 
906                 posy = lasty = y;
907                 cpos = 0;
908                 needsfix = 0;
909            } else if(subpath->getCurve(s) && cpos==0) {
910                 bx = x;
911                 by = y;
912                 cpos = 1;
913            } else if(subpath->getCurve(s) && cpos==1) {
914                 cx = x;
915                 cy = y;
916                 cpos = 2;
917            } else {
918                 posx = x;
919                 posy = y;
920                 if(cpos==0) {
921                     draw.lineTo(&draw, x,y);
922                 } else {
923                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
924                 }
925                 needsfix = 1;
926                 cpos = 0;
927            }
928         }
929     }
930     /* fix non-closed lines */
931     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
932         draw.lineTo(&draw, lastx, lasty);
933     }
934     gfxline_t*result = (gfxline_t*)draw.result(&draw);
935
936     gfxline_optimize(result);
937
938     return result;
939 }
940
941 GBool GFXOutputDev::useTilingPatternFill()
942 {
943     infofeature("tiled patterns");
944 //    if(config_convertgradients)
945 //      return gTrue;
946     return gFalse;
947 }
948 GBool GFXOutputDev::useShadedFills()
949 {
950     infofeature("shaded fills");
951     if(config_convertgradients)
952         return gTrue;
953     return gFalse;
954 }
955
956 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
957 {
958     state->transform(x,y,nx,ny);
959     *nx += user_movex + clipmovex;
960     *ny += user_movey + clipmovey;
961 }
962
963
964 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
965 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
966                                int paintType, Dict *resDict,
967                                double *mat, double *bbox,
968                                int x0, int y0, int x1, int y1,
969                                double xStep, double yStep)
970 #else
971 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
972                                int paintType, Dict *resDict,
973                                double *mat, double *bbox,
974                                int x0, int y0, int x1, int y1,
975                                double xStep, double yStep) 
976 #endif
977 {
978     msg("<debug> tilingPatternFill");
979     infofeature("tiling pattern fills");
980 }
981
982 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
983 {
984     msg("<error> functionShadedFill not supported yet");
985     infofeature("function shaded fills");
986     return gFalse;
987 }
988 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
989 {
990     gfxcolor_t c;
991     GfxRGB rgb;
992     colspace->getRGB(col, &rgb);
993     c.r = colToByte(rgb.r);
994     c.g = colToByte(rgb.g);
995     c.b = colToByte(rgb.b);
996     c.a = 255;
997     return c;
998 }
999
1000 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1001 {
1002     double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
1003     shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
1004     x1=x0+r1;y1=y0;
1005     x2=x0;   y2=y0+r1;
1006     this->transformXY(state, x0,y0, &x0,&y0);
1007     this->transformXY(state, x1,y1, &x1,&y1);
1008     this->transformXY(state, x2,y2, &x2,&y2);
1009     
1010     GfxColor color0;
1011     GfxColor color1;
1012     GfxColor color2;
1013     shading->getColor(0.0, &color0);
1014     shading->getColor(0.5, &color1);
1015     shading->getColor(1.0, &color2);
1016   
1017     GfxColorSpace* colspace = shading->getColorSpace();
1018     
1019     msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
1020             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
1021             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1022             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1023     infofeature("radial shaded fills");
1024
1025     gfxgradient_t gr[3];
1026     gfxgradient_t*g = &gr[0];
1027     g[0].next = &g[1];
1028     g[1].next = &g[2];
1029     g[2].next = 0;
1030     g[0].color = col2col(colspace, &color0);
1031     g[1].color = col2col(colspace, &color1);
1032     g[2].color = col2col(colspace, &color2);
1033     g[0].pos = 0.0;
1034     g[1].pos = 0.5;
1035     g[2].pos = 1.0;
1036
1037     gfxbbox_t b = states[statepos].clipbbox;
1038     gfxline_t p1,p2,p3,p4,p5;
1039     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1040     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1041     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1042     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1043     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1044     
1045     gfxmatrix_t m;
1046     //m.m00 = (x3-x0); m.m10 = (x1-x0);
1047     //m.m01 = (y3-y0); m.m11 = (y1-y0);
1048     //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1049     m.m00 = (x1-x0); m.m10 = (x2-x0);
1050     m.m01 = (y1-y0); m.m11 = (y2-y0);
1051     m.tx = x0 - 0.5;
1052     m.ty = y0 - 0.5;
1053
1054     device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1055     return gTrue;
1056 }
1057
1058 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1059 {
1060     double x0,y0,x1,y1;
1061     shading->getCoords(&x0,&y0,&x1,&y1);
1062     this->transformXY(state, x0,y0,&x0,&y0);
1063     this->transformXY(state, x1,y1,&x1,&y1);
1064
1065     GfxColor color0;
1066     GfxColor color1;
1067     GfxColor color2;
1068     shading->getColor(0.0, &color0);
1069     shading->getColor(0.5, &color1);
1070     shading->getColor(1.0, &color2);
1071     
1072     GfxColorSpace* colspace = shading->getColorSpace();
1073     
1074     msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1075             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
1076             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1077             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1078             );
1079     infofeature("axial shaded fills");
1080
1081     gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1082     g[0].next = &g[1];
1083     g[1].next = &g[2];
1084     g[2].next = 0;
1085     g[0].color = col2col(colspace, &color0);
1086     g[1].color = col2col(colspace, &color1);
1087     g[2].color = col2col(colspace, &color2);
1088     g[0].pos = 0.0;
1089     g[1].pos = 0.5;
1090     g[2].pos = 1.0;
1091  
1092     double xMin,yMin,xMax,yMax;
1093     state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1094     this->transformXY(state, xMin, yMin, &xMin, &yMin);
1095     msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1096
1097     xMin = 0; yMin = 0;
1098     xMin = 1024; yMin = 1024;
1099
1100     gfxbbox_t b = states[statepos].clipbbox;
1101     gfxline_t p1,p2,p3,p4,p5;
1102     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1103     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1104     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1105     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1106     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1107    
1108     /* the gradient starts at (-1.0,0.0), so move (0,0) to
1109        the middle of the two control points */
1110     gfxmatrix_t m;
1111     m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1112     m.m01 = (y1-y0)/2; m.m11 =  (x1-x0)/2;
1113     m.tx = (x0 + x1)/2 - 0.5;
1114     m.ty = (y0 + y1)/2 - 0.5;
1115
1116     device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1117
1118     free(g);
1119     return gTrue;
1120 }
1121   
1122 GBool GFXOutputDev::useDrawForm() 
1123
1124     infofeature("forms");
1125     return gFalse; 
1126 }
1127 void GFXOutputDev::drawForm(Ref id) 
1128 {
1129     msg("<error> drawForm not implemented");
1130 }
1131 GBool GFXOutputDev::needNonText() 
1132
1133     return gTrue; 
1134 }
1135 void GFXOutputDev::endPage() 
1136 {
1137     msg("<verbose> endPage (GfxOutputDev)");
1138     if(outer_clip_box) {
1139         device->endclip(device);
1140         outer_clip_box = 0;
1141     }
1142     /* notice: we're not fully done yet with this page- there might still be 
1143        a few calls to drawLink() yet to come */
1144 }
1145
1146 static inline double sqr(double x) {return x*x;}
1147
1148 #define STROKE_FILL 1
1149 #define STROKE_CLIP 2
1150 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1151 {
1152     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1153     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1154     double miterLimit = state->getMiterLimit();
1155     double width = state->getTransformedLineWidth();
1156
1157     GfxRGB rgb;
1158     double opaq = state->getStrokeOpacity();
1159     if(type3active)
1160         state->getFillRGB(&rgb);
1161     else
1162         state->getStrokeRGB(&rgb);
1163     gfxcolor_t col;
1164     col.r = colToByte(rgb.r);
1165     col.g = colToByte(rgb.g);
1166     col.b = colToByte(rgb.b);
1167     col.a = (unsigned char)(opaq*255);
1168
1169     gfx_capType capType = gfx_capRound;
1170     if(lineCap == 0) capType = gfx_capButt;
1171     else if(lineCap == 1) capType = gfx_capRound;
1172     else if(lineCap == 2) capType = gfx_capSquare;
1173     else msg("<error> Invalid line cap type");
1174
1175     gfx_joinType joinType = gfx_joinRound;
1176     if(lineJoin == 0) joinType = gfx_joinMiter;
1177     else if(lineJoin == 1) joinType = gfx_joinRound;
1178     else if(lineJoin == 2) joinType = gfx_joinBevel;
1179     else msg("<error> Invalid line join type");
1180
1181     gfxline_t*line2 = 0;
1182
1183     int dashLength = states[statepos].dashLength;
1184     double*dashPattern = states[statepos].dashPattern;
1185     double dashStart = states[statepos].dashStart;
1186     if(dashLength && dashPattern) {
1187         float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1188         int t;
1189
1190         /* try to find out how much the transformation matrix would
1191            stretch the dashes, and factor that into the dash lengths.
1192            This is not the entirely correct approach- it would be 
1193            better to first convert the path to an unscaled version,
1194            then apply dashing, and then transform the path using
1195            the current transformation matrix. However there are few
1196            PDFs which actually stretch a dashed path in a non-orthonormal
1197            way */
1198         double tx1, ty1, tx2, ty2, tx3, ty3;
1199         this->transformXY(state, 0, 0, &tx1, &ty1);
1200         this->transformXY(state, 0, 1, &tx2, &ty2);
1201         this->transformXY(state, 1, 0, &tx3, &ty3);
1202         double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1203         double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1204         if(fabs(d1-d2)>0.5)
1205             warnfeature("non-ortogonally dashed strokes", 0);
1206         double f = (d1+d2)/2;
1207
1208         msg("<trace> %d dashes", dashLength);
1209         msg("<trace> |  phase: %f", dashStart);
1210         for(t=0;t<dashLength;t++) {
1211             dash[t] = (float)dashPattern[t] * f;
1212             if(!dash[t]) {
1213                 dash[t] = 1e-37;
1214             }
1215             msg("<trace> |  d%-3d: %f", t, dashPattern[t]);
1216         }
1217         dash[dashLength] = -1;
1218         if(getLogLevel() >= LOGLEVEL_TRACE) {
1219             dump_outline(line);
1220         }
1221         
1222         line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1223         line = line2;
1224
1225         free(dash);
1226         msg("<trace> After dashing:");
1227     }
1228     
1229     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1230         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1231                 width,
1232                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1233                 lineCap==0?"butt": (lineCap==1?"round":"square"),
1234                 dashLength,
1235                 col.r,col.g,col.b,col.a
1236                 );
1237         dump_outline(line);
1238     }
1239
1240     if(flags&STROKE_FILL) {
1241         gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1242         gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1243         if(getLogLevel() >= LOGLEVEL_TRACE)  {
1244             dump_outline(gfxline);
1245         }
1246         if(!gfxline) {
1247             msg("<warning> Empty polygon (resulting from stroked line)");
1248         }
1249         if(flags&STROKE_CLIP) {
1250             device->startclip(device, gfxline);
1251             states[statepos].clipping++;
1252         } else {
1253             device->fill(device, gfxline, &col);
1254         }
1255         gfxline_free(gfxline);
1256         gfxpoly_destroy(poly);
1257     } else {
1258         if(flags&STROKE_CLIP) 
1259             msg("<error> Stroke&clip not supported at the same time");
1260         device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1261     }
1262     
1263     if(line2)
1264         gfxline_free(line2);
1265 }
1266
1267 gfxcolor_t getFillColor(GfxState * state)
1268 {
1269     GfxRGB rgb;
1270     double opaq = state->getFillOpacity();
1271     state->getFillRGB(&rgb);
1272     gfxcolor_t col;
1273     col.r = colToByte(rgb.r);
1274     col.g = colToByte(rgb.g);
1275     col.b = colToByte(rgb.b);
1276     col.a = (unsigned char)(opaq*255);
1277     return col;
1278 }
1279
1280 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd) 
1281 {
1282     gfxcolor_t col = getFillColor(state);
1283
1284     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1285         msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1286         dump_outline(line);
1287     }
1288     device->fill(device, line, &col);
1289 }
1290
1291 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd) 
1292 {
1293     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1294         msg("<trace> %sclip", evenodd?"eo":"");
1295         dump_outline(line);
1296     }
1297     gfxbbox_t bbox = gfxline_getbbox(line);
1298     gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1299
1300     device->startclip(device, line);
1301     states[statepos].clipping++;
1302 }
1303
1304 void GFXOutputDev::clip(GfxState *state) 
1305 {
1306     GfxPath * path = state->getPath();
1307     msg("<trace> clip");
1308     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1309     if(!config_disable_polygon_conversion) {
1310         gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1311         gfxline_free(line);
1312         line = line2;
1313     }
1314     clipToGfxLine(state, line, 0);
1315     gfxline_free(line);
1316 }
1317
1318 void GFXOutputDev::eoClip(GfxState *state) 
1319 {
1320     GfxPath * path = state->getPath();
1321     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1322     clipToGfxLine(state, line, 1);
1323     gfxline_free(line);
1324 }
1325 void GFXOutputDev::clipToStrokePath(GfxState *state)
1326 {
1327     GfxPath * path = state->getPath();
1328     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1329
1330     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1331         double width = state->getTransformedLineWidth();
1332         msg("<trace> cliptostrokepath width=%f", width);
1333         dump_outline(line);
1334     }
1335
1336     strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1337     gfxline_free(line);
1338 }
1339
1340 void GFXOutputDev::finish()
1341 {
1342     if(outer_clip_box) {
1343         if(device) {
1344             device->endclip(device);
1345         }
1346         outer_clip_box = 0;
1347     }
1348 }
1349
1350 GFXOutputDev::~GFXOutputDev() 
1351 {
1352     finish();
1353 };
1354 GBool GFXOutputDev::upsideDown() 
1355 {
1356     return gTrue;
1357 };
1358 GBool GFXOutputDev::useDrawChar() 
1359 {
1360     return gTrue;
1361 }
1362
1363 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1364                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1365
1366 static char tmp_printstr[4096];
1367 char* makeStringPrintable(char*str)
1368 {
1369     int len = strlen(str);
1370     int dots = 0;
1371     if(len>=80) {
1372         len = 80;
1373         dots = 1;
1374     }
1375     int t;
1376     for(t=0;t<len;t++) {
1377         char c = str[t];
1378         if(c<32 || c>124) {
1379             c = '.';
1380         }
1381         tmp_printstr[t] = c;
1382     }
1383     if(dots) {
1384         tmp_printstr[len++] = '.';
1385         tmp_printstr[len++] = '.';
1386         tmp_printstr[len++] = '.';
1387     }
1388     tmp_printstr[len] = 0;
1389     return tmp_printstr;
1390 }
1391 void GFXOutputDev::updateFontMatrix(GfxState*state)
1392 {
1393     double* ctm = state->getCTM();
1394     double fontSize = state->getFontSize();
1395     double*textMat = state->getTextMat();
1396
1397     /*  taking the absolute value of horizScaling seems to be required for
1398         some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1399     double hscale = fabs(state->getHorizScaling());
1400    
1401     // from xpdf-3.02/SplashOutputDev:updateFont
1402     double mm11 = textMat[0] * fontSize * hscale;
1403     double mm12 = textMat[1] * fontSize * hscale;
1404     double mm21 = textMat[2] * fontSize;
1405     double mm22 = textMat[3] * fontSize;
1406
1407     // multiply with ctm, like state->getFontTransMat() does
1408     this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1409     this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1410     this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1411     this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1412     this->current_font_matrix.tx = 0;
1413     this->current_font_matrix.ty = 0;
1414 }
1415
1416 void GFXOutputDev::beginString(GfxState *state, GString *s) 
1417
1418     int render = state->getRender();
1419     if(current_text_stroke) {
1420         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1421     }
1422     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1423 }
1424
1425 static gfxline_t* mkEmptyGfxShape(double x, double y)
1426 {
1427     gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1428     line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1429     return line;
1430 }
1431
1432 static char isValidUnicode(int c)
1433 {
1434     if(c>=32 && c<0x2fffe)
1435         return 1;
1436     return 0;
1437 }
1438
1439 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1440                         double dx, double dy,
1441                         double originX, double originY,
1442                         CharCode charid, int nBytes, Unicode *_u, int uLen)
1443 {
1444     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1445         msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1446         return;
1447     }
1448   
1449     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1450
1451     int render = state->getRender();
1452     gfxcolor_t col = getFillColor(state);
1453
1454     // check for invisible text -- this is used by Acrobat Capture
1455     if (render == RENDER_INVISIBLE) {
1456         col.a = 0;
1457         if(!config_extrafontdata)
1458             return;
1459     }
1460
1461     GfxFont*font = state->getFont();
1462
1463     if(font->getType() == fontType3) {
1464         /* type 3 chars are passed as graphics */
1465         msg("<debug> type3 char at %f/%f", x, y);
1466         return;
1467     }
1468
1469     Unicode u = uLen?(_u[0]):0;
1470
1471     gfxmatrix_t m = this->current_font_matrix;
1472     this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1473     //m.tx += originX; m.ty += originY;
1474     
1475     msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%p",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1476
1477     if((render == RENDER_FILL && !config_drawonlyshapes) ||
1478        (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1479        (render == RENDER_INVISIBLE)) {
1480
1481         int space = this->current_fontinfo->space_char;
1482         if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1483             /* space char detection */
1484             if(last_char_gfxfont == current_gfxfont && 
1485                last_char_y == m.ty &&
1486                !last_char_was_space) {
1487                 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1488                 int space = this->current_fontinfo->space_char;
1489                 float width = this->current_fontinfo->average_advance;
1490                 if(m.tx - expected_x >= m.m00*width*4/10) {
1491                     msg("<debug> There's a %f pixel gap between char %d and char %d (expected no more than %f), I'm inserting a space here", 
1492                             m.tx-expected_x, 
1493                             width*m.m00*4/10,
1494                             last_char, glyphid);
1495                     gfxmatrix_t m2 = m;
1496                     m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1497                     if(m2.tx < expected_x) m2.tx = expected_x;
1498                     device->drawchar(device, current_gfxfont, space, &col, &m2);
1499                 }
1500             }
1501             last_char_gfxfont = current_gfxfont;
1502             last_char = glyphid;
1503             last_char_x = m.tx;
1504             last_char_y = m.ty;
1505             last_char_was_space = GLYPH_IS_SPACE(&current_gfxfont->glyphs[glyphid]);
1506         }
1507         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1508     } else {
1509         msg("<debug> Drawing glyph %d as shape", charid);
1510         if(!gfxglobals->textmodeinfo) {
1511             msg("<notice> Some texts will be rendered as shape");
1512             gfxglobals->textmodeinfo = 1;
1513         }
1514
1515         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1516         gfxline_t*tglyph = gfxline_clone(glyph);
1517         gfxline_transform(tglyph, &m);
1518         if((render&3) != RENDER_INVISIBLE) {
1519             gfxline_t*add = gfxline_clone(tglyph);
1520             current_text_stroke = gfxline_append(current_text_stroke, add);
1521         }
1522         if(render&RENDER_CLIP) {
1523             gfxline_t*add = gfxline_clone(tglyph);
1524             current_text_clip = gfxline_append(current_text_clip, add);
1525             if(!current_text_clip) {
1526                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1527             }
1528         }
1529         gfxline_free(tglyph);
1530     }
1531 }
1532
1533 void GFXOutputDev::endString(GfxState *state) 
1534
1535     int render = state->getRender();
1536     msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1537     
1538     if(current_text_stroke) {
1539         /* fillstroke and stroke text rendering objects we can process right
1540            now (as there may be texts of other rendering modes in this
1541            text object)- clipping objects have to wait until endTextObject,
1542            however */
1543         device->setparameter(device, "mark","TXT");
1544         if((render&3) == RENDER_FILL) {
1545             fillGfxLine(state, current_text_stroke, 0);
1546             gfxline_free(current_text_stroke);
1547             current_text_stroke = 0;
1548         } else if((render&3) == RENDER_FILLSTROKE) {
1549             fillGfxLine(state, current_text_stroke, 0);
1550             strokeGfxline(state, current_text_stroke,0);
1551             gfxline_free(current_text_stroke);
1552             current_text_stroke = 0;
1553         } else if((render&3) == RENDER_STROKE) {
1554             strokeGfxline(state, current_text_stroke,0);
1555             gfxline_free(current_text_stroke);
1556             current_text_stroke = 0;
1557         }
1558         device->setparameter(device, "mark","");
1559     }
1560 }    
1561
1562 void GFXOutputDev::endTextObject(GfxState *state)
1563 {
1564     int render = state->getRender();
1565     msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1566     
1567     if(current_text_clip) {
1568         device->setparameter(device, "mark","TXT");
1569         clipToGfxLine(state, current_text_clip, 0);
1570         device->setparameter(device, "mark","");
1571         gfxline_free(current_text_clip);
1572         current_text_clip = 0;
1573     }
1574 }
1575
1576 /* the logic seems to be as following:
1577    first, beginType3Char is called, with the charcode and the coordinates.
1578    if this function returns true, it already knew about the char and has now drawn it.
1579    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1580    called with some parameters.
1581    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1582    at the position first passed to beginType3Char). the char ends with endType3Char.
1583
1584    The drawing operations between beginType3Char and endType3Char are somewhat different to
1585    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1586    color determines the color of a font)
1587 */
1588
1589 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1590 {
1591     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1592     type3active = 1;
1593     
1594     if(config_extrafontdata && current_fontinfo) {
1595
1596         gfxmatrix_t m = this->current_font_matrix;
1597         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1598
1599         /*m.m00*=INTERNAL_FONT_SIZE;
1600         m.m01*=INTERNAL_FONT_SIZE;
1601         m.m10*=INTERNAL_FONT_SIZE;
1602         m.m11*=INTERNAL_FONT_SIZE;*/
1603
1604         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1605             msg("<error> Invalid charid %d for font", charid);
1606             return gFalse;
1607         }
1608         gfxcolor_t col={0,0,0,0};
1609         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1610         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1611     }
1612
1613
1614     /* the character itself is going to be passed using the draw functions */
1615     return gFalse; /* gTrue= is_in_cache? */
1616 }
1617
1618 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1619 }
1620 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1621 }
1622
1623 void GFXOutputDev::endType3Char(GfxState *state)
1624 {
1625     type3active = 0;
1626     msg("<debug> endType3Char");
1627 }
1628
1629 void GFXOutputDev::startPage(int pageNum, GfxState *state)
1630 {
1631     this->currentpage = pageNum;
1632     double x1,y1,x2,y2;
1633     int rot = doc->getPageRotate(1);
1634     gfxcolor_t white = {255,255,255,255};
1635     gfxcolor_t black = {255,0,0,0};
1636     laststate = state;
1637     gfxline_t clippath[5];
1638     PDFRectangle *r = this->page->getCropBox();
1639
1640     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1641     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1642     Use CropBox, not MediaBox, as page size
1643     */
1644     
1645     /*x1 = crop_x1;
1646     y1 = crop_y1;
1647     x2 = crop_x2;
1648     y2 = crop_y2;*/
1649     state->transform(r->x1,r->y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1650     state->transform(r->x2,r->y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1651
1652     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1653     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1654
1655     this->clipmovex = -(int)x1;
1656     this->clipmovey = -(int)y1;
1657     
1658     /* apply user clip box */
1659     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1660         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1661         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1662         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1663         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1664         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1665     } else {
1666         x1 += this->clipmovex;
1667         y1 += this->clipmovey;
1668         x2 += this->clipmovex;
1669         y2 += this->clipmovey;
1670     }
1671
1672     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1673     
1674     msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex + clipmovex, user_movey + clipmovey);
1675     if(rot!=0)
1676         msg("<verbose> page is rotated %d degrees", rot);
1677
1678     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1679     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1680     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1681     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1682     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1683     device->startclip(device, clippath); outer_clip_box = 1;
1684     if(!config_transparent) {
1685         device->fill(device, clippath, &white);
1686     }
1687     states[statepos].clipbbox.xmin = x1;
1688     states[statepos].clipbbox.ymin = x1;
1689     states[statepos].clipbbox.xmax = x2;
1690     states[statepos].clipbbox.ymax = y2;
1691     
1692     states[statepos].dashPattern = 0;
1693     states[statepos].dashLength = 0;
1694     states[statepos].dashStart = 0;
1695     
1696     this->last_char_gfxfont = 0;
1697 }
1698
1699
1700 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1701 {
1702     double x1, y1, x2, y2;
1703     gfxline_t points[5];
1704     int x, y;
1705     
1706     msg("<debug> drawlink");
1707
1708     link->getRect(&x1, &y1, &x2, &y2);
1709     cvtUserToDev(x1, y1, &x, &y);
1710     points[0].type = gfx_moveTo;
1711     points[0].x = points[4].x = x + user_movex + clipmovex;
1712     points[0].y = points[4].y = y + user_movey + clipmovey;
1713     points[0].next = &points[1];
1714     cvtUserToDev(x2, y1, &x, &y);
1715     points[1].type = gfx_lineTo;
1716     points[1].x = x + user_movex + clipmovex;
1717     points[1].y = y + user_movey + clipmovey;
1718     points[1].next = &points[2];
1719     cvtUserToDev(x2, y2, &x, &y);
1720     points[2].type = gfx_lineTo;
1721     points[2].x = x + user_movex + clipmovex;
1722     points[2].y = y + user_movey + clipmovey;
1723     points[2].next = &points[3];
1724     cvtUserToDev(x1, y2, &x, &y);
1725     points[3].type = gfx_lineTo;
1726     points[3].x = x + user_movex + clipmovex;
1727     points[3].y = y + user_movey + clipmovey;
1728     points[3].next = &points[4];
1729     cvtUserToDev(x1, y1, &x, &y);
1730     points[4].type = gfx_lineTo;
1731     points[4].x = x + user_movex + clipmovex;
1732     points[4].y = y + user_movey + clipmovey;
1733     points[4].next = 0;
1734     
1735     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1736             points[0].x, points[0].y,
1737             points[1].x, points[1].y,
1738             points[2].x, points[2].y,
1739             points[3].x, points[3].y); 
1740     
1741     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1742         dump_outline(points);
1743     }
1744
1745     LinkAction*action=link->getAction();
1746     char buf[128];
1747     char*s = 0;
1748     const char*type = "-?-";
1749     char*named = 0;
1750     int page = -1;
1751     msg("<trace> drawlink action=%d", action->getKind());
1752     switch(action->getKind())
1753     {
1754         case actionGoTo: {
1755             type = "GoTo";
1756             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1757             LinkDest *dest=NULL;
1758             if (ha->getDest()==NULL) 
1759                 dest=catalog->findDest(ha->getNamedDest());
1760             else 
1761                 dest=ha->getDest()->copy();
1762             if (dest){ 
1763               if (dest->isPageRef()){
1764                 Ref pageref=dest->getPageRef();
1765                 page=catalog->findPage(pageref.num,pageref.gen);
1766               }
1767               else  page=dest->getPageNum();
1768               sprintf(buf, "%d", page);
1769               s = strdup(buf);
1770               delete dest;
1771             }
1772         }
1773         break;
1774         case actionGoToR: {
1775             type = "GoToR";
1776             LinkGoToR*l = (LinkGoToR*)action;
1777             GString*g = l->getFileName();
1778             if(g)
1779              s = strdup(g->getCString());
1780             if(!s) {
1781                 /* if the GoToR link has no filename, then
1782                    try to find a refernce in the *local*
1783                    file */
1784                 GString*g = l->getNamedDest();
1785                 if(g)
1786                  s = strdup(g->getCString());
1787             }
1788         }
1789         break;
1790         case actionNamed: {
1791             type = "Named";
1792             LinkNamed*l = (LinkNamed*)action;
1793             GString*name = l->getName();
1794             if(name) {
1795                 s = strdup(name->lowerCase()->getCString());
1796                 named = name->getCString();
1797                 if(!strchr(s,':')) 
1798                 {
1799                     if(strstr(s, "next") || strstr(s, "forward"))
1800                     {
1801                         page = currentpage + 1;
1802                     }
1803                     else if(strstr(s, "prev") || strstr(s, "back"))
1804                     {
1805                         page = currentpage - 1;
1806                     }
1807                     else if(strstr(s, "last") || strstr(s, "end"))
1808                     {
1809                         if(this->page2page && this->num_pages) {
1810                             page = this->page2page[this->num_pages-1];
1811                         }
1812                     }
1813                     else if(strstr(s, "first") || strstr(s, "top"))
1814                     {
1815                         page = 1;
1816                     }
1817                 }
1818             }
1819         }
1820         break;
1821         case actionLaunch: {
1822             type = "Launch";
1823             LinkLaunch*l = (LinkLaunch*)action;
1824             GString * str = new GString(l->getFileName());
1825             GString * params = l->getParams();
1826             if(params)
1827                 str->append(params);
1828             s = strdup(str->getCString());
1829             delete str;
1830         }
1831         break;
1832         case actionURI: {
1833             char*url = 0;
1834             type = "URI";
1835             LinkURI*l = (LinkURI*)action;
1836             GString*g = l->getURI();
1837             if(g) {
1838              url = g->getCString();
1839              s = strdup(url);
1840             }
1841         }
1842         break;
1843         case actionUnknown: {
1844             type = "Unknown";
1845             LinkUnknown*l = (LinkUnknown*)action;
1846             s = strdup("");
1847         }
1848         break;
1849         default: {
1850             msg("<error> Unknown link type!");
1851             break;
1852         }
1853     }
1854
1855     if(!s) s = strdup("-?-");
1856     
1857     msg("<trace> drawlink s=%s", s);
1858
1859     if(!gfxglobals->linkinfo && (page || s))
1860     {
1861         msg("<notice> File contains links");
1862         gfxglobals->linkinfo = 1;
1863     }
1864     
1865     if(page>0) {
1866         int t;
1867         int lpage = -1;
1868         for(t=1;t<=this->num_pages;t++) {
1869             if(this->page2page[t]==page) {
1870                 lpage = t;
1871                 break;
1872             }
1873         }
1874         if(lpage<0) {
1875             lpage = page;
1876         }
1877
1878         char buf[80];
1879         sprintf(buf, "page%d", lpage);
1880         device->drawlink(device, points, buf);
1881     }
1882     else if(s)
1883     {
1884         device->drawlink(device, points, s);
1885         if(this->config_linkdatafile) {
1886             FILE*fi = fopen(config_linkdatafile, "ab+");
1887             fprintf(fi, "%s\n", s);
1888             fclose(fi);
1889         }
1890     }
1891         
1892     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1893     free(s);s=0;
1894 }
1895
1896 void GFXOutputDev::saveState(GfxState *state) {
1897     dbg("saveState %p", state); dbgindent+=2;
1898
1899     msg("<trace> saveState %p", state);
1900     updateAll(state);
1901     if(statepos>=64) {
1902       msg("<fatal> Too many nested states in pdf.");
1903       exit(1);
1904     }
1905     statepos ++;
1906     states[statepos].state = state;
1907     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1908     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1909     states[statepos].clipping = 0;
1910     states[statepos].olddevice = 0;
1911     states[statepos].clipbbox = states[statepos-1].clipbbox;
1912
1913     states[statepos].dashPattern = states[statepos-1].dashPattern;
1914     states[statepos].dashStart = states[statepos-1].dashStart;
1915     states[statepos].dashLength = states[statepos-1].dashLength;
1916 };
1917
1918 void GFXOutputDev::restoreState(GfxState *state) {
1919   dbgindent-=2; dbg("restoreState %p", state);
1920
1921   if(statepos==0) {
1922       msg("<fatal> Invalid restoreState");
1923       exit(1);
1924   }
1925   msg("<trace> restoreState %p%s%s", state,
1926                                   states[statepos].softmask?" (end softmask)":"",
1927                                   states[statepos].clipping?" (end clipping)":"");
1928   if(states[statepos].softmask) {
1929       clearSoftMask(state);
1930   }
1931
1932   if(states[statepos].dashPattern) {
1933       if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1934           free(states[statepos].dashPattern);
1935           states[statepos].dashPattern = 0;
1936       }
1937   }
1938
1939   updateAll(state);
1940   
1941   while(states[statepos].clipping) {
1942       device->endclip(device);
1943       states[statepos].clipping--;
1944   }
1945   if(states[statepos].state!=state) {
1946       msg("<fatal> bad state nesting");
1947       if(verbose) {
1948           int t;
1949           for(t=0;t<=statepos;t++) {
1950               printf("%p ", states[t].state);
1951           }
1952           printf("\n");
1953       }
1954       exit(1);
1955   }
1956   states[statepos].state=0;
1957   statepos--;
1958 }
1959  
1960 void GFXOutputDev::updateLineDash(GfxState *state) 
1961 {
1962     if(states[statepos].dashPattern &&
1963        (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1964         free(states[statepos].dashPattern);
1965         states[statepos].dashPattern = 0;
1966     }
1967     double *pattern = 0;
1968     int dashLength;
1969     double dashStart;
1970     state->getLineDash(&pattern, &dashLength, &dashStart);
1971     msg("<debug> updateLineDash, %d dashes", dashLength);
1972     if(!dashLength) {
1973         states[statepos].dashPattern = 0;
1974         states[statepos].dashLength = 0;
1975     } else {
1976         double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1977         memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1978         states[statepos].dashPattern = p;
1979         states[statepos].dashLength = dashLength;
1980         states[statepos].dashStart = dashStart;
1981     }
1982 }
1983   
1984 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1985 {
1986     this->page2page = page2page;
1987     this->num_pages = num_pages;
1988 }
1989
1990 void GFXOutputDev::updateLineWidth(GfxState *state)
1991 {
1992     double width = state->getTransformedLineWidth();
1993 }
1994
1995 void GFXOutputDev::updateLineCap(GfxState *state)
1996 {
1997     int c = state->getLineCap();
1998 }
1999
2000 void GFXOutputDev::updateLineJoin(GfxState *state)
2001 {
2002     int j = state->getLineJoin();
2003 }
2004
2005 void GFXOutputDev::updateFillColor(GfxState *state) 
2006 {
2007     GfxRGB rgb;
2008     double opaq = state->getFillOpacity();
2009     state->getFillRGB(&rgb);
2010 }
2011 void GFXOutputDev::updateFillOpacity(GfxState *state)
2012 {
2013     GfxRGB rgb;
2014     double opaq = state->getFillOpacity();
2015     state->getFillRGB(&rgb);
2016     dbg("update fillopaq %f", opaq);
2017 }
2018 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2019 {
2020     double opaq = state->getFillOpacity();
2021     dbg("update strokeopaq %f", opaq);
2022 }
2023 void GFXOutputDev::updateFillOverprint(GfxState *state)
2024 {
2025     double opaq = state->getFillOverprint();
2026     dbg("update filloverprint %f", opaq);
2027 }
2028 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2029 {
2030     double opaq = state->getStrokeOverprint();
2031     dbg("update strokeoverprint %f", opaq);
2032 }
2033 void GFXOutputDev::updateTransfer(GfxState *state)
2034 {
2035     dbg("update transfer");
2036 }
2037
2038
2039 void GFXOutputDev::updateStrokeColor(GfxState *state) 
2040 {
2041     GfxRGB rgb;
2042     double opaq = state->getStrokeOpacity();
2043     state->getStrokeRGB(&rgb);
2044 }
2045
2046 void GFXOutputDev::updateFont(GfxState *state) 
2047 {
2048     GfxFont* gfxFont = state->getFont();
2049     if (!gfxFont) {
2050         return; 
2051     }  
2052     char*id = getFontID(gfxFont);
2053     msg("<verbose> Updating font to %s", id);
2054     if(gfxFont->getType() == fontType3) {
2055         infofeature("Type3 fonts");
2056         if(!config_extrafontdata) {
2057             return;
2058         }
2059     }
2060     if(!id) {
2061         msg("<error> Internal Error: FontID is null");
2062         return; 
2063     }
2064
2065     this->current_fontinfo = this->info->getFont(id);
2066
2067     if(!this->current_fontinfo) {
2068         msg("<error> Internal Error: no fontinfo for font %s", id);
2069         return;
2070     }
2071     if(!this->current_fontinfo->seen) {
2072         dumpFontInfo("<verbose>", gfxFont);
2073     }
2074
2075     current_gfxfont = this->current_fontinfo->getGfxFont();
2076     device->addfont(device, current_gfxfont);
2077     free(id);
2078     
2079     updateFontMatrix(state);
2080 }
2081
2082 #define SQR(x) ((x)*(x))
2083
2084 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2085 {
2086     if((newwidth<1 || newheight<1) ||
2087        (width<=newwidth || height<=newheight))
2088         return 0;
2089     unsigned char*newdata;
2090     int x,y;
2091     newdata= (unsigned char*)malloc(newwidth*newheight);
2092     double fx = ((double)width)/newwidth;
2093     double fy = ((double)height)/newheight;
2094     double px = 0;
2095     int blocksize = (int)(8192/(fx*fy));
2096     int r = 8192*256/palettesize;
2097     for(x=0;x<newwidth;x++) {
2098         double ex = px + fx;
2099         int fromx = (int)px;
2100         int tox = (int)ex;
2101         int xweight1 = (int)((1-(px-fromx))*256);
2102         int xweight2 = (int)((ex-tox)*256);
2103         double py =0;
2104         for(y=0;y<newheight;y++) {
2105             double ey = py + fy;
2106             int fromy = (int)py;
2107             int toy = (int)ey;
2108             int yweight1 = (int)((1-(py-fromy))*256);
2109             int yweight2 = (int)((ey-toy)*256);
2110             int a = 0;
2111             int xx,yy;
2112             if(tox>=width) 
2113                 tox = width-1;
2114             if(toy>=height) 
2115                 toy = height-1;
2116             for(xx=fromx;xx<=tox;xx++)
2117             for(yy=fromy;yy<=toy;yy++) {
2118                 int b = 1-data[width*yy+xx];
2119                 int weight=256;
2120                 if(xx==fromx) weight = (weight*xweight1)/256;
2121                 if(xx==tox) weight = (weight*xweight2)/256;
2122                 if(yy==fromy) weight = (weight*yweight1)/256;
2123                 if(yy==toy) weight = (weight*yweight2)/256;
2124                 a+=b*weight;
2125             }
2126             //if(a) a=(palettesize-1)*r/blocksize;
2127             newdata[y*newwidth+x] = (a*blocksize)/r;
2128             py = ey;
2129         }
2130         px = ex;
2131     }
2132     return newdata;
2133 }
2134
2135 #define IMAGE_TYPE_JPEG 0
2136 #define IMAGE_TYPE_LOSSLESS 1
2137
2138 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2139         double x1,double y1,
2140         double x2,double y2,
2141         double x3,double y3,
2142         double x4,double y4, int type, int multiply)
2143 {
2144     gfxcolor_t*newpic=0;
2145     
2146     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2147     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2148    
2149     gfxline_t p1,p2,p3,p4,p5;
2150     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2151     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2152     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2153     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2154     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2155
2156     {p1.x = (int)(p1.x*20)/20.0;
2157      p1.y = (int)(p1.y*20)/20.0;
2158      p2.x = (int)(p2.x*20)/20.0;
2159      p2.y = (int)(p2.y*20)/20.0;
2160      p3.x = (int)(p3.x*20)/20.0;
2161      p3.y = (int)(p3.y*20)/20.0;
2162      p4.x = (int)(p4.x*20)/20.0;
2163      p4.y = (int)(p4.y*20)/20.0;
2164      p5.x = (int)(p5.x*20)/20.0;
2165      p5.y = (int)(p5.y*20)/20.0;
2166     }
2167
2168     gfxmatrix_t m;
2169     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2170     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2171         
2172     m.tx = p1.x - 0.5*multiply;
2173     m.ty = p1.y - 0.5*multiply;
2174
2175     gfximage_t img;
2176     img.data = (gfxcolor_t*)data;
2177     img.width = sizex;
2178     img.height = sizey;
2179   
2180     if(type == IMAGE_TYPE_JPEG)
2181         /* TODO: pass image_dpi to device instead */
2182         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2183
2184     dump_outline(&p1);
2185     dev->fillbitmap(dev, &p1, &img, &m, 0);
2186 }
2187
2188 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2189         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2190 {
2191     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2192 }
2193
2194 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2195         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2196 {
2197     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2198 }
2199
2200
2201 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2202                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2203                                    GBool inlineImg, int mask, int*maskColors,
2204                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2205 {
2206   /* the code in this function is *old*. It's not pretty, but it works. */
2207
2208   double x1,y1,x2,y2,x3,y3,x4,y4;
2209   ImageStream *imgStr;
2210   Guchar pixBuf[4];
2211   GfxRGB rgb;
2212   int ncomps = 1;
2213   int bits = 1;
2214   unsigned char* maskbitmap = 0;
2215                                  
2216   if(colorMap) {
2217     ncomps = colorMap->getNumPixelComps();
2218     bits = colorMap->getBits();
2219   }
2220       
2221   if(maskStr) {
2222       int x,y;
2223       unsigned char buf[8];
2224       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2225       if(maskColorMap) {
2226           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2227           imgMaskStr->reset();
2228           unsigned char pal[256];
2229           int n = 1 << colorMap->getBits();
2230           int t;
2231           for(t=0;t<n;t++) {
2232               GfxGray gray;
2233               pixBuf[0] = t;
2234               maskColorMap->getGray(pixBuf, &gray);
2235               pal[t] = colToByte(gray);
2236           }
2237           for (y = 0; y < maskHeight; y++) {
2238               for (x = 0; x < maskWidth; x++) {
2239                   imgMaskStr->getPixel(buf);
2240                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2241               }
2242           }
2243           delete imgMaskStr;
2244       } else {
2245           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2246           imgMaskStr->reset();
2247           for (y = 0; y < maskHeight; y++) {
2248               for (x = 0; x < maskWidth; x++) {
2249                   imgMaskStr->getPixel(buf);
2250                   buf[0]^=maskInvert;
2251                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2252               }
2253           }
2254           delete imgMaskStr;
2255       }
2256       maskStr->close();
2257   }
2258   
2259   imgStr = new ImageStream(str, width, ncomps,bits);
2260   imgStr->reset();
2261
2262   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2263   {
2264       msg("<verbose> Ignoring %d by %d image", width, height);
2265       unsigned char buf[8];
2266       int x,y;
2267       for (y = 0; y < height; ++y)
2268       for (x = 0; x < width; ++x) {
2269           imgStr->getPixel(buf);
2270       }
2271       delete imgStr;
2272       if(maskbitmap)
2273           free(maskbitmap);
2274       return;
2275   }
2276
2277   this->transformXY(state, 0, 1, &x1, &y1);
2278   this->transformXY(state, 0, 0, &x2, &y2);
2279   this->transformXY(state, 1, 0, &x3, &y3);
2280   this->transformXY(state, 1, 1, &x4, &y4);
2281
2282   if(type3active) {
2283       /* as type 3 bitmaps are antialized, we need to place them
2284          at integer coordinates, otherwise flash player's antializing
2285          will kick in and make everything blurry */
2286       x1 = (int)(x1);y1 = (int)(y1);
2287       x2 = (int)(x2);y2 = (int)(y2);
2288       x3 = (int)(x3);y3 = (int)(y3);
2289       x4 = (int)(x4);y4 = (int)(y4);
2290   }
2291
2292   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2293       if(!type3active) {
2294           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2295           gfxglobals->pbminfo = 1;
2296       }
2297       if(mask)
2298       msg("<verbose> drawing %d by %d masked picture", width, height);
2299   }
2300   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2301       msg("<notice> File contains jpeg pictures");
2302       gfxglobals->jpeginfo = 1;
2303   }
2304
2305   if(mask) {
2306       unsigned char buf[8];
2307       int x,y;
2308       unsigned char*pic = new unsigned char[width*height];
2309       gfxcolor_t pal[256];
2310       GfxRGB rgb;
2311       state->getFillRGB(&rgb);
2312
2313       memset(pal,255,sizeof(pal));
2314       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2315       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2316       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2317       pal[0].a = 255;              pal[1].a = 0;
2318     
2319       int numpalette = 2;
2320       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2321       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2322       for (y = 0; y < height; ++y)
2323       for (x = 0; x < width; ++x)
2324       {
2325             imgStr->getPixel(buf);
2326             if(invert) 
2327                 buf[0]=1-buf[0];
2328             pic[width*y+x] = buf[0];
2329       }
2330      
2331       if(type3active) {
2332           unsigned char*pic2 = 0;
2333           numpalette = 16;
2334           
2335           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2336
2337           if(!pic2) {
2338             delete[] pic;
2339             delete imgStr;
2340             return;
2341           }
2342           
2343           width = realwidth;
2344           height = realheight;
2345           delete[] pic;
2346           pic = pic2;
2347           
2348           /* make a black/white palette */
2349
2350           float r = 255./(float)(numpalette-1);
2351           int t;
2352           for(t=0;t<numpalette;t++) {
2353               pal[t].r = colToByte(rgb.r);
2354               pal[t].g = colToByte(rgb.g);
2355               pal[t].b = colToByte(rgb.b);
2356               pal[t].a = (unsigned char)(t*r);
2357           }
2358           
2359       }
2360
2361       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2362       for (y = 0; y < height; ++y) {
2363         for (x = 0; x < width; ++x) {
2364           pic2[width*y+x] = pal[pic[y*width+x]];
2365         }
2366       }
2367       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2368       delete[] pic2;
2369       delete[] pic;
2370       delete imgStr;
2371       if(maskbitmap) free(maskbitmap);
2372       return;
2373   }
2374
2375   int x,y;
2376
2377   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2378       gfxcolor_t*pic=new gfxcolor_t[width*height];
2379       for (y = 0; y < height; ++y) {
2380         for (x = 0; x < width; ++x) {
2381           imgStr->getPixel(pixBuf);
2382           colorMap->getRGB(pixBuf, &rgb);
2383           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2384           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2385           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2386           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2387           if(maskbitmap) {
2388               int x1 = x*maskWidth/width;
2389               int y1 = y*maskHeight/height;
2390               int x2 = (x+1)*maskWidth/width;
2391               int y2 = (y+1)*maskHeight/height;
2392               int xx,yy;
2393               unsigned int alpha=0;
2394               unsigned int count=0;
2395               for(xx=x1;xx<x2;xx++)
2396               for(yy=y1;yy<y2;yy++) {
2397                   alpha += maskbitmap[yy*maskWidth+xx];
2398                   count ++;
2399               }
2400               if(count) {
2401                 pic[width*y+x].a = alpha / count;
2402               } else {
2403                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2404               }
2405           }
2406         }
2407       }
2408       if(str->getKind()==strDCT)
2409           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2410       else
2411           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2412       delete[] pic;
2413       delete imgStr;
2414       if(maskbitmap) free(maskbitmap);
2415       return;
2416   } else {
2417       gfxcolor_t*pic=new gfxcolor_t[width*height];
2418       gfxcolor_t pal[256];
2419       int n = 1 << colorMap->getBits();
2420       int t;
2421       for(t=0;t<256;t++) {
2422           pixBuf[0] = t;
2423           colorMap->getRGB(pixBuf, &rgb);
2424           pal[t].r = (unsigned char)(colToByte(rgb.r));
2425           pal[t].g = (unsigned char)(colToByte(rgb.g));
2426           pal[t].b = (unsigned char)(colToByte(rgb.b));
2427           pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2428       }
2429       for (y = 0; y < height; ++y) {
2430         for (x = 0; x < width; ++x) {
2431           imgStr->getPixel(pixBuf);
2432           pic[width*y+x] = pal[pixBuf[0]];
2433           if(maskColors && *maskColors==pixBuf[0]) {
2434               pic[width*y+x].a = 0;
2435           }
2436         }
2437       }
2438       if(maskbitmap) {
2439           if(maskWidth < width && maskHeight < height) {
2440               for(y = 0; y < height; y++) {
2441                   for (x = 0; x < width; x++) {
2442                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2443                   }
2444               }
2445           } else {
2446               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2447               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2448               double dx = width / (double)maskWidth;
2449               double dy = height / (double)maskHeight;
2450               double yy = 0;
2451               for(y = 0; y < maskHeight; y++) {
2452                   double xx = 0;
2453                   for (x = 0; x < maskWidth; x++) {
2454                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2455                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2456                       xx += dx;
2457                   }
2458                   yy += dy;
2459               }
2460               delete[] pic;
2461               pic = newpic;
2462               width = maskWidth;
2463               height = maskHeight;
2464           }
2465       }
2466       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2467
2468       delete[] pic;
2469       delete imgStr;
2470       if(maskbitmap) free(maskbitmap);
2471       return;
2472   }
2473 }
2474
2475 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2476                                    int width, int height, GBool invert,
2477                                    GBool inlineImg) 
2478 {
2479     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2480     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2481     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2482 }
2483
2484 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2485                          int width, int height, GfxImageColorMap *colorMap,
2486                          int *maskColors, GBool inlineImg)
2487 {
2488     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2489             colorMap?"colorMap":"no colorMap", 
2490             maskColors?"maskColors":"no maskColors",
2491             inlineImg);
2492     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2493             colorMap?"colorMap":"no colorMap", 
2494             maskColors?"maskColors":"no maskColors",
2495             inlineImg);
2496     if(colorMap)
2497         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2498                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2499     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2500 }
2501   
2502 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2503                                int width, int height,
2504                                GfxImageColorMap *colorMap,
2505                                Stream *maskStr, int maskWidth, int maskHeight,
2506                                GBool maskInvert)
2507 {
2508     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2509             colorMap?"colorMap":"no colorMap", 
2510             maskWidth, maskHeight);
2511     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2512             colorMap?"colorMap":"no colorMap", 
2513             maskWidth, maskHeight);
2514     if(colorMap)
2515         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2516                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2517     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2518 }
2519
2520 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2521                                    int width, int height,
2522                                    GfxImageColorMap *colorMap,
2523                                    Stream *maskStr,
2524                                    int maskWidth, int maskHeight,
2525                                    GfxImageColorMap *maskColorMap)
2526 {
2527     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2528             colorMap?"colorMap":"no colorMap", 
2529             maskWidth, maskHeight);
2530     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2531             colorMap?"colorMap":"no colorMap", 
2532             maskWidth, maskHeight);
2533     if(colorMap)
2534         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2535                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2536     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2537 }
2538
2539 void GFXOutputDev::stroke(GfxState *state) 
2540 {
2541     dbg("stroke");
2542
2543     GfxPath * path = state->getPath();
2544     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2545     strokeGfxline(state, line, 0);
2546     gfxline_free(line);
2547 }
2548
2549 void GFXOutputDev::fill(GfxState *state) 
2550 {
2551     gfxcolor_t col = getFillColor(state);
2552     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2553
2554     GfxPath * path = state->getPath();
2555     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2556     if(!config_disable_polygon_conversion) {
2557         gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2558         gfxline_free(line);
2559         line = line2;
2560     }
2561     fillGfxLine(state, line, 0);
2562     gfxline_free(line);
2563 }
2564
2565 void GFXOutputDev::eoFill(GfxState *state) 
2566 {
2567     gfxcolor_t col = getFillColor(state);
2568     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2569
2570     GfxPath * path = state->getPath();
2571     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2572     fillGfxLine(state, line, 1);
2573     gfxline_free(line);
2574 }
2575
2576
2577 static const char* dirseparator()
2578 {
2579 #ifdef WIN32
2580     return "\\";
2581 #else
2582     return "/";
2583 #endif
2584 }
2585
2586 void addGlobalFont(const char*filename)
2587 {
2588     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2589     memset(f, 0, sizeof(fontfile_t));
2590     f->filename = filename;
2591     int len = strlen(filename);
2592     char*r1 = strrchr((char*)filename, '/');
2593     char*r2 = strrchr((char*)filename, '\\');
2594     if(r2>r1)
2595         r1 = r2;
2596     if(r1) {
2597         len = strlen(r1+1);
2598     }
2599     f->len = len;
2600
2601     msg("<verbose> Adding font \"%s\".", filename);
2602     if(global_fonts_next) {
2603         global_fonts_next->next = f;
2604         global_fonts_next = global_fonts_next->next;
2605     } else {
2606         global_fonts_next = global_fonts = f;
2607     }
2608 }
2609
2610 void addGlobalLanguageDir(const char*dir)
2611 {
2612     msg("<notice> Adding %s to language pack directories", dir);
2613
2614     FILE*fi = 0;
2615     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2616     strcpy(config_file, dir);
2617     strcat(config_file, dirseparator());
2618     strcat(config_file, "add-to-xpdfrc");
2619
2620     fi = fopen(config_file, "rb");
2621     if(!fi) {
2622         msg("<error> Could not open %s", config_file);
2623         return;
2624     }
2625     globalParams->parseFile(new GString(config_file), fi);
2626     fclose(fi);
2627 }
2628
2629 void addGlobalFontDir(const char*dirname)
2630 {
2631 #ifdef HAVE_DIRENT_H
2632     DIR*dir = opendir(dirname);
2633     if(!dir) {
2634         msg("<warning> Couldn't open directory %s", dirname);
2635         return;
2636     }
2637     struct dirent*ent;
2638     int fonts = 0;
2639     while(1) {
2640         ent = readdir (dir);
2641         if (!ent) 
2642             break;
2643         int l;
2644         char*name = ent->d_name;
2645         char type = 0;
2646         if(!name) continue;
2647         l=strlen(name);
2648         if(l<4)
2649             continue;
2650         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2651             type=1;
2652         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2653             type=3;
2654         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2655             type=2;
2656         if(type) {
2657             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2658             strcpy(fontname, dirname);
2659             strcat(fontname, dirseparator());
2660             strcat(fontname, name);
2661             addGlobalFont(fontname);
2662             fonts++;
2663         }
2664     }
2665     msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2666     closedir(dir);
2667 #else
2668     msg("<warning> No dirent.h");
2669 #endif
2670 }
2671
2672 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2673                                       GfxColorSpace *blendingColorSpace,
2674                                       GBool isolated, GBool knockout,
2675                                       GBool forSoftMask)
2676 {
2677     const char*colormodename = "";
2678   
2679     if(blendingColorSpace) {
2680         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2681     }
2682     dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2683     msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2684     
2685     //states[statepos].createsoftmask |= forSoftMask;
2686     states[statepos].createsoftmask = forSoftMask;
2687     states[statepos].transparencygroup = !forSoftMask;
2688     states[statepos].isolated = isolated;
2689
2690     states[statepos].olddevice = this->device;
2691     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2692     dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2693
2694     gfxdevice_record_init(this->device, 0);
2695     
2696     /*if(!forSoftMask) { ////???
2697         state->setFillOpacity(0.0);
2698     }*/
2699     dbgindent+=2;
2700 }
2701
2702 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2703 {
2704     dbgindent-=2;
2705     gfxdevice_t*r = this->device;
2706
2707     dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2708     
2709     this->device = states[statepos].olddevice;
2710     if(!this->device) {
2711         msg("<error> Invalid state nesting");
2712     }
2713     states[statepos].olddevice = 0;
2714
2715     gfxresult_t*recording = r->finish(r);
2716     
2717     dbg("                     forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2718     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2719
2720     if(states[statepos].createsoftmask) {
2721         states[statepos-1].softmaskrecording = recording;
2722     } else {
2723         states[statepos-1].grouprecording = recording;
2724     }
2725     
2726     states[statepos].createsoftmask = 0;
2727     states[statepos].transparencygroup = 0;
2728     free(r);
2729 }
2730
2731 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2732 {
2733     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2734                                "colordodge","colorburn","hardlight","softlight","difference",
2735                                "exclusion","hue","saturation","color","luminosity"};
2736
2737     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2738     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2739
2740     if(state->getBlendMode() == gfxBlendNormal)
2741         infofeature("transparency groups");
2742     else {
2743         char buffer[80];
2744         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2745         warnfeature(buffer, 0);
2746     }
2747
2748     gfxresult_t*grouprecording = states[statepos].grouprecording;
2749   
2750     int blendmode = state->getBlendMode();
2751     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2752         int alpha = (int)(state->getFillOpacity()*255);
2753         if(blendmode == gfxBlendMultiply && alpha>200)
2754             alpha = 128;
2755         gfxdevice_t ops;
2756         dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2757         gfxdevice_ops_init(&ops, this->device, alpha);
2758         gfxresult_record_replay(grouprecording, &ops, 0);
2759         ops.finish(&ops);
2760     }
2761     grouprecording->destroy(grouprecording);
2762
2763     states[statepos].grouprecording = 0;
2764 }
2765
2766 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2767 {
2768     if(states[statepos].softmask) {
2769         /* shouldn't happen, but *does* happen */
2770         clearSoftMask(state);
2771     }
2772
2773     /* alpha = 1: retrieve mask values from alpha layer
2774        alpha = 0: retrieve mask values from luminance */
2775
2776     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2777             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2778     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2779             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2780     if(!alpha)
2781         infofeature("soft masks");
2782     else
2783         warnfeature("soft masks from alpha channel",0);
2784    
2785     if(states[statepos].olddevice) {
2786         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2787         exit(1);
2788     }
2789     states[statepos].olddevice = this->device;
2790     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2791     gfxdevice_record_init(this->device, 0);
2792
2793     dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2794     
2795     states[statepos].softmask = 1;
2796     states[statepos].softmask_alpha = alpha;
2797 }
2798
2799 static inline Guchar div255(int x) {
2800   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2801 }
2802
2803 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2804 {
2805     if(c < min) c = min;
2806     if(c > max) c = max;
2807     return c;
2808 }
2809
2810 void GFXOutputDev::clearSoftMask(GfxState *state)
2811 {
2812     if(!states[statepos].softmask)
2813         return;
2814     states[statepos].softmask = 0;
2815     dbg("clearSoftMask statepos=%d", statepos);
2816     msg("<verbose> clearSoftMask statepos=%d", statepos);
2817     
2818     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2819         msg("<error> Error in softmask/tgroup ordering");
2820         return;
2821     }
2822   
2823     gfxresult_t*mask = states[statepos].softmaskrecording;
2824     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2825     this->device = states[statepos].olddevice;
2826
2827     /* get outline of all objects below the soft mask */
2828     gfxdevice_t uniondev;
2829     gfxdevice_union_init(&uniondev, 0);
2830     gfxresult_record_replay(below, &uniondev, 0);
2831     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2832     uniondev.finish(&uniondev);
2833     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2834     gfxline_free(belowoutline);belowoutline=0;
2835 #if 0 
2836     this->device->startclip(this->device, belowoutline);
2837     gfxresult_record_replay(below, this->device, 0);
2838     gfxresult_record_replay(mask, this->device, 0);
2839     this->device->endclip(this->device);
2840 #endif
2841     
2842     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2843     if(width<=0 || height<=0)
2844         return;
2845
2846     gfxdevice_t belowrender;
2847     gfxdevice_render_init(&belowrender);
2848     if(states[statepos+1].isolated) {
2849         belowrender.setparameter(&belowrender, "fillwhite", "1");
2850     }
2851     belowrender.setparameter(&belowrender, "antialize", "2");
2852     belowrender.startpage(&belowrender, width, height);
2853     gfxresult_record_replay(below, &belowrender, 0);
2854     belowrender.endpage(&belowrender);
2855     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2856     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2857     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2858
2859     gfxdevice_t maskrender;
2860     gfxdevice_render_init(&maskrender);
2861     maskrender.startpage(&maskrender, width, height);
2862     gfxresult_record_replay(mask, &maskrender, 0);
2863     maskrender.endpage(&maskrender);
2864     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2865     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2866
2867     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2868         msg("<fatal> Internal error in mask drawing");
2869         return;
2870     }
2871
2872     int y,x;
2873     for(y=0;y<height;y++) {
2874         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2875         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2876         for(x=0;x<width;x++) {
2877             int alpha;
2878             if(states[statepos].softmask_alpha) {
2879                 alpha = l1->a;
2880             } else {
2881                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2882             }
2883
2884             l2->a = div255(alpha*l2->a);
2885
2886             /* DON'T premultiply alpha- this is done by fillbitmap,
2887                depending on the output device */
2888             //l2->r = div255(alpha*l2->r);
2889             //l2->g = div255(alpha*l2->g);
2890             //l2->b = div255(alpha*l2->b);
2891
2892             l1++;
2893             l2++;
2894         }
2895     }
2896     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2897
2898     gfxmatrix_t matrix;
2899     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2900     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2901
2902     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2903
2904     mask->destroy(mask);
2905     below->destroy(below);
2906     maskresult->destroy(maskresult);
2907     belowresult->destroy(belowresult);
2908     states[statepos].softmaskrecording = 0;
2909 }
2910   
2911 //class MemCheck
2912 //{
2913 //    public: ~MemCheck()
2914 //    {
2915 //        delete globalParams;globalParams=0;
2916 //        Object::memCheck(stderr);
2917 //        gMemReport(stderr);
2918 //    }
2919 //} myMemCheck;
2920