add font dirs to fontconfig's config
[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 fontfile_t* global_fontdirs = 0;
97 static fontfile_t* global_fontdirs_next = 0;
98
99 static int fontnum = 0;
100
101 /* config */
102
103 struct fontentry {
104     const char*pdffont;
105     const char*filename;
106     char*afm;
107     int afmlen;
108     char*pfb;
109     int pfblen;
110     char*fullfilename;
111 } pdf2t1map[] ={
112 {"Times-Roman",           "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
113 {"Times-Italic",          "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
114 {"Times-Bold",            "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
115 {"Times-BoldItalic",      "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
116 {"Helvetica",             "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
117 {"Helvetica-Oblique",     "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
118 {"Helvetica-Bold",        "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
119 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
120 {"Courier",               "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
121 {"Courier-Oblique",       "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
122 {"Courier-Bold",          "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
123 {"Courier-BoldOblique",   "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
124 {"Symbol",                "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
125 {"ZapfDingbats",          "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
126
127
128 static int verbose = 0;
129 static int dbgindent = 1;
130 static void dbg(const char*format, ...)
131 {
132     char buf[1024];
133     int l;
134     va_list arglist;
135     if(!verbose)
136         return;
137     va_start(arglist, format);
138     vsprintf(buf, format, arglist);
139     va_end(arglist);
140     l = strlen(buf);
141     while(l && buf[l-1]=='\n') {
142         buf[l-1] = 0;
143         l--;
144     }
145     printf("(pdf) ");
146     int indent = dbgindent;
147     while(indent) {
148         printf(" ");
149         indent--;
150     }
151     printf("%s\n", buf);
152     fflush(stdout);
153 }
154
155 GFXOutputGlobals*gfxglobals=0;
156
157 GFXOutputGlobals::GFXOutputGlobals()
158 {
159     this->featurewarnings = 0;
160     this->jpeginfo = 0;
161     this->textmodeinfo = 0;
162     this->linkinfo = 0;
163     this->pbminfo = 0;
164 }
165 GFXOutputGlobals::~GFXOutputGlobals()
166 {
167     feature_t*f = this->featurewarnings;
168     while(f) {
169         feature_t*next = f->next;
170         if(f->string) {
171             free(f->string);f->string =0;
172         }
173         f->next = 0;
174         free(f);
175         f = next;
176     }
177     this->featurewarnings = 0;
178 }
179
180 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
181 {
182     feature_t*f = gfxglobals->featurewarnings;
183     while(f) {
184         if(!strcmp(feature, f->string))
185             return;
186         f = f->next;
187     }
188     f = (feature_t*)malloc(sizeof(feature_t));
189     f->string = strdup(feature);
190     f->next = gfxglobals->featurewarnings;
191     gfxglobals->featurewarnings = f;
192     if(warn) {
193         msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
194         if(this->config_break_on_warning) {
195             msg("<fatal> Aborting conversion due to unsupported feature");
196             exit(1);
197         }
198     } else {
199         msg("<notice> File contains %s",feature);
200     }
201 }
202 void GFXOutputDev::warnfeature(const char*feature,char fully)
203 {
204     showfeature(feature,fully,1);
205 }
206 void GFXOutputDev::infofeature(const char*feature)
207 {
208     showfeature(feature,0,0);
209 }
210
211 GFXOutputState::GFXOutputState() {
212     this->clipping = 0;
213     this->createsoftmask = 0;
214     this->transparencygroup = 0;
215     this->softmask = 0;
216     this->grouprecording = 0;
217     this->isolated = 0;
218 }
219
220 GBool GFXOutputDev::interpretType3Chars() 
221 {
222     return gTrue;
223 }
224
225 typedef struct _drawnchar
226 {
227     gfxcoord_t x,y;
228     int charid;
229     gfxcolor_t color;
230 } drawnchar_t;
231
232 class CharBuffer
233 {
234     drawnchar_t * chars;
235     int buf_size;
236     int num_chars;
237
238 public:
239
240     CharBuffer()
241     {
242         buf_size = 32;
243         chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
244         memset(chars, 0, sizeof(drawnchar_t)*buf_size);
245         num_chars = 0;
246     }
247     ~CharBuffer()
248     {
249         free(chars);chars = 0;
250     }
251
252     void grow(int size)
253     {
254         if(size>=buf_size) {
255             buf_size += 32;
256             chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
257         }
258     }
259
260     void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
261     {
262         grow(num_chars);
263         chars[num_chars].x = x;
264         chars[num_chars].y = y;
265         chars[num_chars].color = color;
266         chars[num_chars].charid = charid;
267     }
268 };
269     
270 char* writeOutStdFont(fontentry* f)
271 {
272     FILE*fi;
273     char namebuf1[512];
274     char namebuf2[512];
275     char* tmpFileName = mktmpname(namebuf1);
276
277     sprintf(namebuf2, "%s.afm", tmpFileName);
278     fi = fopen(namebuf2, "wb");
279     if(!fi)
280         return 0;
281     fwrite(f->afm, 1, f->afmlen, fi);
282     fclose(fi);
283
284     sprintf(namebuf2, "%s.pfb", tmpFileName);
285     fi = fopen(namebuf2, "wb");
286     if(!fi)
287         return 0;
288     fwrite(f->pfb, 1, f->pfblen, fi);
289     fclose(fi);
290     return strdup(namebuf2);
291 }
292 void unlinkfont(char* filename)
293 {
294     int l;
295     if(!filename)
296         return;
297     msg("<verbose> Removing temporary font file %s", filename);
298     l=strlen(filename);
299     unlink(filename);
300     if(!strncmp(&filename[l-4],".afm",4)) {
301         memcpy(&filename[l-4],".pfb",4); unlink(filename);
302         memcpy(&filename[l-4],".pfa",4); unlink(filename);
303         memcpy(&filename[l-4],".afm",4);
304         return;
305     } else 
306     if(!strncmp(&filename[l-4],".pfa",4)) {
307         memcpy(&filename[l-4],".afm",4); unlink(filename);
308         memcpy(&filename[l-4],".pfa",4);
309         return;
310     } else 
311     if(!strncmp(&filename[l-4],".pfb",4)) {
312         memcpy(&filename[l-4],".afm",4); unlink(filename);
313         memcpy(&filename[l-4],".pfb",4);
314         return;
315     }
316 }
317
318 static int config_use_fontconfig = 1;
319 static int fcinitcalled = 0; 
320
321 GFXGlobalParams::GFXGlobalParams()
322 : GlobalParams((char*)"")
323 {
324     //setupBaseFonts(char *dir); //not tested yet
325 }
326 GFXGlobalParams::~GFXGlobalParams()
327 {
328     msg("<verbose> Performing cleanups");
329     int t;
330     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
331         if(pdf2t1map[t].fullfilename) {
332             unlinkfont(pdf2t1map[t].fullfilename);
333         }
334     }
335 #ifdef HAVE_FONTCONFIG
336     if(config_use_fontconfig && fcinitcalled)
337         FcFini();
338 #endif
339 }
340 #ifdef HAVE_FONTCONFIG
341 static char fc_ismatch(FcPattern*match, char*family, char*style)
342 {
343     char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
344     FcBool scalable=FcFalse, outline=FcFalse;
345     FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
346     FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
347     FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
348     FcPatternGetBool(match, "outline", 0, &outline);
349     FcPatternGetBool(match, "scalable", 0, &scalable);
350
351     if(scalable!=FcTrue || outline!=FcTrue)
352         return 0;
353
354     if (!strcasecmp(fcfamily, family)) {
355         msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
356         return 1;
357     } else {
358         //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
359         return 0;
360     }
361 }
362 #endif
363
364 char* fontconfig_searchForFont(char*name)
365 {
366 #ifdef HAVE_FONTCONFIG
367     if(!config_use_fontconfig)
368         return 0;
369     
370     // call init ony once
371     if (!fcinitcalled) {
372         fcinitcalled = 1;
373
374         // check whether we have a config file
375         char* configfile = (char*)FcConfigFilename(0);
376         int configexists = 0;
377         FILE*fi = fopen(configfile, "rb");
378         if(fi) {
379             configexists = 1;fclose(fi);
380             msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
381         } else {
382             msg("<debug> Initializing FontConfig (no configfile)");
383         }
384
385         if(!configexists) {
386             /* A fontconfig instance which didn't find a configfile is unbelievably
387                cranky, so let's just write out a small xml file and make fontconfig
388                happy */
389             FcConfig*c = FcConfigCreate();
390             char namebuf[512];
391             char* tmpFileName = mktmpname(namebuf);
392             FILE*fi = fopen(tmpFileName, "wb");
393             fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
394 #ifdef WIN32
395             fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
396 #endif
397             fprintf(fi, "<dir>~/.fonts</dir>\n");
398
399             /* add external font dirs to fontconfig's config. Maybe fc will make more out
400                of them than we did 
401                 FIXME: we don't do that yet if there's a system config file
402              */
403             fontfile_t*fd = global_fontdirs;
404             while(fd) {
405                 fprintf(fi, "<dir>%s</dir>\n", fd->filename);
406                 fd = fd->next;
407             }
408
409 #ifdef WIN32
410             fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
411 #endif
412             fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
413             fprintf(fi, "</fontconfig>\n");
414             fclose(fi);
415             FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
416             FcConfigBuildFonts(c);
417             FcConfigSetCurrent(c);
418         }
419
420         if(!FcInit()) {
421             msg("<debug> FontConfig Initialization failed. Disabling.");
422             config_use_fontconfig = 0;
423             return 0;
424         }
425         FcConfig * config = FcConfigGetCurrent();
426         if(!config) {
427             msg("<debug> FontConfig Config Initialization failed. Disabling.");
428             config_use_fontconfig = 0;
429             return 0;
430         }
431         FcFontSet * set =  FcConfigGetFonts(config, FcSetSystem);
432         msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
433         if(!set || !set->nfont) {
434             msg("<debug> FontConfig has zero fonts. Disabling.");
435             config_use_fontconfig = 0;
436             return 0;
437         }
438
439         if(getLogLevel() >= LOGLEVEL_TRACE) {
440             int t;
441             for(t=0;t<set->nfont;t++) {
442                 char*fcfamily=0,*fcstyle=0,*filename=0;
443                 FcBool scalable=FcFalse, outline=FcFalse;
444                 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
445                 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
446                 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
447                 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
448                 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
449                 if(scalable && outline) {
450                     msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
451                 }
452             }
453         }
454     }
455
456     char*family = strdup(name);
457     char*style = 0;
458     char*dash = strchr(family, '-');
459     FcPattern*pattern = 0;
460     if(dash) {
461         *dash = 0;
462         style = dash+1;
463         msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
464         pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
465     } else {
466         msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
467         pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
468     }
469
470     FcResult result;
471     FcConfigSubstitute(0, pattern, FcMatchPattern); 
472     FcDefaultSubstitute(pattern);
473
474     FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
475     if(set) {
476         int t;
477         for(t=0;t<set->nfont;t++) {
478             FcPattern*match = set->fonts[t];
479             //FcPattern*match = FcFontMatch(0, pattern, &result);
480             if(fc_ismatch(match, family, style)) {
481                 char*filename=0;
482                 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
483                     msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
484                     filename=0;
485                 }
486                 //FcPatternDestroy(match);
487                 msg("<debug> fontconfig: returning filename %s", filename);
488                 free(family);
489                 FcPatternDestroy(pattern);
490                 FcFontSetDestroy(set);
491                 return filename?strdup(filename):0;
492             }
493         }
494     }
495     free(family);
496     FcPatternDestroy(pattern);
497     FcFontSetDestroy(set);
498     return 0;
499 #else
500     return 0;
501 #endif
502 }
503
504 static DisplayFontParamKind detectFontType(const char*filename)
505 {
506     if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
507         return displayFontTT;
508     if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
509         return displayFontT1;
510     return displayFontTT;
511 }
512
513 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
514 {
515     msg("<verbose> looking for font %s in global params", fontName->getCString());
516
517     char*name = fontName->getCString();
518     
519     /* see if it is a pdf standard font */
520     int t;
521     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
522         if(!strcmp(name, pdf2t1map[t].pdffont)) {
523             if(!pdf2t1map[t].fullfilename) {
524                 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
525                 if(!pdf2t1map[t].fullfilename) {
526                     msg("<error> Couldn't save default font- is the Temp Directory writable?");
527                 } else {
528                     msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
529                 }
530             }
531             DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
532             dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
533             return dfp;
534         }
535     }
536     
537     int bestlen = 0x7fffffff;
538     const char*bestfilename = 0;
539    
540 #ifndef HAVE_FONTCONFIG
541     /* if we don't have fontconfig, try a simple approach */
542     fontfile_t*f = global_fonts;
543     while(f) {
544         if(strstr(f->filename, name)) {
545             if(f->len < bestlen) {
546                 bestlen = f->len;
547                 bestfilename = f->filename;
548             }
549         }
550         f = f->next;
551     }
552 #endif
553
554     /* if we didn't find anything up to now, try looking for the
555        font via fontconfig */
556     char*filename = 0;
557     if(!bestfilename) {
558         filename = fontconfig_searchForFont(name);
559     } else {
560         filename = strdup(bestfilename);
561     }
562
563     if(filename) {
564         msg("<verbose> Font %s maps to %s\n", name, filename);
565         DisplayFontParamKind kind = detectFontType(filename);
566         DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
567         if(kind == displayFontTT) {
568             dfp->tt.fileName = new GString(filename);
569         } else {
570             dfp->t1.fileName = new GString(filename);
571         }
572         free(filename);
573         return dfp;
574     }
575     return GlobalParams::getDisplayFont(fontName);
576 }
577
578 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
579 {
580     if(!gfxglobals)
581         gfxglobals = new GFXOutputGlobals();
582
583     this->info = info;
584     this->doc = doc;
585     this->xref = doc->getXRef();
586     
587     this->type3active = 0;
588     this->statepos = 0;
589     this->xref = 0;
590     this->user_movex = 0;
591     this->user_movey = 0;
592     this->clipmovex = 0;
593     this->clipmovey = 0;
594     this->user_clipx1 = 0;
595     this->user_clipy1 = 0;
596     this->user_clipx2 = 0;
597     this->user_clipy2 = 0;
598     this->current_fontinfo = 0;
599     this->current_text_stroke = 0;
600     this->current_text_clip = 0;
601     this->outer_clip_box = 0;
602     this->config_bigchar=0;
603     this->config_convertgradients=1;
604     this->config_break_on_warning=0;
605     this->config_remapunicode=0;
606     this->config_transparent=0;
607     this->config_extrafontdata = 0;
608     this->config_optimize_polygons = 0;
609     this->config_multiply = 1;
610     this->dashPattern = 0;
611     this->page2page = 0;
612     this->num_pages = 0;
613   
614     memset(states, 0, sizeof(states));
615 };
616
617 void GFXOutputDev::setParameter(const char*key, const char*value)
618 {
619     if(!strcmp(key,"breakonwarning")) {
620         this->config_break_on_warning = atoi(value);
621     } else if(!strcmp(key,"remapunicode")) {
622         this->config_remapunicode = atoi(value);
623     } else if(!strcmp(key,"transparent")) {
624         this->config_transparent = atoi(value);
625     } else if(!strcmp(key,"extrafontdata")) {
626         this->config_extrafontdata = atoi(value);
627     } else if(!strcmp(key,"convertgradients")) {
628         this->config_convertgradients = atoi(value);
629     } else if(!strcmp(key,"multiply")) {
630         this->config_multiply = atoi(value);
631         if(this->config_multiply<1) 
632             this->config_multiply=1;
633     } else if(!strcmp(key,"optimize_polygons")) {
634         this->config_optimize_polygons = atoi(value);
635     }
636 }
637   
638 void GFXOutputDev::setDevice(gfxdevice_t*dev)
639 {
640     this->device = dev;
641 }
642   
643 void GFXOutputDev::setMove(int x,int y)
644 {
645     this->user_movex = x;
646     this->user_movey = y;
647 }
648
649 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
650 {
651     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
652     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
653
654     this->user_clipx1 = x1;
655     this->user_clipy1 = y1;
656     this->user_clipx2 = x2;
657     this->user_clipy2 = y2;
658 }
659
660 static char*getFontName(GfxFont*font)
661 {
662     char*fontid;
663     GString*gstr = font->getName();
664     char* fname = gstr==0?0:gstr->getCString();
665     if(fname==0) {
666         char buf[32];
667         Ref*r=font->getID();
668         sprintf(buf, "UFONT%d", r->num);
669         fontid = strdup(buf);
670     } else {
671         fontid = strdup(fname);
672     }
673
674     char*fontname= 0;
675     char* plus = strchr(fontid, '+');
676     if(plus && plus < &fontid[strlen(fontid)-1]) {
677         fontname = strdup(plus+1);
678     } else {
679         fontname = strdup(fontid);
680     }
681     free(fontid);
682     return fontname;
683 }
684
685 static void dumpFontInfo(const char*loglevel, GfxFont*font);
686 static int lastdumps[1024];
687 static int lastdumppos = 0;
688 /* nr = 0  unknown
689    nr = 1  substituting
690    nr = 2  type 3
691  */
692 static void showFontError(GfxFont*font, int nr) 
693 {  
694     Ref*r=font->getID();
695     int t;
696     for(t=0;t<lastdumppos;t++)
697         if(lastdumps[t] == r->num)
698             break;
699     if(t < lastdumppos)
700       return;
701     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
702     lastdumps[lastdumppos++] = r->num;
703     if(nr == 0)
704       msg("<warning> The following font caused problems:");
705     else if(nr == 1)
706       msg("<warning> The following font caused problems (substituting):");
707     else if(nr == 2)
708       msg("<warning> The following Type 3 Font will be rendered as graphics:");
709     dumpFontInfo("<warning>", font);
710 }
711
712 static void dumpFontInfo(const char*loglevel, GfxFont*font)
713 {
714   char* id = getFontID(font);
715   char* name = getFontName(font);
716   Ref* r=font->getID();
717   msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
718
719   GString*gstr  = font->getTag();
720    
721   msg("%s| Tag: %s", loglevel, id);
722   
723   if(font->isCIDFont()) msg("%s| is CID font", loglevel);
724
725   GfxFontType type=font->getType();
726   switch(type) {
727     case fontUnknownType:
728      msg("%s| Type: unknown",loglevel);
729     break;
730     case fontType1:
731      msg("%s| Type: 1",loglevel);
732     break;
733     case fontType1C:
734      msg("%s| Type: 1C",loglevel);
735     break;
736     case fontType3:
737      msg("%s| Type: 3",loglevel);
738     break;
739     case fontTrueType:
740      msg("%s| Type: TrueType",loglevel);
741     break;
742     case fontCIDType0:
743      msg("%s| Type: CIDType0",loglevel);
744     break;
745     case fontCIDType0C:
746      msg("%s| Type: CIDType0C",loglevel);
747     break;
748     case fontCIDType2:
749      msg("%s| Type: CIDType2",loglevel);
750     break;
751   }
752   
753   Ref embRef;
754   GBool embedded = font->getEmbeddedFontID(&embRef);
755   char*embeddedName=0;
756   if(font->getEmbeddedFontName()) {
757     embeddedName = font->getEmbeddedFontName()->getCString();
758   }
759   if(embedded)
760    msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
761
762   gstr = font->getExtFontFile();
763   if(gstr)
764    msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
765
766   // Get font descriptor flags.
767   if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
768   if(font->isSerif()) msg("%s| is serif", loglevel);
769   if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
770   if(font->isItalic()) msg("%s| is italic", loglevel);
771   if(font->isBold()) msg("%s| is bold", loglevel);
772
773   free(id);
774   free(name);
775 }
776
777 //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");}
778 //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");}
779
780 void dump_outline(gfxline_t*line)
781 {
782     /*gfxbbox_t*r = gfxline_isrectangle(line);
783     if(!r)
784         printf("is not a rectangle\n");
785     else
786         printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
787     */
788
789     while(line) {
790         if(line->type == gfx_moveTo) {
791             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
792         } else if(line->type == gfx_lineTo) {
793             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
794         } else if(line->type == gfx_splineTo) {
795             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
796         }
797         line = line->next;
798     }
799 }
800
801 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
802 {
803     int num = path->getNumSubpaths();
804     int s,t;
805     int cpos = 0;
806     double lastx=0,lasty=0,posx=0,posy=0;
807     int needsfix=0;
808     if(!num) {
809         msg("<warning> empty path");
810         return 0;
811     }
812     gfxdrawer_t draw;
813     gfxdrawer_target_gfxline(&draw);
814
815     for(t = 0; t < num; t++) {
816         GfxSubpath *subpath = path->getSubpath(t);
817         int subnum = subpath->getNumPoints();
818         double bx=0,by=0,cx=0,cy=0;
819
820         for(s=0;s<subnum;s++) {
821            double x,y;
822            
823            this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
824
825            if(s==0) {
826                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
827                     draw.lineTo(&draw, lastx, lasty);
828                 }
829                 draw.moveTo(&draw, x,y);
830                 posx = lastx = x; 
831                 posy = lasty = y;
832                 cpos = 0;
833                 needsfix = 0;
834            } else if(subpath->getCurve(s) && cpos==0) {
835                 bx = x;
836                 by = y;
837                 cpos = 1;
838            } else if(subpath->getCurve(s) && cpos==1) {
839                 cx = x;
840                 cy = y;
841                 cpos = 2;
842            } else {
843                 posx = x;
844                 posy = y;
845                 if(cpos==0) {
846                     draw.lineTo(&draw, x,y);
847                 } else {
848                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
849                 }
850                 needsfix = 1;
851                 cpos = 0;
852            }
853         }
854     }
855     /* fix non-closed lines */
856     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
857         draw.lineTo(&draw, lastx, lasty);
858     }
859     gfxline_t*result = (gfxline_t*)draw.result(&draw);
860
861     gfxline_optimize(result);
862
863     return result;
864 }
865
866 GBool GFXOutputDev::useTilingPatternFill()
867 {
868     infofeature("tiled patterns");
869 //    if(config_convertgradients)
870 //      return gTrue;
871     return gFalse;
872 }
873 GBool GFXOutputDev::useShadedFills()
874 {
875     infofeature("shaded fills");
876     if(config_convertgradients)
877         return gTrue;
878     return gFalse;
879 }
880
881 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
882 {
883     state->transform(x,y,nx,ny);
884     *nx += user_movex + clipmovex;
885     *ny += user_movey + clipmovey;
886 }
887
888
889 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
890 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
891                                int paintType, Dict *resDict,
892                                double *mat, double *bbox,
893                                int x0, int y0, int x1, int y1,
894                                double xStep, double yStep)
895 #else
896 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
897                                int paintType, Dict *resDict,
898                                double *mat, double *bbox,
899                                int x0, int y0, int x1, int y1,
900                                double xStep, double yStep) 
901 #endif
902 {
903     msg("<debug> tilingPatternFill");
904     infofeature("tiling pattern fills");
905 }
906
907 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
908 {
909     msg("<error> functionShadedFill not supported yet");
910     infofeature("function shaded fills");
911     return gFalse;
912 }
913 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
914 {
915     gfxcolor_t c;
916     GfxRGB rgb;
917     colspace->getRGB(col, &rgb);
918     c.r = colToByte(rgb.r);
919     c.g = colToByte(rgb.g);
920     c.b = colToByte(rgb.b);
921     c.a = 255;
922     return c;
923 }
924
925 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
926 {
927     double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
928     shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
929     x1=x0+r1;y1=y0;
930     x2=x0;   y2=y0+r1;
931     this->transformXY(state, x0,y0, &x0,&y0);
932     this->transformXY(state, x1,y1, &x1,&y1);
933     this->transformXY(state, x2,y2, &x2,&y2);
934     
935     GfxColor color0;
936     GfxColor color1;
937     GfxColor color2;
938     shading->getColor(0.0, &color0);
939     shading->getColor(0.5, &color1);
940     shading->getColor(1.0, &color2);
941   
942     GfxColorSpace* colspace = shading->getColorSpace();
943     
944     msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
945             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
946             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
947             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
948     infofeature("radial shaded fills");
949
950     gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
951     g[0].next = &g[1];
952     g[1].next = &g[2];
953     g[2].next = 0;
954     g[0].color = col2col(colspace, &color0);
955     g[1].color = col2col(colspace, &color1);
956     g[2].color = col2col(colspace, &color2);
957     g[0].pos = 0.0;
958     g[1].pos = 0.5;
959     g[2].pos = 1.0;
960
961     gfxbbox_t b = states[statepos].clipbbox;
962     gfxline_t p1,p2,p3,p4,p5;
963     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
964     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
965     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
966     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
967     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
968     
969     gfxmatrix_t m;
970     //m.m00 = (x3-x0); m.m10 = (x1-x0);
971     //m.m01 = (y3-y0); m.m11 = (y1-y0);
972     //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
973     m.m00 = (x1-x0); m.m10 = (x2-x0);
974     m.m01 = (y1-y0); m.m11 = (y2-y0);
975     m.tx = x0 - 0.5;
976     m.ty = y0 - 0.5;
977
978     device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
979     return gTrue;
980 }
981
982 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
983 {
984     double x0,y0,x1,y1;
985     shading->getCoords(&x0,&y0,&x1,&y1);
986     this->transformXY(state, x0,y0,&x0,&y0);
987     this->transformXY(state, x1,y1,&x1,&y1);
988
989     GfxColor color0;
990     GfxColor color1;
991     GfxColor color2;
992     shading->getColor(0.0, &color0);
993     shading->getColor(0.5, &color1);
994     shading->getColor(1.0, &color2);
995     
996     GfxColorSpace* colspace = shading->getColorSpace();
997     
998     msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
999             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
1000             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1001             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1002             );
1003     infofeature("axial shaded fills");
1004
1005     gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1006     g[0].next = &g[1];
1007     g[1].next = &g[2];
1008     g[2].next = 0;
1009     g[0].color = col2col(colspace, &color0);
1010     g[1].color = col2col(colspace, &color1);
1011     g[2].color = col2col(colspace, &color2);
1012     g[0].pos = 0.0;
1013     g[1].pos = 0.5;
1014     g[2].pos = 1.0;
1015  
1016     double xMin,yMin,xMax,yMax;
1017     state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1018     this->transformXY(state, xMin, yMin, &xMin, &yMin);
1019     msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1020
1021     xMin = 0; yMin = 0;
1022     xMin = 1024; yMin = 1024;
1023
1024     gfxbbox_t b = states[statepos].clipbbox;
1025     gfxline_t p1,p2,p3,p4,p5;
1026     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1027     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1028     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1029     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1030     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1031    
1032     /* the gradient starts at (-1.0,0.0), so move (0,0) to
1033        the middle of the two control points */
1034     gfxmatrix_t m;
1035     m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1036     m.m01 = (y1-y0)/2; m.m11 =  (x1-x0)/2;
1037     m.tx = (x0 + x1)/2 - 0.5;
1038     m.ty = (y0 + y1)/2 - 0.5;
1039
1040     device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1041
1042     free(g);
1043     return gTrue;
1044 }
1045   
1046 GBool GFXOutputDev::useDrawForm() 
1047
1048     infofeature("forms");
1049     return gFalse; 
1050 }
1051 void GFXOutputDev::drawForm(Ref id) 
1052 {
1053     msg("<error> drawForm not implemented");
1054 }
1055 GBool GFXOutputDev::needNonText() 
1056
1057     return gTrue; 
1058 }
1059 void GFXOutputDev::endPage() 
1060 {
1061     msg("<verbose> endPage (GfxOutputDev)");
1062     if(outer_clip_box) {
1063         device->endclip(device);
1064         outer_clip_box = 0;
1065     }
1066     this->dashPattern = 0;
1067     /* notice: we're not fully done yet with this page- there might still be 
1068        a few calls to drawLink() yet to come */
1069 }
1070
1071 static inline double sqr(double x) {return x*x;}
1072
1073 #define STROKE_FILL 1
1074 #define STROKE_CLIP 2
1075 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1076 {
1077     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1078     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1079     double miterLimit = state->getMiterLimit();
1080     double width = state->getTransformedLineWidth();
1081
1082     GfxRGB rgb;
1083     double opaq = state->getStrokeOpacity();
1084     if(type3active)
1085         state->getFillRGB(&rgb);
1086     else
1087         state->getStrokeRGB(&rgb);
1088     gfxcolor_t col;
1089     col.r = colToByte(rgb.r);
1090     col.g = colToByte(rgb.g);
1091     col.b = colToByte(rgb.b);
1092     col.a = (unsigned char)(opaq*255);
1093
1094     gfx_capType capType = gfx_capRound;
1095     if(lineCap == 0) capType = gfx_capButt;
1096     else if(lineCap == 1) capType = gfx_capRound;
1097     else if(lineCap == 2) capType = gfx_capSquare;
1098
1099     gfx_joinType joinType = gfx_joinRound;
1100     if(lineJoin == 0) joinType = gfx_joinMiter;
1101     else if(lineJoin == 1) joinType = gfx_joinRound;
1102     else if(lineJoin == 2) joinType = gfx_joinBevel;
1103
1104     gfxline_t*line2 = 0;
1105
1106     if(this->dashLength && this->dashPattern) {
1107         float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1108         int t;
1109
1110         /* try to find out how much the transformation matrix would
1111            stretch the dashes, and factor that into the dash lengths.
1112            This is not the entirely correct approach- it would be 
1113            better to first convert the path to an unscaled version,
1114            then apply dashing, and then transform the path using
1115            the current transformation matrix. However there are few
1116            PDFs which actually stretch a dashed path in a non-orthonormal
1117            way */
1118         double tx1, ty1, tx2, ty2;
1119         this->transformXY(state, 0, 0, &tx1, &ty1);
1120         this->transformXY(state, 1, 1, &tx2, &ty2);
1121         double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1122
1123         msg("<trace> %d dashes", this->dashLength);
1124         msg("<trace> |  phase: %f", this->dashStart);
1125         for(t=0;t<this->dashLength;t++) {
1126             dash[t] = (float)this->dashPattern[t] * f;
1127             if(!dash[t])
1128                 dash[t] = 1e-37;
1129             msg("<trace> |  d%-3d: %f", t, this->dashPattern[t]);
1130         }
1131         dash[this->dashLength] = -1;
1132         if(getLogLevel() >= LOGLEVEL_TRACE) {
1133             dump_outline(line);
1134         }
1135
1136         line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1137         line = line2;
1138         free(dash);
1139         msg("<trace> After dashing:");
1140     }
1141     
1142     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1143         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1144                 width,
1145                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1146                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1147                 this->dashLength,
1148                 col.r,col.g,col.b,col.a
1149                 );
1150         dump_outline(line);
1151     }
1152
1153     if(flags&STROKE_FILL) {
1154         gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1155         gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1156         if(getLogLevel() >= LOGLEVEL_TRACE)  {
1157             dump_outline(gfxline);
1158         }
1159         if(!gfxline) {
1160             msg("<warning> Empty polygon (resulting from stroked line)");
1161         }
1162         if(flags&STROKE_CLIP) {
1163             device->startclip(device, gfxline);
1164             states[statepos].clipping++;
1165         } else {
1166             device->fill(device, gfxline, &col);
1167         }
1168         gfxline_free(gfxline);
1169         gfxpoly_free(poly);
1170     } else {
1171         if(flags&STROKE_CLIP) 
1172             msg("<error> Stroke&clip not supported at the same time");
1173         device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1174     }
1175     
1176     if(line2)
1177         gfxline_free(line2);
1178 }
1179
1180 gfxcolor_t getFillColor(GfxState * state)
1181 {
1182     GfxRGB rgb;
1183     double opaq = state->getFillOpacity();
1184     state->getFillRGB(&rgb);
1185     gfxcolor_t col;
1186     col.r = colToByte(rgb.r);
1187     col.g = colToByte(rgb.g);
1188     col.b = colToByte(rgb.b);
1189     col.a = (unsigned char)(opaq*255);
1190     return col;
1191 }
1192
1193 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
1194 {
1195     gfxcolor_t col = getFillColor(state);
1196
1197     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1198         msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1199         dump_outline(line);
1200     }
1201     device->fill(device, line, &col);
1202 }
1203
1204 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
1205 {
1206     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1207         dump_outline(line);
1208     }
1209     gfxbbox_t bbox = gfxline_getbbox(line);
1210     gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1211
1212     device->startclip(device, line);
1213     states[statepos].clipping++;
1214 }
1215
1216 void GFXOutputDev::clip(GfxState *state) 
1217 {
1218     GfxPath * path = state->getPath();
1219     msg("<trace> clip");
1220     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1221     if(config_optimize_polygons) {
1222         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1223         gfxline_free(line);
1224         line = line2;
1225     }
1226     clipToGfxLine(state, line);
1227     gfxline_free(line);
1228 }
1229
1230 void GFXOutputDev::eoClip(GfxState *state) 
1231 {
1232     GfxPath * path = state->getPath();
1233     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1234     clipToGfxLine(state, line);
1235     gfxline_free(line);
1236 }
1237 void GFXOutputDev::clipToStrokePath(GfxState *state)
1238 {
1239     GfxPath * path = state->getPath();
1240     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1241
1242     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1243         double width = state->getTransformedLineWidth();
1244         msg("<trace> cliptostrokepath width=%f", width);
1245         dump_outline(line);
1246     }
1247
1248     strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1249     gfxline_free(line);
1250 }
1251
1252 void GFXOutputDev::finish()
1253 {
1254     if(outer_clip_box) {
1255         if(device) {
1256             device->endclip(device);
1257         }
1258         outer_clip_box = 0;
1259     }
1260 }
1261
1262 GFXOutputDev::~GFXOutputDev() 
1263 {
1264     finish();
1265     if(this->dashPattern) {
1266         free(this->dashPattern);this->dashPattern = 0;
1267     }
1268 };
1269 GBool GFXOutputDev::upsideDown() 
1270 {
1271     return gTrue;
1272 };
1273 GBool GFXOutputDev::useDrawChar() 
1274 {
1275     return gTrue;
1276 }
1277
1278 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1279                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1280
1281 static char tmp_printstr[4096];
1282 char* makeStringPrintable(char*str)
1283 {
1284     int len = strlen(str);
1285     int dots = 0;
1286     if(len>=80) {
1287         len = 80;
1288         dots = 1;
1289     }
1290     int t;
1291     for(t=0;t<len;t++) {
1292         char c = str[t];
1293         if(c<32 || c>124) {
1294             c = '.';
1295         }
1296         tmp_printstr[t] = c;
1297     }
1298     if(dots) {
1299         tmp_printstr[len++] = '.';
1300         tmp_printstr[len++] = '.';
1301         tmp_printstr[len++] = '.';
1302     }
1303     tmp_printstr[len] = 0;
1304     return tmp_printstr;
1305 }
1306 void GFXOutputDev::updateFontMatrix(GfxState*state)
1307 {
1308     double* ctm = state->getCTM();
1309     double fontSize = state->getFontSize();
1310     double*textMat = state->getTextMat();
1311
1312     /*  taking the absolute value of horizScaling seems to be required for
1313         some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1314     double hscale = fabs(state->getHorizScaling());
1315    
1316     // from xpdf-3.02/SplashOutputDev:updateFont
1317     double mm11 = textMat[0] * fontSize * hscale;
1318     double mm12 = textMat[1] * fontSize * hscale;
1319     double mm21 = textMat[2] * fontSize;
1320     double mm22 = textMat[3] * fontSize;
1321
1322     // multiply with ctm, like state->getFontTransMat() does
1323     this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1324     this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1325     this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1326     this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1327     this->current_font_matrix.tx = 0;
1328     this->current_font_matrix.ty = 0;
1329 }
1330
1331 void GFXOutputDev::beginString(GfxState *state, GString *s) 
1332
1333     int render = state->getRender();
1334     if(current_text_stroke) {
1335         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1336     }
1337
1338     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1339 }
1340
1341 static gfxline_t* mkEmptyGfxShape(double x, double y)
1342 {
1343     gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1344     line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1345     return line;
1346 }
1347
1348 static char isValidUnicode(int c)
1349 {
1350     if(c>=32 && c<0x2fffe)
1351         return 1;
1352     return 0;
1353 }
1354
1355 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1356                         double dx, double dy,
1357                         double originX, double originY,
1358                         CharCode charid, int nBytes, Unicode *_u, int uLen)
1359 {
1360     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1361         msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1362         return;
1363     }
1364   
1365     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1366
1367     int render = state->getRender();
1368     gfxcolor_t col = getFillColor(state);
1369
1370     // check for invisible text -- this is used by Acrobat Capture
1371     if (render == RENDER_INVISIBLE) {
1372         col.a = 255;
1373         if(!config_extrafontdata)
1374             return;
1375     }
1376
1377     GfxFont*font = state->getFont();
1378
1379     if(font->getType() == fontType3) {
1380         /* type 3 chars are passed as graphics */
1381         msg("<debug> type3 char at %f/%f", x, y);
1382         return;
1383     }
1384
1385     Unicode u = uLen?(_u[0]):0;
1386     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);
1387
1388     gfxmatrix_t m = this->current_font_matrix;
1389     this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1390     //m.tx += originX; m.ty += originY;
1391
1392     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1393         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1394     } else {
1395         msg("<debug> Drawing glyph %d as shape", charid);
1396         if(!gfxglobals->textmodeinfo) {
1397             msg("<notice> Some texts will be rendered as shape");
1398             gfxglobals->textmodeinfo = 1;
1399         }
1400         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1401         gfxline_t*tglyph = gfxline_clone(glyph);
1402         gfxline_transform(tglyph, &m);
1403         if((render&3) != RENDER_INVISIBLE) {
1404             gfxline_t*add = gfxline_clone(tglyph);
1405             current_text_stroke = gfxline_append(current_text_stroke, add);
1406         }
1407         if(render&RENDER_CLIP) {
1408             gfxline_t*add = gfxline_clone(tglyph);
1409             current_text_clip = gfxline_append(current_text_clip, add);
1410             if(!current_text_clip) {
1411                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1412             }
1413         }
1414         gfxline_free(tglyph);
1415     }
1416 }
1417
1418 void GFXOutputDev::endString(GfxState *state) 
1419
1420     int render = state->getRender();
1421     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1422     
1423     if(current_text_stroke) {
1424         /* fillstroke and stroke text rendering objects we can process right
1425            now (as there may be texts of other rendering modes in this
1426            text object)- clipping objects have to wait until endTextObject,
1427            however */
1428         device->setparameter(device, "mark","TXT");
1429         if((render&3) == RENDER_FILL) {
1430             fillGfxLine(state, current_text_stroke);
1431             gfxline_free(current_text_stroke);
1432             current_text_stroke = 0;
1433         } else if((render&3) == RENDER_FILLSTROKE) {
1434             fillGfxLine(state, current_text_stroke);
1435             strokeGfxline(state, current_text_stroke,0);
1436             gfxline_free(current_text_stroke);
1437             current_text_stroke = 0;
1438         } else if((render&3) == RENDER_STROKE) {
1439             strokeGfxline(state, current_text_stroke,0);
1440             gfxline_free(current_text_stroke);
1441             current_text_stroke = 0;
1442         }
1443         device->setparameter(device, "mark","");
1444     }
1445 }    
1446
1447 void GFXOutputDev::endTextObject(GfxState *state)
1448 {
1449     int render = state->getRender();
1450     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1451     
1452     if(current_text_clip) {
1453         device->setparameter(device, "mark","TXT");
1454         clipToGfxLine(state, current_text_clip);
1455         device->setparameter(device, "mark","");
1456         gfxline_free(current_text_clip);
1457         current_text_clip = 0;
1458     }
1459 }
1460
1461 /* the logic seems to be as following:
1462    first, beginType3Char is called, with the charcode and the coordinates.
1463    if this function returns true, it already knew about the char and has now drawn it.
1464    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1465    called with some parameters.
1466    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1467    at the position first passed to beginType3Char). the char ends with endType3Char.
1468
1469    The drawing operations between beginType3Char and endType3Char are somewhat different to
1470    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1471    color determines the color of a font)
1472 */
1473
1474 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1475 {
1476     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1477     type3active = 1;
1478     
1479     if(config_extrafontdata && current_fontinfo) {
1480
1481         gfxmatrix_t m = this->current_font_matrix;
1482         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1483         m.m00*=INTERNAL_FONT_SIZE;
1484         m.m01*=INTERNAL_FONT_SIZE;
1485         m.m10*=INTERNAL_FONT_SIZE;
1486         m.m11*=INTERNAL_FONT_SIZE;
1487
1488         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1489             msg("<error> Invalid charid %d for font", charid);
1490             return gFalse;
1491         }
1492         gfxcolor_t col={0,0,0,0};
1493         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1494         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1495     }
1496
1497
1498     /* the character itself is going to be passed using the draw functions */
1499     return gFalse; /* gTrue= is_in_cache? */
1500 }
1501
1502 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1503 }
1504 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1505 }
1506
1507 void GFXOutputDev::endType3Char(GfxState *state)
1508 {
1509     type3active = 0;
1510     msg("<debug> endType3Char");
1511 }
1512
1513 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1514 {
1515     this->currentpage = pageNum;
1516     double x1,y1,x2,y2;
1517     int rot = doc->getPageRotate(1);
1518     gfxcolor_t white = {255,255,255,255};
1519     gfxcolor_t black = {255,0,0,0};
1520     laststate = state;
1521     gfxline_t clippath[5];
1522
1523     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1524     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1525     Use CropBox, not MediaBox, as page size
1526     */
1527     
1528     /*x1 = crop_x1;
1529     y1 = crop_y1;
1530     x2 = crop_x2;
1531     y2 = crop_y2;*/
1532     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1533     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1534
1535     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1536     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1537
1538     this->clipmovex = -(int)x1;
1539     this->clipmovey = -(int)y1;
1540     
1541     /* apply user clip box */
1542     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1543         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1544         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1545         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1546         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1547         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1548     }
1549
1550     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1551     
1552     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);
1553     if(rot!=0)
1554         msg("<verbose> page is rotated %d degrees", rot);
1555
1556     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1557     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1558     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1559     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1560     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1561     device->startclip(device, clippath); outer_clip_box = 1;
1562     if(!config_transparent) {
1563         device->fill(device, clippath, &white);
1564     }
1565     states[statepos].clipbbox.xmin = x1;
1566     states[statepos].clipbbox.ymin = x1;
1567     states[statepos].clipbbox.xmax = x2;
1568     states[statepos].clipbbox.ymax = y2;
1569     
1570     this->dashPattern = 0;
1571     this->dashLength = 0;
1572     this->dashStart = 0;
1573 }
1574
1575
1576 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1577 {
1578     double x1, y1, x2, y2;
1579     gfxline_t points[5];
1580     int x, y;
1581     
1582     msg("<debug> drawlink");
1583
1584     link->getRect(&x1, &y1, &x2, &y2);
1585     cvtUserToDev(x1, y1, &x, &y);
1586     points[0].type = gfx_moveTo;
1587     points[0].x = points[4].x = x + user_movex + clipmovex;
1588     points[0].y = points[4].y = y + user_movey + clipmovey;
1589     points[0].next = &points[1];
1590     cvtUserToDev(x2, y1, &x, &y);
1591     points[1].type = gfx_lineTo;
1592     points[1].x = x + user_movex + clipmovex;
1593     points[1].y = y + user_movey + clipmovey;
1594     points[1].next = &points[2];
1595     cvtUserToDev(x2, y2, &x, &y);
1596     points[2].type = gfx_lineTo;
1597     points[2].x = x + user_movex + clipmovex;
1598     points[2].y = y + user_movey + clipmovey;
1599     points[2].next = &points[3];
1600     cvtUserToDev(x1, y2, &x, &y);
1601     points[3].type = gfx_lineTo;
1602     points[3].x = x + user_movex + clipmovex;
1603     points[3].y = y + user_movey + clipmovey;
1604     points[3].next = &points[4];
1605     cvtUserToDev(x1, y1, &x, &y);
1606     points[4].type = gfx_lineTo;
1607     points[4].x = x + user_movex + clipmovex;
1608     points[4].y = y + user_movey + clipmovey;
1609     points[4].next = 0;
1610     
1611     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1612             points[0].x, points[0].y,
1613             points[1].x, points[1].y,
1614             points[2].x, points[2].y,
1615             points[3].x, points[3].y); 
1616     
1617     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1618         dump_outline(points);
1619     }
1620
1621     LinkAction*action=link->getAction();
1622     char buf[128];
1623     char*s = 0;
1624     const char*type = "-?-";
1625     char*named = 0;
1626     int page = -1;
1627     msg("<trace> drawlink action=%d", action->getKind());
1628     switch(action->getKind())
1629     {
1630         case actionGoTo: {
1631             type = "GoTo";
1632             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1633             LinkDest *dest=NULL;
1634             if (ha->getDest()==NULL) 
1635                 dest=catalog->findDest(ha->getNamedDest());
1636             else dest=ha->getDest();
1637             if (dest){ 
1638               if (dest->isPageRef()){
1639                 Ref pageref=dest->getPageRef();
1640                 page=catalog->findPage(pageref.num,pageref.gen);
1641               }
1642               else  page=dest->getPageNum();
1643               sprintf(buf, "%d", page);
1644               s = strdup(buf);
1645             }
1646         }
1647         break;
1648         case actionGoToR: {
1649             type = "GoToR";
1650             LinkGoToR*l = (LinkGoToR*)action;
1651             GString*g = l->getFileName();
1652             if(g)
1653              s = strdup(g->getCString());
1654             if(!s) {
1655                 /* if the GoToR link has no filename, then
1656                    try to find a refernce in the *local*
1657                    file */
1658                 GString*g = l->getNamedDest();
1659                 if(g)
1660                  s = strdup(g->getCString());
1661             }
1662         }
1663         break;
1664         case actionNamed: {
1665             type = "Named";
1666             LinkNamed*l = (LinkNamed*)action;
1667             GString*name = l->getName();
1668             if(name) {
1669                 s = strdup(name->lowerCase()->getCString());
1670                 named = name->getCString();
1671                 if(!strchr(s,':')) 
1672                 {
1673                     if(strstr(s, "next") || strstr(s, "forward"))
1674                     {
1675                         page = currentpage + 1;
1676                     }
1677                     else if(strstr(s, "prev") || strstr(s, "back"))
1678                     {
1679                         page = currentpage - 1;
1680                     }
1681                     else if(strstr(s, "last") || strstr(s, "end"))
1682                     {
1683                         if(this->page2page && this->num_pages) {
1684                             page = this->page2page[this->num_pages-1];
1685                         }
1686                     }
1687                     else if(strstr(s, "first") || strstr(s, "top"))
1688                     {
1689                         page = 1;
1690                     }
1691                 }
1692             }
1693         }
1694         break;
1695         case actionLaunch: {
1696             type = "Launch";
1697             LinkLaunch*l = (LinkLaunch*)action;
1698             GString * str = new GString(l->getFileName());
1699             GString * params = l->getParams();
1700             if(params)
1701                 str->append(params);
1702             s = strdup(str->getCString());
1703             delete str;
1704         }
1705         break;
1706         case actionURI: {
1707             char*url = 0;
1708             type = "URI";
1709             LinkURI*l = (LinkURI*)action;
1710             GString*g = l->getURI();
1711             if(g) {
1712              url = g->getCString();
1713              s = strdup(url);
1714             }
1715         }
1716         break;
1717         case actionUnknown: {
1718             type = "Unknown";
1719             LinkUnknown*l = (LinkUnknown*)action;
1720             s = strdup("");
1721         }
1722         break;
1723         default: {
1724             msg("<error> Unknown link type!");
1725             break;
1726         }
1727     }
1728
1729     if(!s) s = strdup("-?-");
1730     
1731     msg("<trace> drawlink s=%s", s);
1732
1733     if(!gfxglobals->linkinfo && (page || s))
1734     {
1735         msg("<notice> File contains links");
1736         gfxglobals->linkinfo = 1;
1737     }
1738     
1739     if(page>0) {
1740         int t;
1741         int lpage = -1;
1742         for(t=1;t<=this->num_pages;t++) {
1743             if(this->page2page[t]==page) {
1744                 lpage = t;
1745                 break;
1746             }
1747         }
1748         if(lpage<0) {
1749             lpage = page;
1750         }
1751
1752         char buf[80];
1753         sprintf(buf, "page%d", lpage);
1754         device->drawlink(device, points, buf);
1755     }
1756     else if(s)
1757     {
1758         device->drawlink(device, points, s);
1759     }
1760
1761     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1762     free(s);s=0;
1763 }
1764
1765 void GFXOutputDev::saveState(GfxState *state) {
1766     dbg("saveState %08x", state); dbgindent+=2;
1767
1768     msg("<trace> saveState %08x", state);
1769     updateAll(state);
1770     if(statepos>=64) {
1771       msg("<fatal> Too many nested states in pdf.");
1772       exit(1);
1773     }
1774     statepos ++;
1775     states[statepos].state = state;
1776     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1777     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1778     states[statepos].clipping = 0;
1779     states[statepos].olddevice = 0;
1780     states[statepos].clipbbox = states[statepos-1].clipbbox;
1781 };
1782
1783 void GFXOutputDev::restoreState(GfxState *state) {
1784   dbgindent-=2; dbg("restoreState %08x", state);
1785
1786   if(statepos==0) {
1787       msg("<fatal> Invalid restoreState");
1788       exit(1);
1789   }
1790   msg("<trace> restoreState %08x%s%s", state,
1791                                   states[statepos].softmask?" (end softmask)":"",
1792                                   states[statepos].clipping?" (end clipping)":"");
1793   if(states[statepos].softmask) {
1794       clearSoftMask(state);
1795   }
1796   updateAll(state);
1797   
1798   while(states[statepos].clipping) {
1799       device->endclip(device);
1800       states[statepos].clipping--;
1801   }
1802   if(states[statepos].state!=state) {
1803       msg("<fatal> bad state nesting");
1804       exit(1);
1805   }
1806   states[statepos].state=0;
1807   statepos--;
1808 }
1809  
1810 void GFXOutputDev::updateLineDash(GfxState *state) 
1811 {
1812     if(this->dashPattern) {
1813         free(this->dashPattern);this->dashPattern = 0;
1814     }
1815     double *pattern = 0;
1816     state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1817     msg("<debug> updateLineDash, %d dashes", this->dashLength);
1818     if(!this->dashLength) {
1819         this->dashPattern = 0;
1820     } else {
1821         double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1822         memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1823         this->dashPattern = p;
1824     }
1825 }
1826   
1827 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1828 {
1829     this->page2page = page2page;
1830     this->num_pages = num_pages;
1831 }
1832
1833 void GFXOutputDev::updateLineWidth(GfxState *state)
1834 {
1835     double width = state->getTransformedLineWidth();
1836 }
1837
1838 void GFXOutputDev::updateLineCap(GfxState *state)
1839 {
1840     int c = state->getLineCap();
1841 }
1842
1843 void GFXOutputDev::updateLineJoin(GfxState *state)
1844 {
1845     int j = state->getLineJoin();
1846 }
1847
1848 void GFXOutputDev::updateFillColor(GfxState *state) 
1849 {
1850     GfxRGB rgb;
1851     double opaq = state->getFillOpacity();
1852     state->getFillRGB(&rgb);
1853 }
1854 void GFXOutputDev::updateFillOpacity(GfxState *state)
1855 {
1856     GfxRGB rgb;
1857     double opaq = state->getFillOpacity();
1858     state->getFillRGB(&rgb);
1859     dbg("update fillopaq %f", opaq);
1860 }
1861 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1862 {
1863     double opaq = state->getFillOpacity();
1864     dbg("update strokeopaq %f", opaq);
1865 }
1866 void GFXOutputDev::updateFillOverprint(GfxState *state)
1867 {
1868     double opaq = state->getFillOverprint();
1869     dbg("update filloverprint %f", opaq);
1870 }
1871 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1872 {
1873     double opaq = state->getStrokeOverprint();
1874     dbg("update strokeoverprint %f", opaq);
1875 }
1876 void GFXOutputDev::updateTransfer(GfxState *state)
1877 {
1878     dbg("update transfer");
1879 }
1880
1881
1882 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1883 {
1884     GfxRGB rgb;
1885     double opaq = state->getStrokeOpacity();
1886     state->getStrokeRGB(&rgb);
1887 }
1888
1889 void GFXOutputDev::updateFont(GfxState *state) 
1890 {
1891     GfxFont* gfxFont = state->getFont();
1892     if (!gfxFont) {
1893         return; 
1894     }  
1895     char*id = getFontID(gfxFont);
1896     msg("<verbose> Updating font to %s", id);
1897     if(gfxFont->getType() == fontType3) {
1898         infofeature("Type3 fonts");
1899         if(!config_extrafontdata) {
1900             return;
1901         }
1902     }
1903     if(!id) {
1904         msg("<error> Internal Error: FontID is null");
1905         return; 
1906     }
1907
1908     this->current_fontinfo = this->info->getFont(id);
1909
1910     if(!this->current_fontinfo) {
1911         msg("<error> Internal Error: no fontinfo for font %s", id);
1912         return;
1913     }
1914     if(!this->current_fontinfo->seen) {
1915         dumpFontInfo("<verbose>", gfxFont);
1916     }
1917
1918     current_gfxfont = this->current_fontinfo->getGfxFont();
1919     device->addfont(device, current_gfxfont);
1920     free(id);
1921
1922     updateFontMatrix(state);
1923 }
1924
1925 #define SQR(x) ((x)*(x))
1926
1927 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1928 {
1929     if((newwidth<1 || newheight<1) ||
1930        (width<=newwidth || height<=newheight))
1931         return 0;
1932     unsigned char*newdata;
1933     int x,y;
1934     newdata= (unsigned char*)malloc(newwidth*newheight);
1935     double fx = ((double)width)/newwidth;
1936     double fy = ((double)height)/newheight;
1937     double px = 0;
1938     int blocksize = (int)(8192/(fx*fy));
1939     int r = 8192*256/palettesize;
1940     for(x=0;x<newwidth;x++) {
1941         double ex = px + fx;
1942         int fromx = (int)px;
1943         int tox = (int)ex;
1944         int xweight1 = (int)((1-(px-fromx))*256);
1945         int xweight2 = (int)((ex-tox)*256);
1946         double py =0;
1947         for(y=0;y<newheight;y++) {
1948             double ey = py + fy;
1949             int fromy = (int)py;
1950             int toy = (int)ey;
1951             int yweight1 = (int)((1-(py-fromy))*256);
1952             int yweight2 = (int)((ey-toy)*256);
1953             int a = 0;
1954             int xx,yy;
1955             if(tox>=width) 
1956                 tox = width-1;
1957             if(toy>=height) 
1958                 toy = height-1;
1959             for(xx=fromx;xx<=tox;xx++)
1960             for(yy=fromy;yy<=toy;yy++) {
1961                 int b = 1-data[width*yy+xx];
1962                 int weight=256;
1963                 if(xx==fromx) weight = (weight*xweight1)/256;
1964                 if(xx==tox) weight = (weight*xweight2)/256;
1965                 if(yy==fromy) weight = (weight*yweight1)/256;
1966                 if(yy==toy) weight = (weight*yweight2)/256;
1967                 a+=b*weight;
1968             }
1969             //if(a) a=(palettesize-1)*r/blocksize;
1970             newdata[y*newwidth+x] = (a*blocksize)/r;
1971             py = ey;
1972         }
1973         px = ex;
1974     }
1975     return newdata;
1976 }
1977
1978 #define IMAGE_TYPE_JPEG 0
1979 #define IMAGE_TYPE_LOSSLESS 1
1980
1981 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1982         double x1,double y1,
1983         double x2,double y2,
1984         double x3,double y3,
1985         double x4,double y4, int type, int multiply)
1986 {
1987     gfxcolor_t*newpic=0;
1988     
1989     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1990     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1991    
1992     gfxline_t p1,p2,p3,p4,p5;
1993     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1994     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1995     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1996     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1997     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1998
1999     {p1.x = (int)(p1.x*20)/20.0;
2000      p1.y = (int)(p1.y*20)/20.0;
2001      p2.x = (int)(p2.x*20)/20.0;
2002      p2.y = (int)(p2.y*20)/20.0;
2003      p3.x = (int)(p3.x*20)/20.0;
2004      p3.y = (int)(p3.y*20)/20.0;
2005      p4.x = (int)(p4.x*20)/20.0;
2006      p4.y = (int)(p4.y*20)/20.0;
2007      p5.x = (int)(p5.x*20)/20.0;
2008      p5.y = (int)(p5.y*20)/20.0;
2009     }
2010
2011     gfxmatrix_t m;
2012     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2013     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2014         
2015     m.tx = p1.x - 0.5*multiply;
2016     m.ty = p1.y - 0.5*multiply;
2017
2018     gfximage_t img;
2019     img.data = (gfxcolor_t*)data;
2020     img.width = sizex;
2021     img.height = sizey;
2022   
2023     if(type == IMAGE_TYPE_JPEG)
2024         /* TODO: pass image_dpi to device instead */
2025         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2026
2027     dump_outline(&p1);
2028     dev->fillbitmap(dev, &p1, &img, &m, 0);
2029 }
2030
2031 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2032         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2033 {
2034     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2035 }
2036
2037 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2038         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2039 {
2040     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2041 }
2042
2043
2044 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2045                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2046                                    GBool inlineImg, int mask, int*maskColors,
2047                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2048 {
2049   /* the code in this function is *old*. It's not pretty, but it works. */
2050
2051   double x1,y1,x2,y2,x3,y3,x4,y4;
2052   ImageStream *imgStr;
2053   Guchar pixBuf[4];
2054   GfxRGB rgb;
2055   int ncomps = 1;
2056   int bits = 1;
2057   unsigned char* maskbitmap = 0;
2058                                  
2059   if(colorMap) {
2060     ncomps = colorMap->getNumPixelComps();
2061     bits = colorMap->getBits();
2062   }
2063       
2064   if(maskStr) {
2065       int x,y;
2066       unsigned char buf[8];
2067       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2068       if(maskColorMap) {
2069           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2070           imgMaskStr->reset();
2071           unsigned char pal[256];
2072           int n = 1 << colorMap->getBits();
2073           int t;
2074           for(t=0;t<n;t++) {
2075               GfxGray gray;
2076               pixBuf[0] = t;
2077               maskColorMap->getGray(pixBuf, &gray);
2078               pal[t] = colToByte(gray);
2079           }
2080           for (y = 0; y < maskHeight; y++) {
2081               for (x = 0; x < maskWidth; x++) {
2082                   imgMaskStr->getPixel(buf);
2083                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2084               }
2085           }
2086           delete imgMaskStr;
2087       } else {
2088           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2089           imgMaskStr->reset();
2090           for (y = 0; y < maskHeight; y++) {
2091               for (x = 0; x < maskWidth; x++) {
2092                   imgMaskStr->getPixel(buf);
2093                   buf[0]^=maskInvert;
2094                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2095               }
2096           }
2097           delete imgMaskStr;
2098       }
2099       maskStr->close();
2100   }
2101   
2102   imgStr = new ImageStream(str, width, ncomps,bits);
2103   imgStr->reset();
2104
2105   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2106   {
2107       msg("<verbose> Ignoring %d by %d image", width, height);
2108       unsigned char buf[8];
2109       int x,y;
2110       for (y = 0; y < height; ++y)
2111       for (x = 0; x < width; ++x) {
2112           imgStr->getPixel(buf);
2113       }
2114       delete imgStr;
2115       if(maskbitmap)
2116           free(maskbitmap);
2117       return;
2118   }
2119
2120   this->transformXY(state, 0, 1, &x1, &y1);
2121   this->transformXY(state, 0, 0, &x2, &y2);
2122   this->transformXY(state, 1, 0, &x3, &y3);
2123   this->transformXY(state, 1, 1, &x4, &y4);
2124
2125   if(type3active) {
2126       /* as type 3 bitmaps are antialized, we need to place them
2127          at integer coordinates, otherwise flash player's antializing
2128          will kick in and make everything blurry */
2129       x1 = (int)(x1);y1 = (int)(y1);
2130       x2 = (int)(x2);y2 = (int)(y2);
2131       x3 = (int)(x3);y3 = (int)(y3);
2132       x4 = (int)(x4);y4 = (int)(y4);
2133   }
2134
2135   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2136       if(!type3active) {
2137           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2138           gfxglobals->pbminfo = 1;
2139       }
2140       if(mask)
2141       msg("<verbose> drawing %d by %d masked picture", width, height);
2142   }
2143   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2144       msg("<notice> File contains jpeg pictures");
2145       gfxglobals->jpeginfo = 1;
2146   }
2147
2148   if(mask) {
2149       unsigned char buf[8];
2150       int x,y;
2151       unsigned char*pic = new unsigned char[width*height];
2152       gfxcolor_t pal[256];
2153       GfxRGB rgb;
2154       state->getFillRGB(&rgb);
2155
2156       memset(pal,255,sizeof(pal));
2157       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2158       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2159       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2160       pal[0].a = 255;              pal[1].a = 0;
2161     
2162       int numpalette = 2;
2163       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2164       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2165       for (y = 0; y < height; ++y)
2166       for (x = 0; x < width; ++x)
2167       {
2168             imgStr->getPixel(buf);
2169             if(invert) 
2170                 buf[0]=1-buf[0];
2171             pic[width*y+x] = buf[0];
2172       }
2173      
2174       if(type3active) {
2175           unsigned char*pic2 = 0;
2176           numpalette = 16;
2177           
2178           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2179
2180           if(!pic2) {
2181             delete[] pic;
2182             delete imgStr;
2183             return;
2184           }
2185           
2186           width = realwidth;
2187           height = realheight;
2188           delete[] pic;
2189           pic = pic2;
2190           
2191           /* make a black/white palette */
2192
2193           float r = 255./(float)(numpalette-1);
2194           int t;
2195           for(t=0;t<numpalette;t++) {
2196               pal[t].r = colToByte(rgb.r);
2197               pal[t].g = colToByte(rgb.g);
2198               pal[t].b = colToByte(rgb.b);
2199               pal[t].a = (unsigned char)(t*r);
2200           }
2201           
2202       }
2203
2204       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2205       for (y = 0; y < height; ++y) {
2206         for (x = 0; x < width; ++x) {
2207           pic2[width*y+x] = pal[pic[y*width+x]];
2208         }
2209       }
2210       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2211       delete[] pic2;
2212       delete[] pic;
2213       delete imgStr;
2214       if(maskbitmap) free(maskbitmap);
2215       return;
2216   }
2217
2218   int x,y;
2219
2220   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2221       gfxcolor_t*pic=new gfxcolor_t[width*height];
2222       for (y = 0; y < height; ++y) {
2223         for (x = 0; x < width; ++x) {
2224           imgStr->getPixel(pixBuf);
2225           colorMap->getRGB(pixBuf, &rgb);
2226           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2227           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2228           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2229           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2230           if(maskbitmap) {
2231               int x1 = x*maskWidth/width;
2232               int y1 = y*maskHeight/height;
2233               int x2 = (x+1)*maskWidth/width;
2234               int y2 = (y+1)*maskHeight/height;
2235               int xx,yy;
2236               unsigned int alpha=0;
2237               unsigned int count=0;
2238               for(xx=x1;xx<x2;xx++)
2239               for(yy=y1;yy<y2;yy++) {
2240                   alpha += maskbitmap[yy*maskWidth+xx];
2241                   count ++;
2242               }
2243               if(count) {
2244                 pic[width*y+x].a = alpha / count;
2245               } else {
2246                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2247               }
2248           }
2249         }
2250       }
2251       if(str->getKind()==strDCT)
2252           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2253       else
2254           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2255       delete[] pic;
2256       delete imgStr;
2257       if(maskbitmap) free(maskbitmap);
2258       return;
2259   } else {
2260       gfxcolor_t*pic=new gfxcolor_t[width*height];
2261       gfxcolor_t pal[256];
2262       int n = 1 << colorMap->getBits();
2263       int t;
2264       for(t=0;t<256;t++) {
2265           pixBuf[0] = t;
2266           colorMap->getRGB(pixBuf, &rgb);
2267
2268           {/*if(maskColors && *maskColors==t) {
2269               msg("<notice> Color %d is transparent", t);
2270               if (imgData->maskColors) {
2271                 *alpha = 0;
2272                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2273                   if (pix[i] < imgData->maskColors[2*i] ||
2274                       pix[i] > imgData->maskColors[2*i+1]) {
2275                     *alpha = 1;
2276                     break;
2277                   }
2278                 }
2279               } else {
2280                 *alpha = 1;
2281               }
2282               if(!*alpha) {
2283                     pal[t].r = 0;
2284                     pal[t].g = 0;
2285                     pal[t].b = 0;
2286                     pal[t].a = 0;
2287               }
2288           } else {*/
2289               pal[t].r = (unsigned char)(colToByte(rgb.r));
2290               pal[t].g = (unsigned char)(colToByte(rgb.g));
2291               pal[t].b = (unsigned char)(colToByte(rgb.b));
2292               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2293           }
2294       }
2295       for (y = 0; y < height; ++y) {
2296         for (x = 0; x < width; ++x) {
2297           imgStr->getPixel(pixBuf);
2298           pic[width*y+x] = pal[pixBuf[0]];
2299         }
2300       }
2301       if(maskbitmap) {
2302           if(maskWidth < width && maskHeight < height) {
2303               for(y = 0; y < height; y++) {
2304                   for (x = 0; x < width; x++) {
2305                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2306                   }
2307               }
2308           } else {
2309               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2310               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2311               double dx = width / maskWidth;
2312               double dy = height / maskHeight;
2313               double yy = 0;
2314               for(y = 0; y < maskHeight; y++) {
2315                   double xx = 0;
2316                   for (x = 0; x < maskWidth; x++) {
2317                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2318                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2319                       xx += dx;
2320                   }
2321                   yy += dy;
2322               }
2323               delete[] pic;
2324               pic = newpic;
2325               width = maskWidth;
2326               height = maskHeight;
2327           }
2328       }
2329       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2330
2331       delete[] pic;
2332       delete imgStr;
2333       if(maskbitmap) free(maskbitmap);
2334       return;
2335   }
2336 }
2337
2338 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2339                                    int width, int height, GBool invert,
2340                                    GBool inlineImg) 
2341 {
2342     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2343     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2344     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2345 }
2346
2347 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2348                          int width, int height, GfxImageColorMap *colorMap,
2349                          int *maskColors, GBool inlineImg)
2350 {
2351     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2352             colorMap?"colorMap":"no colorMap", 
2353             maskColors?"maskColors":"no maskColors",
2354             inlineImg);
2355     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2356             colorMap?"colorMap":"no colorMap", 
2357             maskColors?"maskColors":"no maskColors",
2358             inlineImg);
2359     if(colorMap)
2360         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2361                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2362     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2363 }
2364   
2365 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2366                                int width, int height,
2367                                GfxImageColorMap *colorMap,
2368                                Stream *maskStr, int maskWidth, int maskHeight,
2369                                GBool maskInvert)
2370 {
2371     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2372             colorMap?"colorMap":"no colorMap", 
2373             maskWidth, maskHeight);
2374     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2375             colorMap?"colorMap":"no colorMap", 
2376             maskWidth, maskHeight);
2377     if(colorMap)
2378         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2379                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2380     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2381 }
2382
2383 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2384                                    int width, int height,
2385                                    GfxImageColorMap *colorMap,
2386                                    Stream *maskStr,
2387                                    int maskWidth, int maskHeight,
2388                                    GfxImageColorMap *maskColorMap)
2389 {
2390     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2391             colorMap?"colorMap":"no colorMap", 
2392             maskWidth, maskHeight);
2393     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2394             colorMap?"colorMap":"no colorMap", 
2395             maskWidth, maskHeight);
2396     if(colorMap)
2397         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2398                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2399     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2400 }
2401
2402 void GFXOutputDev::stroke(GfxState *state) 
2403 {
2404     dbg("stroke");
2405
2406     GfxPath * path = state->getPath();
2407     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2408     strokeGfxline(state, line, 0);
2409     gfxline_free(line);
2410 }
2411
2412 void GFXOutputDev::fill(GfxState *state) 
2413 {
2414     gfxcolor_t col = getFillColor(state);
2415     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2416
2417     GfxPath * path = state->getPath();
2418     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2419     if(config_optimize_polygons) {
2420         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2421         gfxline_free(line);
2422         line = line2;
2423     }
2424     fillGfxLine(state, line);
2425     gfxline_free(line);
2426 }
2427
2428 void GFXOutputDev::eoFill(GfxState *state) 
2429 {
2430     gfxcolor_t col = getFillColor(state);
2431     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2432
2433     GfxPath * path = state->getPath();
2434     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2435     fillGfxLine(state, line);
2436     gfxline_free(line);
2437 }
2438
2439
2440 static const char* dirseparator()
2441 {
2442 #ifdef WIN32
2443     return "\\";
2444 #else
2445     return "/";
2446 #endif
2447 }
2448
2449 void addGlobalFont(const char*filename)
2450 {
2451     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2452     memset(f, 0, sizeof(fontfile_t));
2453     f->filename = filename;
2454     int len = strlen(filename);
2455     char*r1 = strrchr(filename, '/');
2456     char*r2 = strrchr(filename, '\\');
2457     if(r2>r1)
2458         r1 = r2;
2459     if(r1) {
2460         len = strlen(r1+1);
2461     }
2462     f->len = len;
2463
2464     msg("<notice> Adding font \"%s\".", filename);
2465     if(global_fonts_next) {
2466         global_fonts_next->next = f;
2467         global_fonts_next = global_fonts_next->next;
2468     } else {
2469         global_fonts_next = global_fonts = f;
2470     }
2471 }
2472
2473 void addGlobalLanguageDir(const char*dir)
2474 {
2475     msg("<notice> Adding %s to language pack directories", dir);
2476
2477     FILE*fi = 0;
2478     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2479     strcpy(config_file, dir);
2480     strcat(config_file, dirseparator());
2481     strcat(config_file, "add-to-xpdfrc");
2482
2483     fi = fopen(config_file, "rb");
2484     if(!fi) {
2485         msg("<error> Could not open %s", config_file);
2486         return;
2487     }
2488     globalParams->parseFile(new GString(config_file), fi);
2489     fclose(fi);
2490 }
2491
2492 void addGlobalFontDir(const char*dirname)
2493 {
2494 #ifdef HAVE_DIRENT_H
2495     msg("<notice> Adding %s to font directories", dirname);
2496     DIR*dir = opendir(dirname);
2497     if(!dir) {
2498         msg("<warning> Couldn't open directory %s", dirname);
2499         return;
2500     }
2501     struct dirent*ent;
2502     while(1) {
2503         ent = readdir (dir);
2504         if (!ent) 
2505             break;
2506         int l;
2507         char*name = ent->d_name;
2508         char type = 0;
2509         if(!name) continue;
2510         l=strlen(name);
2511         if(l<4)
2512             continue;
2513         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2514             type=1;
2515         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2516             type=3;
2517         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2518             type=2;
2519         if(type) {
2520             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2521             strcpy(fontname, dirname);
2522             strcat(fontname, dirseparator());
2523             strcat(fontname, name);
2524             addGlobalFont(fontname);
2525         }
2526     }
2527     closedir(dir);
2528 #else
2529     msg("<warning> No dirent.h");
2530 #endif
2531     
2532     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2533     memset(f, 0, sizeof(fontfile_t));
2534     f->filename = dirname;
2535     if(global_fontdirs_next) {
2536         global_fontdirs_next->next = f;
2537         global_fontdirs_next = global_fontdirs_next->next;
2538     } else {
2539         global_fontdirs_next = global_fontdirs = f;
2540     }
2541 }
2542
2543 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2544                                       GfxColorSpace *blendingColorSpace,
2545                                       GBool isolated, GBool knockout,
2546                                       GBool forSoftMask)
2547 {
2548     const char*colormodename = "";
2549   
2550     if(blendingColorSpace) {
2551         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2552     }
2553     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);
2554     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);
2555     
2556     //states[statepos].createsoftmask |= forSoftMask;
2557     states[statepos].createsoftmask = forSoftMask;
2558     states[statepos].transparencygroup = !forSoftMask;
2559     states[statepos].isolated = isolated;
2560
2561     states[statepos].olddevice = this->device;
2562     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2563     dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2564
2565     gfxdevice_record_init(this->device);
2566     
2567     /*if(!forSoftMask) { ////???
2568         state->setFillOpacity(0.0);
2569     }*/
2570     dbgindent+=2;
2571 }
2572
2573 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2574 {
2575     dbgindent-=2;
2576     gfxdevice_t*r = this->device;
2577
2578     dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2579     
2580     this->device = states[statepos].olddevice;
2581     if(!this->device) {
2582         msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2583         /* if these errors occur more often, we should build a seperate
2584            transparency group stack, like xpdf/SplashOutputDev.cc does */
2585         restoreState(state);
2586         this->device = states[statepos].olddevice;
2587     }
2588     states[statepos].olddevice = 0;
2589
2590     gfxresult_t*recording = r->finish(r);
2591     
2592     dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2593     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2594
2595     if(states[statepos].createsoftmask) {
2596         states[statepos-1].softmaskrecording = recording;
2597     } else {
2598         states[statepos-1].grouprecording = recording;
2599     }
2600     
2601     states[statepos].createsoftmask = 0;
2602     states[statepos].transparencygroup = 0;
2603     free(r);
2604 }
2605
2606 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2607 {
2608     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2609                                "colordodge","colorburn","hardlight","softlight","difference",
2610                                "exclusion","hue","saturation","color","luminosity"};
2611
2612     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2613     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2614
2615     if(state->getBlendMode() == gfxBlendNormal)
2616         infofeature("transparency groups");
2617     else {
2618         char buffer[80];
2619         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2620         warnfeature(buffer, 0);
2621     }
2622
2623     gfxresult_t*grouprecording = states[statepos].grouprecording;
2624   
2625     int blendmode = state->getBlendMode();
2626     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2627         int alpha = (int)(state->getFillOpacity()*255);
2628         if(blendmode == gfxBlendMultiply && alpha>200)
2629             alpha = 128;
2630         gfxdevice_t ops;
2631         dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2632         gfxdevice_ops_init(&ops, this->device, alpha);
2633         gfxresult_record_replay(grouprecording, &ops);
2634         ops.finish(&ops);
2635     }
2636     grouprecording->destroy(grouprecording);
2637
2638     states[statepos].grouprecording = 0;
2639 }
2640
2641 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2642 {
2643     if(states[statepos].softmask) {
2644         /* shouldn't happen, but *does* happen */
2645         clearSoftMask(state);
2646     }
2647
2648     /* alpha = 1: retrieve mask values from alpha layer
2649        alpha = 0: retrieve mask values from luminance */
2650
2651     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2652             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2653     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2654             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2655     if(!alpha)
2656         infofeature("soft masks");
2657     else
2658         warnfeature("soft masks from alpha channel",0);
2659    
2660     if(states[statepos].olddevice) {
2661         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2662         exit(1);
2663     }
2664     states[statepos].olddevice = this->device;
2665     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2666     gfxdevice_record_init(this->device);
2667
2668     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2669     
2670     states[statepos].softmask = 1;
2671     states[statepos].softmask_alpha = alpha;
2672 }
2673
2674 static inline Guchar div255(int x) {
2675   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2676 }
2677
2678 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2679 {
2680     if(c < min) c = min;
2681     if(c > max) c = max;
2682     return c;
2683 }
2684
2685 void GFXOutputDev::clearSoftMask(GfxState *state)
2686 {
2687     if(!states[statepos].softmask)
2688         return;
2689     states[statepos].softmask = 0;
2690     dbg("clearSoftMask statepos=%d", statepos);
2691     msg("<verbose> clearSoftMask statepos=%d", statepos);
2692     
2693     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2694         msg("<error> Error in softmask/tgroup ordering");
2695         return;
2696     }
2697   
2698     gfxresult_t*mask = states[statepos].softmaskrecording;
2699     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2700     this->device = states[statepos].olddevice;
2701
2702     /* get outline of all objects below the soft mask */
2703     gfxdevice_t uniondev;
2704     gfxdevice_union_init(&uniondev, 0);
2705     gfxresult_record_replay(below, &uniondev);
2706     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2707     uniondev.finish(&uniondev);
2708     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2709     gfxline_free(belowoutline);belowoutline=0;
2710 #if 0 
2711     this->device->startclip(this->device, belowoutline);
2712     gfxresult_record_replay(below, this->device);
2713     gfxresult_record_replay(mask, this->device);
2714     this->device->endclip(this->device);
2715 #endif
2716     
2717     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2718     if(width<=0 || height<=0)
2719         return;
2720
2721     gfxdevice_t belowrender;
2722     gfxdevice_render_init(&belowrender);
2723     if(states[statepos+1].isolated) {
2724         belowrender.setparameter(&belowrender, "fillwhite", "1");
2725     }
2726     belowrender.setparameter(&belowrender, "antialize", "2");
2727     belowrender.startpage(&belowrender, width, height);
2728     gfxresult_record_replay(below, &belowrender);
2729     belowrender.endpage(&belowrender);
2730     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2731     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2732     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2733
2734     gfxdevice_t maskrender;
2735     gfxdevice_render_init(&maskrender);
2736     maskrender.startpage(&maskrender, width, height);
2737     gfxresult_record_replay(mask, &maskrender);
2738     maskrender.endpage(&maskrender);
2739     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2740     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2741
2742     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2743         msg("<fatal> Internal error in mask drawing");
2744         return;
2745     }
2746
2747     int y,x;
2748     for(y=0;y<height;y++) {
2749         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2750         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2751         for(x=0;x<width;x++) {
2752             int alpha;
2753             if(states[statepos].softmask_alpha) {
2754                 alpha = l1->a;
2755             } else {
2756                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2757             }
2758
2759             l2->a = div255(alpha*l2->a);
2760
2761             /* DON'T premultiply alpha- this is done by fillbitmap,
2762                depending on the output device */
2763             //l2->r = div255(alpha*l2->r);
2764             //l2->g = div255(alpha*l2->g);
2765             //l2->b = div255(alpha*l2->b);
2766
2767             l1++;
2768             l2++;
2769         }
2770     }
2771     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2772
2773     gfxmatrix_t matrix;
2774     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2775     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2776
2777     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2778
2779     mask->destroy(mask);
2780     below->destroy(below);
2781     maskresult->destroy(maskresult);
2782     belowresult->destroy(belowresult);
2783     states[statepos].softmaskrecording = 0;
2784 }
2785   
2786 //class MemCheck
2787 //{
2788 //    public: ~MemCheck()
2789 //    {
2790 //        delete globalParams;globalParams=0;
2791 //        Object::memCheck(stderr);
2792 //        gMemReport(stderr);
2793 //    }
2794 //} myMemCheck;
2795