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