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