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