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