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