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