fixed links
[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 #if xpdfUpdateVersion >= 16
1204 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1205 #else
1206 void GFXOutputDev::drawLink(Link *link, Catalog *catalog) 
1207 #endif
1208 {
1209     double x1, y1, x2, y2, w;
1210     gfxline_t points[5];
1211     int x, y;
1212     
1213     msg("<debug> drawlink\n");
1214
1215     link->getRect(&x1, &y1, &x2, &y2);
1216     cvtUserToDev(x1, y1, &x, &y);
1217     points[0].type = gfx_moveTo;
1218     points[0].x = points[4].x = x + user_movex + clipmovex;
1219     points[0].y = points[4].y = y + user_movey + clipmovey;
1220     points[0].next = &points[1];
1221     cvtUserToDev(x2, y1, &x, &y);
1222     points[1].type = gfx_lineTo;
1223     points[1].x = x + user_movex + clipmovex;
1224     points[1].y = y + user_movey + clipmovey;
1225     points[1].next = &points[2];
1226     cvtUserToDev(x2, y2, &x, &y);
1227     points[2].type = gfx_lineTo;
1228     points[2].x = x + user_movex + clipmovex;
1229     points[2].y = y + user_movey + clipmovey;
1230     points[2].next = &points[3];
1231     cvtUserToDev(x1, y2, &x, &y);
1232     points[3].type = gfx_lineTo;
1233     points[3].x = x + user_movex + clipmovex;
1234     points[3].y = y + user_movey + clipmovey;
1235     points[3].next = &points[4];
1236     cvtUserToDev(x1, y1, &x, &y);
1237     points[4].type = gfx_lineTo;
1238     points[4].x = x + user_movex + clipmovex;
1239     points[4].y = y + user_movey + clipmovey;
1240     points[4].next = 0;
1241     
1242     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1243             points[0].x, points[0].y,
1244             points[1].x, points[1].y,
1245             points[2].x, points[2].y,
1246             points[3].x, points[3].y); 
1247
1248     LinkAction*action=link->getAction();
1249     char buf[128];
1250     char*s = 0;
1251     char*type = "-?-";
1252     char*named = 0;
1253     int page = -1;
1254     msg("<trace> drawlink action=%d\n", action->getKind());
1255     switch(action->getKind())
1256     {
1257         case actionGoTo: {
1258             type = "GoTo";
1259             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1260             LinkDest *dest=NULL;
1261             if (ha->getDest()==NULL) 
1262                 dest=catalog->findDest(ha->getNamedDest());
1263             else dest=ha->getDest();
1264             if (dest){ 
1265               if (dest->isPageRef()){
1266                 Ref pageref=dest->getPageRef();
1267                 page=catalog->findPage(pageref.num,pageref.gen);
1268               }
1269               else  page=dest->getPageNum();
1270               sprintf(buf, "%d", page);
1271               s = strdup(buf);
1272             }
1273         }
1274         break;
1275         case actionGoToR: {
1276             type = "GoToR";
1277             LinkGoToR*l = (LinkGoToR*)action;
1278             GString*g = l->getFileName();
1279             if(g)
1280              s = strdup(g->getCString());
1281             if(!s) {
1282                 /* if the GoToR link has no filename, then
1283                    try to find a refernce in the *local*
1284                    file */
1285                 GString*g = l->getNamedDest();
1286                 if(g)
1287                  s = strdup(g->getCString());
1288             }
1289         }
1290         break;
1291         case actionNamed: {
1292             type = "Named";
1293             LinkNamed*l = (LinkNamed*)action;
1294             GString*name = l->getName();
1295             if(name) {
1296                 s = strdup(name->lowerCase()->getCString());
1297                 named = name->getCString();
1298                 if(!strchr(s,':')) 
1299                 {
1300                     if(strstr(s, "next") || strstr(s, "forward"))
1301                     {
1302                         page = currentpage + 1;
1303                     }
1304                     else if(strstr(s, "prev") || strstr(s, "back"))
1305                     {
1306                         page = currentpage - 1;
1307                     }
1308                     else if(strstr(s, "last") || strstr(s, "end"))
1309                     {
1310                         if(pages && pagepos>0)
1311                             page = pages[pagepos-1];
1312                     }
1313                     else if(strstr(s, "first") || strstr(s, "top"))
1314                     {
1315                         page = 1;
1316                     }
1317                 }
1318             }
1319         }
1320         break;
1321         case actionLaunch: {
1322             type = "Launch";
1323             LinkLaunch*l = (LinkLaunch*)action;
1324             GString * str = new GString(l->getFileName());
1325             GString * params = l->getParams();
1326             if(params)
1327                 str->append(params);
1328             s = strdup(str->getCString());
1329             delete str;
1330         }
1331         break;
1332         case actionURI: {
1333             char*url = 0;
1334             type = "URI";
1335             LinkURI*l = (LinkURI*)action;
1336             GString*g = l->getURI();
1337             if(g) {
1338              url = g->getCString();
1339              s = strdup(url);
1340             }
1341         }
1342         break;
1343         case actionUnknown: {
1344             type = "Unknown";
1345             LinkUnknown*l = (LinkUnknown*)action;
1346             s = strdup("");
1347         }
1348         break;
1349         default: {
1350             msg("<error> Unknown link type!\n");
1351             break;
1352         }
1353     }
1354
1355     if(!s) s = strdup("-?-");
1356     
1357     msg("<trace> drawlink s=%s\n", s);
1358
1359     if(!linkinfo && (page || s))
1360     {
1361         msg("<notice> File contains links");
1362         linkinfo = 1;
1363     }
1364     
1365     if(page>0)
1366     {
1367         int t;
1368         int lpage = -1;
1369         for(t=1;t<=pagepos;t++) {
1370             if(pages[t]==page) {
1371                 lpage = t;
1372                 break;
1373             }
1374         }
1375         if(lpage<0) {
1376             lpage = page;
1377         }
1378         char buf[80];
1379         sprintf(buf, "page%d", lpage);
1380         device->drawlink(device, points, buf);
1381     }
1382     else if(s)
1383     {
1384         device->drawlink(device, points, s);
1385     }
1386
1387     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1388     free(s);s=0;
1389 }
1390
1391 void GFXOutputDev::saveState(GfxState *state) {
1392     dbg("saveState");dbgindent+=2;
1393
1394     msg("<trace> saveState\n");
1395     updateAll(state);
1396     if(statepos>=64) {
1397       msg("<error> Too many nested states in pdf.");
1398       return;
1399     }
1400     statepos ++;
1401     states[statepos].textRender = states[statepos-1].textRender;
1402     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1403     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1404     states[statepos].clipping = 0;
1405 };
1406
1407 void GFXOutputDev::restoreState(GfxState *state) {
1408   dbgindent-=2; dbg("restoreState");
1409
1410   if(statepos==0) {
1411       msg("<error> Invalid restoreState");
1412       return;
1413   }
1414   msg("<trace> restoreState");
1415   if(states[statepos].softmask) {
1416       clearSoftMask(state);
1417   }
1418   updateAll(state);
1419   while(states[statepos].clipping) {
1420       device->endclip(device);
1421       states[statepos].clipping--;
1422   }
1423   statepos--;
1424 }
1425
1426 char* GFXOutputDev::searchFont(char*name) 
1427 {       
1428     int i;
1429     char*filename=0;
1430     int is_standard_font = 0;
1431         
1432     msg("<verbose> SearchFont(%s)", name);
1433
1434     /* see if it is a pdf standard font */
1435     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
1436     {
1437         if(!strcmp(name, pdf2t1map[i].pdffont))
1438         {
1439             name = pdf2t1map[i].filename;
1440             is_standard_font = 1;
1441             break;
1442         }
1443     }
1444     /* look in all font files */
1445     for(i=0;i<fontnum;i++) 
1446     {
1447         if(strstr(fonts[i].filename, name))
1448         {
1449             if(!fonts[i].used) {
1450
1451                 fonts[i].used = 1;
1452                 if(!is_standard_font)
1453                     msg("<notice> Using %s for %s", fonts[i].filename, name);
1454             }
1455             return strdup(fonts[i].filename);
1456         }
1457     }
1458     return 0;
1459 }
1460
1461 void GFXOutputDev::updateLineWidth(GfxState *state)
1462 {
1463     double width = state->getTransformedLineWidth();
1464     //swfoutput_setlinewidth(&device, width);
1465 }
1466
1467 void GFXOutputDev::updateLineCap(GfxState *state)
1468 {
1469     int c = state->getLineCap();
1470 }
1471
1472 void GFXOutputDev::updateLineJoin(GfxState *state)
1473 {
1474     int j = state->getLineJoin();
1475 }
1476
1477 void GFXOutputDev::updateFillColor(GfxState *state) 
1478 {
1479     GfxRGB rgb;
1480     double opaq = state->getFillOpacity();
1481     state->getFillRGB(&rgb);
1482 }
1483 void GFXOutputDev::updateFillOpacity(GfxState *state)
1484 {
1485     GfxRGB rgb;
1486     double opaq = state->getFillOpacity();
1487     state->getFillRGB(&rgb);
1488     dbg("update fillopaq %f", opaq);
1489 }
1490 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1491 {
1492     double opaq = state->getFillOpacity();
1493     dbg("update strokeopaq %f", opaq);
1494 }
1495 void GFXOutputDev::updateFillOverprint(GfxState *state)
1496 {
1497     double opaq = state->getFillOverprint();
1498     dbg("update filloverprint %f", opaq);
1499 }
1500 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1501 {
1502     double opaq = state->getStrokeOverprint();
1503     dbg("update strokeoverprint %f", opaq);
1504 }
1505 void GFXOutputDev::updateTransfer(GfxState *state)
1506 {
1507     dbg("update transfer");
1508 }
1509
1510
1511 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1512 {
1513     GfxRGB rgb;
1514     double opaq = state->getStrokeOpacity();
1515     state->getStrokeRGB(&rgb);
1516 }
1517
1518 void FoFiWrite(void *stream, char *data, int len)
1519 {
1520    int ret = fwrite(data, len, 1, (FILE*)stream);
1521 }
1522
1523 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1524 {
1525     char*tmpFileName = NULL;
1526     FILE *f;
1527     int c;
1528     char *fontBuf;
1529     int fontLen;
1530     Ref embRef;
1531     Object refObj, strObj;
1532     char namebuf[512];
1533     tmpFileName = mktmpname(namebuf);
1534     int ret;
1535
1536     ret = font->getEmbeddedFontID(&embRef);
1537     if(!ret) {
1538         msg("<verbose> Didn't get embedded font id");
1539         /* not embedded- the caller should now search the font
1540            directories for this font */
1541         return 0;
1542     }
1543
1544     f = fopen(tmpFileName, "wb");
1545     if (!f) {
1546       msg("<error> Couldn't create temporary Type 1 font file");
1547         return 0;
1548     }
1549
1550     /*if(font->isCIDFont()) {
1551         GfxCIDFont* cidFont = (GfxCIDFont *)font;
1552         GString c = cidFont->getCollection();
1553         msg("<notice> Collection: %s", c.getCString());
1554     }*/
1555
1556     //if (font->getType() == fontType1C) {
1557     if (0) { //font->getType() == fontType1C) {
1558       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1559         fclose(f);
1560         msg("<error> Couldn't read embedded font file");
1561         return 0;
1562       }
1563       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1564       if(!cvt) return 0;
1565       cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1566       //cvt->convertToCIDType0("test", f);
1567       //cvt->convertToType0("test", f);
1568       delete cvt;
1569       gfree(fontBuf);
1570     } else if(font->getType() == fontTrueType) {
1571       msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1572       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1573         fclose(f);
1574         msg("<error> Couldn't read embedded font file");
1575         return 0;
1576       }
1577       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1578       cvt->writeTTF(FoFiWrite, f);
1579       delete cvt;
1580       gfree(fontBuf);
1581     } else {
1582       font->getEmbeddedFontID(&embRef);
1583       refObj.initRef(embRef.num, embRef.gen);
1584       refObj.fetch(ref, &strObj);
1585       refObj.free();
1586       strObj.streamReset();
1587       int f4[4];
1588       char f4c[4];
1589       int t;
1590       for(t=0;t<4;t++) {
1591           f4[t] = strObj.streamGetChar();
1592           f4c[t] = (char)f4[t];
1593           if(f4[t] == EOF)
1594               break;
1595       }
1596       if(t==4) {
1597           if(!strncmp(f4c, "true", 4)) {
1598               /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1599                  Change this on the fly */
1600               f4[0] = f4[2] = f4[3] = 0;
1601               f4[1] = 1;
1602           }
1603           fputc(f4[0], f);
1604           fputc(f4[1], f);
1605           fputc(f4[2], f);
1606           fputc(f4[3], f);
1607
1608           while ((c = strObj.streamGetChar()) != EOF) {
1609             fputc(c, f);
1610           }
1611       }
1612       strObj.streamClose();
1613       strObj.free();
1614     }
1615     fclose(f);
1616
1617     return strdup(tmpFileName);
1618 }
1619     
1620 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1621 {
1622     char*name = getFontName(gfxFont);
1623     char*fontname = 0;
1624     char*filename = 0;
1625
1626     if(!this->config_use_fontconfig)
1627         return 0;
1628     
1629 #ifdef HAVE_FONTCONFIG
1630     FcPattern *pattern, *match;
1631     FcResult result;
1632     FcChar8 *v;
1633
1634     static int fcinitcalled = false; 
1635         
1636     msg("<debug> searchForSuitableFont(%s)", name);
1637     
1638     // call init ony once
1639     if (!fcinitcalled) {
1640         msg("<debug> Initializing FontConfig...");
1641         fcinitcalled = true;
1642         if(!FcInit()) {
1643             msg("<debug> FontConfig Initialization failed. Disabling.");
1644             config_use_fontconfig = 0;
1645             return 0;
1646         }
1647         msg("<debug> ...initialized FontConfig");
1648     }
1649    
1650     msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1651     pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1652     if (gfxFont->isItalic()) // check for italic
1653         msg("<debug> FontConfig: Adding Italic Slant");
1654         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1655     if (gfxFont->isBold()) // check for bold
1656         msg("<debug> FontConfig: Adding Bold Weight");
1657         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1658
1659     msg("<debug> FontConfig: Try to match...");
1660     // configure and match using the original font name 
1661     FcConfigSubstitute(0, pattern, FcMatchPattern); 
1662     FcDefaultSubstitute(pattern);
1663     match = FcFontMatch(0, pattern, &result);
1664     
1665     if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1666         msg("<debug> FontConfig: family=%s", (char*)v);
1667         // if we get an exact match
1668         if (strcmp((char *)v, name) == 0) {
1669             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1670                 filename = strdup((char*)v); // mem leak
1671                 char *nfn = strrchr(filename, '/');
1672                 if(nfn) fontname = strdup(nfn+1);
1673                 else    fontname = filename;
1674             }
1675             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1676         } else {
1677             // initialize patterns
1678             FcPatternDestroy(pattern);
1679             FcPatternDestroy(match);
1680
1681             // now match against serif etc.
1682             if (gfxFont->isSerif()) {
1683                 msg("<debug> FontConfig: Create Serif Family Pattern");
1684                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1685             } else if (gfxFont->isFixedWidth()) {
1686                 msg("<debug> FontConfig: Create Monospace Family Pattern");
1687                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1688             } else {
1689                 msg("<debug> FontConfig: Create Sans Family Pattern");
1690                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1691             }
1692
1693             // check for italic
1694             if (gfxFont->isItalic()) {
1695                 msg("<debug> FontConfig: Adding Italic Slant");
1696                 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1697             }
1698             // check for bold
1699             if (gfxFont->isBold()) {
1700                 msg("<debug> FontConfig: Adding Bold Weight");
1701                 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1702             }
1703
1704             msg("<debug> FontConfig: Try to match... (2)");
1705             // configure and match using serif etc
1706             FcConfigSubstitute (0, pattern, FcMatchPattern);
1707             FcDefaultSubstitute (pattern);
1708             match = FcFontMatch (0, pattern, &result);
1709             
1710             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1711                 filename = strdup((char*)v); // mem leak
1712                 char *nfn = strrchr(filename, '/');
1713                 if(nfn) fontname = strdup(nfn+1);
1714                 else    fontname = filename;
1715             }
1716             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1717         }        
1718     }
1719
1720     //printf("FONTCONFIG: pattern");
1721     //FcPatternPrint(pattern);
1722     //printf("FONTCONFIG: match");
1723     //FcPatternPrint(match);
1724  
1725     FcPatternDestroy(pattern);
1726     FcPatternDestroy(match);
1727
1728     pdfswf_addfont(filename);
1729     return fontname;
1730 #else
1731     return 0;
1732 #endif
1733 }
1734
1735 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1736 {
1737     char*fontname = 0, *filename = 0;
1738     msg("<notice> substituteFont(%s)", oldname);
1739
1740     if(!(fontname = searchForSuitableFont(gfxFont))) {
1741         fontname = "Times-Roman";
1742     }
1743     filename = searchFont(fontname);
1744     if(!filename) {
1745         msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1746         return 0;
1747     }
1748
1749     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1750         msg("<fatal> Too many fonts in file.");
1751         exit(1);
1752     }
1753     if(oldname) {
1754         substitutesource[substitutepos] = strdup(oldname); //mem leak
1755         substitutetarget[substitutepos] = fontname;
1756         msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1757         substitutepos ++;
1758     }
1759     return strdup(filename); //mem leak
1760 }
1761
1762 void unlinkfont(char* filename)
1763 {
1764     int l;
1765     if(!filename)
1766         return;
1767     l=strlen(filename);
1768     unlink(filename);
1769     if(!strncmp(&filename[l-4],".afm",4)) {
1770         memcpy(&filename[l-4],".pfb",4);
1771         unlink(filename);
1772         memcpy(&filename[l-4],".pfa",4);
1773         unlink(filename);
1774         memcpy(&filename[l-4],".afm",4);
1775         return;
1776     } else 
1777     if(!strncmp(&filename[l-4],".pfa",4)) {
1778         memcpy(&filename[l-4],".afm",4);
1779         unlink(filename);
1780         memcpy(&filename[l-4],".pfa",4);
1781         return;
1782     } else 
1783     if(!strncmp(&filename[l-4],".pfb",4)) {
1784         memcpy(&filename[l-4],".afm",4);
1785         unlink(filename);
1786         memcpy(&filename[l-4],".pfb",4);
1787         return;
1788     }
1789 }
1790
1791 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1792 {
1793     this->doc = doc;
1794     this->xref = xref;
1795 }
1796
1797 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1798 {
1799     gfxfont_t*font = 0;
1800     fontlist_t*last=0,*l = this->fontlist;
1801
1802     if(!id)
1803         msg("<error> Internal Error: FontID is null");
1804
1805     /* TODO: should this be part of the state? */
1806     while(l) {
1807         last = l;
1808         if(!strcmp(l->font->id, id)) {
1809             current_gfxfont = l->font;
1810             font = l->font;
1811             device->addfont(device, current_gfxfont);
1812             return 1;
1813         }
1814         l = l->next;
1815     }
1816     if(!filename) return 0;
1817
1818     /* A font size of e.g. 9 means the font will be scaled down by
1819        1024 and scaled up by 9. So to have a maximum error of 1/20px,
1820        we have to divide 0.05 by (fontsize/1024)
1821      */
1822     double quality = (1024 * 0.05) / maxSize;
1823    
1824     msg("<verbose> Loading %s...", filename);
1825     font = gfxfont_load(id, filename, quality);
1826     if(!font) {
1827         msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1828         return 0;
1829     }
1830     msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1831
1832     l = new fontlist_t;
1833     l->font = font;
1834     l->filename = strdup(filename);
1835     l->next = 0;
1836     current_gfxfont = l->font;
1837     if(last) {
1838         last->next = l;
1839     } else {
1840         this->fontlist = l;
1841     }
1842     device->addfont(device, current_gfxfont);
1843     return 1;
1844 }
1845
1846 void GFXOutputDev::updateFont(GfxState *state) 
1847 {
1848     GfxFont*gfxFont = state->getFont();
1849       
1850     if (!gfxFont) {
1851         return;
1852     }  
1853     
1854     char * fontid = getFontID(gfxFont);
1855     char * fontname = getFontName(gfxFont);
1856
1857     double maxSize = 1.0;
1858
1859     if(this->info) {
1860         maxSize = this->info->getMaximumFontSize(fontid);
1861     }
1862     
1863     int t;
1864     /* first, look if we substituted this font before-
1865        this way, we don't initialize the T1 Fonts
1866        too often */
1867     for(t=0;t<substitutepos;t++) {
1868         if(!strcmp(fontid, substitutesource[t])) {
1869             free(fontid);fontid=0;
1870             fontid = strdup(substitutetarget[t]);
1871             break;
1872         }
1873     }
1874
1875     /* second, see if this is a font which was used before-
1876        if so, we are done */
1877     if(setGfxFont(fontid, fontname, 0, 0)) {
1878         free(fontid);
1879         free(fontname);
1880         return;
1881     }
1882 /*    if(swfoutput_queryfont(&device, fontid))
1883         swfoutput_setfont(&device, fontid, 0);
1884         
1885         msg("<debug> updateFont(%s) [cached]", fontid);
1886         return;
1887     }*/
1888
1889     // look for Type 3 font
1890     if (gfxFont->getType() == fontType3) {
1891         if(!type3Warning) {
1892             type3Warning = gTrue;
1893             showFontError(gfxFont, 2);
1894         }
1895         free(fontid);
1896         free(fontname);
1897         return;
1898     }
1899
1900     /* now either load the font, or find a substitution */
1901
1902     Ref embRef;
1903     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1904
1905     char*fileName = 0;
1906     int del = 0;
1907     if(embedded &&
1908        (gfxFont->getType() == fontType1 ||
1909         gfxFont->getType() == fontType1C ||
1910         gfxFont->getType() == fontCIDType0C ||
1911         gfxFont->getType() == fontTrueType ||
1912         gfxFont->getType() == fontCIDType2
1913        ))
1914     {
1915       fileName = writeEmbeddedFontToFile(xref, gfxFont);
1916       if(!fileName) showFontError(gfxFont,0);
1917       else del = 1;
1918     } else {
1919       fileName = searchFont(fontname);
1920       if(!fileName) showFontError(gfxFont,0);
1921     }
1922     if(!fileName) {
1923         char * fontname = getFontName(gfxFont);
1924         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1925         
1926         if(lastfontdir)
1927             msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1928         else
1929             msg("<warning> Try specifying one or more font directories");
1930
1931         fileName = substituteFont(gfxFont, fontid);
1932         if(!fileName)
1933             exit(1);
1934         if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1935         msg("<notice> Font is now %s (%s)", fontid, fileName);
1936     }
1937
1938     if(!fileName) {
1939         msg("<error> Couldn't set font %s\n", fontid);
1940         free(fontid);
1941         free(fontname);
1942         return;
1943     }
1944         
1945     msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1946     dumpFontInfo("<verbose>", gfxFont);
1947
1948     //swfoutput_setfont(&device, fontid, fileName);
1949     
1950     if(!setGfxFont(fontid, fontname, 0, 0)) {
1951         setGfxFont(fontid, fontname, fileName, maxSize);
1952     }
1953    
1954     if(fileName && del)
1955         unlinkfont(fileName);
1956
1957     if(fileName)
1958         free(fileName);
1959     free(fontid);
1960     free(fontname);
1961
1962     msg("<verbose> |");
1963 }
1964
1965 #define SQR(x) ((x)*(x))
1966
1967 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1968 {
1969     if((newwidth<2 || newheight<2) ||
1970        (width<=newwidth || height<=newheight))
1971         return 0;
1972     unsigned char*newdata;
1973     int x,y;
1974     newdata= (unsigned char*)malloc(newwidth*newheight);
1975     int t;
1976     double fx = (double)(width)/newwidth;
1977     double fy = (double)(height)/newheight;
1978     double px = 0;
1979     int blocksize = (int)(8192/(fx*fy));
1980     int r = 8192*256/palettesize;
1981     for(x=0;x<newwidth;x++) {
1982         double ex = px + fx;
1983         int fromx = (int)px;
1984         int tox = (int)ex;
1985         int xweight1 = (int)(((fromx+1)-px)*256);
1986         int xweight2 = (int)((ex-tox)*256);
1987         double py =0;
1988         for(y=0;y<newheight;y++) {
1989             double ey = py + fy;
1990             int fromy = (int)py;
1991             int toy = (int)ey;
1992             int yweight1 = (int)(((fromy+1)-py)*256);
1993             int yweight2 = (int)((ey-toy)*256);
1994             int a = 0;
1995             int xx,yy;
1996             for(xx=fromx;xx<=tox;xx++)
1997             for(yy=fromy;yy<=toy;yy++) {
1998                 int b = 1-data[width*yy+xx];
1999                 int weight=256;
2000                 if(xx==fromx) weight = (weight*xweight1)/256;
2001                 if(xx==tox) weight = (weight*xweight2)/256;
2002                 if(yy==fromy) weight = (weight*yweight1)/256;
2003                 if(yy==toy) weight = (weight*yweight2)/256;
2004                 a+=b*weight;
2005             }
2006             //if(a) a=(palettesize-1)*r/blocksize;
2007             newdata[y*newwidth+x] = (a*blocksize)/r;
2008             py = ey;
2009         }
2010         px = ex;
2011     }
2012     return newdata;
2013 }
2014
2015 #define IMAGE_TYPE_JPEG 0
2016 #define IMAGE_TYPE_LOSSLESS 1
2017
2018 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2019         double x1,double y1,
2020         double x2,double y2,
2021         double x3,double y3,
2022         double x4,double y4, int type)
2023 {
2024     gfxcolor_t*newpic=0;
2025     
2026     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2027     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2028    
2029     gfxline_t p1,p2,p3,p4,p5;
2030     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2031     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2032     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2033     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2034     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2035
2036     {p1.x = (int)(p1.x*20)/20.0;
2037      p1.y = (int)(p1.y*20)/20.0;
2038      p2.x = (int)(p2.x*20)/20.0;
2039      p2.y = (int)(p2.y*20)/20.0;
2040      p3.x = (int)(p3.x*20)/20.0;
2041      p3.y = (int)(p3.y*20)/20.0;
2042      p4.x = (int)(p4.x*20)/20.0;
2043      p4.y = (int)(p4.y*20)/20.0;
2044      p5.x = (int)(p5.x*20)/20.0;
2045      p5.y = (int)(p5.y*20)/20.0;
2046     }
2047     
2048     float m00,m10,tx;
2049     float m01,m11,ty;
2050     
2051     gfxmatrix_t m;
2052     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2053     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2054     m.tx = p1.x - 0.5;
2055     m.ty = p1.y - 0.5;
2056
2057     gfximage_t img;
2058     img.data = (gfxcolor_t*)data;
2059     img.width = sizex;
2060     img.height = sizey;
2061   
2062     if(type == IMAGE_TYPE_JPEG)
2063         /* TODO: pass image_dpi to device instead */
2064         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2065
2066     dev->fillbitmap(dev, &p1, &img, &m, 0);
2067 }
2068
2069 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2070         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2071 {
2072     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2073 }
2074
2075 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2076         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2077 {
2078     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2079 }
2080
2081
2082 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2083                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2084                                    GBool inlineImg, int mask, int*maskColors,
2085                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2086 {
2087   double x1,y1,x2,y2,x3,y3,x4,y4;
2088   ImageStream *imgStr;
2089   Guchar pixBuf[4];
2090   GfxRGB rgb;
2091   int ncomps = 1;
2092   int bits = 1;
2093   unsigned char* maskbitmap = 0;
2094                                  
2095   if(colorMap) {
2096     ncomps = colorMap->getNumPixelComps();
2097     bits = colorMap->getBits();
2098   }
2099       
2100   if(maskStr) {
2101       int x,y;
2102       unsigned char buf[8];
2103       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2104       if(maskColorMap) {
2105           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2106           imgMaskStr->reset();
2107           unsigned char pal[256];
2108           int n = 1 << colorMap->getBits();
2109           int t;
2110           for(t=0;t<n;t++) {
2111               GfxGray gray;
2112               pixBuf[0] = t;
2113               maskColorMap->getGray(pixBuf, &gray);
2114               pal[t] = colToByte(gray);
2115           }
2116           for (y = 0; y < maskHeight; y++) {
2117               for (x = 0; x < maskWidth; x++) {
2118                   imgMaskStr->getPixel(buf);
2119                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2120               }
2121           }
2122           delete imgMaskStr;
2123       } else {
2124           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2125           imgMaskStr->reset();
2126           for (y = 0; y < maskHeight; y++) {
2127               for (x = 0; x < maskWidth; x++) {
2128                   imgMaskStr->getPixel(buf);
2129                   buf[0]^=maskInvert;
2130                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2131               }
2132           }
2133           delete imgMaskStr;
2134       }
2135       maskStr->close();
2136   }
2137   
2138   imgStr = new ImageStream(str, width, ncomps,bits);
2139   imgStr->reset();
2140
2141   if(!width || !height || (height<=1 && width<=1))
2142   {
2143       msg("<verbose> Ignoring %d by %d image", width, height);
2144       unsigned char buf[8];
2145       int x,y;
2146       for (y = 0; y < height; ++y)
2147       for (x = 0; x < width; ++x) {
2148           imgStr->getPixel(buf);
2149       }
2150       delete imgStr;
2151       if(maskbitmap)
2152           free(maskbitmap);
2153       return;
2154   }
2155
2156   state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2157   state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2158   state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2159   state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2160
2161   if(!pbminfo && !(str->getKind()==strDCT)) {
2162       if(!type3active) {
2163           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2164           pbminfo = 1;
2165       }
2166       if(mask)
2167       msg("<verbose> drawing %d by %d masked picture\n", width, height);
2168   }
2169   if(!jpeginfo && (str->getKind()==strDCT)) {
2170       msg("<notice> file contains jpeg pictures");
2171       jpeginfo = 1;
2172   }
2173
2174   if(mask) {
2175       int i,j;
2176       unsigned char buf[8];
2177       int x,y;
2178       unsigned char*pic = new unsigned char[width*height];
2179       gfxcolor_t pal[256];
2180       GfxRGB rgb;
2181       state->getFillRGB(&rgb);
2182
2183       memset(pal,255,sizeof(pal));
2184       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2185       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2186       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2187       pal[0].a = 255;              pal[1].a = 0;
2188     
2189       int numpalette = 2;
2190       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2191       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2192       for (y = 0; y < height; ++y)
2193       for (x = 0; x < width; ++x)
2194       {
2195             imgStr->getPixel(buf);
2196             if(invert) 
2197                 buf[0]=1-buf[0];
2198             pic[width*y+x] = buf[0];
2199       }
2200       
2201       /* the size of the drawn image is added to the identifier
2202          as the same image may require different bitmaps if displayed
2203          at different sizes (due to antialiasing): */
2204       int t,found = -1;
2205       if(type3active) {
2206           unsigned char*pic2 = 0;
2207           numpalette = 16;
2208           
2209           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2210
2211           if(!pic2) {
2212             delete pic;
2213             delete imgStr;
2214             return;
2215           }
2216
2217           width = realwidth;
2218           height = realheight;
2219           free(pic);
2220           pic = pic2;
2221           
2222           /* make a black/white palette */
2223
2224           float r = 255/(numpalette-1);
2225           int t;
2226           for(t=0;t<numpalette;t++) {
2227               pal[t].r = colToByte(rgb.r);
2228               pal[t].g = colToByte(rgb.g);
2229               pal[t].b = colToByte(rgb.b);
2230               pal[t].a = (unsigned char)(t*r);
2231           }
2232       }
2233
2234       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2235       for (y = 0; y < height; ++y) {
2236         for (x = 0; x < width; ++x) {
2237           pic2[width*y+x] = pal[pic[y*width+x]];
2238         }
2239       }
2240       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2241       free(pic2);
2242       free(pic);
2243       delete imgStr;
2244       if(maskbitmap) free(maskbitmap);
2245       return;
2246   }
2247
2248   int x,y;
2249
2250   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2251       gfxcolor_t*pic=new gfxcolor_t[width*height];
2252       for (y = 0; y < height; ++y) {
2253         for (x = 0; x < width; ++x) {
2254           imgStr->getPixel(pixBuf);
2255           colorMap->getRGB(pixBuf, &rgb);
2256           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2257           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2258           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2259           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2260           if(maskbitmap) {
2261               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2262           }
2263         }
2264       }
2265       if(str->getKind()==strDCT)
2266           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2267       else
2268           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2269       delete pic;
2270       delete imgStr;
2271       if(maskbitmap) free(maskbitmap);
2272       return;
2273   } else {
2274       gfxcolor_t*pic=new gfxcolor_t[width*height];
2275       gfxcolor_t pal[256];
2276       int n = 1 << colorMap->getBits();
2277       int t;
2278       for(t=0;t<256;t++) {
2279           pixBuf[0] = t;
2280           colorMap->getRGB(pixBuf, &rgb);
2281
2282           {/*if(maskColors && *maskColors==t) {
2283               msg("<notice> Color %d is transparent", t);
2284               if (imgData->maskColors) {
2285                 *alpha = 0;
2286                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2287                   if (pix[i] < imgData->maskColors[2*i] ||
2288                       pix[i] > imgData->maskColors[2*i+1]) {
2289                     *alpha = 1;
2290                     break;
2291                   }
2292                 }
2293               } else {
2294                 *alpha = 1;
2295               }
2296               if(!*alpha) {
2297                     pal[t].r = 0;
2298                     pal[t].g = 0;
2299                     pal[t].b = 0;
2300                     pal[t].a = 0;
2301               }
2302           } else {*/
2303               pal[t].r = (unsigned char)(colToByte(rgb.r));
2304               pal[t].g = (unsigned char)(colToByte(rgb.g));
2305               pal[t].b = (unsigned char)(colToByte(rgb.b));
2306               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2307           }
2308       }
2309       for (y = 0; y < height; ++y) {
2310         for (x = 0; x < width; ++x) {
2311           imgStr->getPixel(pixBuf);
2312           pic[width*y+x] = pal[pixBuf[0]];
2313           if(maskbitmap) {
2314               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2315           }
2316         }
2317       }
2318       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2319
2320       delete pic;
2321       delete imgStr;
2322       if(maskbitmap) free(maskbitmap);
2323       return;
2324   }
2325 }
2326
2327 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2328                                    int width, int height, GBool invert,
2329                                    GBool inlineImg) 
2330 {
2331     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2332     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2333     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2334 }
2335
2336 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2337                          int width, int height, GfxImageColorMap *colorMap,
2338                          int *maskColors, GBool inlineImg)
2339 {
2340     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2341             colorMap?"colorMap":"no colorMap", 
2342             maskColors?"maskColors":"no maskColors",
2343             inlineImg);
2344     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2345             colorMap?"colorMap":"no colorMap", 
2346             maskColors?"maskColors":"no maskColors",
2347             inlineImg);
2348     if(colorMap)
2349         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2350                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2351     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2352 }
2353   
2354 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2355                                int width, int height,
2356                                GfxImageColorMap *colorMap,
2357                                Stream *maskStr, int maskWidth, int maskHeight,
2358                                GBool maskInvert)
2359 {
2360     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2361             colorMap?"colorMap":"no colorMap", 
2362             maskWidth, maskHeight);
2363     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2364             colorMap?"colorMap":"no colorMap", 
2365             maskWidth, maskHeight);
2366     if(colorMap)
2367         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2368                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2369     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2370 }
2371
2372 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2373                                    int width, int height,
2374                                    GfxImageColorMap *colorMap,
2375                                    Stream *maskStr,
2376                                    int maskWidth, int maskHeight,
2377                                    GfxImageColorMap *maskColorMap)
2378 {
2379     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2380             colorMap?"colorMap":"no colorMap", 
2381             maskWidth, maskHeight);
2382     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2383             colorMap?"colorMap":"no colorMap", 
2384             maskWidth, maskHeight);
2385     if(colorMap)
2386         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2387                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2388     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2389 }
2390
2391 void GFXOutputDev::stroke(GfxState *state) 
2392 {
2393     dbg("stroke");
2394
2395     GfxPath * path = state->getPath();
2396     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2397     strokeGfxline(state, line);
2398     gfxline_free(line);
2399 }
2400
2401 void GFXOutputDev::fill(GfxState *state) 
2402 {
2403     gfxcolor_t col = getFillColor(state);
2404     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2405
2406     GfxPath * path = state->getPath();
2407     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2408     fillGfxLine(state, line);
2409     gfxline_free(line);
2410 }
2411
2412 void GFXOutputDev::eoFill(GfxState *state) 
2413 {
2414     gfxcolor_t col = getFillColor(state);
2415     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2416
2417     GfxPath * path = state->getPath();
2418     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2419     fillGfxLine(state, line);
2420     gfxline_free(line);
2421 }
2422
2423
2424 static char* dirseparator()
2425 {
2426 #ifdef WIN32
2427     return "\\";
2428 #else
2429     return "/";
2430 #endif
2431 }
2432
2433 void addGlobalFont(char*filename)
2434 {
2435     fontfile_t f;
2436     memset(&f, 0, sizeof(fontfile_t));
2437     f.filename = filename;
2438     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2439         msg("<verbose> Adding font \"%s\".", filename);
2440         fonts[fontnum++] = f;
2441     } else {
2442         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2443     }
2444 }
2445
2446 void addGlobalLanguageDir(char*dir)
2447 {
2448     if(!globalParams)
2449         globalParams = new GlobalParams("");
2450     
2451     msg("<notice> Adding %s to language pack directories", dir);
2452
2453     int l;
2454     FILE*fi = 0;
2455     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2456     strcpy(config_file, dir);
2457     strcat(config_file, dirseparator());
2458     strcat(config_file, "add-to-xpdfrc");
2459
2460     fi = fopen(config_file, "rb");
2461     if(!fi) {
2462         msg("<error> Could not open %s", config_file);
2463         return;
2464     }
2465     globalParams->parseFile(new GString(config_file), fi);
2466     fclose(fi);
2467 }
2468
2469 void addGlobalFontDir(char*dirname)
2470 {
2471 #ifdef HAVE_DIRENT_H
2472     msg("<notice> Adding %s to font directories", dirname);
2473     lastfontdir = strdup(dirname);
2474     DIR*dir = opendir(dirname);
2475     if(!dir) {
2476         msg("<warning> Couldn't open directory %s\n", dirname);
2477         return;
2478     }
2479     struct dirent*ent;
2480     while(1) {
2481         ent = readdir (dir);
2482         if (!ent) 
2483             break;
2484         int l;
2485         char*name = ent->d_name;
2486         char type = 0;
2487         if(!name) continue;
2488         l=strlen(name);
2489         if(l<4)
2490             continue;
2491         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2492             type=1;
2493         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2494             type=3;
2495         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2496             type=2;
2497         if(type)
2498         {
2499             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2500             strcpy(fontname, dirname);
2501             strcat(fontname, dirseparator());
2502             strcat(fontname, name);
2503             addGlobalFont(fontname);
2504         }
2505     }
2506     closedir(dir);
2507 #else
2508     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2509 #endif
2510 }
2511
2512 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2513 {
2514     if(pdfpage < 0)
2515         return;
2516
2517     if(!this->pages) {
2518         this->pagebuflen = 1024;
2519         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2520         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2521     } else {
2522         while(pdfpage >= this->pagebuflen)
2523         {
2524             int oldlen = this->pagebuflen;
2525             this->pagebuflen+=1024;
2526             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2527             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2528         }
2529     }
2530     this->pages[pdfpage] = outputpage;
2531     if(pdfpage>this->pagepos)
2532         this->pagepos = pdfpage;
2533 }
2534
2535 struct BBox
2536 {
2537     double posx,posy;
2538     double width,height;
2539 };
2540
2541 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2542 {
2543     double xMin, yMin, xMax, yMax, x, y;
2544     double tx, ty, w, h;
2545     // transform the bbox
2546     state->transform(bbox[0], bbox[1], &x, &y);
2547     xMin = xMax = x;
2548     yMin = yMax = y;
2549     state->transform(bbox[0], bbox[3], &x, &y);
2550     if (x < xMin) {
2551       xMin = x;
2552     } else if (x > xMax) {
2553       xMax = x;
2554     }
2555     if (y < yMin) {
2556       yMin = y;
2557     } else if (y > yMax) {
2558       yMax = y;
2559     }
2560     state->transform(bbox[2], bbox[1], &x, &y);
2561     if (x < xMin) {
2562       xMin = x;
2563     } else if (x > xMax) {
2564       xMax = x;
2565     }
2566     if (y < yMin) {
2567       yMin = y;
2568     } else if (y > yMax) {
2569       yMax = y;
2570     }
2571     state->transform(bbox[2], bbox[3], &x, &y);
2572     if (x < xMin) {
2573       xMin = x;
2574     } else if (x > xMax) {
2575       xMax = x;
2576     }
2577     if (y < yMin) {
2578       yMin = y;
2579     } else if (y > yMax) {
2580       yMax = y;
2581     }
2582     tx = (int)floor(xMin);
2583     if (tx < 0) {
2584       tx = 0;
2585     } else if (tx > width) {
2586       tx = width;
2587     }
2588     ty = (int)floor(yMin);
2589     if (ty < 0) {
2590       ty = 0;
2591     } else if (ty > height) {
2592       ty = height;
2593     }
2594     w = (int)ceil(xMax) - tx + 1;
2595     if (tx + w > width) {
2596       w = width - tx;
2597     }
2598     if (w < 1) {
2599       w = 1;
2600     }
2601     h = (int)ceil(yMax) - ty + 1;
2602     if (ty + h > height) {
2603       h = height - ty;
2604     }
2605     if (h < 1) {
2606       h = 1;
2607     }
2608     BBox nbbox;
2609     nbbox.posx = xMin;
2610     nbbox.posx = yMin;
2611     nbbox.width = w;
2612     nbbox.height = h;
2613     return nbbox;
2614 }
2615
2616 #if xpdfUpdateVersion >= 16
2617 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2618                                       GfxColorSpace *blendingColorSpace,
2619                                       GBool isolated, GBool knockout,
2620                                       GBool forSoftMask)
2621 {
2622     char*colormodename = "";
2623     BBox rect = mkBBox(state, bbox, this->width, this->height);
2624
2625     if(blendingColorSpace) {
2626         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2627     }
2628     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);
2629     dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2630     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);
2631     states[statepos].createsoftmask |= forSoftMask;
2632     states[statepos].transparencygroup = !forSoftMask;
2633     states[statepos].olddevice = this->device;
2634
2635     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2636
2637     gfxdevice_record_init(this->device);
2638     
2639     /*if(!forSoftMask) { ////???
2640         state->setFillOpacity(0.0);
2641     }*/
2642     dbgindent+=2;
2643 }
2644
2645 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2646 {
2647     dbgindent-=2;
2648     dbg("endTransparencyGroup");
2649     msg("<verbose> endTransparencyGroup");
2650
2651     gfxdevice_t*r = this->device;
2652
2653     this->device = states[statepos].olddevice;
2654
2655     if(states[statepos].createsoftmask) {
2656         states[statepos-1].softmaskrecording = r->finish(r);
2657     } else {
2658         states[statepos-1].grouprecording = r->finish(r);
2659     }
2660     
2661     states[statepos].createsoftmask = 0;
2662     states[statepos].transparencygroup = 0;
2663     free(r);
2664 }
2665
2666 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2667 {
2668     char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2669                        "colordodge","colorburn","hardlight","softlight","difference",
2670                        "exclusion","hue","saturation","color","luminosity"};
2671
2672     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2673     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2674    
2675     if(state->getBlendMode() == gfxBlendNormal)
2676         infofeature("transparency groups");
2677     else {
2678         char buffer[80];
2679         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2680         warnfeature(buffer, 0);
2681     }
2682
2683     gfxresult_t*grouprecording = states[statepos].grouprecording;
2684    
2685     if(state->getBlendMode() == gfxBlendNormal) {
2686         gfxdevice_t ops;
2687         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2688         gfxresult_record_replay(grouprecording, &ops);
2689         ops.finish(&ops);
2690     }
2691     grouprecording->destroy(grouprecording);
2692
2693     states[statepos].grouprecording = 0;
2694 }
2695
2696 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2697 {
2698     /* alpha = 1: retrieve mask values from alpha layer
2699        alpha = 0: retrieve mask values from luminance */
2700     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2701             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2702     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2703             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2704     if(!alpha)
2705         infofeature("soft masks");
2706     else
2707         warnfeature("soft masks from alpha channel",0);
2708     
2709     states[statepos].olddevice = this->device;
2710     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2711     gfxdevice_record_init(this->device);
2712
2713     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2714     
2715     states[statepos].softmask = 1;
2716 }
2717
2718 static inline Guchar div255(int x) {
2719   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2720 }
2721
2722 void GFXOutputDev::clearSoftMask(GfxState *state)
2723 {
2724     if(!states[statepos].softmask)
2725         return;
2726     states[statepos].softmask = 0;
2727     dbg("clearSoftMask statepos=%d", statepos);
2728     msg("<verbose> clearSoftMask");
2729     
2730     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2731         msg("<error> Error in softmask/tgroup ordering");
2732         return;
2733     }
2734   
2735     gfxresult_t*mask = states[statepos].softmaskrecording;
2736     gfxresult_t*below = this->device->finish(this->device);
2737     this->device = states[statepos].olddevice;
2738
2739     /* get outline of all objects below the soft mask */
2740     gfxdevice_t uniondev;
2741     gfxdevice_union_init(&uniondev, 0);
2742     gfxresult_record_replay(below, &uniondev);
2743     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2744     uniondev.finish(&uniondev);
2745
2746     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2747 #if 0 
2748     this->device->startclip(this->device, belowoutline);
2749     gfxresult_record_replay(below, this->device);
2750     gfxresult_record_replay(mask, this->device);
2751     this->device->endclip(this->device);
2752     gfxline_free(belowoutline);
2753 #endif
2754     
2755     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2756
2757     gfxdevice_t belowrender;
2758     gfxdevice_render_init(&belowrender);
2759     belowrender.setparameter(&belowrender, "fillwhite", "1"); //for isolated=0?
2760     belowrender.setparameter(&belowrender, "antialize", "2");
2761     belowrender.startpage(&belowrender, width, height);
2762     gfxresult_record_replay(below, &belowrender);
2763     belowrender.endpage(&belowrender);
2764     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2765     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2766
2767     gfxdevice_t maskrender;
2768     gfxdevice_render_init(&maskrender);
2769     maskrender.startpage(&maskrender, width, height);
2770     gfxresult_record_replay(mask, &maskrender);
2771     maskrender.endpage(&maskrender);
2772     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2773     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2774
2775     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2776         msg("<fatal> Internal error in mask drawing");
2777         return;
2778     }
2779
2780     int y,x;
2781     for(y=0;y<height;y++) {
2782         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2783         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2784         for(x=0;x<width;x++) {
2785             l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2786
2787             /* premultiply alpha */
2788             l2->r = div255(l2->a*l2->r);
2789             l2->g = div255(l2->a*l2->g);
2790             l2->b = div255(l2->a*l2->b);
2791
2792             l1++;
2793             l2++;
2794         }
2795     }
2796     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2797
2798     gfxmatrix_t matrix;
2799     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2800     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2801
2802     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2803
2804     mask->destroy(mask);
2805     below->destroy(below);
2806     maskresult->destroy(maskresult);
2807     belowresult->destroy(belowresult);
2808     states[statepos].softmaskrecording = 0;
2809 }
2810   
2811 #endif
2812
2813 /*class MemCheck
2814 {
2815     public: ~MemCheck()
2816     {
2817         delete globalParams;globalParams=0;
2818         Object::memCheck(stderr);
2819         gMemReport(stderr);
2820     }
2821 } myMemCheck;*/
2822