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