fixed mem leaks
[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, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
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
1639     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1640     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1641     Use CropBox, not MediaBox, as page size
1642     */
1643     
1644     /*x1 = crop_x1;
1645     y1 = crop_y1;
1646     x2 = crop_x2;
1647     y2 = crop_y2;*/
1648     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1649     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1650
1651     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1652     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1653
1654     this->clipmovex = -(int)x1;
1655     this->clipmovey = -(int)y1;
1656     
1657     /* apply user clip box */
1658     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1659         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1660         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1661         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1662         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1663         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1664     } else {
1665         x1 += this->clipmovex;
1666         y1 += this->clipmovey;
1667         x2 += this->clipmovex;
1668         y2 += this->clipmovey;
1669     }
1670
1671     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1672     
1673     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);
1674     if(rot!=0)
1675         msg("<verbose> page is rotated %d degrees", rot);
1676
1677     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1678     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1679     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1680     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1681     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1682     device->startclip(device, clippath); outer_clip_box = 1;
1683     if(!config_transparent) {
1684         device->fill(device, clippath, &white);
1685     }
1686     states[statepos].clipbbox.xmin = x1;
1687     states[statepos].clipbbox.ymin = x1;
1688     states[statepos].clipbbox.xmax = x2;
1689     states[statepos].clipbbox.ymax = y2;
1690     
1691     states[statepos].dashPattern = 0;
1692     states[statepos].dashLength = 0;
1693     states[statepos].dashStart = 0;
1694     
1695     this->last_char_gfxfont = 0;
1696 }
1697
1698
1699 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1700 {
1701     double x1, y1, x2, y2;
1702     gfxline_t points[5];
1703     int x, y;
1704     
1705     msg("<debug> drawlink");
1706
1707     link->getRect(&x1, &y1, &x2, &y2);
1708     cvtUserToDev(x1, y1, &x, &y);
1709     points[0].type = gfx_moveTo;
1710     points[0].x = points[4].x = x + user_movex + clipmovex;
1711     points[0].y = points[4].y = y + user_movey + clipmovey;
1712     points[0].next = &points[1];
1713     cvtUserToDev(x2, y1, &x, &y);
1714     points[1].type = gfx_lineTo;
1715     points[1].x = x + user_movex + clipmovex;
1716     points[1].y = y + user_movey + clipmovey;
1717     points[1].next = &points[2];
1718     cvtUserToDev(x2, y2, &x, &y);
1719     points[2].type = gfx_lineTo;
1720     points[2].x = x + user_movex + clipmovex;
1721     points[2].y = y + user_movey + clipmovey;
1722     points[2].next = &points[3];
1723     cvtUserToDev(x1, y2, &x, &y);
1724     points[3].type = gfx_lineTo;
1725     points[3].x = x + user_movex + clipmovex;
1726     points[3].y = y + user_movey + clipmovey;
1727     points[3].next = &points[4];
1728     cvtUserToDev(x1, y1, &x, &y);
1729     points[4].type = gfx_lineTo;
1730     points[4].x = x + user_movex + clipmovex;
1731     points[4].y = y + user_movey + clipmovey;
1732     points[4].next = 0;
1733     
1734     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1735             points[0].x, points[0].y,
1736             points[1].x, points[1].y,
1737             points[2].x, points[2].y,
1738             points[3].x, points[3].y); 
1739     
1740     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1741         dump_outline(points);
1742     }
1743
1744     LinkAction*action=link->getAction();
1745     char buf[128];
1746     char*s = 0;
1747     const char*type = "-?-";
1748     char*named = 0;
1749     int page = -1;
1750     msg("<trace> drawlink action=%d", action->getKind());
1751     switch(action->getKind())
1752     {
1753         case actionGoTo: {
1754             type = "GoTo";
1755             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1756             LinkDest *dest=NULL;
1757             if (ha->getDest()==NULL) 
1758                 dest=catalog->findDest(ha->getNamedDest());
1759             else 
1760                 dest=ha->getDest()->copy();
1761             if (dest){ 
1762               if (dest->isPageRef()){
1763                 Ref pageref=dest->getPageRef();
1764                 page=catalog->findPage(pageref.num,pageref.gen);
1765               }
1766               else  page=dest->getPageNum();
1767               sprintf(buf, "%d", page);
1768               s = strdup(buf);
1769               delete dest;
1770             }
1771         }
1772         break;
1773         case actionGoToR: {
1774             type = "GoToR";
1775             LinkGoToR*l = (LinkGoToR*)action;
1776             GString*g = l->getFileName();
1777             if(g)
1778              s = strdup(g->getCString());
1779             if(!s) {
1780                 /* if the GoToR link has no filename, then
1781                    try to find a refernce in the *local*
1782                    file */
1783                 GString*g = l->getNamedDest();
1784                 if(g)
1785                  s = strdup(g->getCString());
1786             }
1787         }
1788         break;
1789         case actionNamed: {
1790             type = "Named";
1791             LinkNamed*l = (LinkNamed*)action;
1792             GString*name = l->getName();
1793             if(name) {
1794                 s = strdup(name->lowerCase()->getCString());
1795                 named = name->getCString();
1796                 if(!strchr(s,':')) 
1797                 {
1798                     if(strstr(s, "next") || strstr(s, "forward"))
1799                     {
1800                         page = currentpage + 1;
1801                     }
1802                     else if(strstr(s, "prev") || strstr(s, "back"))
1803                     {
1804                         page = currentpage - 1;
1805                     }
1806                     else if(strstr(s, "last") || strstr(s, "end"))
1807                     {
1808                         if(this->page2page && this->num_pages) {
1809                             page = this->page2page[this->num_pages-1];
1810                         }
1811                     }
1812                     else if(strstr(s, "first") || strstr(s, "top"))
1813                     {
1814                         page = 1;
1815                     }
1816                 }
1817             }
1818         }
1819         break;
1820         case actionLaunch: {
1821             type = "Launch";
1822             LinkLaunch*l = (LinkLaunch*)action;
1823             GString * str = new GString(l->getFileName());
1824             GString * params = l->getParams();
1825             if(params)
1826                 str->append(params);
1827             s = strdup(str->getCString());
1828             delete str;
1829         }
1830         break;
1831         case actionURI: {
1832             char*url = 0;
1833             type = "URI";
1834             LinkURI*l = (LinkURI*)action;
1835             GString*g = l->getURI();
1836             if(g) {
1837              url = g->getCString();
1838              s = strdup(url);
1839             }
1840         }
1841         break;
1842         case actionUnknown: {
1843             type = "Unknown";
1844             LinkUnknown*l = (LinkUnknown*)action;
1845             s = strdup("");
1846         }
1847         break;
1848         default: {
1849             msg("<error> Unknown link type!");
1850             break;
1851         }
1852     }
1853
1854     if(!s) s = strdup("-?-");
1855     
1856     msg("<trace> drawlink s=%s", s);
1857
1858     if(!gfxglobals->linkinfo && (page || s))
1859     {
1860         msg("<notice> File contains links");
1861         gfxglobals->linkinfo = 1;
1862     }
1863     
1864     if(page>0) {
1865         int t;
1866         int lpage = -1;
1867         for(t=1;t<=this->num_pages;t++) {
1868             if(this->page2page[t]==page) {
1869                 lpage = t;
1870                 break;
1871             }
1872         }
1873         if(lpage<0) {
1874             lpage = page;
1875         }
1876
1877         char buf[80];
1878         sprintf(buf, "page%d", lpage);
1879         device->drawlink(device, points, buf);
1880     }
1881     else if(s)
1882     {
1883         device->drawlink(device, points, s);
1884         if(this->config_linkdatafile) {
1885             FILE*fi = fopen(config_linkdatafile, "ab+");
1886             fprintf(fi, "%s\n", s);
1887             fclose(fi);
1888         }
1889     }
1890         
1891     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1892     free(s);s=0;
1893 }
1894
1895 void GFXOutputDev::saveState(GfxState *state) {
1896     dbg("saveState %p", state); dbgindent+=2;
1897
1898     msg("<trace> saveState %p", state);
1899     updateAll(state);
1900     if(statepos>=64) {
1901       msg("<fatal> Too many nested states in pdf.");
1902       exit(1);
1903     }
1904     statepos ++;
1905     states[statepos].state = state;
1906     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1907     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1908     states[statepos].clipping = 0;
1909     states[statepos].olddevice = 0;
1910     states[statepos].clipbbox = states[statepos-1].clipbbox;
1911
1912     states[statepos].dashPattern = states[statepos-1].dashPattern;
1913     states[statepos].dashStart = states[statepos-1].dashStart;
1914     states[statepos].dashLength = states[statepos-1].dashLength;
1915 };
1916
1917 void GFXOutputDev::restoreState(GfxState *state) {
1918   dbgindent-=2; dbg("restoreState %p", state);
1919
1920   if(statepos==0) {
1921       msg("<fatal> Invalid restoreState");
1922       exit(1);
1923   }
1924   msg("<trace> restoreState %p%s%s", state,
1925                                   states[statepos].softmask?" (end softmask)":"",
1926                                   states[statepos].clipping?" (end clipping)":"");
1927   if(states[statepos].softmask) {
1928       clearSoftMask(state);
1929   }
1930
1931   if(states[statepos].dashPattern) {
1932       if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1933           free(states[statepos].dashPattern);
1934           states[statepos].dashPattern = 0;
1935       }
1936   }
1937
1938   updateAll(state);
1939   
1940   while(states[statepos].clipping) {
1941       device->endclip(device);
1942       states[statepos].clipping--;
1943   }
1944   if(states[statepos].state!=state) {
1945       msg("<fatal> bad state nesting");
1946       if(verbose) {
1947           int t;
1948           for(t=0;t<=statepos;t++) {
1949               printf("%p ", states[t].state);
1950           }
1951           printf("\n");
1952       }
1953       exit(1);
1954   }
1955   states[statepos].state=0;
1956   statepos--;
1957 }
1958  
1959 void GFXOutputDev::updateLineDash(GfxState *state) 
1960 {
1961     if(states[statepos].dashPattern &&
1962        (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1963         free(states[statepos].dashPattern);
1964         states[statepos].dashPattern = 0;
1965     }
1966     double *pattern = 0;
1967     int dashLength;
1968     double dashStart;
1969     state->getLineDash(&pattern, &dashLength, &dashStart);
1970     msg("<debug> updateLineDash, %d dashes", dashLength);
1971     if(!dashLength) {
1972         states[statepos].dashPattern = 0;
1973         states[statepos].dashLength = 0;
1974     } else {
1975         double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1976         memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1977         states[statepos].dashPattern = p;
1978         states[statepos].dashLength = dashLength;
1979         states[statepos].dashStart = dashStart;
1980     }
1981 }
1982   
1983 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1984 {
1985     this->page2page = page2page;
1986     this->num_pages = num_pages;
1987 }
1988
1989 void GFXOutputDev::updateLineWidth(GfxState *state)
1990 {
1991     double width = state->getTransformedLineWidth();
1992 }
1993
1994 void GFXOutputDev::updateLineCap(GfxState *state)
1995 {
1996     int c = state->getLineCap();
1997 }
1998
1999 void GFXOutputDev::updateLineJoin(GfxState *state)
2000 {
2001     int j = state->getLineJoin();
2002 }
2003
2004 void GFXOutputDev::updateFillColor(GfxState *state) 
2005 {
2006     GfxRGB rgb;
2007     double opaq = state->getFillOpacity();
2008     state->getFillRGB(&rgb);
2009 }
2010 void GFXOutputDev::updateFillOpacity(GfxState *state)
2011 {
2012     GfxRGB rgb;
2013     double opaq = state->getFillOpacity();
2014     state->getFillRGB(&rgb);
2015     dbg("update fillopaq %f", opaq);
2016 }
2017 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2018 {
2019     double opaq = state->getFillOpacity();
2020     dbg("update strokeopaq %f", opaq);
2021 }
2022 void GFXOutputDev::updateFillOverprint(GfxState *state)
2023 {
2024     double opaq = state->getFillOverprint();
2025     dbg("update filloverprint %f", opaq);
2026 }
2027 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2028 {
2029     double opaq = state->getStrokeOverprint();
2030     dbg("update strokeoverprint %f", opaq);
2031 }
2032 void GFXOutputDev::updateTransfer(GfxState *state)
2033 {
2034     dbg("update transfer");
2035 }
2036
2037
2038 void GFXOutputDev::updateStrokeColor(GfxState *state) 
2039 {
2040     GfxRGB rgb;
2041     double opaq = state->getStrokeOpacity();
2042     state->getStrokeRGB(&rgb);
2043 }
2044
2045 void GFXOutputDev::updateFont(GfxState *state) 
2046 {
2047     GfxFont* gfxFont = state->getFont();
2048     if (!gfxFont) {
2049         return; 
2050     }  
2051     char*id = getFontID(gfxFont);
2052     msg("<verbose> Updating font to %s", id);
2053     if(gfxFont->getType() == fontType3) {
2054         infofeature("Type3 fonts");
2055         if(!config_extrafontdata) {
2056             return;
2057         }
2058     }
2059     if(!id) {
2060         msg("<error> Internal Error: FontID is null");
2061         return; 
2062     }
2063
2064     this->current_fontinfo = this->info->getFont(id);
2065
2066     if(!this->current_fontinfo) {
2067         msg("<error> Internal Error: no fontinfo for font %s", id);
2068         return;
2069     }
2070     if(!this->current_fontinfo->seen) {
2071         dumpFontInfo("<verbose>", gfxFont);
2072     }
2073
2074     current_gfxfont = this->current_fontinfo->getGfxFont();
2075     device->addfont(device, current_gfxfont);
2076     free(id);
2077     
2078     updateFontMatrix(state);
2079 }
2080
2081 #define SQR(x) ((x)*(x))
2082
2083 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2084 {
2085     if((newwidth<1 || newheight<1) ||
2086        (width<=newwidth || height<=newheight))
2087         return 0;
2088     unsigned char*newdata;
2089     int x,y;
2090     newdata= (unsigned char*)malloc(newwidth*newheight);
2091     double fx = ((double)width)/newwidth;
2092     double fy = ((double)height)/newheight;
2093     double px = 0;
2094     int blocksize = (int)(8192/(fx*fy));
2095     int r = 8192*256/palettesize;
2096     for(x=0;x<newwidth;x++) {
2097         double ex = px + fx;
2098         int fromx = (int)px;
2099         int tox = (int)ex;
2100         int xweight1 = (int)((1-(px-fromx))*256);
2101         int xweight2 = (int)((ex-tox)*256);
2102         double py =0;
2103         for(y=0;y<newheight;y++) {
2104             double ey = py + fy;
2105             int fromy = (int)py;
2106             int toy = (int)ey;
2107             int yweight1 = (int)((1-(py-fromy))*256);
2108             int yweight2 = (int)((ey-toy)*256);
2109             int a = 0;
2110             int xx,yy;
2111             if(tox>=width) 
2112                 tox = width-1;
2113             if(toy>=height) 
2114                 toy = height-1;
2115             for(xx=fromx;xx<=tox;xx++)
2116             for(yy=fromy;yy<=toy;yy++) {
2117                 int b = 1-data[width*yy+xx];
2118                 int weight=256;
2119                 if(xx==fromx) weight = (weight*xweight1)/256;
2120                 if(xx==tox) weight = (weight*xweight2)/256;
2121                 if(yy==fromy) weight = (weight*yweight1)/256;
2122                 if(yy==toy) weight = (weight*yweight2)/256;
2123                 a+=b*weight;
2124             }
2125             //if(a) a=(palettesize-1)*r/blocksize;
2126             newdata[y*newwidth+x] = (a*blocksize)/r;
2127             py = ey;
2128         }
2129         px = ex;
2130     }
2131     return newdata;
2132 }
2133
2134 #define IMAGE_TYPE_JPEG 0
2135 #define IMAGE_TYPE_LOSSLESS 1
2136
2137 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2138         double x1,double y1,
2139         double x2,double y2,
2140         double x3,double y3,
2141         double x4,double y4, int type, int multiply)
2142 {
2143     gfxcolor_t*newpic=0;
2144     
2145     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2146     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2147    
2148     gfxline_t p1,p2,p3,p4,p5;
2149     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2150     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2151     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2152     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2153     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2154
2155     {p1.x = (int)(p1.x*20)/20.0;
2156      p1.y = (int)(p1.y*20)/20.0;
2157      p2.x = (int)(p2.x*20)/20.0;
2158      p2.y = (int)(p2.y*20)/20.0;
2159      p3.x = (int)(p3.x*20)/20.0;
2160      p3.y = (int)(p3.y*20)/20.0;
2161      p4.x = (int)(p4.x*20)/20.0;
2162      p4.y = (int)(p4.y*20)/20.0;
2163      p5.x = (int)(p5.x*20)/20.0;
2164      p5.y = (int)(p5.y*20)/20.0;
2165     }
2166
2167     gfxmatrix_t m;
2168     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2169     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2170         
2171     m.tx = p1.x - 0.5*multiply;
2172     m.ty = p1.y - 0.5*multiply;
2173
2174     gfximage_t img;
2175     img.data = (gfxcolor_t*)data;
2176     img.width = sizex;
2177     img.height = sizey;
2178   
2179     if(type == IMAGE_TYPE_JPEG)
2180         /* TODO: pass image_dpi to device instead */
2181         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2182
2183     dump_outline(&p1);
2184     dev->fillbitmap(dev, &p1, &img, &m, 0);
2185 }
2186
2187 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2188         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2189 {
2190     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2191 }
2192
2193 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2194         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2195 {
2196     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2197 }
2198
2199
2200 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2201                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2202                                    GBool inlineImg, int mask, int*maskColors,
2203                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2204 {
2205   /* the code in this function is *old*. It's not pretty, but it works. */
2206
2207   double x1,y1,x2,y2,x3,y3,x4,y4;
2208   ImageStream *imgStr;
2209   Guchar pixBuf[4];
2210   GfxRGB rgb;
2211   int ncomps = 1;
2212   int bits = 1;
2213   unsigned char* maskbitmap = 0;
2214                                  
2215   if(colorMap) {
2216     ncomps = colorMap->getNumPixelComps();
2217     bits = colorMap->getBits();
2218   }
2219       
2220   if(maskStr) {
2221       int x,y;
2222       unsigned char buf[8];
2223       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2224       if(maskColorMap) {
2225           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2226           imgMaskStr->reset();
2227           unsigned char pal[256];
2228           int n = 1 << colorMap->getBits();
2229           int t;
2230           for(t=0;t<n;t++) {
2231               GfxGray gray;
2232               pixBuf[0] = t;
2233               maskColorMap->getGray(pixBuf, &gray);
2234               pal[t] = colToByte(gray);
2235           }
2236           for (y = 0; y < maskHeight; y++) {
2237               for (x = 0; x < maskWidth; x++) {
2238                   imgMaskStr->getPixel(buf);
2239                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2240               }
2241           }
2242           delete imgMaskStr;
2243       } else {
2244           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2245           imgMaskStr->reset();
2246           for (y = 0; y < maskHeight; y++) {
2247               for (x = 0; x < maskWidth; x++) {
2248                   imgMaskStr->getPixel(buf);
2249                   buf[0]^=maskInvert;
2250                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2251               }
2252           }
2253           delete imgMaskStr;
2254       }
2255       maskStr->close();
2256   }
2257   
2258   imgStr = new ImageStream(str, width, ncomps,bits);
2259   imgStr->reset();
2260
2261   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2262   {
2263       msg("<verbose> Ignoring %d by %d image", width, height);
2264       unsigned char buf[8];
2265       int x,y;
2266       for (y = 0; y < height; ++y)
2267       for (x = 0; x < width; ++x) {
2268           imgStr->getPixel(buf);
2269       }
2270       delete imgStr;
2271       if(maskbitmap)
2272           free(maskbitmap);
2273       return;
2274   }
2275
2276   this->transformXY(state, 0, 1, &x1, &y1);
2277   this->transformXY(state, 0, 0, &x2, &y2);
2278   this->transformXY(state, 1, 0, &x3, &y3);
2279   this->transformXY(state, 1, 1, &x4, &y4);
2280
2281   if(type3active) {
2282       /* as type 3 bitmaps are antialized, we need to place them
2283          at integer coordinates, otherwise flash player's antializing
2284          will kick in and make everything blurry */
2285       x1 = (int)(x1);y1 = (int)(y1);
2286       x2 = (int)(x2);y2 = (int)(y2);
2287       x3 = (int)(x3);y3 = (int)(y3);
2288       x4 = (int)(x4);y4 = (int)(y4);
2289   }
2290
2291   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2292       if(!type3active) {
2293           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2294           gfxglobals->pbminfo = 1;
2295       }
2296       if(mask)
2297       msg("<verbose> drawing %d by %d masked picture", width, height);
2298   }
2299   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2300       msg("<notice> File contains jpeg pictures");
2301       gfxglobals->jpeginfo = 1;
2302   }
2303
2304   if(mask) {
2305       unsigned char buf[8];
2306       int x,y;
2307       unsigned char*pic = new unsigned char[width*height];
2308       gfxcolor_t pal[256];
2309       GfxRGB rgb;
2310       state->getFillRGB(&rgb);
2311
2312       memset(pal,255,sizeof(pal));
2313       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2314       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2315       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2316       pal[0].a = 255;              pal[1].a = 0;
2317     
2318       int numpalette = 2;
2319       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2320       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2321       for (y = 0; y < height; ++y)
2322       for (x = 0; x < width; ++x)
2323       {
2324             imgStr->getPixel(buf);
2325             if(invert) 
2326                 buf[0]=1-buf[0];
2327             pic[width*y+x] = buf[0];
2328       }
2329      
2330       if(type3active) {
2331           unsigned char*pic2 = 0;
2332           numpalette = 16;
2333           
2334           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2335
2336           if(!pic2) {
2337             delete[] pic;
2338             delete imgStr;
2339             return;
2340           }
2341           
2342           width = realwidth;
2343           height = realheight;
2344           delete[] pic;
2345           pic = pic2;
2346           
2347           /* make a black/white palette */
2348
2349           float r = 255./(float)(numpalette-1);
2350           int t;
2351           for(t=0;t<numpalette;t++) {
2352               pal[t].r = colToByte(rgb.r);
2353               pal[t].g = colToByte(rgb.g);
2354               pal[t].b = colToByte(rgb.b);
2355               pal[t].a = (unsigned char)(t*r);
2356           }
2357           
2358       }
2359
2360       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2361       for (y = 0; y < height; ++y) {
2362         for (x = 0; x < width; ++x) {
2363           pic2[width*y+x] = pal[pic[y*width+x]];
2364         }
2365       }
2366       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2367       delete[] pic2;
2368       delete[] pic;
2369       delete imgStr;
2370       if(maskbitmap) free(maskbitmap);
2371       return;
2372   }
2373
2374   int x,y;
2375
2376   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2377       gfxcolor_t*pic=new gfxcolor_t[width*height];
2378       for (y = 0; y < height; ++y) {
2379         for (x = 0; x < width; ++x) {
2380           imgStr->getPixel(pixBuf);
2381           colorMap->getRGB(pixBuf, &rgb);
2382           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2383           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2384           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2385           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2386           if(maskbitmap) {
2387               int x1 = x*maskWidth/width;
2388               int y1 = y*maskHeight/height;
2389               int x2 = (x+1)*maskWidth/width;
2390               int y2 = (y+1)*maskHeight/height;
2391               int xx,yy;
2392               unsigned int alpha=0;
2393               unsigned int count=0;
2394               for(xx=x1;xx<x2;xx++)
2395               for(yy=y1;yy<y2;yy++) {
2396                   alpha += maskbitmap[yy*maskWidth+xx];
2397                   count ++;
2398               }
2399               if(count) {
2400                 pic[width*y+x].a = alpha / count;
2401               } else {
2402                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2403               }
2404           }
2405         }
2406       }
2407       if(str->getKind()==strDCT)
2408           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2409       else
2410           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2411       delete[] pic;
2412       delete imgStr;
2413       if(maskbitmap) free(maskbitmap);
2414       return;
2415   } else {
2416       gfxcolor_t*pic=new gfxcolor_t[width*height];
2417       gfxcolor_t pal[256];
2418       int n = 1 << colorMap->getBits();
2419       int t;
2420       for(t=0;t<256;t++) {
2421           pixBuf[0] = t;
2422           colorMap->getRGB(pixBuf, &rgb);
2423           pal[t].r = (unsigned char)(colToByte(rgb.r));
2424           pal[t].g = (unsigned char)(colToByte(rgb.g));
2425           pal[t].b = (unsigned char)(colToByte(rgb.b));
2426           pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2427       }
2428       for (y = 0; y < height; ++y) {
2429         for (x = 0; x < width; ++x) {
2430           imgStr->getPixel(pixBuf);
2431           pic[width*y+x] = pal[pixBuf[0]];
2432           if(maskColors && *maskColors==pixBuf[0]) {
2433               pic[width*y+x].a = 0;
2434           }
2435         }
2436       }
2437       if(maskbitmap) {
2438           if(maskWidth < width && maskHeight < height) {
2439               for(y = 0; y < height; y++) {
2440                   for (x = 0; x < width; x++) {
2441                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2442                   }
2443               }
2444           } else {
2445               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2446               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2447               double dx = width / (double)maskWidth;
2448               double dy = height / (double)maskHeight;
2449               double yy = 0;
2450               for(y = 0; y < maskHeight; y++) {
2451                   double xx = 0;
2452                   for (x = 0; x < maskWidth; x++) {
2453                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2454                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2455                       xx += dx;
2456                   }
2457                   yy += dy;
2458               }
2459               delete[] pic;
2460               pic = newpic;
2461               width = maskWidth;
2462               height = maskHeight;
2463           }
2464       }
2465       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2466
2467       delete[] pic;
2468       delete imgStr;
2469       if(maskbitmap) free(maskbitmap);
2470       return;
2471   }
2472 }
2473
2474 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2475                                    int width, int height, GBool invert,
2476                                    GBool inlineImg) 
2477 {
2478     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2479     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2480     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2481 }
2482
2483 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2484                          int width, int height, GfxImageColorMap *colorMap,
2485                          int *maskColors, GBool inlineImg)
2486 {
2487     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2488             colorMap?"colorMap":"no colorMap", 
2489             maskColors?"maskColors":"no maskColors",
2490             inlineImg);
2491     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2492             colorMap?"colorMap":"no colorMap", 
2493             maskColors?"maskColors":"no maskColors",
2494             inlineImg);
2495     if(colorMap)
2496         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2497                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2498     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2499 }
2500   
2501 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2502                                int width, int height,
2503                                GfxImageColorMap *colorMap,
2504                                Stream *maskStr, int maskWidth, int maskHeight,
2505                                GBool maskInvert)
2506 {
2507     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2508             colorMap?"colorMap":"no colorMap", 
2509             maskWidth, maskHeight);
2510     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2511             colorMap?"colorMap":"no colorMap", 
2512             maskWidth, maskHeight);
2513     if(colorMap)
2514         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2515                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2516     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2517 }
2518
2519 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2520                                    int width, int height,
2521                                    GfxImageColorMap *colorMap,
2522                                    Stream *maskStr,
2523                                    int maskWidth, int maskHeight,
2524                                    GfxImageColorMap *maskColorMap)
2525 {
2526     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2527             colorMap?"colorMap":"no colorMap", 
2528             maskWidth, maskHeight);
2529     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2530             colorMap?"colorMap":"no colorMap", 
2531             maskWidth, maskHeight);
2532     if(colorMap)
2533         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2534                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2535     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2536 }
2537
2538 void GFXOutputDev::stroke(GfxState *state) 
2539 {
2540     dbg("stroke");
2541
2542     GfxPath * path = state->getPath();
2543     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2544     strokeGfxline(state, line, 0);
2545     gfxline_free(line);
2546 }
2547
2548 void GFXOutputDev::fill(GfxState *state) 
2549 {
2550     gfxcolor_t col = getFillColor(state);
2551     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2552
2553     GfxPath * path = state->getPath();
2554     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2555     if(!config_disable_polygon_conversion) {
2556         gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2557         gfxline_free(line);
2558         line = line2;
2559     }
2560     fillGfxLine(state, line, 0);
2561     gfxline_free(line);
2562 }
2563
2564 void GFXOutputDev::eoFill(GfxState *state) 
2565 {
2566     gfxcolor_t col = getFillColor(state);
2567     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2568
2569     GfxPath * path = state->getPath();
2570     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2571     fillGfxLine(state, line, 1);
2572     gfxline_free(line);
2573 }
2574
2575
2576 static const char* dirseparator()
2577 {
2578 #ifdef WIN32
2579     return "\\";
2580 #else
2581     return "/";
2582 #endif
2583 }
2584
2585 void addGlobalFont(const char*filename)
2586 {
2587     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2588     memset(f, 0, sizeof(fontfile_t));
2589     f->filename = filename;
2590     int len = strlen(filename);
2591     char*r1 = strrchr((char*)filename, '/');
2592     char*r2 = strrchr((char*)filename, '\\');
2593     if(r2>r1)
2594         r1 = r2;
2595     if(r1) {
2596         len = strlen(r1+1);
2597     }
2598     f->len = len;
2599
2600     msg("<verbose> Adding font \"%s\".", filename);
2601     if(global_fonts_next) {
2602         global_fonts_next->next = f;
2603         global_fonts_next = global_fonts_next->next;
2604     } else {
2605         global_fonts_next = global_fonts = f;
2606     }
2607 }
2608
2609 void addGlobalLanguageDir(const char*dir)
2610 {
2611     msg("<notice> Adding %s to language pack directories", dir);
2612
2613     FILE*fi = 0;
2614     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2615     strcpy(config_file, dir);
2616     strcat(config_file, dirseparator());
2617     strcat(config_file, "add-to-xpdfrc");
2618
2619     fi = fopen(config_file, "rb");
2620     if(!fi) {
2621         msg("<error> Could not open %s", config_file);
2622         return;
2623     }
2624     globalParams->parseFile(new GString(config_file), fi);
2625     fclose(fi);
2626 }
2627
2628 void addGlobalFontDir(const char*dirname)
2629 {
2630 #ifdef HAVE_DIRENT_H
2631     DIR*dir = opendir(dirname);
2632     if(!dir) {
2633         msg("<warning> Couldn't open directory %s", dirname);
2634         return;
2635     }
2636     struct dirent*ent;
2637     int fonts = 0;
2638     while(1) {
2639         ent = readdir (dir);
2640         if (!ent) 
2641             break;
2642         int l;
2643         char*name = ent->d_name;
2644         char type = 0;
2645         if(!name) continue;
2646         l=strlen(name);
2647         if(l<4)
2648             continue;
2649         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2650             type=1;
2651         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2652             type=3;
2653         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2654             type=2;
2655         if(type) {
2656             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2657             strcpy(fontname, dirname);
2658             strcat(fontname, dirseparator());
2659             strcat(fontname, name);
2660             addGlobalFont(fontname);
2661             fonts++;
2662         }
2663     }
2664     msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2665     closedir(dir);
2666 #else
2667     msg("<warning> No dirent.h");
2668 #endif
2669 }
2670
2671 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2672                                       GfxColorSpace *blendingColorSpace,
2673                                       GBool isolated, GBool knockout,
2674                                       GBool forSoftMask)
2675 {
2676     const char*colormodename = "";
2677   
2678     if(blendingColorSpace) {
2679         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2680     }
2681     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);
2682     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);
2683     
2684     //states[statepos].createsoftmask |= forSoftMask;
2685     states[statepos].createsoftmask = forSoftMask;
2686     states[statepos].transparencygroup = !forSoftMask;
2687     states[statepos].isolated = isolated;
2688
2689     states[statepos].olddevice = this->device;
2690     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2691     dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2692
2693     gfxdevice_record_init(this->device, 0);
2694     
2695     /*if(!forSoftMask) { ////???
2696         state->setFillOpacity(0.0);
2697     }*/
2698     dbgindent+=2;
2699 }
2700
2701 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2702 {
2703     dbgindent-=2;
2704     gfxdevice_t*r = this->device;
2705
2706     dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2707     
2708     this->device = states[statepos].olddevice;
2709     if(!this->device) {
2710         msg("<error> Invalid state nesting");
2711     }
2712     states[statepos].olddevice = 0;
2713
2714     gfxresult_t*recording = r->finish(r);
2715     
2716     dbg("                     forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2717     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2718
2719     if(states[statepos].createsoftmask) {
2720         states[statepos-1].softmaskrecording = recording;
2721     } else {
2722         states[statepos-1].grouprecording = recording;
2723     }
2724     
2725     states[statepos].createsoftmask = 0;
2726     states[statepos].transparencygroup = 0;
2727     free(r);
2728 }
2729
2730 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2731 {
2732     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2733                                "colordodge","colorburn","hardlight","softlight","difference",
2734                                "exclusion","hue","saturation","color","luminosity"};
2735
2736     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2737     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2738
2739     if(state->getBlendMode() == gfxBlendNormal)
2740         infofeature("transparency groups");
2741     else {
2742         char buffer[80];
2743         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2744         warnfeature(buffer, 0);
2745     }
2746
2747     gfxresult_t*grouprecording = states[statepos].grouprecording;
2748   
2749     int blendmode = state->getBlendMode();
2750     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2751         int alpha = (int)(state->getFillOpacity()*255);
2752         if(blendmode == gfxBlendMultiply && alpha>200)
2753             alpha = 128;
2754         gfxdevice_t ops;
2755         dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2756         gfxdevice_ops_init(&ops, this->device, alpha);
2757         gfxresult_record_replay(grouprecording, &ops, 0);
2758         ops.finish(&ops);
2759     }
2760     grouprecording->destroy(grouprecording);
2761
2762     states[statepos].grouprecording = 0;
2763 }
2764
2765 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2766 {
2767     if(states[statepos].softmask) {
2768         /* shouldn't happen, but *does* happen */
2769         clearSoftMask(state);
2770     }
2771
2772     /* alpha = 1: retrieve mask values from alpha layer
2773        alpha = 0: retrieve mask values from luminance */
2774
2775     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2776             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2777     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2778             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2779     if(!alpha)
2780         infofeature("soft masks");
2781     else
2782         warnfeature("soft masks from alpha channel",0);
2783    
2784     if(states[statepos].olddevice) {
2785         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2786         exit(1);
2787     }
2788     states[statepos].olddevice = this->device;
2789     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2790     gfxdevice_record_init(this->device, 0);
2791
2792     dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2793     
2794     states[statepos].softmask = 1;
2795     states[statepos].softmask_alpha = alpha;
2796 }
2797
2798 static inline Guchar div255(int x) {
2799   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2800 }
2801
2802 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2803 {
2804     if(c < min) c = min;
2805     if(c > max) c = max;
2806     return c;
2807 }
2808
2809 void GFXOutputDev::clearSoftMask(GfxState *state)
2810 {
2811     if(!states[statepos].softmask)
2812         return;
2813     states[statepos].softmask = 0;
2814     dbg("clearSoftMask statepos=%d", statepos);
2815     msg("<verbose> clearSoftMask statepos=%d", statepos);
2816     
2817     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2818         msg("<error> Error in softmask/tgroup ordering");
2819         return;
2820     }
2821   
2822     gfxresult_t*mask = states[statepos].softmaskrecording;
2823     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2824     this->device = states[statepos].olddevice;
2825
2826     /* get outline of all objects below the soft mask */
2827     gfxdevice_t uniondev;
2828     gfxdevice_union_init(&uniondev, 0);
2829     gfxresult_record_replay(below, &uniondev, 0);
2830     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2831     uniondev.finish(&uniondev);
2832     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2833     gfxline_free(belowoutline);belowoutline=0;
2834 #if 0 
2835     this->device->startclip(this->device, belowoutline);
2836     gfxresult_record_replay(below, this->device, 0);
2837     gfxresult_record_replay(mask, this->device, 0);
2838     this->device->endclip(this->device);
2839 #endif
2840     
2841     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2842     if(width<=0 || height<=0)
2843         return;
2844
2845     gfxdevice_t belowrender;
2846     gfxdevice_render_init(&belowrender);
2847     if(states[statepos+1].isolated) {
2848         belowrender.setparameter(&belowrender, "fillwhite", "1");
2849     }
2850     belowrender.setparameter(&belowrender, "antialize", "2");
2851     belowrender.startpage(&belowrender, width, height);
2852     gfxresult_record_replay(below, &belowrender, 0);
2853     belowrender.endpage(&belowrender);
2854     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2855     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2856     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2857
2858     gfxdevice_t maskrender;
2859     gfxdevice_render_init(&maskrender);
2860     maskrender.startpage(&maskrender, width, height);
2861     gfxresult_record_replay(mask, &maskrender, 0);
2862     maskrender.endpage(&maskrender);
2863     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2864     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2865
2866     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2867         msg("<fatal> Internal error in mask drawing");
2868         return;
2869     }
2870
2871     int y,x;
2872     for(y=0;y<height;y++) {
2873         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2874         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2875         for(x=0;x<width;x++) {
2876             int alpha;
2877             if(states[statepos].softmask_alpha) {
2878                 alpha = l1->a;
2879             } else {
2880                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2881             }
2882
2883             l2->a = div255(alpha*l2->a);
2884
2885             /* DON'T premultiply alpha- this is done by fillbitmap,
2886                depending on the output device */
2887             //l2->r = div255(alpha*l2->r);
2888             //l2->g = div255(alpha*l2->g);
2889             //l2->b = div255(alpha*l2->b);
2890
2891             l1++;
2892             l2++;
2893         }
2894     }
2895     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2896
2897     gfxmatrix_t matrix;
2898     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2899     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2900
2901     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2902
2903     mask->destroy(mask);
2904     below->destroy(below);
2905     maskresult->destroy(maskresult);
2906     belowresult->destroy(belowresult);
2907     states[statepos].softmaskrecording = 0;
2908 }
2909   
2910 //class MemCheck
2911 //{
2912 //    public: ~MemCheck()
2913 //    {
2914 //        delete globalParams;globalParams=0;
2915 //        Object::memCheck(stderr);
2916 //        gMemReport(stderr);
2917 //    }
2918 //} myMemCheck;
2919