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