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