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