d3a9e4e0786d1360abbb7b74a12c5809cad1f207
[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->textRender = 0;
189     this->createsoftmask = 0;
190     this->transparencygroup = 0;
191     this->softmask = 0;
192     this->grouprecording = 0;
193     this->isolated = 0;
194 }
195
196 GBool GFXOutputDev::interpretType3Chars() 
197 {
198     return this->do_interpretType3Chars;
199 }
200
201 typedef struct _drawnchar
202 {
203     gfxcoord_t x,y;
204     int charid;
205     gfxcolor_t color;
206 } drawnchar_t;
207
208 class CharBuffer
209 {
210     drawnchar_t * chars;
211     int buf_size;
212     int num_chars;
213
214 public:
215
216     CharBuffer()
217     {
218         buf_size = 32;
219         chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
220         memset(chars, 0, sizeof(drawnchar_t)*buf_size);
221         num_chars = 0;
222     }
223     ~CharBuffer()
224     {
225         free(chars);chars = 0;
226     }
227
228     void grow(int size)
229     {
230         if(size>=buf_size) {
231             buf_size += 32;
232             chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
233         }
234     }
235
236     void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
237     {
238         grow(num_chars);
239         chars[num_chars].x = x;
240         chars[num_chars].y = y;
241         chars[num_chars].color = color;
242         chars[num_chars].charid = charid;
243     }
244 };
245
246 static char*getFontID(GfxFont*font);
247
248 GFXOutputDev::GFXOutputDev(parameter_t*p)
249 {
250     this->jpeginfo = 0;
251     this->textmodeinfo = 0;
252     this->linkinfo = 0;
253     this->pbminfo = 0;
254     this->type3active = 0;
255     this->statepos = 0;
256     this->xref = 0;
257     this->substitutepos = 0;
258     this->type3Warning = 0;
259     this->user_movex = 0;
260     this->user_movey = 0;
261     this->clipmovex = 0;
262     this->clipmovey = 0;
263     this->user_clipx1 = 0;
264     this->user_clipy1 = 0;
265     this->user_clipx2 = 0;
266     this->user_clipy2 = 0;
267     this->current_text_stroke = 0;
268     this->current_text_clip = 0;
269     this->fontlist = 0;
270     this->outer_clip_box = 0;
271     this->pages = 0;
272     this->pagebuflen = 0;
273     this->pagepos = 0;
274     this->config_use_fontconfig=1;
275     this->config_break_on_warning=0;
276     this->do_interpretType3Chars = gTrue;
277
278     this->parameters = p;
279   
280     memset(states, 0, sizeof(states));
281
282     /* configure device */
283     while(p) {
284         setParameter(p->name, p->value);
285         p = p->next;
286     }
287 };
288
289 void GFXOutputDev::setParameter(char*key, char*value)
290 {
291     if(!strcmp(key,"rawtext")) {
292         this->do_interpretType3Chars = atoi(value)^1;
293     } else if(!strcmp(key,"breakonwarning")) {
294         this->config_break_on_warning = atoi(value);
295     } else if(!strcmp(key,"fontconfig")) {
296         this->config_use_fontconfig = atoi(value);
297     } else {
298         msg("<warning> Ignored parameter: %s=%s", key, value);
299     }
300 }
301   
302 void GFXOutputDev::setDevice(gfxdevice_t*dev)
303 {
304     parameter_t*p = this->parameters;
305
306     /* pass parameters to output device */
307     this->device = dev;
308     if(this->device) {
309         while(p) {
310             this->device->setparameter(this->device, p->name, p->value);
311             p = p->next;
312         }
313     }
314 }
315   
316 void GFXOutputDev::setMove(int x,int y)
317 {
318     this->user_movex = x;
319     this->user_movey = y;
320 }
321
322 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
323 {
324     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
325     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
326
327     this->user_clipx1 = x1;
328     this->user_clipy1 = y1;
329     this->user_clipx2 = x2;
330     this->user_clipy2 = y2;
331 }
332
333 static char*getFontID(GfxFont*font)
334 {
335     Ref*ref = font->getID();
336     GString*gstr = font->getName();
337     char* fname = gstr==0?0:gstr->getCString();
338     char buf[128];
339     if(fname==0) {
340         sprintf(buf, "font-%d-%d", ref->num, ref->gen);
341     } else {
342         sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
343     }
344     return strdup(buf);
345 }
346
347 static char*getFontName(GfxFont*font)
348 {
349     char*fontid;
350     GString*gstr = font->getName();
351     char* fname = gstr==0?0:gstr->getCString();
352     if(fname==0) {
353         char buf[32];
354         Ref*r=font->getID();
355         sprintf(buf, "UFONT%d", r->num);
356         fontid = strdup(buf);
357     } else {
358         fontid = strdup(fname);
359     }
360
361     char*fontname= 0;
362     char* plus = strchr(fontid, '+');
363     if(plus && plus < &fontid[strlen(fontid)-1]) {
364         fontname = strdup(plus+1);
365     } else {
366         fontname = strdup(fontid);
367     }
368     free(fontid);
369     return fontname;
370 }
371
372 static char mybuf[1024];
373 static char* gfxstate2str(GfxState *state)
374 {
375   char*bufpos = mybuf;
376   GfxRGB rgb;
377   bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
378                                     state->getCTM()[0],
379                                     state->getCTM()[1],
380                                     state->getCTM()[2],
381                                     state->getCTM()[3],
382                                     state->getCTM()[4],
383                                     state->getCTM()[5]);
384   if(state->getX1()!=0.0)
385   bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
386   if(state->getY1()!=0.0)
387   bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
388   bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
389   bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
390   bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
391   bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
392   /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
393           state->getFillColor()->c[0], state->getFillColor()->c[1]);
394   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
395           state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
396 /*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
397           state->getFillColor()->c[0], state->getFillColor()->c[1],
398           state->getFillColor()->c[2], state->getFillColor()->c[3],
399           state->getFillColor()->c[4], state->getFillColor()->c[5],
400           state->getFillColor()->c[6], state->getFillColor()->c[7]);
401   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
402           state->getStrokeColor()->c[0], state->getFillColor()->c[1],
403           state->getStrokeColor()->c[2], state->getFillColor()->c[3],
404           state->getStrokeColor()->c[4], state->getFillColor()->c[5],
405           state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
406   state->getFillRGB(&rgb);
407   if(rgb.r || rgb.g || rgb.b)
408   bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
409   state->getStrokeRGB(&rgb);
410   if(rgb.r || rgb.g || rgb.b)
411   bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
412   if(state->getFillColorSpace()->getNComps()>1)
413   bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
414   if(state->getStrokeColorSpace()->getNComps()>1)
415   bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
416   if(state->getFillPattern())
417   bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
418   if(state->getStrokePattern())
419   bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
420  
421   if(state->getFillOpacity()!=1.0)
422   bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
423   if(state->getStrokeOpacity()!=1.0)
424   bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
425
426   bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
427  
428   double * dash;
429   int length;
430   double start;
431   state->getLineDash(&dash, &length, &start);
432   int t;
433   if(length)
434   {
435       bufpos+=sprintf(bufpos,"DASH%.1f[",start);
436       for(t=0;t<length;t++) {
437           bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
438       }
439       bufpos+=sprintf(bufpos,"]");
440   }
441
442   if(state->getFlatness()!=1)
443   bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
444   if(state->getLineJoin()!=0)
445   bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
446   if(state->getLineJoin()!=0)
447   bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
448   if(state->getLineJoin()!=0)
449   bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
450
451   if(state->getFont() && getFontID(state->getFont()))
452   bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
453   bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
454   bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
455                                    state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
456   if(state->getCharSpace())
457   bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
458   if(state->getWordSpace())
459   bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
460   if(state->getHorizScaling()!=1.0)
461   bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
462   if(state->getLeading())
463   bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
464   if(state->getRise())
465   bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
466   if(state->getRender())
467   bufpos+=sprintf(bufpos,"R%d ", state->getRender());
468   bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
469   bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
470   bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
471   if(state->getLineX())
472   bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
473   if(state->getLineY())
474   bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
475   bufpos+=sprintf(bufpos," ");
476   return mybuf;
477 }
478
479 static void dumpFontInfo(const char*loglevel, GfxFont*font);
480 static int lastdumps[1024];
481 static int lastdumppos = 0;
482 /* nr = 0  unknown
483    nr = 1  substituting
484    nr = 2  type 3
485  */
486 static void showFontError(GfxFont*font, int nr) 
487 {  
488     Ref*r=font->getID();
489     int t;
490     for(t=0;t<lastdumppos;t++)
491         if(lastdumps[t] == r->num)
492             break;
493     if(t < lastdumppos)
494       return;
495     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
496     lastdumps[lastdumppos++] = r->num;
497     if(nr == 0)
498       msg("<warning> The following font caused problems:");
499     else if(nr == 1)
500       msg("<warning> The following font caused problems (substituting):");
501     else if(nr == 2)
502       msg("<warning> The following Type 3 Font will be rendered as graphics:");
503     dumpFontInfo("<warning>", font);
504 }
505
506 static void dumpFontInfo(const char*loglevel, GfxFont*font)
507 {
508   char* id = getFontID(font);
509   char* name = getFontName(font);
510   Ref* r=font->getID();
511   msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
512
513   GString*gstr  = font->getTag();
514    
515   msg("%s| Tag: %s\n", loglevel, id);
516   
517   if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
518
519   GfxFontType type=font->getType();
520   switch(type) {
521     case fontUnknownType:
522      msg("%s| Type: unknown\n",loglevel);
523     break;
524     case fontType1:
525      msg("%s| Type: 1\n",loglevel);
526     break;
527     case fontType1C:
528      msg("%s| Type: 1C\n",loglevel);
529     break;
530     case fontType3:
531      msg("%s| Type: 3\n",loglevel);
532     break;
533     case fontTrueType:
534      msg("%s| Type: TrueType\n",loglevel);
535     break;
536     case fontCIDType0:
537      msg("%s| Type: CIDType0\n",loglevel);
538     break;
539     case fontCIDType0C:
540      msg("%s| Type: CIDType0C\n",loglevel);
541     break;
542     case fontCIDType2:
543      msg("%s| Type: CIDType2\n",loglevel);
544     break;
545   }
546   
547   Ref embRef;
548   GBool embedded = font->getEmbeddedFontID(&embRef);
549   char*embeddedName=0;
550   if(font->getEmbeddedFontName()) {
551     embeddedName = font->getEmbeddedFontName()->getCString();
552   }
553   if(embedded)
554    msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
555
556   gstr = font->getExtFontFile();
557   if(gstr)
558    msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
559
560   // Get font descriptor flags.
561   if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
562   if(font->isSerif()) msg("%s| is serif\n", loglevel);
563   if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
564   if(font->isItalic()) msg("%s| is italic\n", loglevel);
565   if(font->isBold()) msg("%s| is bold\n", loglevel);
566
567   free(id);
568   free(name);
569 }
570
571 //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");}
572 //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");}
573
574
575 void dump_outline(gfxline_t*line)
576 {
577     while(line) {
578         if(line->type == gfx_moveTo) {
579             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
580         } else if(line->type == gfx_lineTo) {
581             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
582         } else if(line->type == gfx_splineTo) {
583             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
584         }
585         line = line->next;
586     }
587 }
588
589 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
590 {
591     int num = path->getNumSubpaths();
592     int s,t;
593     int cpos = 0;
594     double lastx=0,lasty=0,posx=0,posy=0;
595     int needsfix=0;
596     if(!num) {
597         msg("<warning> empty path");
598         return 0;
599     }
600     gfxdrawer_t draw;
601     gfxdrawer_target_gfxline(&draw);
602
603     for(t = 0; t < num; t++) {
604         GfxSubpath *subpath = path->getSubpath(t);
605         int subnum = subpath->getNumPoints();
606         double bx=0,by=0,cx=0,cy=0;
607
608         for(s=0;s<subnum;s++) {
609            double x,y;
610            
611            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
612            x += user_movex;
613            y += user_movey;
614
615            if(s==0) {
616                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
617                     draw.lineTo(&draw, lastx, lasty);
618                 }
619                 draw.moveTo(&draw, x,y);
620                 posx = lastx = x; 
621                 posy = lasty = y;
622                 cpos = 0;
623                 needsfix = 0;
624            } else if(subpath->getCurve(s) && cpos==0) {
625                 bx = x;
626                 by = y;
627                 cpos = 1;
628            } else if(subpath->getCurve(s) && cpos==1) {
629                 cx = x;
630                 cy = y;
631                 cpos = 2;
632            } else {
633                 posx = x;
634                 posy = y;
635                 if(cpos==0) {
636                     draw.lineTo(&draw, x,y);
637                 } else {
638                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
639                 }
640                 needsfix = 1;
641                 cpos = 0;
642            }
643         }
644     }
645     /* fix non-closed lines */
646     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
647         draw.lineTo(&draw, lastx, lasty);
648     }
649     gfxline_t*result = (gfxline_t*)draw.result(&draw);
650
651     gfxline_optimize(result);
652
653     return result;
654 }
655
656 GBool GFXOutputDev::useTilingPatternFill()
657 {
658     infofeature("tiled patterns");
659     return gFalse;
660 }
661
662 GBool GFXOutputDev::useShadedFills()
663 {
664     infofeature("shaded fills");
665     return gFalse;
666 }
667
668 #define STROKE_FILL 1
669 #define STROKE_CLIP 2
670 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
671 {
672     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
673     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
674     double miterLimit = state->getMiterLimit();
675     double width = state->getTransformedLineWidth();
676
677     GfxRGB rgb;
678     double opaq = state->getStrokeOpacity();
679     if(type3active)
680         state->getFillRGB(&rgb);
681     else
682         state->getStrokeRGB(&rgb);
683     gfxcolor_t col;
684     col.r = colToByte(rgb.r);
685     col.g = colToByte(rgb.g);
686     col.b = colToByte(rgb.b);
687     col.a = (unsigned char)(opaq*255);
688    
689     gfx_capType capType = gfx_capRound;
690     if(lineCap == 0) capType = gfx_capButt;
691     else if(lineCap == 1) capType = gfx_capRound;
692     else if(lineCap == 2) capType = gfx_capSquare;
693
694     gfx_joinType joinType = gfx_joinRound;
695     if(lineJoin == 0) joinType = gfx_joinMiter;
696     else if(lineJoin == 1) joinType = gfx_joinRound;
697     else if(lineJoin == 2) joinType = gfx_joinBevel;
698
699     int dashnum = 0;
700     double dashphase = 0;
701     double * ldash = 0;
702     state->getLineDash(&ldash, &dashnum, &dashphase);
703
704     gfxline_t*line2 = 0;
705
706     if(dashnum && ldash) {
707         float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
708         int t;
709         double cut = 0;
710         int fixzero = 0;
711         msg("<trace> %d dashes", dashnum);
712         msg("<trace> |  phase: %f", dashphase);
713         for(t=0;t<dashnum;t++) {
714             dash[t] = ldash[t];
715             msg("<trace> |  d%-3d: %f", t, ldash[t]);
716         }
717         dash[dashnum] = -1;
718         if(getLogLevel() >= LOGLEVEL_TRACE) {
719             dump_outline(line);
720         }
721
722         line2 = gfxtool_dash_line(line, dash, dashphase);
723         line = line2;
724         free(dash);
725         msg("<trace> After dashing:");
726     }
727     
728     if(getLogLevel() >= LOGLEVEL_TRACE)  {
729         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
730                 width,
731                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
732                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
733                 dashnum,
734                 col.r,col.g,col.b,col.a
735                 );
736         dump_outline(line);
737     }
738
739     if(flags&STROKE_FILL) {
740         ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
741         gfxline_t*gfxline = SVPtogfxline(svp);
742         if(flags&STROKE_CLIP) {
743             device->startclip(device, gfxline);
744             states[statepos].clipping++;
745         } else {
746             device->fill(device, gfxline, &col);
747         }
748         free(gfxline);
749         art_svp_free(svp);
750     } else {
751         if(flags&STROKE_CLIP) 
752             msg("<error> Stroke&clip not supported at the same time");
753         device->stroke(device, line, width, &col, capType, joinType, miterLimit);
754     }
755     
756     if(line2)
757         gfxline_free(line2);
758 }
759
760 gfxcolor_t getFillColor(GfxState * state)
761 {
762     GfxRGB rgb;
763     double opaq = state->getFillOpacity();
764     state->getFillRGB(&rgb);
765     gfxcolor_t col;
766     col.r = colToByte(rgb.r);
767     col.g = colToByte(rgb.g);
768     col.b = colToByte(rgb.b);
769     col.a = (unsigned char)(opaq*255);
770     return col;
771 }
772
773 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
774 {
775     gfxcolor_t col = getFillColor(state);
776
777     if(getLogLevel() >= LOGLEVEL_TRACE)  {
778         msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
779         dump_outline(line);
780     }
781     device->fill(device, line, &col);
782 }
783
784 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
785 {
786     if(getLogLevel() >= LOGLEVEL_TRACE)  {
787         msg("<trace> clip\n");
788         dump_outline(line);
789     }
790
791     device->startclip(device, line);
792     states[statepos].clipping++;
793 }
794
795 void GFXOutputDev::clip(GfxState *state) 
796 {
797     GfxPath * path = state->getPath();
798     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
799     clipToGfxLine(state, line);
800     gfxline_free(line);
801 }
802
803 void GFXOutputDev::eoClip(GfxState *state) 
804 {
805     GfxPath * path = state->getPath();
806     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
807
808     if(getLogLevel() >= LOGLEVEL_TRACE)  {
809         msg("<trace> eoclip\n");
810         dump_outline(line);
811     }
812
813     device->startclip(device, line);
814     states[statepos].clipping++;
815     gfxline_free(line);
816 }
817 void GFXOutputDev::clipToStrokePath(GfxState *state)
818 {
819     GfxPath * path = state->getPath();
820     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
821     strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
822     gfxline_free(line);
823 }
824
825 void GFXOutputDev::endframe()
826 {
827     if(outer_clip_box) {
828         device->endclip(device);
829         outer_clip_box = 0;
830     }
831 }
832
833 void GFXOutputDev::finish()
834 {
835     if(outer_clip_box) {
836         if(device) {
837             device->endclip(device);
838         }
839         outer_clip_box = 0;
840     }
841 }
842
843 GFXOutputDev::~GFXOutputDev() 
844 {
845     finish();
846
847     if(this->pages) {
848         free(this->pages); this->pages = 0;
849     }
850
851     fontlist_t*l = this->fontlist;
852     while(l) {
853         fontlist_t*next = l->next;
854         l->next = 0;
855         gfxfont_free(l->font);
856         free(l->filename);l->filename=0;
857         free(l);
858         l = next;
859     }
860     this->fontlist = 0;
861 };
862 GBool GFXOutputDev::upsideDown() 
863 {
864     return gTrue;
865 };
866 GBool GFXOutputDev::useDrawChar() 
867 {
868     return gTrue;
869 }
870
871 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
872                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
873
874 #define RENDER_FILL 0
875 #define RENDER_STROKE 1
876 #define RENDER_FILLSTROKE 2
877 #define RENDER_INVISIBLE 3
878 #define RENDER_CLIP 4
879
880 static char tmp_printstr[4096];
881 char* makeStringPrintable(char*str)
882 {
883     int len = strlen(str);
884     int dots = 0;
885     if(len>=80) {
886         len = 80;
887         dots = 1;
888     }
889     int t;
890     for(t=0;t<len;t++) {
891         char c = str[t];
892         if(c<32 || c>124) {
893             c = '.';
894         }
895         tmp_printstr[t] = c;
896     }
897     if(dots) {
898         tmp_printstr[len++] = '.';
899         tmp_printstr[len++] = '.';
900         tmp_printstr[len++] = '.';
901     }
902     tmp_printstr[len] = 0;
903     return tmp_printstr;
904 }
905
906
907 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
908 {
909     const char*uniname = 0;
910     if(!font)
911         return charnr;
912     if(u>0) {
913         int t;
914         /* find out char name from unicode index 
915            TODO: should be precomputed
916          */
917         for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
918             if(nameToUnicodeTab[t].u == u) {
919                 uniname = nameToUnicodeTab[t].name;
920                 break;
921             }
922         }
923     }
924
925     if(charname) {
926         int t;
927         for(t=0;t<font->num_glyphs;t++) {
928             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
929                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
930                 return t;
931             }
932         }
933         /* if we didn't find the character, maybe
934            we can find the capitalized version */
935         for(t=0;t<font->num_glyphs;t++) {
936             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
937                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
938                 return t;
939             }
940         }
941     }
942
943     if(uniname) {
944         int t;
945         for(t=0;t<font->num_glyphs;t++) {
946             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
947                 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
948                 return t;
949             }
950         }
951         /* if we didn't find the character, maybe
952            we can find the capitalized version */
953         for(t=0;t<font->num_glyphs;t++) {
954             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
955                 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
956                 return t;
957             }
958         }
959     }
960
961     /* try to use the unicode id */
962     if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
963         msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
964         return font->unicode2glyph[u];
965     }
966     /* try to use the unicode|0xe000 (needed for some WingDings fonts)
967        FIXME: do this only if we know the font is wingdings?
968      */
969     u |= 0xe000;
970     if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
971         msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
972         return font->unicode2glyph[u];
973     }
974
975     if(charnr>=0 && charnr<font->num_glyphs) {
976         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
977         return charnr;
978     }
979     
980     return -1;
981 }
982
983
984 void GFXOutputDev::beginString(GfxState *state, GString *s) 
985
986     int render = state->getRender();
987     if(current_text_stroke) {
988         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
989     }
990
991     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
992     double m11,m21,m12,m22;
993 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
994     state->getFontTransMat(&m11, &m12, &m21, &m22);
995     m11 *= state->getHorizScaling();
996     m21 *= state->getHorizScaling();
997
998     this->current_font_matrix.m00 = m11 / 1024.0;
999     this->current_font_matrix.m01 = m12 / 1024.0;
1000     this->current_font_matrix.m10 = -m21 / 1024.0;
1001     this->current_font_matrix.m11 = -m22 / 1024.0;
1002     this->current_font_matrix.tx = 0;
1003     this->current_font_matrix.ty = 0;
1004
1005     gfxmatrix_t m = this->current_font_matrix;
1006
1007     states[statepos].textRender = render;
1008 }
1009
1010 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1011                         double dx, double dy,
1012                         double originX, double originY,
1013                         CharCode c, int nBytes, Unicode *_u, int uLen)
1014 {
1015     int render = state->getRender();
1016     // check for invisible text -- this is used by Acrobat Capture
1017     if (render == 3) {
1018         msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1019         return;
1020     }
1021
1022     if(states[statepos].textRender != render)
1023         msg("<error> Internal error: drawChar.render!=beginString.render");
1024
1025     gfxcolor_t col = getFillColor(state);
1026
1027     Gushort *CIDToGIDMap = 0;
1028     GfxFont*font = state->getFont();
1029
1030     if(font->getType() == fontType3 && do_interpretType3Chars) {
1031         /* type 3 chars are passed as graphics */
1032         msg("<debug> type3 char at %f/%f", x, y);
1033         return;
1034     }
1035     
1036     Unicode u=0;
1037     char*name=0;
1038
1039     if(uLen)
1040         u = _u[0];
1041
1042 /*    char*fontname = getFontName(font);
1043     if(u<256 && strstr(fontname, "ingdings")) {
1044         // symbols are at 0xe000 in the unicode table
1045         u |= 0xe000;
1046     }
1047     free(fontname);*/
1048
1049     if(font->isCIDFont()) {
1050         GfxCIDFont*cfont = (GfxCIDFont*)font;
1051
1052         if(font->getType() == fontCIDType2)
1053             CIDToGIDMap = cfont->getCIDToGID();
1054     } else {
1055         Gfx8BitFont*font8;
1056         font8 = (Gfx8BitFont*)font;
1057         char**enc=font8->getEncoding();
1058         name = enc[c];
1059     }
1060     if (CIDToGIDMap) {
1061         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);
1062         c = CIDToGIDMap[c];
1063     } else {
1064         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);
1065     }
1066
1067     int charid = -1;
1068    
1069     if(uLen<=1) {
1070         charid = getGfxCharID(current_gfxfont, c, name, u);
1071     } else {
1072         charid = getGfxCharID(current_gfxfont, c, name, -1);
1073
1074         if(charid < 0) {
1075             /* multiple unicodes- should usually map to a ligature.
1076                if the ligature doesn't exist, we need to draw
1077                the characters one-by-one. */
1078             int t;
1079             msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1080             for(t=0;t<uLen;t++) {
1081                 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1082             }
1083             return;
1084         }
1085     }
1086     if(charid<0) {
1087         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1088                 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1089         return;
1090     }
1091
1092     gfxmatrix_t m = this->current_font_matrix;
1093     state->transform(x, y, &m.tx, &m.ty);
1094     m.tx += user_movex + clipmovex;
1095     m.ty += user_movey + clipmovey;
1096
1097     if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1098     {
1099         gfxline_t*l = current_gfxfont->glyphs[charid].line;
1100         double x,y;
1101         char ok = 0;
1102         while(l) {
1103             if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1104                 ok = 1;
1105             }
1106             l = l->next;
1107         }
1108         if(!ok) {
1109             msg("<warning> Drawing empty character charid=%d", charid);
1110         }
1111     }
1112
1113     if(render == RENDER_FILL) {
1114         device->drawchar(device, current_gfxfont, charid, &col, &m);
1115     } else {
1116         msg("<debug> Drawing glyph %d as shape", charid);
1117         if(!textmodeinfo) {
1118             msg("<notice> Some texts will be rendered as shape");
1119             textmodeinfo = 1;
1120         }
1121         gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1122         gfxline_t*tglyph = gfxline_clone(glyph);
1123         gfxline_transform(tglyph, &m);
1124         if((render&3) != RENDER_INVISIBLE) {
1125             gfxline_t*add = gfxline_clone(tglyph);
1126             current_text_stroke = gfxline_append(current_text_stroke, add);
1127         }
1128         if(render&RENDER_CLIP) {
1129             gfxline_t*add = gfxline_clone(tglyph);
1130             current_text_clip = gfxline_append(current_text_clip, add);
1131         }
1132         gfxline_free(tglyph);
1133     }
1134 }
1135
1136 void GFXOutputDev::endString(GfxState *state) 
1137
1138     int render = state->getRender();
1139     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1140     if(states[statepos].textRender != render)
1141         msg("<error> Internal error: drawChar.render!=beginString.render");
1142     
1143     if(current_text_stroke) {
1144         /* fillstroke and stroke text rendering objects we can process right
1145            now (as there may be texts of other rendering modes in this
1146            text object)- clipping objects have to wait until endTextObject,
1147            however */
1148         device->setparameter(device, "mark","TXT");
1149         if((render&3) == RENDER_FILL) {
1150             fillGfxLine(state, current_text_stroke);
1151             gfxline_free(current_text_stroke);
1152             current_text_stroke = 0;
1153         } else if((render&3) == RENDER_FILLSTROKE) {
1154             fillGfxLine(state, current_text_stroke);
1155             strokeGfxline(state, current_text_stroke,0);
1156             gfxline_free(current_text_stroke);
1157             current_text_stroke = 0;
1158         } else if((render&3) == RENDER_STROKE) {
1159             strokeGfxline(state, current_text_stroke,0);
1160             gfxline_free(current_text_stroke);
1161             current_text_stroke = 0;
1162         }
1163         device->setparameter(device, "mark","");
1164     }
1165 }    
1166
1167 void GFXOutputDev::endTextObject(GfxState *state)
1168 {
1169     int render = state->getRender();
1170     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1171     if(states[statepos].textRender != render)
1172         msg("<error> Internal error: drawChar.render!=beginString.render");
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].textRender = states[statepos-1].textRender;
1487     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1488     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1489     states[statepos].clipping = 0;
1490 };
1491
1492 void GFXOutputDev::restoreState(GfxState *state) {
1493   dbgindent-=2; dbg("restoreState");
1494
1495   if(statepos==0) {
1496       msg("<error> Invalid restoreState");
1497       return;
1498   }
1499   msg("<trace> restoreState");
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 void GFXOutputDev::clearSoftMask(GfxState *state)
2832 {
2833     if(!states[statepos].softmask)
2834         return;
2835     states[statepos].softmask = 0;
2836     dbg("clearSoftMask statepos=%d", statepos);
2837     msg("<verbose> clearSoftMask");
2838     
2839     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2840         msg("<error> Error in softmask/tgroup ordering");
2841         return;
2842     }
2843   
2844     gfxresult_t*mask = states[statepos].softmaskrecording;
2845     gfxresult_t*below = this->device->finish(this->device);
2846     this->device = states[statepos].olddevice;
2847
2848     /* get outline of all objects below the soft mask */
2849     gfxdevice_t uniondev;
2850     gfxdevice_union_init(&uniondev, 0);
2851     gfxresult_record_replay(below, &uniondev);
2852     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2853     uniondev.finish(&uniondev);
2854
2855     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2856 #if 0 
2857     this->device->startclip(this->device, belowoutline);
2858     gfxresult_record_replay(below, this->device);
2859     gfxresult_record_replay(mask, this->device);
2860     this->device->endclip(this->device);
2861     gfxline_free(belowoutline);
2862 #endif
2863     
2864     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2865     if(width<=0 || height<=0)
2866         return;
2867
2868     gfxdevice_t belowrender;
2869     gfxdevice_render_init(&belowrender);
2870     if(states[statepos+1].isolated) {
2871         belowrender.setparameter(&belowrender, "fillwhite", "1");
2872     }
2873     belowrender.setparameter(&belowrender, "antialize", "2");
2874     belowrender.startpage(&belowrender, width, height);
2875     gfxresult_record_replay(below, &belowrender);
2876     belowrender.endpage(&belowrender);
2877     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2878     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2879     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2880
2881     gfxdevice_t maskrender;
2882     gfxdevice_render_init(&maskrender);
2883     maskrender.startpage(&maskrender, width, height);
2884     gfxresult_record_replay(mask, &maskrender);
2885     maskrender.endpage(&maskrender);
2886     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2887     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2888
2889     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2890         msg("<fatal> Internal error in mask drawing");
2891         return;
2892     }
2893
2894     int y,x;
2895     for(y=0;y<height;y++) {
2896         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2897         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2898         for(x=0;x<width;x++) {
2899             int alpha;
2900             if(states[statepos].softmask_alpha) {
2901                 alpha = l1->a;
2902             } else {
2903                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2904             }
2905
2906             /* premultiply alpha */
2907             l2->a = div255(alpha*l2->a);
2908             l2->r = div255(alpha*l2->r);
2909             l2->g = div255(alpha*l2->g);
2910             l2->b = div255(alpha*l2->b);
2911
2912             l1++;
2913             l2++;
2914         }
2915     }
2916     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2917
2918     gfxmatrix_t matrix;
2919     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2920     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2921
2922     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2923
2924     mask->destroy(mask);
2925     below->destroy(below);
2926     maskresult->destroy(maskresult);
2927     belowresult->destroy(belowresult);
2928     states[statepos].softmaskrecording = 0;
2929 }
2930   
2931 /*class MemCheck
2932 {
2933     public: ~MemCheck()
2934     {
2935         delete globalParams;globalParams=0;
2936         Object::memCheck(stderr);
2937         gMemReport(stderr);
2938     }
2939 } myMemCheck;*/
2940