fixed crash in antialize(), truncate image coordinates to integer before bitmap-filling
[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=1;
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         f = 1.0; //disable dash length transform for now
1087
1088         msg("<trace> %d dashes", this->dashLength);
1089         msg("<trace> |  phase: %f", this->dashStart);
1090         for(t=0;t<this->dashLength;t++) {
1091             dash[t] = (float)this->dashPattern[t] * f;
1092             msg("<trace> |  d%-3d: %f", t, this->dashPattern[t]);
1093         }
1094         dash[this->dashLength] = -1;
1095         if(getLogLevel() >= LOGLEVEL_TRACE) {
1096             dump_outline(line);
1097         }
1098
1099         line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1100         line = line2;
1101         free(dash);
1102         msg("<trace> After dashing:");
1103     }
1104     
1105     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1106         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1107                 width,
1108                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1109                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1110                 this->dashLength,
1111                 col.r,col.g,col.b,col.a
1112                 );
1113         dump_outline(line);
1114     }
1115
1116     if(flags&STROKE_FILL) {
1117         gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1118         gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1119         if(getLogLevel() >= LOGLEVEL_TRACE)  {
1120             dump_outline(gfxline);
1121         }
1122         if(!gfxline) {
1123             msg("<warning> Empty polygon (resulting from stroked line)");
1124         }
1125         if(flags&STROKE_CLIP) {
1126             device->startclip(device, gfxline);
1127             states[statepos].clipping++;
1128         } else {
1129             device->fill(device, gfxline, &col);
1130         }
1131         gfxline_free(gfxline);
1132         gfxpoly_free(poly);
1133     } else {
1134         if(flags&STROKE_CLIP) 
1135             msg("<error> Stroke&clip not supported at the same time");
1136         device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1137     }
1138     
1139     if(line2)
1140         gfxline_free(line2);
1141 }
1142
1143 gfxcolor_t getFillColor(GfxState * state)
1144 {
1145     GfxRGB rgb;
1146     double opaq = state->getFillOpacity();
1147     state->getFillRGB(&rgb);
1148     gfxcolor_t col;
1149     col.r = colToByte(rgb.r);
1150     col.g = colToByte(rgb.g);
1151     col.b = colToByte(rgb.b);
1152     col.a = (unsigned char)(opaq*255);
1153     return col;
1154 }
1155
1156 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
1157 {
1158     gfxcolor_t col = getFillColor(state);
1159
1160     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1161         msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1162         dump_outline(line);
1163     }
1164     device->fill(device, line, &col);
1165 }
1166
1167 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
1168 {
1169     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1170         dump_outline(line);
1171     }
1172     gfxbbox_t bbox = gfxline_getbbox(line);
1173     gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1174
1175     device->startclip(device, line);
1176     states[statepos].clipping++;
1177 }
1178
1179 void GFXOutputDev::clip(GfxState *state) 
1180 {
1181     GfxPath * path = state->getPath();
1182     msg("<trace> clip");
1183     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1184     if(config_optimize_polygons) {
1185         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1186         gfxline_free(line);
1187         line = line2;
1188     }
1189     clipToGfxLine(state, line);
1190     gfxline_free(line);
1191 }
1192
1193 void GFXOutputDev::eoClip(GfxState *state) 
1194 {
1195     GfxPath * path = state->getPath();
1196     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1197     clipToGfxLine(state, line);
1198     gfxline_free(line);
1199 }
1200 void GFXOutputDev::clipToStrokePath(GfxState *state)
1201 {
1202     GfxPath * path = state->getPath();
1203     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1204
1205     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1206         double width = state->getTransformedLineWidth();
1207         msg("<trace> cliptostrokepath width=%f", width);
1208         dump_outline(line);
1209     }
1210
1211     strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1212     gfxline_free(line);
1213 }
1214
1215 void GFXOutputDev::finish()
1216 {
1217     if(outer_clip_box) {
1218         if(device) {
1219             device->endclip(device);
1220         }
1221         outer_clip_box = 0;
1222     }
1223 }
1224
1225 GFXOutputDev::~GFXOutputDev() 
1226 {
1227     finish();
1228
1229     if(this->pages) {
1230         free(this->pages); this->pages = 0;
1231     }
1232     
1233     feature_t*f = this->featurewarnings;
1234     while(f) {
1235         feature_t*next = f->next;
1236         if(f->string) {
1237             free(f->string);f->string =0;
1238         }
1239         f->next = 0;
1240         free(f);
1241         f = next;
1242     }
1243     this->featurewarnings = 0;
1244
1245     gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1246 };
1247 GBool GFXOutputDev::upsideDown() 
1248 {
1249     return gTrue;
1250 };
1251 GBool GFXOutputDev::useDrawChar() 
1252 {
1253     return gTrue;
1254 }
1255
1256 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1257                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1258
1259 static char tmp_printstr[4096];
1260 char* makeStringPrintable(char*str)
1261 {
1262     int len = strlen(str);
1263     int dots = 0;
1264     if(len>=80) {
1265         len = 80;
1266         dots = 1;
1267     }
1268     int t;
1269     for(t=0;t<len;t++) {
1270         char c = str[t];
1271         if(c<32 || c>124) {
1272             c = '.';
1273         }
1274         tmp_printstr[t] = c;
1275     }
1276     if(dots) {
1277         tmp_printstr[len++] = '.';
1278         tmp_printstr[len++] = '.';
1279         tmp_printstr[len++] = '.';
1280     }
1281     tmp_printstr[len] = 0;
1282     return tmp_printstr;
1283 }
1284 #define INTERNAL_FONT_SIZE 1024.0
1285 void GFXOutputDev::updateFontMatrix(GfxState*state)
1286 {
1287     double* ctm = state->getCTM();
1288     double fontSize = state->getFontSize();
1289     double*textMat = state->getTextMat();
1290
1291     /*  taking the absolute value of horizScaling seems to be required for
1292         some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1293     double hscale = fabs(state->getHorizScaling());
1294    
1295     // from xpdf-3.02/SplashOutputDev:updateFont
1296     double mm11 = textMat[0] * fontSize * hscale;
1297     double mm12 = textMat[1] * fontSize * hscale;
1298     double mm21 = textMat[2] * fontSize;
1299     double mm22 = textMat[3] * fontSize;
1300
1301     // multiply with ctm, like state->getFontTransMat() does
1302     this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1303     this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1304     this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1305     this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1306     this->current_font_matrix.tx = 0;
1307     this->current_font_matrix.ty = 0;
1308 }
1309
1310 void GFXOutputDev::beginString(GfxState *state, GString *s) 
1311
1312     int render = state->getRender();
1313     if(current_text_stroke) {
1314         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1315     }
1316
1317     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1318 }
1319
1320 static gfxline_t* mkEmptyGfxShape(double x, double y)
1321 {
1322     gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1323     line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1324     return line;
1325 }
1326
1327 static char isValidUnicode(int c)
1328 {
1329     if(c>=32 && c<0x2fffe)
1330         return 1;
1331     return 0;
1332 }
1333
1334 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1335                         double dx, double dy,
1336                         double originX, double originY,
1337                         CharCode charid, int nBytes, Unicode *_u, int uLen)
1338 {
1339     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1340         msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1341         return;
1342     }
1343   
1344     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1345
1346     int render = state->getRender();
1347     gfxcolor_t col = getFillColor(state);
1348
1349     // check for invisible text -- this is used by Acrobat Capture
1350     if (render == RENDER_INVISIBLE) {
1351         col.a = 255;
1352         if(!config_extrafontdata)
1353             return;
1354     }
1355
1356     GfxFont*font = state->getFont();
1357
1358     if(font->getType() == fontType3) {
1359         /* type 3 chars are passed as graphics */
1360         msg("<debug> type3 char at %f/%f", x, y);
1361         return;
1362     }
1363
1364     Unicode u = uLen?(_u[0]):0;
1365     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);
1366
1367     gfxmatrix_t m = this->current_font_matrix;
1368     this->transformXY(state, x, y, &m.tx, &m.ty);
1369
1370     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1371         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1372     } else {
1373         msg("<debug> Drawing glyph %d as shape", charid);
1374         if(!textmodeinfo) {
1375             msg("<notice> Some texts will be rendered as shape");
1376             textmodeinfo = 1;
1377         }
1378         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1379         gfxline_t*tglyph = gfxline_clone(glyph);
1380         gfxline_transform(tglyph, &m);
1381         if((render&3) != RENDER_INVISIBLE) {
1382             gfxline_t*add = gfxline_clone(tglyph);
1383             current_text_stroke = gfxline_append(current_text_stroke, add);
1384         }
1385         if(render&RENDER_CLIP) {
1386             gfxline_t*add = gfxline_clone(tglyph);
1387             current_text_clip = gfxline_append(current_text_clip, add);
1388             if(!current_text_clip) {
1389                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1390             }
1391         }
1392         gfxline_free(tglyph);
1393     }
1394 }
1395
1396 void GFXOutputDev::endString(GfxState *state) 
1397
1398     int render = state->getRender();
1399     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1400     
1401     if(current_text_stroke) {
1402         /* fillstroke and stroke text rendering objects we can process right
1403            now (as there may be texts of other rendering modes in this
1404            text object)- clipping objects have to wait until endTextObject,
1405            however */
1406         device->setparameter(device, "mark","TXT");
1407         if((render&3) == RENDER_FILL) {
1408             fillGfxLine(state, current_text_stroke);
1409             gfxline_free(current_text_stroke);
1410             current_text_stroke = 0;
1411         } else if((render&3) == RENDER_FILLSTROKE) {
1412             fillGfxLine(state, current_text_stroke);
1413             strokeGfxline(state, current_text_stroke,0);
1414             gfxline_free(current_text_stroke);
1415             current_text_stroke = 0;
1416         } else if((render&3) == RENDER_STROKE) {
1417             strokeGfxline(state, current_text_stroke,0);
1418             gfxline_free(current_text_stroke);
1419             current_text_stroke = 0;
1420         }
1421         device->setparameter(device, "mark","");
1422     }
1423 }    
1424
1425 void GFXOutputDev::endTextObject(GfxState *state)
1426 {
1427     int render = state->getRender();
1428     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1429     
1430     if(current_text_clip) {
1431         device->setparameter(device, "mark","TXT");
1432         clipToGfxLine(state, current_text_clip);
1433         device->setparameter(device, "mark","");
1434         gfxline_free(current_text_clip);
1435         current_text_clip = 0;
1436     }
1437 }
1438
1439 /* the logic seems to be as following:
1440    first, beginType3Char is called, with the charcode and the coordinates.
1441    if this function returns true, it already knew about the char and has now drawn it.
1442    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1443    called with some parameters.
1444    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1445    at the position first passed to beginType3Char). the char ends with endType3Char.
1446
1447    The drawing operations between beginType3Char and endType3Char are somewhat different to
1448    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1449    color determines the color of a font)
1450 */
1451
1452 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1453 {
1454     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1455     type3active = 1;
1456     
1457     if(config_extrafontdata && current_fontinfo) {
1458
1459         gfxmatrix_t m = this->current_font_matrix;
1460         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1461         m.m00*=INTERNAL_FONT_SIZE;
1462         m.m01*=INTERNAL_FONT_SIZE;
1463         m.m10*=INTERNAL_FONT_SIZE;
1464         m.m11*=INTERNAL_FONT_SIZE;
1465
1466         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1467             msg("<error> Invalid charid %d for font", charid);
1468             return gFalse;
1469         }
1470         gfxcolor_t col={0,0,0,0};
1471         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1472         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1473     }
1474
1475
1476     /* the character itself is going to be passed using the draw functions */
1477     return gFalse; /* gTrue= is_in_cache? */
1478 }
1479
1480 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1481 }
1482 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1483 }
1484
1485 void GFXOutputDev::endType3Char(GfxState *state)
1486 {
1487     type3active = 0;
1488     msg("<debug> endType3Char");
1489 }
1490
1491 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1492 {
1493     this->currentpage = pageNum;
1494     double x1,y1,x2,y2;
1495     int rot = doc->getPageRotate(1);
1496     gfxcolor_t white = {255,255,255,255};
1497     gfxcolor_t black = {255,0,0,0};
1498     laststate = state;
1499     gfxline_t clippath[5];
1500
1501     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1502     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1503     Use CropBox, not MediaBox, as page size
1504     */
1505     
1506     /*x1 = crop_x1;
1507     y1 = crop_y1;
1508     x2 = crop_x2;
1509     y2 = crop_y2;*/
1510     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1511     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1512
1513     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1514     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1515
1516     this->clipmovex = -(int)x1;
1517     this->clipmovey = -(int)y1;
1518     
1519     /* apply user clip box */
1520     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1521         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1522         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1523         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1524         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1525         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1526     }
1527
1528     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1529     
1530     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);
1531     if(rot!=0)
1532         msg("<verbose> page is rotated %d degrees", rot);
1533
1534     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1535     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1536     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1537     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1538     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1539     device->startclip(device, clippath); outer_clip_box = 1;
1540     if(!config_transparent) {
1541         device->fill(device, clippath, &white);
1542     }
1543     states[statepos].clipbbox.xmin = x1;
1544     states[statepos].clipbbox.ymin = x1;
1545     states[statepos].clipbbox.xmax = x2;
1546     states[statepos].clipbbox.ymax = y2;
1547     
1548     this->dashPattern = 0;
1549     this->dashLength = 0;
1550     this->dashStart = 0;
1551 }
1552
1553
1554 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1555 {
1556     double x1, y1, x2, y2;
1557     gfxline_t points[5];
1558     int x, y;
1559     
1560     msg("<debug> drawlink");
1561
1562     link->getRect(&x1, &y1, &x2, &y2);
1563     cvtUserToDev(x1, y1, &x, &y);
1564     points[0].type = gfx_moveTo;
1565     points[0].x = points[4].x = x + user_movex + clipmovex;
1566     points[0].y = points[4].y = y + user_movey + clipmovey;
1567     points[0].next = &points[1];
1568     cvtUserToDev(x2, y1, &x, &y);
1569     points[1].type = gfx_lineTo;
1570     points[1].x = x + user_movex + clipmovex;
1571     points[1].y = y + user_movey + clipmovey;
1572     points[1].next = &points[2];
1573     cvtUserToDev(x2, y2, &x, &y);
1574     points[2].type = gfx_lineTo;
1575     points[2].x = x + user_movex + clipmovex;
1576     points[2].y = y + user_movey + clipmovey;
1577     points[2].next = &points[3];
1578     cvtUserToDev(x1, y2, &x, &y);
1579     points[3].type = gfx_lineTo;
1580     points[3].x = x + user_movex + clipmovex;
1581     points[3].y = y + user_movey + clipmovey;
1582     points[3].next = &points[4];
1583     cvtUserToDev(x1, y1, &x, &y);
1584     points[4].type = gfx_lineTo;
1585     points[4].x = x + user_movex + clipmovex;
1586     points[4].y = y + user_movey + clipmovey;
1587     points[4].next = 0;
1588     
1589     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1590             points[0].x, points[0].y,
1591             points[1].x, points[1].y,
1592             points[2].x, points[2].y,
1593             points[3].x, points[3].y); 
1594     
1595     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1596         dump_outline(points);
1597     }
1598
1599     LinkAction*action=link->getAction();
1600     char buf[128];
1601     char*s = 0;
1602     const char*type = "-?-";
1603     char*named = 0;
1604     int page = -1;
1605     msg("<trace> drawlink action=%d", action->getKind());
1606     switch(action->getKind())
1607     {
1608         case actionGoTo: {
1609             type = "GoTo";
1610             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1611             LinkDest *dest=NULL;
1612             if (ha->getDest()==NULL) 
1613                 dest=catalog->findDest(ha->getNamedDest());
1614             else dest=ha->getDest();
1615             if (dest){ 
1616               if (dest->isPageRef()){
1617                 Ref pageref=dest->getPageRef();
1618                 page=catalog->findPage(pageref.num,pageref.gen);
1619               }
1620               else  page=dest->getPageNum();
1621               sprintf(buf, "%d", page);
1622               s = strdup(buf);
1623             }
1624         }
1625         break;
1626         case actionGoToR: {
1627             type = "GoToR";
1628             LinkGoToR*l = (LinkGoToR*)action;
1629             GString*g = l->getFileName();
1630             if(g)
1631              s = strdup(g->getCString());
1632             if(!s) {
1633                 /* if the GoToR link has no filename, then
1634                    try to find a refernce in the *local*
1635                    file */
1636                 GString*g = l->getNamedDest();
1637                 if(g)
1638                  s = strdup(g->getCString());
1639             }
1640         }
1641         break;
1642         case actionNamed: {
1643             type = "Named";
1644             LinkNamed*l = (LinkNamed*)action;
1645             GString*name = l->getName();
1646             if(name) {
1647                 s = strdup(name->lowerCase()->getCString());
1648                 named = name->getCString();
1649                 if(!strchr(s,':')) 
1650                 {
1651                     if(strstr(s, "next") || strstr(s, "forward"))
1652                     {
1653                         page = currentpage + 1;
1654                     }
1655                     else if(strstr(s, "prev") || strstr(s, "back"))
1656                     {
1657                         page = currentpage - 1;
1658                     }
1659                     else if(strstr(s, "last") || strstr(s, "end"))
1660                     {
1661                         if(pages && pagepos>0)
1662                             page = pages[pagepos-1];
1663                     }
1664                     else if(strstr(s, "first") || strstr(s, "top"))
1665                     {
1666                         page = 1;
1667                     }
1668                 }
1669             }
1670         }
1671         break;
1672         case actionLaunch: {
1673             type = "Launch";
1674             LinkLaunch*l = (LinkLaunch*)action;
1675             GString * str = new GString(l->getFileName());
1676             GString * params = l->getParams();
1677             if(params)
1678                 str->append(params);
1679             s = strdup(str->getCString());
1680             delete str;
1681         }
1682         break;
1683         case actionURI: {
1684             char*url = 0;
1685             type = "URI";
1686             LinkURI*l = (LinkURI*)action;
1687             GString*g = l->getURI();
1688             if(g) {
1689              url = g->getCString();
1690              s = strdup(url);
1691             }
1692         }
1693         break;
1694         case actionUnknown: {
1695             type = "Unknown";
1696             LinkUnknown*l = (LinkUnknown*)action;
1697             s = strdup("");
1698         }
1699         break;
1700         default: {
1701             msg("<error> Unknown link type!");
1702             break;
1703         }
1704     }
1705
1706     if(!s) s = strdup("-?-");
1707     
1708     msg("<trace> drawlink s=%s", s);
1709
1710     if(!linkinfo && (page || s))
1711     {
1712         msg("<notice> File contains links");
1713         linkinfo = 1;
1714     }
1715     
1716     if(page>0)
1717     {
1718         int t;
1719         int lpage = -1;
1720         for(t=1;t<=pagepos;t++) {
1721             if(pages[t]==page) {
1722                 lpage = t;
1723                 break;
1724             }
1725         }
1726         if(lpage<0) {
1727             lpage = page;
1728         }
1729         char buf[80];
1730         sprintf(buf, "page%d", lpage);
1731         device->drawlink(device, points, buf);
1732     }
1733     else if(s)
1734     {
1735         device->drawlink(device, points, s);
1736     }
1737
1738     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1739     free(s);s=0;
1740 }
1741
1742 void GFXOutputDev::saveState(GfxState *state) {
1743     dbg("saveState"); dbgindent+=2;
1744
1745     msg("<trace> saveState");
1746     updateAll(state);
1747     if(statepos>=64) {
1748       msg("<error> Too many nested states in pdf.");
1749       return;
1750     }
1751     statepos ++;
1752     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1753     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1754     states[statepos].clipping = 0;
1755     states[statepos].clipbbox = states[statepos-1].clipbbox;
1756 };
1757
1758 void GFXOutputDev::restoreState(GfxState *state) {
1759   dbgindent-=2; dbg("restoreState");
1760
1761   if(statepos==0) {
1762       msg("<error> Invalid restoreState");
1763       return;
1764   }
1765   msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1766                                   states[statepos].clipping?" (end clipping)":"");
1767   if(states[statepos].softmask) {
1768       clearSoftMask(state);
1769   }
1770   updateAll(state);
1771   while(states[statepos].clipping) {
1772       device->endclip(device);
1773       states[statepos].clipping--;
1774   }
1775   statepos--;
1776 }
1777  
1778 void GFXOutputDev::updateLineDash(GfxState *state) 
1779 {
1780     state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1781     msg("<debug> updateLineDash, %d dashes", this->dashLength);
1782     if(!this->dashLength) {
1783         this->dashPattern = 0;
1784     }
1785 }
1786
1787 void GFXOutputDev::updateLineWidth(GfxState *state)
1788 {
1789     double width = state->getTransformedLineWidth();
1790 }
1791
1792 void GFXOutputDev::updateLineCap(GfxState *state)
1793 {
1794     int c = state->getLineCap();
1795 }
1796
1797 void GFXOutputDev::updateLineJoin(GfxState *state)
1798 {
1799     int j = state->getLineJoin();
1800 }
1801
1802 void GFXOutputDev::updateFillColor(GfxState *state) 
1803 {
1804     GfxRGB rgb;
1805     double opaq = state->getFillOpacity();
1806     state->getFillRGB(&rgb);
1807 }
1808 void GFXOutputDev::updateFillOpacity(GfxState *state)
1809 {
1810     GfxRGB rgb;
1811     double opaq = state->getFillOpacity();
1812     state->getFillRGB(&rgb);
1813     dbg("update fillopaq %f", opaq);
1814 }
1815 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1816 {
1817     double opaq = state->getFillOpacity();
1818     dbg("update strokeopaq %f", opaq);
1819 }
1820 void GFXOutputDev::updateFillOverprint(GfxState *state)
1821 {
1822     double opaq = state->getFillOverprint();
1823     dbg("update filloverprint %f", opaq);
1824 }
1825 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1826 {
1827     double opaq = state->getStrokeOverprint();
1828     dbg("update strokeoverprint %f", opaq);
1829 }
1830 void GFXOutputDev::updateTransfer(GfxState *state)
1831 {
1832     dbg("update transfer");
1833 }
1834
1835
1836 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1837 {
1838     GfxRGB rgb;
1839     double opaq = state->getStrokeOpacity();
1840     state->getStrokeRGB(&rgb);
1841 }
1842
1843
1844 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1845 {
1846     gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1847     memset(font, 0, sizeof(gfxfont_t));
1848
1849     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1850     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1851     font->id = 0;
1852     int t;
1853     
1854     double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1855     double scale = 1;
1856     //printf("%d glyphs\n", font->num_glyphs);
1857     font->num_glyphs = 0;
1858     for(t=0;t<src->num_glyphs;t++) {
1859         if(src->glyphs[t]) {
1860             SplashPath*path = src->glyphs[t]->path;
1861             int len = path?path->getLength():0;
1862             //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1863             gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1864             src->glyphs[t]->glyphid = font->num_glyphs;
1865             glyph->unicode = src->glyphs[t]->unicode;
1866             if(glyph->unicode >= font->max_unicode)
1867                 font->max_unicode = glyph->unicode+1;
1868             gfxdrawer_t drawer;
1869             gfxdrawer_target_gfxline(&drawer);
1870             int s;
1871             int count = 0;
1872             double xmax = 0;
1873             for(s=0;s<len;s++) {
1874                 Guchar f;
1875                 double x, y;
1876                 path->getPoint(s, &x, &y, &f);
1877                 if(!s || x > xmax)
1878                     xmax = x;
1879                 if(f&splashPathFirst) {
1880                     drawer.moveTo(&drawer, x*scale, y*scale);
1881                 }
1882                 if(f&splashPathCurve) {
1883                     double x2,y2;
1884                     path->getPoint(++s, &x2, &y2, &f);
1885                     if(f&splashPathCurve) {
1886                         double x3,y3;
1887                         path->getPoint(++s, &x3, &y3, &f);
1888                         gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1889                     } else {
1890                         drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1891                     }
1892                 } else {
1893                     drawer.lineTo(&drawer, x*scale, y*scale);
1894                 }
1895              //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1896              //                           (f&splashPathFirst)?"first":"",
1897              //                           (f&splashPathLast)?"last":"");
1898             }
1899             glyph->line = (gfxline_t*)drawer.result(&drawer);
1900             glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1901             font->num_glyphs++;
1902         }
1903     }
1904     font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1905     memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1906     for(t=0;t<font->num_glyphs;t++) {
1907         if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1908             font->unicode2glyph[font->glyphs[t].unicode] = t;
1909         }
1910
1911     }
1912     msg("<trace> %d glyphs.", t, font->num_glyphs);
1913     return font;
1914 }
1915
1916 void GFXOutputDev::updateFont(GfxState *state) 
1917 {
1918     GfxFont* gfxFont = state->getFont();
1919     if (!gfxFont) {
1920         return; 
1921     }  
1922     char*id = getFontID(gfxFont);
1923     msg("<verbose> Updating font to %s", id);
1924     if(gfxFont->getType() == fontType3) {
1925         infofeature("Type3 fonts");
1926         if(!config_extrafontdata) {
1927             return;
1928         }
1929     }
1930     if(!id) {
1931         msg("<error> Internal Error: FontID is null");
1932         return; 
1933     }
1934
1935     this->current_fontinfo = this->info->getFont(id);
1936     if(!this->current_fontinfo) {
1937         msg("<error> Internal Error: no fontinfo for font %s", id);
1938         return;
1939     }
1940     if(!this->current_fontinfo->seen) {
1941         dumpFontInfo("<verbose>", gfxFont);
1942     }
1943     
1944     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1945     if(!font) {
1946         font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1947         font->id = strdup(id);
1948         this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1949     }
1950     device->addfont(device, font);
1951
1952     current_gfxfont = font;
1953     free(id);
1954
1955     updateFontMatrix(state);
1956 }
1957
1958 #define SQR(x) ((x)*(x))
1959
1960 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1961 {
1962     if((newwidth<2 || newheight<2) ||
1963        (width<=newwidth || height<=newheight))
1964         return 0;
1965     unsigned char*newdata;
1966     int x,y;
1967     newdata= (unsigned char*)malloc(newwidth*newheight);
1968     double fx = ((double)width)/newwidth;
1969     double fy = ((double)height)/newheight;
1970     double px = 0;
1971     int blocksize = (int)(8192/(fx*fy));
1972     int r = 8192*256/palettesize;
1973     for(x=0;x<newwidth;x++) {
1974         double ex = px + fx;
1975         int fromx = (int)px;
1976         int tox = (int)ex;
1977         int xweight1 = (int)(((fromx+1)-px)*256);
1978         int xweight2 = (int)((ex-tox)*256);
1979         double py =0;
1980         for(y=0;y<newheight;y++) {
1981             double ey = py + fy;
1982             int fromy = (int)py;
1983             int toy = (int)ey;
1984             int yweight1 = (int)(((fromy+1)-py)*256);
1985             int yweight2 = (int)((ey-toy)*256);
1986             int a = 0;
1987             int xx,yy;
1988             for(xx=fromx;xx<tox;xx++)
1989             for(yy=fromy;yy<toy;yy++) {
1990                 int b = 1-data[width*yy+xx];
1991                 int weight=256;
1992                 if(xx==fromx) weight = (weight*xweight1)/256;
1993                 if(xx==tox) weight = (weight*xweight2)/256;
1994                 if(yy==fromy) weight = (weight*yweight1)/256;
1995                 if(yy==toy) weight = (weight*yweight2)/256;
1996                 a+=b*weight;
1997             }
1998             //if(a) a=(palettesize-1)*r/blocksize;
1999             newdata[y*newwidth+x] = (a*blocksize)/r;
2000             py = ey;
2001         }
2002         px = ex;
2003     }
2004     return newdata;
2005 }
2006
2007 #define IMAGE_TYPE_JPEG 0
2008 #define IMAGE_TYPE_LOSSLESS 1
2009
2010 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2011         double x1,double y1,
2012         double x2,double y2,
2013         double x3,double y3,
2014         double x4,double y4, int type)
2015 {
2016     gfxcolor_t*newpic=0;
2017     
2018     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2019     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2020    
2021     gfxline_t p1,p2,p3,p4,p5;
2022     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2023     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2024     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2025     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2026     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2027
2028     {p1.x = (int)(p1.x*20)/20.0;
2029      p1.y = (int)(p1.y*20)/20.0;
2030      p2.x = (int)(p2.x*20)/20.0;
2031      p2.y = (int)(p2.y*20)/20.0;
2032      p3.x = (int)(p3.x*20)/20.0;
2033      p3.y = (int)(p3.y*20)/20.0;
2034      p4.x = (int)(p4.x*20)/20.0;
2035      p4.y = (int)(p4.y*20)/20.0;
2036      p5.x = (int)(p5.x*20)/20.0;
2037      p5.y = (int)(p5.y*20)/20.0;
2038     }
2039     
2040     gfxmatrix_t m;
2041     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2042     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2043     m.tx = p1.x - 0.5;
2044     m.ty = p1.y - 0.5;
2045
2046     gfximage_t img;
2047     img.data = (gfxcolor_t*)data;
2048     img.width = sizex;
2049     img.height = sizey;
2050   
2051     if(type == IMAGE_TYPE_JPEG)
2052         /* TODO: pass image_dpi to device instead */
2053         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2054
2055     dev->fillbitmap(dev, &p1, &img, &m, 0);
2056 }
2057
2058 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2059         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2060 {
2061     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2062 }
2063
2064 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2065         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2066 {
2067     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2068 }
2069
2070
2071 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2072                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2073                                    GBool inlineImg, int mask, int*maskColors,
2074                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2075 {
2076   double x1,y1,x2,y2,x3,y3,x4,y4;
2077   ImageStream *imgStr;
2078   Guchar pixBuf[4];
2079   GfxRGB rgb;
2080   int ncomps = 1;
2081   int bits = 1;
2082   unsigned char* maskbitmap = 0;
2083                                  
2084   if(colorMap) {
2085     ncomps = colorMap->getNumPixelComps();
2086     bits = colorMap->getBits();
2087   }
2088       
2089   if(maskStr) {
2090       int x,y;
2091       unsigned char buf[8];
2092       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2093       if(maskColorMap) {
2094           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2095           imgMaskStr->reset();
2096           unsigned char pal[256];
2097           int n = 1 << colorMap->getBits();
2098           int t;
2099           for(t=0;t<n;t++) {
2100               GfxGray gray;
2101               pixBuf[0] = t;
2102               maskColorMap->getGray(pixBuf, &gray);
2103               pal[t] = colToByte(gray);
2104           }
2105           for (y = 0; y < maskHeight; y++) {
2106               for (x = 0; x < maskWidth; x++) {
2107                   imgMaskStr->getPixel(buf);
2108                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2109               }
2110           }
2111           delete imgMaskStr;
2112       } else {
2113           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2114           imgMaskStr->reset();
2115           for (y = 0; y < maskHeight; y++) {
2116               for (x = 0; x < maskWidth; x++) {
2117                   imgMaskStr->getPixel(buf);
2118                   buf[0]^=maskInvert;
2119                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2120               }
2121           }
2122           delete imgMaskStr;
2123       }
2124       maskStr->close();
2125   }
2126   
2127   imgStr = new ImageStream(str, width, ncomps,bits);
2128   imgStr->reset();
2129
2130   if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2131   {
2132       msg("<verbose> Ignoring %d by %d image", width, height);
2133       unsigned char buf[8];
2134       int x,y;
2135       for (y = 0; y < height; ++y)
2136       for (x = 0; x < width; ++x) {
2137           imgStr->getPixel(buf);
2138       }
2139       delete imgStr;
2140       if(maskbitmap)
2141           free(maskbitmap);
2142       return;
2143   }
2144
2145   this->transformXY(state, 0, 1, &x1, &y1); x1 = (int)(x1);y1 = (int)(y1);
2146   this->transformXY(state, 0, 0, &x2, &y2); x2 = (int)(x2);y2 = (int)(y2);
2147   this->transformXY(state, 1, 0, &x3, &y3); x3 = (int)(x3);y3 = (int)(y3);
2148   this->transformXY(state, 1, 1, &x4, &y4); x4 = (int)(x4);y4 = (int)(y4);
2149
2150   if(!pbminfo && !(str->getKind()==strDCT)) {
2151       if(!type3active) {
2152           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2153           pbminfo = 1;
2154       }
2155       if(mask)
2156       msg("<verbose> drawing %d by %d masked picture", width, height);
2157   }
2158   if(!jpeginfo && (str->getKind()==strDCT)) {
2159       msg("<notice> File contains jpeg pictures");
2160       jpeginfo = 1;
2161   }
2162
2163   if(mask) {
2164       unsigned char buf[8];
2165       int x,y;
2166       unsigned char*pic = new unsigned char[width*height];
2167       gfxcolor_t pal[256];
2168       GfxRGB rgb;
2169       state->getFillRGB(&rgb);
2170
2171       memset(pal,255,sizeof(pal));
2172       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2173       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2174       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2175       pal[0].a = 255;              pal[1].a = 0;
2176     
2177       int numpalette = 2;
2178       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2179       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2180       for (y = 0; y < height; ++y)
2181       for (x = 0; x < width; ++x)
2182       {
2183             imgStr->getPixel(buf);
2184             if(invert) 
2185                 buf[0]=1-buf[0];
2186             pic[width*y+x] = buf[0];
2187       }
2188       
2189       /* the size of the drawn image is added to the identifier
2190          as the same image may require different bitmaps if displayed
2191          at different sizes (due to antialiasing): */
2192       int found = -1;
2193       if(type3active) {
2194           unsigned char*pic2 = 0;
2195           numpalette = 16;
2196           
2197           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2198
2199           if(!pic2) {
2200             delete[] pic;
2201             delete imgStr;
2202             return;
2203           }
2204           
2205           width = realwidth;
2206           height = realheight;
2207           delete[] pic;
2208           pic = pic2;
2209           
2210           /* make a black/white palette */
2211
2212           float r = 255./(float)(numpalette-1);
2213           int t;
2214           for(t=0;t<numpalette;t++) {
2215               pal[t].r = colToByte(rgb.r);
2216               pal[t].g = colToByte(rgb.g);
2217               pal[t].b = colToByte(rgb.b);
2218               pal[t].a = (unsigned char)(t*r);
2219           }
2220           
2221       }
2222
2223       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2224       for (y = 0; y < height; ++y) {
2225         for (x = 0; x < width; ++x) {
2226           pic2[width*y+x] = pal[pic[y*width+x]];
2227         }
2228       }
2229       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2230       delete[] pic2;
2231       delete[] pic;
2232       delete imgStr;
2233       if(maskbitmap) free(maskbitmap);
2234       return;
2235   }
2236
2237   int x,y;
2238
2239   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2240       gfxcolor_t*pic=new gfxcolor_t[width*height];
2241       for (y = 0; y < height; ++y) {
2242         for (x = 0; x < width; ++x) {
2243           imgStr->getPixel(pixBuf);
2244           colorMap->getRGB(pixBuf, &rgb);
2245           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2246           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2247           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2248           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2249           if(maskbitmap) {
2250               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2251           }
2252         }
2253       }
2254       if(str->getKind()==strDCT)
2255           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2256       else
2257           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2258       delete[] pic;
2259       delete imgStr;
2260       if(maskbitmap) free(maskbitmap);
2261       return;
2262   } else {
2263       gfxcolor_t*pic=new gfxcolor_t[width*height];
2264       gfxcolor_t pal[256];
2265       int n = 1 << colorMap->getBits();
2266       int t;
2267       for(t=0;t<256;t++) {
2268           pixBuf[0] = t;
2269           colorMap->getRGB(pixBuf, &rgb);
2270
2271           {/*if(maskColors && *maskColors==t) {
2272               msg("<notice> Color %d is transparent", t);
2273               if (imgData->maskColors) {
2274                 *alpha = 0;
2275                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2276                   if (pix[i] < imgData->maskColors[2*i] ||
2277                       pix[i] > imgData->maskColors[2*i+1]) {
2278                     *alpha = 1;
2279                     break;
2280                   }
2281                 }
2282               } else {
2283                 *alpha = 1;
2284               }
2285               if(!*alpha) {
2286                     pal[t].r = 0;
2287                     pal[t].g = 0;
2288                     pal[t].b = 0;
2289                     pal[t].a = 0;
2290               }
2291           } else {*/
2292               pal[t].r = (unsigned char)(colToByte(rgb.r));
2293               pal[t].g = (unsigned char)(colToByte(rgb.g));
2294               pal[t].b = (unsigned char)(colToByte(rgb.b));
2295               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2296           }
2297       }
2298       for (y = 0; y < height; ++y) {
2299         for (x = 0; x < width; ++x) {
2300           imgStr->getPixel(pixBuf);
2301           pic[width*y+x] = pal[pixBuf[0]];
2302         }
2303       }
2304       if(maskbitmap) {
2305           if(maskWidth < width && maskHeight < height) {
2306               for(y = 0; y < height; y++) {
2307                   for (x = 0; x < width; x++) {
2308                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2309                   }
2310               }
2311           } else {
2312               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2313               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2314               double dx = width / maskWidth;
2315               double dy = height / maskHeight;
2316               double yy = 0;
2317               for(y = 0; y < maskHeight; y++) {
2318                   double xx = 0;
2319                   for (x = 0; x < maskWidth; x++) {
2320                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2321                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2322                       xx += dx;
2323                   }
2324                   yy += dy;
2325               }
2326               delete[] pic;
2327               pic = newpic;
2328               width = maskWidth;
2329               height = maskHeight;
2330           }
2331       }
2332       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2333
2334       delete[] pic;
2335       delete imgStr;
2336       if(maskbitmap) free(maskbitmap);
2337       return;
2338   }
2339 }
2340
2341 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2342                                    int width, int height, GBool invert,
2343                                    GBool inlineImg) 
2344 {
2345     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2346     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2347     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2348 }
2349
2350 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2351                          int width, int height, GfxImageColorMap *colorMap,
2352                          int *maskColors, GBool inlineImg)
2353 {
2354     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2355             colorMap?"colorMap":"no colorMap", 
2356             maskColors?"maskColors":"no maskColors",
2357             inlineImg);
2358     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2359             colorMap?"colorMap":"no colorMap", 
2360             maskColors?"maskColors":"no maskColors",
2361             inlineImg);
2362     if(colorMap)
2363         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2364                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2365     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2366 }
2367   
2368 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2369                                int width, int height,
2370                                GfxImageColorMap *colorMap,
2371                                Stream *maskStr, int maskWidth, int maskHeight,
2372                                GBool maskInvert)
2373 {
2374     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2375             colorMap?"colorMap":"no colorMap", 
2376             maskWidth, maskHeight);
2377     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2378             colorMap?"colorMap":"no colorMap", 
2379             maskWidth, maskHeight);
2380     if(colorMap)
2381         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2382                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2383     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2384 }
2385
2386 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2387                                    int width, int height,
2388                                    GfxImageColorMap *colorMap,
2389                                    Stream *maskStr,
2390                                    int maskWidth, int maskHeight,
2391                                    GfxImageColorMap *maskColorMap)
2392 {
2393     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2394             colorMap?"colorMap":"no colorMap", 
2395             maskWidth, maskHeight);
2396     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2397             colorMap?"colorMap":"no colorMap", 
2398             maskWidth, maskHeight);
2399     if(colorMap)
2400         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2401                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2402     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2403 }
2404
2405 void GFXOutputDev::stroke(GfxState *state) 
2406 {
2407     dbg("stroke");
2408
2409     GfxPath * path = state->getPath();
2410     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2411     strokeGfxline(state, line, 0);
2412     gfxline_free(line);
2413 }
2414
2415 void GFXOutputDev::fill(GfxState *state) 
2416 {
2417     gfxcolor_t col = getFillColor(state);
2418     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2419
2420     GfxPath * path = state->getPath();
2421     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2422     if(config_optimize_polygons) {
2423         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2424         gfxline_free(line);
2425         line = line2;
2426     }
2427     fillGfxLine(state, line);
2428     gfxline_free(line);
2429 }
2430
2431 void GFXOutputDev::eoFill(GfxState *state) 
2432 {
2433     gfxcolor_t col = getFillColor(state);
2434     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2435
2436     GfxPath * path = state->getPath();
2437     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2438     fillGfxLine(state, line);
2439     gfxline_free(line);
2440 }
2441
2442
2443 static const char* dirseparator()
2444 {
2445 #ifdef WIN32
2446     return "\\";
2447 #else
2448     return "/";
2449 #endif
2450 }
2451
2452 void addGlobalFont(const char*filename)
2453 {
2454     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2455     memset(f, 0, sizeof(fontfile_t));
2456     f->filename = filename;
2457     int len = strlen(filename);
2458     char*r1 = strrchr(filename, '/');
2459     char*r2 = strrchr(filename, '\\');
2460     if(r2>r1)
2461         r1 = r2;
2462     if(r1) {
2463         len = strlen(r1+1);
2464     }
2465     f->len = len;
2466
2467     msg("<notice> Adding font \"%s\".", filename);
2468     if(global_fonts_next) {
2469         global_fonts_next->next = f;
2470         global_fonts_next = global_fonts_next->next;
2471     } else {
2472         global_fonts_next = global_fonts = f;
2473     }
2474 }
2475
2476 void addGlobalLanguageDir(const char*dir)
2477 {
2478     msg("<notice> Adding %s to language pack directories", dir);
2479
2480     FILE*fi = 0;
2481     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2482     strcpy(config_file, dir);
2483     strcat(config_file, dirseparator());
2484     strcat(config_file, "add-to-xpdfrc");
2485
2486     fi = fopen(config_file, "rb");
2487     if(!fi) {
2488         msg("<error> Could not open %s", config_file);
2489         return;
2490     }
2491     globalParams->parseFile(new GString(config_file), fi);
2492     fclose(fi);
2493 }
2494
2495 void addGlobalFontDir(const char*dirname)
2496 {
2497 #ifdef HAVE_DIRENT_H
2498     msg("<notice> Adding %s to font directories", dirname);
2499     lastfontdir = strdup(dirname);
2500     DIR*dir = opendir(dirname);
2501     if(!dir) {
2502         msg("<warning> Couldn't open directory %s", dirname);
2503         return;
2504     }
2505     struct dirent*ent;
2506     while(1) {
2507         ent = readdir (dir);
2508         if (!ent) 
2509             break;
2510         int l;
2511         char*name = ent->d_name;
2512         char type = 0;
2513         if(!name) continue;
2514         l=strlen(name);
2515         if(l<4)
2516             continue;
2517         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2518             type=1;
2519         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2520             type=3;
2521         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2522             type=2;
2523         if(type) {
2524             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2525             strcpy(fontname, dirname);
2526             strcat(fontname, dirseparator());
2527             strcat(fontname, name);
2528             addGlobalFont(fontname);
2529         }
2530     }
2531     closedir(dir);
2532 #else
2533     msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2534 #endif
2535 }
2536
2537 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2538 {
2539     if(pdfpage < 0)
2540         return;
2541
2542     if(!this->pages) {
2543         this->pagebuflen = 1024;
2544         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2545         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2546     } else {
2547         while(pdfpage >= this->pagebuflen)
2548         {
2549             int oldlen = this->pagebuflen;
2550             this->pagebuflen+=1024;
2551             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2552             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2553         }
2554     }
2555     this->pages[pdfpage] = outputpage;
2556     if(pdfpage>this->pagepos)
2557         this->pagepos = pdfpage;
2558 }
2559
2560 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2561                                       GfxColorSpace *blendingColorSpace,
2562                                       GBool isolated, GBool knockout,
2563                                       GBool forSoftMask)
2564 {
2565     const char*colormodename = "";
2566   
2567     if(blendingColorSpace) {
2568         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2569     }
2570     dbg("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2571     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);
2572     
2573     //states[statepos].createsoftmask |= forSoftMask;
2574     states[statepos].createsoftmask = forSoftMask;
2575     states[statepos].transparencygroup = !forSoftMask;
2576     states[statepos].isolated = isolated;
2577
2578     states[statepos].olddevice = this->device;
2579     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2580
2581     gfxdevice_record_init(this->device);
2582     
2583     /*if(!forSoftMask) { ////???
2584         state->setFillOpacity(0.0);
2585     }*/
2586     dbgindent+=2;
2587 }
2588
2589 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2590 {
2591     dbgindent-=2;
2592     gfxdevice_t*r = this->device;
2593
2594     this->device = states[statepos].olddevice;
2595
2596     gfxresult_t*recording = r->finish(r);
2597     
2598     dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2599     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2600
2601     if(states[statepos].createsoftmask) {
2602         states[statepos-1].softmaskrecording = recording;
2603     } else {
2604         states[statepos-1].grouprecording = recording;
2605     }
2606     
2607     states[statepos].createsoftmask = 0;
2608     states[statepos].transparencygroup = 0;
2609     free(r);
2610 }
2611
2612 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2613 {
2614     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2615                                "colordodge","colorburn","hardlight","softlight","difference",
2616                                "exclusion","hue","saturation","color","luminosity"};
2617
2618     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2619     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2620    
2621     if(state->getBlendMode() == gfxBlendNormal)
2622         infofeature("transparency groups");
2623     else {
2624         char buffer[80];
2625         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2626         warnfeature(buffer, 0);
2627     }
2628
2629     gfxresult_t*grouprecording = states[statepos].grouprecording;
2630   
2631     int blendmode = state->getBlendMode();
2632     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2633         int alpha = (int)(state->getFillOpacity()*255);
2634         if(blendmode == gfxBlendMultiply && alpha>200)
2635             alpha = 128;
2636         gfxdevice_t ops;
2637         gfxdevice_ops_init(&ops, this->device, alpha);
2638         gfxresult_record_replay(grouprecording, &ops);
2639         ops.finish(&ops);
2640     }
2641     grouprecording->destroy(grouprecording);
2642
2643     states[statepos].grouprecording = 0;
2644 }
2645
2646 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2647 {
2648     if(states[statepos].softmask) {
2649         /* shouldn't happen, but *does* happen */
2650         clearSoftMask(state);
2651     }
2652
2653     /* alpha = 1: retrieve mask values from alpha layer
2654        alpha = 0: retrieve mask values from luminance */
2655
2656     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2657             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2658     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2659             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2660     if(!alpha)
2661         infofeature("soft masks");
2662     else
2663         warnfeature("soft masks from alpha channel",0);
2664     
2665     states[statepos].olddevice = this->device;
2666     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2667     gfxdevice_record_init(this->device);
2668
2669     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2670     
2671     states[statepos].softmask = 1;
2672     states[statepos].softmask_alpha = alpha;
2673 }
2674
2675 static inline Guchar div255(int x) {
2676   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2677 }
2678
2679 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2680 {
2681     if(c < min) c = min;
2682     if(c > max) c = max;
2683     return c;
2684 }
2685
2686 void GFXOutputDev::clearSoftMask(GfxState *state)
2687 {
2688     if(!states[statepos].softmask)
2689         return;
2690     states[statepos].softmask = 0;
2691     dbg("clearSoftMask statepos=%d", statepos);
2692     msg("<verbose> clearSoftMask statepos=%d", statepos);
2693     
2694     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2695         msg("<error> Error in softmask/tgroup ordering");
2696         return;
2697     }
2698   
2699     gfxresult_t*mask = states[statepos].softmaskrecording;
2700     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2701     this->device = states[statepos].olddevice;
2702
2703     /* get outline of all objects below the soft mask */
2704     gfxdevice_t uniondev;
2705     gfxdevice_union_init(&uniondev, 0);
2706     gfxresult_record_replay(below, &uniondev);
2707     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2708     uniondev.finish(&uniondev);
2709     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2710     gfxline_free(belowoutline);belowoutline=0;
2711 #if 0 
2712     this->device->startclip(this->device, belowoutline);
2713     gfxresult_record_replay(below, this->device);
2714     gfxresult_record_replay(mask, this->device);
2715     this->device->endclip(this->device);
2716 #endif
2717     
2718     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2719     if(width<=0 || height<=0)
2720         return;
2721
2722     gfxdevice_t belowrender;
2723     gfxdevice_render_init(&belowrender);
2724     if(states[statepos+1].isolated) {
2725         belowrender.setparameter(&belowrender, "fillwhite", "1");
2726     }
2727     belowrender.setparameter(&belowrender, "antialize", "2");
2728     belowrender.startpage(&belowrender, width, height);
2729     gfxresult_record_replay(below, &belowrender);
2730     belowrender.endpage(&belowrender);
2731     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2732     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2733     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2734
2735     gfxdevice_t maskrender;
2736     gfxdevice_render_init(&maskrender);
2737     maskrender.startpage(&maskrender, width, height);
2738     gfxresult_record_replay(mask, &maskrender);
2739     maskrender.endpage(&maskrender);
2740     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2741     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2742
2743     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2744         msg("<fatal> Internal error in mask drawing");
2745         return;
2746     }
2747
2748     int y,x;
2749     for(y=0;y<height;y++) {
2750         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2751         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2752         for(x=0;x<width;x++) {
2753             int alpha;
2754             if(states[statepos].softmask_alpha) {
2755                 alpha = l1->a;
2756             } else {
2757                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2758             }
2759
2760             l2->a = div255(alpha*l2->a);
2761
2762             /* DON'T premultiply alpha- this is done by fillbitmap,
2763                depending on the output device */
2764             //l2->r = div255(alpha*l2->r);
2765             //l2->g = div255(alpha*l2->g);
2766             //l2->b = div255(alpha*l2->b);
2767
2768             l1++;
2769             l2++;
2770         }
2771     }
2772     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2773
2774     gfxmatrix_t matrix;
2775     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2776     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2777
2778     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2779
2780     mask->destroy(mask);
2781     below->destroy(below);
2782     maskresult->destroy(maskresult);
2783     belowresult->destroy(belowresult);
2784     states[statepos].softmaskrecording = 0;
2785 }
2786   
2787 //class MemCheck
2788 //{
2789 //    public: ~MemCheck()
2790 //    {
2791 //        delete globalParams;globalParams=0;
2792 //        Object::memCheck(stderr);
2793 //        gMemReport(stderr);
2794 //    }
2795 //} myMemCheck;
2796