added static font files
[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     device->endpage(device);
796 }
797
798 void GFXOutputDev::finish()
799 {
800     if(outer_clip_box) {
801         if(device) {
802             device->endclip(device);
803         }
804         outer_clip_box = 0;
805     }
806 }
807
808 GFXOutputDev::~GFXOutputDev() 
809 {
810     finish();
811
812     if(this->pages) {
813         free(this->pages); this->pages = 0;
814     }
815
816     fontlist_t*l = this->fontlist;
817     while(l) {
818         fontlist_t*next = l->next;
819         l->next = 0;
820         gfxfont_free(l->font);
821         free(l->filename);l->filename=0;
822         free(l);
823         l = next;
824     }
825     this->fontlist = 0;
826 };
827 GBool GFXOutputDev::upsideDown() 
828 {
829     return gTrue;
830 };
831 GBool GFXOutputDev::useDrawChar() 
832 {
833     return gTrue;
834 }
835
836 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
837                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
838
839 #define RENDER_FILL 0
840 #define RENDER_STROKE 1
841 #define RENDER_FILLSTROKE 2
842 #define RENDER_INVISIBLE 3
843 #define RENDER_CLIP 4
844
845 static char tmp_printstr[4096];
846 char* makeStringPrintable(char*str)
847 {
848     int len = strlen(str);
849     int dots = 0;
850     if(len>=80) {
851         len = 80;
852         dots = 1;
853     }
854     int t;
855     for(t=0;t<len;t++) {
856         char c = str[t];
857         if(c<32 || c>124) {
858             c = '.';
859         }
860         tmp_printstr[t] = c;
861     }
862     if(dots) {
863         tmp_printstr[len++] = '.';
864         tmp_printstr[len++] = '.';
865         tmp_printstr[len++] = '.';
866     }
867     tmp_printstr[len] = 0;
868     return tmp_printstr;
869 }
870
871
872 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
873 {
874     char*uniname = 0;
875     if(u>0) {
876         int t;
877         /* find out char name from unicode index 
878            TODO: should be precomputed
879          */
880         for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
881             if(nameToUnicodeTab[t].u == u) {
882                 uniname = nameToUnicodeTab[t].name;
883                 break;
884             }
885         }
886     }
887
888     if(charname) {
889         int t;
890         for(t=0;t<font->num_glyphs;t++) {
891             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
892                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
893                 return t;
894             }
895         }
896         /* if we didn't find the character, maybe
897            we can find the capitalized version */
898         for(t=0;t<font->num_glyphs;t++) {
899             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
900                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
901                 return t;
902             }
903         }
904     }
905
906     if(uniname) {
907         int t;
908         for(t=0;t<font->num_glyphs;t++) {
909             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
910                 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
911                 return t;
912             }
913         }
914         /* if we didn't find the character, maybe
915            we can find the capitalized version */
916         for(t=0;t<font->num_glyphs;t++) {
917             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
918                 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
919                 return t;
920             }
921         }
922     }
923
924     /* try to use the unicode id */
925     if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
926         msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
927         return font->unicode2glyph[u];
928     }
929
930     if(charnr>=0 && charnr<font->num_glyphs) {
931         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
932         return charnr;
933     }
934     
935     return -1;
936 }
937
938
939 void GFXOutputDev::beginString(GfxState *state, GString *s) 
940
941     int render = state->getRender();
942     if(current_text_stroke) {
943         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
944     }
945
946     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
947     double m11,m21,m12,m22;
948 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
949     state->getFontTransMat(&m11, &m12, &m21, &m22);
950     m11 *= state->getHorizScaling();
951     m21 *= state->getHorizScaling();
952
953     this->current_font_matrix.m00 = m11 / 1024.0;
954     this->current_font_matrix.m01 = m12 / 1024.0;
955     this->current_font_matrix.m10 = -m21 / 1024.0;
956     this->current_font_matrix.m11 = -m22 / 1024.0;
957     this->current_font_matrix.tx = 0;
958     this->current_font_matrix.ty = 0;
959
960     gfxmatrix_t m = this->current_font_matrix;
961
962     states[statepos].textRender = render;
963 }
964
965 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
966                         double dx, double dy,
967                         double originX, double originY,
968                         CharCode c, int nBytes, Unicode *_u, int uLen)
969 {
970     int render = state->getRender();
971     // check for invisible text -- this is used by Acrobat Capture
972     if (render == 3) {
973         msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
974         return;
975     }
976
977     if(states[statepos].textRender != render)
978         msg("<error> Internal error: drawChar.render!=beginString.render");
979
980     gfxcolor_t col = getFillColor(state);
981
982     Gushort *CIDToGIDMap = 0;
983     GfxFont*font = state->getFont();
984
985     if(font->getType() == fontType3) {
986         /* type 3 chars are passed as graphics */
987         msg("<debug> type3 char at %f/%f", x, y);
988         return;
989     }
990     
991     Unicode u=0;
992     char*name=0;
993
994     if(uLen)
995         u = _u[0];
996
997     if(font->isCIDFont()) {
998         GfxCIDFont*cfont = (GfxCIDFont*)font;
999
1000         if(font->getType() == fontCIDType2)
1001             CIDToGIDMap = cfont->getCIDToGID();
1002     } else {
1003         Gfx8BitFont*font8;
1004         font8 = (Gfx8BitFont*)font;
1005         char**enc=font8->getEncoding();
1006         name = enc[c];
1007     }
1008     if (CIDToGIDMap) {
1009         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);
1010         c = CIDToGIDMap[c];
1011     } else {
1012         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);
1013     }
1014
1015     int charid = -1;
1016    
1017     if(uLen<=1) {
1018         charid = getGfxCharID(current_gfxfont, c, name, u);
1019     } else {
1020         charid = getGfxCharID(current_gfxfont, c, name, -1);
1021
1022         if(charid < 0) {
1023             /* multiple unicodes- should usually map to a ligature.
1024                if the ligature doesn't exist, we need to draw
1025                the characters one-by-one. */
1026             int t;
1027             msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1028             for(t=0;t<uLen;t++) {
1029                 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1030             }
1031             return;
1032         }
1033     }
1034     if(charid<0) {
1035         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1036                 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1037         return;
1038     }
1039
1040     gfxmatrix_t m = this->current_font_matrix;
1041     state->transform(x, y, &m.tx, &m.ty);
1042     m.tx += user_movex + clipmovex;
1043     m.ty += user_movey + clipmovey;
1044
1045     if(render == RENDER_FILL) {
1046         device->drawchar(device, current_gfxfont, charid, &col, &m);
1047     } else {
1048         msg("<debug> Drawing glyph %d as shape", charid);
1049         if(!textmodeinfo) {
1050             msg("<notice> Some texts will be rendered as shape");
1051             textmodeinfo = 1;
1052         }
1053         gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1054         gfxline_t*tglyph = gfxline_clone(glyph);
1055         gfxline_transform(tglyph, &m);
1056         if((render&3) != RENDER_INVISIBLE) {
1057             gfxline_t*add = gfxline_clone(tglyph);
1058             current_text_stroke = gfxline_append(current_text_stroke, add);
1059         }
1060         if(render&RENDER_CLIP) {
1061             gfxline_t*add = gfxline_clone(tglyph);
1062             current_text_clip = gfxline_append(current_text_clip, add);
1063         }
1064         gfxline_free(tglyph);
1065     }
1066 }
1067
1068 void GFXOutputDev::endString(GfxState *state) 
1069
1070     int render = state->getRender();
1071     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1072     if(states[statepos].textRender != render)
1073         msg("<error> Internal error: drawChar.render!=beginString.render");
1074     
1075     if(current_text_stroke) {
1076         /* fillstroke and stroke text rendering objects we can process right
1077            now (as there may be texts of other rendering modes in this
1078            text object)- clipping objects have to wait until endTextObject,
1079            however */
1080         device->setparameter(device, "mark","TXT");
1081         if((render&3) == RENDER_FILL) {
1082             fillGfxLine(state, current_text_stroke);
1083             gfxline_free(current_text_stroke);
1084             current_text_stroke = 0;
1085         } else if((render&3) == RENDER_FILLSTROKE) {
1086             fillGfxLine(state, current_text_stroke);
1087             strokeGfxline(state, current_text_stroke);
1088             gfxline_free(current_text_stroke);
1089             current_text_stroke = 0;
1090         } else if((render&3) == RENDER_STROKE) {
1091             strokeGfxline(state, current_text_stroke);
1092             gfxline_free(current_text_stroke);
1093             current_text_stroke = 0;
1094         }
1095         device->setparameter(device, "mark","");
1096     }
1097 }    
1098
1099 void GFXOutputDev::endTextObject(GfxState *state)
1100 {
1101     int render = state->getRender();
1102     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1103     if(states[statepos].textRender != render)
1104         msg("<error> Internal error: drawChar.render!=beginString.render");
1105     
1106     if(current_text_clip) {
1107         device->setparameter(device, "mark","TXT");
1108         clipToGfxLine(state, current_text_clip);
1109         device->setparameter(device, "mark","");
1110         gfxline_free(current_text_clip);
1111         current_text_clip = 0;
1112     }
1113 }
1114
1115 /* the logic seems to be as following:
1116    first, beginType3Char is called, with the charcode and the coordinates.
1117    if this function returns true, it already knew about the char and has now drawn it.
1118    if the function returns false, it's a new char, and type3D1 is called with some parameters-
1119    the all draw operations until endType3Char are part of the char (which in this moment is
1120    at the position first passed to beginType3Char). the char ends with endType3Char.
1121
1122    The drawing operations between beginType3Char and endType3Char are somewhat different to
1123    the normal ones. For example, the fillcolor equals the stroke color.
1124 */
1125
1126 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1127 {
1128     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1129     type3active = 1;
1130     /* the character itself is going to be passed using the draw functions */
1131     return gFalse; /* gTrue= is_in_cache? */
1132 }
1133
1134 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1135     msg("<debug> type3D0 width=%f height=%f", wx, wy);
1136 }
1137 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1138     msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1139             llx,lly,urx,ury);
1140 }
1141
1142 void GFXOutputDev::endType3Char(GfxState *state)
1143 {
1144     type3active = 0;
1145     msg("<debug> endType3Char");
1146 }
1147
1148 void GFXOutputDev::startFrame(int width, int height) 
1149 {
1150     if(outer_clip_box) {
1151         device->endclip(device);
1152         outer_clip_box = 0;
1153     }
1154
1155     device->startpage(device, width, height);
1156     this->width = width;
1157     this->height = height;
1158 }
1159
1160 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1161 {
1162     this->currentpage = pageNum;
1163     double x1,y1,x2,y2;
1164     int rot = doc->getPageRotate(1);
1165     gfxcolor_t white;
1166     laststate = state;
1167     gfxline_t clippath[5];
1168
1169     white.r = white.g = white.b = white.a = 255;
1170
1171     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1172     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1173     Use CropBox, not MediaBox, as page size
1174     */
1175     
1176     /*x1 = crop_x1;
1177     y1 = crop_y1;
1178     x2 = crop_x2;
1179     y2 = crop_y2;*/
1180     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1181     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1182
1183     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1184     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1185
1186     this->clipmovex = -(int)x1;
1187     this->clipmovey = -(int)y1;
1188     
1189     /* apply user clip box */
1190     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1191         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1192         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1193         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1194         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1195         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1196     }
1197
1198     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1199     
1200     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);
1201     if(rot!=0)
1202         msg("<verbose> page is rotated %d degrees\n", rot);
1203
1204     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1205     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1206     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1207     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1208     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1209     device->startclip(device, clippath); outer_clip_box = 1;
1210     device->fill(device, clippath, &white);
1211 }
1212
1213
1214 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1215 {
1216     double x1, y1, x2, y2, w;
1217     gfxline_t points[5];
1218     int x, y;
1219     
1220     msg("<debug> drawlink\n");
1221
1222     link->getRect(&x1, &y1, &x2, &y2);
1223     cvtUserToDev(x1, y1, &x, &y);
1224     points[0].type = gfx_moveTo;
1225     points[0].x = points[4].x = x + user_movex + clipmovex;
1226     points[0].y = points[4].y = y + user_movey + clipmovey;
1227     points[0].next = &points[1];
1228     cvtUserToDev(x2, y1, &x, &y);
1229     points[1].type = gfx_lineTo;
1230     points[1].x = x + user_movex + clipmovex;
1231     points[1].y = y + user_movey + clipmovey;
1232     points[1].next = &points[2];
1233     cvtUserToDev(x2, y2, &x, &y);
1234     points[2].type = gfx_lineTo;
1235     points[2].x = x + user_movex + clipmovex;
1236     points[2].y = y + user_movey + clipmovey;
1237     points[2].next = &points[3];
1238     cvtUserToDev(x1, y2, &x, &y);
1239     points[3].type = gfx_lineTo;
1240     points[3].x = x + user_movex + clipmovex;
1241     points[3].y = y + user_movey + clipmovey;
1242     points[3].next = &points[4];
1243     cvtUserToDev(x1, y1, &x, &y);
1244     points[4].type = gfx_lineTo;
1245     points[4].x = x + user_movex + clipmovex;
1246     points[4].y = y + user_movey + clipmovey;
1247     points[4].next = 0;
1248     
1249     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1250             points[0].x, points[0].y,
1251             points[1].x, points[1].y,
1252             points[2].x, points[2].y,
1253             points[3].x, points[3].y); 
1254
1255     LinkAction*action=link->getAction();
1256     char buf[128];
1257     char*s = 0;
1258     char*type = "-?-";
1259     char*named = 0;
1260     int page = -1;
1261     msg("<trace> drawlink action=%d\n", action->getKind());
1262     switch(action->getKind())
1263     {
1264         case actionGoTo: {
1265             type = "GoTo";
1266             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1267             LinkDest *dest=NULL;
1268             if (ha->getDest()==NULL) 
1269                 dest=catalog->findDest(ha->getNamedDest());
1270             else dest=ha->getDest();
1271             if (dest){ 
1272               if (dest->isPageRef()){
1273                 Ref pageref=dest->getPageRef();
1274                 page=catalog->findPage(pageref.num,pageref.gen);
1275               }
1276               else  page=dest->getPageNum();
1277               sprintf(buf, "%d", page);
1278               s = strdup(buf);
1279             }
1280         }
1281         break;
1282         case actionGoToR: {
1283             type = "GoToR";
1284             LinkGoToR*l = (LinkGoToR*)action;
1285             GString*g = l->getFileName();
1286             if(g)
1287              s = strdup(g->getCString());
1288             if(!s) {
1289                 /* if the GoToR link has no filename, then
1290                    try to find a refernce in the *local*
1291                    file */
1292                 GString*g = l->getNamedDest();
1293                 if(g)
1294                  s = strdup(g->getCString());
1295             }
1296         }
1297         break;
1298         case actionNamed: {
1299             type = "Named";
1300             LinkNamed*l = (LinkNamed*)action;
1301             GString*name = l->getName();
1302             if(name) {
1303                 s = strdup(name->lowerCase()->getCString());
1304                 named = name->getCString();
1305                 if(!strchr(s,':')) 
1306                 {
1307                     if(strstr(s, "next") || strstr(s, "forward"))
1308                     {
1309                         page = currentpage + 1;
1310                     }
1311                     else if(strstr(s, "prev") || strstr(s, "back"))
1312                     {
1313                         page = currentpage - 1;
1314                     }
1315                     else if(strstr(s, "last") || strstr(s, "end"))
1316                     {
1317                         if(pages && pagepos>0)
1318                             page = pages[pagepos-1];
1319                     }
1320                     else if(strstr(s, "first") || strstr(s, "top"))
1321                     {
1322                         page = 1;
1323                     }
1324                 }
1325             }
1326         }
1327         break;
1328         case actionLaunch: {
1329             type = "Launch";
1330             LinkLaunch*l = (LinkLaunch*)action;
1331             GString * str = new GString(l->getFileName());
1332             GString * params = l->getParams();
1333             if(params)
1334                 str->append(params);
1335             s = strdup(str->getCString());
1336             delete str;
1337         }
1338         break;
1339         case actionURI: {
1340             char*url = 0;
1341             type = "URI";
1342             LinkURI*l = (LinkURI*)action;
1343             GString*g = l->getURI();
1344             if(g) {
1345              url = g->getCString();
1346              s = strdup(url);
1347             }
1348         }
1349         break;
1350         case actionUnknown: {
1351             type = "Unknown";
1352             LinkUnknown*l = (LinkUnknown*)action;
1353             s = strdup("");
1354         }
1355         break;
1356         default: {
1357             msg("<error> Unknown link type!\n");
1358             break;
1359         }
1360     }
1361
1362     if(!s) s = strdup("-?-");
1363     
1364     msg("<trace> drawlink s=%s\n", s);
1365
1366     if(!linkinfo && (page || s))
1367     {
1368         msg("<notice> File contains links");
1369         linkinfo = 1;
1370     }
1371     
1372     if(page>0)
1373     {
1374         int t;
1375         int lpage = -1;
1376         for(t=1;t<=pagepos;t++) {
1377             if(pages[t]==page) {
1378                 lpage = t;
1379                 break;
1380             }
1381         }
1382         if(lpage<0) {
1383             lpage = page;
1384         }
1385         char buf[80];
1386         sprintf(buf, "page%d", lpage);
1387         device->drawlink(device, points, buf);
1388     }
1389     else if(s)
1390     {
1391         device->drawlink(device, points, s);
1392     }
1393
1394     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1395     free(s);s=0;
1396 }
1397
1398 void GFXOutputDev::saveState(GfxState *state) {
1399     dbg("saveState");dbgindent+=2;
1400
1401     msg("<trace> saveState\n");
1402     updateAll(state);
1403     if(statepos>=64) {
1404       msg("<error> Too many nested states in pdf.");
1405       return;
1406     }
1407     statepos ++;
1408     states[statepos].textRender = states[statepos-1].textRender;
1409     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1410     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1411     states[statepos].clipping = 0;
1412 };
1413
1414 void GFXOutputDev::restoreState(GfxState *state) {
1415   dbgindent-=2; dbg("restoreState");
1416
1417   if(statepos==0) {
1418       msg("<error> Invalid restoreState");
1419       return;
1420   }
1421   msg("<trace> restoreState");
1422   if(states[statepos].softmask) {
1423       clearSoftMask(state);
1424   }
1425   updateAll(state);
1426   while(states[statepos].clipping) {
1427       device->endclip(device);
1428       states[statepos].clipping--;
1429   }
1430   statepos--;
1431 }
1432
1433 char* writeOutStdFont(fontentry* f)
1434 {
1435     FILE*fi;
1436     char namebuf1[512];
1437     char namebuf2[512];
1438     char* tmpFileName = mktmpname(namebuf1);
1439
1440     sprintf(namebuf2, "%s.afm", tmpFileName);
1441     fi = fopen(namebuf2, "wb");
1442     if(!fi)
1443         return 0;
1444     fwrite(f->afm, 1, f->afmlen, fi);
1445     fclose(fi);
1446
1447     sprintf(namebuf2, "%s.pfb", tmpFileName);
1448     fi = fopen(namebuf2, "wb");
1449     if(!fi)
1450         return 0;
1451     fwrite(f->pfb, 1, f->pfblen, fi);
1452     fclose(fi);
1453
1454     return strdup(namebuf2);
1455 }
1456
1457 char* GFXOutputDev::searchFont(char*name) 
1458 {       
1459     int i;
1460     char*filename=0;
1461         
1462     msg("<verbose> SearchFont(%s)", name);
1463
1464     /* see if it is a pdf standard font */
1465     for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++) 
1466     {
1467         if(!strcmp(name, pdf2t1map[i].pdffont))
1468         {
1469             if(!pdf2t1map[i].fullfilename) {
1470                 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1471                 if(!pdf2t1map[i].fullfilename) {
1472                     msg("<error> Couldn't save default font- is the Temp Directory writable?");
1473                 } else {
1474                     msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1475                 }
1476             }
1477             return strdup(pdf2t1map[i].fullfilename);
1478         }
1479     }
1480     /* else look in all font files */
1481     for(i=0;i<fontnum;i++) 
1482     {
1483         if(strstr(fonts[i].filename, name)) {
1484             return strdup(fonts[i].filename);
1485         }
1486     }
1487     return 0;
1488 }
1489
1490 void GFXOutputDev::updateLineWidth(GfxState *state)
1491 {
1492     double width = state->getTransformedLineWidth();
1493     //swfoutput_setlinewidth(&device, width);
1494 }
1495
1496 void GFXOutputDev::updateLineCap(GfxState *state)
1497 {
1498     int c = state->getLineCap();
1499 }
1500
1501 void GFXOutputDev::updateLineJoin(GfxState *state)
1502 {
1503     int j = state->getLineJoin();
1504 }
1505
1506 void GFXOutputDev::updateFillColor(GfxState *state) 
1507 {
1508     GfxRGB rgb;
1509     double opaq = state->getFillOpacity();
1510     state->getFillRGB(&rgb);
1511 }
1512 void GFXOutputDev::updateFillOpacity(GfxState *state)
1513 {
1514     GfxRGB rgb;
1515     double opaq = state->getFillOpacity();
1516     state->getFillRGB(&rgb);
1517     dbg("update fillopaq %f", opaq);
1518 }
1519 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1520 {
1521     double opaq = state->getFillOpacity();
1522     dbg("update strokeopaq %f", opaq);
1523 }
1524 void GFXOutputDev::updateFillOverprint(GfxState *state)
1525 {
1526     double opaq = state->getFillOverprint();
1527     dbg("update filloverprint %f", opaq);
1528 }
1529 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1530 {
1531     double opaq = state->getStrokeOverprint();
1532     dbg("update strokeoverprint %f", opaq);
1533 }
1534 void GFXOutputDev::updateTransfer(GfxState *state)
1535 {
1536     dbg("update transfer");
1537 }
1538
1539
1540 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1541 {
1542     GfxRGB rgb;
1543     double opaq = state->getStrokeOpacity();
1544     state->getStrokeRGB(&rgb);
1545 }
1546
1547 void FoFiWrite(void *stream, char *data, int len)
1548 {
1549    int ret = fwrite(data, len, 1, (FILE*)stream);
1550 }
1551
1552 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1553 {
1554     char*tmpFileName = NULL;
1555     FILE *f;
1556     int c;
1557     char *fontBuf;
1558     int fontLen;
1559     Ref embRef;
1560     Object refObj, strObj;
1561     char namebuf[512];
1562     tmpFileName = mktmpname(namebuf);
1563     int ret;
1564
1565     ret = font->getEmbeddedFontID(&embRef);
1566     if(!ret) {
1567         msg("<verbose> Didn't get embedded font id");
1568         /* not embedded- the caller should now search the font
1569            directories for this font */
1570         return 0;
1571     }
1572
1573     f = fopen(tmpFileName, "wb");
1574     if (!f) {
1575       msg("<error> Couldn't create temporary Type 1 font file");
1576         return 0;
1577     }
1578
1579     /*if(font->isCIDFont()) {
1580         GfxCIDFont* cidFont = (GfxCIDFont *)font;
1581         GString c = cidFont->getCollection();
1582         msg("<notice> Collection: %s", c.getCString());
1583     }*/
1584
1585     //if (font->getType() == fontType1C) {
1586     if (0) { //font->getType() == fontType1C) {
1587       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1588         fclose(f);
1589         msg("<error> Couldn't read embedded font file");
1590         return 0;
1591       }
1592       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1593       if(!cvt) return 0;
1594       cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1595       //cvt->convertToCIDType0("test", f);
1596       //cvt->convertToType0("test", f);
1597       delete cvt;
1598       gfree(fontBuf);
1599     } else if(font->getType() == fontTrueType) {
1600       msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1601       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1602         fclose(f);
1603         msg("<error> Couldn't read embedded font file");
1604         return 0;
1605       }
1606       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1607       cvt->writeTTF(FoFiWrite, f);
1608       delete cvt;
1609       gfree(fontBuf);
1610     } else {
1611       font->getEmbeddedFontID(&embRef);
1612       refObj.initRef(embRef.num, embRef.gen);
1613       refObj.fetch(ref, &strObj);
1614       refObj.free();
1615       strObj.streamReset();
1616       int f4[4];
1617       char f4c[4];
1618       int t;
1619       for(t=0;t<4;t++) {
1620           f4[t] = strObj.streamGetChar();
1621           f4c[t] = (char)f4[t];
1622           if(f4[t] == EOF)
1623               break;
1624       }
1625       if(t==4) {
1626           if(!strncmp(f4c, "true", 4)) {
1627               /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1628                  Change this on the fly */
1629               f4[0] = f4[2] = f4[3] = 0;
1630               f4[1] = 1;
1631           }
1632           fputc(f4[0], f);
1633           fputc(f4[1], f);
1634           fputc(f4[2], f);
1635           fputc(f4[3], f);
1636
1637           while ((c = strObj.streamGetChar()) != EOF) {
1638             fputc(c, f);
1639           }
1640       }
1641       strObj.streamClose();
1642       strObj.free();
1643     }
1644     fclose(f);
1645
1646     return strdup(tmpFileName);
1647 }
1648     
1649 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1650 {
1651     char*name = getFontName(gfxFont);
1652     char*fontname = 0;
1653     char*filename = 0;
1654
1655     if(!this->config_use_fontconfig)
1656         return 0;
1657     
1658 #ifdef HAVE_FONTCONFIG
1659     FcPattern *pattern, *match;
1660     FcResult result;
1661     FcChar8 *v;
1662
1663     static int fcinitcalled = false; 
1664         
1665     msg("<debug> searchForSuitableFont(%s)", name);
1666     
1667     // call init ony once
1668     if (!fcinitcalled) {
1669         msg("<debug> Initializing FontConfig...");
1670         fcinitcalled = true;
1671         if(!FcInit()) {
1672             msg("<debug> FontConfig Initialization failed. Disabling.");
1673             config_use_fontconfig = 0;
1674             return 0;
1675         }
1676         msg("<debug> ...initialized FontConfig");
1677     }
1678    
1679     msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1680     pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1681     if (gfxFont->isItalic()) // check for italic
1682         msg("<debug> FontConfig: Adding Italic Slant");
1683         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1684     if (gfxFont->isBold()) // check for bold
1685         msg("<debug> FontConfig: Adding Bold Weight");
1686         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1687
1688     msg("<debug> FontConfig: Try to match...");
1689     // configure and match using the original font name 
1690     FcConfigSubstitute(0, pattern, FcMatchPattern); 
1691     FcDefaultSubstitute(pattern);
1692     match = FcFontMatch(0, pattern, &result);
1693     
1694     if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1695         msg("<debug> FontConfig: family=%s", (char*)v);
1696         // if we get an exact match
1697         if (strcmp((char *)v, name) == 0) {
1698             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1699                 filename = strdup((char*)v); // mem leak
1700                 char *nfn = strrchr(filename, '/');
1701                 if(nfn) fontname = strdup(nfn+1);
1702                 else    fontname = filename;
1703             }
1704             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1705         } else {
1706             // initialize patterns
1707             FcPatternDestroy(pattern);
1708             FcPatternDestroy(match);
1709
1710             // now match against serif etc.
1711             if (gfxFont->isSerif()) {
1712                 msg("<debug> FontConfig: Create Serif Family Pattern");
1713                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1714             } else if (gfxFont->isFixedWidth()) {
1715                 msg("<debug> FontConfig: Create Monospace Family Pattern");
1716                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1717             } else {
1718                 msg("<debug> FontConfig: Create Sans Family Pattern");
1719                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1720             }
1721
1722             // check for italic
1723             if (gfxFont->isItalic()) {
1724                 msg("<debug> FontConfig: Adding Italic Slant");
1725                 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1726             }
1727             // check for bold
1728             if (gfxFont->isBold()) {
1729                 msg("<debug> FontConfig: Adding Bold Weight");
1730                 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1731             }
1732
1733             msg("<debug> FontConfig: Try to match... (2)");
1734             // configure and match using serif etc
1735             FcConfigSubstitute (0, pattern, FcMatchPattern);
1736             FcDefaultSubstitute (pattern);
1737             match = FcFontMatch (0, pattern, &result);
1738             
1739             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1740                 filename = strdup((char*)v); // mem leak
1741                 char *nfn = strrchr(filename, '/');
1742                 if(nfn) fontname = strdup(nfn+1);
1743                 else    fontname = filename;
1744             }
1745             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1746         }        
1747     }
1748
1749     //printf("FONTCONFIG: pattern");
1750     //FcPatternPrint(pattern);
1751     //printf("FONTCONFIG: match");
1752     //FcPatternPrint(match);
1753  
1754     FcPatternDestroy(pattern);
1755     FcPatternDestroy(match);
1756
1757     pdfswf_addfont(filename);
1758     return fontname;
1759 #else
1760     return 0;
1761 #endif
1762 }
1763
1764 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1765 {
1766     char*fontname = 0, *filename = 0;
1767     msg("<notice> substituteFont(%s)", oldname);
1768
1769     if(!(fontname = searchForSuitableFont(gfxFont))) {
1770         fontname = "Times-Roman";
1771     }
1772     filename = searchFont(fontname);
1773     if(!filename) {
1774         msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1775         return 0;
1776     }
1777
1778     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1779         msg("<fatal> Too many fonts in file.");
1780         exit(1);
1781     }
1782     if(oldname) {
1783         substitutesource[substitutepos] = strdup(oldname); //mem leak
1784         substitutetarget[substitutepos] = fontname;
1785         msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1786         substitutepos ++;
1787     }
1788     return strdup(filename); //mem leak
1789 }
1790
1791 void unlinkfont(char* filename)
1792 {
1793     int l;
1794     if(!filename)
1795         return;
1796     l=strlen(filename);
1797     unlink(filename);
1798     if(!strncmp(&filename[l-4],".afm",4)) {
1799         memcpy(&filename[l-4],".pfb",4);
1800         unlink(filename);
1801         memcpy(&filename[l-4],".pfa",4);
1802         unlink(filename);
1803         memcpy(&filename[l-4],".afm",4);
1804         return;
1805     } else 
1806     if(!strncmp(&filename[l-4],".pfa",4)) {
1807         memcpy(&filename[l-4],".afm",4);
1808         unlink(filename);
1809         memcpy(&filename[l-4],".pfa",4);
1810         return;
1811     } else 
1812     if(!strncmp(&filename[l-4],".pfb",4)) {
1813         memcpy(&filename[l-4],".afm",4);
1814         unlink(filename);
1815         memcpy(&filename[l-4],".pfb",4);
1816         return;
1817     }
1818 }
1819
1820 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1821 {
1822     this->doc = doc;
1823     this->xref = xref;
1824 }
1825
1826 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1827 {
1828     gfxfont_t*font = 0;
1829     fontlist_t*last=0,*l = this->fontlist;
1830
1831     if(!id)
1832         msg("<error> Internal Error: FontID is null");
1833
1834     /* TODO: should this be part of the state? */
1835     while(l) {
1836         last = l;
1837         if(!strcmp(l->font->id, id)) {
1838             current_gfxfont = l->font;
1839             font = l->font;
1840             device->addfont(device, current_gfxfont);
1841             return 1;
1842         }
1843         l = l->next;
1844     }
1845     if(!filename) return 0;
1846
1847     /* A font size of e.g. 9 means the font will be scaled down by
1848        1024 and scaled up by 9. So to have a maximum error of 1/20px,
1849        we have to divide 0.05 by (fontsize/1024)
1850      */
1851     double quality = (1024 * 0.05) / maxSize;
1852    
1853     msg("<verbose> Loading %s...", filename);
1854     font = gfxfont_load(id, filename, quality);
1855     if(!font) {
1856         msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1857         return 0;
1858     }
1859     msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1860
1861     l = new fontlist_t;
1862     l->font = font;
1863     l->filename = strdup(filename);
1864     l->next = 0;
1865     current_gfxfont = l->font;
1866     if(last) {
1867         last->next = l;
1868     } else {
1869         this->fontlist = l;
1870     }
1871     device->addfont(device, current_gfxfont);
1872     return 1;
1873 }
1874
1875 void GFXOutputDev::updateFont(GfxState *state) 
1876 {
1877     GfxFont*gfxFont = state->getFont();
1878       
1879     if (!gfxFont) {
1880         return;
1881     }  
1882     
1883     char * fontid = getFontID(gfxFont);
1884     char * fontname = getFontName(gfxFont);
1885
1886     double maxSize = 1.0;
1887
1888     if(this->info) {
1889         maxSize = this->info->getMaximumFontSize(fontid);
1890     }
1891     
1892     int t;
1893     /* first, look if we substituted this font before-
1894        this way, we don't initialize the T1 Fonts
1895        too often */
1896     for(t=0;t<substitutepos;t++) {
1897         if(!strcmp(fontid, substitutesource[t])) {
1898             free(fontid);fontid=0;
1899             fontid = strdup(substitutetarget[t]);
1900             break;
1901         }
1902     }
1903
1904     /* second, see if this is a font which was used before-
1905        if so, we are done */
1906     if(setGfxFont(fontid, fontname, 0, 0)) {
1907         free(fontid);
1908         free(fontname);
1909         return;
1910     }
1911 /*    if(swfoutput_queryfont(&device, fontid))
1912         swfoutput_setfont(&device, fontid, 0);
1913         
1914         msg("<debug> updateFont(%s) [cached]", fontid);
1915         return;
1916     }*/
1917
1918     // look for Type 3 font
1919     if (gfxFont->getType() == fontType3) {
1920         if(!type3Warning) {
1921             type3Warning = gTrue;
1922             showFontError(gfxFont, 2);
1923         }
1924         free(fontid);
1925         free(fontname);
1926         return;
1927     }
1928
1929     /* now either load the font, or find a substitution */
1930
1931     Ref embRef;
1932     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1933
1934     char*fileName = 0;
1935     int del = 0;
1936     if(embedded &&
1937        (gfxFont->getType() == fontType1 ||
1938         gfxFont->getType() == fontType1C ||
1939         gfxFont->getType() == fontCIDType0C ||
1940         gfxFont->getType() == fontTrueType ||
1941         gfxFont->getType() == fontCIDType2
1942        ))
1943     {
1944       fileName = writeEmbeddedFontToFile(xref, gfxFont);
1945       if(!fileName) showFontError(gfxFont,0);
1946       else del = 1;
1947     } else {
1948       fileName = searchFont(fontname);
1949       if(!fileName) showFontError(gfxFont,0);
1950     }
1951     if(!fileName) {
1952         char * fontname = getFontName(gfxFont);
1953         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1954         
1955         if(lastfontdir)
1956             msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1957         else
1958             msg("<warning> Try specifying one or more font directories");
1959
1960         fileName = substituteFont(gfxFont, fontid);
1961         if(!fileName)
1962             exit(1);
1963         if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1964         msg("<notice> Font is now %s (%s)", fontid, fileName);
1965     }
1966
1967     if(!fileName) {
1968         msg("<error> Couldn't set font %s\n", fontid);
1969         free(fontid);
1970         free(fontname);
1971         return;
1972     }
1973         
1974     msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1975     dumpFontInfo("<verbose>", gfxFont);
1976
1977     //swfoutput_setfont(&device, fontid, fileName);
1978     
1979     if(!setGfxFont(fontid, fontname, 0, 0)) {
1980         setGfxFont(fontid, fontname, fileName, maxSize);
1981     }
1982    
1983     if(fileName && del)
1984         unlinkfont(fileName);
1985
1986     if(fileName)
1987         free(fileName);
1988     free(fontid);
1989     free(fontname);
1990
1991     msg("<verbose> |");
1992 }
1993
1994 #define SQR(x) ((x)*(x))
1995
1996 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1997 {
1998     if((newwidth<2 || newheight<2) ||
1999        (width<=newwidth || height<=newheight))
2000         return 0;
2001     unsigned char*newdata;
2002     int x,y;
2003     newdata= (unsigned char*)malloc(newwidth*newheight);
2004     int t;
2005     double fx = (double)(width)/newwidth;
2006     double fy = (double)(height)/newheight;
2007     double px = 0;
2008     int blocksize = (int)(8192/(fx*fy));
2009     int r = 8192*256/palettesize;
2010     for(x=0;x<newwidth;x++) {
2011         double ex = px + fx;
2012         int fromx = (int)px;
2013         int tox = (int)ex;
2014         int xweight1 = (int)(((fromx+1)-px)*256);
2015         int xweight2 = (int)((ex-tox)*256);
2016         double py =0;
2017         for(y=0;y<newheight;y++) {
2018             double ey = py + fy;
2019             int fromy = (int)py;
2020             int toy = (int)ey;
2021             int yweight1 = (int)(((fromy+1)-py)*256);
2022             int yweight2 = (int)((ey-toy)*256);
2023             int a = 0;
2024             int xx,yy;
2025             for(xx=fromx;xx<=tox;xx++)
2026             for(yy=fromy;yy<=toy;yy++) {
2027                 int b = 1-data[width*yy+xx];
2028                 int weight=256;
2029                 if(xx==fromx) weight = (weight*xweight1)/256;
2030                 if(xx==tox) weight = (weight*xweight2)/256;
2031                 if(yy==fromy) weight = (weight*yweight1)/256;
2032                 if(yy==toy) weight = (weight*yweight2)/256;
2033                 a+=b*weight;
2034             }
2035             //if(a) a=(palettesize-1)*r/blocksize;
2036             newdata[y*newwidth+x] = (a*blocksize)/r;
2037             py = ey;
2038         }
2039         px = ex;
2040     }
2041     return newdata;
2042 }
2043
2044 #define IMAGE_TYPE_JPEG 0
2045 #define IMAGE_TYPE_LOSSLESS 1
2046
2047 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2048         double x1,double y1,
2049         double x2,double y2,
2050         double x3,double y3,
2051         double x4,double y4, int type)
2052 {
2053     gfxcolor_t*newpic=0;
2054     
2055     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2056     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2057    
2058     gfxline_t p1,p2,p3,p4,p5;
2059     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2060     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2061     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2062     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2063     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2064
2065     {p1.x = (int)(p1.x*20)/20.0;
2066      p1.y = (int)(p1.y*20)/20.0;
2067      p2.x = (int)(p2.x*20)/20.0;
2068      p2.y = (int)(p2.y*20)/20.0;
2069      p3.x = (int)(p3.x*20)/20.0;
2070      p3.y = (int)(p3.y*20)/20.0;
2071      p4.x = (int)(p4.x*20)/20.0;
2072      p4.y = (int)(p4.y*20)/20.0;
2073      p5.x = (int)(p5.x*20)/20.0;
2074      p5.y = (int)(p5.y*20)/20.0;
2075     }
2076     
2077     float m00,m10,tx;
2078     float m01,m11,ty;
2079     
2080     gfxmatrix_t m;
2081     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2082     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2083     m.tx = p1.x - 0.5;
2084     m.ty = p1.y - 0.5;
2085
2086     gfximage_t img;
2087     img.data = (gfxcolor_t*)data;
2088     img.width = sizex;
2089     img.height = sizey;
2090   
2091     if(type == IMAGE_TYPE_JPEG)
2092         /* TODO: pass image_dpi to device instead */
2093         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2094
2095     dev->fillbitmap(dev, &p1, &img, &m, 0);
2096 }
2097
2098 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2099         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2100 {
2101     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2102 }
2103
2104 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2105         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2106 {
2107     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2108 }
2109
2110
2111 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2112                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2113                                    GBool inlineImg, int mask, int*maskColors,
2114                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2115 {
2116   double x1,y1,x2,y2,x3,y3,x4,y4;
2117   ImageStream *imgStr;
2118   Guchar pixBuf[4];
2119   GfxRGB rgb;
2120   int ncomps = 1;
2121   int bits = 1;
2122   unsigned char* maskbitmap = 0;
2123                                  
2124   if(colorMap) {
2125     ncomps = colorMap->getNumPixelComps();
2126     bits = colorMap->getBits();
2127   }
2128       
2129   if(maskStr) {
2130       int x,y;
2131       unsigned char buf[8];
2132       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2133       if(maskColorMap) {
2134           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2135           imgMaskStr->reset();
2136           unsigned char pal[256];
2137           int n = 1 << colorMap->getBits();
2138           int t;
2139           for(t=0;t<n;t++) {
2140               GfxGray gray;
2141               pixBuf[0] = t;
2142               maskColorMap->getGray(pixBuf, &gray);
2143               pal[t] = colToByte(gray);
2144           }
2145           for (y = 0; y < maskHeight; y++) {
2146               for (x = 0; x < maskWidth; x++) {
2147                   imgMaskStr->getPixel(buf);
2148                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2149               }
2150           }
2151           delete imgMaskStr;
2152       } else {
2153           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2154           imgMaskStr->reset();
2155           for (y = 0; y < maskHeight; y++) {
2156               for (x = 0; x < maskWidth; x++) {
2157                   imgMaskStr->getPixel(buf);
2158                   buf[0]^=maskInvert;
2159                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2160               }
2161           }
2162           delete imgMaskStr;
2163       }
2164       maskStr->close();
2165   }
2166   
2167   imgStr = new ImageStream(str, width, ncomps,bits);
2168   imgStr->reset();
2169
2170   if(!width || !height || (height<=1 && width<=1))
2171   {
2172       msg("<verbose> Ignoring %d by %d image", width, height);
2173       unsigned char buf[8];
2174       int x,y;
2175       for (y = 0; y < height; ++y)
2176       for (x = 0; x < width; ++x) {
2177           imgStr->getPixel(buf);
2178       }
2179       delete imgStr;
2180       if(maskbitmap)
2181           free(maskbitmap);
2182       return;
2183   }
2184
2185   state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2186   state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2187   state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2188   state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2189
2190   if(!pbminfo && !(str->getKind()==strDCT)) {
2191       if(!type3active) {
2192           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2193           pbminfo = 1;
2194       }
2195       if(mask)
2196       msg("<verbose> drawing %d by %d masked picture\n", width, height);
2197   }
2198   if(!jpeginfo && (str->getKind()==strDCT)) {
2199       msg("<notice> file contains jpeg pictures");
2200       jpeginfo = 1;
2201   }
2202
2203   if(mask) {
2204       int i,j;
2205       unsigned char buf[8];
2206       int x,y;
2207       unsigned char*pic = new unsigned char[width*height];
2208       gfxcolor_t pal[256];
2209       GfxRGB rgb;
2210       state->getFillRGB(&rgb);
2211
2212       memset(pal,255,sizeof(pal));
2213       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2214       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2215       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2216       pal[0].a = 255;              pal[1].a = 0;
2217     
2218       int numpalette = 2;
2219       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2220       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2221       for (y = 0; y < height; ++y)
2222       for (x = 0; x < width; ++x)
2223       {
2224             imgStr->getPixel(buf);
2225             if(invert) 
2226                 buf[0]=1-buf[0];
2227             pic[width*y+x] = buf[0];
2228       }
2229       
2230       /* the size of the drawn image is added to the identifier
2231          as the same image may require different bitmaps if displayed
2232          at different sizes (due to antialiasing): */
2233       int t,found = -1;
2234       if(type3active) {
2235           unsigned char*pic2 = 0;
2236           numpalette = 16;
2237           
2238           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2239
2240           if(!pic2) {
2241             delete pic;
2242             delete imgStr;
2243             return;
2244           }
2245
2246           width = realwidth;
2247           height = realheight;
2248           free(pic);
2249           pic = pic2;
2250           
2251           /* make a black/white palette */
2252
2253           float r = 255/(numpalette-1);
2254           int t;
2255           for(t=0;t<numpalette;t++) {
2256               pal[t].r = colToByte(rgb.r);
2257               pal[t].g = colToByte(rgb.g);
2258               pal[t].b = colToByte(rgb.b);
2259               pal[t].a = (unsigned char)(t*r);
2260           }
2261       }
2262
2263       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2264       for (y = 0; y < height; ++y) {
2265         for (x = 0; x < width; ++x) {
2266           pic2[width*y+x] = pal[pic[y*width+x]];
2267         }
2268       }
2269       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2270       free(pic2);
2271       free(pic);
2272       delete imgStr;
2273       if(maskbitmap) free(maskbitmap);
2274       return;
2275   }
2276
2277   int x,y;
2278
2279   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2280       gfxcolor_t*pic=new gfxcolor_t[width*height];
2281       for (y = 0; y < height; ++y) {
2282         for (x = 0; x < width; ++x) {
2283           imgStr->getPixel(pixBuf);
2284           colorMap->getRGB(pixBuf, &rgb);
2285           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2286           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2287           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2288           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2289           if(maskbitmap) {
2290               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2291           }
2292         }
2293       }
2294       if(str->getKind()==strDCT)
2295           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2296       else
2297           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2298       delete pic;
2299       delete imgStr;
2300       if(maskbitmap) free(maskbitmap);
2301       return;
2302   } else {
2303       gfxcolor_t*pic=new gfxcolor_t[width*height];
2304       gfxcolor_t pal[256];
2305       int n = 1 << colorMap->getBits();
2306       int t;
2307       for(t=0;t<256;t++) {
2308           pixBuf[0] = t;
2309           colorMap->getRGB(pixBuf, &rgb);
2310
2311           {/*if(maskColors && *maskColors==t) {
2312               msg("<notice> Color %d is transparent", t);
2313               if (imgData->maskColors) {
2314                 *alpha = 0;
2315                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2316                   if (pix[i] < imgData->maskColors[2*i] ||
2317                       pix[i] > imgData->maskColors[2*i+1]) {
2318                     *alpha = 1;
2319                     break;
2320                   }
2321                 }
2322               } else {
2323                 *alpha = 1;
2324               }
2325               if(!*alpha) {
2326                     pal[t].r = 0;
2327                     pal[t].g = 0;
2328                     pal[t].b = 0;
2329                     pal[t].a = 0;
2330               }
2331           } else {*/
2332               pal[t].r = (unsigned char)(colToByte(rgb.r));
2333               pal[t].g = (unsigned char)(colToByte(rgb.g));
2334               pal[t].b = (unsigned char)(colToByte(rgb.b));
2335               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2336           }
2337       }
2338       for (y = 0; y < height; ++y) {
2339         for (x = 0; x < width; ++x) {
2340           imgStr->getPixel(pixBuf);
2341           pic[width*y+x] = pal[pixBuf[0]];
2342           if(maskbitmap) {
2343               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2344           }
2345         }
2346       }
2347       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2348
2349       delete pic;
2350       delete imgStr;
2351       if(maskbitmap) free(maskbitmap);
2352       return;
2353   }
2354 }
2355
2356 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2357                                    int width, int height, GBool invert,
2358                                    GBool inlineImg) 
2359 {
2360     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2361     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2362     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2363 }
2364
2365 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2366                          int width, int height, GfxImageColorMap *colorMap,
2367                          int *maskColors, GBool inlineImg)
2368 {
2369     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2370             colorMap?"colorMap":"no colorMap", 
2371             maskColors?"maskColors":"no maskColors",
2372             inlineImg);
2373     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2374             colorMap?"colorMap":"no colorMap", 
2375             maskColors?"maskColors":"no maskColors",
2376             inlineImg);
2377     if(colorMap)
2378         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2379                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2380     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2381 }
2382   
2383 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2384                                int width, int height,
2385                                GfxImageColorMap *colorMap,
2386                                Stream *maskStr, int maskWidth, int maskHeight,
2387                                GBool maskInvert)
2388 {
2389     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2390             colorMap?"colorMap":"no colorMap", 
2391             maskWidth, maskHeight);
2392     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2393             colorMap?"colorMap":"no colorMap", 
2394             maskWidth, maskHeight);
2395     if(colorMap)
2396         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2397                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2398     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2399 }
2400
2401 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2402                                    int width, int height,
2403                                    GfxImageColorMap *colorMap,
2404                                    Stream *maskStr,
2405                                    int maskWidth, int maskHeight,
2406                                    GfxImageColorMap *maskColorMap)
2407 {
2408     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2409             colorMap?"colorMap":"no colorMap", 
2410             maskWidth, maskHeight);
2411     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2412             colorMap?"colorMap":"no colorMap", 
2413             maskWidth, maskHeight);
2414     if(colorMap)
2415         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2416                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2417     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2418 }
2419
2420 void GFXOutputDev::stroke(GfxState *state) 
2421 {
2422     dbg("stroke");
2423
2424     GfxPath * path = state->getPath();
2425     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2426     strokeGfxline(state, line);
2427     gfxline_free(line);
2428 }
2429
2430 void GFXOutputDev::fill(GfxState *state) 
2431 {
2432     gfxcolor_t col = getFillColor(state);
2433     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2434
2435     GfxPath * path = state->getPath();
2436     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2437     fillGfxLine(state, line);
2438     gfxline_free(line);
2439 }
2440
2441 void GFXOutputDev::eoFill(GfxState *state) 
2442 {
2443     gfxcolor_t col = getFillColor(state);
2444     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2445
2446     GfxPath * path = state->getPath();
2447     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2448     fillGfxLine(state, line);
2449     gfxline_free(line);
2450 }
2451
2452
2453 static char* dirseparator()
2454 {
2455 #ifdef WIN32
2456     return "\\";
2457 #else
2458     return "/";
2459 #endif
2460 }
2461
2462 void addGlobalFont(char*filename)
2463 {
2464     fontfile_t f;
2465     memset(&f, 0, sizeof(fontfile_t));
2466     f.filename = filename;
2467     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2468         msg("<verbose> Adding font \"%s\".", filename);
2469         fonts[fontnum++] = f;
2470     } else {
2471         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2472     }
2473 }
2474
2475 void addGlobalLanguageDir(char*dir)
2476 {
2477     if(!globalParams)
2478         globalParams = new GlobalParams("");
2479     
2480     msg("<notice> Adding %s to language pack directories", dir);
2481
2482     int l;
2483     FILE*fi = 0;
2484     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2485     strcpy(config_file, dir);
2486     strcat(config_file, dirseparator());
2487     strcat(config_file, "add-to-xpdfrc");
2488
2489     fi = fopen(config_file, "rb");
2490     if(!fi) {
2491         msg("<error> Could not open %s", config_file);
2492         return;
2493     }
2494     globalParams->parseFile(new GString(config_file), fi);
2495     fclose(fi);
2496 }
2497
2498 void addGlobalFontDir(char*dirname)
2499 {
2500 #ifdef HAVE_DIRENT_H
2501     msg("<notice> Adding %s to font directories", dirname);
2502     lastfontdir = strdup(dirname);
2503     DIR*dir = opendir(dirname);
2504     if(!dir) {
2505         msg("<warning> Couldn't open directory %s\n", dirname);
2506         return;
2507     }
2508     struct dirent*ent;
2509     while(1) {
2510         ent = readdir (dir);
2511         if (!ent) 
2512             break;
2513         int l;
2514         char*name = ent->d_name;
2515         char type = 0;
2516         if(!name) continue;
2517         l=strlen(name);
2518         if(l<4)
2519             continue;
2520         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2521             type=1;
2522         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2523             type=3;
2524         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2525             type=2;
2526         if(type)
2527         {
2528             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2529             strcpy(fontname, dirname);
2530             strcat(fontname, dirseparator());
2531             strcat(fontname, name);
2532             addGlobalFont(fontname);
2533         }
2534     }
2535     closedir(dir);
2536 #else
2537     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2538 #endif
2539 }
2540
2541 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2542 {
2543     if(pdfpage < 0)
2544         return;
2545
2546     if(!this->pages) {
2547         this->pagebuflen = 1024;
2548         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2549         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2550     } else {
2551         while(pdfpage >= this->pagebuflen)
2552         {
2553             int oldlen = this->pagebuflen;
2554             this->pagebuflen+=1024;
2555             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2556             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2557         }
2558     }
2559     this->pages[pdfpage] = outputpage;
2560     if(pdfpage>this->pagepos)
2561         this->pagepos = pdfpage;
2562 }
2563
2564 struct BBox
2565 {
2566     double posx,posy;
2567     double width,height;
2568 };
2569
2570 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2571 {
2572     double xMin, yMin, xMax, yMax, x, y;
2573     double tx, ty, w, h;
2574     // transform the bbox
2575     state->transform(bbox[0], bbox[1], &x, &y);
2576     xMin = xMax = x;
2577     yMin = yMax = y;
2578     state->transform(bbox[0], bbox[3], &x, &y);
2579     if (x < xMin) {
2580       xMin = x;
2581     } else if (x > xMax) {
2582       xMax = x;
2583     }
2584     if (y < yMin) {
2585       yMin = y;
2586     } else if (y > yMax) {
2587       yMax = y;
2588     }
2589     state->transform(bbox[2], bbox[1], &x, &y);
2590     if (x < xMin) {
2591       xMin = x;
2592     } else if (x > xMax) {
2593       xMax = x;
2594     }
2595     if (y < yMin) {
2596       yMin = y;
2597     } else if (y > yMax) {
2598       yMax = y;
2599     }
2600     state->transform(bbox[2], bbox[3], &x, &y);
2601     if (x < xMin) {
2602       xMin = x;
2603     } else if (x > xMax) {
2604       xMax = x;
2605     }
2606     if (y < yMin) {
2607       yMin = y;
2608     } else if (y > yMax) {
2609       yMax = y;
2610     }
2611     tx = (int)floor(xMin);
2612     if (tx < 0) {
2613       tx = 0;
2614     } else if (tx > width) {
2615       tx = width;
2616     }
2617     ty = (int)floor(yMin);
2618     if (ty < 0) {
2619       ty = 0;
2620     } else if (ty > height) {
2621       ty = height;
2622     }
2623     w = (int)ceil(xMax) - tx + 1;
2624     if (tx + w > width) {
2625       w = width - tx;
2626     }
2627     if (w < 1) {
2628       w = 1;
2629     }
2630     h = (int)ceil(yMax) - ty + 1;
2631     if (ty + h > height) {
2632       h = height - ty;
2633     }
2634     if (h < 1) {
2635       h = 1;
2636     }
2637     BBox nbbox;
2638     nbbox.posx = xMin;
2639     nbbox.posx = yMin;
2640     nbbox.width = w;
2641     nbbox.height = h;
2642     return nbbox;
2643 }
2644
2645 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2646                                       GfxColorSpace *blendingColorSpace,
2647                                       GBool isolated, GBool knockout,
2648                                       GBool forSoftMask)
2649 {
2650     char*colormodename = "";
2651     BBox rect = mkBBox(state, bbox, this->width, this->height);
2652
2653     if(blendingColorSpace) {
2654         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2655     }
2656     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);
2657     dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2658     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);
2659     
2660     states[statepos].createsoftmask |= forSoftMask;
2661     states[statepos].transparencygroup = !forSoftMask;
2662     states[statepos].isolated = isolated;
2663
2664     states[statepos].olddevice = this->device;
2665     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2666
2667     gfxdevice_record_init(this->device);
2668     
2669     /*if(!forSoftMask) { ////???
2670         state->setFillOpacity(0.0);
2671     }*/
2672     dbgindent+=2;
2673 }
2674
2675 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2676 {
2677     dbgindent-=2;
2678     dbg("endTransparencyGroup");
2679     msg("<verbose> endTransparencyGroup");
2680
2681     gfxdevice_t*r = this->device;
2682
2683     this->device = states[statepos].olddevice;
2684
2685     if(states[statepos].createsoftmask) {
2686         states[statepos-1].softmaskrecording = r->finish(r);
2687     } else {
2688         states[statepos-1].grouprecording = r->finish(r);
2689     }
2690     
2691     states[statepos].createsoftmask = 0;
2692     states[statepos].transparencygroup = 0;
2693     free(r);
2694 }
2695
2696 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2697 {
2698     char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2699                        "colordodge","colorburn","hardlight","softlight","difference",
2700                        "exclusion","hue","saturation","color","luminosity"};
2701
2702     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2703     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2704    
2705     if(state->getBlendMode() == gfxBlendNormal)
2706         infofeature("transparency groups");
2707     else {
2708         char buffer[80];
2709         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2710         warnfeature(buffer, 0);
2711     }
2712
2713     gfxresult_t*grouprecording = states[statepos].grouprecording;
2714    
2715     if(state->getBlendMode() == gfxBlendNormal) {
2716         gfxdevice_t ops;
2717         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2718         gfxresult_record_replay(grouprecording, &ops);
2719         ops.finish(&ops);
2720     }
2721     grouprecording->destroy(grouprecording);
2722
2723     states[statepos].grouprecording = 0;
2724 }
2725
2726 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2727 {
2728     /* alpha = 1: retrieve mask values from alpha layer
2729        alpha = 0: retrieve mask values from luminance */
2730     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2731             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2732     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2733             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2734     if(!alpha)
2735         infofeature("soft masks");
2736     else
2737         warnfeature("soft masks from alpha channel",0);
2738     
2739     states[statepos].olddevice = this->device;
2740     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2741     gfxdevice_record_init(this->device);
2742
2743     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2744     
2745     states[statepos].softmask = 1;
2746     states[statepos].softmask_alpha = alpha;
2747 }
2748
2749 static inline Guchar div255(int x) {
2750   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2751 }
2752
2753 void GFXOutputDev::clearSoftMask(GfxState *state)
2754 {
2755     if(!states[statepos].softmask)
2756         return;
2757     states[statepos].softmask = 0;
2758     dbg("clearSoftMask statepos=%d", statepos);
2759     msg("<verbose> clearSoftMask");
2760     
2761     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2762         msg("<error> Error in softmask/tgroup ordering");
2763         return;
2764     }
2765   
2766     gfxresult_t*mask = states[statepos].softmaskrecording;
2767     gfxresult_t*below = this->device->finish(this->device);
2768     this->device = states[statepos].olddevice;
2769
2770     /* get outline of all objects below the soft mask */
2771     gfxdevice_t uniondev;
2772     gfxdevice_union_init(&uniondev, 0);
2773     gfxresult_record_replay(below, &uniondev);
2774     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2775     uniondev.finish(&uniondev);
2776
2777     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2778 #if 0 
2779     this->device->startclip(this->device, belowoutline);
2780     gfxresult_record_replay(below, this->device);
2781     gfxresult_record_replay(mask, this->device);
2782     this->device->endclip(this->device);
2783     gfxline_free(belowoutline);
2784 #endif
2785     
2786     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2787
2788     gfxdevice_t belowrender;
2789     gfxdevice_render_init(&belowrender);
2790     if(states[statepos+1].isolated) {
2791         belowrender.setparameter(&belowrender, "fillwhite", "1");
2792     }
2793     belowrender.setparameter(&belowrender, "antialize", "2");
2794     belowrender.startpage(&belowrender, width, height);
2795     gfxresult_record_replay(below, &belowrender);
2796     belowrender.endpage(&belowrender);
2797     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2798     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2799     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2800
2801     gfxdevice_t maskrender;
2802     gfxdevice_render_init(&maskrender);
2803     maskrender.startpage(&maskrender, width, height);
2804     gfxresult_record_replay(mask, &maskrender);
2805     maskrender.endpage(&maskrender);
2806     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2807     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2808
2809     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2810         msg("<fatal> Internal error in mask drawing");
2811         return;
2812     }
2813
2814     int y,x;
2815     for(y=0;y<height;y++) {
2816         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2817         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2818         for(x=0;x<width;x++) {
2819             int alpha;
2820             if(states[statepos].softmask_alpha) {
2821                 alpha = l1->a;
2822             } else {
2823                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2824             }
2825
2826             /* premultiply alpha */
2827             l2->a = div255(alpha*l2->a);
2828             l2->r = div255(alpha*l2->r);
2829             l2->g = div255(alpha*l2->g);
2830             l2->b = div255(alpha*l2->b);
2831
2832             l1++;
2833             l2++;
2834         }
2835     }
2836     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2837
2838     gfxmatrix_t matrix;
2839     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2840     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2841
2842     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2843
2844     mask->destroy(mask);
2845     below->destroy(below);
2846     maskresult->destroy(maskresult);
2847     belowresult->destroy(belowresult);
2848     states[statepos].softmaskrecording = 0;
2849 }
2850   
2851 /*class MemCheck
2852 {
2853     public: ~MemCheck()
2854     {
2855         delete globalParams;globalParams=0;
2856         Object::memCheck(stderr);
2857         gMemReport(stderr);
2858     }
2859 } myMemCheck;*/
2860