fixed bug in SWFOutputDev::getDimensions().
[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_H
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 "OutputDev.h"
50 #include "GfxState.h"
51 #include "GfxFont.h"
52 #include "CharCodeToUnicode.h"
53 #include "NameToUnicodeTable.h"
54 #include "GlobalParams.h"
55 //#define XPDF_101
56 #ifdef XPDF_101
57 #include "FontFile.h"
58 #else
59 #include "FoFiType1C.h"
60 #include "FoFiTrueType.h"
61 #endif
62 #include "SWFOutputDev.h"
63
64 //swftools header files
65 #include "swfoutput.h"
66 #include "../lib/log.h"
67
68 #include <math.h>
69
70 typedef struct _fontfile
71 {
72     char*filename;
73     int used;
74 } fontfile_t;
75
76 // for pdfswf_addfont
77 static fontfile_t fonts[2048];
78 static int fontnum = 0;
79
80 static int config_use_fontconfig = 1;
81
82 // swf <-> pdf pages
83 // TODO: move into pdf_doc_t
84 static int*pages = 0;
85 static int pagebuflen = 0;
86 static int pagepos = 0;
87
88 /* config */
89 static double caplinewidth = 3.0;
90 static int zoom = 72; /* xpdf: 86 */
91
92 static void printInfoString(Dict *infoDict, char *key, char *fmt);
93 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
94
95 struct mapping {
96     char*pdffont;
97     char*filename;
98 } pdf2t1map[] ={
99 {"Times-Roman",           "n021003l"},
100 {"Times-Italic",          "n021023l"},
101 {"Times-Bold",            "n021004l"},
102 {"Times-BoldItalic",      "n021024l"},
103 {"Helvetica",             "n019003l"},
104 {"Helvetica-Oblique",     "n019023l"},
105 {"Helvetica-Bold",        "n019004l"},
106 {"Helvetica-BoldOblique", "n019024l"},
107 {"Courier",               "n022003l"},
108 {"Courier-Oblique",       "n022023l"},
109 {"Courier-Bold",          "n022004l"},
110 {"Courier-BoldOblique",   "n022024l"},
111 {"Symbol",                "s050000l"},
112 {"ZapfDingbats",          "d050000l"}};
113
114 class SWFOutputDev:  public OutputDev {
115   int outputstarted;
116   struct swfoutput output;
117 public:
118
119   // Constructor.
120   SWFOutputDev();
121
122   // Destructor.
123   virtual ~SWFOutputDev() ;
124
125   void setMove(int x,int y);
126   void setClip(int x1,int y1,int x2,int y2);
127   
128   int save(char*filename);
129   void  pagefeed();
130   void* getSWF();
131
132   void getDimensions(int*x1,int*y1,int*x2,int*y2);
133
134   //----- get info about output device
135
136   // Does this device use upside-down coordinates?
137   // (Upside-down means (0,0) is the top left corner of the page.)
138   virtual GBool upsideDown();
139
140   // Does this device use drawChar() or drawString()?
141   virtual GBool useDrawChar();
142   
143   // Can this device draw gradients?
144   virtual GBool useGradients();
145   
146   virtual GBool interpretType3Chars() {return gTrue;}
147
148   //----- initialization and control
149
150   void setXRef(PDFDoc*doc, XRef *xref);
151
152   // Start a page.
153   virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
154
155   //----- link borders
156   virtual void drawLink(Link *link, Catalog *catalog) ;
157
158   //----- save/restore graphics state
159   virtual void saveState(GfxState *state) ;
160   virtual void restoreState(GfxState *state) ;
161
162   //----- update graphics state
163
164   virtual void updateFont(GfxState *state);
165   virtual void updateFillColor(GfxState *state);
166   virtual void updateStrokeColor(GfxState *state);
167   virtual void updateLineWidth(GfxState *state);
168   virtual void updateLineJoin(GfxState *state);
169   virtual void updateLineCap(GfxState *state);
170   
171   virtual void updateAll(GfxState *state) 
172   {
173       updateFont(state);
174       updateFillColor(state);
175       updateStrokeColor(state);
176       updateLineWidth(state);
177       updateLineJoin(state);
178       updateLineCap(state);
179   };
180
181   //----- path painting
182   virtual void stroke(GfxState *state) ;
183   virtual void fill(GfxState *state) ;
184   virtual void eoFill(GfxState *state) ;
185
186   //----- path clipping
187   virtual void clip(GfxState *state) ;
188   virtual void eoClip(GfxState *state) ;
189
190   //----- text drawing
191   virtual void beginString(GfxState *state, GString *s) ;
192   virtual void endString(GfxState *state) ;
193   virtual void drawChar(GfxState *state, double x, double y,
194                         double dx, double dy,
195                         double originX, double originY,
196                         CharCode code, Unicode *u, int uLen);
197
198   //----- image drawing
199   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
200                              int width, int height, GBool invert,
201                              GBool inlineImg);
202   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
203                          int width, int height, GfxImageColorMap *colorMap,
204                          int *maskColors, GBool inlineImg);
205   
206   virtual GBool beginType3Char(GfxState *state,
207                                CharCode code, Unicode *u, int uLen);
208   virtual void endType3Char(GfxState *state);
209
210   private:
211   void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
212                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
213                                    GBool inlineImg, int mask);
214   int clipping[64];
215   int clippos;
216
217   int currentpage;
218
219   PDFDoc*doc;
220   XRef*xref;
221
222   char* searchFont(char*name);
223   char* substituteFont(GfxFont*gfxFont, char*oldname);
224   char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
225   int t1id;
226   int jpeginfo; // did we write "File contains jpegs" yet?
227   int pbminfo; // did we write "File contains jpegs" yet?
228   int linkinfo; // did we write "File contains links" yet?
229   int ttfinfo; // did we write "File contains TrueType Fonts" yet?
230   int gradientinfo; // did we write "File contains Gradients yet?
231
232   int type3active; // are we between beginType3()/endType3()?
233
234   GfxState *laststate;
235
236   int pic_xids[1024];
237   int pic_yids[1024];
238   int pic_ids[1024];
239   int pic_width[1024];
240   int pic_height[1024];
241   int picpos;
242   int pic_id;
243   char type3Warning;
244
245   char* substitutetarget[256];
246   char* substitutesource[256];
247   int substitutepos;
248
249   int user_movex,user_movey;
250   int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
251 };
252
253 static char*getFontID(GfxFont*font);
254
255 class InfoOutputDev:  public OutputDev 
256 {
257   public:
258   int x1,y1,x2,y2;
259   int num_links;
260   int num_images;
261   int num_fonts;
262
263   InfoOutputDev() 
264   {
265       num_links = 0;
266       num_images = 0;
267       num_fonts = 0;
268   }
269   virtual ~InfoOutputDev() 
270   {
271   }
272   virtual GBool upsideDown() {return gTrue;}
273   virtual GBool useDrawChar() {return gTrue;}
274   virtual GBool useGradients() {return gTrue;}
275   virtual GBool interpretType3Chars() {return gTrue;}
276   virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
277   {
278       double x1,y1,x2,y2;
279       state->transform(crop_x1,crop_y1,&x1,&y1);
280       state->transform(crop_x2,crop_y2,&x2,&y2);
281       if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
282       if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
283       this->x1 = (int)x1;
284       this->y1 = (int)y1;
285       this->x2 = (int)x2;
286       this->y2 = (int)y2;
287   }
288   virtual void drawLink(Link *link, Catalog *catalog) 
289   {
290       num_links++;
291   }
292   virtual void updateFont(GfxState *state) 
293   {
294       GfxFont*font = state->getFont();
295       if(!font)
296           return;
297       char*id = getFontID(font);
298       /* FIXME*/
299       num_fonts++;
300   }
301   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
302                              int width, int height, GBool invert,
303                              GBool inlineImg) 
304   {
305       num_images++;
306   }
307   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
308                          int width, int height, GfxImageColorMap *colorMap,
309                          int *maskColors, GBool inlineImg)
310   {
311       num_images++;
312   }
313 };
314
315 SWFOutputDev::SWFOutputDev()
316 {
317     jpeginfo = 0;
318     ttfinfo = 0;
319     linkinfo = 0;
320     pbminfo = 0;
321     type3active = 0;
322     clippos = 0;
323     clipping[clippos] = 0;
324     outputstarted = 0;
325     xref = 0;
326     picpos = 0;
327     pic_id = 0;
328     substitutepos = 0;
329     type3Warning = 0;
330     user_movex = 0;
331     user_movey = 0;
332     user_clipx1 = 0;
333     user_clipy1 = 0;
334     user_clipx2 = 0;
335     user_clipy2 = 0;
336     memset(&output, 0, sizeof(output));
337 //    printf("SWFOutputDev::SWFOutputDev() \n");
338 };
339   
340 void SWFOutputDev::setMove(int x,int y)
341 {
342     this->user_movex = x;
343     this->user_movey = y;
344 }
345
346 void SWFOutputDev::setClip(int x1,int y1,int x2,int y2)
347 {
348     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
349     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
350
351     this->user_clipx1 = x1;
352     this->user_clipy1 = y1;
353     this->user_clipx2 = x2;
354     this->user_clipy2 = y2;
355 }
356 void SWFOutputDev::getDimensions(int*x1,int*y1,int*x2,int*y2)
357 {
358     return swfoutput_getdimensions(&output, x1,y1,x2,y2);
359 }
360
361 static char*getFontID(GfxFont*font)
362 {
363     GString*gstr = font->getName();
364     char* fontname = gstr==0?0:gstr->getCString();
365     if(fontname==0) {
366         char buf[32];
367         Ref*r=font->getID();
368         sprintf(buf, "UFONT%d", r->num);
369         return strdup(buf);
370     }
371     return strdup(fontname);
372 }
373
374 static char*getFontName(GfxFont*font)
375 {
376     char*fontid = getFontID(font);
377     char*fontname= 0;
378     char* plus = strchr(fontid, '+');
379     if(plus && plus < &fontid[strlen(fontid)-1]) {
380         fontname = strdup(plus+1);
381     } else {
382         fontname = strdup(fontid);
383     }
384     free(fontid);
385     return fontname;
386 }
387
388 static char mybuf[1024];
389 static char* gfxstate2str(GfxState *state)
390 {
391   char*bufpos = mybuf;
392   GfxRGB rgb;
393   bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
394                                     state->getCTM()[0],
395                                     state->getCTM()[1],
396                                     state->getCTM()[2],
397                                     state->getCTM()[3],
398                                     state->getCTM()[4],
399                                     state->getCTM()[5]);
400   if(state->getX1()!=0.0)
401   bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
402   if(state->getY1()!=0.0)
403   bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
404   bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
405   bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
406   bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
407   bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
408   /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
409           state->getFillColor()->c[0], state->getFillColor()->c[1]);
410   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
411           state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
412 /*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
413           state->getFillColor()->c[0], state->getFillColor()->c[1],
414           state->getFillColor()->c[2], state->getFillColor()->c[3],
415           state->getFillColor()->c[4], state->getFillColor()->c[5],
416           state->getFillColor()->c[6], state->getFillColor()->c[7]);
417   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
418           state->getStrokeColor()->c[0], state->getFillColor()->c[1],
419           state->getStrokeColor()->c[2], state->getFillColor()->c[3],
420           state->getStrokeColor()->c[4], state->getFillColor()->c[5],
421           state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
422   state->getFillRGB(&rgb);
423   if(rgb.r || rgb.g || rgb.b)
424   bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
425   state->getStrokeRGB(&rgb);
426   if(rgb.r || rgb.g || rgb.b)
427   bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
428   if(state->getFillColorSpace()->getNComps()>1)
429   bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
430   if(state->getStrokeColorSpace()->getNComps()>1)
431   bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
432   if(state->getFillPattern())
433   bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
434   if(state->getStrokePattern())
435   bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
436  
437   if(state->getFillOpacity()!=1.0)
438   bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
439   if(state->getStrokeOpacity()!=1.0)
440   bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
441
442   bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
443  
444   double * dash;
445   int length;
446   double start;
447   state->getLineDash(&dash, &length, &start);
448   int t;
449   if(length)
450   {
451       bufpos+=sprintf(bufpos,"DASH%.1f[",start);
452       for(t=0;t<length;t++) {
453           bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
454       }
455       bufpos+=sprintf(bufpos,"]");
456   }
457
458   if(state->getFlatness()!=1)
459   bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
460   if(state->getLineJoin()!=0)
461   bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
462   if(state->getLineJoin()!=0)
463   bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
464   if(state->getLineJoin()!=0)
465   bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
466
467   if(state->getFont() && getFontID(state->getFont()))
468   bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
469   bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
470   bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
471                                    state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
472   if(state->getCharSpace())
473   bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
474   if(state->getWordSpace())
475   bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
476   if(state->getHorizScaling()!=1.0)
477   bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
478   if(state->getLeading())
479   bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
480   if(state->getRise())
481   bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
482   if(state->getRender())
483   bufpos+=sprintf(bufpos,"R%d ", state->getRender());
484   bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
485   bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
486   bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
487   if(state->getLineX())
488   bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
489   if(state->getLineY())
490   bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
491   bufpos+=sprintf(bufpos," ");
492   return mybuf;
493 }
494
495 static void dumpFontInfo(char*loglevel, GfxFont*font);
496 static int lastdumps[1024];
497 static int lastdumppos = 0;
498 /* nr = 0  unknown
499    nr = 1  substituting
500    nr = 2  type 3
501  */
502 static void showFontError(GfxFont*font, int nr) 
503 {  
504     Ref*r=font->getID();
505     int t;
506     for(t=0;t<lastdumppos;t++)
507         if(lastdumps[t] == r->num)
508             break;
509     if(t < lastdumppos)
510       return;
511     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
512     lastdumps[lastdumppos++] = r->num;
513     if(nr == 0)
514       msg("<warning> The following font caused problems:");
515     else if(nr == 1)
516       msg("<warning> The following font caused problems (substituting):");
517     else if(nr == 2)
518       msg("<warning> The following Type 3 Font will be rendered as bitmap:");
519     dumpFontInfo("<warning>", font);
520 }
521
522 static void dumpFontInfo(char*loglevel, GfxFont*font)
523 {
524   char* name = getFontID(font);
525   Ref* r=font->getID();
526   msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, getFontName(font), r->num,r->gen);
527
528   GString*gstr  = font->getTag();
529    
530   msg("%s| Tag: %s\n", loglevel, name);
531   
532   if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
533
534   GfxFontType type=font->getType();
535   switch(type) {
536     case fontUnknownType:
537      msg("%s| Type: unknown\n",loglevel);
538     break;
539     case fontType1:
540      msg("%s| Type: 1\n",loglevel);
541     break;
542     case fontType1C:
543      msg("%s| Type: 1C\n",loglevel);
544     break;
545     case fontType3:
546      msg("%s| Type: 3\n",loglevel);
547     break;
548     case fontTrueType:
549      msg("%s| Type: TrueType\n",loglevel);
550     break;
551     case fontCIDType0:
552      msg("%s| Type: CIDType0\n",loglevel);
553     break;
554     case fontCIDType0C:
555      msg("%s| Type: CIDType0C\n",loglevel);
556     break;
557     case fontCIDType2:
558      msg("%s| Type: CIDType2\n",loglevel);
559     break;
560   }
561   
562   Ref embRef;
563   GBool embedded = font->getEmbeddedFontID(&embRef);
564   if(font->getEmbeddedFontName())
565     name = font->getEmbeddedFontName()->getCString();
566   if(embedded)
567    msg("%s| Embedded name: %s id: %d\n",loglevel, FIXNULL(name), embRef.num);
568
569   gstr = font->getExtFontFile();
570   if(gstr)
571    msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
572
573   // Get font descriptor flags.
574   if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
575   if(font->isSerif()) msg("%s| is serif\n", loglevel);
576   if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
577   if(font->isItalic()) msg("%s| is italic\n", loglevel);
578   if(font->isBold()) msg("%s| is bold\n", loglevel);
579 }
580
581 //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");}
582 //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");}
583
584 static void free_outline(SWF_OUTLINE*outline)
585 {
586     while(outline) {
587         SWF_OUTLINE*next = outline->link;
588         free(outline);
589         outline = next;
590     }
591 }
592
593 static void dump_outline(SWF_OUTLINE*outline)
594 {
595     double x=0,y=0;
596     while(outline) {
597         double lastx=x,lasty=y;
598         x += (outline->dest.x/(float)0xffff);
599         y += (outline->dest.y/(float)0xffff);
600         if(outline->type == SWF_PATHTYPE_MOVE) {
601             msg("<trace> | moveto %f,%f", x,y);
602         } else if(outline->type == SWF_PATHTYPE_LINE) {
603             msg("<trace> | lineto: %f,%f\n",x,y);
604         } else if(outline->type == SWF_PATHTYPE_BEZIER) {
605             SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
606             float x1 = o2->C.x/(float)0xffff+lastx;
607             float y1 = o2->C.y/(float)0xffff+lasty;
608             float x2 = o2->B.x/(float)0xffff+lastx;
609             float y2 = o2->B.y/(float)0xffff+lasty;
610             msg("<trace> | spline: %f,%f -> %f,%f -> %f,%f\n",x1,y1,x2,y2,x,y);
611         } 
612         outline = outline->link;
613     }
614 }
615
616 SWF_OUTLINE* gfxPath_to_SWF_OUTLINE(GfxState*state, GfxPath*path)
617 {
618     int num = path->getNumSubpaths();
619     int s,t;
620     bezierpathsegment*start,*last=0;
621     bezierpathsegment*outline = start = (bezierpathsegment*)malloc(sizeof(bezierpathsegment));
622     int cpos = 0;
623     double lastx=0,lasty=0;
624     if(!num) {
625         msg("<warning> empty path");
626         outline->type = SWF_PATHTYPE_MOVE;
627         outline->dest.x = 0;
628         outline->dest.y = 0;
629         outline->link = 0;
630         return (SWF_OUTLINE*)outline;
631     }
632     for(t = 0; t < num; t++) {
633         GfxSubpath *subpath = path->getSubpath(t);
634         int subnum = subpath->getNumPoints();
635
636         for(s=0;s<subnum;s++) {
637            double nx,ny;
638            state->transform(subpath->getX(s),subpath->getY(s),&nx,&ny);
639            int x = (int)((nx-lastx)*0xffff);
640            int y = (int)((ny-lasty)*0xffff);
641            if(s==0) 
642            {
643                 last = outline;
644                 outline->type = SWF_PATHTYPE_MOVE;
645                 outline->dest.x = x;
646                 outline->dest.y = y;
647                 outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment));
648                 outline = (bezierpathsegment*)outline->link;
649                 cpos = 0;
650                 lastx = nx;
651                 lasty = ny;
652            }
653            else if(subpath->getCurve(s) && !cpos)
654            {
655                 outline->B.x = x;
656                 outline->B.y = y;
657                 cpos = 1;
658            } 
659            else if(subpath->getCurve(s) && cpos)
660            {
661                 outline->C.x = x;
662                 outline->C.y = y;
663                 cpos = 2;
664            }
665            else
666            {
667                 last = outline;
668                 outline->dest.x = x;
669                 outline->dest.y = y;
670                 outline->type = cpos?SWF_PATHTYPE_BEZIER:SWF_PATHTYPE_LINE;
671                 outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment));
672                 outline = (bezierpathsegment*)outline->link;
673                 cpos = 0;
674                 lastx = nx;
675                 lasty = ny;
676            }
677         }
678     }
679     if(last->link) {free(last->link);}
680     last->link = 0;
681
682     return (SWF_OUTLINE*)start;
683 }
684 /*----------------------------------------------------------------------------
685  * Primitive Graphic routines
686  *----------------------------------------------------------------------------*/
687
688 void SWFOutputDev::stroke(GfxState *state) 
689 {
690     GfxPath * path = state->getPath();
691     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
692     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
693     double miterLimit = state->getMiterLimit();
694     double width = state->getTransformedLineWidth();
695     struct swfmatrix m;
696     GfxRGB rgb;
697     double opaq = state->getStrokeOpacity();
698     state->getStrokeRGB(&rgb);
699
700     m.m11 = 1; m.m21 = 0; m.m22 = 1;
701     m.m12 = 0; m.m13 = 0; m.m23 = 0;
702     SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
703     
704     if(getLogLevel() >= LOGLEVEL_TRACE)  {
705         msg("<trace> stroke\n");
706         dump_outline(outline);
707     }
708
709     lineJoin = 1; // other line joins are not yet supported by the swf encoder
710                   // TODO: support bevel joints
711
712     if(((lineCap==1) && (lineJoin==1)) || width<=caplinewidth) {
713         /* FIXME- if the path is smaller than 2 segments, we could ignore
714            lineJoin */
715         swfoutput_setdrawmode(&output, DRAWMODE_STROKE);
716         swfoutput_drawpath(&output, outline, &m);
717     } else {
718         swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
719                                         (char)(rgb.b*255), (char)(opaq*255));
720
721         //swfoutput_setlinewidth(&output, 1.0); //only for debugging
722         //swfoutput_setstrokecolor(&output, 0, 255, 0, 255); //likewise, see below
723         //swfoutput_setfillcolor(&output, 255, 0, 0, 255); //likewise, see below
724
725         swfoutput_drawpath2poly(&output, outline, &m, lineJoin, lineCap, width, miterLimit);
726         updateLineWidth(state);  //reset
727         updateStrokeColor(state); //reset
728         updateFillColor(state);  //reset
729     }
730     free_outline(outline);
731 }
732 void SWFOutputDev::fill(GfxState *state) 
733 {
734     GfxPath * path = state->getPath();
735     struct swfmatrix m;
736     m.m11 = 1; m.m21 = 0; m.m22 = 1;
737     m.m12 = 0; m.m13 = 0; m.m23 = 0;
738     SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
739
740     if(getLogLevel() >= LOGLEVEL_TRACE)  {
741         msg("<trace> fill\n");
742         dump_outline(outline);
743     }
744
745     swfoutput_setdrawmode(&output, DRAWMODE_FILL);
746     swfoutput_drawpath(&output, outline, &m);
747     free_outline(outline);
748 }
749 void SWFOutputDev::eoFill(GfxState *state) 
750 {
751     GfxPath * path = state->getPath();
752     struct swfmatrix m;
753     m.m11 = 1; m.m21 = 0; m.m22 = 1;
754     m.m12 = 0; m.m13 = 0; m.m23 = 0;
755     SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
756
757     if(getLogLevel() >= LOGLEVEL_TRACE)  {
758         msg("<trace> eofill\n");
759         dump_outline(outline);
760     }
761
762     swfoutput_setdrawmode(&output, DRAWMODE_EOFILL);
763     swfoutput_drawpath(&output, outline, &m);
764     free_outline(outline);
765 }
766 void SWFOutputDev::clip(GfxState *state) 
767 {
768     GfxPath * path = state->getPath();
769     struct swfmatrix m;
770     m.m11 = 1; m.m22 = 1;
771     m.m12 = 0; m.m21 = 0; 
772     m.m13 = 0; m.m23 = 0;
773     SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
774
775     if(getLogLevel() >= LOGLEVEL_TRACE)  {
776         msg("<trace> clip\n");
777         dump_outline(outline);
778     }
779
780     swfoutput_startclip(&output, outline, &m);
781     clipping[clippos] ++;
782     free_outline(outline);
783 }
784 void SWFOutputDev::eoClip(GfxState *state) 
785 {
786     GfxPath * path = state->getPath();
787     struct swfmatrix m;
788     m.m11 = 1; m.m21 = 0; m.m22 = 1;
789     m.m12 = 0; m.m13 = 0; m.m23 = 0;
790     SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path);
791
792     if(getLogLevel() >= LOGLEVEL_TRACE)  {
793         msg("<trace> eoclip\n");
794         dump_outline(outline);
795     }
796
797     swfoutput_startclip(&output, outline, &m);
798     clipping[clippos] ++;
799     free_outline(outline);
800 }
801
802 /* pass through functions for swf_output */
803 int SWFOutputDev::save(char*filename)
804 {
805     return swfoutput_save(&output, filename);
806 }
807 void SWFOutputDev::pagefeed()
808 {
809     swfoutput_pagefeed(&output);
810 }
811 void* SWFOutputDev::getSWF()
812 {
813     return (void*)swfoutput_get(&output);
814 }
815
816 SWFOutputDev::~SWFOutputDev() 
817 {
818     swfoutput_destroy(&output);
819     outputstarted = 0;
820 };
821 GBool SWFOutputDev::upsideDown() 
822 {
823     msg("<debug> upsidedown? yes");
824     return gTrue;
825 };
826 GBool SWFOutputDev::useDrawChar() 
827 {
828     return gTrue;
829 }
830 GBool SWFOutputDev::useGradients()
831 {
832     if(!gradientinfo)
833     {
834         msg("<notice> File contains gradients");
835         gradientinfo = 1;
836     }
837     return gTrue;
838 }
839
840 void SWFOutputDev::beginString(GfxState *state, GString *s) 
841
842     double m11,m21,m12,m22;
843 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
844     state->getFontTransMat(&m11, &m12, &m21, &m22);
845     m11 *= state->getHorizScaling();
846     m21 *= state->getHorizScaling();
847     swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22);
848 }
849
850 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
851                         double dx, double dy,
852                         double originX, double originY,
853                         CharCode c, Unicode *_u, int uLen)
854 {
855     // check for invisible text -- this is used by Acrobat Capture
856     if ((state->getRender() & 3) == 3)
857         return;
858     Gushort *CIDToGIDMap = 0;
859     GfxFont*font = state->getFont();
860
861     if(font->getType() == fontType3) {
862         /* type 3 chars are passed as graphics */
863         return;
864     }
865     double x1,y1;
866     x1 = x;
867     y1 = y;
868     state->transform(x, y, &x1, &y1);
869     
870     Unicode u=0;
871     char*name=0;
872
873     if(_u && uLen) {
874         u = *_u;
875         if (u) {
876             int t;
877             /* find out char name from unicode index 
878                TODO: should be precomputed
879              */
880             for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
881                 if(nameToUnicodeTab[t].u == u) {
882                     name = nameToUnicodeTab[t].name;
883                     break;
884                 }
885             }
886         }
887     }
888
889     if(font->isCIDFont()) {
890         GfxCIDFont*cfont = (GfxCIDFont*)font;
891
892         if(font->getType() == fontCIDType2) {
893             CIDToGIDMap = cfont->getCIDToGID();
894         }
895     } else {
896         Gfx8BitFont*font8;
897         font8 = (Gfx8BitFont*)font;
898         char**enc=font8->getEncoding();
899         if(enc && enc[c])
900            name = enc[c];
901     }
902  
903     if (CIDToGIDMap) {
904         msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\"\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name));
905         swfoutput_drawchar(&output, x1, y1, name, CIDToGIDMap[c], u);
906     } else {
907         msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\"\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name));
908         swfoutput_drawchar(&output, x1, y1, name, c, u);
909     }
910 }
911
912 void SWFOutputDev::endString(GfxState *state) { 
913 }    
914
915  
916 GBool SWFOutputDev::beginType3Char(GfxState *state,
917                                CharCode code, Unicode *u, int uLen)
918 {
919     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
920     type3active = 1;
921     /* the character itself is going to be passed using
922        drawImageMask() */
923     return gFalse; /* gTrue= is_in_cache? */
924 }
925
926 void SWFOutputDev::endType3Char(GfxState *state)
927 {
928     type3active = 0;
929     msg("<debug> endType3Char");
930 }
931
932 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
933 {
934     this->currentpage = pageNum;
935     double x1,y1,x2,y2;
936     int rot = doc->getPageRotate(1);
937     laststate = state;
938     msg("<verbose> startPage %d (%f,%f,%f,%f)\n", pageNum, crop_x1, crop_y1, crop_x2, crop_y2);
939     if(rot!=0)
940         msg("<verbose> page is rotated %d degrees\n", rot);
941
942     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
943     state->transform(state->getX2(),state->getY2(),&x2,&y2);
944     Use CropBox, not MediaBox, as page size
945     */
946     
947     /*x1 = crop_x1;
948     y1 = crop_y1;
949     x2 = crop_x2;
950     y2 = crop_y2;*/
951     state->transform(crop_x1,crop_y1,&x1,&y1);
952     state->transform(crop_x2,crop_y2,&x2,&y2);
953
954     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
955     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
956
957     /* apply user clip box */
958     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
959         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
960         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
961         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
962         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
963     }
964
965     if(!outputstarted) {
966         msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
967         swfoutput_init(&output);
968         outputstarted = 1;
969     }
970       
971     swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2);
972 }
973
974 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
975 {
976     msg("<debug> drawlink\n");
977     double x1, y1, x2, y2, w;
978     GfxRGB rgb;
979     swfcoord points[5];
980     int x, y;
981
982 #ifdef XPDF_101
983     link->getBorder(&x1, &y1, &x2, &y2, &w);
984 #else
985     link->getRect(&x1, &y1, &x2, &y2);
986 #endif
987     rgb.r = 0;
988     rgb.g = 0;
989     rgb.b = 1;
990     cvtUserToDev(x1, y1, &x, &y);
991     points[0].x = points[4].x = (int)x;
992     points[0].y = points[4].y = (int)y;
993     cvtUserToDev(x2, y1, &x, &y);
994     points[1].x = (int)x;
995     points[1].y = (int)y;
996     cvtUserToDev(x2, y2, &x, &y);
997     points[2].x = (int)x;
998     points[2].y = (int)y;
999     cvtUserToDev(x1, y2, &x, &y);
1000     points[3].x = (int)x;
1001     points[3].y = (int)y;
1002
1003     LinkAction*action=link->getAction();
1004     char buf[128];
1005     char*s = 0;
1006     char*type = "-?-";
1007     char*url = 0;
1008     char*named = 0;
1009     int page = -1;
1010     switch(action->getKind())
1011     {
1012         case actionGoTo: {
1013             type = "GoTo";
1014             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1015             LinkDest *dest=NULL;
1016             if (ha->getDest()==NULL) 
1017                 dest=catalog->findDest(ha->getNamedDest());
1018             else dest=ha->getDest();
1019             if (dest){ 
1020               if (dest->isPageRef()){
1021                 Ref pageref=dest->getPageRef();
1022                 page=catalog->findPage(pageref.num,pageref.gen);
1023               }
1024               else  page=dest->getPageNum();
1025               sprintf(buf, "%d", page);
1026               s = strdup(buf);
1027             }
1028         }
1029         break;
1030         case actionGoToR: {
1031             type = "GoToR";
1032             LinkGoToR*l = (LinkGoToR*)action;
1033             GString*g = l->getNamedDest();
1034             if(g)
1035              s = strdup(g->getCString());
1036         }
1037         break;
1038         case actionNamed: {
1039             type = "Named";
1040             LinkNamed*l = (LinkNamed*)action;
1041             GString*name = l->getName();
1042             if(name) {
1043                 s = strdup(name->lowerCase()->getCString());
1044                 named = name->getCString();
1045                 if(!strchr(s,':')) 
1046                 {
1047                     if(strstr(s, "next") || strstr(s, "forward"))
1048                     {
1049                         page = currentpage + 1;
1050                     }
1051                     else if(strstr(s, "prev") || strstr(s, "back"))
1052                     {
1053                         page = currentpage - 1;
1054                     }
1055                     else if(strstr(s, "last") || strstr(s, "end"))
1056                     {
1057                         page = pagepos>0?pages[pagepos-1]:0;
1058                     }
1059                     else if(strstr(s, "first") || strstr(s, "top"))
1060                     {
1061                         page = 1;
1062                     }
1063                 }
1064             }
1065         }
1066         break;
1067         case actionLaunch: {
1068             type = "Launch";
1069             LinkLaunch*l = (LinkLaunch*)action;
1070             GString * str = new GString(l->getFileName());
1071             str->append(l->getParams());
1072             s = strdup(str->getCString());
1073             delete str;
1074         }
1075         break;
1076         case actionURI: {
1077             type = "URI";
1078             LinkURI*l = (LinkURI*)action;
1079             GString*g = l->getURI();
1080             if(g) {
1081              url = g->getCString();
1082              s = strdup(url);
1083             }
1084         }
1085         break;
1086         case actionUnknown: {
1087             type = "Unknown";
1088             LinkUnknown*l = (LinkUnknown*)action;
1089             s = strdup("");
1090         }
1091         break;
1092         default: {
1093             msg("<error> Unknown link type!\n");
1094             break;
1095         }
1096     }
1097     if(!s) s = strdup("-?-");
1098
1099     if(!linkinfo && (page || url))
1100     {
1101         msg("<notice> File contains links");
1102         linkinfo = 1;
1103     }
1104     if(page>0)
1105     {
1106         int t;
1107         for(t=0;t<pagepos;t++)
1108             if(pages[t]==page)
1109                 break;
1110         if(t!=pagepos)
1111             swfoutput_linktopage(&output, t, points);
1112     }
1113     else if(url)
1114     {
1115         swfoutput_linktourl(&output, url, points);
1116     }
1117     else if(named)
1118     {
1119         swfoutput_namedlink(&output, named, points);
1120     }
1121     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1122     free(s);s=0;
1123 }
1124
1125 void SWFOutputDev::saveState(GfxState *state) {
1126   msg("<debug> saveState\n");
1127   updateAll(state);
1128   if(clippos<64)
1129     clippos ++;
1130   else
1131     msg("<error> Too many nested states in pdf.");
1132   clipping[clippos] = 0;
1133 };
1134
1135 void SWFOutputDev::restoreState(GfxState *state) {
1136   msg("<debug> restoreState\n");
1137   updateAll(state);
1138   while(clipping[clippos]) {
1139       swfoutput_endclip(&output);
1140       clipping[clippos]--;
1141   }
1142   clippos--;
1143 }
1144
1145 char* SWFOutputDev::searchFont(char*name) 
1146 {       
1147     int i;
1148     char*filename=0;
1149     int is_standard_font = 0;
1150         
1151     msg("<verbose> SearchFont(%s)", name);
1152
1153     /* see if it is a pdf standard font */
1154     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
1155     {
1156         if(!strcmp(name, pdf2t1map[i].pdffont))
1157         {
1158             name = pdf2t1map[i].filename;
1159             is_standard_font = 1;
1160             break;
1161         }
1162     }
1163     /* look in all font files */
1164     for(i=0;i<fontnum;i++) 
1165     {
1166         if(strstr(fonts[i].filename, name))
1167         {
1168             if(!fonts[i].used) {
1169
1170                 fonts[i].used = 1;
1171                 if(!is_standard_font)
1172                     msg("<notice> Using %s for %s", fonts[i].filename, name);
1173             }
1174             return strdup(fonts[i].filename);
1175         }
1176     }
1177     return 0;
1178 }
1179
1180 void SWFOutputDev::updateLineWidth(GfxState *state)
1181 {
1182     double width = state->getTransformedLineWidth();
1183     swfoutput_setlinewidth(&output, width);
1184 }
1185
1186 void SWFOutputDev::updateLineCap(GfxState *state)
1187 {
1188     int c = state->getLineCap();
1189 }
1190
1191 void SWFOutputDev::updateLineJoin(GfxState *state)
1192 {
1193     int j = state->getLineJoin();
1194 }
1195
1196 void SWFOutputDev::updateFillColor(GfxState *state) 
1197 {
1198     GfxRGB rgb;
1199     double opaq = state->getFillOpacity();
1200     state->getFillRGB(&rgb);
1201
1202     swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
1203                                     (char)(rgb.b*255), (char)(opaq*255));
1204 }
1205
1206 void SWFOutputDev::updateStrokeColor(GfxState *state) 
1207 {
1208     GfxRGB rgb;
1209     double opaq = state->getStrokeOpacity();
1210     state->getStrokeRGB(&rgb);
1211
1212     swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
1213                                       (char)(rgb.b*255), (char)(opaq*255));
1214 }
1215
1216 void FoFiWrite(void *stream, char *data, int len)
1217 {
1218    fwrite(data, len, 1, (FILE*)stream);
1219 }
1220
1221 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1222 {
1223     char*tmpFileName = NULL;
1224     FILE *f;
1225     int c;
1226     char *fontBuf;
1227     int fontLen;
1228     Ref embRef;
1229     Object refObj, strObj;
1230     char namebuf[512];
1231     tmpFileName = mktmpname(namebuf);
1232     int ret;
1233
1234     ret = font->getEmbeddedFontID(&embRef);
1235     if(!ret) {
1236         msg("<verbose> Didn't get embedded font id");
1237         /* not embedded- the caller should now search the font
1238            directories for this font */
1239         return 0;
1240     }
1241
1242     f = fopen(tmpFileName, "wb");
1243     if (!f) {
1244       msg("<error> Couldn't create temporary Type 1 font file");
1245         return 0;
1246     }
1247
1248     /*if(font->isCIDFont()) {
1249         GfxCIDFont* cidFont = (GfxCIDFont *)font;
1250         GString c = cidFont->getCollection();
1251         msg("<notice> Collection: %s", c.getCString());
1252     }*/
1253
1254     if (font->getType() == fontType1C ||
1255         font->getType() == fontCIDType0C) {
1256       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1257         fclose(f);
1258         msg("<error> Couldn't read embedded font file");
1259         return 0;
1260       }
1261 #ifdef XPDF_101
1262       Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
1263       cvt->convertToType1(f);
1264 #else
1265       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1266       cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1267 #endif
1268       //cvt->convertToCIDType0("test", f);
1269       //cvt->convertToType0("test", f);
1270       delete cvt;
1271       gfree(fontBuf);
1272     } else if(font->getType() == fontTrueType) {
1273       msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1274       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1275         fclose(f);
1276         msg("<error> Couldn't read embedded font file");
1277         return 0;
1278       }
1279 #ifdef XPDF_101
1280       TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
1281       cvt->writeTTF(f);
1282 #else
1283       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1284       cvt->writeTTF(FoFiWrite, f);
1285 #endif
1286       delete cvt;
1287       gfree(fontBuf);
1288     } else {
1289       font->getEmbeddedFontID(&embRef);
1290       refObj.initRef(embRef.num, embRef.gen);
1291       refObj.fetch(ref, &strObj);
1292       refObj.free();
1293       strObj.streamReset();
1294       int f4[4];
1295       char f4c[4];
1296       int t;
1297       for(t=0;t<4;t++) {
1298           f4[t] = strObj.streamGetChar();
1299           f4c[t] = (char)f4[t];
1300           if(f4[t] == EOF)
1301               break;
1302       }
1303       if(t==4) {
1304           if(!strncmp(f4c, "true", 4)) {
1305               /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1306                  Change this on the fly */
1307               f4[0] = f4[2] = f4[3] = 0;
1308               f4[1] = 1;
1309           }
1310           fputc(f4[0], f);
1311           fputc(f4[1], f);
1312           fputc(f4[2], f);
1313           fputc(f4[3], f);
1314
1315           while ((c = strObj.streamGetChar()) != EOF) {
1316             fputc(c, f);
1317           }
1318       }
1319       strObj.streamClose();
1320       strObj.free();
1321     }
1322     fclose(f);
1323
1324     return strdup(tmpFileName);
1325 }
1326     
1327 char* searchForSuitableFont(GfxFont*gfxFont)
1328 {
1329     char*name = getFontName(gfxFont);
1330     char*fontname = 0;
1331     char*filename = 0;
1332
1333     if(!config_use_fontconfig)
1334         return 0;
1335     
1336 #ifdef HAVE_FONTCONFIG
1337     FcPattern *pattern, *match;
1338     FcResult result;
1339     FcChar8 *v;
1340
1341     static int fcinitcalled = false; 
1342         
1343     msg("<debug> searchForSuitableFont(%s)", name);
1344     
1345     // call init ony once
1346     if (!fcinitcalled) {
1347         msg("<debug> Initializing FontConfig...");
1348         fcinitcalled = true;
1349         if(FcInit()) {
1350             msg("<debug> FontConfig Initialization failed. Disabling.");
1351             config_use_fontconfig = 0;
1352             return 0;
1353         }
1354         msg("<debug> ...initialized FontConfig");
1355     }
1356    
1357     msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1358     pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1359     if (gfxFont->isItalic()) // check for italic
1360         msg("<debug> FontConfig: Adding Italic Slant");
1361         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1362     if (gfxFont->isBold()) // check for bold
1363         msg("<debug> FontConfig: Adding Bold Weight");
1364         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1365
1366     msg("<debug> FontConfig: Try to match...");
1367     // configure and match using the original font name 
1368     FcConfigSubstitute(0, pattern, FcMatchPattern); 
1369     FcDefaultSubstitute(pattern);
1370     match = FcFontMatch(0, pattern, &result);
1371     
1372     if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1373         msg("<debug> FontConfig: family=%s", (char*)v);
1374         // if we get an exact match
1375         if (strcmp((char *)v, name) == 0) {
1376             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1377                 filename = strdup((char*)v);
1378                 char *nfn = strrchr(filename, '/');
1379                 if(nfn) fontname = strdup(nfn+1);
1380                 else    fontname = filename;
1381             }
1382             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1383         } else {
1384             // initialize patterns
1385             FcPatternDestroy(pattern);
1386             FcPatternDestroy(match);
1387
1388             // now match against serif etc.
1389             if (gfxFont->isSerif()) {
1390                 msg("<debug> FontConfig: Create Serif Family Pattern");
1391                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1392             } else if (gfxFont->isFixedWidth()) {
1393                 msg("<debug> FontConfig: Create Monospace Family Pattern");
1394                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1395             } else {
1396                 msg("<debug> FontConfig: Create Sans Family Pattern");
1397                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1398             }
1399
1400             // check for italic
1401             if (gfxFont->isItalic()) {
1402                 msg("<debug> FontConfig: Adding Italic Slant");
1403                 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1404             }
1405             // check for bold
1406             if (gfxFont->isBold()) {
1407                 msg("<debug> FontConfig: Adding Bold Weight");
1408                 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1409             }
1410
1411             msg("<debug> FontConfig: Try to match... (2)");
1412             // configure and match using serif etc
1413             FcConfigSubstitute (0, pattern, FcMatchPattern);
1414             FcDefaultSubstitute (pattern);
1415             match = FcFontMatch (0, pattern, &result);
1416             
1417             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1418                 filename = strdup((char*)v);
1419                 char *nfn = strrchr(filename, '/');
1420                 if(nfn) fontname = strdup(nfn+1);
1421                 else    fontname = filename;
1422             }
1423             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1424         }        
1425     }
1426
1427     //printf("FONTCONFIG: pattern");
1428     //FcPatternPrint(pattern);
1429     //printf("FONTCONFIG: match");
1430     //FcPatternPrint(match);
1431  
1432     FcPatternDestroy(pattern);
1433     FcPatternDestroy(match);
1434
1435     pdfswf_addfont(filename);
1436     return fontname;
1437 #else
1438     return 0;
1439 #endif
1440 }
1441
1442 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1443 {
1444     char*fontname = 0, *filename = 0;
1445     msg("<notice> subsituteFont(%s)", oldname);
1446
1447     if(!(fontname = searchForSuitableFont(gfxFont))) {
1448         fontname = "Times-Roman";
1449     }
1450     filename = searchFont(fontname);
1451
1452     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1453         msg("<fatal> Too many fonts in file.");
1454         exit(1);
1455     }
1456     if(oldname) {
1457         substitutesource[substitutepos] = oldname;
1458         substitutetarget[substitutepos] = fontname;
1459         msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1460         substitutepos ++;
1461     }
1462     return strdup(filename);
1463 }
1464
1465 void unlinkfont(char* filename)
1466 {
1467     int l;
1468     if(!filename)
1469         return;
1470     l=strlen(filename);
1471     unlink(filename);
1472     if(!strncmp(&filename[l-4],".afm",4)) {
1473         memcpy(&filename[l-4],".pfb",4);
1474         unlink(filename);
1475         memcpy(&filename[l-4],".pfa",4);
1476         unlink(filename);
1477         memcpy(&filename[l-4],".afm",4);
1478         return;
1479     } else 
1480     if(!strncmp(&filename[l-4],".pfa",4)) {
1481         memcpy(&filename[l-4],".afm",4);
1482         unlink(filename);
1483         memcpy(&filename[l-4],".pfa",4);
1484         return;
1485     } else 
1486     if(!strncmp(&filename[l-4],".pfb",4)) {
1487         memcpy(&filename[l-4],".afm",4);
1488         unlink(filename);
1489         memcpy(&filename[l-4],".pfb",4);
1490         return;
1491     }
1492 }
1493
1494 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1495 {
1496     this->doc = doc;
1497     this->xref = xref;
1498 }
1499
1500
1501 void SWFOutputDev::updateFont(GfxState *state) 
1502 {
1503     GfxFont*gfxFont = state->getFont();
1504       
1505     if (!gfxFont) {
1506         return;
1507     }  
1508     char * fontid = getFontID(gfxFont);
1509     
1510     int t;
1511     /* first, look if we substituted this font before-
1512        this way, we don't initialize the T1 Fonts
1513        too often */
1514     for(t=0;t<substitutepos;t++) {
1515         if(!strcmp(fontid, substitutesource[t])) {
1516             fontid = substitutetarget[t];
1517             break;
1518         }
1519     }
1520
1521     /* second, see if swfoutput already has this font
1522        cached- if so, we are done */
1523     if(swfoutput_queryfont(&output, fontid))
1524     {
1525         swfoutput_setfont(&output, fontid, 0);
1526         
1527         msg("<debug> updateFont(%s) [cached]", fontid);
1528         return;
1529     }
1530
1531     // look for Type 3 font
1532     if (gfxFont->getType() == fontType3) {
1533         if(!type3Warning) {
1534             type3Warning = gTrue;
1535             showFontError(gfxFont, 2);
1536         }
1537         return;
1538     }
1539
1540     /* now either load the font, or find a substitution */
1541
1542     Ref embRef;
1543     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1544
1545     char*fileName = 0;
1546     int del = 0;
1547     if(embedded &&
1548        (gfxFont->getType() == fontType1 ||
1549         gfxFont->getType() == fontType1C ||
1550         //gfxFont->getType() == fontCIDType0C ||
1551         gfxFont->getType() == fontTrueType ||
1552         gfxFont->getType() == fontCIDType2
1553        ))
1554     {
1555       fileName = writeEmbeddedFontToFile(xref, gfxFont);
1556       if(!fileName) showFontError(gfxFont,0);
1557       else del = 1;
1558     } else {
1559       char * fontname = getFontName(gfxFont);
1560       fileName = searchFont(fontname);
1561       if(!fileName) showFontError(gfxFont,0);
1562     }
1563     if(!fileName) {
1564         char * fontname = getFontName(gfxFont);
1565         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1566         msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname);
1567         fileName = substituteFont(gfxFont, fontid);
1568         if(fontid) { fontid = substitutetarget[substitutepos-1]; /*ugly hack*/};
1569         msg("<notice> Font is now %s (%s)", fontid, fileName);
1570     }
1571
1572     if(!fileName) {
1573         msg("<error> Couldn't set font %s\n", fontid);
1574         return;
1575     }
1576         
1577     msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
1578     dumpFontInfo("<verbose>", gfxFont);
1579
1580     swfoutput_setfont(&output, fontid, fileName);
1581    
1582     if(fileName && del)
1583         unlinkfont(fileName);
1584     if(fileName)
1585         free(fileName);
1586 }
1587
1588 #define SQR(x) ((x)*(x))
1589
1590 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1591 {
1592     if((newwidth<2 || newheight<2) ||
1593        (width<=newwidth || height<=newheight))
1594         return 0;
1595     unsigned char*newdata;
1596     int x,y;
1597     newdata= (unsigned char*)malloc(newwidth*newheight);
1598     int t;
1599     double fx = (double)(width)/newwidth;
1600     double fy = (double)(height)/newheight;
1601     double px = 0;
1602     int blocksize = (int)(8192/(fx*fy));
1603     int r = 8192*256/palettesize;
1604     for(x=0;x<newwidth;x++) {
1605         double ex = px + fx;
1606         int fromx = (int)px;
1607         int tox = (int)ex;
1608         int xweight1 = (int)(((fromx+1)-px)*256);
1609         int xweight2 = (int)((ex-tox)*256);
1610         double py =0;
1611         for(y=0;y<newheight;y++) {
1612             double ey = py + fy;
1613             int fromy = (int)py;
1614             int toy = (int)ey;
1615             int yweight1 = (int)(((fromy+1)-py)*256);
1616             int yweight2 = (int)((ey-toy)*256);
1617             int a = 0;
1618             int xx,yy;
1619             for(xx=fromx;xx<=tox;xx++)
1620             for(yy=fromy;yy<=toy;yy++) {
1621                 int b = 1-data[width*yy+xx];
1622                 int weight=256;
1623                 if(xx==fromx) weight = (weight*xweight1)/256;
1624                 if(xx==tox) weight = (weight*xweight2)/256;
1625                 if(yy==fromy) weight = (weight*yweight1)/256;
1626                 if(yy==toy) weight = (weight*yweight2)/256;
1627                 a+=b*weight;
1628             }
1629             //if(a) a=(palettesize-1)*r/blocksize;
1630             newdata[y*newwidth+x] = (a*blocksize)/r;
1631             py = ey;
1632         }
1633         px = ex;
1634     }
1635     return newdata;
1636 }
1637
1638 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1639                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1640                                    GBool inlineImg, int mask)
1641 {
1642   FILE *fi;
1643   int c;
1644   char fileName[128];
1645   double x1,y1,x2,y2,x3,y3,x4,y4;
1646   ImageStream *imgStr;
1647   Guchar pixBuf[4];
1648   GfxRGB rgb;
1649   int ncomps = 1;
1650   int bits = 1;
1651                                  
1652   if(colorMap) {
1653     ncomps = colorMap->getNumPixelComps();
1654     bits = colorMap->getBits();
1655   }
1656   imgStr = new ImageStream(str, width, ncomps,bits);
1657   imgStr->reset();
1658
1659   if(!width || !height || (height<=1 && width<=1))
1660   {
1661       msg("<verbose> Ignoring %d by %d image", width, height);
1662       unsigned char buf[8];
1663       int x,y;
1664       for (y = 0; y < height; ++y)
1665       for (x = 0; x < width; ++x) {
1666           imgStr->getPixel(buf);
1667       }
1668       delete imgStr;
1669       return;
1670   }
1671   
1672   state->transform(0, 1, &x1, &y1);
1673   state->transform(0, 0, &x2, &y2);
1674   state->transform(1, 0, &x3, &y3);
1675   state->transform(1, 1, &x4, &y4);
1676
1677   if(!pbminfo && !(str->getKind()==strDCT)) {
1678       if(!type3active) {
1679           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1680           pbminfo = 1;
1681       }
1682       if(mask)
1683       msg("<verbose> drawing %d by %d masked picture\n", width, height);
1684   }
1685   if(!jpeginfo && (str->getKind()==strDCT)) {
1686       msg("<notice> file contains jpeg pictures");
1687       jpeginfo = 1;
1688   }
1689
1690   if(mask) {
1691       int yes=0,i,j;
1692       unsigned char buf[8];
1693       int xid = 0;
1694       int yid = 0;
1695       int x,y;
1696       unsigned char*pic = new unsigned char[width*height];
1697       RGBA pal[256];
1698       GfxRGB rgb;
1699       state->getFillRGB(&rgb);
1700       memset(pal,255,sizeof(pal));
1701       pal[0].r = (int)(rgb.r*255); pal[0].g = (int)(rgb.g*255); 
1702       pal[0].b = (int)(rgb.b*255); pal[0].a = 255;
1703       pal[1].r = 0; pal[1].g = 0; pal[1].b = 0; pal[1].a = 0;
1704       int numpalette = 2;
1705       xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17;
1706       yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23;
1707       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1708       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1709       for (y = 0; y < height; ++y)
1710       for (x = 0; x < width; ++x)
1711       {
1712             imgStr->getPixel(buf);
1713             if(invert) 
1714                 buf[0]=1-buf[0];
1715             pic[width*y+x] = buf[0];
1716             xid+=x*buf[0]+1;
1717             yid+=y*buf[0]*3+1;
1718       }
1719       
1720       /* the size of the drawn image is added to the identifier
1721          as the same image may require different bitmaps if displayed
1722          at different sizes (due to antialiasing): */
1723       if(type3active) {
1724           xid += realwidth;
1725           yid += realheight;
1726       }
1727       int t,found = -1;
1728       for(t=0;t<picpos;t++)
1729       {
1730           if(pic_xids[t] == xid &&
1731              pic_yids[t] == yid) {
1732               /* if the image was antialiased, the size has changed: */
1733               width = pic_width[t];
1734               height = pic_height[t];
1735               found = t;break;
1736           }
1737       }
1738       if(found<0) {
1739           if(type3active) {
1740               numpalette = 16;
1741               unsigned char*pic2 = 0;
1742               
1743               pic2 = antialize(pic,width,height,realwidth,realheight, numpalette);
1744
1745               if(pic2) {
1746                   width = realwidth;
1747                   height = realheight;
1748                   free(pic);
1749                   pic = pic2;
1750                   /* make a black/white palette */
1751                   int t;
1752                   GfxRGB rgb2;
1753                   rgb2.r = 1 - rgb.r;
1754                   rgb2.g = 1 - rgb.g;
1755                   rgb2.b = 1 - rgb.b;
1756
1757                   float r = 255/(numpalette-1);
1758                   for(t=0;t<numpalette;t++) {
1759                       /*pal[t].r = (U8)(t*r*rgb.r+(numpalette-1-t)*r*rgb2.r);
1760                       pal[t].g = (U8)(t*r*rgb.g+(numpalette-1-t)*r*rgb2.g);
1761                       pal[t].b = (U8)(t*r*rgb.b+(numpalette-1-t)*r*rgb2.b);
1762                       pal[t].a = 255; */
1763                       pal[t].r = (U8)(255*rgb.r);
1764                       pal[t].g = (U8)(255*rgb.g);
1765                       pal[t].b = (U8)(255*rgb.b);
1766                       pal[t].a = (U8)(t*r);
1767                   }
1768               }
1769           }
1770           pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, 
1771                   x1,y1,x2,y2,x3,y3,x4,y4, numpalette);
1772           pic_xids[picpos] = xid;
1773           pic_yids[picpos] = yid;
1774           pic_width[picpos] = width;
1775           pic_height[picpos] = height;
1776           if(picpos<1024)
1777               picpos++;
1778       } else {
1779           swfoutput_drawimageagain(&output, pic_ids[found], width, height,
1780                   x1,y1,x2,y2,x3,y3,x4,y4);
1781       }
1782       free(pic);
1783       delete imgStr;
1784       return;
1785   } 
1786
1787   int x,y;
1788   
1789   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT)
1790   {
1791       RGBA*pic=new RGBA[width*height];
1792       int xid = 0;
1793       int yid = 0;
1794       for (y = 0; y < height; ++y) {
1795         for (x = 0; x < width; ++x) {
1796           int r,g,b,a;
1797           imgStr->getPixel(pixBuf);
1798           colorMap->getRGB(pixBuf, &rgb);
1799           pic[width*y+x].r = r = (U8)(rgb.r * 255 + 0.5);
1800           pic[width*y+x].g = g = (U8)(rgb.g * 255 + 0.5);
1801           pic[width*y+x].b = b = (U8)(rgb.b * 255 + 0.5);
1802           pic[width*y+x].a = a = 255;//(U8)(rgb.a * 255 + 0.5);
1803           xid += x*r+x*b*3+x*g*7+x*a*11;
1804           yid += y*r*3+y*b*17+y*g*19+y*a*11;
1805         }
1806       }
1807       int t,found = -1;
1808       for(t=0;t<picpos;t++)
1809       {
1810           if(pic_xids[t] == xid &&
1811              pic_yids[t] == yid) {
1812               found = t;break;
1813           }
1814       }
1815       if(found<0) {
1816           if(str->getKind()==strDCT)
1817               pic_ids[picpos] = swfoutput_drawimagejpeg(&output, pic, width, height, 
1818                       x1,y1,x2,y2,x3,y3,x4,y4);
1819           else
1820               pic_ids[picpos] = swfoutput_drawimagelossless(&output, pic, width, height, 
1821                       x1,y1,x2,y2,x3,y3,x4,y4);
1822           pic_xids[picpos] = xid;
1823           pic_yids[picpos] = yid;
1824           pic_width[picpos] = width;
1825           pic_height[picpos] = height;
1826           if(picpos<1024)
1827               picpos++;
1828       } else {
1829           swfoutput_drawimageagain(&output, pic_ids[found], width, height,
1830                   x1,y1,x2,y2,x3,y3,x4,y4);
1831       }
1832       delete pic;
1833       delete imgStr;
1834       return;
1835   }
1836   else
1837   {
1838       U8*pic = new U8[width*height];
1839       RGBA pal[256];
1840       int t;
1841       int xid=0,yid=0;
1842       for(t=0;t<256;t++)
1843       {
1844           int r,g,b,a;
1845           pixBuf[0] = t;
1846           colorMap->getRGB(pixBuf, &rgb);
1847           pal[t].r = r = (U8)(rgb.r * 255 + 0.5);
1848           pal[t].g = g = (U8)(rgb.g * 255 + 0.5);
1849           pal[t].b = b = (U8)(rgb.b * 255 + 0.5);
1850           pal[t].a = a = 255;//(U8)(rgb.b * 255 + 0.5);
1851           xid += t*r+t*b*3+t*g*7+t*a*11;
1852           xid += (~t)*r+t*b*3+t*g*7+t*a*11;
1853       }
1854       for (y = 0; y < height; ++y) {
1855         for (x = 0; x < width; ++x) {
1856           imgStr->getPixel(pixBuf);
1857           pic[width*y+x] = pixBuf[0];
1858           xid += x*pixBuf[0]*7;
1859           yid += y*pixBuf[0]*3;
1860         }
1861       }
1862       int found = -1;
1863       for(t=0;t<picpos;t++)
1864       {
1865           if(pic_xids[t] == xid &&
1866              pic_yids[t] == yid) {
1867               found = t;break;
1868           }
1869       }
1870       if(found<0) {
1871           pic_ids[picpos] = swfoutput_drawimagelosslessN(&output, pic, pal, width, height, 
1872                   x1,y1,x2,y2,x3,y3,x4,y4,256);
1873           pic_xids[picpos] = xid;
1874           pic_yids[picpos] = yid;
1875           pic_width[picpos] = width;
1876           pic_height[picpos] = height;
1877           if(picpos<1024)
1878               picpos++;
1879       } else {
1880           swfoutput_drawimageagain(&output, pic_ids[found], width, height,
1881                   x1,y1,x2,y2,x3,y3,x4,y4);
1882       }
1883       delete pic;
1884       delete imgStr;
1885       return;
1886   }
1887 }
1888
1889 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1890                                    int width, int height, GBool invert,
1891                                    GBool inlineImg) 
1892 {
1893   msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1894   drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1);
1895 }
1896
1897 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1898                          int width, int height, GfxImageColorMap *colorMap,
1899                          int *maskColors, GBool inlineImg)
1900 {
1901   msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height, 
1902           colorMap?"colorMap":"no colorMap", 
1903           maskColors?"maskColors":"no maskColors",
1904           inlineImg);
1905   if(colorMap)
1906       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1907               colorMap->getBits(),colorMap->getColorSpace()->getMode());
1908   drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0);
1909 }
1910
1911 SWFOutputDev*output = 0; 
1912
1913 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
1914   Object obj;
1915   GString *s1, *s2;
1916   int i;
1917
1918   if (infoDict->lookup(key, &obj)->isString()) {
1919     s1 = obj.getString();
1920     if ((s1->getChar(0) & 0xff) == 0xfe &&
1921         (s1->getChar(1) & 0xff) == 0xff) {
1922       s2 = new GString();
1923       for (i = 2; i < obj.getString()->getLength(); i += 2) {
1924         if (s1->getChar(i) == '\0') {
1925           s2->append(s1->getChar(i+1));
1926         } else {
1927           delete s2;
1928           s2 = new GString("<unicode>");
1929           break;
1930         }
1931       }
1932       printf(fmt, s2->getCString());
1933       delete s2;
1934     } else {
1935       printf(fmt, s1->getCString());
1936     }
1937   }
1938   obj.free();
1939 }
1940
1941 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
1942   Object obj;
1943   char *s;
1944
1945   if (infoDict->lookup(key, &obj)->isString()) {
1946     s = obj.getString()->getCString();
1947     if (s[0] == 'D' && s[1] == ':') {
1948       s += 2;
1949     }
1950     printf(fmt, s);
1951   }
1952   obj.free();
1953 }
1954
1955 void pdfswf_setparameter(char*name, char*value)
1956 {
1957     msg("<verbose> setting parameter %s to \"%s\"", name, value);
1958     if(!strcmp(name, "caplinewidth")) {
1959         caplinewidth = atof(value);
1960     } else if(!strcmp(name, "zoom")) {
1961         zoom = atoi(value);
1962     } else if(!strcmp(name, "fontdir")) {
1963         pdfswf_addfontdir(value);
1964     } else if(!strcmp(name, "languagedir")) {
1965         pdfswf_addlanguagedir(value);
1966     } else if(!strcmp(name, "fontconfig")) {
1967         config_use_fontconfig = atoi(value);
1968     } else {
1969         swfoutput_setparameter(name, value);
1970     }
1971 }
1972 void pdfswf_addfont(char*filename)
1973 {
1974     fontfile_t f;
1975     memset(&f, 0, sizeof(fontfile_t));
1976     f.filename = filename;
1977     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
1978         fonts[fontnum++] = f;
1979     } else {
1980         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
1981     }
1982 }
1983
1984 static char* dirseparator()
1985 {
1986 #ifdef WIN32
1987     return "\\";
1988 #else
1989     return "/";
1990 #endif
1991 }
1992
1993 void pdfswf_addlanguagedir(char*dir)
1994 {
1995     if(!globalParams)
1996         globalParams = new GlobalParams("");
1997     
1998     msg("<notice> Adding %s to language pack directories", dir);
1999
2000     int l;
2001     FILE*fi = 0;
2002     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2003     strcpy(config_file, dir);
2004     strcat(config_file, dirseparator());
2005     strcat(config_file, "add-to-xpdfrc");
2006
2007     fi = fopen(config_file, "rb");
2008     if(!fi) {
2009         msg("<error> Could not open %s", config_file);
2010         return;
2011     }
2012     globalParams->parseFile(new GString(config_file), fi);
2013     fclose(fi);
2014 }
2015
2016 void pdfswf_addfontdir(char*dirname)
2017 {
2018 #ifdef HAVE_DIRENT_H
2019     msg("<notice> Adding %s to font directories", dirname);
2020     DIR*dir = opendir(dirname);
2021     if(!dir) {
2022         msg("<warning> Couldn't open directory %s\n", dirname);
2023         return;
2024     }
2025     struct dirent*ent;
2026     while(1) {
2027         ent = readdir (dir);
2028         if (!ent) 
2029             break;
2030         int l;
2031         char*name = ent->d_name;
2032         char type = 0;
2033         if(!name) continue;
2034         l=strlen(name);
2035         if(l<4)
2036             continue;
2037         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2038             type=1;
2039         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2040             type=3;
2041         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2042             type=2;
2043         if(type)
2044         {
2045             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2046             strcpy(fontname, dirname);
2047             strcat(fontname, dirseparator());
2048             strcat(fontname, name);
2049             msg("<verbose> Adding %s to fonts", fontname);
2050             pdfswf_addfont(fontname);
2051         }
2052     }
2053     closedir(dir);
2054 #else
2055     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2056 #endif
2057 }
2058
2059
2060 typedef struct _pdf_doc_internal
2061 {
2062     int protect;
2063     PDFDoc*doc;
2064 } pdf_doc_internal_t;
2065 typedef struct _pdf_page_internal
2066 {
2067 } pdf_page_internal_t;
2068 typedef struct _swf_output_internal
2069 {
2070     SWFOutputDev*outputDev;
2071 } swf_output_internal_t;
2072
2073 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2074 {
2075     pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2076     memset(pdf_doc, 0, sizeof(pdf_doc_t));
2077     pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2078     memset(i, 0, sizeof(pdf_doc_internal_t));
2079     pdf_doc->internal = i;
2080     
2081     GString *fileName = new GString(filename);
2082     GString *userPW;
2083     Object info;
2084
2085     // read config file
2086     if(!globalParams)
2087         globalParams = new GlobalParams("");
2088
2089     // open PDF file
2090     if (userPassword && userPassword[0]) {
2091       userPW = new GString(userPassword);
2092     } else {
2093       userPW = NULL;
2094     }
2095     i->doc = new PDFDoc(fileName, userPW);
2096     if (userPW) {
2097       delete userPW;
2098     }
2099     if (!i->doc->isOk()) {
2100         return 0;
2101     }
2102
2103     // print doc info
2104     i->doc->getDocInfo(&info);
2105     if (info.isDict() &&
2106       (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2107       printInfoString(info.getDict(), "Title",        "Title:        %s\n");
2108       printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
2109       printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
2110       printInfoString(info.getDict(), "Author",       "Author:       %s\n");
2111       printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
2112       printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
2113       printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
2114       printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
2115       printf("Pages:        %d\n", i->doc->getNumPages());
2116       printf("Linearized:   %s\n", i->doc->isLinearized() ? "yes" : "no");
2117       printf("Encrypted:    ");
2118       if (i->doc->isEncrypted()) {
2119         printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2120                i->doc->okToPrint() ? "yes" : "no",
2121                i->doc->okToCopy() ? "yes" : "no",
2122                i->doc->okToChange() ? "yes" : "no",
2123                i->doc->okToAddNotes() ? "yes" : "no");
2124       } else {
2125         printf("no\n");
2126       }
2127     }
2128     info.free();
2129                    
2130     pdf_doc->num_pages = i->doc->getNumPages();
2131     i->protect = 0;
2132     if (i->doc->isEncrypted()) {
2133           if(!i->doc->okToCopy()) {
2134               printf("PDF disallows copying.\n");
2135               return 0;
2136           }
2137           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2138               i->protect = 1;
2139     }
2140    
2141     return pdf_doc;
2142 }
2143
2144 void pdfswf_preparepage(int page)
2145 {
2146     /*FIXME*/
2147     if(!pages) {
2148         pages = (int*)malloc(1024*sizeof(int));
2149         pagebuflen = 1024;
2150     } else {
2151         if(pagepos == pagebuflen)
2152         {
2153             pagebuflen+=1024;
2154             pages = (int*)realloc(pages, pagebuflen);
2155         }
2156     }
2157     pages[pagepos++] = page;
2158 }
2159
2160 class MemCheck
2161 {
2162     public: ~MemCheck()
2163     {
2164         delete globalParams;globalParams=0;
2165         Object::memCheck(stderr);
2166         gMemReport(stderr);
2167     }
2168 } myMemCheck;
2169
2170 void pdf_destroy(pdf_doc_t*pdf_doc)
2171 {
2172     pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2173
2174     msg("<debug> pdfswf.cc: pdfswf_close()");
2175     delete i->doc; i->doc=0;
2176     
2177     free(pages); pages = 0; //FIXME
2178
2179     free(pdf_doc->internal);pdf_doc->internal=0;
2180     free(pdf_doc);pdf_doc=0;
2181 }
2182
2183 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2184 {
2185     pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2186
2187     if(page < 1 || page > pdf_doc->num_pages)
2188         return 0;
2189     
2190     pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2191     pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2192     memset(pi, 0, sizeof(pdf_page_internal_t));
2193     pdf_page->internal = pi;
2194
2195     pdf_page->parent = pdf_doc;
2196     pdf_page->nr = page;
2197     return pdf_page;
2198 }
2199
2200 void pdf_page_destroy(pdf_page_t*pdf_page)
2201 {
2202     pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2203     free(pdf_page->internal);pdf_page->internal = 0;
2204     free(pdf_page);pdf_page=0;
2205 }
2206
2207 swf_output_t* swf_output_init() 
2208 {
2209     swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2210     memset(swf_output, 0, sizeof(swf_output_t));
2211     swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2212     memset(i, 0, sizeof(swf_output_internal_t));
2213     swf_output->internal = i;
2214
2215     i->outputDev = new SWFOutputDev();
2216     return swf_output;
2217 }
2218
2219 void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
2220 {
2221     /* FIXME */
2222     pdfswf_setparameter(name, value);
2223 }
2224
2225 void swf_output_pagefeed(swf_output_t*swf)
2226 {
2227     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2228     i->outputDev->pagefeed();
2229     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2230 }
2231
2232 int swf_output_save(swf_output_t*swf, char*filename)
2233 {
2234     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2235     int ret = i->outputDev->save(filename);
2236     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2237     return ret;
2238 }
2239
2240 void* swf_output_get(swf_output_t*swf)
2241 {
2242     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2243     void* ret = i->outputDev->getSWF();
2244     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2245     return ret;
2246 }
2247
2248 void swf_output_destroy(swf_output_t*output)
2249 {
2250     swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2251     delete i->outputDev; i->outputDev=0;
2252     free(output->internal);output->internal=0;
2253     free(output);
2254 }
2255
2256 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2257 {
2258     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2259     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2260
2261     if(pi->protect) {
2262         swfoutput_setparameter("protect", "1");
2263     }
2264     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2265 #ifdef XPDF_101
2266     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2267 #else
2268     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2269 #endif
2270     si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2271 }
2272
2273 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2274 {
2275     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2276     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2277
2278     si->outputDev->setMove(x,y);
2279     if((x1|y1|x2|y2)==0) x2++;
2280     si->outputDev->setClip(x1,y1,x2,y2);
2281
2282     pdf_page_render2(page, output);
2283 }
2284 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2285 {
2286     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2287     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2288     
2289     si->outputDev->setMove(0,0);
2290     si->outputDev->setClip(0,0,0,0);
2291     
2292     pdf_page_render2(page, output);
2293 }
2294
2295
2296 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2297 {
2298     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2299     pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2300     pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2301     memset(info, 0, sizeof(pdf_page_info_t));
2302
2303     InfoOutputDev*output = new InfoOutputDev;
2304     
2305 #ifdef XPDF_101
2306     pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2307 #else
2308     pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2309 #endif
2310
2311     info->xMin = output->x1;
2312     info->yMin = output->y1;
2313     info->xMax = output->x2;
2314     info->yMax = output->y2;
2315     info->number_of_images = output->num_images;
2316     info->number_of_links = output->num_links;
2317     info->number_of_fonts = output->num_fonts;
2318
2319     delete output;
2320
2321     return info;
2322 }
2323
2324 void pdf_page_info_destroy(pdf_page_info_t*info)
2325 {
2326     free(info);
2327 }