small bugfix in drawchar(), removed rfxswf.h references
[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) {
1263         if(!name || 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     msg("<verbose> Loading %s...", filename);
2012     font = gfxfont_load(filename, quality);
2013     msg("<verbose> Font %s loaded successfully", filename);
2014
2015     l = new fontlist_t;
2016     l->font = font;
2017     l->filename = strdup(filename);
2018     l->id = strdup(id);
2019     l->next = 0;
2020     current_font_id = l->id;
2021     current_gfxfont = l->font;
2022     if(last) {
2023         last->next = l;
2024     } else {
2025         this->fontlist = l;
2026     }
2027     output->addfont(output, id, current_gfxfont);
2028     return 1;
2029 }
2030
2031 void SWFOutputDev::updateFont(GfxState *state) 
2032 {
2033     GfxFont*gfxFont = state->getFont();
2034       
2035     if (!gfxFont) {
2036         return;
2037     }  
2038     char * fontid = getFontID(gfxFont);
2039     double maxSize = 1.0;
2040
2041     if(this->info) {
2042         maxSize = this->info->getMaximumFontSize(fontid);
2043     }
2044     
2045     int t;
2046     /* first, look if we substituted this font before-
2047        this way, we don't initialize the T1 Fonts
2048        too often */
2049     for(t=0;t<substitutepos;t++) {
2050         if(!strcmp(fontid, substitutesource[t])) {
2051             free(fontid);fontid=0;
2052             fontid = strdup(substitutetarget[t]);
2053             break;
2054         }
2055     }
2056
2057     /* second, see if this is a font which was used before-
2058        if so, we are done */
2059     if(setGfxFont(fontid, 0, 0)) {
2060         free(fontid);
2061         return;
2062     }
2063 /*    if(swfoutput_queryfont(&output, fontid))
2064         swfoutput_setfont(&output, fontid, 0);
2065         
2066         msg("<debug> updateFont(%s) [cached]", fontid);
2067         return;
2068     }*/
2069
2070     // look for Type 3 font
2071     if (gfxFont->getType() == fontType3) {
2072         if(!type3Warning) {
2073             type3Warning = gTrue;
2074             showFontError(gfxFont, 2);
2075         }
2076         free(fontid);
2077         return;
2078     }
2079
2080     /* now either load the font, or find a substitution */
2081
2082     Ref embRef;
2083     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2084
2085     char*fileName = 0;
2086     int del = 0;
2087     if(embedded &&
2088        (gfxFont->getType() == fontType1 ||
2089         gfxFont->getType() == fontType1C ||
2090        (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
2091         gfxFont->getType() == fontTrueType ||
2092         gfxFont->getType() == fontCIDType2
2093        ))
2094     {
2095       fileName = writeEmbeddedFontToFile(xref, gfxFont);
2096       if(!fileName) showFontError(gfxFont,0);
2097       else del = 1;
2098     } else {
2099       char * fontname = getFontName(gfxFont);
2100       fileName = searchFont(fontname);
2101       if(!fileName) showFontError(gfxFont,0);
2102       free(fontname);
2103     }
2104     if(!fileName) {
2105         char * fontname = getFontName(gfxFont);
2106         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2107         
2108         if(lastfontdir)
2109             msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2110         else
2111             msg("<warning> Try specifying one or more font directories");
2112
2113         fileName = substituteFont(gfxFont, fontid);
2114         if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2115         msg("<notice> Font is now %s (%s)", fontid, fileName);
2116     }
2117
2118     if(!fileName) {
2119         msg("<error> Couldn't set font %s\n", fontid);
2120         free(fontid);
2121         return;
2122     }
2123         
2124     msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2125     dumpFontInfo("<verbose>", gfxFont);
2126
2127     //swfoutput_setfont(&output, fontid, fileName);
2128     
2129     if(!setGfxFont(fontid, 0, 0)) {
2130         setGfxFont(fontid, fileName, maxSize);
2131     }
2132    
2133     if(fileName && del)
2134         unlinkfont(fileName);
2135     if(fileName)
2136         free(fileName);
2137     free(fontid);
2138
2139     msg("<verbose> |");
2140 }
2141
2142 #define SQR(x) ((x)*(x))
2143
2144 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2145 {
2146     if((newwidth<2 || newheight<2) ||
2147        (width<=newwidth || height<=newheight))
2148         return 0;
2149     unsigned char*newdata;
2150     int x,y;
2151     newdata= (unsigned char*)malloc(newwidth*newheight);
2152     int t;
2153     double fx = (double)(width)/newwidth;
2154     double fy = (double)(height)/newheight;
2155     double px = 0;
2156     int blocksize = (int)(8192/(fx*fy));
2157     int r = 8192*256/palettesize;
2158     for(x=0;x<newwidth;x++) {
2159         double ex = px + fx;
2160         int fromx = (int)px;
2161         int tox = (int)ex;
2162         int xweight1 = (int)(((fromx+1)-px)*256);
2163         int xweight2 = (int)((ex-tox)*256);
2164         double py =0;
2165         for(y=0;y<newheight;y++) {
2166             double ey = py + fy;
2167             int fromy = (int)py;
2168             int toy = (int)ey;
2169             int yweight1 = (int)(((fromy+1)-py)*256);
2170             int yweight2 = (int)((ey-toy)*256);
2171             int a = 0;
2172             int xx,yy;
2173             for(xx=fromx;xx<=tox;xx++)
2174             for(yy=fromy;yy<=toy;yy++) {
2175                 int b = 1-data[width*yy+xx];
2176                 int weight=256;
2177                 if(xx==fromx) weight = (weight*xweight1)/256;
2178                 if(xx==tox) weight = (weight*xweight2)/256;
2179                 if(yy==fromy) weight = (weight*yweight1)/256;
2180                 if(yy==toy) weight = (weight*yweight2)/256;
2181                 a+=b*weight;
2182             }
2183             //if(a) a=(palettesize-1)*r/blocksize;
2184             newdata[y*newwidth+x] = (a*blocksize)/r;
2185             py = ey;
2186         }
2187         px = ex;
2188     }
2189     return newdata;
2190 }
2191
2192 #define IMAGE_TYPE_JPEG 0
2193 #define IMAGE_TYPE_LOSSLESS 1
2194
2195 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
2196         double x1,double y1,
2197         double x2,double y2,
2198         double x3,double y3,
2199         double x4,double y4, int type)
2200 {
2201     gfxcolor_t*newpic=0;
2202     
2203     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2204     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2205    
2206     gfxline_t p1,p2,p3,p4,p5;
2207     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2208     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2209     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2210     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2211     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2212
2213     {p1.x = (int)(p1.x*20)/20.0;
2214      p1.y = (int)(p1.y*20)/20.0;
2215      p2.x = (int)(p2.x*20)/20.0;
2216      p2.y = (int)(p2.y*20)/20.0;
2217      p3.x = (int)(p3.x*20)/20.0;
2218      p3.y = (int)(p3.y*20)/20.0;
2219      p4.x = (int)(p4.x*20)/20.0;
2220      p4.y = (int)(p4.y*20)/20.0;
2221      p5.x = (int)(p5.x*20)/20.0;
2222      p5.y = (int)(p5.y*20)/20.0;
2223     }
2224     
2225     float m00,m10,tx;
2226     float m01,m11,ty;
2227     
2228     gfxmatrix_t m;
2229     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2230     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2231     m.tx = p1.x - 0.5;
2232     m.ty = p1.y - 0.5;
2233
2234     gfximage_t img;
2235     img.data = (gfxcolor_t*)data;
2236     img.width = sizex;
2237     img.height = sizey;
2238   
2239     if(type == IMAGE_TYPE_JPEG)
2240         /* TODO: pass image_dpi to device instead */
2241         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2242
2243     dev->fillbitmap(dev, &p1, &img, &m, 0);
2244 }
2245
2246 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2247         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2248 {
2249     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2250 }
2251
2252 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
2253         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2254 {
2255     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2256 }
2257
2258
2259 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2260                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
2261                                    GBool inlineImg, int mask, int*maskColors)
2262 {
2263   FILE *fi;
2264   int c;
2265   char fileName[128];
2266   double x1,y1,x2,y2,x3,y3,x4,y4;
2267   ImageStream *imgStr;
2268   Guchar pixBuf[4];
2269   GfxRGB rgb;
2270   int ncomps = 1;
2271   int bits = 1;
2272                                  
2273   if(colorMap) {
2274     ncomps = colorMap->getNumPixelComps();
2275     bits = colorMap->getBits();
2276   }
2277   imgStr = new ImageStream(str, width, ncomps,bits);
2278   imgStr->reset();
2279
2280   if(!width || !height || (height<=1 && width<=1))
2281   {
2282       msg("<verbose> Ignoring %d by %d image", width, height);
2283       unsigned char buf[8];
2284       int x,y;
2285       for (y = 0; y < height; ++y)
2286       for (x = 0; x < width; ++x) {
2287           imgStr->getPixel(buf);
2288       }
2289       delete imgStr;
2290       return;
2291   }
2292   
2293   state->transform(0, 1, &x1, &y1); x1 += user_movex; y1+= user_movey;
2294   state->transform(0, 0, &x2, &y2); x2 += user_movex; y2+= user_movey;
2295   state->transform(1, 0, &x3, &y3); x3 += user_movex; y3+= user_movey;
2296   state->transform(1, 1, &x4, &y4); x4 += user_movex; y4+= user_movey;
2297
2298   if(!pbminfo && !(str->getKind()==strDCT)) {
2299       if(!type3active) {
2300           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2301           pbminfo = 1;
2302       }
2303       if(mask)
2304       msg("<verbose> drawing %d by %d masked picture\n", width, height);
2305   }
2306   if(!jpeginfo && (str->getKind()==strDCT)) {
2307       msg("<notice> file contains jpeg pictures");
2308       jpeginfo = 1;
2309   }
2310
2311   if(mask) {
2312       int i,j;
2313       unsigned char buf[8];
2314       int x,y;
2315       unsigned char*pic = new unsigned char[width*height];
2316       gfxcolor_t pal[256];
2317       GfxRGB rgb;
2318       state->getFillRGB(&rgb);
2319
2320       memset(pal,255,sizeof(pal));
2321       pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
2322       pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
2323       pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
2324       pal[0].a = 255;              pal[1].a = 0;
2325
2326       int numpalette = 2;
2327       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2328       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2329       for (y = 0; y < height; ++y)
2330       for (x = 0; x < width; ++x)
2331       {
2332             imgStr->getPixel(buf);
2333             if(invert) 
2334                 buf[0]=1-buf[0];
2335             pic[width*y+x] = buf[0];
2336       }
2337       
2338       /* the size of the drawn image is added to the identifier
2339          as the same image may require different bitmaps if displayed
2340          at different sizes (due to antialiasing): */
2341       int t,found = -1;
2342       if(type3active) {
2343           unsigned char*pic2 = 0;
2344           numpalette = 16;
2345           
2346           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2347
2348           if(!pic2) {
2349             delete pic;
2350             delete imgStr;
2351             return;
2352           }
2353
2354           width = realwidth;
2355           height = realheight;
2356           free(pic);
2357           pic = pic2;
2358           
2359           /* make a black/white palette */
2360           GfxRGB rgb2;
2361           rgb2.r = 1 - rgb.r;
2362           rgb2.g = 1 - rgb.g;
2363           rgb2.b = 1 - rgb.b;
2364           float r = 255/(numpalette-1);
2365           int t;
2366           for(t=0;t<numpalette;t++) {
2367               pal[t].r = (unsigned char)(255*rgb.r);
2368               pal[t].g = (unsigned char)(255*rgb.g);
2369               pal[t].b = (unsigned char)(255*rgb.b);
2370               pal[t].a = (unsigned char)(t*r);
2371           }
2372       }
2373
2374       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2375       for (y = 0; y < height; ++y) {
2376         for (x = 0; x < width; ++x) {
2377           pic2[width*y+x] = pal[pic[y*width+x]];
2378         }
2379       }
2380       drawimagelossless(output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2381       free(pic2);
2382       free(pic);
2383       delete imgStr;
2384       return;
2385   } 
2386
2387   int x,y;
2388
2389   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2390       gfxcolor_t*pic=new gfxcolor_t[width*height];
2391       for (y = 0; y < height; ++y) {
2392         for (x = 0; x < width; ++x) {
2393           imgStr->getPixel(pixBuf);
2394           colorMap->getRGB(pixBuf, &rgb);
2395           pic[width*y+x].r = (unsigned char)(rgb.r * 255 + 0.5);
2396           pic[width*y+x].g = (unsigned char)(rgb.g * 255 + 0.5);
2397           pic[width*y+x].b = (unsigned char)(rgb.b * 255 + 0.5);
2398           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2399         }
2400       }
2401       if(str->getKind()==strDCT)
2402           drawimagejpeg(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2403       else
2404           drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2405       delete pic;
2406       delete imgStr;
2407       return;
2408   } else {
2409       gfxcolor_t*pic=new gfxcolor_t[width*height];
2410       gfxcolor_t pal[256];
2411       int t;
2412       for(t=0;t<256;t++) {
2413           pixBuf[0] = t;
2414           colorMap->getRGB(pixBuf, &rgb);
2415           /*if(maskColors && *maskColors==t) {
2416               msg("<notice> Color %d is transparent", t);
2417               if (imgData->maskColors) {
2418                 *alpha = 0;
2419                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2420                   if (pix[i] < imgData->maskColors[2*i] ||
2421                       pix[i] > imgData->maskColors[2*i+1]) {
2422                     *alpha = 1;
2423                     break;
2424                   }
2425                 }
2426               } else {
2427                 *alpha = 1;
2428               }
2429               if(!*alpha) {
2430                     pal[t].r = 0;
2431                     pal[t].g = 0;
2432                     pal[t].b = 0;
2433                     pal[t].a = 0;
2434               }
2435           } else*/ {
2436               pal[t].r = (unsigned char)(rgb.r * 255 + 0.5);
2437               pal[t].g = (unsigned char)(rgb.g * 255 + 0.5);
2438               pal[t].b = (unsigned char)(rgb.b * 255 + 0.5);
2439               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2440           }
2441       }
2442       for (y = 0; y < height; ++y) {
2443         for (x = 0; x < width; ++x) {
2444           imgStr->getPixel(pixBuf);
2445           pic[width*y+x] = pal[pixBuf[0]];
2446         }
2447       }
2448       drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2449
2450       delete pic;
2451       delete imgStr;
2452       return;
2453   }
2454 }
2455
2456 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2457                                    int width, int height, GBool invert,
2458                                    GBool inlineImg) 
2459 {
2460   if(states[statepos].textRender & 4) //clipped
2461       return;
2462   msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2463   drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
2464 }
2465
2466 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2467                          int width, int height, GfxImageColorMap *colorMap,
2468                          int *maskColors, GBool inlineImg)
2469 {
2470   if(states[statepos].textRender & 4) //clipped
2471       return;
2472
2473   msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height, 
2474           colorMap?"colorMap":"no colorMap", 
2475           maskColors?"maskColors":"no maskColors",
2476           inlineImg);
2477   if(colorMap)
2478       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2479               colorMap->getBits(),colorMap->getColorSpace()->getMode());
2480   drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
2481 }
2482
2483 //SWFOutputDev*output = 0; 
2484
2485 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
2486   Object obj;
2487   GString *s1, *s2;
2488   int i;
2489
2490   if (infoDict->lookup(key, &obj)->isString()) {
2491     s1 = obj.getString();
2492     if ((s1->getChar(0) & 0xff) == 0xfe &&
2493         (s1->getChar(1) & 0xff) == 0xff) {
2494       s2 = new GString();
2495       for (i = 2; i < obj.getString()->getLength(); i += 2) {
2496         if (s1->getChar(i) == '\0') {
2497           s2->append(s1->getChar(i+1));
2498         } else {
2499           delete s2;
2500           s2 = new GString("<unicode>");
2501           break;
2502         }
2503       }
2504       printf(fmt, s2->getCString());
2505       delete s2;
2506     } else {
2507       printf(fmt, s1->getCString());
2508     }
2509   }
2510   obj.free();
2511 }
2512
2513 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
2514   Object obj;
2515   char *s;
2516
2517   if (infoDict->lookup(key, &obj)->isString()) {
2518     s = obj.getString()->getCString();
2519     if (s[0] == 'D' && s[1] == ':') {
2520       s += 2;
2521     }
2522     printf(fmt, s);
2523   }
2524   obj.free();
2525 }
2526
2527 int jpeg_dpi = 0;
2528 int ppm_dpi = 0;
2529
2530 void storeDeviceParameter(char*name, char*value)
2531 {
2532     parameter_t*p = new parameter_t();
2533     p->name = strdup(name);
2534     p->value = strdup(value);
2535     p->next = 0;
2536     if(device_config_next) {
2537         device_config_next->next = p;
2538         device_config_next = p;
2539     } else {
2540         device_config = p;
2541         device_config_next = p;
2542     }
2543 }
2544
2545 void pdfswf_setparameter(char*name, char*value)
2546 {
2547     msg("<verbose> setting parameter %s to \"%s\"", name, value);
2548     if(!strcmp(name, "caplinewidth")) {
2549         caplinewidth = atof(value);
2550     } else if(!strcmp(name, "zoom")) {
2551         char buf[80];
2552         zoom = atof(value);
2553         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2554         storeDeviceParameter("jpegsubpixels", buf);
2555         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2556         storeDeviceParameter("ppmsubpixels", buf);
2557     } else if(!strcmp(name, "jpegdpi")) {
2558         char buf[80];
2559         jpeg_dpi = atoi(value);
2560         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2561         storeDeviceParameter("jpegsubpixels", buf);
2562     } else if(!strcmp(name, "ppmdpi")) {
2563         char buf[80];
2564         ppm_dpi = atoi(value);
2565         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2566         storeDeviceParameter("ppmsubpixels", buf);
2567     } else if(!strcmp(name, "forceType0Fonts")) {
2568         forceType0Fonts = atoi(value);
2569     } else if(!strcmp(name, "fontdir")) {
2570         pdfswf_addfontdir(value);
2571     } else if(!strcmp(name, "languagedir")) {
2572         pdfswf_addlanguagedir(value);
2573     } else if(!strcmp(name, "fontconfig")) {
2574         config_use_fontconfig = atoi(value);
2575     } else {
2576         storeDeviceParameter(name,value);
2577     }
2578 }
2579 void pdfswf_addfont(char*filename)
2580 {
2581     fontfile_t f;
2582     memset(&f, 0, sizeof(fontfile_t));
2583     f.filename = filename;
2584     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2585         fonts[fontnum++] = f;
2586     } else {
2587         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2588     }
2589 }
2590
2591 static char* dirseparator()
2592 {
2593 #ifdef WIN32
2594     return "\\";
2595 #else
2596     return "/";
2597 #endif
2598 }
2599
2600 void pdfswf_addlanguagedir(char*dir)
2601 {
2602     if(!globalParams)
2603         globalParams = new GlobalParams("");
2604     
2605     msg("<notice> Adding %s to language pack directories", dir);
2606
2607     int l;
2608     FILE*fi = 0;
2609     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2610     strcpy(config_file, dir);
2611     strcat(config_file, dirseparator());
2612     strcat(config_file, "add-to-xpdfrc");
2613
2614     fi = fopen(config_file, "rb");
2615     if(!fi) {
2616         msg("<error> Could not open %s", config_file);
2617         return;
2618     }
2619     globalParams->parseFile(new GString(config_file), fi);
2620     fclose(fi);
2621 }
2622
2623 void pdfswf_addfontdir(char*dirname)
2624 {
2625 #ifdef HAVE_DIRENT_H
2626     msg("<notice> Adding %s to font directories", dirname);
2627     lastfontdir = strdup(dirname);
2628     DIR*dir = opendir(dirname);
2629     if(!dir) {
2630         msg("<warning> Couldn't open directory %s\n", dirname);
2631         return;
2632     }
2633     struct dirent*ent;
2634     while(1) {
2635         ent = readdir (dir);
2636         if (!ent) 
2637             break;
2638         int l;
2639         char*name = ent->d_name;
2640         char type = 0;
2641         if(!name) continue;
2642         l=strlen(name);
2643         if(l<4)
2644             continue;
2645         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2646             type=1;
2647         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2648             type=3;
2649         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2650             type=2;
2651         if(type)
2652         {
2653             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2654             strcpy(fontname, dirname);
2655             strcat(fontname, dirseparator());
2656             strcat(fontname, name);
2657             msg("<verbose> Adding %s to fonts", fontname);
2658             pdfswf_addfont(fontname);
2659         }
2660     }
2661     closedir(dir);
2662 #else
2663     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2664 #endif
2665 }
2666
2667
2668 typedef struct _pdf_doc_internal
2669 {
2670     int protect;
2671     PDFDoc*doc;
2672     InfoOutputDev*info;
2673 } pdf_doc_internal_t;
2674 typedef struct _pdf_page_internal
2675 {
2676 } pdf_page_internal_t;
2677 typedef struct _swf_output_internal
2678 {
2679     SWFOutputDev*outputDev;
2680 } swf_output_internal_t;
2681
2682 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2683 {
2684     pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2685     memset(pdf_doc, 0, sizeof(pdf_doc_t));
2686     pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2687     memset(i, 0, sizeof(pdf_doc_internal_t));
2688     pdf_doc->internal = i;
2689     
2690     GString *fileName = new GString(filename);
2691     GString *userPW;
2692     Object info;
2693
2694     // read config file
2695     if(!globalParams)
2696         globalParams = new GlobalParams("");
2697
2698     // open PDF file
2699     if (userPassword && userPassword[0]) {
2700       userPW = new GString(userPassword);
2701     } else {
2702       userPW = NULL;
2703     }
2704     i->doc = new PDFDoc(fileName, userPW);
2705     if (userPW) {
2706       delete userPW;
2707     }
2708     if (!i->doc->isOk()) {
2709         return 0;
2710     }
2711
2712     // print doc info
2713     i->doc->getDocInfo(&info);
2714     if (info.isDict() &&
2715       (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2716       printInfoString(info.getDict(), "Title",        "Title:        %s\n");
2717       printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
2718       printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
2719       printInfoString(info.getDict(), "Author",       "Author:       %s\n");
2720       printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
2721       printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
2722       printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
2723       printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
2724       printf("Pages:        %d\n", i->doc->getNumPages());
2725       printf("Linearized:   %s\n", i->doc->isLinearized() ? "yes" : "no");
2726       printf("Encrypted:    ");
2727       if (i->doc->isEncrypted()) {
2728         printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2729                i->doc->okToPrint() ? "yes" : "no",
2730                i->doc->okToCopy() ? "yes" : "no",
2731                i->doc->okToChange() ? "yes" : "no",
2732                i->doc->okToAddNotes() ? "yes" : "no");
2733       } else {
2734         printf("no\n");
2735       }
2736     }
2737     info.free();
2738                    
2739     pdf_doc->num_pages = i->doc->getNumPages();
2740     i->protect = 0;
2741     if (i->doc->isEncrypted()) {
2742           if(!i->doc->okToCopy()) {
2743               printf("PDF disallows copying.\n");
2744               return 0;
2745           }
2746           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2747               i->protect = 1;
2748     }
2749
2750     InfoOutputDev*io = new InfoOutputDev();
2751     int t;
2752     for(t=1;t<=pdf_doc->num_pages;t++) {
2753 #ifdef XPDF_101
2754         i->doc->displayPage((OutputDev*)io, t, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2755 #else
2756         i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2757 #endif
2758     }
2759     i->info = io;
2760
2761     return pdf_doc;
2762 }
2763
2764 class MemCheck
2765 {
2766     public: ~MemCheck()
2767     {
2768         delete globalParams;globalParams=0;
2769         Object::memCheck(stderr);
2770         gMemReport(stderr);
2771     }
2772 } myMemCheck;
2773
2774 void pdf_destroy(pdf_doc_t*pdf_doc)
2775 {
2776     pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2777
2778     delete i->doc; i->doc=0;
2779     
2780     if(i->info) {
2781         delete i->info;i->info=0;
2782     }
2783
2784     free(pdf_doc->internal);pdf_doc->internal=0;
2785     free(pdf_doc);pdf_doc=0;
2786 }
2787
2788 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2789 {
2790     pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2791
2792     if(page < 1 || page > pdf_doc->num_pages)
2793         return 0;
2794     
2795     pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2796     pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2797     memset(pi, 0, sizeof(pdf_page_internal_t));
2798     pdf_page->internal = pi;
2799
2800     pdf_page->parent = pdf_doc;
2801     pdf_page->nr = page;
2802     return pdf_page;
2803 }
2804
2805 void pdf_page_destroy(pdf_page_t*pdf_page)
2806 {
2807     pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2808     free(pdf_page->internal);pdf_page->internal = 0;
2809     free(pdf_page);pdf_page=0;
2810 }
2811
2812 swf_output_t* swf_output_init() 
2813 {
2814     swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2815     memset(swf_output, 0, sizeof(swf_output_t));
2816     swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2817     memset(i, 0, sizeof(swf_output_internal_t));
2818     swf_output->internal = i;
2819
2820     i->outputDev = new SWFOutputDev();
2821     return swf_output;
2822 }
2823
2824 void swf_output_setparameter(swf_output_t*swf, char*name, char*value)
2825 {
2826     pdfswf_setparameter(name, value);
2827 }
2828
2829 void swf_output_startframe(swf_output_t*swf, int width, int height)
2830 {
2831     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2832     i->outputDev->startFrame(width, height);
2833 }
2834
2835 void swf_output_endframe(swf_output_t*swf)
2836 {
2837     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2838     i->outputDev->endframe();
2839 }
2840
2841 void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage)
2842 {
2843     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2844     SWFOutputDev*o = i->outputDev;
2845
2846     if(pdfpage < 0)
2847         return;
2848
2849     if(!o->pages) {
2850         o->pagebuflen = 1024;
2851         o->pages = (int*)malloc(o->pagebuflen*sizeof(int));
2852         memset(o->pages, -1, o->pagebuflen*sizeof(int));
2853     } else {
2854         while(pdfpage >= o->pagebuflen)
2855         {
2856             int oldlen = o->pagebuflen;
2857             o->pagebuflen+=1024;
2858             o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int));
2859             memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int));
2860         }
2861     }
2862     o->pages[pdfpage] = outputpage;
2863     if(pdfpage>o->pagepos)
2864         o->pagepos = pdfpage;
2865 }
2866
2867 int swf_output_save(swf_output_t*swf, char*filename)
2868 {
2869     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2870     int ret = i->outputDev->save(filename);
2871     return ret;
2872 }
2873
2874 void* swf_output_get(swf_output_t*swf,char*name)
2875 {
2876     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2877     void* ret = i->outputDev->get(name);
2878     return ret;
2879 }
2880
2881 void swf_output_destroy(swf_output_t*output)
2882 {
2883     swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2884     delete i->outputDev; i->outputDev=0;
2885     free(output->internal);output->internal=0;
2886     free(output);
2887 }
2888
2889 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2890 {
2891     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2892     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2893
2894     if(!pi) {
2895         msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
2896         return;
2897     }
2898
2899     if(pi->protect) {
2900         gfxdevice_t*dev = si->outputDev->output;
2901         dev->setparameter(dev, "protect", "1");
2902     }
2903     si->outputDev->setInfo(pi->info);
2904     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2905 #ifdef XPDF_101
2906     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2907 #else
2908     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2909 #endif
2910 }
2911
2912 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2913 {
2914     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2915     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2916
2917     si->outputDev->setMove(x,y);
2918     if((x1|y1|x2|y2)==0) x2++;
2919     si->outputDev->setClip(x1,y1,x2,y2);
2920
2921     pdf_page_render2(page, output);
2922 }
2923 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2924 {
2925     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2926     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2927     
2928     si->outputDev->setMove(0,0);
2929     si->outputDev->setClip(0,0,0,0);
2930     
2931     pdf_page_render2(page, output);
2932 }
2933
2934
2935 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2936 {
2937     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2938     pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2939     pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2940     memset(info, 0, sizeof(pdf_page_info_t));
2941
2942     InfoOutputDev*output = new InfoOutputDev;
2943     
2944 #ifdef XPDF_101
2945     pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2946 #else
2947     pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2948 #endif
2949
2950     info->xMin = output->x1;
2951     info->yMin = output->y1;
2952     info->xMax = output->x2;
2953     info->yMax = output->y2;
2954     info->number_of_images = output->num_images;
2955     info->number_of_links = output->num_links;
2956     info->number_of_fonts = output->num_fonts;
2957
2958     delete output;
2959
2960     return info;
2961 }
2962
2963 void pdf_page_info_destroy(pdf_page_info_t*info)
2964 {
2965     free(info);
2966
2967 }