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