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