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