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