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