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