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