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