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