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