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