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