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