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