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