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