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