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