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