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