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