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