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