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