added test+fix for transparency group stacking
[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       if(verbose) {
1881           int t;
1882           for(t=0;t<=statepos;t++) {
1883               printf("%08x ", states[t].state);
1884           }
1885           printf("\n");
1886       }
1887       exit(1);
1888   }
1889   states[statepos].state=0;
1890   statepos--;
1891 }
1892  
1893 void GFXOutputDev::updateLineDash(GfxState *state) 
1894 {
1895     if(states[statepos].dashPattern &&
1896        (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1897         free(states[statepos].dashPattern);
1898         states[statepos].dashPattern = 0;
1899     }
1900     double *pattern = 0;
1901     int dashLength;
1902     double dashStart;
1903     state->getLineDash(&pattern, &dashLength, &dashStart);
1904     msg("<debug> updateLineDash, %d dashes", dashLength);
1905     if(!dashLength) {
1906         states[statepos].dashPattern = 0;
1907         states[statepos].dashLength = 0;
1908     } else {
1909         double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1910         memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1911         states[statepos].dashPattern = p;
1912         states[statepos].dashLength = dashLength;
1913         states[statepos].dashStart = dashStart;
1914     }
1915 }
1916   
1917 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1918 {
1919     this->page2page = page2page;
1920     this->num_pages = num_pages;
1921 }
1922
1923 void GFXOutputDev::updateLineWidth(GfxState *state)
1924 {
1925     double width = state->getTransformedLineWidth();
1926 }
1927
1928 void GFXOutputDev::updateLineCap(GfxState *state)
1929 {
1930     int c = state->getLineCap();
1931 }
1932
1933 void GFXOutputDev::updateLineJoin(GfxState *state)
1934 {
1935     int j = state->getLineJoin();
1936 }
1937
1938 void GFXOutputDev::updateFillColor(GfxState *state) 
1939 {
1940     GfxRGB rgb;
1941     double opaq = state->getFillOpacity();
1942     state->getFillRGB(&rgb);
1943 }
1944 void GFXOutputDev::updateFillOpacity(GfxState *state)
1945 {
1946     GfxRGB rgb;
1947     double opaq = state->getFillOpacity();
1948     state->getFillRGB(&rgb);
1949     dbg("update fillopaq %f", opaq);
1950 }
1951 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1952 {
1953     double opaq = state->getFillOpacity();
1954     dbg("update strokeopaq %f", opaq);
1955 }
1956 void GFXOutputDev::updateFillOverprint(GfxState *state)
1957 {
1958     double opaq = state->getFillOverprint();
1959     dbg("update filloverprint %f", opaq);
1960 }
1961 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1962 {
1963     double opaq = state->getStrokeOverprint();
1964     dbg("update strokeoverprint %f", opaq);
1965 }
1966 void GFXOutputDev::updateTransfer(GfxState *state)
1967 {
1968     dbg("update transfer");
1969 }
1970
1971
1972 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1973 {
1974     GfxRGB rgb;
1975     double opaq = state->getStrokeOpacity();
1976     state->getStrokeRGB(&rgb);
1977 }
1978
1979 void GFXOutputDev::updateFont(GfxState *state) 
1980 {
1981     GfxFont* gfxFont = state->getFont();
1982     if (!gfxFont) {
1983         return; 
1984     }  
1985     char*id = getFontID(gfxFont);
1986     msg("<verbose> Updating font to %s", id);
1987     if(gfxFont->getType() == fontType3) {
1988         infofeature("Type3 fonts");
1989         if(!config_extrafontdata) {
1990             return;
1991         }
1992     }
1993     if(!id) {
1994         msg("<error> Internal Error: FontID is null");
1995         return; 
1996     }
1997
1998     this->current_fontinfo = this->info->getFont(id);
1999
2000     if(!this->current_fontinfo) {
2001         msg("<error> Internal Error: no fontinfo for font %s", id);
2002         return;
2003     }
2004     if(!this->current_fontinfo->seen) {
2005         dumpFontInfo("<verbose>", gfxFont);
2006     }
2007
2008     current_gfxfont = this->current_fontinfo->getGfxFont();
2009     device->addfont(device, current_gfxfont);
2010     free(id);
2011
2012     updateFontMatrix(state);
2013 }
2014
2015 #define SQR(x) ((x)*(x))
2016
2017 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2018 {
2019     if((newwidth<1 || newheight<1) ||
2020        (width<=newwidth || height<=newheight))
2021         return 0;
2022     unsigned char*newdata;
2023     int x,y;
2024     newdata= (unsigned char*)malloc(newwidth*newheight);
2025     double fx = ((double)width)/newwidth;
2026     double fy = ((double)height)/newheight;
2027     double px = 0;
2028     int blocksize = (int)(8192/(fx*fy));
2029     int r = 8192*256/palettesize;
2030     for(x=0;x<newwidth;x++) {
2031         double ex = px + fx;
2032         int fromx = (int)px;
2033         int tox = (int)ex;
2034         int xweight1 = (int)((1-(px-fromx))*256);
2035         int xweight2 = (int)((ex-tox)*256);
2036         double py =0;
2037         for(y=0;y<newheight;y++) {
2038             double ey = py + fy;
2039             int fromy = (int)py;
2040             int toy = (int)ey;
2041             int yweight1 = (int)((1-(py-fromy))*256);
2042             int yweight2 = (int)((ey-toy)*256);
2043             int a = 0;
2044             int xx,yy;
2045             if(tox>=width) 
2046                 tox = width-1;
2047             if(toy>=height) 
2048                 toy = height-1;
2049             for(xx=fromx;xx<=tox;xx++)
2050             for(yy=fromy;yy<=toy;yy++) {
2051                 int b = 1-data[width*yy+xx];
2052                 int weight=256;
2053                 if(xx==fromx) weight = (weight*xweight1)/256;
2054                 if(xx==tox) weight = (weight*xweight2)/256;
2055                 if(yy==fromy) weight = (weight*yweight1)/256;
2056                 if(yy==toy) weight = (weight*yweight2)/256;
2057                 a+=b*weight;
2058             }
2059             //if(a) a=(palettesize-1)*r/blocksize;
2060             newdata[y*newwidth+x] = (a*blocksize)/r;
2061             py = ey;
2062         }
2063         px = ex;
2064     }
2065     return newdata;
2066 }
2067
2068 #define IMAGE_TYPE_JPEG 0
2069 #define IMAGE_TYPE_LOSSLESS 1
2070
2071 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2072         double x1,double y1,
2073         double x2,double y2,
2074         double x3,double y3,
2075         double x4,double y4, int type, int multiply)
2076 {
2077     gfxcolor_t*newpic=0;
2078     
2079     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2080     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2081    
2082     gfxline_t p1,p2,p3,p4,p5;
2083     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2084     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2085     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2086     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2087     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2088
2089     {p1.x = (int)(p1.x*20)/20.0;
2090      p1.y = (int)(p1.y*20)/20.0;
2091      p2.x = (int)(p2.x*20)/20.0;
2092      p2.y = (int)(p2.y*20)/20.0;
2093      p3.x = (int)(p3.x*20)/20.0;
2094      p3.y = (int)(p3.y*20)/20.0;
2095      p4.x = (int)(p4.x*20)/20.0;
2096      p4.y = (int)(p4.y*20)/20.0;
2097      p5.x = (int)(p5.x*20)/20.0;
2098      p5.y = (int)(p5.y*20)/20.0;
2099     }
2100
2101     gfxmatrix_t m;
2102     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2103     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2104         
2105     m.tx = p1.x - 0.5*multiply;
2106     m.ty = p1.y - 0.5*multiply;
2107
2108     gfximage_t img;
2109     img.data = (gfxcolor_t*)data;
2110     img.width = sizex;
2111     img.height = sizey;
2112   
2113     if(type == IMAGE_TYPE_JPEG)
2114         /* TODO: pass image_dpi to device instead */
2115         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2116
2117     dump_outline(&p1);
2118     dev->fillbitmap(dev, &p1, &img, &m, 0);
2119 }
2120
2121 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2122         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2123 {
2124     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2125 }
2126
2127 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2128         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2129 {
2130     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2131 }
2132
2133
2134 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2135                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2136                                    GBool inlineImg, int mask, int*maskColors,
2137                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2138 {
2139   /* the code in this function is *old*. It's not pretty, but it works. */
2140
2141   double x1,y1,x2,y2,x3,y3,x4,y4;
2142   ImageStream *imgStr;
2143   Guchar pixBuf[4];
2144   GfxRGB rgb;
2145   int ncomps = 1;
2146   int bits = 1;
2147   unsigned char* maskbitmap = 0;
2148                                  
2149   if(colorMap) {
2150     ncomps = colorMap->getNumPixelComps();
2151     bits = colorMap->getBits();
2152   }
2153       
2154   if(maskStr) {
2155       int x,y;
2156       unsigned char buf[8];
2157       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2158       if(maskColorMap) {
2159           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2160           imgMaskStr->reset();
2161           unsigned char pal[256];
2162           int n = 1 << colorMap->getBits();
2163           int t;
2164           for(t=0;t<n;t++) {
2165               GfxGray gray;
2166               pixBuf[0] = t;
2167               maskColorMap->getGray(pixBuf, &gray);
2168               pal[t] = colToByte(gray);
2169           }
2170           for (y = 0; y < maskHeight; y++) {
2171               for (x = 0; x < maskWidth; x++) {
2172                   imgMaskStr->getPixel(buf);
2173                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2174               }
2175           }
2176           delete imgMaskStr;
2177       } else {
2178           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2179           imgMaskStr->reset();
2180           for (y = 0; y < maskHeight; y++) {
2181               for (x = 0; x < maskWidth; x++) {
2182                   imgMaskStr->getPixel(buf);
2183                   buf[0]^=maskInvert;
2184                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2185               }
2186           }
2187           delete imgMaskStr;
2188       }
2189       maskStr->close();
2190   }
2191   
2192   imgStr = new ImageStream(str, width, ncomps,bits);
2193   imgStr->reset();
2194
2195   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2196   {
2197       msg("<verbose> Ignoring %d by %d image", width, height);
2198       unsigned char buf[8];
2199       int x,y;
2200       for (y = 0; y < height; ++y)
2201       for (x = 0; x < width; ++x) {
2202           imgStr->getPixel(buf);
2203       }
2204       delete imgStr;
2205       if(maskbitmap)
2206           free(maskbitmap);
2207       return;
2208   }
2209
2210   this->transformXY(state, 0, 1, &x1, &y1);
2211   this->transformXY(state, 0, 0, &x2, &y2);
2212   this->transformXY(state, 1, 0, &x3, &y3);
2213   this->transformXY(state, 1, 1, &x4, &y4);
2214
2215   if(type3active) {
2216       /* as type 3 bitmaps are antialized, we need to place them
2217          at integer coordinates, otherwise flash player's antializing
2218          will kick in and make everything blurry */
2219       x1 = (int)(x1);y1 = (int)(y1);
2220       x2 = (int)(x2);y2 = (int)(y2);
2221       x3 = (int)(x3);y3 = (int)(y3);
2222       x4 = (int)(x4);y4 = (int)(y4);
2223   }
2224
2225   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2226       if(!type3active) {
2227           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2228           gfxglobals->pbminfo = 1;
2229       }
2230       if(mask)
2231       msg("<verbose> drawing %d by %d masked picture", width, height);
2232   }
2233   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2234       msg("<notice> File contains jpeg pictures");
2235       gfxglobals->jpeginfo = 1;
2236   }
2237
2238   if(mask) {
2239       unsigned char buf[8];
2240       int x,y;
2241       unsigned char*pic = new unsigned char[width*height];
2242       gfxcolor_t pal[256];
2243       GfxRGB rgb;
2244       state->getFillRGB(&rgb);
2245
2246       memset(pal,255,sizeof(pal));
2247       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2248       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2249       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2250       pal[0].a = 255;              pal[1].a = 0;
2251     
2252       int numpalette = 2;
2253       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2254       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2255       for (y = 0; y < height; ++y)
2256       for (x = 0; x < width; ++x)
2257       {
2258             imgStr->getPixel(buf);
2259             if(invert) 
2260                 buf[0]=1-buf[0];
2261             pic[width*y+x] = buf[0];
2262       }
2263      
2264       if(type3active) {
2265           unsigned char*pic2 = 0;
2266           numpalette = 16;
2267           
2268           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2269
2270           if(!pic2) {
2271             delete[] pic;
2272             delete imgStr;
2273             return;
2274           }
2275           
2276           width = realwidth;
2277           height = realheight;
2278           delete[] pic;
2279           pic = pic2;
2280           
2281           /* make a black/white palette */
2282
2283           float r = 255./(float)(numpalette-1);
2284           int t;
2285           for(t=0;t<numpalette;t++) {
2286               pal[t].r = colToByte(rgb.r);
2287               pal[t].g = colToByte(rgb.g);
2288               pal[t].b = colToByte(rgb.b);
2289               pal[t].a = (unsigned char)(t*r);
2290           }
2291           
2292       }
2293
2294       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2295       for (y = 0; y < height; ++y) {
2296         for (x = 0; x < width; ++x) {
2297           pic2[width*y+x] = pal[pic[y*width+x]];
2298         }
2299       }
2300       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2301       delete[] pic2;
2302       delete[] pic;
2303       delete imgStr;
2304       if(maskbitmap) free(maskbitmap);
2305       return;
2306   }
2307
2308   int x,y;
2309
2310   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2311       gfxcolor_t*pic=new gfxcolor_t[width*height];
2312       for (y = 0; y < height; ++y) {
2313         for (x = 0; x < width; ++x) {
2314           imgStr->getPixel(pixBuf);
2315           colorMap->getRGB(pixBuf, &rgb);
2316           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2317           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2318           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2319           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2320           if(maskbitmap) {
2321               int x1 = x*maskWidth/width;
2322               int y1 = y*maskHeight/height;
2323               int x2 = (x+1)*maskWidth/width;
2324               int y2 = (y+1)*maskHeight/height;
2325               int xx,yy;
2326               unsigned int alpha=0;
2327               unsigned int count=0;
2328               for(xx=x1;xx<x2;xx++)
2329               for(yy=y1;yy<y2;yy++) {
2330                   alpha += maskbitmap[yy*maskWidth+xx];
2331                   count ++;
2332               }
2333               if(count) {
2334                 pic[width*y+x].a = alpha / count;
2335               } else {
2336                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2337               }
2338           }
2339         }
2340       }
2341       if(str->getKind()==strDCT)
2342           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2343       else
2344           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2345       delete[] pic;
2346       delete imgStr;
2347       if(maskbitmap) free(maskbitmap);
2348       return;
2349   } else {
2350       gfxcolor_t*pic=new gfxcolor_t[width*height];
2351       gfxcolor_t pal[256];
2352       int n = 1 << colorMap->getBits();
2353       int t;
2354       for(t=0;t<256;t++) {
2355           pixBuf[0] = t;
2356           colorMap->getRGB(pixBuf, &rgb);
2357
2358           {/*if(maskColors && *maskColors==t) {
2359               msg("<notice> Color %d is transparent", t);
2360               if (imgData->maskColors) {
2361                 *alpha = 0;
2362                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2363                   if (pix[i] < imgData->maskColors[2*i] ||
2364                       pix[i] > imgData->maskColors[2*i+1]) {
2365                     *alpha = 1;
2366                     break;
2367                   }
2368                 }
2369               } else {
2370                 *alpha = 1;
2371               }
2372               if(!*alpha) {
2373                     pal[t].r = 0;
2374                     pal[t].g = 0;
2375                     pal[t].b = 0;
2376                     pal[t].a = 0;
2377               }
2378           } else {*/
2379               pal[t].r = (unsigned char)(colToByte(rgb.r));
2380               pal[t].g = (unsigned char)(colToByte(rgb.g));
2381               pal[t].b = (unsigned char)(colToByte(rgb.b));
2382               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2383           }
2384       }
2385       for (y = 0; y < height; ++y) {
2386         for (x = 0; x < width; ++x) {
2387           imgStr->getPixel(pixBuf);
2388           pic[width*y+x] = pal[pixBuf[0]];
2389         }
2390       }
2391       if(maskbitmap) {
2392           if(maskWidth < width && maskHeight < height) {
2393               for(y = 0; y < height; y++) {
2394                   for (x = 0; x < width; x++) {
2395                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2396                   }
2397               }
2398           } else {
2399               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2400               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2401               double dx = width / maskWidth;
2402               double dy = height / maskHeight;
2403               double yy = 0;
2404               for(y = 0; y < maskHeight; y++) {
2405                   double xx = 0;
2406                   for (x = 0; x < maskWidth; x++) {
2407                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2408                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2409                       xx += dx;
2410                   }
2411                   yy += dy;
2412               }
2413               delete[] pic;
2414               pic = newpic;
2415               width = maskWidth;
2416               height = maskHeight;
2417           }
2418       }
2419       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2420
2421       delete[] pic;
2422       delete imgStr;
2423       if(maskbitmap) free(maskbitmap);
2424       return;
2425   }
2426 }
2427
2428 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2429                                    int width, int height, GBool invert,
2430                                    GBool inlineImg) 
2431 {
2432     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2433     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2434     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2435 }
2436
2437 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2438                          int width, int height, GfxImageColorMap *colorMap,
2439                          int *maskColors, GBool inlineImg)
2440 {
2441     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2442             colorMap?"colorMap":"no colorMap", 
2443             maskColors?"maskColors":"no maskColors",
2444             inlineImg);
2445     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2446             colorMap?"colorMap":"no colorMap", 
2447             maskColors?"maskColors":"no maskColors",
2448             inlineImg);
2449     if(colorMap)
2450         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2451                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2452     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2453 }
2454   
2455 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2456                                int width, int height,
2457                                GfxImageColorMap *colorMap,
2458                                Stream *maskStr, int maskWidth, int maskHeight,
2459                                GBool maskInvert)
2460 {
2461     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2462             colorMap?"colorMap":"no colorMap", 
2463             maskWidth, maskHeight);
2464     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2465             colorMap?"colorMap":"no colorMap", 
2466             maskWidth, maskHeight);
2467     if(colorMap)
2468         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2469                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2470     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2471 }
2472
2473 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2474                                    int width, int height,
2475                                    GfxImageColorMap *colorMap,
2476                                    Stream *maskStr,
2477                                    int maskWidth, int maskHeight,
2478                                    GfxImageColorMap *maskColorMap)
2479 {
2480     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2481             colorMap?"colorMap":"no colorMap", 
2482             maskWidth, maskHeight);
2483     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2484             colorMap?"colorMap":"no colorMap", 
2485             maskWidth, maskHeight);
2486     if(colorMap)
2487         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2488                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2489     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2490 }
2491
2492 void GFXOutputDev::stroke(GfxState *state) 
2493 {
2494     dbg("stroke");
2495
2496     GfxPath * path = state->getPath();
2497     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2498     strokeGfxline(state, line, 0);
2499     gfxline_free(line);
2500 }
2501
2502 void GFXOutputDev::fill(GfxState *state) 
2503 {
2504     gfxcolor_t col = getFillColor(state);
2505     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2506
2507     GfxPath * path = state->getPath();
2508     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2509     if(!config_disable_polygon_conversion) {
2510         gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2511         gfxline_free(line);
2512         line = line2;
2513     }
2514     fillGfxLine(state, line, 0);
2515     gfxline_free(line);
2516 }
2517
2518 void GFXOutputDev::eoFill(GfxState *state) 
2519 {
2520     gfxcolor_t col = getFillColor(state);
2521     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2522
2523     GfxPath * path = state->getPath();
2524     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2525     fillGfxLine(state, line, 1);
2526     gfxline_free(line);
2527 }
2528
2529
2530 static const char* dirseparator()
2531 {
2532 #ifdef WIN32
2533     return "\\";
2534 #else
2535     return "/";
2536 #endif
2537 }
2538
2539 void addGlobalFont(const char*filename)
2540 {
2541     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2542     memset(f, 0, sizeof(fontfile_t));
2543     f->filename = filename;
2544     int len = strlen(filename);
2545     char*r1 = strrchr((char*)filename, '/');
2546     char*r2 = strrchr((char*)filename, '\\');
2547     if(r2>r1)
2548         r1 = r2;
2549     if(r1) {
2550         len = strlen(r1+1);
2551     }
2552     f->len = len;
2553
2554     msg("<verbose> Adding font \"%s\".", filename);
2555     if(global_fonts_next) {
2556         global_fonts_next->next = f;
2557         global_fonts_next = global_fonts_next->next;
2558     } else {
2559         global_fonts_next = global_fonts = f;
2560     }
2561 }
2562
2563 void addGlobalLanguageDir(const char*dir)
2564 {
2565     msg("<notice> Adding %s to language pack directories", dir);
2566
2567     FILE*fi = 0;
2568     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2569     strcpy(config_file, dir);
2570     strcat(config_file, dirseparator());
2571     strcat(config_file, "add-to-xpdfrc");
2572
2573     fi = fopen(config_file, "rb");
2574     if(!fi) {
2575         msg("<error> Could not open %s", config_file);
2576         return;
2577     }
2578     globalParams->parseFile(new GString(config_file), fi);
2579     fclose(fi);
2580 }
2581
2582 void addGlobalFontDir(const char*dirname)
2583 {
2584 #ifdef HAVE_DIRENT_H
2585     DIR*dir = opendir(dirname);
2586     if(!dir) {
2587         msg("<warning> Couldn't open directory %s", dirname);
2588         return;
2589     }
2590     struct dirent*ent;
2591     int fonts = 0;
2592     while(1) {
2593         ent = readdir (dir);
2594         if (!ent) 
2595             break;
2596         int l;
2597         char*name = ent->d_name;
2598         char type = 0;
2599         if(!name) continue;
2600         l=strlen(name);
2601         if(l<4)
2602             continue;
2603         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2604             type=1;
2605         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2606             type=3;
2607         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2608             type=2;
2609         if(type) {
2610             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2611             strcpy(fontname, dirname);
2612             strcat(fontname, dirseparator());
2613             strcat(fontname, name);
2614             addGlobalFont(fontname);
2615             fonts++;
2616         }
2617     }
2618     msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2619     closedir(dir);
2620 #else
2621     msg("<warning> No dirent.h");
2622 #endif
2623 }
2624
2625 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2626                                       GfxColorSpace *blendingColorSpace,
2627                                       GBool isolated, GBool knockout,
2628                                       GBool forSoftMask)
2629 {
2630     const char*colormodename = "";
2631   
2632     if(blendingColorSpace) {
2633         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2634     }
2635     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);
2636     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);
2637     
2638     //states[statepos].createsoftmask |= forSoftMask;
2639     states[statepos].createsoftmask = forSoftMask;
2640     states[statepos].transparencygroup = !forSoftMask;
2641     states[statepos].isolated = isolated;
2642
2643     states[statepos].olddevice = this->device;
2644     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2645     dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2646
2647     gfxdevice_record_init(this->device);
2648     
2649     /*if(!forSoftMask) { ////???
2650         state->setFillOpacity(0.0);
2651     }*/
2652     dbgindent+=2;
2653 }
2654
2655 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2656 {
2657     dbgindent-=2;
2658     gfxdevice_t*r = this->device;
2659
2660     dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2661     
2662     this->device = states[statepos].olddevice;
2663     if(!this->device) {
2664         msg("<error> Invalid state nesting");
2665     }
2666     states[statepos].olddevice = 0;
2667
2668     gfxresult_t*recording = r->finish(r);
2669     
2670     dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2671     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2672
2673     if(states[statepos].createsoftmask) {
2674         states[statepos-1].softmaskrecording = recording;
2675     } else {
2676         states[statepos-1].grouprecording = recording;
2677     }
2678     
2679     states[statepos].createsoftmask = 0;
2680     states[statepos].transparencygroup = 0;
2681     free(r);
2682 }
2683
2684 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2685 {
2686     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2687                                "colordodge","colorburn","hardlight","softlight","difference",
2688                                "exclusion","hue","saturation","color","luminosity"};
2689
2690     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2691     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2692
2693     if(state->getBlendMode() == gfxBlendNormal)
2694         infofeature("transparency groups");
2695     else {
2696         char buffer[80];
2697         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2698         warnfeature(buffer, 0);
2699     }
2700
2701     gfxresult_t*grouprecording = states[statepos].grouprecording;
2702   
2703     int blendmode = state->getBlendMode();
2704     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2705         int alpha = (int)(state->getFillOpacity()*255);
2706         if(blendmode == gfxBlendMultiply && alpha>200)
2707             alpha = 128;
2708         gfxdevice_t ops;
2709         dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2710         gfxdevice_ops_init(&ops, this->device, alpha);
2711         gfxresult_record_replay(grouprecording, &ops);
2712         ops.finish(&ops);
2713     }
2714     grouprecording->destroy(grouprecording);
2715
2716     states[statepos].grouprecording = 0;
2717 }
2718
2719 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2720 {
2721     if(states[statepos].softmask) {
2722         /* shouldn't happen, but *does* happen */
2723         clearSoftMask(state);
2724     }
2725
2726     /* alpha = 1: retrieve mask values from alpha layer
2727        alpha = 0: retrieve mask values from luminance */
2728
2729     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2730             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2731     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2732             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2733     if(!alpha)
2734         infofeature("soft masks");
2735     else
2736         warnfeature("soft masks from alpha channel",0);
2737    
2738     if(states[statepos].olddevice) {
2739         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2740         exit(1);
2741     }
2742     states[statepos].olddevice = this->device;
2743     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2744     gfxdevice_record_init(this->device);
2745
2746     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2747     
2748     states[statepos].softmask = 1;
2749     states[statepos].softmask_alpha = alpha;
2750 }
2751
2752 static inline Guchar div255(int x) {
2753   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2754 }
2755
2756 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2757 {
2758     if(c < min) c = min;
2759     if(c > max) c = max;
2760     return c;
2761 }
2762
2763 void GFXOutputDev::clearSoftMask(GfxState *state)
2764 {
2765     if(!states[statepos].softmask)
2766         return;
2767     states[statepos].softmask = 0;
2768     dbg("clearSoftMask statepos=%d", statepos);
2769     msg("<verbose> clearSoftMask statepos=%d", statepos);
2770     
2771     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2772         msg("<error> Error in softmask/tgroup ordering");
2773         return;
2774     }
2775   
2776     gfxresult_t*mask = states[statepos].softmaskrecording;
2777     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2778     this->device = states[statepos].olddevice;
2779
2780     /* get outline of all objects below the soft mask */
2781     gfxdevice_t uniondev;
2782     gfxdevice_union_init(&uniondev, 0);
2783     gfxresult_record_replay(below, &uniondev);
2784     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2785     uniondev.finish(&uniondev);
2786     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2787     gfxline_free(belowoutline);belowoutline=0;
2788 #if 0 
2789     this->device->startclip(this->device, belowoutline);
2790     gfxresult_record_replay(below, this->device);
2791     gfxresult_record_replay(mask, this->device);
2792     this->device->endclip(this->device);
2793 #endif
2794     
2795     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2796     if(width<=0 || height<=0)
2797         return;
2798
2799     gfxdevice_t belowrender;
2800     gfxdevice_render_init(&belowrender);
2801     if(states[statepos+1].isolated) {
2802         belowrender.setparameter(&belowrender, "fillwhite", "1");
2803     }
2804     belowrender.setparameter(&belowrender, "antialize", "2");
2805     belowrender.startpage(&belowrender, width, height);
2806     gfxresult_record_replay(below, &belowrender);
2807     belowrender.endpage(&belowrender);
2808     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2809     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2810     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2811
2812     gfxdevice_t maskrender;
2813     gfxdevice_render_init(&maskrender);
2814     maskrender.startpage(&maskrender, width, height);
2815     gfxresult_record_replay(mask, &maskrender);
2816     maskrender.endpage(&maskrender);
2817     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2818     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2819
2820     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2821         msg("<fatal> Internal error in mask drawing");
2822         return;
2823     }
2824
2825     int y,x;
2826     for(y=0;y<height;y++) {
2827         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2828         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2829         for(x=0;x<width;x++) {
2830             int alpha;
2831             if(states[statepos].softmask_alpha) {
2832                 alpha = l1->a;
2833             } else {
2834                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2835             }
2836
2837             l2->a = div255(alpha*l2->a);
2838
2839             /* DON'T premultiply alpha- this is done by fillbitmap,
2840                depending on the output device */
2841             //l2->r = div255(alpha*l2->r);
2842             //l2->g = div255(alpha*l2->g);
2843             //l2->b = div255(alpha*l2->b);
2844
2845             l1++;
2846             l2++;
2847         }
2848     }
2849     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2850
2851     gfxmatrix_t matrix;
2852     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2853     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2854
2855     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2856
2857     mask->destroy(mask);
2858     below->destroy(below);
2859     maskresult->destroy(maskresult);
2860     belowresult->destroy(belowresult);
2861     states[statepos].softmaskrecording = 0;
2862 }
2863   
2864 //class MemCheck
2865 //{
2866 //    public: ~MemCheck()
2867 //    {
2868 //        delete globalParams;globalParams=0;
2869 //        Object::memCheck(stderr);
2870 //        gMemReport(stderr);
2871 //    }
2872 //} myMemCheck;
2873