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