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