font ids now contain the pdf reference id
[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     msg("<debug> drawlink\n");
1465     double x1, y1, x2, y2, w;
1466     gfxline_t points[5];
1467     int x, y;
1468
1469     link->getRect(&x1, &y1, &x2, &y2);
1470     cvtUserToDev(x1, y1, &x, &y);
1471     points[0].type = gfx_moveTo;
1472     points[0].x = points[4].x = x + user_movex;
1473     points[0].y = points[4].y = y + user_movey;
1474     points[0].next = &points[1];
1475     cvtUserToDev(x2, y1, &x, &y);
1476     points[1].type = gfx_lineTo;
1477     points[1].x = x + user_movex;
1478     points[1].y = y + user_movey;
1479     points[1].next = &points[2];
1480     cvtUserToDev(x2, y2, &x, &y);
1481     points[2].type = gfx_lineTo;
1482     points[2].x = x + user_movex;
1483     points[2].y = y + user_movey;
1484     points[2].next = &points[3];
1485     cvtUserToDev(x1, y2, &x, &y);
1486     points[3].type = gfx_lineTo;
1487     points[3].x = x + user_movex;
1488     points[3].y = y + user_movey;
1489     points[3].next = &points[4];
1490     cvtUserToDev(x1, y1, &x, &y);
1491     points[4].type = gfx_lineTo;
1492     points[4].x = x + user_movex;
1493     points[4].y = y + user_movey;
1494     points[4].next = 0;
1495
1496     LinkAction*action=link->getAction();
1497     char buf[128];
1498     char*s = 0;
1499     char*type = "-?-";
1500     char*url = 0;
1501     char*named = 0;
1502     int page = -1;
1503     switch(action->getKind())
1504     {
1505         case actionGoTo: {
1506             type = "GoTo";
1507             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1508             LinkDest *dest=NULL;
1509             if (ha->getDest()==NULL) 
1510                 dest=catalog->findDest(ha->getNamedDest());
1511             else dest=ha->getDest();
1512             if (dest){ 
1513               if (dest->isPageRef()){
1514                 Ref pageref=dest->getPageRef();
1515                 page=catalog->findPage(pageref.num,pageref.gen);
1516               }
1517               else  page=dest->getPageNum();
1518               sprintf(buf, "%d", page);
1519               s = strdup(buf);
1520             }
1521         }
1522         break;
1523         case actionGoToR: {
1524             type = "GoToR";
1525             LinkGoToR*l = (LinkGoToR*)action;
1526             GString*g = l->getNamedDest();
1527             if(g)
1528              s = strdup(g->getCString());
1529         }
1530         break;
1531         case actionNamed: {
1532             type = "Named";
1533             LinkNamed*l = (LinkNamed*)action;
1534             GString*name = l->getName();
1535             if(name) {
1536                 s = strdup(name->lowerCase()->getCString());
1537                 named = name->getCString();
1538                 if(!strchr(s,':')) 
1539                 {
1540                     if(strstr(s, "next") || strstr(s, "forward"))
1541                     {
1542                         page = currentpage + 1;
1543                     }
1544                     else if(strstr(s, "prev") || strstr(s, "back"))
1545                     {
1546                         page = currentpage - 1;
1547                     }
1548                     else if(strstr(s, "last") || strstr(s, "end"))
1549                     {
1550                         if(pages && pagepos>0)
1551                             page = pages[pagepos-1];
1552                     }
1553                     else if(strstr(s, "first") || strstr(s, "top"))
1554                     {
1555                         page = 1;
1556                     }
1557                 }
1558             }
1559         }
1560         break;
1561         case actionLaunch: {
1562             type = "Launch";
1563             LinkLaunch*l = (LinkLaunch*)action;
1564             GString * str = new GString(l->getFileName());
1565             str->append(l->getParams());
1566             s = strdup(str->getCString());
1567             delete str;
1568         }
1569         break;
1570         case actionURI: {
1571             type = "URI";
1572             LinkURI*l = (LinkURI*)action;
1573             GString*g = l->getURI();
1574             if(g) {
1575              url = g->getCString();
1576              s = strdup(url);
1577             }
1578         }
1579         break;
1580         case actionUnknown: {
1581             type = "Unknown";
1582             LinkUnknown*l = (LinkUnknown*)action;
1583             s = strdup("");
1584         }
1585         break;
1586         default: {
1587             msg("<error> Unknown link type!\n");
1588             break;
1589         }
1590     }
1591     if(!s) s = strdup("-?-");
1592
1593     if(!linkinfo && (page || url))
1594     {
1595         msg("<notice> File contains links");
1596         linkinfo = 1;
1597     }
1598     
1599     if(page>0)
1600     {
1601         int t;
1602         int lpage = -1;
1603         for(t=1;t<=pagepos;t++) {
1604             if(pages[t]==page) {
1605                 lpage = t;
1606                 break;
1607             }
1608         }
1609         if(lpage<0) {
1610             lpage = page;
1611         }
1612         char buf[80];
1613         sprintf(buf, "page%d", lpage);
1614         output->drawlink(output, points, buf);
1615     }
1616     else if(url)
1617     {
1618         output->drawlink(output, points, url);
1619     }
1620
1621     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1622     free(s);s=0;
1623 }
1624
1625 void SWFOutputDev::saveState(GfxState *state) {
1626   msg("<trace> saveState\n");
1627   updateAll(state);
1628   if(statepos>=64) {
1629     msg("<error> Too many nested states in pdf.");
1630     return;
1631   }
1632   statepos ++;
1633   states[statepos].clipping = 0; //? shouldn't this be the current value?
1634   states[statepos].textRender = states[statepos-1].textRender;
1635 };
1636
1637 void SWFOutputDev::restoreState(GfxState *state) {
1638   msg("<trace> restoreState\n");
1639   updateAll(state);
1640   while(states[statepos].clipping) {
1641       output->endclip(output);
1642       states[statepos].clipping--;
1643   }
1644   statepos--;
1645 }
1646
1647 char* SWFOutputDev::searchFont(char*name) 
1648 {       
1649     int i;
1650     char*filename=0;
1651     int is_standard_font = 0;
1652         
1653     msg("<verbose> SearchFont(%s)", name);
1654
1655     /* see if it is a pdf standard font */
1656     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
1657     {
1658         if(!strcmp(name, pdf2t1map[i].pdffont))
1659         {
1660             name = pdf2t1map[i].filename;
1661             is_standard_font = 1;
1662             break;
1663         }
1664     }
1665     /* look in all font files */
1666     for(i=0;i<fontnum;i++) 
1667     {
1668         if(strstr(fonts[i].filename, name))
1669         {
1670             if(!fonts[i].used) {
1671
1672                 fonts[i].used = 1;
1673                 if(!is_standard_font)
1674                     msg("<notice> Using %s for %s", fonts[i].filename, name);
1675             }
1676             return strdup(fonts[i].filename);
1677         }
1678     }
1679     return 0;
1680 }
1681
1682 void SWFOutputDev::updateLineWidth(GfxState *state)
1683 {
1684     double width = state->getTransformedLineWidth();
1685     //swfoutput_setlinewidth(&output, width);
1686 }
1687
1688 void SWFOutputDev::updateLineCap(GfxState *state)
1689 {
1690     int c = state->getLineCap();
1691 }
1692
1693 void SWFOutputDev::updateLineJoin(GfxState *state)
1694 {
1695     int j = state->getLineJoin();
1696 }
1697
1698 void SWFOutputDev::updateFillColor(GfxState *state) 
1699 {
1700     GfxRGB rgb;
1701     double opaq = state->getFillOpacity();
1702     state->getFillRGB(&rgb);
1703
1704     //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1705 }
1706
1707 void SWFOutputDev::updateStrokeColor(GfxState *state) 
1708 {
1709     GfxRGB rgb;
1710     double opaq = state->getStrokeOpacity();
1711     state->getStrokeRGB(&rgb);
1712     //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1713 }
1714
1715 void FoFiWrite(void *stream, char *data, int len)
1716 {
1717    fwrite(data, len, 1, (FILE*)stream);
1718 }
1719
1720 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1721 {
1722     char*tmpFileName = NULL;
1723     FILE *f;
1724     int c;
1725     char *fontBuf;
1726     int fontLen;
1727     Ref embRef;
1728     Object refObj, strObj;
1729     char namebuf[512];
1730     tmpFileName = mktmpname(namebuf);
1731     int ret;
1732
1733     ret = font->getEmbeddedFontID(&embRef);
1734     if(!ret) {
1735         msg("<verbose> Didn't get embedded font id");
1736         /* not embedded- the caller should now search the font
1737            directories for this font */
1738         return 0;
1739     }
1740
1741     f = fopen(tmpFileName, "wb");
1742     if (!f) {
1743       msg("<error> Couldn't create temporary Type 1 font file");
1744         return 0;
1745     }
1746
1747     /*if(font->isCIDFont()) {
1748         GfxCIDFont* cidFont = (GfxCIDFont *)font;
1749         GString c = cidFont->getCollection();
1750         msg("<notice> Collection: %s", c.getCString());
1751     }*/
1752
1753     //if (font->getType() == fontType1C) {
1754     if (0) { //font->getType() == fontType1C) {
1755       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1756         fclose(f);
1757         msg("<error> Couldn't read embedded font file");
1758         return 0;
1759       }
1760       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1761       if(!cvt) return 0;
1762       cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1763       //cvt->convertToCIDType0("test", f);
1764       //cvt->convertToType0("test", f);
1765       delete cvt;
1766       gfree(fontBuf);
1767     } else if(font->getType() == fontTrueType) {
1768       msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1769       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1770         fclose(f);
1771         msg("<error> Couldn't read embedded font file");
1772         return 0;
1773       }
1774       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1775       cvt->writeTTF(FoFiWrite, f);
1776       delete cvt;
1777       gfree(fontBuf);
1778     } else {
1779       font->getEmbeddedFontID(&embRef);
1780       refObj.initRef(embRef.num, embRef.gen);
1781       refObj.fetch(ref, &strObj);
1782       refObj.free();
1783       strObj.streamReset();
1784       int f4[4];
1785       char f4c[4];
1786       int t;
1787       for(t=0;t<4;t++) {
1788           f4[t] = strObj.streamGetChar();
1789           f4c[t] = (char)f4[t];
1790           if(f4[t] == EOF)
1791               break;
1792       }
1793       if(t==4) {
1794           if(!strncmp(f4c, "true", 4)) {
1795               /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1796                  Change this on the fly */
1797               f4[0] = f4[2] = f4[3] = 0;
1798               f4[1] = 1;
1799           }
1800           fputc(f4[0], f);
1801           fputc(f4[1], f);
1802           fputc(f4[2], f);
1803           fputc(f4[3], f);
1804
1805           while ((c = strObj.streamGetChar()) != EOF) {
1806             fputc(c, f);
1807           }
1808       }
1809       strObj.streamClose();
1810       strObj.free();
1811     }
1812     fclose(f);
1813
1814     return strdup(tmpFileName);
1815 }
1816     
1817 char* searchForSuitableFont(GfxFont*gfxFont)
1818 {
1819     char*name = getFontName(gfxFont);
1820     char*fontname = 0;
1821     char*filename = 0;
1822
1823     if(!config_use_fontconfig)
1824         return 0;
1825     
1826 #ifdef HAVE_FONTCONFIG
1827     FcPattern *pattern, *match;
1828     FcResult result;
1829     FcChar8 *v;
1830
1831     static int fcinitcalled = false; 
1832         
1833     msg("<debug> searchForSuitableFont(%s)", name);
1834     
1835     // call init ony once
1836     if (!fcinitcalled) {
1837         msg("<debug> Initializing FontConfig...");
1838         fcinitcalled = true;
1839         if(!FcInit()) {
1840             msg("<debug> FontConfig Initialization failed. Disabling.");
1841             config_use_fontconfig = 0;
1842             return 0;
1843         }
1844         msg("<debug> ...initialized FontConfig");
1845     }
1846    
1847     msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1848     pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1849     if (gfxFont->isItalic()) // check for italic
1850         msg("<debug> FontConfig: Adding Italic Slant");
1851         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1852     if (gfxFont->isBold()) // check for bold
1853         msg("<debug> FontConfig: Adding Bold Weight");
1854         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1855
1856     msg("<debug> FontConfig: Try to match...");
1857     // configure and match using the original font name 
1858     FcConfigSubstitute(0, pattern, FcMatchPattern); 
1859     FcDefaultSubstitute(pattern);
1860     match = FcFontMatch(0, pattern, &result);
1861     
1862     if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1863         msg("<debug> FontConfig: family=%s", (char*)v);
1864         // if we get an exact match
1865         if (strcmp((char *)v, name) == 0) {
1866             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1867                 filename = strdup((char*)v); // mem leak
1868                 char *nfn = strrchr(filename, '/');
1869                 if(nfn) fontname = strdup(nfn+1);
1870                 else    fontname = filename;
1871             }
1872             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1873         } else {
1874             // initialize patterns
1875             FcPatternDestroy(pattern);
1876             FcPatternDestroy(match);
1877
1878             // now match against serif etc.
1879             if (gfxFont->isSerif()) {
1880                 msg("<debug> FontConfig: Create Serif Family Pattern");
1881                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1882             } else if (gfxFont->isFixedWidth()) {
1883                 msg("<debug> FontConfig: Create Monospace Family Pattern");
1884                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1885             } else {
1886                 msg("<debug> FontConfig: Create Sans Family Pattern");
1887                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1888             }
1889
1890             // check for italic
1891             if (gfxFont->isItalic()) {
1892                 msg("<debug> FontConfig: Adding Italic Slant");
1893                 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1894             }
1895             // check for bold
1896             if (gfxFont->isBold()) {
1897                 msg("<debug> FontConfig: Adding Bold Weight");
1898                 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1899             }
1900
1901             msg("<debug> FontConfig: Try to match... (2)");
1902             // configure and match using serif etc
1903             FcConfigSubstitute (0, pattern, FcMatchPattern);
1904             FcDefaultSubstitute (pattern);
1905             match = FcFontMatch (0, pattern, &result);
1906             
1907             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1908                 filename = strdup((char*)v); // mem leak
1909                 char *nfn = strrchr(filename, '/');
1910                 if(nfn) fontname = strdup(nfn+1);
1911                 else    fontname = filename;
1912             }
1913             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1914         }        
1915     }
1916
1917     //printf("FONTCONFIG: pattern");
1918     //FcPatternPrint(pattern);
1919     //printf("FONTCONFIG: match");
1920     //FcPatternPrint(match);
1921  
1922     FcPatternDestroy(pattern);
1923     FcPatternDestroy(match);
1924
1925     pdfswf_addfont(filename);
1926     return fontname;
1927 #else
1928     return 0;
1929 #endif
1930 }
1931
1932 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1933 {
1934     char*fontname = 0, *filename = 0;
1935     msg("<notice> substituteFont(%s)", oldname);
1936
1937     if(!(fontname = searchForSuitableFont(gfxFont))) {
1938         fontname = "Times-Roman";
1939     }
1940     filename = searchFont(fontname);
1941     if(!filename) {
1942         msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1943         return 0;
1944     }
1945
1946     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1947         msg("<fatal> Too many fonts in file.");
1948         exit(1);
1949     }
1950     if(oldname) {
1951         substitutesource[substitutepos] = strdup(oldname); //mem leak
1952         substitutetarget[substitutepos] = fontname;
1953         msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1954         substitutepos ++;
1955     }
1956     return strdup(filename); //mem leak
1957 }
1958
1959 void unlinkfont(char* filename)
1960 {
1961     int l;
1962     if(!filename)
1963         return;
1964     l=strlen(filename);
1965     unlink(filename);
1966     if(!strncmp(&filename[l-4],".afm",4)) {
1967         memcpy(&filename[l-4],".pfb",4);
1968         unlink(filename);
1969         memcpy(&filename[l-4],".pfa",4);
1970         unlink(filename);
1971         memcpy(&filename[l-4],".afm",4);
1972         return;
1973     } else 
1974     if(!strncmp(&filename[l-4],".pfa",4)) {
1975         memcpy(&filename[l-4],".afm",4);
1976         unlink(filename);
1977         memcpy(&filename[l-4],".pfa",4);
1978         return;
1979     } else 
1980     if(!strncmp(&filename[l-4],".pfb",4)) {
1981         memcpy(&filename[l-4],".afm",4);
1982         unlink(filename);
1983         memcpy(&filename[l-4],".pfb",4);
1984         return;
1985     }
1986 }
1987
1988 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1989 {
1990     this->doc = doc;
1991     this->xref = xref;
1992 }
1993
1994 int SWFOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1995 {
1996     gfxfont_t*font = 0;
1997     fontlist_t*last=0,*l = this->fontlist;
1998
1999     /* TODO: should this be part of the state? */
2000     while(l) {
2001         last = l;
2002         if(!strcmp(l->id, id)) {
2003             current_font_id = l->id;
2004             current_gfxfont = l->font;
2005             font = l->font;
2006             output->addfont(output, id, current_gfxfont);
2007             return 1;
2008         }
2009         l = l->next;
2010     }
2011     if(!filename) return 0;
2012
2013     /* A font size of e.g. 9 means the font will be scaled down by
2014        1024 and scaled up by 9. So to have a maximum error of 1/20px,
2015        we have to divide 0.05 by (fontsize/1024)
2016      */
2017     double quality = (1024 * 0.05) / maxSize;
2018    
2019     msg("<verbose> Loading %s...", filename);
2020     font = gfxfont_load(filename, quality);
2021     msg("<verbose> Font %s loaded successfully", filename);
2022
2023     l = new fontlist_t;
2024     l->font = font;
2025     l->filename = strdup(filename);
2026     l->id = strdup(id);
2027     l->next = 0;
2028     current_font_id = l->id;
2029     current_gfxfont = l->font;
2030     if(last) {
2031         last->next = l;
2032     } else {
2033         this->fontlist = l;
2034     }
2035     output->addfont(output, id, current_gfxfont);
2036     return 1;
2037 }
2038
2039 void SWFOutputDev::updateFont(GfxState *state) 
2040 {
2041     GfxFont*gfxFont = state->getFont();
2042       
2043     if (!gfxFont) {
2044         return;
2045     }  
2046     
2047     char * fontid = getFontID(gfxFont);
2048     char * fontname = getFontName(gfxFont);
2049
2050     double maxSize = 1.0;
2051
2052     if(this->info) {
2053         maxSize = this->info->getMaximumFontSize(fontid);
2054     }
2055     
2056     int t;
2057     /* first, look if we substituted this font before-
2058        this way, we don't initialize the T1 Fonts
2059        too often */
2060     for(t=0;t<substitutepos;t++) {
2061         if(!strcmp(fontid, substitutesource[t])) {
2062             free(fontid);fontid=0;
2063             fontid = strdup(substitutetarget[t]);
2064             break;
2065         }
2066     }
2067
2068     /* second, see if this is a font which was used before-
2069        if so, we are done */
2070     if(setGfxFont(fontid, fontname, 0, 0)) {
2071         free(fontid);
2072         free(fontname);
2073         return;
2074     }
2075 /*    if(swfoutput_queryfont(&output, fontid))
2076         swfoutput_setfont(&output, fontid, 0);
2077         
2078         msg("<debug> updateFont(%s) [cached]", fontid);
2079         return;
2080     }*/
2081
2082     // look for Type 3 font
2083     if (gfxFont->getType() == fontType3) {
2084         if(!type3Warning) {
2085             type3Warning = gTrue;
2086             showFontError(gfxFont, 2);
2087         }
2088         free(fontid);
2089         free(fontname);
2090         return;
2091     }
2092
2093     /* now either load the font, or find a substitution */
2094
2095     Ref embRef;
2096     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2097
2098     char*fileName = 0;
2099     int del = 0;
2100     if(embedded &&
2101        (gfxFont->getType() == fontType1 ||
2102         gfxFont->getType() == fontType1C ||
2103        (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
2104         gfxFont->getType() == fontTrueType ||
2105         gfxFont->getType() == fontCIDType2
2106        ))
2107     {
2108       fileName = writeEmbeddedFontToFile(xref, gfxFont);
2109       if(!fileName) showFontError(gfxFont,0);
2110       else del = 1;
2111     } else {
2112       fileName = searchFont(fontname);
2113       if(!fileName) showFontError(gfxFont,0);
2114     }
2115     if(!fileName) {
2116         char * fontname = getFontName(gfxFont);
2117         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2118         
2119         if(lastfontdir)
2120             msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2121         else
2122             msg("<warning> Try specifying one or more font directories");
2123
2124         fileName = substituteFont(gfxFont, fontid);
2125         if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2126         msg("<notice> Font is now %s (%s)", fontid, fileName);
2127     }
2128
2129     if(!fileName) {
2130         msg("<error> Couldn't set font %s\n", fontid);
2131         free(fontid);
2132         free(fontname);
2133         return;
2134     }
2135         
2136     msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2137     dumpFontInfo("<verbose>", gfxFont);
2138
2139     //swfoutput_setfont(&output, fontid, fileName);
2140     
2141     if(!setGfxFont(fontid, fontname, 0, 0)) {
2142         setGfxFont(fontid, fontname, fileName, maxSize);
2143     }
2144    
2145     if(fileName && del)
2146         unlinkfont(fileName);
2147
2148     if(fileName)
2149         free(fileName);
2150     free(fontid);
2151     free(fontname);
2152
2153     msg("<verbose> |");
2154 }
2155
2156 #define SQR(x) ((x)*(x))
2157
2158 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2159 {
2160     if((newwidth<2 || newheight<2) ||
2161        (width<=newwidth || height<=newheight))
2162         return 0;
2163     unsigned char*newdata;
2164     int x,y;
2165     newdata= (unsigned char*)malloc(newwidth*newheight);
2166     int t;
2167     double fx = (double)(width)/newwidth;
2168     double fy = (double)(height)/newheight;
2169     double px = 0;
2170     int blocksize = (int)(8192/(fx*fy));
2171     int r = 8192*256/palettesize;
2172     for(x=0;x<newwidth;x++) {
2173         double ex = px + fx;
2174         int fromx = (int)px;
2175         int tox = (int)ex;
2176         int xweight1 = (int)(((fromx+1)-px)*256);
2177         int xweight2 = (int)((ex-tox)*256);
2178         double py =0;
2179         for(y=0;y<newheight;y++) {
2180             double ey = py + fy;
2181             int fromy = (int)py;
2182             int toy = (int)ey;
2183             int yweight1 = (int)(((fromy+1)-py)*256);
2184             int yweight2 = (int)((ey-toy)*256);
2185             int a = 0;
2186             int xx,yy;
2187             for(xx=fromx;xx<=tox;xx++)
2188             for(yy=fromy;yy<=toy;yy++) {
2189                 int b = 1-data[width*yy+xx];
2190                 int weight=256;
2191                 if(xx==fromx) weight = (weight*xweight1)/256;
2192                 if(xx==tox) weight = (weight*xweight2)/256;
2193                 if(yy==fromy) weight = (weight*yweight1)/256;
2194                 if(yy==toy) weight = (weight*yweight2)/256;
2195                 a+=b*weight;
2196             }
2197             //if(a) a=(palettesize-1)*r/blocksize;
2198             newdata[y*newwidth+x] = (a*blocksize)/r;
2199             py = ey;
2200         }
2201         px = ex;
2202     }
2203     return newdata;
2204 }
2205
2206 #define IMAGE_TYPE_JPEG 0
2207 #define IMAGE_TYPE_LOSSLESS 1
2208
2209 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2210         double x1,double y1,
2211         double x2,double y2,
2212         double x3,double y3,
2213         double x4,double y4, int type)
2214 {
2215     gfxcolor_t*newpic=0;
2216     
2217     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2218     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2219    
2220     gfxline_t p1,p2,p3,p4,p5;
2221     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2222     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2223     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2224     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2225     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2226
2227     {p1.x = (int)(p1.x*20)/20.0;
2228      p1.y = (int)(p1.y*20)/20.0;
2229      p2.x = (int)(p2.x*20)/20.0;
2230      p2.y = (int)(p2.y*20)/20.0;
2231      p3.x = (int)(p3.x*20)/20.0;
2232      p3.y = (int)(p3.y*20)/20.0;
2233      p4.x = (int)(p4.x*20)/20.0;
2234      p4.y = (int)(p4.y*20)/20.0;
2235      p5.x = (int)(p5.x*20)/20.0;
2236      p5.y = (int)(p5.y*20)/20.0;
2237     }
2238     
2239     float m00,m10,tx;
2240     float m01,m11,ty;
2241     
2242     gfxmatrix_t m;
2243     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2244     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2245     m.tx = p1.x - 0.5;
2246     m.ty = p1.y - 0.5;
2247
2248     gfximage_t img;
2249     img.data = (gfxcolor_t*)data;
2250     img.width = sizex;
2251     img.height = sizey;
2252   
2253     if(type == IMAGE_TYPE_JPEG)
2254         /* TODO: pass image_dpi to device instead */
2255         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2256
2257     dev->fillbitmap(dev, &p1, &img, &m, 0);
2258 }
2259
2260 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2261         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2262 {
2263     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2264 }
2265
2266 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2267         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2268 {
2269     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2270 }
2271
2272
2273 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2274                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2275                                    GBool inlineImg, int mask, int*maskColors,
2276                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2277 {
2278   double x1,y1,x2,y2,x3,y3,x4,y4;
2279   ImageStream *imgStr;
2280   Guchar pixBuf[4];
2281   GfxRGB rgb;
2282   int ncomps = 1;
2283   int bits = 1;
2284   unsigned char* maskbitmap = 0;
2285                                  
2286   if(colorMap) {
2287     ncomps = colorMap->getNumPixelComps();
2288     bits = colorMap->getBits();
2289   }
2290   
2291   if(maskStr) {
2292       int x,y;
2293       unsigned char buf[8];
2294       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2295       if(maskColorMap) {
2296           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2297           imgMaskStr->reset();
2298           unsigned char pal[256];
2299           int n = 1 << colorMap->getBits();
2300           int t;
2301           for(t=0;t<n;t++) {
2302               GfxGray gray;
2303               pixBuf[0] = t;
2304               maskColorMap->getGray(pixBuf, &gray);
2305               pal[t] = colToByte(gray);
2306           }
2307           for (y = 0; y < maskHeight; y++) {
2308               for (x = 0; x < maskWidth; x++) {
2309                   imgMaskStr->getPixel(buf);
2310                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
2311               }
2312           }
2313           delete imgMaskStr;
2314       } else {
2315           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2316           imgMaskStr->reset();
2317           for (y = 0; y < maskHeight; y++) {
2318               for (x = 0; x < maskWidth; x++) {
2319                   imgMaskStr->getPixel(buf);
2320                   buf[0]^=maskInvert;
2321                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2322               }
2323           }
2324           delete imgMaskStr;
2325       }
2326   }
2327       
2328   imgStr = new ImageStream(str, width, ncomps,bits);
2329   imgStr->reset();
2330
2331   if(!width || !height || (height<=1 && width<=1))
2332   {
2333       msg("<verbose> Ignoring %d by %d image", width, height);
2334       unsigned char buf[8];
2335       int x,y;
2336       for (y = 0; y < height; ++y)
2337       for (x = 0; x < width; ++x) {
2338           imgStr->getPixel(buf);
2339       }
2340       delete imgStr;
2341       if(maskbitmap)
2342           free(maskbitmap);
2343       return;
2344   }
2345
2346   state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
2347   state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
2348   state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
2349   state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
2350
2351   if(!pbminfo && !(str->getKind()==strDCT)) {
2352       if(!type3active) {
2353           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2354           pbminfo = 1;
2355       }
2356       if(mask)
2357       msg("<verbose> drawing %d by %d masked picture\n", width, height);
2358   }
2359   if(!jpeginfo && (str->getKind()==strDCT)) {
2360       msg("<notice> file contains jpeg pictures");
2361       jpeginfo = 1;
2362   }
2363
2364   if(mask) {
2365       int i,j;
2366       unsigned char buf[8];
2367       int x,y;
2368       unsigned char*pic = new unsigned char[width*height];
2369       gfxcolor_t pal[256];
2370       GfxRGB rgb;
2371       state->getFillRGB(&rgb);
2372
2373       memset(pal,255,sizeof(pal));
2374       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2375       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2376       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2377       pal[0].a = 255;              pal[1].a = 0;
2378     
2379       int numpalette = 2;
2380       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2381       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2382       for (y = 0; y < height; ++y)
2383       for (x = 0; x < width; ++x)
2384       {
2385             imgStr->getPixel(buf);
2386             if(invert) 
2387                 buf[0]=1-buf[0];
2388             pic[width*y+x] = buf[0];
2389       }
2390       
2391       /* the size of the drawn image is added to the identifier
2392          as the same image may require different bitmaps if displayed
2393          at different sizes (due to antialiasing): */
2394       int t,found = -1;
2395       if(type3active) {
2396           unsigned char*pic2 = 0;
2397           numpalette = 16;
2398           
2399           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2400
2401           if(!pic2) {
2402             delete pic;
2403             delete imgStr;
2404             return;
2405           }
2406
2407           width = realwidth;
2408           height = realheight;
2409           free(pic);
2410           pic = pic2;
2411           
2412           /* make a black/white palette */
2413
2414           float r = 255/(numpalette-1);
2415           int t;
2416           for(t=0;t<numpalette;t++) {
2417               pal[t].r = colToByte(rgb.r);
2418               pal[t].g = colToByte(rgb.g);
2419               pal[t].b = colToByte(rgb.b);
2420               pal[t].a = (unsigned char)(t*r);
2421           }
2422       }
2423
2424       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2425       for (y = 0; y < height; ++y) {
2426         for (x = 0; x < width; ++x) {
2427           pic2[width*y+x] = pal[pic[y*width+x]];
2428         }
2429       }
2430       drawimagelossless(output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2431       free(pic2);
2432       free(pic);
2433       delete imgStr;
2434       if(maskbitmap) free(maskbitmap);
2435       return;
2436   }
2437
2438   int x,y;
2439
2440   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2441       gfxcolor_t*pic=new gfxcolor_t[width*height];
2442       for (y = 0; y < height; ++y) {
2443         for (x = 0; x < width; ++x) {
2444           imgStr->getPixel(pixBuf);
2445           colorMap->getRGB(pixBuf, &rgb);
2446           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2447           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2448           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2449           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2450           if(maskbitmap) {
2451               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2452           }
2453         }
2454       }
2455       if(str->getKind()==strDCT)
2456           drawimagejpeg(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2457       else
2458           drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2459       delete pic;
2460       delete imgStr;
2461       if(maskbitmap) free(maskbitmap);
2462       return;
2463   } else {
2464       gfxcolor_t*pic=new gfxcolor_t[width*height];
2465       gfxcolor_t pal[256];
2466       int n = 1 << colorMap->getBits();
2467       int t;
2468       for(t=0;t<256;t++) {
2469           pixBuf[0] = t;
2470           colorMap->getRGB(pixBuf, &rgb);
2471
2472           {/*if(maskColors && *maskColors==t) {
2473               msg("<notice> Color %d is transparent", t);
2474               if (imgData->maskColors) {
2475                 *alpha = 0;
2476                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2477                   if (pix[i] < imgData->maskColors[2*i] ||
2478                       pix[i] > imgData->maskColors[2*i+1]) {
2479                     *alpha = 1;
2480                     break;
2481                   }
2482                 }
2483               } else {
2484                 *alpha = 1;
2485               }
2486               if(!*alpha) {
2487                     pal[t].r = 0;
2488                     pal[t].g = 0;
2489                     pal[t].b = 0;
2490                     pal[t].a = 0;
2491               }
2492           } else {*/
2493               pal[t].r = (unsigned char)(colToByte(rgb.r));
2494               pal[t].g = (unsigned char)(colToByte(rgb.g));
2495               pal[t].b = (unsigned char)(colToByte(rgb.b));
2496               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2497           }
2498       }
2499       for (y = 0; y < height; ++y) {
2500         for (x = 0; x < width; ++x) {
2501           imgStr->getPixel(pixBuf);
2502           pic[width*y+x] = pal[pixBuf[0]];
2503           if(maskbitmap) {
2504               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2505           }
2506         }
2507       }
2508       drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2509
2510       delete pic;
2511       delete imgStr;
2512       if(maskbitmap) free(maskbitmap);
2513       return;
2514   }
2515 }
2516
2517 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2518                                    int width, int height, GBool invert,
2519                                    GBool inlineImg) 
2520 {
2521   if(states[statepos].textRender & 4) //clipped
2522       return;
2523   msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2524   drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2525 }
2526
2527 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2528                          int width, int height, GfxImageColorMap *colorMap,
2529                          int *maskColors, GBool inlineImg)
2530 {
2531   if(states[statepos].textRender & 4) //clipped
2532       return;
2533
2534   msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2535           colorMap?"colorMap":"no colorMap", 
2536           maskColors?"maskColors":"no maskColors",
2537           inlineImg);
2538   if(colorMap)
2539       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2540               colorMap->getBits(),colorMap->getColorSpace()->getMode());
2541   drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2542 }
2543   
2544 void SWFOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2545                                int width, int height,
2546                                GfxImageColorMap *colorMap,
2547                                Stream *maskStr, int maskWidth, int maskHeight,
2548                                GBool maskInvert)
2549 {
2550   if(states[statepos].textRender & 4) //clipped
2551       return;
2552
2553   msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2554           colorMap?"colorMap":"no colorMap", 
2555           maskWidth, maskHeight);
2556   if(colorMap)
2557       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2558               colorMap->getBits(),colorMap->getColorSpace()->getMode());
2559   drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2560 }
2561
2562 void SWFOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2563                                    int width, int height,
2564                                    GfxImageColorMap *colorMap,
2565                                    Stream *maskStr,
2566                                    int maskWidth, int maskHeight,
2567                                    GfxImageColorMap *maskColorMap)
2568 {
2569   if(states[statepos].textRender & 4) //clipped
2570       return;
2571
2572   msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2573           colorMap?"colorMap":"no colorMap", 
2574           maskWidth, maskHeight);
2575   if(colorMap)
2576       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2577               colorMap->getBits(),colorMap->getColorSpace()->getMode());
2578   drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2579 }
2580
2581 //SWFOutputDev*output = 0; 
2582
2583 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
2584   Object obj;
2585   GString *s1, *s2;
2586   int i;
2587
2588   if (infoDict->lookup(key, &obj)->isString()) {
2589     s1 = obj.getString();
2590     if ((s1->getChar(0) & 0xff) == 0xfe &&
2591         (s1->getChar(1) & 0xff) == 0xff) {
2592       s2 = new GString();
2593       for (i = 2; i < obj.getString()->getLength(); i += 2) {
2594         if (s1->getChar(i) == '\0') {
2595           s2->append(s1->getChar(i+1));
2596         } else {
2597           delete s2;
2598           s2 = new GString("<unicode>");
2599           break;
2600         }
2601       }
2602       printf(fmt, s2->getCString());
2603       delete s2;
2604     } else {
2605       printf(fmt, s1->getCString());
2606     }
2607   }
2608   obj.free();
2609 }
2610
2611 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
2612   Object obj;
2613   char *s;
2614
2615   if (infoDict->lookup(key, &obj)->isString()) {
2616     s = obj.getString()->getCString();
2617     if (s[0] == 'D' && s[1] == ':') {
2618       s += 2;
2619     }
2620     printf(fmt, s);
2621   }
2622   obj.free();
2623 }
2624
2625 int jpeg_dpi = 0;
2626 int ppm_dpi = 0;
2627
2628 void storeDeviceParameter(char*name, char*value)
2629 {
2630     parameter_t*p = new parameter_t();
2631     p->name = strdup(name);
2632     p->value = strdup(value);
2633     p->next = 0;
2634     if(device_config_next) {
2635         device_config_next->next = p;
2636         device_config_next = p;
2637     } else {
2638         device_config = p;
2639         device_config_next = p;
2640     }
2641 }
2642
2643 void pdfswf_setparameter(char*name, char*value)
2644 {
2645     msg("<verbose> setting parameter %s to \"%s\"", name, value);
2646     if(!strcmp(name, "caplinewidth")) {
2647         caplinewidth = atof(value);
2648     } else if(!strcmp(name, "zoom")) {
2649         char buf[80];
2650         zoom = atof(value);
2651         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2652         storeDeviceParameter("jpegsubpixels", buf);
2653         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2654         storeDeviceParameter("ppmsubpixels", buf);
2655     } else if(!strcmp(name, "jpegdpi")) {
2656         char buf[80];
2657         jpeg_dpi = atoi(value);
2658         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2659         storeDeviceParameter("jpegsubpixels", buf);
2660     } else if(!strcmp(name, "ppmdpi")) {
2661         char buf[80];
2662         ppm_dpi = atoi(value);
2663         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2664         storeDeviceParameter("ppmsubpixels", buf);
2665     } else if(!strcmp(name, "forceType0Fonts")) {
2666         forceType0Fonts = atoi(value);
2667     } else if(!strncmp(name, "fontdir", strlen("fontdir"))) {
2668         pdfswf_addfontdir(value);
2669     } else if(!strncmp(name, "languagedir", strlen("languagedir"))) {
2670         pdfswf_addlanguagedir(value);
2671     } else if(!strcmp(name, "fontconfig")) {
2672         config_use_fontconfig = atoi(value);
2673     } else {
2674         storeDeviceParameter(name,value);
2675     }
2676 }
2677 void pdfswf_addfont(char*filename)
2678 {
2679     fontfile_t f;
2680     memset(&f, 0, sizeof(fontfile_t));
2681     f.filename = filename;
2682     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2683         fonts[fontnum++] = f;
2684     } else {
2685         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2686     }
2687 }
2688
2689 static char* dirseparator()
2690 {
2691 #ifdef WIN32
2692     return "\\";
2693 #else
2694     return "/";
2695 #endif
2696 }
2697
2698 void pdfswf_addlanguagedir(char*dir)
2699 {
2700     if(!globalParams)
2701         globalParams = new GlobalParams("");
2702     
2703     msg("<notice> Adding %s to language pack directories", dir);
2704
2705     int l;
2706     FILE*fi = 0;
2707     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2708     strcpy(config_file, dir);
2709     strcat(config_file, dirseparator());
2710     strcat(config_file, "add-to-xpdfrc");
2711
2712     fi = fopen(config_file, "rb");
2713     if(!fi) {
2714         msg("<error> Could not open %s", config_file);
2715         return;
2716     }
2717     globalParams->parseFile(new GString(config_file), fi);
2718     fclose(fi);
2719 }
2720
2721 void pdfswf_addfontdir(char*dirname)
2722 {
2723 #ifdef HAVE_DIRENT_H
2724     msg("<notice> Adding %s to font directories", dirname);
2725     lastfontdir = strdup(dirname);
2726     DIR*dir = opendir(dirname);
2727     if(!dir) {
2728         msg("<warning> Couldn't open directory %s\n", dirname);
2729         return;
2730     }
2731     struct dirent*ent;
2732     while(1) {
2733         ent = readdir (dir);
2734         if (!ent) 
2735             break;
2736         int l;
2737         char*name = ent->d_name;
2738         char type = 0;
2739         if(!name) continue;
2740         l=strlen(name);
2741         if(l<4)
2742             continue;
2743         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2744             type=1;
2745         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2746             type=3;
2747         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2748             type=2;
2749         if(type)
2750         {
2751             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2752             strcpy(fontname, dirname);
2753             strcat(fontname, dirseparator());
2754             strcat(fontname, name);
2755             msg("<verbose> Adding %s to fonts", fontname);
2756             pdfswf_addfont(fontname);
2757         }
2758     }
2759     closedir(dir);
2760 #else
2761     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2762 #endif
2763 }
2764
2765
2766 typedef struct _pdf_doc_internal
2767 {
2768     int protect;
2769     PDFDoc*doc;
2770     InfoOutputDev*info;
2771 } pdf_doc_internal_t;
2772 typedef struct _pdf_page_internal
2773 {
2774 } pdf_page_internal_t;
2775 typedef struct _swf_output_internal
2776 {
2777     SWFOutputDev*outputDev;
2778 } swf_output_internal_t;
2779
2780 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2781 {
2782     pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2783     memset(pdf_doc, 0, sizeof(pdf_doc_t));
2784     pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2785     memset(i, 0, sizeof(pdf_doc_internal_t));
2786     pdf_doc->internal = i;
2787     
2788     GString *fileName = new GString(filename);
2789     GString *userPW;
2790     Object info;
2791
2792     // read config file
2793     if(!globalParams)
2794         globalParams = new GlobalParams("");
2795
2796     // open PDF file
2797     if (userPassword && userPassword[0]) {
2798       userPW = new GString(userPassword);
2799     } else {
2800       userPW = NULL;
2801     }
2802     i->doc = new PDFDoc(fileName, userPW);
2803     if (userPW) {
2804       delete userPW;
2805     }
2806     if (!i->doc->isOk()) {
2807         return 0;
2808     }
2809
2810     // print doc info
2811     i->doc->getDocInfo(&info);
2812     if (info.isDict() &&
2813       (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2814       printInfoString(info.getDict(), "Title",        "Title:        %s\n");
2815       printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
2816       printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
2817       printInfoString(info.getDict(), "Author",       "Author:       %s\n");
2818       printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
2819       printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
2820       printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
2821       printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
2822       printf("Pages:        %d\n", i->doc->getNumPages());
2823       printf("Linearized:   %s\n", i->doc->isLinearized() ? "yes" : "no");
2824       printf("Encrypted:    ");
2825       if (i->doc->isEncrypted()) {
2826         printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2827                i->doc->okToPrint() ? "yes" : "no",
2828                i->doc->okToCopy() ? "yes" : "no",
2829                i->doc->okToChange() ? "yes" : "no",
2830                i->doc->okToAddNotes() ? "yes" : "no");
2831       } else {
2832         printf("no\n");
2833       }
2834     }
2835     info.free();
2836                    
2837     pdf_doc->num_pages = i->doc->getNumPages();
2838     i->protect = 0;
2839     if (i->doc->isEncrypted()) {
2840           if(!i->doc->okToCopy()) {
2841               printf("PDF disallows copying.\n");
2842               return 0;
2843           }
2844           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2845               i->protect = 1;
2846     }
2847
2848     InfoOutputDev*io = new InfoOutputDev();
2849     int t;
2850     for(t=1;t<=pdf_doc->num_pages;t++) {
2851         i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, /*usemediabox*/true, /*crop*/true, /*doLinks*/(int)1);
2852     }
2853     i->info = io;
2854
2855     return pdf_doc;
2856 }
2857
2858 class MemCheck
2859 {
2860     public: ~MemCheck()
2861     {
2862         delete globalParams;globalParams=0;
2863         Object::memCheck(stderr);
2864         gMemReport(stderr);
2865     }
2866 } myMemCheck;
2867
2868 void pdf_destroy(pdf_doc_t*pdf_doc)
2869 {
2870     pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2871
2872     delete i->doc; i->doc=0;
2873     
2874     if(i->info) {
2875         delete i->info;i->info=0;
2876     }
2877
2878     free(pdf_doc->internal);pdf_doc->internal=0;
2879     free(pdf_doc);pdf_doc=0;
2880 }
2881
2882 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2883 {
2884     pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2885
2886     if(page < 1 || page > pdf_doc->num_pages)
2887         return 0;
2888     
2889     pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2890     pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2891     memset(pi, 0, sizeof(pdf_page_internal_t));
2892     pdf_page->internal = pi;
2893
2894     pdf_page->parent = pdf_doc;
2895     pdf_page->nr = page;
2896     return pdf_page;
2897 }
2898
2899 void pdf_page_destroy(pdf_page_t*pdf_page)
2900 {
2901     pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2902     free(pdf_page->internal);pdf_page->internal = 0;
2903     free(pdf_page);pdf_page=0;
2904 }
2905
2906 swf_output_t* swf_output_init() 
2907 {
2908     swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2909     memset(swf_output, 0, sizeof(swf_output_t));
2910     swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2911     memset(i, 0, sizeof(swf_output_internal_t));
2912     swf_output->internal = i;
2913
2914     i->outputDev = new SWFOutputDev();
2915     return swf_output;
2916 }
2917
2918 void swf_output_setparameter(swf_output_t*swf, char*name, char*value)
2919 {
2920     pdfswf_setparameter(name, value);
2921 }
2922
2923 void swf_output_startframe(swf_output_t*swf, int width, int height)
2924 {
2925     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2926     i->outputDev->startFrame(width, height);
2927 }
2928
2929 void swf_output_endframe(swf_output_t*swf)
2930 {
2931     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2932     i->outputDev->endframe();
2933 }
2934
2935 void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage)
2936 {
2937     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2938     SWFOutputDev*o = i->outputDev;
2939
2940     if(pdfpage < 0)
2941         return;
2942
2943     if(!o->pages) {
2944         o->pagebuflen = 1024;
2945         o->pages = (int*)malloc(o->pagebuflen*sizeof(int));
2946         memset(o->pages, -1, o->pagebuflen*sizeof(int));
2947     } else {
2948         while(pdfpage >= o->pagebuflen)
2949         {
2950             int oldlen = o->pagebuflen;
2951             o->pagebuflen+=1024;
2952             o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int));
2953             memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int));
2954         }
2955     }
2956     o->pages[pdfpage] = outputpage;
2957     if(pdfpage>o->pagepos)
2958         o->pagepos = pdfpage;
2959 }
2960
2961 int swf_output_save(swf_output_t*swf, char*filename)
2962 {
2963     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2964     int ret = i->outputDev->save(filename);
2965     return ret;
2966 }
2967
2968 void* swf_output_get(swf_output_t*swf,char*name)
2969 {
2970     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2971     void* ret = i->outputDev->get(name);
2972     return ret;
2973 }
2974
2975 void swf_output_destroy(swf_output_t*output)
2976 {
2977     swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2978     delete i->outputDev; i->outputDev=0;
2979     free(output->internal);output->internal=0;
2980     free(output);
2981 }
2982
2983 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2984 {
2985     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2986     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2987
2988     if(!pi) {
2989         msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
2990         return;
2991     }
2992
2993     if(pi->protect) {
2994         gfxdevice_t*dev = si->outputDev->output;
2995         dev->setparameter(dev, "protect", "1");
2996     }
2997     si->outputDev->setInfo(pi->info);
2998     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2999     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, true, /*doLinks*/(int)1);
3000 }
3001
3002 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
3003 {
3004     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
3005     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
3006
3007     si->outputDev->setMove(x,y);
3008     if((x1|y1|x2|y2)==0) x2++;
3009     si->outputDev->setClip(x1,y1,x2,y2);
3010
3011     pdf_page_render2(page, output);
3012 }
3013 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
3014 {
3015     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
3016     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
3017     
3018     si->outputDev->setMove(0,0);
3019     si->outputDev->setClip(0,0,0,0);
3020     
3021     pdf_page_render2(page, output);
3022 }
3023
3024
3025 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
3026 {
3027     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
3028     pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
3029     pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
3030     memset(info, 0, sizeof(pdf_page_info_t));
3031
3032     InfoOutputDev*output = new InfoOutputDev;
3033     
3034     pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, true, /*doLinks*/(int)1);
3035
3036     info->xMin = output->x1;
3037     info->yMin = output->y1;
3038     info->xMax = output->x2;
3039     info->yMax = output->y2;
3040     info->number_of_images = output->num_images;
3041     info->number_of_links = output->num_links;
3042     info->number_of_fonts = output->num_fonts;
3043
3044     delete output;
3045
3046     return info;
3047 }
3048
3049 void pdf_page_info_destroy(pdf_page_info_t*info)
3050 {
3051     free(info);
3052
3053 }