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