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