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