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