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