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