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