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