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