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