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