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