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