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