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