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