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