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