removed debugging code
[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 = 1;
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     this->isolated = 0;
183 }
184
185 GBool GFXOutputDev::interpretType3Chars() 
186 {
187     return gTrue;
188 }
189
190 typedef struct _drawnchar
191 {
192     gfxcoord_t x,y;
193     int charid;
194     gfxcolor_t color;
195 } drawnchar_t;
196
197 class CharBuffer
198 {
199     drawnchar_t * chars;
200     int buf_size;
201     int num_chars;
202
203 public:
204
205     CharBuffer()
206     {
207         buf_size = 32;
208         chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
209         memset(chars, 0, sizeof(drawnchar_t)*buf_size);
210         num_chars = 0;
211     }
212     ~CharBuffer()
213     {
214         free(chars);chars = 0;
215     }
216
217     void grow(int size)
218     {
219         if(size>=buf_size) {
220             buf_size += 32;
221             chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
222         }
223     }
224
225     void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
226     {
227         grow(num_chars);
228         chars[num_chars].x = x;
229         chars[num_chars].y = y;
230         chars[num_chars].color = color;
231         chars[num_chars].charid = charid;
232     }
233 };
234
235 static char*getFontID(GfxFont*font);
236
237 GFXOutputDev::GFXOutputDev(parameter_t*p)
238 {
239     this->jpeginfo = 0;
240     this->textmodeinfo = 0;
241     this->linkinfo = 0;
242     this->pbminfo = 0;
243     this->type3active = 0;
244     this->statepos = 0;
245     this->xref = 0;
246     this->substitutepos = 0;
247     this->type3Warning = 0;
248     this->user_movex = 0;
249     this->user_movey = 0;
250     this->clipmovex = 0;
251     this->clipmovey = 0;
252     this->user_clipx1 = 0;
253     this->user_clipy1 = 0;
254     this->user_clipx2 = 0;
255     this->user_clipy2 = 0;
256     this->current_text_stroke = 0;
257     this->current_text_clip = 0;
258     this->fontlist = 0;
259     this->outer_clip_box = 0;
260     this->pages = 0;
261     this->pagebuflen = 0;
262     this->pagepos = 0;
263     this->config_use_fontconfig=1;
264     this->config_break_on_warning=0;
265
266     this->parameters = p;
267   
268     memset(states, 0, sizeof(states));
269
270     /* configure device */
271     while(p) {
272         if(!strcmp(p->name,"fontconfig")) {
273             this->config_use_fontconfig = atoi(p->value);
274         } else if(!strcmp(p->name,"breakonwarning")) {
275             this->config_break_on_warning = atoi(p->value);
276         }
277         p = p->next;
278     }
279 };
280   
281 void GFXOutputDev::setDevice(gfxdevice_t*dev)
282 {
283     parameter_t*p = this->parameters;
284
285     /* pass parameters to output device */
286     this->device = dev;
287     if(this->device) {
288         while(p) {
289             this->device->setparameter(this->device, p->name, p->value);
290             p = p->next;
291         }
292     }
293 }
294   
295 void GFXOutputDev::setMove(int x,int y)
296 {
297     this->user_movex = x;
298     this->user_movey = y;
299 }
300
301 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
302 {
303     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
304     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
305
306     this->user_clipx1 = x1;
307     this->user_clipy1 = y1;
308     this->user_clipx2 = x2;
309     this->user_clipy2 = y2;
310 }
311
312 static char*getFontID(GfxFont*font)
313 {
314     Ref*ref = font->getID();
315     GString*gstr = font->getName();
316     char* fname = gstr==0?0:gstr->getCString();
317     char buf[128];
318     if(fname==0) {
319         sprintf(buf, "font-%d-%d", ref->num, ref->gen);
320     } else {
321         sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
322     }
323     return strdup(buf);
324 }
325
326 static char*getFontName(GfxFont*font)
327 {
328     char*fontid;
329     GString*gstr = font->getName();
330     char* fname = gstr==0?0:gstr->getCString();
331     if(fname==0) {
332         char buf[32];
333         Ref*r=font->getID();
334         sprintf(buf, "UFONT%d", r->num);
335         fontid = strdup(buf);
336     } else {
337         fontid = strdup(fname);
338     }
339
340     char*fontname= 0;
341     char* plus = strchr(fontid, '+');
342     if(plus && plus < &fontid[strlen(fontid)-1]) {
343         fontname = strdup(plus+1);
344     } else {
345         fontname = strdup(fontid);
346     }
347     free(fontid);
348     return fontname;
349 }
350
351 static char mybuf[1024];
352 static char* gfxstate2str(GfxState *state)
353 {
354   char*bufpos = mybuf;
355   GfxRGB rgb;
356   bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
357                                     state->getCTM()[0],
358                                     state->getCTM()[1],
359                                     state->getCTM()[2],
360                                     state->getCTM()[3],
361                                     state->getCTM()[4],
362                                     state->getCTM()[5]);
363   if(state->getX1()!=0.0)
364   bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
365   if(state->getY1()!=0.0)
366   bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
367   bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
368   bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
369   bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
370   bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
371   /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
372           state->getFillColor()->c[0], state->getFillColor()->c[1]);
373   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
374           state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
375 /*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
376           state->getFillColor()->c[0], state->getFillColor()->c[1],
377           state->getFillColor()->c[2], state->getFillColor()->c[3],
378           state->getFillColor()->c[4], state->getFillColor()->c[5],
379           state->getFillColor()->c[6], state->getFillColor()->c[7]);
380   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
381           state->getStrokeColor()->c[0], state->getFillColor()->c[1],
382           state->getStrokeColor()->c[2], state->getFillColor()->c[3],
383           state->getStrokeColor()->c[4], state->getFillColor()->c[5],
384           state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
385   state->getFillRGB(&rgb);
386   if(rgb.r || rgb.g || rgb.b)
387   bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
388   state->getStrokeRGB(&rgb);
389   if(rgb.r || rgb.g || rgb.b)
390   bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
391   if(state->getFillColorSpace()->getNComps()>1)
392   bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
393   if(state->getStrokeColorSpace()->getNComps()>1)
394   bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
395   if(state->getFillPattern())
396   bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
397   if(state->getStrokePattern())
398   bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
399  
400   if(state->getFillOpacity()!=1.0)
401   bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
402   if(state->getStrokeOpacity()!=1.0)
403   bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
404
405   bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
406  
407   double * dash;
408   int length;
409   double start;
410   state->getLineDash(&dash, &length, &start);
411   int t;
412   if(length)
413   {
414       bufpos+=sprintf(bufpos,"DASH%.1f[",start);
415       for(t=0;t<length;t++) {
416           bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
417       }
418       bufpos+=sprintf(bufpos,"]");
419   }
420
421   if(state->getFlatness()!=1)
422   bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
423   if(state->getLineJoin()!=0)
424   bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
425   if(state->getLineJoin()!=0)
426   bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
427   if(state->getLineJoin()!=0)
428   bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
429
430   if(state->getFont() && getFontID(state->getFont()))
431   bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
432   bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
433   bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
434                                    state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
435   if(state->getCharSpace())
436   bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
437   if(state->getWordSpace())
438   bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
439   if(state->getHorizScaling()!=1.0)
440   bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
441   if(state->getLeading())
442   bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
443   if(state->getRise())
444   bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
445   if(state->getRender())
446   bufpos+=sprintf(bufpos,"R%d ", state->getRender());
447   bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
448   bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
449   bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
450   if(state->getLineX())
451   bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
452   if(state->getLineY())
453   bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
454   bufpos+=sprintf(bufpos," ");
455   return mybuf;
456 }
457
458 static void dumpFontInfo(char*loglevel, GfxFont*font);
459 static int lastdumps[1024];
460 static int lastdumppos = 0;
461 /* nr = 0  unknown
462    nr = 1  substituting
463    nr = 2  type 3
464  */
465 static void showFontError(GfxFont*font, int nr) 
466 {  
467     Ref*r=font->getID();
468     int t;
469     for(t=0;t<lastdumppos;t++)
470         if(lastdumps[t] == r->num)
471             break;
472     if(t < lastdumppos)
473       return;
474     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
475     lastdumps[lastdumppos++] = r->num;
476     if(nr == 0)
477       msg("<warning> The following font caused problems:");
478     else if(nr == 1)
479       msg("<warning> The following font caused problems (substituting):");
480     else if(nr == 2)
481       msg("<warning> The following Type 3 Font will be rendered as bitmap:");
482     dumpFontInfo("<warning>", font);
483 }
484
485 static void dumpFontInfo(char*loglevel, GfxFont*font)
486 {
487   char* id = getFontID(font);
488   char* name = getFontName(font);
489   Ref* r=font->getID();
490   msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
491
492   GString*gstr  = font->getTag();
493    
494   msg("%s| Tag: %s\n", loglevel, id);
495   
496   if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
497
498   GfxFontType type=font->getType();
499   switch(type) {
500     case fontUnknownType:
501      msg("%s| Type: unknown\n",loglevel);
502     break;
503     case fontType1:
504      msg("%s| Type: 1\n",loglevel);
505     break;
506     case fontType1C:
507      msg("%s| Type: 1C\n",loglevel);
508     break;
509     case fontType3:
510      msg("%s| Type: 3\n",loglevel);
511     break;
512     case fontTrueType:
513      msg("%s| Type: TrueType\n",loglevel);
514     break;
515     case fontCIDType0:
516      msg("%s| Type: CIDType0\n",loglevel);
517     break;
518     case fontCIDType0C:
519      msg("%s| Type: CIDType0C\n",loglevel);
520     break;
521     case fontCIDType2:
522      msg("%s| Type: CIDType2\n",loglevel);
523     break;
524   }
525   
526   Ref embRef;
527   GBool embedded = font->getEmbeddedFontID(&embRef);
528   char*embeddedName=0;
529   if(font->getEmbeddedFontName()) {
530     embeddedName = font->getEmbeddedFontName()->getCString();
531   }
532   if(embedded)
533    msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
534
535   gstr = font->getExtFontFile();
536   if(gstr)
537    msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
538
539   // Get font descriptor flags.
540   if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
541   if(font->isSerif()) msg("%s| is serif\n", loglevel);
542   if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
543   if(font->isItalic()) msg("%s| is italic\n", loglevel);
544   if(font->isBold()) msg("%s| is bold\n", loglevel);
545
546   free(id);
547   free(name);
548 }
549
550 //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");}
551 //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");}
552
553
554 void dump_outline(gfxline_t*line)
555 {
556     while(line) {
557         if(line->type == gfx_moveTo) {
558             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
559         } else if(line->type == gfx_lineTo) {
560             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
561         } else if(line->type == gfx_splineTo) {
562             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
563         }
564         line = line->next;
565     }
566 }
567
568 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
569 {
570     int num = path->getNumSubpaths();
571     int s,t;
572     int cpos = 0;
573     double lastx=0,lasty=0,posx=0,posy=0;
574     int needsfix=0;
575     if(!num) {
576         msg("<warning> empty path");
577         return 0;
578     }
579     gfxdrawer_t draw;
580     gfxdrawer_target_gfxline(&draw);
581
582     for(t = 0; t < num; t++) {
583         GfxSubpath *subpath = path->getSubpath(t);
584         int subnum = subpath->getNumPoints();
585         double bx=0,by=0,cx=0,cy=0;
586
587         for(s=0;s<subnum;s++) {
588            double x,y;
589            
590            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
591            x += user_movex;
592            y += user_movey;
593
594            if(s==0) {
595                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
596                     draw.lineTo(&draw, lastx, lasty);
597                 }
598                 draw.moveTo(&draw, x,y);
599                 posx = lastx = x; 
600                 posy = lasty = y;
601                 cpos = 0;
602                 needsfix = 0;
603            } else if(subpath->getCurve(s) && cpos==0) {
604                 bx = x;
605                 by = y;
606                 cpos = 1;
607            } else if(subpath->getCurve(s) && cpos==1) {
608                 cx = x;
609                 cy = y;
610                 cpos = 2;
611            } else {
612                 posx = x;
613                 posy = y;
614                 if(cpos==0) {
615                     draw.lineTo(&draw, x,y);
616                 } else {
617                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
618                 }
619                 needsfix = 1;
620                 cpos = 0;
621            }
622         }
623     }
624     /* fix non-closed lines */
625     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
626         draw.lineTo(&draw, lastx, lasty);
627     }
628     gfxline_t*result = (gfxline_t*)draw.result(&draw);
629
630     gfxline_optimize(result);
631
632     return result;
633 }
634
635 GBool GFXOutputDev::useTilingPatternFill()
636 {
637     infofeature("tiled patterns");
638     return gFalse;
639 }
640
641 GBool GFXOutputDev::useShadedFills()
642 {
643     infofeature("shaded fills");
644     return gFalse;
645 }
646
647 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
648 {
649     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
650     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
651     double miterLimit = state->getMiterLimit();
652     double width = state->getTransformedLineWidth();
653
654     GfxRGB rgb;
655     double opaq = state->getStrokeOpacity();
656     if(type3active)
657         state->getFillRGB(&rgb);
658     else
659         state->getStrokeRGB(&rgb);
660     gfxcolor_t col;
661     col.r = colToByte(rgb.r);
662     col.g = colToByte(rgb.g);
663     col.b = colToByte(rgb.b);
664     col.a = (unsigned char)(opaq*255);
665    
666     gfx_capType capType = gfx_capRound;
667     if(lineCap == 0) capType = gfx_capButt;
668     else if(lineCap == 1) capType = gfx_capRound;
669     else if(lineCap == 2) capType = gfx_capSquare;
670
671     gfx_joinType joinType = gfx_joinRound;
672     if(lineJoin == 0) joinType = gfx_joinMiter;
673     else if(lineJoin == 1) joinType = gfx_joinRound;
674     else if(lineJoin == 2) joinType = gfx_joinBevel;
675
676     int dashnum = 0;
677     double dashphase = 0;
678     double * ldash = 0;
679     state->getLineDash(&ldash, &dashnum, &dashphase);
680
681     gfxline_t*line2 = 0;
682
683     if(dashnum && ldash) {
684         float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
685         int t;
686         double cut = 0;
687         int fixzero = 0;
688         msg("<trace> %d dashes", dashnum);
689         msg("<trace> |  phase: %f", dashphase);
690         for(t=0;t<dashnum;t++) {
691             dash[t] = ldash[t];
692             msg("<trace> |  d%-3d: %f", t, ldash[t]);
693         }
694         dash[dashnum] = -1;
695         if(getLogLevel() >= LOGLEVEL_TRACE) {
696             dump_outline(line);
697         }
698
699         line2 = gfxtool_dash_line(line, dash, dashphase);
700         line = line2;
701         free(dash);
702         msg("<trace> After dashing:");
703     }
704     
705     if(getLogLevel() >= LOGLEVEL_TRACE)  {
706         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
707                 width,
708                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
709                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
710                 dashnum,
711                 col.r,col.g,col.b,col.a
712                 );
713         dump_outline(line);
714     }
715    
716     //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
717     device->stroke(device, line, width, &col, capType, joinType, miterLimit);
718     
719     if(line2)
720         gfxline_free(line2);
721 }
722
723 gfxcolor_t getFillColor(GfxState * state)
724 {
725     GfxRGB rgb;
726     double opaq = state->getFillOpacity();
727     state->getFillRGB(&rgb);
728     gfxcolor_t col;
729     col.r = colToByte(rgb.r);
730     col.g = colToByte(rgb.g);
731     col.b = colToByte(rgb.b);
732     col.a = (unsigned char)(opaq*255);
733     return col;
734 }
735
736 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
737 {
738     gfxcolor_t col = getFillColor(state);
739
740     if(getLogLevel() >= LOGLEVEL_TRACE)  {
741         msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
742         dump_outline(line);
743     }
744     device->fill(device, line, &col);
745 }
746
747 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
748 {
749     if(getLogLevel() >= LOGLEVEL_TRACE)  {
750         msg("<trace> clip\n");
751         dump_outline(line);
752     }
753
754     device->startclip(device, line);
755     states[statepos].clipping++;
756 }
757
758 void GFXOutputDev::clip(GfxState *state) 
759 {
760     GfxPath * path = state->getPath();
761     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
762     clipToGfxLine(state, line);
763     gfxline_free(line);
764 }
765
766 void GFXOutputDev::eoClip(GfxState *state) 
767 {
768     GfxPath * path = state->getPath();
769     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
770
771     if(getLogLevel() >= LOGLEVEL_TRACE)  {
772         msg("<trace> eoclip\n");
773         dump_outline(line);
774     }
775
776     device->startclip(device, line);
777     states[statepos].clipping++;
778     gfxline_free(line);
779 }
780
781 void GFXOutputDev::endframe()
782 {
783     if(outer_clip_box) {
784         device->endclip(device);
785         outer_clip_box = 0;
786     }
787
788     device->endpage(device);
789 }
790
791 void GFXOutputDev::finish()
792 {
793     if(outer_clip_box) {
794         if(device) {
795             device->endclip(device);
796         }
797         outer_clip_box = 0;
798     }
799 }
800
801 GFXOutputDev::~GFXOutputDev() 
802 {
803     finish();
804
805     if(this->pages) {
806         free(this->pages); this->pages = 0;
807     }
808
809     fontlist_t*l = this->fontlist;
810     while(l) {
811         fontlist_t*next = l->next;
812         l->next = 0;
813         gfxfont_free(l->font);
814         free(l->filename);l->filename=0;
815         free(l);
816         l = next;
817     }
818     this->fontlist = 0;
819 };
820 GBool GFXOutputDev::upsideDown() 
821 {
822     return gTrue;
823 };
824 GBool GFXOutputDev::useDrawChar() 
825 {
826     return gTrue;
827 }
828
829 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
830                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
831
832 #define RENDER_FILL 0
833 #define RENDER_STROKE 1
834 #define RENDER_FILLSTROKE 2
835 #define RENDER_INVISIBLE 3
836 #define RENDER_CLIP 4
837
838 static char tmp_printstr[4096];
839 char* makeStringPrintable(char*str)
840 {
841     int len = strlen(str);
842     int dots = 0;
843     if(len>=80) {
844         len = 80;
845         dots = 1;
846     }
847     int t;
848     for(t=0;t<len;t++) {
849         char c = str[t];
850         if(c<32 || c>124) {
851             c = '.';
852         }
853         tmp_printstr[t] = c;
854     }
855     if(dots) {
856         tmp_printstr[len++] = '.';
857         tmp_printstr[len++] = '.';
858         tmp_printstr[len++] = '.';
859     }
860     tmp_printstr[len] = 0;
861     return tmp_printstr;
862 }
863
864
865 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
866 {
867     char*uniname = 0;
868     if(u>0) {
869         int t;
870         /* find out char name from unicode index 
871            TODO: should be precomputed
872          */
873         for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
874             if(nameToUnicodeTab[t].u == u) {
875                 uniname = nameToUnicodeTab[t].name;
876                 break;
877             }
878         }
879     }
880
881     if(charname) {
882         int t;
883         for(t=0;t<font->num_glyphs;t++) {
884             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
885                 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
886                 return t;
887             }
888         }
889         /* if we didn't find the character, maybe
890            we can find the capitalized version */
891         for(t=0;t<font->num_glyphs;t++) {
892             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
893                 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
894                 return t;
895             }
896         }
897     }
898
899     if(uniname) {
900         int t;
901         for(t=0;t<font->num_glyphs;t++) {
902             if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
903                 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
904                 return t;
905             }
906         }
907         /* if we didn't find the character, maybe
908            we can find the capitalized version */
909         for(t=0;t<font->num_glyphs;t++) {
910             if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
911                 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
912                 return t;
913             }
914         }
915     }
916
917     /* try to use the unicode id */
918     if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
919         msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
920         return font->unicode2glyph[u];
921     }
922
923     if(charnr>=0 && charnr<font->num_glyphs) {
924         msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
925         return charnr;
926     }
927     
928     return -1;
929 }
930
931
932 void GFXOutputDev::beginString(GfxState *state, GString *s) 
933
934     int render = state->getRender();
935     if(current_text_stroke) {
936         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
937     }
938
939     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
940     double m11,m21,m12,m22;
941 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
942     state->getFontTransMat(&m11, &m12, &m21, &m22);
943     m11 *= state->getHorizScaling();
944     m21 *= state->getHorizScaling();
945
946     this->current_font_matrix.m00 = m11 / 1024.0;
947     this->current_font_matrix.m01 = m12 / 1024.0;
948     this->current_font_matrix.m10 = -m21 / 1024.0;
949     this->current_font_matrix.m11 = -m22 / 1024.0;
950     this->current_font_matrix.tx = 0;
951     this->current_font_matrix.ty = 0;
952
953     gfxmatrix_t m = this->current_font_matrix;
954
955     states[statepos].textRender = render;
956 }
957
958 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
959                         double dx, double dy,
960                         double originX, double originY,
961                         CharCode c, int nBytes, Unicode *_u, int uLen)
962 {
963     int render = state->getRender();
964     // check for invisible text -- this is used by Acrobat Capture
965     if (render == 3) {
966         msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
967         return;
968     }
969
970     if(states[statepos].textRender != render)
971         msg("<error> Internal error: drawChar.render!=beginString.render");
972
973     gfxcolor_t col = getFillColor(state);
974
975     Gushort *CIDToGIDMap = 0;
976     GfxFont*font = state->getFont();
977
978     if(font->getType() == fontType3) {
979         /* type 3 chars are passed as graphics */
980         msg("<debug> type3 char at %f/%f", x, y);
981         return;
982     }
983     
984     Unicode u=0;
985     char*name=0;
986
987     if(uLen)
988         u = _u[0];
989
990     if(font->isCIDFont()) {
991         GfxCIDFont*cfont = (GfxCIDFont*)font;
992
993         if(font->getType() == fontCIDType2)
994             CIDToGIDMap = cfont->getCIDToGID();
995     } else {
996         Gfx8BitFont*font8;
997         font8 = (Gfx8BitFont*)font;
998         char**enc=font8->getEncoding();
999         name = enc[c];
1000     }
1001     if (CIDToGIDMap) {
1002         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);
1003         c = CIDToGIDMap[c];
1004     } else {
1005         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);
1006     }
1007
1008     int charid = -1;
1009    
1010     if(uLen<=1) {
1011         charid = getGfxCharID(current_gfxfont, c, name, u);
1012     } else {
1013         charid = getGfxCharID(current_gfxfont, c, name, -1);
1014
1015         if(charid < 0) {
1016             /* multiple unicodes- should usually map to a ligature.
1017                if the ligature doesn't exist, we need to draw
1018                the characters one-by-one. */
1019             int t;
1020             msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1021             for(t=0;t<uLen;t++) {
1022                 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1023             }
1024             return;
1025         }
1026     }
1027     if(charid<0) {
1028         msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
1029                 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1030         return;
1031     }
1032
1033     gfxmatrix_t m = this->current_font_matrix;
1034     state->transform(x, y, &m.tx, &m.ty);
1035     m.tx += user_movex + clipmovex;
1036     m.ty += user_movey + clipmovey;
1037
1038     if(render == RENDER_FILL) {
1039         device->drawchar(device, current_gfxfont, charid, &col, &m);
1040     } else {
1041         msg("<debug> Drawing glyph %d as shape", charid);
1042         if(!textmodeinfo) {
1043             msg("<notice> Some texts will be rendered as shape");
1044             textmodeinfo = 1;
1045         }
1046         gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1047         gfxline_t*tglyph = gfxline_clone(glyph);
1048         gfxline_transform(tglyph, &m);
1049         if((render&3) != RENDER_INVISIBLE) {
1050             gfxline_t*add = gfxline_clone(tglyph);
1051             current_text_stroke = gfxline_append(current_text_stroke, add);
1052         }
1053         if(render&RENDER_CLIP) {
1054             gfxline_t*add = gfxline_clone(tglyph);
1055             current_text_clip = gfxline_append(current_text_clip, add);
1056         }
1057         gfxline_free(tglyph);
1058     }
1059 }
1060
1061 void GFXOutputDev::endString(GfxState *state) 
1062
1063     int render = state->getRender();
1064     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1065     if(states[statepos].textRender != render)
1066         msg("<error> Internal error: drawChar.render!=beginString.render");
1067     
1068     if(current_text_stroke) {
1069         /* fillstroke and stroke text rendering objects we can process right
1070            now (as there may be texts of other rendering modes in this
1071            text object)- clipping objects have to wait until endTextObject,
1072            however */
1073         device->setparameter(device, "mark","TXT");
1074         if((render&3) == RENDER_FILL) {
1075             fillGfxLine(state, current_text_stroke);
1076             gfxline_free(current_text_stroke);
1077             current_text_stroke = 0;
1078         } else if((render&3) == RENDER_FILLSTROKE) {
1079             fillGfxLine(state, current_text_stroke);
1080             strokeGfxline(state, current_text_stroke);
1081             gfxline_free(current_text_stroke);
1082             current_text_stroke = 0;
1083         } else if((render&3) == RENDER_STROKE) {
1084             strokeGfxline(state, current_text_stroke);
1085             gfxline_free(current_text_stroke);
1086             current_text_stroke = 0;
1087         }
1088         device->setparameter(device, "mark","");
1089     }
1090 }    
1091
1092 void GFXOutputDev::endTextObject(GfxState *state)
1093 {
1094     int render = state->getRender();
1095     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1096     if(states[statepos].textRender != render)
1097         msg("<error> Internal error: drawChar.render!=beginString.render");
1098     
1099     if(current_text_clip) {
1100         device->setparameter(device, "mark","TXT");
1101         clipToGfxLine(state, current_text_clip);
1102         device->setparameter(device, "mark","");
1103         gfxline_free(current_text_clip);
1104         current_text_clip = 0;
1105     }
1106 }
1107
1108 /* the logic seems to be as following:
1109    first, beginType3Char is called, with the charcode and the coordinates.
1110    if this function returns true, it already knew about the char and has now drawn it.
1111    if the function returns false, it's a new char, and type3D1 is called with some parameters-
1112    the all draw operations until endType3Char are part of the char (which in this moment is
1113    at the position first passed to beginType3Char). the char ends with endType3Char.
1114
1115    The drawing operations between beginType3Char and endType3Char are somewhat different to
1116    the normal ones. For example, the fillcolor equals the stroke color.
1117 */
1118
1119 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1120 {
1121     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1122     type3active = 1;
1123     /* the character itself is going to be passed using the draw functions */
1124     return gFalse; /* gTrue= is_in_cache? */
1125 }
1126
1127 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1128     msg("<debug> type3D0 width=%f height=%f", wx, wy);
1129 }
1130 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1131     msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1132             llx,lly,urx,ury);
1133 }
1134
1135 void GFXOutputDev::endType3Char(GfxState *state)
1136 {
1137     type3active = 0;
1138     msg("<debug> endType3Char");
1139 }
1140
1141 void GFXOutputDev::startFrame(int width, int height) 
1142 {
1143     if(outer_clip_box) {
1144         device->endclip(device);
1145         outer_clip_box = 0;
1146     }
1147
1148     device->startpage(device, width, height);
1149     this->width = width;
1150     this->height = height;
1151 }
1152
1153 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1154 {
1155     this->currentpage = pageNum;
1156     double x1,y1,x2,y2;
1157     int rot = doc->getPageRotate(1);
1158     gfxcolor_t white;
1159     laststate = state;
1160     gfxline_t clippath[5];
1161
1162     white.r = white.g = white.b = white.a = 255;
1163
1164     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1165     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1166     Use CropBox, not MediaBox, as page size
1167     */
1168     
1169     /*x1 = crop_x1;
1170     y1 = crop_y1;
1171     x2 = crop_x2;
1172     y2 = crop_y2;*/
1173     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1174     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1175
1176     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1177     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1178
1179     this->clipmovex = -(int)x1;
1180     this->clipmovey = -(int)y1;
1181     
1182     /* apply user clip box */
1183     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1184         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1185         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1186         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1187         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1188         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1189     }
1190
1191     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1192     
1193     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);
1194     if(rot!=0)
1195         msg("<verbose> page is rotated %d degrees\n", rot);
1196
1197     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1198     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1199     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1200     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1201     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1202     device->startclip(device, clippath); outer_clip_box = 1;
1203     device->fill(device, clippath, &white);
1204 }
1205
1206
1207 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
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(0, 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 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2617                                       GfxColorSpace *blendingColorSpace,
2618                                       GBool isolated, GBool knockout,
2619                                       GBool forSoftMask)
2620 {
2621     char*colormodename = "";
2622     BBox rect = mkBBox(state, bbox, this->width, this->height);
2623
2624     if(blendingColorSpace) {
2625         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2626     }
2627     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);
2628     dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2629     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);
2630     
2631     states[statepos].createsoftmask |= forSoftMask;
2632     states[statepos].transparencygroup = !forSoftMask;
2633     states[statepos].isolated = isolated;
2634
2635     states[statepos].olddevice = this->device;
2636     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2637
2638     gfxdevice_record_init(this->device);
2639     
2640     /*if(!forSoftMask) { ////???
2641         state->setFillOpacity(0.0);
2642     }*/
2643     dbgindent+=2;
2644 }
2645
2646 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2647 {
2648     dbgindent-=2;
2649     dbg("endTransparencyGroup");
2650     msg("<verbose> endTransparencyGroup");
2651
2652     gfxdevice_t*r = this->device;
2653
2654     this->device = states[statepos].olddevice;
2655
2656     if(states[statepos].createsoftmask) {
2657         states[statepos-1].softmaskrecording = r->finish(r);
2658     } else {
2659         states[statepos-1].grouprecording = r->finish(r);
2660     }
2661     
2662     states[statepos].createsoftmask = 0;
2663     states[statepos].transparencygroup = 0;
2664     free(r);
2665 }
2666
2667 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2668 {
2669     char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2670                        "colordodge","colorburn","hardlight","softlight","difference",
2671                        "exclusion","hue","saturation","color","luminosity"};
2672
2673     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2674     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2675    
2676     if(state->getBlendMode() == gfxBlendNormal)
2677         infofeature("transparency groups");
2678     else {
2679         char buffer[80];
2680         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2681         warnfeature(buffer, 0);
2682     }
2683
2684     gfxresult_t*grouprecording = states[statepos].grouprecording;
2685    
2686     if(state->getBlendMode() == gfxBlendNormal) {
2687         gfxdevice_t ops;
2688         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2689         gfxresult_record_replay(grouprecording, &ops);
2690         ops.finish(&ops);
2691     }
2692     grouprecording->destroy(grouprecording);
2693
2694     states[statepos].grouprecording = 0;
2695 }
2696
2697 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2698 {
2699     /* alpha = 1: retrieve mask values from alpha layer
2700        alpha = 0: retrieve mask values from luminance */
2701     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2702             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2703     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2704             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2705     if(!alpha)
2706         infofeature("soft masks");
2707     else
2708         warnfeature("soft masks from alpha channel",0);
2709     
2710     states[statepos].olddevice = this->device;
2711     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2712     gfxdevice_record_init(this->device);
2713
2714     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2715     
2716     states[statepos].softmask = 1;
2717     states[statepos].softmask_alpha = alpha;
2718 }
2719
2720 static inline Guchar div255(int x) {
2721   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2722 }
2723
2724 void GFXOutputDev::clearSoftMask(GfxState *state)
2725 {
2726     if(!states[statepos].softmask)
2727         return;
2728     states[statepos].softmask = 0;
2729     dbg("clearSoftMask statepos=%d", statepos);
2730     msg("<verbose> clearSoftMask");
2731     
2732     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2733         msg("<error> Error in softmask/tgroup ordering");
2734         return;
2735     }
2736   
2737     gfxresult_t*mask = states[statepos].softmaskrecording;
2738     gfxresult_t*below = this->device->finish(this->device);
2739     this->device = states[statepos].olddevice;
2740
2741     /* get outline of all objects below the soft mask */
2742     gfxdevice_t uniondev;
2743     gfxdevice_union_init(&uniondev, 0);
2744     gfxresult_record_replay(below, &uniondev);
2745     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2746     uniondev.finish(&uniondev);
2747
2748     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2749 #if 0 
2750     this->device->startclip(this->device, belowoutline);
2751     gfxresult_record_replay(below, this->device);
2752     gfxresult_record_replay(mask, this->device);
2753     this->device->endclip(this->device);
2754     gfxline_free(belowoutline);
2755 #endif
2756     
2757     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2758
2759     gfxdevice_t belowrender;
2760     gfxdevice_render_init(&belowrender);
2761     if(states[statepos+1].isolated) {
2762         belowrender.setparameter(&belowrender, "fillwhite", "1");
2763     }
2764     belowrender.setparameter(&belowrender, "antialize", "2");
2765     belowrender.startpage(&belowrender, width, height);
2766     gfxresult_record_replay(below, &belowrender);
2767     belowrender.endpage(&belowrender);
2768     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2769     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2770     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2771
2772     gfxdevice_t maskrender;
2773     gfxdevice_render_init(&maskrender);
2774     maskrender.startpage(&maskrender, width, height);
2775     gfxresult_record_replay(mask, &maskrender);
2776     maskrender.endpage(&maskrender);
2777     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2778     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2779
2780     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2781         msg("<fatal> Internal error in mask drawing");
2782         return;
2783     }
2784
2785     int y,x;
2786     for(y=0;y<height;y++) {
2787         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2788         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2789         for(x=0;x<width;x++) {
2790             int alpha;
2791             if(states[statepos].softmask_alpha) {
2792                 alpha = l1->a;
2793             } else {
2794                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2795             }
2796
2797             /* premultiply alpha */
2798             l2->a = div255(alpha*l2->a);
2799             l2->r = div255(alpha*l2->r);
2800             l2->g = div255(alpha*l2->g);
2801             l2->b = div255(alpha*l2->b);
2802
2803             l1++;
2804             l2++;
2805         }
2806     }
2807     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2808
2809     gfxmatrix_t matrix;
2810     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2811     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2812
2813     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2814
2815     mask->destroy(mask);
2816     below->destroy(below);
2817     maskresult->destroy(maskresult);
2818     belowresult->destroy(belowresult);
2819     states[statepos].softmaskrecording = 0;
2820 }
2821   
2822 /*class MemCheck
2823 {
2824     public: ~MemCheck()
2825     {
2826         delete globalParams;globalParams=0;
2827         Object::memCheck(stderr);
2828         gMemReport(stderr);
2829     }
2830 } myMemCheck;*/
2831