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