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