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