da9ec41e0d7b3d90d260d5875d1de679b6b9a781
[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) || render == RENDER_INVISIBLE) {
1449         int space = this->current_fontinfo->space_char;
1450         if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1451             /* space char detection */
1452             if(last_char_gfxfont == current_gfxfont && 
1453                last_char_y == m.ty &&
1454                !last_char_was_space) {
1455                 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1456                 int space = this->current_fontinfo->space_char;
1457                 if(m.tx - expected_x >= m.m00*64) {
1458                     msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here", 
1459                             m.tx-expected_x, 
1460                             (m.tx-expected_x)/m.m00,
1461                             last_char, glyphid);
1462                     gfxmatrix_t m2 = m;
1463                     m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1464                     if(m2.tx < expected_x) m2.tx = expected_x;
1465                     device->drawchar(device, current_gfxfont, space, &col, &m2);
1466                 }
1467             }
1468             last_char_gfxfont = current_gfxfont;
1469             last_char = glyphid;
1470             last_char_x = m.tx;
1471             last_char_y = m.ty;
1472             last_char_was_space = GLYPH_IS_SPACE(&current_gfxfont->glyphs[glyphid]);
1473         }
1474         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1475     } else {
1476         msg("<debug> Drawing glyph %d as shape", charid);
1477         if(!gfxglobals->textmodeinfo) {
1478             msg("<notice> Some texts will be rendered as shape");
1479             gfxglobals->textmodeinfo = 1;
1480         }
1481         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1482         gfxline_t*tglyph = gfxline_clone(glyph);
1483         gfxline_transform(tglyph, &m);
1484         if((render&3) != RENDER_INVISIBLE) {
1485             gfxline_t*add = gfxline_clone(tglyph);
1486             current_text_stroke = gfxline_append(current_text_stroke, add);
1487         }
1488         if(render&RENDER_CLIP) {
1489             gfxline_t*add = gfxline_clone(tglyph);
1490             current_text_clip = gfxline_append(current_text_clip, add);
1491             if(!current_text_clip) {
1492                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1493             }
1494         }
1495         gfxline_free(tglyph);
1496     }
1497 }
1498
1499 void GFXOutputDev::endString(GfxState *state) 
1500
1501     int render = state->getRender();
1502     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1503     
1504     if(current_text_stroke) {
1505         /* fillstroke and stroke text rendering objects we can process right
1506            now (as there may be texts of other rendering modes in this
1507            text object)- clipping objects have to wait until endTextObject,
1508            however */
1509         device->setparameter(device, "mark","TXT");
1510         if((render&3) == RENDER_FILL) {
1511             fillGfxLine(state, current_text_stroke, 0);
1512             gfxline_free(current_text_stroke);
1513             current_text_stroke = 0;
1514         } else if((render&3) == RENDER_FILLSTROKE) {
1515             fillGfxLine(state, current_text_stroke, 0);
1516             strokeGfxline(state, current_text_stroke,0);
1517             gfxline_free(current_text_stroke);
1518             current_text_stroke = 0;
1519         } else if((render&3) == RENDER_STROKE) {
1520             strokeGfxline(state, current_text_stroke,0);
1521             gfxline_free(current_text_stroke);
1522             current_text_stroke = 0;
1523         }
1524         device->setparameter(device, "mark","");
1525     }
1526 }    
1527
1528 void GFXOutputDev::endTextObject(GfxState *state)
1529 {
1530     int render = state->getRender();
1531     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1532     
1533     if(current_text_clip) {
1534         device->setparameter(device, "mark","TXT");
1535         clipToGfxLine(state, current_text_clip, 0);
1536         device->setparameter(device, "mark","");
1537         gfxline_free(current_text_clip);
1538         current_text_clip = 0;
1539     }
1540 }
1541
1542 /* the logic seems to be as following:
1543    first, beginType3Char is called, with the charcode and the coordinates.
1544    if this function returns true, it already knew about the char and has now drawn it.
1545    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1546    called with some parameters.
1547    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1548    at the position first passed to beginType3Char). the char ends with endType3Char.
1549
1550    The drawing operations between beginType3Char and endType3Char are somewhat different to
1551    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1552    color determines the color of a font)
1553 */
1554
1555 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1556 {
1557     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1558     type3active = 1;
1559     
1560     if(config_extrafontdata && current_fontinfo) {
1561
1562         gfxmatrix_t m = this->current_font_matrix;
1563         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1564         m.m00*=INTERNAL_FONT_SIZE;
1565         m.m01*=INTERNAL_FONT_SIZE;
1566         m.m10*=INTERNAL_FONT_SIZE;
1567         m.m11*=INTERNAL_FONT_SIZE;
1568
1569         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1570             msg("<error> Invalid charid %d for font", charid);
1571             return gFalse;
1572         }
1573         gfxcolor_t col={0,0,0,0};
1574         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1575         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1576     }
1577
1578
1579     /* the character itself is going to be passed using the draw functions */
1580     return gFalse; /* gTrue= is_in_cache? */
1581 }
1582
1583 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1584 }
1585 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1586 }
1587
1588 void GFXOutputDev::endType3Char(GfxState *state)
1589 {
1590     type3active = 0;
1591     msg("<debug> endType3Char");
1592 }
1593
1594 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1595 {
1596     this->currentpage = pageNum;
1597     double x1,y1,x2,y2;
1598     int rot = doc->getPageRotate(1);
1599     gfxcolor_t white = {255,255,255,255};
1600     gfxcolor_t black = {255,0,0,0};
1601     laststate = state;
1602     gfxline_t clippath[5];
1603
1604     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1605     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1606     Use CropBox, not MediaBox, as page size
1607     */
1608     
1609     /*x1 = crop_x1;
1610     y1 = crop_y1;
1611     x2 = crop_x2;
1612     y2 = crop_y2;*/
1613     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1614     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1615
1616     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1617     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1618
1619     this->clipmovex = -(int)x1;
1620     this->clipmovey = -(int)y1;
1621     
1622     /* apply user clip box */
1623     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1624         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1625         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1626         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1627         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1628         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1629     } else {
1630         x1 += this->clipmovex;
1631         y1 += this->clipmovey;
1632         x2 += this->clipmovex;
1633         y2 += this->clipmovey;
1634     }
1635
1636     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1637     
1638     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);
1639     if(rot!=0)
1640         msg("<verbose> page is rotated %d degrees", rot);
1641
1642     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1643     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1644     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1645     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1646     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1647     device->startclip(device, clippath); outer_clip_box = 1;
1648     if(!config_transparent) {
1649         device->fill(device, clippath, &white);
1650     }
1651     states[statepos].clipbbox.xmin = x1;
1652     states[statepos].clipbbox.ymin = x1;
1653     states[statepos].clipbbox.xmax = x2;
1654     states[statepos].clipbbox.ymax = y2;
1655     
1656     states[statepos].dashPattern = 0;
1657     states[statepos].dashLength = 0;
1658     states[statepos].dashStart = 0;
1659     
1660     this->last_char_gfxfont = 0;
1661 }
1662
1663
1664 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1665 {
1666     double x1, y1, x2, y2;
1667     gfxline_t points[5];
1668     int x, y;
1669     
1670     msg("<debug> drawlink");
1671
1672     link->getRect(&x1, &y1, &x2, &y2);
1673     cvtUserToDev(x1, y1, &x, &y);
1674     points[0].type = gfx_moveTo;
1675     points[0].x = points[4].x = x + user_movex + clipmovex;
1676     points[0].y = points[4].y = y + user_movey + clipmovey;
1677     points[0].next = &points[1];
1678     cvtUserToDev(x2, y1, &x, &y);
1679     points[1].type = gfx_lineTo;
1680     points[1].x = x + user_movex + clipmovex;
1681     points[1].y = y + user_movey + clipmovey;
1682     points[1].next = &points[2];
1683     cvtUserToDev(x2, y2, &x, &y);
1684     points[2].type = gfx_lineTo;
1685     points[2].x = x + user_movex + clipmovex;
1686     points[2].y = y + user_movey + clipmovey;
1687     points[2].next = &points[3];
1688     cvtUserToDev(x1, y2, &x, &y);
1689     points[3].type = gfx_lineTo;
1690     points[3].x = x + user_movex + clipmovex;
1691     points[3].y = y + user_movey + clipmovey;
1692     points[3].next = &points[4];
1693     cvtUserToDev(x1, y1, &x, &y);
1694     points[4].type = gfx_lineTo;
1695     points[4].x = x + user_movex + clipmovex;
1696     points[4].y = y + user_movey + clipmovey;
1697     points[4].next = 0;
1698     
1699     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1700             points[0].x, points[0].y,
1701             points[1].x, points[1].y,
1702             points[2].x, points[2].y,
1703             points[3].x, points[3].y); 
1704     
1705     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1706         dump_outline(points);
1707     }
1708
1709     LinkAction*action=link->getAction();
1710     char buf[128];
1711     char*s = 0;
1712     const char*type = "-?-";
1713     char*named = 0;
1714     int page = -1;
1715     msg("<trace> drawlink action=%d", action->getKind());
1716     switch(action->getKind())
1717     {
1718         case actionGoTo: {
1719             type = "GoTo";
1720             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1721             LinkDest *dest=NULL;
1722             if (ha->getDest()==NULL) 
1723                 dest=catalog->findDest(ha->getNamedDest());
1724             else 
1725                 dest=ha->getDest()->copy();
1726             if (dest){ 
1727               if (dest->isPageRef()){
1728                 Ref pageref=dest->getPageRef();
1729                 page=catalog->findPage(pageref.num,pageref.gen);
1730               }
1731               else  page=dest->getPageNum();
1732               sprintf(buf, "%d", page);
1733               s = strdup(buf);
1734               delete dest;
1735             }
1736         }
1737         break;
1738         case actionGoToR: {
1739             type = "GoToR";
1740             LinkGoToR*l = (LinkGoToR*)action;
1741             GString*g = l->getFileName();
1742             if(g)
1743              s = strdup(g->getCString());
1744             if(!s) {
1745                 /* if the GoToR link has no filename, then
1746                    try to find a refernce in the *local*
1747                    file */
1748                 GString*g = l->getNamedDest();
1749                 if(g)
1750                  s = strdup(g->getCString());
1751             }
1752         }
1753         break;
1754         case actionNamed: {
1755             type = "Named";
1756             LinkNamed*l = (LinkNamed*)action;
1757             GString*name = l->getName();
1758             if(name) {
1759                 s = strdup(name->lowerCase()->getCString());
1760                 named = name->getCString();
1761                 if(!strchr(s,':')) 
1762                 {
1763                     if(strstr(s, "next") || strstr(s, "forward"))
1764                     {
1765                         page = currentpage + 1;
1766                     }
1767                     else if(strstr(s, "prev") || strstr(s, "back"))
1768                     {
1769                         page = currentpage - 1;
1770                     }
1771                     else if(strstr(s, "last") || strstr(s, "end"))
1772                     {
1773                         if(this->page2page && this->num_pages) {
1774                             page = this->page2page[this->num_pages-1];
1775                         }
1776                     }
1777                     else if(strstr(s, "first") || strstr(s, "top"))
1778                     {
1779                         page = 1;
1780                     }
1781                 }
1782             }
1783         }
1784         break;
1785         case actionLaunch: {
1786             type = "Launch";
1787             LinkLaunch*l = (LinkLaunch*)action;
1788             GString * str = new GString(l->getFileName());
1789             GString * params = l->getParams();
1790             if(params)
1791                 str->append(params);
1792             s = strdup(str->getCString());
1793             delete str;
1794         }
1795         break;
1796         case actionURI: {
1797             char*url = 0;
1798             type = "URI";
1799             LinkURI*l = (LinkURI*)action;
1800             GString*g = l->getURI();
1801             if(g) {
1802              url = g->getCString();
1803              s = strdup(url);
1804             }
1805         }
1806         break;
1807         case actionUnknown: {
1808             type = "Unknown";
1809             LinkUnknown*l = (LinkUnknown*)action;
1810             s = strdup("");
1811         }
1812         break;
1813         default: {
1814             msg("<error> Unknown link type!");
1815             break;
1816         }
1817     }
1818
1819     if(!s) s = strdup("-?-");
1820     
1821     msg("<trace> drawlink s=%s", s);
1822
1823     if(!gfxglobals->linkinfo && (page || s))
1824     {
1825         msg("<notice> File contains links");
1826         gfxglobals->linkinfo = 1;
1827     }
1828     
1829     if(page>0) {
1830         int t;
1831         int lpage = -1;
1832         for(t=1;t<=this->num_pages;t++) {
1833             if(this->page2page[t]==page) {
1834                 lpage = t;
1835                 break;
1836             }
1837         }
1838         if(lpage<0) {
1839             lpage = page;
1840         }
1841
1842         char buf[80];
1843         sprintf(buf, "page%d", lpage);
1844         device->drawlink(device, points, buf);
1845     }
1846     else if(s)
1847     {
1848         device->drawlink(device, points, s);
1849         if(this->config_linkdatafile) {
1850             FILE*fi = fopen(config_linkdatafile, "ab+");
1851             fprintf(fi, "%s\n", s);
1852             fclose(fi);
1853         }
1854     }
1855         
1856     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1857     free(s);s=0;
1858 }
1859
1860 void GFXOutputDev::saveState(GfxState *state) {
1861     dbg("saveState %08x", state); dbgindent+=2;
1862
1863     msg("<trace> saveState %08x", state);
1864     updateAll(state);
1865     if(statepos>=64) {
1866       msg("<fatal> Too many nested states in pdf.");
1867       exit(1);
1868     }
1869     statepos ++;
1870     states[statepos].state = state;
1871     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1872     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1873     states[statepos].clipping = 0;
1874     states[statepos].olddevice = 0;
1875     states[statepos].clipbbox = states[statepos-1].clipbbox;
1876
1877     states[statepos].dashPattern = states[statepos-1].dashPattern;
1878     states[statepos].dashStart = states[statepos-1].dashStart;
1879     states[statepos].dashLength = states[statepos-1].dashLength;
1880 };
1881
1882 void GFXOutputDev::restoreState(GfxState *state) {
1883   dbgindent-=2; dbg("restoreState %08x", state);
1884
1885   if(statepos==0) {
1886       msg("<fatal> Invalid restoreState");
1887       exit(1);
1888   }
1889   msg("<trace> restoreState %08x%s%s", state,
1890                                   states[statepos].softmask?" (end softmask)":"",
1891                                   states[statepos].clipping?" (end clipping)":"");
1892   if(states[statepos].softmask) {
1893       clearSoftMask(state);
1894   }
1895
1896   if(states[statepos].dashPattern) {
1897       if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1898           free(states[statepos].dashPattern);
1899           states[statepos].dashPattern = 0;
1900       }
1901   }
1902
1903   updateAll(state);
1904   
1905   while(states[statepos].clipping) {
1906       device->endclip(device);
1907       states[statepos].clipping--;
1908   }
1909   if(states[statepos].state!=state) {
1910       msg("<fatal> bad state nesting");
1911       if(verbose) {
1912           int t;
1913           for(t=0;t<=statepos;t++) {
1914               printf("%08x ", states[t].state);
1915           }
1916           printf("\n");
1917       }
1918       exit(1);
1919   }
1920   states[statepos].state=0;
1921   statepos--;
1922 }
1923  
1924 void GFXOutputDev::updateLineDash(GfxState *state) 
1925 {
1926     if(states[statepos].dashPattern &&
1927        (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1928         free(states[statepos].dashPattern);
1929         states[statepos].dashPattern = 0;
1930     }
1931     double *pattern = 0;
1932     int dashLength;
1933     double dashStart;
1934     state->getLineDash(&pattern, &dashLength, &dashStart);
1935     msg("<debug> updateLineDash, %d dashes", dashLength);
1936     if(!dashLength) {
1937         states[statepos].dashPattern = 0;
1938         states[statepos].dashLength = 0;
1939     } else {
1940         double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1941         memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1942         states[statepos].dashPattern = p;
1943         states[statepos].dashLength = dashLength;
1944         states[statepos].dashStart = dashStart;
1945     }
1946 }
1947   
1948 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1949 {
1950     this->page2page = page2page;
1951     this->num_pages = num_pages;
1952 }
1953
1954 void GFXOutputDev::updateLineWidth(GfxState *state)
1955 {
1956     double width = state->getTransformedLineWidth();
1957 }
1958
1959 void GFXOutputDev::updateLineCap(GfxState *state)
1960 {
1961     int c = state->getLineCap();
1962 }
1963
1964 void GFXOutputDev::updateLineJoin(GfxState *state)
1965 {
1966     int j = state->getLineJoin();
1967 }
1968
1969 void GFXOutputDev::updateFillColor(GfxState *state) 
1970 {
1971     GfxRGB rgb;
1972     double opaq = state->getFillOpacity();
1973     state->getFillRGB(&rgb);
1974 }
1975 void GFXOutputDev::updateFillOpacity(GfxState *state)
1976 {
1977     GfxRGB rgb;
1978     double opaq = state->getFillOpacity();
1979     state->getFillRGB(&rgb);
1980     dbg("update fillopaq %f", opaq);
1981 }
1982 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1983 {
1984     double opaq = state->getFillOpacity();
1985     dbg("update strokeopaq %f", opaq);
1986 }
1987 void GFXOutputDev::updateFillOverprint(GfxState *state)
1988 {
1989     double opaq = state->getFillOverprint();
1990     dbg("update filloverprint %f", opaq);
1991 }
1992 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1993 {
1994     double opaq = state->getStrokeOverprint();
1995     dbg("update strokeoverprint %f", opaq);
1996 }
1997 void GFXOutputDev::updateTransfer(GfxState *state)
1998 {
1999     dbg("update transfer");
2000 }
2001
2002
2003 void GFXOutputDev::updateStrokeColor(GfxState *state) 
2004 {
2005     GfxRGB rgb;
2006     double opaq = state->getStrokeOpacity();
2007     state->getStrokeRGB(&rgb);
2008 }
2009
2010 void GFXOutputDev::updateFont(GfxState *state) 
2011 {
2012     GfxFont* gfxFont = state->getFont();
2013     if (!gfxFont) {
2014         return; 
2015     }  
2016     char*id = getFontID(gfxFont);
2017     msg("<verbose> Updating font to %s", id);
2018     if(gfxFont->getType() == fontType3) {
2019         infofeature("Type3 fonts");
2020         if(!config_extrafontdata) {
2021             return;
2022         }
2023     }
2024     if(!id) {
2025         msg("<error> Internal Error: FontID is null");
2026         return; 
2027     }
2028
2029     this->current_fontinfo = this->info->getFont(id);
2030
2031     if(!this->current_fontinfo) {
2032         msg("<error> Internal Error: no fontinfo for font %s", id);
2033         return;
2034     }
2035     if(!this->current_fontinfo->seen) {
2036         dumpFontInfo("<verbose>", gfxFont);
2037     }
2038
2039     current_gfxfont = this->current_fontinfo->getGfxFont();
2040     device->addfont(device, current_gfxfont);
2041     free(id);
2042
2043     updateFontMatrix(state);
2044 }
2045
2046 #define SQR(x) ((x)*(x))
2047
2048 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2049 {
2050     if((newwidth<1 || newheight<1) ||
2051        (width<=newwidth || height<=newheight))
2052         return 0;
2053     unsigned char*newdata;
2054     int x,y;
2055     newdata= (unsigned char*)malloc(newwidth*newheight);
2056     double fx = ((double)width)/newwidth;
2057     double fy = ((double)height)/newheight;
2058     double px = 0;
2059     int blocksize = (int)(8192/(fx*fy));
2060     int r = 8192*256/palettesize;
2061     for(x=0;x<newwidth;x++) {
2062         double ex = px + fx;
2063         int fromx = (int)px;
2064         int tox = (int)ex;
2065         int xweight1 = (int)((1-(px-fromx))*256);
2066         int xweight2 = (int)((ex-tox)*256);
2067         double py =0;
2068         for(y=0;y<newheight;y++) {
2069             double ey = py + fy;
2070             int fromy = (int)py;
2071             int toy = (int)ey;
2072             int yweight1 = (int)((1-(py-fromy))*256);
2073             int yweight2 = (int)((ey-toy)*256);
2074             int a = 0;
2075             int xx,yy;
2076             if(tox>=width) 
2077                 tox = width-1;
2078             if(toy>=height) 
2079                 toy = height-1;
2080             for(xx=fromx;xx<=tox;xx++)
2081             for(yy=fromy;yy<=toy;yy++) {
2082                 int b = 1-data[width*yy+xx];
2083                 int weight=256;
2084                 if(xx==fromx) weight = (weight*xweight1)/256;
2085                 if(xx==tox) weight = (weight*xweight2)/256;
2086                 if(yy==fromy) weight = (weight*yweight1)/256;
2087                 if(yy==toy) weight = (weight*yweight2)/256;
2088                 a+=b*weight;
2089             }
2090             //if(a) a=(palettesize-1)*r/blocksize;
2091             newdata[y*newwidth+x] = (a*blocksize)/r;
2092             py = ey;
2093         }
2094         px = ex;
2095     }
2096     return newdata;
2097 }
2098
2099 #define IMAGE_TYPE_JPEG 0
2100 #define IMAGE_TYPE_LOSSLESS 1
2101
2102 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2103         double x1,double y1,
2104         double x2,double y2,
2105         double x3,double y3,
2106         double x4,double y4, int type, int multiply)
2107 {
2108     gfxcolor_t*newpic=0;
2109     
2110     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2111     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2112    
2113     gfxline_t p1,p2,p3,p4,p5;
2114     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2115     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2116     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2117     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2118     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2119
2120     {p1.x = (int)(p1.x*20)/20.0;
2121      p1.y = (int)(p1.y*20)/20.0;
2122      p2.x = (int)(p2.x*20)/20.0;
2123      p2.y = (int)(p2.y*20)/20.0;
2124      p3.x = (int)(p3.x*20)/20.0;
2125      p3.y = (int)(p3.y*20)/20.0;
2126      p4.x = (int)(p4.x*20)/20.0;
2127      p4.y = (int)(p4.y*20)/20.0;
2128      p5.x = (int)(p5.x*20)/20.0;
2129      p5.y = (int)(p5.y*20)/20.0;
2130     }
2131
2132     gfxmatrix_t m;
2133     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2134     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2135         
2136     m.tx = p1.x - 0.5*multiply;
2137     m.ty = p1.y - 0.5*multiply;
2138
2139     gfximage_t img;
2140     img.data = (gfxcolor_t*)data;
2141     img.width = sizex;
2142     img.height = sizey;
2143   
2144     if(type == IMAGE_TYPE_JPEG)
2145         /* TODO: pass image_dpi to device instead */
2146         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2147
2148     dump_outline(&p1);
2149     dev->fillbitmap(dev, &p1, &img, &m, 0);
2150 }
2151
2152 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2153         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2154 {
2155     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2156 }
2157
2158 void drawimagelossless(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_LOSSLESS, multiply);
2162 }
2163
2164
2165 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2166                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2167                                    GBool inlineImg, int mask, int*maskColors,
2168                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2169 {
2170   /* the code in this function is *old*. It's not pretty, but it works. */
2171
2172   double x1,y1,x2,y2,x3,y3,x4,y4;
2173   ImageStream *imgStr;
2174   Guchar pixBuf[4];
2175   GfxRGB rgb;
2176   int ncomps = 1;
2177   int bits = 1;
2178   unsigned char* maskbitmap = 0;
2179                                  
2180   if(colorMap) {
2181     ncomps = colorMap->getNumPixelComps();
2182     bits = colorMap->getBits();
2183   }
2184       
2185   if(maskStr) {
2186       int x,y;
2187       unsigned char buf[8];
2188       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2189       if(maskColorMap) {
2190           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2191           imgMaskStr->reset();
2192           unsigned char pal[256];
2193           int n = 1 << colorMap->getBits();
2194           int t;
2195           for(t=0;t<n;t++) {
2196               GfxGray gray;
2197               pixBuf[0] = t;
2198               maskColorMap->getGray(pixBuf, &gray);
2199               pal[t] = colToByte(gray);
2200           }
2201           for (y = 0; y < maskHeight; y++) {
2202               for (x = 0; x < maskWidth; x++) {
2203                   imgMaskStr->getPixel(buf);
2204                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2205               }
2206           }
2207           delete imgMaskStr;
2208       } else {
2209           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2210           imgMaskStr->reset();
2211           for (y = 0; y < maskHeight; y++) {
2212               for (x = 0; x < maskWidth; x++) {
2213                   imgMaskStr->getPixel(buf);
2214                   buf[0]^=maskInvert;
2215                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2216               }
2217           }
2218           delete imgMaskStr;
2219       }
2220       maskStr->close();
2221   }
2222   
2223   imgStr = new ImageStream(str, width, ncomps,bits);
2224   imgStr->reset();
2225
2226   if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2227   {
2228       msg("<verbose> Ignoring %d by %d image", width, height);
2229       unsigned char buf[8];
2230       int x,y;
2231       for (y = 0; y < height; ++y)
2232       for (x = 0; x < width; ++x) {
2233           imgStr->getPixel(buf);
2234       }
2235       delete imgStr;
2236       if(maskbitmap)
2237           free(maskbitmap);
2238       return;
2239   }
2240
2241   this->transformXY(state, 0, 1, &x1, &y1);
2242   this->transformXY(state, 0, 0, &x2, &y2);
2243   this->transformXY(state, 1, 0, &x3, &y3);
2244   this->transformXY(state, 1, 1, &x4, &y4);
2245
2246   if(type3active) {
2247       /* as type 3 bitmaps are antialized, we need to place them
2248          at integer coordinates, otherwise flash player's antializing
2249          will kick in and make everything blurry */
2250       x1 = (int)(x1);y1 = (int)(y1);
2251       x2 = (int)(x2);y2 = (int)(y2);
2252       x3 = (int)(x3);y3 = (int)(y3);
2253       x4 = (int)(x4);y4 = (int)(y4);
2254   }
2255
2256   if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2257       if(!type3active) {
2258           msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2259           gfxglobals->pbminfo = 1;
2260       }
2261       if(mask)
2262       msg("<verbose> drawing %d by %d masked picture", width, height);
2263   }
2264   if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2265       msg("<notice> File contains jpeg pictures");
2266       gfxglobals->jpeginfo = 1;
2267   }
2268
2269   if(mask) {
2270       unsigned char buf[8];
2271       int x,y;
2272       unsigned char*pic = new unsigned char[width*height];
2273       gfxcolor_t pal[256];
2274       GfxRGB rgb;
2275       state->getFillRGB(&rgb);
2276
2277       memset(pal,255,sizeof(pal));
2278       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2279       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2280       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2281       pal[0].a = 255;              pal[1].a = 0;
2282     
2283       int numpalette = 2;
2284       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2285       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2286       for (y = 0; y < height; ++y)
2287       for (x = 0; x < width; ++x)
2288       {
2289             imgStr->getPixel(buf);
2290             if(invert) 
2291                 buf[0]=1-buf[0];
2292             pic[width*y+x] = buf[0];
2293       }
2294      
2295       if(type3active) {
2296           unsigned char*pic2 = 0;
2297           numpalette = 16;
2298           
2299           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2300
2301           if(!pic2) {
2302             delete[] pic;
2303             delete imgStr;
2304             return;
2305           }
2306           
2307           width = realwidth;
2308           height = realheight;
2309           delete[] pic;
2310           pic = pic2;
2311           
2312           /* make a black/white palette */
2313
2314           float r = 255./(float)(numpalette-1);
2315           int t;
2316           for(t=0;t<numpalette;t++) {
2317               pal[t].r = colToByte(rgb.r);
2318               pal[t].g = colToByte(rgb.g);
2319               pal[t].b = colToByte(rgb.b);
2320               pal[t].a = (unsigned char)(t*r);
2321           }
2322           
2323       }
2324
2325       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2326       for (y = 0; y < height; ++y) {
2327         for (x = 0; x < width; ++x) {
2328           pic2[width*y+x] = pal[pic[y*width+x]];
2329         }
2330       }
2331       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2332       delete[] pic2;
2333       delete[] pic;
2334       delete imgStr;
2335       if(maskbitmap) free(maskbitmap);
2336       return;
2337   }
2338
2339   int x,y;
2340
2341   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2342       gfxcolor_t*pic=new gfxcolor_t[width*height];
2343       for (y = 0; y < height; ++y) {
2344         for (x = 0; x < width; ++x) {
2345           imgStr->getPixel(pixBuf);
2346           colorMap->getRGB(pixBuf, &rgb);
2347           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2348           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2349           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2350           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2351           if(maskbitmap) {
2352               int x1 = x*maskWidth/width;
2353               int y1 = y*maskHeight/height;
2354               int x2 = (x+1)*maskWidth/width;
2355               int y2 = (y+1)*maskHeight/height;
2356               int xx,yy;
2357               unsigned int alpha=0;
2358               unsigned int count=0;
2359               for(xx=x1;xx<x2;xx++)
2360               for(yy=y1;yy<y2;yy++) {
2361                   alpha += maskbitmap[yy*maskWidth+xx];
2362                   count ++;
2363               }
2364               if(count) {
2365                 pic[width*y+x].a = alpha / count;
2366               } else {
2367                 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2368               }
2369           }
2370         }
2371       }
2372       if(str->getKind()==strDCT)
2373           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2374       else
2375           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2376       delete[] pic;
2377       delete imgStr;
2378       if(maskbitmap) free(maskbitmap);
2379       return;
2380   } else {
2381       gfxcolor_t*pic=new gfxcolor_t[width*height];
2382       gfxcolor_t pal[256];
2383       int n = 1 << colorMap->getBits();
2384       int t;
2385       for(t=0;t<256;t++) {
2386           pixBuf[0] = t;
2387           colorMap->getRGB(pixBuf, &rgb);
2388
2389           {/*if(maskColors && *maskColors==t) {
2390               msg("<notice> Color %d is transparent", t);
2391               if (imgData->maskColors) {
2392                 *alpha = 0;
2393                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2394                   if (pix[i] < imgData->maskColors[2*i] ||
2395                       pix[i] > imgData->maskColors[2*i+1]) {
2396                     *alpha = 1;
2397                     break;
2398                   }
2399                 }
2400               } else {
2401                 *alpha = 1;
2402               }
2403               if(!*alpha) {
2404                     pal[t].r = 0;
2405                     pal[t].g = 0;
2406                     pal[t].b = 0;
2407                     pal[t].a = 0;
2408               }
2409           } else {*/
2410               pal[t].r = (unsigned char)(colToByte(rgb.r));
2411               pal[t].g = (unsigned char)(colToByte(rgb.g));
2412               pal[t].b = (unsigned char)(colToByte(rgb.b));
2413               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2414           }
2415       }
2416       for (y = 0; y < height; ++y) {
2417         for (x = 0; x < width; ++x) {
2418           imgStr->getPixel(pixBuf);
2419           pic[width*y+x] = pal[pixBuf[0]];
2420         }
2421       }
2422       if(maskbitmap) {
2423           if(maskWidth < width && maskHeight < height) {
2424               for(y = 0; y < height; y++) {
2425                   for (x = 0; x < width; x++) {
2426                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2427                   }
2428               }
2429           } else {
2430               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2431               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2432               double dx = width / maskWidth;
2433               double dy = height / maskHeight;
2434               double yy = 0;
2435               for(y = 0; y < maskHeight; y++) {
2436                   double xx = 0;
2437                   for (x = 0; x < maskWidth; x++) {
2438                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2439                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2440                       xx += dx;
2441                   }
2442                   yy += dy;
2443               }
2444               delete[] pic;
2445               pic = newpic;
2446               width = maskWidth;
2447               height = maskHeight;
2448           }
2449       }
2450       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2451
2452       delete[] pic;
2453       delete imgStr;
2454       if(maskbitmap) free(maskbitmap);
2455       return;
2456   }
2457 }
2458
2459 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2460                                    int width, int height, GBool invert,
2461                                    GBool inlineImg) 
2462 {
2463     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2464     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2465     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2466 }
2467
2468 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2469                          int width, int height, GfxImageColorMap *colorMap,
2470                          int *maskColors, GBool inlineImg)
2471 {
2472     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2473             colorMap?"colorMap":"no colorMap", 
2474             maskColors?"maskColors":"no maskColors",
2475             inlineImg);
2476     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2477             colorMap?"colorMap":"no colorMap", 
2478             maskColors?"maskColors":"no maskColors",
2479             inlineImg);
2480     if(colorMap)
2481         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2482                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2483     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2484 }
2485   
2486 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2487                                int width, int height,
2488                                GfxImageColorMap *colorMap,
2489                                Stream *maskStr, int maskWidth, int maskHeight,
2490                                GBool maskInvert)
2491 {
2492     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2493             colorMap?"colorMap":"no colorMap", 
2494             maskWidth, maskHeight);
2495     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2496             colorMap?"colorMap":"no colorMap", 
2497             maskWidth, maskHeight);
2498     if(colorMap)
2499         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2500                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2501     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2502 }
2503
2504 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2505                                    int width, int height,
2506                                    GfxImageColorMap *colorMap,
2507                                    Stream *maskStr,
2508                                    int maskWidth, int maskHeight,
2509                                    GfxImageColorMap *maskColorMap)
2510 {
2511     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2512             colorMap?"colorMap":"no colorMap", 
2513             maskWidth, maskHeight);
2514     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2515             colorMap?"colorMap":"no colorMap", 
2516             maskWidth, maskHeight);
2517     if(colorMap)
2518         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2519                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2520     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2521 }
2522
2523 void GFXOutputDev::stroke(GfxState *state) 
2524 {
2525     dbg("stroke");
2526
2527     GfxPath * path = state->getPath();
2528     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2529     strokeGfxline(state, line, 0);
2530     gfxline_free(line);
2531 }
2532
2533 void GFXOutputDev::fill(GfxState *state) 
2534 {
2535     gfxcolor_t col = getFillColor(state);
2536     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2537
2538     GfxPath * path = state->getPath();
2539     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2540     if(!config_disable_polygon_conversion) {
2541         gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2542         gfxline_free(line);
2543         line = line2;
2544     }
2545     fillGfxLine(state, line, 0);
2546     gfxline_free(line);
2547 }
2548
2549 void GFXOutputDev::eoFill(GfxState *state) 
2550 {
2551     gfxcolor_t col = getFillColor(state);
2552     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2553
2554     GfxPath * path = state->getPath();
2555     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2556     fillGfxLine(state, line, 1);
2557     gfxline_free(line);
2558 }
2559
2560
2561 static const char* dirseparator()
2562 {
2563 #ifdef WIN32
2564     return "\\";
2565 #else
2566     return "/";
2567 #endif
2568 }
2569
2570 void addGlobalFont(const char*filename)
2571 {
2572     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2573     memset(f, 0, sizeof(fontfile_t));
2574     f->filename = filename;
2575     int len = strlen(filename);
2576     char*r1 = strrchr((char*)filename, '/');
2577     char*r2 = strrchr((char*)filename, '\\');
2578     if(r2>r1)
2579         r1 = r2;
2580     if(r1) {
2581         len = strlen(r1+1);
2582     }
2583     f->len = len;
2584
2585     msg("<verbose> Adding font \"%s\".", filename);
2586     if(global_fonts_next) {
2587         global_fonts_next->next = f;
2588         global_fonts_next = global_fonts_next->next;
2589     } else {
2590         global_fonts_next = global_fonts = f;
2591     }
2592 }
2593
2594 void addGlobalLanguageDir(const char*dir)
2595 {
2596     msg("<notice> Adding %s to language pack directories", dir);
2597
2598     FILE*fi = 0;
2599     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2600     strcpy(config_file, dir);
2601     strcat(config_file, dirseparator());
2602     strcat(config_file, "add-to-xpdfrc");
2603
2604     fi = fopen(config_file, "rb");
2605     if(!fi) {
2606         msg("<error> Could not open %s", config_file);
2607         return;
2608     }
2609     globalParams->parseFile(new GString(config_file), fi);
2610     fclose(fi);
2611 }
2612
2613 void addGlobalFontDir(const char*dirname)
2614 {
2615 #ifdef HAVE_DIRENT_H
2616     DIR*dir = opendir(dirname);
2617     if(!dir) {
2618         msg("<warning> Couldn't open directory %s", dirname);
2619         return;
2620     }
2621     struct dirent*ent;
2622     int fonts = 0;
2623     while(1) {
2624         ent = readdir (dir);
2625         if (!ent) 
2626             break;
2627         int l;
2628         char*name = ent->d_name;
2629         char type = 0;
2630         if(!name) continue;
2631         l=strlen(name);
2632         if(l<4)
2633             continue;
2634         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2635             type=1;
2636         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2637             type=3;
2638         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2639             type=2;
2640         if(type) {
2641             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2642             strcpy(fontname, dirname);
2643             strcat(fontname, dirseparator());
2644             strcat(fontname, name);
2645             addGlobalFont(fontname);
2646             fonts++;
2647         }
2648     }
2649     msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2650     closedir(dir);
2651 #else
2652     msg("<warning> No dirent.h");
2653 #endif
2654 }
2655
2656 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2657                                       GfxColorSpace *blendingColorSpace,
2658                                       GBool isolated, GBool knockout,
2659                                       GBool forSoftMask)
2660 {
2661     const char*colormodename = "";
2662   
2663     if(blendingColorSpace) {
2664         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2665     }
2666     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);
2667     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);
2668     
2669     //states[statepos].createsoftmask |= forSoftMask;
2670     states[statepos].createsoftmask = forSoftMask;
2671     states[statepos].transparencygroup = !forSoftMask;
2672     states[statepos].isolated = isolated;
2673
2674     states[statepos].olddevice = this->device;
2675     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2676     dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2677
2678     gfxdevice_record_init(this->device);
2679     
2680     /*if(!forSoftMask) { ////???
2681         state->setFillOpacity(0.0);
2682     }*/
2683     dbgindent+=2;
2684 }
2685
2686 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2687 {
2688     dbgindent-=2;
2689     gfxdevice_t*r = this->device;
2690
2691     dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2692     
2693     this->device = states[statepos].olddevice;
2694     if(!this->device) {
2695         msg("<error> Invalid state nesting");
2696     }
2697     states[statepos].olddevice = 0;
2698
2699     gfxresult_t*recording = r->finish(r);
2700     
2701     dbg("                     forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2702     msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2703
2704     if(states[statepos].createsoftmask) {
2705         states[statepos-1].softmaskrecording = recording;
2706     } else {
2707         states[statepos-1].grouprecording = recording;
2708     }
2709     
2710     states[statepos].createsoftmask = 0;
2711     states[statepos].transparencygroup = 0;
2712     free(r);
2713 }
2714
2715 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2716 {
2717     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2718                                "colordodge","colorburn","hardlight","softlight","difference",
2719                                "exclusion","hue","saturation","color","luminosity"};
2720
2721     dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2722     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2723
2724     if(state->getBlendMode() == gfxBlendNormal)
2725         infofeature("transparency groups");
2726     else {
2727         char buffer[80];
2728         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2729         warnfeature(buffer, 0);
2730     }
2731
2732     gfxresult_t*grouprecording = states[statepos].grouprecording;
2733   
2734     int blendmode = state->getBlendMode();
2735     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2736         int alpha = (int)(state->getFillOpacity()*255);
2737         if(blendmode == gfxBlendMultiply && alpha>200)
2738             alpha = 128;
2739         gfxdevice_t ops;
2740         dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2741         gfxdevice_ops_init(&ops, this->device, alpha);
2742         gfxresult_record_replay(grouprecording, &ops);
2743         ops.finish(&ops);
2744     }
2745     grouprecording->destroy(grouprecording);
2746
2747     states[statepos].grouprecording = 0;
2748 }
2749
2750 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2751 {
2752     if(states[statepos].softmask) {
2753         /* shouldn't happen, but *does* happen */
2754         clearSoftMask(state);
2755     }
2756
2757     /* alpha = 1: retrieve mask values from alpha layer
2758        alpha = 0: retrieve mask values from luminance */
2759
2760     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2761             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2762     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2763             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2764     if(!alpha)
2765         infofeature("soft masks");
2766     else
2767         warnfeature("soft masks from alpha channel",0);
2768    
2769     if(states[statepos].olddevice) {
2770         msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2771         exit(1);
2772     }
2773     states[statepos].olddevice = this->device;
2774     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2775     gfxdevice_record_init(this->device);
2776
2777     dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2778     
2779     states[statepos].softmask = 1;
2780     states[statepos].softmask_alpha = alpha;
2781 }
2782
2783 static inline Guchar div255(int x) {
2784   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2785 }
2786
2787 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2788 {
2789     if(c < min) c = min;
2790     if(c > max) c = max;
2791     return c;
2792 }
2793
2794 void GFXOutputDev::clearSoftMask(GfxState *state)
2795 {
2796     if(!states[statepos].softmask)
2797         return;
2798     states[statepos].softmask = 0;
2799     dbg("clearSoftMask statepos=%d", statepos);
2800     msg("<verbose> clearSoftMask statepos=%d", statepos);
2801     
2802     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2803         msg("<error> Error in softmask/tgroup ordering");
2804         return;
2805     }
2806   
2807     gfxresult_t*mask = states[statepos].softmaskrecording;
2808     gfxresult_t*below = this->device->finish(this->device);free(this->device);
2809     this->device = states[statepos].olddevice;
2810
2811     /* get outline of all objects below the soft mask */
2812     gfxdevice_t uniondev;
2813     gfxdevice_union_init(&uniondev, 0);
2814     gfxresult_record_replay(below, &uniondev);
2815     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2816     uniondev.finish(&uniondev);
2817     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2818     gfxline_free(belowoutline);belowoutline=0;
2819 #if 0 
2820     this->device->startclip(this->device, belowoutline);
2821     gfxresult_record_replay(below, this->device);
2822     gfxresult_record_replay(mask, this->device);
2823     this->device->endclip(this->device);
2824 #endif
2825     
2826     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2827     if(width<=0 || height<=0)
2828         return;
2829
2830     gfxdevice_t belowrender;
2831     gfxdevice_render_init(&belowrender);
2832     if(states[statepos+1].isolated) {
2833         belowrender.setparameter(&belowrender, "fillwhite", "1");
2834     }
2835     belowrender.setparameter(&belowrender, "antialize", "2");
2836     belowrender.startpage(&belowrender, width, height);
2837     gfxresult_record_replay(below, &belowrender);
2838     belowrender.endpage(&belowrender);
2839     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2840     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2841     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2842
2843     gfxdevice_t maskrender;
2844     gfxdevice_render_init(&maskrender);
2845     maskrender.startpage(&maskrender, width, height);
2846     gfxresult_record_replay(mask, &maskrender);
2847     maskrender.endpage(&maskrender);
2848     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2849     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2850
2851     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2852         msg("<fatal> Internal error in mask drawing");
2853         return;
2854     }
2855
2856     int y,x;
2857     for(y=0;y<height;y++) {
2858         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2859         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2860         for(x=0;x<width;x++) {
2861             int alpha;
2862             if(states[statepos].softmask_alpha) {
2863                 alpha = l1->a;
2864             } else {
2865                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2866             }
2867
2868             l2->a = div255(alpha*l2->a);
2869
2870             /* DON'T premultiply alpha- this is done by fillbitmap,
2871                depending on the output device */
2872             //l2->r = div255(alpha*l2->r);
2873             //l2->g = div255(alpha*l2->g);
2874             //l2->b = div255(alpha*l2->b);
2875
2876             l1++;
2877             l2++;
2878         }
2879     }
2880     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2881
2882     gfxmatrix_t matrix;
2883     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2884     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2885
2886     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2887
2888     mask->destroy(mask);
2889     below->destroy(below);
2890     maskresult->destroy(maskresult);
2891     belowresult->destroy(belowresult);
2892     states[statepos].softmaskrecording = 0;
2893 }
2894   
2895 //class MemCheck
2896 //{
2897 //    public: ~MemCheck()
2898 //    {
2899 //        delete globalParams;globalParams=0;
2900 //        Object::memCheck(stderr);
2901 //        gMemReport(stderr);
2902 //    }
2903 //} myMemCheck;
2904