transform characters around origin, warn about bad state nestings in transparency...
[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 = 1;
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-originX, y-originY, &m.tx, &m.ty);
1360     m.tx += originX; m.ty += originY;
1361
1362     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1363         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1364     } else {
1365         msg("<debug> Drawing glyph %d as shape", charid);
1366         if(!textmodeinfo) {
1367             msg("<notice> Some texts will be rendered as shape");
1368             textmodeinfo = 1;
1369         }
1370         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1371         gfxline_t*tglyph = gfxline_clone(glyph);
1372         gfxline_transform(tglyph, &m);
1373         if((render&3) != RENDER_INVISIBLE) {
1374             gfxline_t*add = gfxline_clone(tglyph);
1375             current_text_stroke = gfxline_append(current_text_stroke, add);
1376         }
1377         if(render&RENDER_CLIP) {
1378             gfxline_t*add = gfxline_clone(tglyph);
1379             current_text_clip = gfxline_append(current_text_clip, add);
1380             if(!current_text_clip) {
1381                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1382             }
1383         }
1384         gfxline_free(tglyph);
1385     }
1386 }
1387
1388 void GFXOutputDev::endString(GfxState *state) 
1389
1390     int render = state->getRender();
1391     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1392     
1393     if(current_text_stroke) {
1394         /* fillstroke and stroke text rendering objects we can process right
1395            now (as there may be texts of other rendering modes in this
1396            text object)- clipping objects have to wait until endTextObject,
1397            however */
1398         device->setparameter(device, "mark","TXT");
1399         if((render&3) == RENDER_FILL) {
1400             fillGfxLine(state, current_text_stroke);
1401             gfxline_free(current_text_stroke);
1402             current_text_stroke = 0;
1403         } else if((render&3) == RENDER_FILLSTROKE) {
1404             fillGfxLine(state, current_text_stroke);
1405             strokeGfxline(state, current_text_stroke,0);
1406             gfxline_free(current_text_stroke);
1407             current_text_stroke = 0;
1408         } else if((render&3) == RENDER_STROKE) {
1409             strokeGfxline(state, current_text_stroke,0);
1410             gfxline_free(current_text_stroke);
1411             current_text_stroke = 0;
1412         }
1413         device->setparameter(device, "mark","");
1414     }
1415 }    
1416
1417 void GFXOutputDev::endTextObject(GfxState *state)
1418 {
1419     int render = state->getRender();
1420     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1421     
1422     if(current_text_clip) {
1423         device->setparameter(device, "mark","TXT");
1424         clipToGfxLine(state, current_text_clip);
1425         device->setparameter(device, "mark","");
1426         gfxline_free(current_text_clip);
1427         current_text_clip = 0;
1428     }
1429 }
1430
1431 /* the logic seems to be as following:
1432    first, beginType3Char is called, with the charcode and the coordinates.
1433    if this function returns true, it already knew about the char and has now drawn it.
1434    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1435    called with some parameters.
1436    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1437    at the position first passed to beginType3Char). the char ends with endType3Char.
1438
1439    The drawing operations between beginType3Char and endType3Char are somewhat different to
1440    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1441    color determines the color of a font)
1442 */
1443
1444 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1445 {
1446     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1447     type3active = 1;
1448     
1449     if(config_extrafontdata && current_fontinfo) {
1450
1451         gfxmatrix_t m = this->current_font_matrix;
1452         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1453         m.m00*=INTERNAL_FONT_SIZE;
1454         m.m01*=INTERNAL_FONT_SIZE;
1455         m.m10*=INTERNAL_FONT_SIZE;
1456         m.m11*=INTERNAL_FONT_SIZE;
1457
1458         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1459             msg("<error> Invalid charid %d for font", charid);
1460             return gFalse;
1461         }
1462         gfxcolor_t col={0,0,0,0};
1463         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1464         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1465     }
1466
1467
1468     /* the character itself is going to be passed using the draw functions */
1469     return gFalse; /* gTrue= is_in_cache? */
1470 }
1471
1472 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1473 }
1474 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1475 }
1476
1477 void GFXOutputDev::endType3Char(GfxState *state)
1478 {
1479     type3active = 0;
1480     msg("<debug> endType3Char");
1481 }
1482
1483 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1484 {
1485     this->currentpage = pageNum;
1486     double x1,y1,x2,y2;
1487     int rot = doc->getPageRotate(1);
1488     gfxcolor_t white = {255,255,255,255};
1489     gfxcolor_t black = {255,0,0,0};
1490     laststate = state;
1491     gfxline_t clippath[5];
1492
1493     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1494     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1495     Use CropBox, not MediaBox, as page size
1496     */
1497     
1498     /*x1 = crop_x1;
1499     y1 = crop_y1;
1500     x2 = crop_x2;
1501     y2 = crop_y2;*/
1502     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1503     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1504
1505     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1506     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1507
1508     this->clipmovex = -(int)x1;
1509     this->clipmovey = -(int)y1;
1510     
1511     /* apply user clip box */
1512     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1513         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1514         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1515         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1516         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1517         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1518     }
1519
1520     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1521     
1522     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);
1523     if(rot!=0)
1524         msg("<verbose> page is rotated %d degrees", rot);
1525
1526     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1527     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1528     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1529     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1530     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1531     device->startclip(device, clippath); outer_clip_box = 1;
1532     if(!config_transparent) {
1533         device->fill(device, clippath, &white);
1534     }
1535     states[statepos].clipbbox.xmin = x1;
1536     states[statepos].clipbbox.ymin = x1;
1537     states[statepos].clipbbox.xmax = x2;
1538     states[statepos].clipbbox.ymax = y2;
1539     
1540     this->dashPattern = 0;
1541     this->dashLength = 0;
1542     this->dashStart = 0;
1543 }
1544
1545
1546 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1547 {
1548     double x1, y1, x2, y2;
1549     gfxline_t points[5];
1550     int x, y;
1551     
1552     msg("<debug> drawlink");
1553
1554     link->getRect(&x1, &y1, &x2, &y2);
1555     cvtUserToDev(x1, y1, &x, &y);
1556     points[0].type = gfx_moveTo;
1557     points[0].x = points[4].x = x + user_movex + clipmovex;
1558     points[0].y = points[4].y = y + user_movey + clipmovey;
1559     points[0].next = &points[1];
1560     cvtUserToDev(x2, y1, &x, &y);
1561     points[1].type = gfx_lineTo;
1562     points[1].x = x + user_movex + clipmovex;
1563     points[1].y = y + user_movey + clipmovey;
1564     points[1].next = &points[2];
1565     cvtUserToDev(x2, y2, &x, &y);
1566     points[2].type = gfx_lineTo;
1567     points[2].x = x + user_movex + clipmovex;
1568     points[2].y = y + user_movey + clipmovey;
1569     points[2].next = &points[3];
1570     cvtUserToDev(x1, y2, &x, &y);
1571     points[3].type = gfx_lineTo;
1572     points[3].x = x + user_movex + clipmovex;
1573     points[3].y = y + user_movey + clipmovey;
1574     points[3].next = &points[4];
1575     cvtUserToDev(x1, y1, &x, &y);
1576     points[4].type = gfx_lineTo;
1577     points[4].x = x + user_movex + clipmovex;
1578     points[4].y = y + user_movey + clipmovey;
1579     points[4].next = 0;
1580     
1581     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1582             points[0].x, points[0].y,
1583             points[1].x, points[1].y,
1584             points[2].x, points[2].y,
1585             points[3].x, points[3].y); 
1586     
1587     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1588         dump_outline(points);
1589     }
1590
1591     LinkAction*action=link->getAction();
1592     char buf[128];
1593     char*s = 0;
1594     const char*type = "-?-";
1595     char*named = 0;
1596     int page = -1;
1597     msg("<trace> drawlink action=%d", action->getKind());
1598     switch(action->getKind())
1599     {
1600         case actionGoTo: {
1601             type = "GoTo";
1602             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1603             LinkDest *dest=NULL;
1604             if (ha->getDest()==NULL) 
1605                 dest=catalog->findDest(ha->getNamedDest());
1606             else dest=ha->getDest();
1607             if (dest){ 
1608               if (dest->isPageRef()){
1609                 Ref pageref=dest->getPageRef();
1610                 page=catalog->findPage(pageref.num,pageref.gen);
1611               }
1612               else  page=dest->getPageNum();
1613               sprintf(buf, "%d", page);
1614               s = strdup(buf);
1615             }
1616         }
1617         break;
1618         case actionGoToR: {
1619             type = "GoToR";
1620             LinkGoToR*l = (LinkGoToR*)action;
1621             GString*g = l->getFileName();
1622             if(g)
1623              s = strdup(g->getCString());
1624             if(!s) {
1625                 /* if the GoToR link has no filename, then
1626                    try to find a refernce in the *local*
1627                    file */
1628                 GString*g = l->getNamedDest();
1629                 if(g)
1630                  s = strdup(g->getCString());
1631             }
1632         }
1633         break;
1634         case actionNamed: {
1635             type = "Named";
1636             LinkNamed*l = (LinkNamed*)action;
1637             GString*name = l->getName();
1638             if(name) {
1639                 s = strdup(name->lowerCase()->getCString());
1640                 named = name->getCString();
1641                 if(!strchr(s,':')) 
1642                 {
1643                     if(strstr(s, "next") || strstr(s, "forward"))
1644                     {
1645                         page = currentpage + 1;
1646                     }
1647                     else if(strstr(s, "prev") || strstr(s, "back"))
1648                     {
1649                         page = currentpage - 1;
1650                     }
1651                     else if(strstr(s, "last") || strstr(s, "end"))
1652                     {
1653                         if(pages && pagepos>0)
1654                             page = pages[pagepos-1];
1655                     }
1656                     else if(strstr(s, "first") || strstr(s, "top"))
1657                     {
1658                         page = 1;
1659                     }
1660                 }
1661             }
1662         }
1663         break;
1664         case actionLaunch: {
1665             type = "Launch";
1666             LinkLaunch*l = (LinkLaunch*)action;
1667             GString * str = new GString(l->getFileName());
1668             GString * params = l->getParams();
1669             if(params)
1670                 str->append(params);
1671             s = strdup(str->getCString());
1672             delete str;
1673         }
1674         break;
1675         case actionURI: {
1676             char*url = 0;
1677             type = "URI";
1678             LinkURI*l = (LinkURI*)action;
1679             GString*g = l->getURI();
1680             if(g) {
1681              url = g->getCString();
1682              s = strdup(url);
1683             }
1684         }
1685         break;
1686         case actionUnknown: {
1687             type = "Unknown";
1688             LinkUnknown*l = (LinkUnknown*)action;
1689             s = strdup("");
1690         }
1691         break;
1692         default: {
1693             msg("<error> Unknown link type!");
1694             break;
1695         }
1696     }
1697
1698     if(!s) s = strdup("-?-");
1699     
1700     msg("<trace> drawlink s=%s", s);
1701
1702     if(!linkinfo && (page || s))
1703     {
1704         msg("<notice> File contains links");
1705         linkinfo = 1;
1706     }
1707     
1708     if(page>0)
1709     {
1710         int t;
1711         int lpage = -1;
1712         for(t=1;t<=pagepos;t++) {
1713             if(pages[t]==page) {
1714                 lpage = t;
1715                 break;
1716             }
1717         }
1718         if(lpage<0) {
1719             lpage = page;
1720         }
1721         char buf[80];
1722         sprintf(buf, "page%d", lpage);
1723         device->drawlink(device, points, buf);
1724     }
1725     else if(s)
1726     {
1727         device->drawlink(device, points, s);
1728     }
1729
1730     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1731     free(s);s=0;
1732 }
1733
1734 void GFXOutputDev::saveState(GfxState *state) {
1735     dbg("saveState %08x", state); dbgindent+=2;
1736
1737     msg("<trace> saveState %08x", state);
1738     updateAll(state);
1739     if(statepos>=64) {
1740       msg("<fatal> Too many nested states in pdf.");
1741       exit(1);
1742     }
1743     statepos ++;
1744     states[statepos].state = state;
1745     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1746     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1747     states[statepos].clipping = 0;
1748     states[statepos].olddevice = 0;
1749     states[statepos].clipbbox = states[statepos-1].clipbbox;
1750 };
1751
1752 void GFXOutputDev::restoreState(GfxState *state) {
1753   dbgindent-=2; dbg("restoreState %08x", state);
1754
1755   if(statepos==0) {
1756       msg("<fatal> Invalid restoreState");
1757       exit(1);
1758   }
1759   msg("<trace> restoreState %08x%s%s", state,
1760                                   states[statepos].softmask?" (end softmask)":"",
1761                                   states[statepos].clipping?" (end clipping)":"");
1762   if(states[statepos].softmask) {
1763       clearSoftMask(state);
1764   }
1765   updateAll(state);
1766   
1767   while(states[statepos].clipping) {
1768       device->endclip(device);
1769       states[statepos].clipping--;
1770   }
1771   if(states[statepos].state!=state) {
1772       msg("<fatal> bad state nesting");
1773       exit(1);
1774   }
1775   states[statepos].state=0;
1776   statepos--;
1777 }
1778  
1779 void GFXOutputDev::updateLineDash(GfxState *state) 
1780 {
1781     state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1782     msg("<debug> updateLineDash, %d dashes", this->dashLength);
1783     if(!this->dashLength) {
1784         this->dashPattern = 0;
1785     }
1786 }
1787
1788 void GFXOutputDev::updateLineWidth(GfxState *state)
1789 {
1790     double width = state->getTransformedLineWidth();
1791 }
1792
1793 void GFXOutputDev::updateLineCap(GfxState *state)
1794 {
1795     int c = state->getLineCap();
1796 }
1797
1798 void GFXOutputDev::updateLineJoin(GfxState *state)
1799 {
1800     int j = state->getLineJoin();
1801 }
1802
1803 void GFXOutputDev::updateFillColor(GfxState *state) 
1804 {
1805     GfxRGB rgb;
1806     double opaq = state->getFillOpacity();
1807     state->getFillRGB(&rgb);
1808 }
1809 void GFXOutputDev::updateFillOpacity(GfxState *state)
1810 {
1811     GfxRGB rgb;
1812     double opaq = state->getFillOpacity();
1813     state->getFillRGB(&rgb);
1814     dbg("update fillopaq %f", opaq);
1815 }
1816 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1817 {
1818     double opaq = state->getFillOpacity();
1819     dbg("update strokeopaq %f", opaq);
1820 }
1821 void GFXOutputDev::updateFillOverprint(GfxState *state)
1822 {
1823     double opaq = state->getFillOverprint();
1824     dbg("update filloverprint %f", opaq);
1825 }
1826 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1827 {
1828     double opaq = state->getStrokeOverprint();
1829     dbg("update strokeoverprint %f", opaq);
1830 }
1831 void GFXOutputDev::updateTransfer(GfxState *state)
1832 {
1833     dbg("update transfer");
1834 }
1835
1836
1837 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1838 {
1839     GfxRGB rgb;
1840     double opaq = state->getStrokeOpacity();
1841     state->getStrokeRGB(&rgb);
1842 }
1843
1844
1845 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1846 {
1847     gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1848     memset(font, 0, sizeof(gfxfont_t));
1849
1850     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1851     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1852     font->id = 0;
1853     int t;
1854     
1855     double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1856     double scale = 1;
1857     //printf("%d glyphs\n", font->num_glyphs);
1858     font->num_glyphs = 0;
1859     for(t=0;t<src->num_glyphs;t++) {
1860         if(src->glyphs[t]) {
1861             SplashPath*path = src->glyphs[t]->path;
1862             int len = path?path->getLength():0;
1863             //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1864             gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1865             src->glyphs[t]->glyphid = font->num_glyphs;
1866             glyph->unicode = src->glyphs[t]->unicode;
1867             if(glyph->unicode >= font->max_unicode)
1868                 font->max_unicode = glyph->unicode+1;
1869             gfxdrawer_t drawer;
1870             gfxdrawer_target_gfxline(&drawer);
1871             int s;
1872             int count = 0;
1873             double xmax = 0;
1874             for(s=0;s<len;s++) {
1875                 Guchar f;
1876                 double x, y;
1877                 path->getPoint(s, &x, &y, &f);
1878                 if(!s || x > xmax)
1879                     xmax = x;
1880                 if(f&splashPathFirst) {
1881                     drawer.moveTo(&drawer, x*scale, y*scale);
1882                 }
1883                 if(f&splashPathCurve) {
1884                     double x2,y2;
1885                     path->getPoint(++s, &x2, &y2, &f);
1886                     if(f&splashPathCurve) {
1887                         double x3,y3;
1888                         path->getPoint(++s, &x3, &y3, &f);
1889                         gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1890                     } else {
1891                         drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1892                     }
1893                 } else {
1894                     drawer.lineTo(&drawer, x*scale, y*scale);
1895                 }
1896              //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1897              //                           (f&splashPathFirst)?"first":"",
1898              //                           (f&splashPathLast)?"last":"");
1899             }
1900             glyph->line = (gfxline_t*)drawer.result(&drawer);
1901             glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1902             font->num_glyphs++;
1903         }
1904     }
1905     font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1906     memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1907     for(t=0;t<font->num_glyphs;t++) {
1908         if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1909             font->unicode2glyph[font->glyphs[t].unicode] = t;
1910         }
1911
1912     }
1913     msg("<trace> %d glyphs.", t, font->num_glyphs);
1914     return font;
1915 }
1916
1917 void GFXOutputDev::updateFont(GfxState *state) 
1918 {
1919     GfxFont* gfxFont = state->getFont();
1920     if (!gfxFont) {
1921         return; 
1922     }  
1923     char*id = getFontID(gfxFont);
1924     msg("<verbose> Updating font to %s", id);
1925     if(gfxFont->getType() == fontType3) {
1926         infofeature("Type3 fonts");
1927         if(!config_extrafontdata) {
1928             return;
1929         }
1930     }
1931     if(!id) {
1932         msg("<error> Internal Error: FontID is null");
1933         return; 
1934     }
1935
1936     this->current_fontinfo = this->info->getFont(id);
1937     if(!this->current_fontinfo) {
1938         msg("<error> Internal Error: no fontinfo for font %s", id);
1939         return;
1940     }
1941     if(!this->current_fontinfo->seen) {
1942         dumpFontInfo("<verbose>", gfxFont);
1943     }
1944     
1945     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1946     if(!font) {
1947         font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1948         font->id = strdup(id);
1949         this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1950     }
1951     device->addfont(device, font);
1952
1953     current_gfxfont = font;
1954     free(id);
1955
1956     updateFontMatrix(state);
1957 }
1958
1959 #define SQR(x) ((x)*(x))
1960
1961 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1962 {
1963     if((newwidth<2 || newheight<2) ||
1964        (width<=newwidth || height<=newheight))
1965         return 0;
1966     unsigned char*newdata;
1967     int x,y;
1968     newdata= (unsigned char*)malloc(newwidth*newheight);
1969     double fx = ((double)width)/newwidth;
1970     double fy = ((double)height)/newheight;
1971     double px = 0;
1972     int blocksize = (int)(8192/(fx*fy));
1973     int r = 8192*256/palettesize;
1974     for(x=0;x<newwidth;x++) {
1975         double ex = px + fx;
1976         int fromx = (int)px;
1977         int tox = (int)ex;
1978         int xweight1 = (int)((1-(px-fromx))*256);
1979         int xweight2 = (int)((ex-tox)*256);
1980         double py =0;
1981         for(y=0;y<newheight;y++) {
1982             double ey = py + fy;
1983             int fromy = (int)py;
1984             int toy = (int)ey;
1985             int yweight1 = (int)((1-(py-fromy))*256);
1986             int yweight2 = (int)((ey-toy)*256);
1987             int a = 0;
1988             int xx,yy;
1989             if(tox>=width) 
1990                 tox = width-1;
1991             if(toy>=height) 
1992                 toy = height-1;
1993             for(xx=fromx;xx<=tox;xx++)
1994             for(yy=fromy;yy<=toy;yy++) {
1995                 int b = 1-data[width*yy+xx];
1996                 int weight=256;
1997                 if(xx==fromx) weight = (weight*xweight1)/256;
1998                 if(xx==tox) weight = (weight*xweight2)/256;
1999                 if(yy==fromy) weight = (weight*yweight1)/256;
2000                 if(yy==toy) weight = (weight*yweight2)/256;
2001                 a+=b*weight;
2002             }
2003             //if(a) a=(palettesize-1)*r/blocksize;
2004             newdata[y*newwidth+x] = (a*blocksize)/r;
2005             py = ey;
2006         }
2007         px = ex;
2008     }
2009     return newdata;
2010 }
2011
2012 #define IMAGE_TYPE_JPEG 0
2013 #define IMAGE_TYPE_LOSSLESS 1
2014
2015 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2016         double x1,double y1,
2017         double x2,double y2,
2018         double x3,double y3,
2019         double x4,double y4, int type)
2020 {
2021     gfxcolor_t*newpic=0;
2022     
2023     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2024     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2025    
2026     gfxline_t p1,p2,p3,p4,p5;
2027     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2028     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2029     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2030     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2031     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2032
2033     {p1.x = (int)(p1.x*20)/20.0;
2034      p1.y = (int)(p1.y*20)/20.0;
2035      p2.x = (int)(p2.x*20)/20.0;
2036      p2.y = (int)(p2.y*20)/20.0;
2037      p3.x = (int)(p3.x*20)/20.0;
2038      p3.y = (int)(p3.y*20)/20.0;
2039      p4.x = (int)(p4.x*20)/20.0;
2040      p4.y = (int)(p4.y*20)/20.0;
2041      p5.x = (int)(p5.x*20)/20.0;
2042      p5.y = (int)(p5.y*20)/20.0;
2043     }
2044     
2045     gfxmatrix_t m;
2046     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2047     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2048     m.tx = p1.x - 0.5;
2049     m.ty = p1.y - 0.5;
2050
2051     gfximage_t img;
2052     img.data = (gfxcolor_t*)data;
2053     img.width = sizex;
2054     img.height = sizey;
2055   
2056     if(type == IMAGE_TYPE_JPEG)
2057         /* TODO: pass image_dpi to device instead */
2058         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2059
2060     dump_outline(&p1);
2061     dev->fillbitmap(dev, &p1, &img, &m, 0);
2062 }
2063
2064 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2065         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2066 {
2067     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2068 }
2069
2070 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2071         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2072 {
2073     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2074 }
2075
2076
2077 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2078                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2079                                    GBool inlineImg, int mask, int*maskColors,
2080                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2081 {
2082   double x1,y1,x2,y2,x3,y3,x4,y4;
2083   ImageStream *imgStr;
2084   Guchar pixBuf[4];
2085   GfxRGB rgb;
2086   int ncomps = 1;
2087   int bits = 1;
2088   unsigned char* maskbitmap = 0;
2089                                  
2090   if(colorMap) {
2091     ncomps = colorMap->getNumPixelComps();
2092     bits = colorMap->getBits();
2093   }
2094       
2095   if(maskStr) {
2096       int x,y;
2097       unsigned char buf[8];
2098       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2099       if(maskColorMap) {
2100           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2101           imgMaskStr->reset();
2102           unsigned char pal[256];
2103           int n = 1 << colorMap->getBits();
2104           int t;
2105           for(t=0;t<n;t++) {
2106               GfxGray gray;
2107               pixBuf[0] = t;
2108               maskColorMap->getGray(pixBuf, &gray);
2109               pal[t] = colToByte(gray);
2110           }
2111           for (y = 0; y < maskHeight; y++) {
2112               for (x = 0; x < maskWidth; x++) {
2113                   imgMaskStr->getPixel(buf);
2114                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2115               }
2116           }
2117           delete imgMaskStr;
2118       } else {
2119           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2120           imgMaskStr->reset();
2121           for (y = 0; y < maskHeight; y++) {
2122               for (x = 0; x < maskWidth; x++) {
2123                   imgMaskStr->getPixel(buf);
2124                   buf[0]^=maskInvert;
2125                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2126               }
2127           }
2128           delete imgMaskStr;
2129       }
2130       maskStr->close();
2131   }
2132   
2133   imgStr = new ImageStream(str, width, ncomps,bits);
2134   imgStr->reset();
2135
2136   if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2137   {
2138       msg("<verbose> Ignoring %d by %d image", width, height);
2139       unsigned char buf[8];
2140       int x,y;
2141       for (y = 0; y < height; ++y)
2142       for (x = 0; x < width; ++x) {
2143           imgStr->getPixel(buf);
2144       }
2145       delete imgStr;
2146       if(maskbitmap)
2147           free(maskbitmap);
2148       return;
2149   }
2150
2151   this->transformXY(state, 0, 1, &x1, &y1);
2152   this->transformXY(state, 0, 0, &x2, &y2);
2153   this->transformXY(state, 1, 0, &x3, &y3);
2154   this->transformXY(state, 1, 1, &x4, &y4);
2155
2156   if(type3active) {
2157       /* as type 3 bitmaps are antialized, we need to place them
2158          at integer coordinates, otherwise flash player's antializing
2159          will kick in and make everything blurry */
2160       x1 = (int)(x1);y1 = (int)(y1);
2161       x2 = (int)(x2);y2 = (int)(y2);
2162       x3 = (int)(x3);y3 = (int)(y3);
2163       x4 = (int)(x4);y4 = (int)(y4);
2164   }
2165
2166   if(!pbminfo && !(str->getKind()==strDCT)) {
2167       if(!type3active) {
2168           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2169           pbminfo = 1;
2170       }
2171       if(mask)
2172       msg("<verbose> drawing %d by %d masked picture", width, height);
2173   }
2174   if(!jpeginfo && (str->getKind()==strDCT)) {
2175       msg("<notice> File contains jpeg pictures");
2176       jpeginfo = 1;
2177   }
2178
2179   if(mask) {
2180       unsigned char buf[8];
2181       int x,y;
2182       unsigned char*pic = new unsigned char[width*height];
2183       gfxcolor_t pal[256];
2184       GfxRGB rgb;
2185       state->getFillRGB(&rgb);
2186
2187       memset(pal,255,sizeof(pal));
2188       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2189       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2190       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2191       pal[0].a = 255;              pal[1].a = 0;
2192     
2193       int numpalette = 2;
2194       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2195       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2196       for (y = 0; y < height; ++y)
2197       for (x = 0; x < width; ++x)
2198       {
2199             imgStr->getPixel(buf);
2200             if(invert) 
2201                 buf[0]=1-buf[0];
2202             pic[width*y+x] = buf[0];
2203       }
2204       
2205       /* the size of the drawn image is added to the identifier
2206          as the same image may require different bitmaps if displayed
2207          at different sizes (due to antialiasing): */
2208       int found = -1;
2209       if(type3active) {
2210           unsigned char*pic2 = 0;
2211           numpalette = 16;
2212           
2213           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2214
2215           if(!pic2) {
2216             delete[] pic;
2217             delete imgStr;
2218             return;
2219           }
2220           
2221           width = realwidth;
2222           height = realheight;
2223           delete[] pic;
2224           pic = pic2;
2225           
2226           /* make a black/white palette */
2227
2228           float r = 255./(float)(numpalette-1);
2229           int t;
2230           for(t=0;t<numpalette;t++) {
2231               pal[t].r = colToByte(rgb.r);
2232               pal[t].g = colToByte(rgb.g);
2233               pal[t].b = colToByte(rgb.b);
2234               pal[t].a = (unsigned char)(t*r);
2235           }
2236           
2237       }
2238
2239       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2240       for (y = 0; y < height; ++y) {
2241         for (x = 0; x < width; ++x) {
2242           pic2[width*y+x] = pal[pic[y*width+x]];
2243         }
2244       }
2245       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2246       delete[] pic2;
2247       delete[] pic;
2248       delete imgStr;
2249       if(maskbitmap) free(maskbitmap);
2250       return;
2251   }
2252
2253   int x,y;
2254
2255   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2256       gfxcolor_t*pic=new gfxcolor_t[width*height];
2257       for (y = 0; y < height; ++y) {
2258         for (x = 0; x < width; ++x) {
2259           imgStr->getPixel(pixBuf);
2260           colorMap->getRGB(pixBuf, &rgb);
2261           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2262           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2263           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2264           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2265           if(maskbitmap) {
2266               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2267           }
2268         }
2269       }
2270       if(str->getKind()==strDCT)
2271           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2272       else
2273           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2274       delete[] pic;
2275       delete imgStr;
2276       if(maskbitmap) free(maskbitmap);
2277       return;
2278   } else {
2279       gfxcolor_t*pic=new gfxcolor_t[width*height];
2280       gfxcolor_t pal[256];
2281       int n = 1 << colorMap->getBits();
2282       int t;
2283       for(t=0;t<256;t++) {
2284           pixBuf[0] = t;
2285           colorMap->getRGB(pixBuf, &rgb);
2286
2287           {/*if(maskColors && *maskColors==t) {
2288               msg("<notice> Color %d is transparent", t);
2289               if (imgData->maskColors) {
2290                 *alpha = 0;
2291                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2292                   if (pix[i] < imgData->maskColors[2*i] ||
2293                       pix[i] > imgData->maskColors[2*i+1]) {
2294                     *alpha = 1;
2295                     break;
2296                   }
2297                 }
2298               } else {
2299                 *alpha = 1;
2300               }
2301               if(!*alpha) {
2302                     pal[t].r = 0;
2303                     pal[t].g = 0;
2304                     pal[t].b = 0;
2305                     pal[t].a = 0;
2306               }
2307           } else {*/
2308               pal[t].r = (unsigned char)(colToByte(rgb.r));
2309               pal[t].g = (unsigned char)(colToByte(rgb.g));
2310               pal[t].b = (unsigned char)(colToByte(rgb.b));
2311               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2312           }
2313       }
2314       for (y = 0; y < height; ++y) {
2315         for (x = 0; x < width; ++x) {
2316           imgStr->getPixel(pixBuf);
2317           pic[width*y+x] = pal[pixBuf[0]];
2318         }
2319       }
2320       if(maskbitmap) {
2321           if(maskWidth < width && maskHeight < height) {
2322               for(y = 0; y < height; y++) {
2323                   for (x = 0; x < width; x++) {
2324                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2325                   }
2326               }
2327           } else {
2328               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2329               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2330               double dx = width / maskWidth;
2331               double dy = height / maskHeight;
2332               double yy = 0;
2333               for(y = 0; y < maskHeight; y++) {
2334                   double xx = 0;
2335                   for (x = 0; x < maskWidth; x++) {
2336                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2337                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2338                       xx += dx;
2339                   }
2340                   yy += dy;
2341               }
2342               delete[] pic;
2343               pic = newpic;
2344               width = maskWidth;
2345               height = maskHeight;
2346           }
2347       }
2348       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2349
2350       delete[] pic;
2351       delete imgStr;
2352       if(maskbitmap) free(maskbitmap);
2353       return;
2354   }
2355 }
2356
2357 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2358                                    int width, int height, GBool invert,
2359                                    GBool inlineImg) 
2360 {
2361     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2362     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2363     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2364 }
2365
2366 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2367                          int width, int height, GfxImageColorMap *colorMap,
2368                          int *maskColors, GBool inlineImg)
2369 {
2370     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2371             colorMap?"colorMap":"no colorMap", 
2372             maskColors?"maskColors":"no maskColors",
2373             inlineImg);
2374     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2375             colorMap?"colorMap":"no colorMap", 
2376             maskColors?"maskColors":"no maskColors",
2377             inlineImg);
2378     if(colorMap)
2379         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2380                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2381     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2382 }
2383   
2384 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2385                                int width, int height,
2386                                GfxImageColorMap *colorMap,
2387                                Stream *maskStr, int maskWidth, int maskHeight,
2388                                GBool maskInvert)
2389 {
2390     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2391             colorMap?"colorMap":"no colorMap", 
2392             maskWidth, maskHeight);
2393     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2394             colorMap?"colorMap":"no colorMap", 
2395             maskWidth, maskHeight);
2396     if(colorMap)
2397         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2398                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2399     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2400 }
2401
2402 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2403                                    int width, int height,
2404                                    GfxImageColorMap *colorMap,
2405                                    Stream *maskStr,
2406                                    int maskWidth, int maskHeight,
2407                                    GfxImageColorMap *maskColorMap)
2408 {
2409     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2410             colorMap?"colorMap":"no colorMap", 
2411             maskWidth, maskHeight);
2412     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2413             colorMap?"colorMap":"no colorMap", 
2414             maskWidth, maskHeight);
2415     if(colorMap)
2416         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2417                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2418     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2419 }
2420
2421 void GFXOutputDev::stroke(GfxState *state) 
2422 {
2423     dbg("stroke");
2424
2425     GfxPath * path = state->getPath();
2426     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2427     strokeGfxline(state, line, 0);
2428     gfxline_free(line);
2429 }
2430
2431 void GFXOutputDev::fill(GfxState *state) 
2432 {
2433     gfxcolor_t col = getFillColor(state);
2434     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2435
2436     GfxPath * path = state->getPath();
2437     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2438     if(config_optimize_polygons) {
2439         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2440         gfxline_free(line);
2441         line = line2;
2442     }
2443     fillGfxLine(state, line);
2444     gfxline_free(line);
2445 }
2446
2447 void GFXOutputDev::eoFill(GfxState *state) 
2448 {
2449     gfxcolor_t col = getFillColor(state);
2450     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2451
2452     GfxPath * path = state->getPath();
2453     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2454     fillGfxLine(state, line);
2455     gfxline_free(line);
2456 }
2457
2458
2459 static const char* dirseparator()
2460 {
2461 #ifdef WIN32
2462     return "\\";
2463 #else
2464     return "/";
2465 #endif
2466 }
2467
2468 void addGlobalFont(const char*filename)
2469 {
2470     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2471     memset(f, 0, sizeof(fontfile_t));
2472     f->filename = filename;
2473     int len = strlen(filename);
2474     char*r1 = strrchr(filename, '/');
2475     char*r2 = strrchr(filename, '\\');
2476     if(r2>r1)
2477         r1 = r2;
2478     if(r1) {
2479         len = strlen(r1+1);
2480     }
2481     f->len = len;
2482
2483     msg("<notice> Adding font \"%s\".", filename);
2484     if(global_fonts_next) {
2485         global_fonts_next->next = f;
2486         global_fonts_next = global_fonts_next->next;
2487     } else {
2488         global_fonts_next = global_fonts = f;
2489     }
2490 }
2491
2492 void addGlobalLanguageDir(const char*dir)
2493 {
2494     msg("<notice> Adding %s to language pack directories", dir);
2495
2496     FILE*fi = 0;
2497     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2498     strcpy(config_file, dir);
2499     strcat(config_file, dirseparator());
2500     strcat(config_file, "add-to-xpdfrc");
2501
2502     fi = fopen(config_file, "rb");
2503     if(!fi) {
2504         msg("<error> Could not open %s", config_file);
2505         return;
2506     }
2507     globalParams->parseFile(new GString(config_file), fi);
2508     fclose(fi);
2509 }
2510
2511 void addGlobalFontDir(const char*dirname)
2512 {
2513 #ifdef HAVE_DIRENT_H
2514     msg("<notice> Adding %s to font directories", dirname);
2515     lastfontdir = strdup(dirname);
2516     DIR*dir = opendir(dirname);
2517     if(!dir) {
2518         msg("<warning> Couldn't open directory %s", dirname);
2519         return;
2520     }
2521     struct dirent*ent;
2522     while(1) {
2523         ent = readdir (dir);
2524         if (!ent) 
2525             break;
2526         int l;
2527         char*name = ent->d_name;
2528         char type = 0;
2529         if(!name) continue;
2530         l=strlen(name);
2531         if(l<4)
2532             continue;
2533         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2534             type=1;
2535         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2536             type=3;
2537         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2538             type=2;
2539         if(type) {
2540             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2541             strcpy(fontname, dirname);
2542             strcat(fontname, dirseparator());
2543             strcat(fontname, name);
2544             addGlobalFont(fontname);
2545         }
2546     }
2547     closedir(dir);
2548 #else
2549     msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2550 #endif
2551 }
2552
2553 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2554 {
2555     if(pdfpage < 0)
2556         return;
2557
2558     if(!this->pages) {
2559         this->pagebuflen = 1024;
2560         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2561         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2562     } else {
2563         while(pdfpage >= this->pagebuflen)
2564         {
2565             int oldlen = this->pagebuflen;
2566             this->pagebuflen+=1024;
2567             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2568             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2569         }
2570     }
2571     this->pages[pdfpage] = outputpage;
2572     if(pdfpage>this->pagepos)
2573         this->pagepos = pdfpage;
2574 }
2575
2576 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2577                                       GfxColorSpace *blendingColorSpace,
2578                                       GBool isolated, GBool knockout,
2579                                       GBool forSoftMask)
2580 {
2581     const char*colormodename = "";
2582   
2583     if(blendingColorSpace) {
2584         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2585     }
2586     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);
2587     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);
2588     
2589     //states[statepos].createsoftmask |= forSoftMask;
2590     states[statepos].createsoftmask = forSoftMask;
2591     states[statepos].transparencygroup = !forSoftMask;
2592     states[statepos].isolated = isolated;
2593
2594     states[statepos].olddevice = this->device;
2595     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2596     dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2597
2598     gfxdevice_record_init(this->device);
2599     
2600     /*if(!forSoftMask) { ////???
2601         state->setFillOpacity(0.0);
2602     }*/
2603     dbgindent+=2;
2604 }
2605
2606 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2607 {
2608     dbgindent-=2;
2609     gfxdevice_t*r = this->device;
2610
2611     dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2612     
2613     this->device = states[statepos].olddevice;
2614     if(!this->device) {
2615         msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2616         /* if these errors occur more often, we should build a seperate
2617            transparency group stack, like xpdf/SplashOutputDev.cc does */
2618         restoreState(state);
2619         this->device = states[statepos].olddevice;
2620     }
2621     states[statepos].olddevice = 0;
2622
2623     gfxresult_t*recording = r->finish(r);
2624     
2625     dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2626     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2627
2628     if(states[statepos].createsoftmask) {
2629         states[statepos-1].softmaskrecording = recording;
2630     } else {
2631         states[statepos-1].grouprecording = recording;
2632     }
2633     
2634     states[statepos].createsoftmask = 0;
2635     states[statepos].transparencygroup = 0;
2636     free(r);
2637 }
2638
2639 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2640 {
2641     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2642                                "colordodge","colorburn","hardlight","softlight","difference",
2643                                "exclusion","hue","saturation","color","luminosity"};
2644
2645     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2646     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2647
2648     if(state->getBlendMode() == gfxBlendNormal)
2649         infofeature("transparency groups");
2650     else {
2651         char buffer[80];
2652         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2653         warnfeature(buffer, 0);
2654     }
2655
2656     gfxresult_t*grouprecording = states[statepos].grouprecording;
2657   
2658     int blendmode = state->getBlendMode();
2659     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2660         int alpha = (int)(state->getFillOpacity()*255);
2661         if(blendmode == gfxBlendMultiply && alpha>200)
2662             alpha = 128;
2663         gfxdevice_t ops;
2664         dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2665         gfxdevice_ops_init(&ops, this->device, alpha);
2666         gfxresult_record_replay(grouprecording, &ops);
2667         ops.finish(&ops);
2668     }
2669     grouprecording->destroy(grouprecording);
2670
2671     states[statepos].grouprecording = 0;
2672 }
2673
2674 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2675 {
2676     if(states[statepos].softmask) {
2677         /* shouldn't happen, but *does* happen */
2678         clearSoftMask(state);
2679     }
2680
2681     /* alpha = 1: retrieve mask values from alpha layer
2682        alpha = 0: retrieve mask values from luminance */
2683
2684     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2685             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2686     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2687             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2688     if(!alpha)
2689         infofeature("soft masks");
2690     else
2691         warnfeature("soft masks from alpha channel",0);
2692    
2693     if(states[statepos].olddevice) {
2694         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2695         exit(1);
2696     }
2697     states[statepos].olddevice = this->device;
2698     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2699     gfxdevice_record_init(this->device);
2700
2701     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2702     
2703     states[statepos].softmask = 1;
2704     states[statepos].softmask_alpha = alpha;
2705 }
2706
2707 static inline Guchar div255(int x) {
2708   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2709 }
2710
2711 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2712 {
2713     if(c < min) c = min;
2714     if(c > max) c = max;
2715     return c;
2716 }
2717
2718 void GFXOutputDev::clearSoftMask(GfxState *state)
2719 {
2720     if(!states[statepos].softmask)
2721         return;
2722     states[statepos].softmask = 0;
2723     dbg("clearSoftMask statepos=%d", statepos);
2724     msg("<verbose> clearSoftMask statepos=%d", statepos);
2725     
2726     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2727         msg("<error> Error in softmask/tgroup ordering");
2728         return;
2729     }
2730   
2731     gfxresult_t*mask = states[statepos].softmaskrecording;
2732     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2733     this->device = states[statepos].olddevice;
2734
2735     /* get outline of all objects below the soft mask */
2736     gfxdevice_t uniondev;
2737     gfxdevice_union_init(&uniondev, 0);
2738     gfxresult_record_replay(below, &uniondev);
2739     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2740     uniondev.finish(&uniondev);
2741     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2742     gfxline_free(belowoutline);belowoutline=0;
2743 #if 0 
2744     this->device->startclip(this->device, belowoutline);
2745     gfxresult_record_replay(below, this->device);
2746     gfxresult_record_replay(mask, this->device);
2747     this->device->endclip(this->device);
2748 #endif
2749     
2750     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2751     if(width<=0 || height<=0)
2752         return;
2753
2754     gfxdevice_t belowrender;
2755     gfxdevice_render_init(&belowrender);
2756     if(states[statepos+1].isolated) {
2757         belowrender.setparameter(&belowrender, "fillwhite", "1");
2758     }
2759     belowrender.setparameter(&belowrender, "antialize", "2");
2760     belowrender.startpage(&belowrender, width, height);
2761     gfxresult_record_replay(below, &belowrender);
2762     belowrender.endpage(&belowrender);
2763     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2764     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2765     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2766
2767     gfxdevice_t maskrender;
2768     gfxdevice_render_init(&maskrender);
2769     maskrender.startpage(&maskrender, width, height);
2770     gfxresult_record_replay(mask, &maskrender);
2771     maskrender.endpage(&maskrender);
2772     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2773     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2774
2775     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2776         msg("<fatal> Internal error in mask drawing");
2777         return;
2778     }
2779
2780     int y,x;
2781     for(y=0;y<height;y++) {
2782         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2783         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2784         for(x=0;x<width;x++) {
2785             int alpha;
2786             if(states[statepos].softmask_alpha) {
2787                 alpha = l1->a;
2788             } else {
2789                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2790             }
2791
2792             l2->a = div255(alpha*l2->a);
2793
2794             /* DON'T premultiply alpha- this is done by fillbitmap,
2795                depending on the output device */
2796             //l2->r = div255(alpha*l2->r);
2797             //l2->g = div255(alpha*l2->g);
2798             //l2->b = div255(alpha*l2->b);
2799
2800             l1++;
2801             l2++;
2802         }
2803     }
2804     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2805
2806     gfxmatrix_t matrix;
2807     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2808     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2809
2810     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2811
2812     mask->destroy(mask);
2813     below->destroy(below);
2814     maskresult->destroy(maskresult);
2815     belowresult->destroy(belowresult);
2816     states[statepos].softmaskrecording = 0;
2817 }
2818   
2819 //class MemCheck
2820 //{
2821 //    public: ~MemCheck()
2822 //    {
2823 //        delete globalParams;globalParams=0;
2824 //        Object::memCheck(stderr);
2825 //        gMemReport(stderr);
2826 //    }
2827 //} myMemCheck;
2828