always handle external fonts using fontconfig, if available (don't do filename compar...
[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 dest=ha->getDest();
1631             if (dest){ 
1632               if (dest->isPageRef()){
1633                 Ref pageref=dest->getPageRef();
1634                 page=catalog->findPage(pageref.num,pageref.gen);
1635               }
1636               else  page=dest->getPageNum();
1637               sprintf(buf, "%d", page);
1638               s = strdup(buf);
1639             }
1640         }
1641         break;
1642         case actionGoToR: {
1643             type = "GoToR";
1644             LinkGoToR*l = (LinkGoToR*)action;
1645             GString*g = l->getFileName();
1646             if(g)
1647              s = strdup(g->getCString());
1648             if(!s) {
1649                 /* if the GoToR link has no filename, then
1650                    try to find a refernce in the *local*
1651                    file */
1652                 GString*g = l->getNamedDest();
1653                 if(g)
1654                  s = strdup(g->getCString());
1655             }
1656         }
1657         break;
1658         case actionNamed: {
1659             type = "Named";
1660             LinkNamed*l = (LinkNamed*)action;
1661             GString*name = l->getName();
1662             if(name) {
1663                 s = strdup(name->lowerCase()->getCString());
1664                 named = name->getCString();
1665                 if(!strchr(s,':')) 
1666                 {
1667                     if(strstr(s, "next") || strstr(s, "forward"))
1668                     {
1669                         page = currentpage + 1;
1670                     }
1671                     else if(strstr(s, "prev") || strstr(s, "back"))
1672                     {
1673                         page = currentpage - 1;
1674                     }
1675                     else if(strstr(s, "last") || strstr(s, "end"))
1676                     {
1677                         if(this->page2page && this->num_pages) {
1678                             page = this->page2page[this->num_pages-1];
1679                         }
1680                     }
1681                     else if(strstr(s, "first") || strstr(s, "top"))
1682                     {
1683                         page = 1;
1684                     }
1685                 }
1686             }
1687         }
1688         break;
1689         case actionLaunch: {
1690             type = "Launch";
1691             LinkLaunch*l = (LinkLaunch*)action;
1692             GString * str = new GString(l->getFileName());
1693             GString * params = l->getParams();
1694             if(params)
1695                 str->append(params);
1696             s = strdup(str->getCString());
1697             delete str;
1698         }
1699         break;
1700         case actionURI: {
1701             char*url = 0;
1702             type = "URI";
1703             LinkURI*l = (LinkURI*)action;
1704             GString*g = l->getURI();
1705             if(g) {
1706              url = g->getCString();
1707              s = strdup(url);
1708             }
1709         }
1710         break;
1711         case actionUnknown: {
1712             type = "Unknown";
1713             LinkUnknown*l = (LinkUnknown*)action;
1714             s = strdup("");
1715         }
1716         break;
1717         default: {
1718             msg("<error> Unknown link type!");
1719             break;
1720         }
1721     }
1722
1723     if(!s) s = strdup("-?-");
1724     
1725     msg("<trace> drawlink s=%s", s);
1726
1727     if(!gfxglobals->linkinfo && (page || s))
1728     {
1729         msg("<notice> File contains links");
1730         gfxglobals->linkinfo = 1;
1731     }
1732     
1733     if(page>0) {
1734         int t;
1735         int lpage = -1;
1736         for(t=1;t<=this->num_pages;t++) {
1737             if(this->page2page[t]==page) {
1738                 lpage = t;
1739                 break;
1740             }
1741         }
1742         if(lpage<0) {
1743             lpage = page;
1744         }
1745
1746         char buf[80];
1747         sprintf(buf, "page%d", lpage);
1748         device->drawlink(device, points, buf);
1749     }
1750     else if(s)
1751     {
1752         device->drawlink(device, points, s);
1753     }
1754
1755     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1756     free(s);s=0;
1757 }
1758
1759 void GFXOutputDev::saveState(GfxState *state) {
1760     dbg("saveState %08x", state); dbgindent+=2;
1761
1762     msg("<trace> saveState %08x", state);
1763     updateAll(state);
1764     if(statepos>=64) {
1765       msg("<fatal> Too many nested states in pdf.");
1766       exit(1);
1767     }
1768     statepos ++;
1769     states[statepos].state = state;
1770     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1771     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1772     states[statepos].clipping = 0;
1773     states[statepos].olddevice = 0;
1774     states[statepos].clipbbox = states[statepos-1].clipbbox;
1775 };
1776
1777 void GFXOutputDev::restoreState(GfxState *state) {
1778   dbgindent-=2; dbg("restoreState %08x", state);
1779
1780   if(statepos==0) {
1781       msg("<fatal> Invalid restoreState");
1782       exit(1);
1783   }
1784   msg("<trace> restoreState %08x%s%s", state,
1785                                   states[statepos].softmask?" (end softmask)":"",
1786                                   states[statepos].clipping?" (end clipping)":"");
1787   if(states[statepos].softmask) {
1788       clearSoftMask(state);
1789   }
1790   updateAll(state);
1791   
1792   while(states[statepos].clipping) {
1793       device->endclip(device);
1794       states[statepos].clipping--;
1795   }
1796   if(states[statepos].state!=state) {
1797       msg("<fatal> bad state nesting");
1798       exit(1);
1799   }
1800   states[statepos].state=0;
1801   statepos--;
1802 }
1803  
1804 void GFXOutputDev::updateLineDash(GfxState *state) 
1805 {
1806     if(this->dashPattern) {
1807         free(this->dashPattern);this->dashPattern = 0;
1808     }
1809     double *pattern = 0;
1810     state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1811     msg("<debug> updateLineDash, %d dashes", this->dashLength);
1812     if(!this->dashLength) {
1813         this->dashPattern = 0;
1814     } else {
1815         double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1816         memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1817         this->dashPattern = p;
1818     }
1819 }
1820   
1821 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1822 {
1823     this->page2page = page2page;
1824     this->num_pages = num_pages;
1825 }
1826
1827 void GFXOutputDev::updateLineWidth(GfxState *state)
1828 {
1829     double width = state->getTransformedLineWidth();
1830 }
1831
1832 void GFXOutputDev::updateLineCap(GfxState *state)
1833 {
1834     int c = state->getLineCap();
1835 }
1836
1837 void GFXOutputDev::updateLineJoin(GfxState *state)
1838 {
1839     int j = state->getLineJoin();
1840 }
1841
1842 void GFXOutputDev::updateFillColor(GfxState *state) 
1843 {
1844     GfxRGB rgb;
1845     double opaq = state->getFillOpacity();
1846     state->getFillRGB(&rgb);
1847 }
1848 void GFXOutputDev::updateFillOpacity(GfxState *state)
1849 {
1850     GfxRGB rgb;
1851     double opaq = state->getFillOpacity();
1852     state->getFillRGB(&rgb);
1853     dbg("update fillopaq %f", opaq);
1854 }
1855 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1856 {
1857     double opaq = state->getFillOpacity();
1858     dbg("update strokeopaq %f", opaq);
1859 }
1860 void GFXOutputDev::updateFillOverprint(GfxState *state)
1861 {
1862     double opaq = state->getFillOverprint();
1863     dbg("update filloverprint %f", opaq);
1864 }
1865 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1866 {
1867     double opaq = state->getStrokeOverprint();
1868     dbg("update strokeoverprint %f", opaq);
1869 }
1870 void GFXOutputDev::updateTransfer(GfxState *state)
1871 {
1872     dbg("update transfer");
1873 }
1874
1875
1876 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1877 {
1878     GfxRGB rgb;
1879     double opaq = state->getStrokeOpacity();
1880     state->getStrokeRGB(&rgb);
1881 }
1882
1883 void GFXOutputDev::updateFont(GfxState *state) 
1884 {
1885     GfxFont* gfxFont = state->getFont();
1886     if (!gfxFont) {
1887         return; 
1888     }  
1889     char*id = getFontID(gfxFont);
1890     msg("<verbose> Updating font to %s", id);
1891     if(gfxFont->getType() == fontType3) {
1892         infofeature("Type3 fonts");
1893         if(!config_extrafontdata) {
1894             return;
1895         }
1896     }
1897     if(!id) {
1898         msg("<error> Internal Error: FontID is null");
1899         return; 
1900     }
1901
1902     this->current_fontinfo = this->info->getFont(id);
1903
1904     if(!this->current_fontinfo) {
1905         msg("<error> Internal Error: no fontinfo for font %s", id);
1906         return;
1907     }
1908     if(!this->current_fontinfo->seen) {
1909         dumpFontInfo("<verbose>", gfxFont);
1910     }
1911
1912     current_gfxfont = this->current_fontinfo->getGfxFont();
1913     device->addfont(device, current_gfxfont);
1914     free(id);
1915
1916     updateFontMatrix(state);
1917 }
1918
1919 #define SQR(x) ((x)*(x))
1920
1921 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1922 {
1923     if((newwidth<1 || newheight<1) ||
1924        (width<=newwidth || height<=newheight))
1925         return 0;
1926     unsigned char*newdata;
1927     int x,y;
1928     newdata= (unsigned char*)malloc(newwidth*newheight);
1929     double fx = ((double)width)/newwidth;
1930     double fy = ((double)height)/newheight;
1931     double px = 0;
1932     int blocksize = (int)(8192/(fx*fy));
1933     int r = 8192*256/palettesize;
1934     for(x=0;x<newwidth;x++) {
1935         double ex = px + fx;
1936         int fromx = (int)px;
1937         int tox = (int)ex;
1938         int xweight1 = (int)((1-(px-fromx))*256);
1939         int xweight2 = (int)((ex-tox)*256);
1940         double py =0;
1941         for(y=0;y<newheight;y++) {
1942             double ey = py + fy;
1943             int fromy = (int)py;
1944             int toy = (int)ey;
1945             int yweight1 = (int)((1-(py-fromy))*256);
1946             int yweight2 = (int)((ey-toy)*256);
1947             int a = 0;
1948             int xx,yy;
1949             if(tox>=width) 
1950                 tox = width-1;
1951             if(toy>=height) 
1952                 toy = height-1;
1953             for(xx=fromx;xx<=tox;xx++)
1954             for(yy=fromy;yy<=toy;yy++) {
1955                 int b = 1-data[width*yy+xx];
1956                 int weight=256;
1957                 if(xx==fromx) weight = (weight*xweight1)/256;
1958                 if(xx==tox) weight = (weight*xweight2)/256;
1959                 if(yy==fromy) weight = (weight*yweight1)/256;
1960                 if(yy==toy) weight = (weight*yweight2)/256;
1961                 a+=b*weight;
1962             }
1963             //if(a) a=(palettesize-1)*r/blocksize;
1964             newdata[y*newwidth+x] = (a*blocksize)/r;
1965             py = ey;
1966         }
1967         px = ex;
1968     }
1969     return newdata;
1970 }
1971
1972 #define IMAGE_TYPE_JPEG 0
1973 #define IMAGE_TYPE_LOSSLESS 1
1974
1975 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1976         double x1,double y1,
1977         double x2,double y2,
1978         double x3,double y3,
1979         double x4,double y4, int type, int multiply)
1980 {
1981     gfxcolor_t*newpic=0;
1982     
1983     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1984     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1985    
1986     gfxline_t p1,p2,p3,p4,p5;
1987     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1988     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1989     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1990     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1991     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1992
1993     {p1.x = (int)(p1.x*20)/20.0;
1994      p1.y = (int)(p1.y*20)/20.0;
1995      p2.x = (int)(p2.x*20)/20.0;
1996      p2.y = (int)(p2.y*20)/20.0;
1997      p3.x = (int)(p3.x*20)/20.0;
1998      p3.y = (int)(p3.y*20)/20.0;
1999      p4.x = (int)(p4.x*20)/20.0;
2000      p4.y = (int)(p4.y*20)/20.0;
2001      p5.x = (int)(p5.x*20)/20.0;
2002      p5.y = (int)(p5.y*20)/20.0;
2003     }
2004
2005     gfxmatrix_t m;
2006     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2007     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2008         
2009     m.tx = p1.x - 0.5*multiply;
2010     m.ty = p1.y - 0.5*multiply;
2011
2012     gfximage_t img;
2013     img.data = (gfxcolor_t*)data;
2014     img.width = sizex;
2015     img.height = sizey;
2016   
2017     if(type == IMAGE_TYPE_JPEG)
2018         /* TODO: pass image_dpi to device instead */
2019         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2020
2021     dump_outline(&p1);
2022     dev->fillbitmap(dev, &p1, &img, &m, 0);
2023 }
2024
2025 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2026         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2027 {
2028     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2029 }
2030
2031 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2032         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2033 {
2034     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2035 }
2036
2037
2038 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2039                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2040                                    GBool inlineImg, int mask, int*maskColors,
2041                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2042 {
2043   /* the code in this function is *old*. It's not pretty, but it works. */
2044
2045   double x1,y1,x2,y2,x3,y3,x4,y4;
2046   ImageStream *imgStr;
2047   Guchar pixBuf[4];
2048   GfxRGB rgb;
2049   int ncomps = 1;
2050   int bits = 1;
2051   unsigned char* maskbitmap = 0;
2052                                  
2053   if(colorMap) {
2054     ncomps = colorMap->getNumPixelComps();
2055     bits = colorMap->getBits();
2056   }
2057       
2058   if(maskStr) {
2059       int x,y;
2060       unsigned char buf[8];
2061       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2062       if(maskColorMap) {
2063           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2064           imgMaskStr->reset();
2065           unsigned char pal[256];
2066           int n = 1 << colorMap->getBits();
2067           int t;
2068           for(t=0;t<n;t++) {
2069               GfxGray gray;
2070               pixBuf[0] = t;
2071               maskColorMap->getGray(pixBuf, &gray);
2072               pal[t] = colToByte(gray);
2073           }
2074           for (y = 0; y < maskHeight; y++) {
2075               for (x = 0; x < maskWidth; x++) {
2076                   imgMaskStr->getPixel(buf);
2077                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2078               }
2079           }
2080           delete imgMaskStr;
2081       } else {
2082           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2083           imgMaskStr->reset();
2084           for (y = 0; y < maskHeight; y++) {
2085               for (x = 0; x < maskWidth; x++) {
2086                   imgMaskStr->getPixel(buf);
2087                   buf[0]^=maskInvert;
2088                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2089               }
2090           }
2091           delete imgMaskStr;
2092       }
2093       maskStr->close();
2094   }
2095   
2096   imgStr = new ImageStream(str, width, ncomps,bits);
2097   imgStr->reset();
2098
2099   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2100   {
2101       msg("<verbose> Ignoring %d by %d image", width, height);
2102       unsigned char buf[8];
2103       int x,y;
2104       for (y = 0; y < height; ++y)
2105       for (x = 0; x < width; ++x) {
2106           imgStr->getPixel(buf);
2107       }
2108       delete imgStr;
2109       if(maskbitmap)
2110           free(maskbitmap);
2111       return;
2112   }
2113
2114   this->transformXY(state, 0, 1, &x1, &y1);
2115   this->transformXY(state, 0, 0, &x2, &y2);
2116   this->transformXY(state, 1, 0, &x3, &y3);
2117   this->transformXY(state, 1, 1, &x4, &y4);
2118
2119   if(type3active) {
2120       /* as type 3 bitmaps are antialized, we need to place them
2121          at integer coordinates, otherwise flash player's antializing
2122          will kick in and make everything blurry */
2123       x1 = (int)(x1);y1 = (int)(y1);
2124       x2 = (int)(x2);y2 = (int)(y2);
2125       x3 = (int)(x3);y3 = (int)(y3);
2126       x4 = (int)(x4);y4 = (int)(y4);
2127   }
2128
2129   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2130       if(!type3active) {
2131           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2132           gfxglobals->pbminfo = 1;
2133       }
2134       if(mask)
2135       msg("<verbose> drawing %d by %d masked picture", width, height);
2136   }
2137   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2138       msg("<notice> File contains jpeg pictures");
2139       gfxglobals->jpeginfo = 1;
2140   }
2141
2142   if(mask) {
2143       unsigned char buf[8];
2144       int x,y;
2145       unsigned char*pic = new unsigned char[width*height];
2146       gfxcolor_t pal[256];
2147       GfxRGB rgb;
2148       state->getFillRGB(&rgb);
2149
2150       memset(pal,255,sizeof(pal));
2151       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2152       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2153       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2154       pal[0].a = 255;              pal[1].a = 0;
2155     
2156       int numpalette = 2;
2157       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2158       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2159       for (y = 0; y < height; ++y)
2160       for (x = 0; x < width; ++x)
2161       {
2162             imgStr->getPixel(buf);
2163             if(invert) 
2164                 buf[0]=1-buf[0];
2165             pic[width*y+x] = buf[0];
2166       }
2167      
2168       if(type3active) {
2169           unsigned char*pic2 = 0;
2170           numpalette = 16;
2171           
2172           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2173
2174           if(!pic2) {
2175             delete[] pic;
2176             delete imgStr;
2177             return;
2178           }
2179           
2180           width = realwidth;
2181           height = realheight;
2182           delete[] pic;
2183           pic = pic2;
2184           
2185           /* make a black/white palette */
2186
2187           float r = 255./(float)(numpalette-1);
2188           int t;
2189           for(t=0;t<numpalette;t++) {
2190               pal[t].r = colToByte(rgb.r);
2191               pal[t].g = colToByte(rgb.g);
2192               pal[t].b = colToByte(rgb.b);
2193               pal[t].a = (unsigned char)(t*r);
2194           }
2195           
2196       }
2197
2198       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2199       for (y = 0; y < height; ++y) {
2200         for (x = 0; x < width; ++x) {
2201           pic2[width*y+x] = pal[pic[y*width+x]];
2202         }
2203       }
2204       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2205       delete[] pic2;
2206       delete[] pic;
2207       delete imgStr;
2208       if(maskbitmap) free(maskbitmap);
2209       return;
2210   }
2211
2212   int x,y;
2213
2214   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2215       gfxcolor_t*pic=new gfxcolor_t[width*height];
2216       for (y = 0; y < height; ++y) {
2217         for (x = 0; x < width; ++x) {
2218           imgStr->getPixel(pixBuf);
2219           colorMap->getRGB(pixBuf, &rgb);
2220           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2221           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2222           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2223           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2224           if(maskbitmap) {
2225               int x1 = x*maskWidth/width;
2226               int y1 = y*maskHeight/height;
2227               int x2 = (x+1)*maskWidth/width;
2228               int y2 = (y+1)*maskHeight/height;
2229               int xx,yy;
2230               unsigned int alpha=0;
2231               unsigned int count=0;
2232               for(xx=x1;xx<x2;xx++)
2233               for(yy=y1;yy<y2;yy++) {
2234                   alpha += maskbitmap[yy*maskWidth+xx];
2235                   count ++;
2236               }
2237               if(count) {
2238                 pic[width*y+x].a = alpha / count;
2239               } else {
2240                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2241               }
2242           }
2243         }
2244       }
2245       if(str->getKind()==strDCT)
2246           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2247       else
2248           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2249       delete[] pic;
2250       delete imgStr;
2251       if(maskbitmap) free(maskbitmap);
2252       return;
2253   } else {
2254       gfxcolor_t*pic=new gfxcolor_t[width*height];
2255       gfxcolor_t pal[256];
2256       int n = 1 << colorMap->getBits();
2257       int t;
2258       for(t=0;t<256;t++) {
2259           pixBuf[0] = t;
2260           colorMap->getRGB(pixBuf, &rgb);
2261
2262           {/*if(maskColors && *maskColors==t) {
2263               msg("<notice> Color %d is transparent", t);
2264               if (imgData->maskColors) {
2265                 *alpha = 0;
2266                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2267                   if (pix[i] < imgData->maskColors[2*i] ||
2268                       pix[i] > imgData->maskColors[2*i+1]) {
2269                     *alpha = 1;
2270                     break;
2271                   }
2272                 }
2273               } else {
2274                 *alpha = 1;
2275               }
2276               if(!*alpha) {
2277                     pal[t].r = 0;
2278                     pal[t].g = 0;
2279                     pal[t].b = 0;
2280                     pal[t].a = 0;
2281               }
2282           } else {*/
2283               pal[t].r = (unsigned char)(colToByte(rgb.r));
2284               pal[t].g = (unsigned char)(colToByte(rgb.g));
2285               pal[t].b = (unsigned char)(colToByte(rgb.b));
2286               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2287           }
2288       }
2289       for (y = 0; y < height; ++y) {
2290         for (x = 0; x < width; ++x) {
2291           imgStr->getPixel(pixBuf);
2292           pic[width*y+x] = pal[pixBuf[0]];
2293         }
2294       }
2295       if(maskbitmap) {
2296           if(maskWidth < width && maskHeight < height) {
2297               for(y = 0; y < height; y++) {
2298                   for (x = 0; x < width; x++) {
2299                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2300                   }
2301               }
2302           } else {
2303               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2304               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2305               double dx = width / maskWidth;
2306               double dy = height / maskHeight;
2307               double yy = 0;
2308               for(y = 0; y < maskHeight; y++) {
2309                   double xx = 0;
2310                   for (x = 0; x < maskWidth; x++) {
2311                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2312                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2313                       xx += dx;
2314                   }
2315                   yy += dy;
2316               }
2317               delete[] pic;
2318               pic = newpic;
2319               width = maskWidth;
2320               height = maskHeight;
2321           }
2322       }
2323       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2324
2325       delete[] pic;
2326       delete imgStr;
2327       if(maskbitmap) free(maskbitmap);
2328       return;
2329   }
2330 }
2331
2332 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2333                                    int width, int height, GBool invert,
2334                                    GBool inlineImg) 
2335 {
2336     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2337     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2338     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2339 }
2340
2341 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2342                          int width, int height, GfxImageColorMap *colorMap,
2343                          int *maskColors, GBool inlineImg)
2344 {
2345     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2346             colorMap?"colorMap":"no colorMap", 
2347             maskColors?"maskColors":"no maskColors",
2348             inlineImg);
2349     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2350             colorMap?"colorMap":"no colorMap", 
2351             maskColors?"maskColors":"no maskColors",
2352             inlineImg);
2353     if(colorMap)
2354         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2355                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2356     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2357 }
2358   
2359 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2360                                int width, int height,
2361                                GfxImageColorMap *colorMap,
2362                                Stream *maskStr, int maskWidth, int maskHeight,
2363                                GBool maskInvert)
2364 {
2365     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2366             colorMap?"colorMap":"no colorMap", 
2367             maskWidth, maskHeight);
2368     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2369             colorMap?"colorMap":"no colorMap", 
2370             maskWidth, maskHeight);
2371     if(colorMap)
2372         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2373                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2374     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2375 }
2376
2377 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2378                                    int width, int height,
2379                                    GfxImageColorMap *colorMap,
2380                                    Stream *maskStr,
2381                                    int maskWidth, int maskHeight,
2382                                    GfxImageColorMap *maskColorMap)
2383 {
2384     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2385             colorMap?"colorMap":"no colorMap", 
2386             maskWidth, maskHeight);
2387     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2388             colorMap?"colorMap":"no colorMap", 
2389             maskWidth, maskHeight);
2390     if(colorMap)
2391         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2392                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2393     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2394 }
2395
2396 void GFXOutputDev::stroke(GfxState *state) 
2397 {
2398     dbg("stroke");
2399
2400     GfxPath * path = state->getPath();
2401     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2402     strokeGfxline(state, line, 0);
2403     gfxline_free(line);
2404 }
2405
2406 void GFXOutputDev::fill(GfxState *state) 
2407 {
2408     gfxcolor_t col = getFillColor(state);
2409     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2410
2411     GfxPath * path = state->getPath();
2412     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2413     if(config_optimize_polygons) {
2414         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2415         gfxline_free(line);
2416         line = line2;
2417     }
2418     fillGfxLine(state, line);
2419     gfxline_free(line);
2420 }
2421
2422 void GFXOutputDev::eoFill(GfxState *state) 
2423 {
2424     gfxcolor_t col = getFillColor(state);
2425     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2426
2427     GfxPath * path = state->getPath();
2428     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2429     fillGfxLine(state, line);
2430     gfxline_free(line);
2431 }
2432
2433
2434 static const char* dirseparator()
2435 {
2436 #ifdef WIN32
2437     return "\\";
2438 #else
2439     return "/";
2440 #endif
2441 }
2442
2443 void addGlobalFont(const char*filename)
2444 {
2445     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2446     memset(f, 0, sizeof(fontfile_t));
2447     f->filename = filename;
2448     int len = strlen(filename);
2449     char*r1 = strrchr(filename, '/');
2450     char*r2 = strrchr(filename, '\\');
2451     if(r2>r1)
2452         r1 = r2;
2453     if(r1) {
2454         len = strlen(r1+1);
2455     }
2456     f->len = len;
2457
2458     msg("<verbose> Adding font \"%s\".", filename);
2459     if(global_fonts_next) {
2460         global_fonts_next->next = f;
2461         global_fonts_next = global_fonts_next->next;
2462     } else {
2463         global_fonts_next = global_fonts = f;
2464     }
2465 }
2466
2467 void addGlobalLanguageDir(const char*dir)
2468 {
2469     msg("<notice> Adding %s to language pack directories", dir);
2470
2471     FILE*fi = 0;
2472     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2473     strcpy(config_file, dir);
2474     strcat(config_file, dirseparator());
2475     strcat(config_file, "add-to-xpdfrc");
2476
2477     fi = fopen(config_file, "rb");
2478     if(!fi) {
2479         msg("<error> Could not open %s", config_file);
2480         return;
2481     }
2482     globalParams->parseFile(new GString(config_file), fi);
2483     fclose(fi);
2484 }
2485
2486 void addGlobalFontDir(const char*dirname)
2487 {
2488 #ifdef HAVE_DIRENT_H
2489     DIR*dir = opendir(dirname);
2490     if(!dir) {
2491         msg("<warning> Couldn't open directory %s", dirname);
2492         return;
2493     }
2494     struct dirent*ent;
2495     int fonts = 0;
2496     while(1) {
2497         ent = readdir (dir);
2498         if (!ent) 
2499             break;
2500         int l;
2501         char*name = ent->d_name;
2502         char type = 0;
2503         if(!name) continue;
2504         l=strlen(name);
2505         if(l<4)
2506             continue;
2507         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2508             type=1;
2509         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2510             type=3;
2511         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2512             type=2;
2513         if(type) {
2514             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2515             strcpy(fontname, dirname);
2516             strcat(fontname, dirseparator());
2517             strcat(fontname, name);
2518             addGlobalFont(fontname);
2519             fonts++;
2520         }
2521     }
2522     msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2523     closedir(dir);
2524 #else
2525     msg("<warning> No dirent.h");
2526 #endif
2527 }
2528
2529 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2530                                       GfxColorSpace *blendingColorSpace,
2531                                       GBool isolated, GBool knockout,
2532                                       GBool forSoftMask)
2533 {
2534     const char*colormodename = "";
2535   
2536     if(blendingColorSpace) {
2537         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2538     }
2539     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);
2540     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);
2541     
2542     //states[statepos].createsoftmask |= forSoftMask;
2543     states[statepos].createsoftmask = forSoftMask;
2544     states[statepos].transparencygroup = !forSoftMask;
2545     states[statepos].isolated = isolated;
2546
2547     states[statepos].olddevice = this->device;
2548     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2549     dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2550
2551     gfxdevice_record_init(this->device);
2552     
2553     /*if(!forSoftMask) { ////???
2554         state->setFillOpacity(0.0);
2555     }*/
2556     dbgindent+=2;
2557 }
2558
2559 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2560 {
2561     dbgindent-=2;
2562     gfxdevice_t*r = this->device;
2563
2564     dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2565     
2566     this->device = states[statepos].olddevice;
2567     if(!this->device) {
2568         msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2569         /* if these errors occur more often, we should build a seperate
2570            transparency group stack, like xpdf/SplashOutputDev.cc does */
2571         restoreState(state);
2572         this->device = states[statepos].olddevice;
2573     }
2574     states[statepos].olddevice = 0;
2575
2576     gfxresult_t*recording = r->finish(r);
2577     
2578     dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2579     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2580
2581     if(states[statepos].createsoftmask) {
2582         states[statepos-1].softmaskrecording = recording;
2583     } else {
2584         states[statepos-1].grouprecording = recording;
2585     }
2586     
2587     states[statepos].createsoftmask = 0;
2588     states[statepos].transparencygroup = 0;
2589     free(r);
2590 }
2591
2592 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2593 {
2594     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2595                                "colordodge","colorburn","hardlight","softlight","difference",
2596                                "exclusion","hue","saturation","color","luminosity"};
2597
2598     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2599     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2600
2601     if(state->getBlendMode() == gfxBlendNormal)
2602         infofeature("transparency groups");
2603     else {
2604         char buffer[80];
2605         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2606         warnfeature(buffer, 0);
2607     }
2608
2609     gfxresult_t*grouprecording = states[statepos].grouprecording;
2610   
2611     int blendmode = state->getBlendMode();
2612     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2613         int alpha = (int)(state->getFillOpacity()*255);
2614         if(blendmode == gfxBlendMultiply && alpha>200)
2615             alpha = 128;
2616         gfxdevice_t ops;
2617         dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2618         gfxdevice_ops_init(&ops, this->device, alpha);
2619         gfxresult_record_replay(grouprecording, &ops);
2620         ops.finish(&ops);
2621     }
2622     grouprecording->destroy(grouprecording);
2623
2624     states[statepos].grouprecording = 0;
2625 }
2626
2627 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2628 {
2629     if(states[statepos].softmask) {
2630         /* shouldn't happen, but *does* happen */
2631         clearSoftMask(state);
2632     }
2633
2634     /* alpha = 1: retrieve mask values from alpha layer
2635        alpha = 0: retrieve mask values from luminance */
2636
2637     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2638             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2639     msg("<verbose> 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     if(!alpha)
2642         infofeature("soft masks");
2643     else
2644         warnfeature("soft masks from alpha channel",0);
2645    
2646     if(states[statepos].olddevice) {
2647         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2648         exit(1);
2649     }
2650     states[statepos].olddevice = this->device;
2651     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2652     gfxdevice_record_init(this->device);
2653
2654     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2655     
2656     states[statepos].softmask = 1;
2657     states[statepos].softmask_alpha = alpha;
2658 }
2659
2660 static inline Guchar div255(int x) {
2661   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2662 }
2663
2664 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2665 {
2666     if(c < min) c = min;
2667     if(c > max) c = max;
2668     return c;
2669 }
2670
2671 void GFXOutputDev::clearSoftMask(GfxState *state)
2672 {
2673     if(!states[statepos].softmask)
2674         return;
2675     states[statepos].softmask = 0;
2676     dbg("clearSoftMask statepos=%d", statepos);
2677     msg("<verbose> clearSoftMask statepos=%d", statepos);
2678     
2679     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2680         msg("<error> Error in softmask/tgroup ordering");
2681         return;
2682     }
2683   
2684     gfxresult_t*mask = states[statepos].softmaskrecording;
2685     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2686     this->device = states[statepos].olddevice;
2687
2688     /* get outline of all objects below the soft mask */
2689     gfxdevice_t uniondev;
2690     gfxdevice_union_init(&uniondev, 0);
2691     gfxresult_record_replay(below, &uniondev);
2692     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2693     uniondev.finish(&uniondev);
2694     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2695     gfxline_free(belowoutline);belowoutline=0;
2696 #if 0 
2697     this->device->startclip(this->device, belowoutline);
2698     gfxresult_record_replay(below, this->device);
2699     gfxresult_record_replay(mask, this->device);
2700     this->device->endclip(this->device);
2701 #endif
2702     
2703     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2704     if(width<=0 || height<=0)
2705         return;
2706
2707     gfxdevice_t belowrender;
2708     gfxdevice_render_init(&belowrender);
2709     if(states[statepos+1].isolated) {
2710         belowrender.setparameter(&belowrender, "fillwhite", "1");
2711     }
2712     belowrender.setparameter(&belowrender, "antialize", "2");
2713     belowrender.startpage(&belowrender, width, height);
2714     gfxresult_record_replay(below, &belowrender);
2715     belowrender.endpage(&belowrender);
2716     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2717     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2718     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2719
2720     gfxdevice_t maskrender;
2721     gfxdevice_render_init(&maskrender);
2722     maskrender.startpage(&maskrender, width, height);
2723     gfxresult_record_replay(mask, &maskrender);
2724     maskrender.endpage(&maskrender);
2725     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2726     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2727
2728     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2729         msg("<fatal> Internal error in mask drawing");
2730         return;
2731     }
2732
2733     int y,x;
2734     for(y=0;y<height;y++) {
2735         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2736         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2737         for(x=0;x<width;x++) {
2738             int alpha;
2739             if(states[statepos].softmask_alpha) {
2740                 alpha = l1->a;
2741             } else {
2742                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2743             }
2744
2745             l2->a = div255(alpha*l2->a);
2746
2747             /* DON'T premultiply alpha- this is done by fillbitmap,
2748                depending on the output device */
2749             //l2->r = div255(alpha*l2->r);
2750             //l2->g = div255(alpha*l2->g);
2751             //l2->b = div255(alpha*l2->b);
2752
2753             l1++;
2754             l2++;
2755         }
2756     }
2757     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2758
2759     gfxmatrix_t matrix;
2760     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2761     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2762
2763     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2764
2765     mask->destroy(mask);
2766     below->destroy(below);
2767     maskresult->destroy(maskresult);
2768     belowresult->destroy(belowresult);
2769     states[statepos].softmaskrecording = 0;
2770 }
2771   
2772 //class MemCheck
2773 //{
2774 //    public: ~MemCheck()
2775 //    {
2776 //        delete globalParams;globalParams=0;
2777 //        Object::memCheck(stderr);
2778 //        gMemReport(stderr);
2779 //    }
2780 //} myMemCheck;
2781