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