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