type 0 fonts are now enabled by default
[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_H
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 #endif
62 #include "SWFOutputDev.h"
63
64 //swftools header files
65 #include "swfoutput.h"
66 #include "../lib/log.h"
67 #include "../lib/gfxdevice.h"
68 #include "gfxtools.h"
69
70 #include <math.h>
71
72 typedef struct _fontfile
73 {
74     char*filename;
75     int used;
76 } fontfile_t;
77
78 // for pdfswf_addfont
79 static fontfile_t fonts[2048];
80 static int fontnum = 0;
81
82 static int config_use_fontconfig = 1;
83
84 // swf <-> pdf pages
85 // TODO: move into pdf_doc_t
86 static int*pages = 0;
87 static int pagebuflen = 0;
88 static int pagepos = 0;
89
90 /* config */
91 static double caplinewidth = 3.0;
92 static int zoom = 72; /* xpdf: 86 */
93 static int forceType0Fonts = 1;
94
95 static void printInfoString(Dict *infoDict, char *key, char *fmt);
96 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
97
98 struct mapping {
99     char*pdffont;
100     char*filename;
101 } pdf2t1map[] ={
102 {"Times-Roman",           "n021003l"},
103 {"Times-Italic",          "n021023l"},
104 {"Times-Bold",            "n021004l"},
105 {"Times-BoldItalic",      "n021024l"},
106 {"Helvetica",             "n019003l"},
107 {"Helvetica-Oblique",     "n019023l"},
108 {"Helvetica-Bold",        "n019004l"},
109 {"Helvetica-BoldOblique", "n019024l"},
110 {"Courier",               "n022003l"},
111 {"Courier-Oblique",       "n022023l"},
112 {"Courier-Bold",          "n022004l"},
113 {"Courier-BoldOblique",   "n022024l"},
114 {"Symbol",                "s050000l"},
115 {"ZapfDingbats",          "d050000l"}};
116
117 class SWFOutputState {
118     public:
119     int clipping;
120     int textRender;
121     SWFOutputState() {
122         this->clipping = 0;
123         this->textRender = 0;
124     }
125 };
126
127 class SWFOutputDev:  public OutputDev {
128   int outputstarted;
129   struct swfoutput output;
130 public:
131
132   // Constructor.
133   SWFOutputDev();
134
135   // Destructor.
136   virtual ~SWFOutputDev() ;
137
138   void setMove(int x,int y);
139   void setClip(int x1,int y1,int x2,int y2);
140   
141   int save(char*filename);
142   void  pagefeed();
143   void* getSWF();
144
145   void getDimensions(int*x1,int*y1,int*x2,int*y2);
146
147   //----- get info about output device
148
149   // Does this device use upside-down coordinates?
150   // (Upside-down means (0,0) is the top left corner of the page.)
151   virtual GBool upsideDown();
152
153   // Does this device use drawChar() or drawString()?
154   virtual GBool useDrawChar();
155   
156   // Can this device draw gradients?
157   virtual GBool useGradients();
158   
159   virtual GBool interpretType3Chars() {return gTrue;}
160
161   //----- initialization and control
162
163   void setXRef(PDFDoc*doc, XRef *xref);
164
165   // Start a page.
166   virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
167
168   //----- link borders
169   virtual void drawLink(Link *link, Catalog *catalog) ;
170
171   //----- save/restore graphics state
172   virtual void saveState(GfxState *state) ;
173   virtual void restoreState(GfxState *state) ;
174
175   //----- update graphics state
176
177   virtual void updateFont(GfxState *state);
178   virtual void updateFillColor(GfxState *state);
179   virtual void updateStrokeColor(GfxState *state);
180   virtual void updateLineWidth(GfxState *state);
181   virtual void updateLineJoin(GfxState *state);
182   virtual void updateLineCap(GfxState *state);
183   
184   virtual void updateAll(GfxState *state) 
185   {
186       updateFont(state);
187       updateFillColor(state);
188       updateStrokeColor(state);
189       updateLineWidth(state);
190       updateLineJoin(state);
191       updateLineCap(state);
192   };
193
194   //----- path painting
195   virtual void stroke(GfxState *state) ;
196   virtual void fill(GfxState *state) ;
197   virtual void eoFill(GfxState *state) ;
198
199   //----- path clipping
200   virtual void clip(GfxState *state) ;
201   virtual void eoClip(GfxState *state) ;
202
203   //----- text drawing
204   virtual void beginString(GfxState *state, GString *s) ;
205   virtual void endString(GfxState *state) ;
206   virtual void endTextObject(GfxState *state);
207   virtual void drawChar(GfxState *state, double x, double y,
208                         double dx, double dy,
209                         double originX, double originY,
210                         CharCode code, Unicode *u, int uLen);
211
212   //----- image drawing
213   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
214                              int width, int height, GBool invert,
215                              GBool inlineImg);
216   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
217                          int width, int height, GfxImageColorMap *colorMap,
218                          int *maskColors, GBool inlineImg);
219   
220   virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
221   virtual void endType3Char(GfxState *state);
222
223   virtual void type3D0(GfxState *state, double wx, double wy);
224   virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury);
225
226   private:
227   void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
228                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
229                                    GBool inlineImg, int mask, int *maskColors);
230   SWFOutputState states[64];
231   int statepos;
232
233   int currentpage;
234
235   PDFDoc*doc;
236   XRef*xref;
237
238   char* searchFont(char*name);
239   char* substituteFont(GfxFont*gfxFont, char*oldname);
240   char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
241   int t1id;
242   int jpeginfo; // did we write "File contains jpegs" yet?
243   int pbminfo; // did we write "File contains jpegs" yet?
244   int linkinfo; // did we write "File contains links" yet?
245   int ttfinfo; // did we write "File contains TrueType Fonts" yet?
246   int gradientinfo; // did we write "File contains Gradients yet?
247
248   int type3active; // are we between beginType3()/endType3()?
249
250   GfxState *laststate;
251
252   char type3Warning;
253
254   char* substitutetarget[256];
255   char* substitutesource[256];
256   int substitutepos;
257
258   int user_movex,user_movey;
259   int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
260 };
261
262 static char*getFontID(GfxFont*font);
263
264 class InfoOutputDev:  public OutputDev 
265 {
266   public:
267   int x1,y1,x2,y2;
268   int num_links;
269   int num_images;
270   int num_fonts;
271
272   InfoOutputDev() 
273   {
274       num_links = 0;
275       num_images = 0;
276       num_fonts = 0;
277   }
278   virtual ~InfoOutputDev() 
279   {
280   }
281   virtual GBool upsideDown() {return gTrue;}
282   virtual GBool useDrawChar() {return gTrue;}
283   virtual GBool useGradients() {return gTrue;}
284   virtual GBool interpretType3Chars() {return gTrue;}
285   virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
286   {
287       double x1,y1,x2,y2;
288       state->transform(crop_x1,crop_y1,&x1,&y1);
289       state->transform(crop_x2,crop_y2,&x2,&y2);
290       if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
291       if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
292       this->x1 = (int)x1;
293       this->y1 = (int)y1;
294       this->x2 = (int)x2;
295       this->y2 = (int)y2;
296   }
297   virtual void drawLink(Link *link, Catalog *catalog) 
298   {
299       num_links++;
300   }
301   virtual void updateFont(GfxState *state) 
302   {
303       GfxFont*font = state->getFont();
304       if(!font)
305           return;
306       /*char*id = getFontID(font);*/
307       /* FIXME*/
308       num_fonts++;
309   }
310   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
311                              int width, int height, GBool invert,
312                              GBool inlineImg) 
313   {
314       num_images++;
315   }
316   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
317                          int width, int height, GfxImageColorMap *colorMap,
318                          int *maskColors, GBool inlineImg)
319   {
320       num_images++;
321   }
322 };
323
324 SWFOutputDev::SWFOutputDev()
325 {
326     jpeginfo = 0;
327     ttfinfo = 0;
328     linkinfo = 0;
329     pbminfo = 0;
330     type3active = 0;
331     statepos = 0;
332     outputstarted = 0;
333     xref = 0;
334     substitutepos = 0;
335     type3Warning = 0;
336     user_movex = 0;
337     user_movey = 0;
338     user_clipx1 = 0;
339     user_clipy1 = 0;
340     user_clipx2 = 0;
341     user_clipy2 = 0;
342     memset(&output, 0, sizeof(output));
343 //    printf("SWFOutputDev::SWFOutputDev() \n");
344 };
345   
346 void SWFOutputDev::setMove(int x,int y)
347 {
348     this->user_movex = x;
349     this->user_movey = y;
350 }
351
352 void SWFOutputDev::setClip(int x1,int y1,int x2,int y2)
353 {
354     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
355     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
356
357     this->user_clipx1 = x1;
358     this->user_clipy1 = y1;
359     this->user_clipx2 = x2;
360     this->user_clipy2 = y2;
361 }
362 void SWFOutputDev::getDimensions(int*x1,int*y1,int*x2,int*y2)
363 {
364     return swfoutput_getdimensions(&output, x1,y1,x2,y2);
365 }
366
367 static char*getFontID(GfxFont*font)
368 {
369     GString*gstr = font->getName();
370     char* fontname = gstr==0?0:gstr->getCString();
371     if(fontname==0) {
372         char buf[32];
373         Ref*r=font->getID();
374         sprintf(buf, "UFONT%d", r->num);
375         return strdup(buf);
376     }
377     return strdup(fontname);
378 }
379
380 static char*getFontName(GfxFont*font)
381 {
382     char*fontid = getFontID(font);
383     char*fontname= 0;
384     char* plus = strchr(fontid, '+');
385     if(plus && plus < &fontid[strlen(fontid)-1]) {
386         fontname = strdup(plus+1);
387     } else {
388         fontname = strdup(fontid);
389     }
390     free(fontid);
391     return fontname;
392 }
393
394 static char mybuf[1024];
395 static char* gfxstate2str(GfxState *state)
396 {
397   char*bufpos = mybuf;
398   GfxRGB rgb;
399   bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
400                                     state->getCTM()[0],
401                                     state->getCTM()[1],
402                                     state->getCTM()[2],
403                                     state->getCTM()[3],
404                                     state->getCTM()[4],
405                                     state->getCTM()[5]);
406   if(state->getX1()!=0.0)
407   bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
408   if(state->getY1()!=0.0)
409   bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
410   bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
411   bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
412   bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
413   bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
414   /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
415           state->getFillColor()->c[0], state->getFillColor()->c[1]);
416   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
417           state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
418 /*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
419           state->getFillColor()->c[0], state->getFillColor()->c[1],
420           state->getFillColor()->c[2], state->getFillColor()->c[3],
421           state->getFillColor()->c[4], state->getFillColor()->c[5],
422           state->getFillColor()->c[6], state->getFillColor()->c[7]);
423   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
424           state->getStrokeColor()->c[0], state->getFillColor()->c[1],
425           state->getStrokeColor()->c[2], state->getFillColor()->c[3],
426           state->getStrokeColor()->c[4], state->getFillColor()->c[5],
427           state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
428   state->getFillRGB(&rgb);
429   if(rgb.r || rgb.g || rgb.b)
430   bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
431   state->getStrokeRGB(&rgb);
432   if(rgb.r || rgb.g || rgb.b)
433   bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
434   if(state->getFillColorSpace()->getNComps()>1)
435   bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
436   if(state->getStrokeColorSpace()->getNComps()>1)
437   bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
438   if(state->getFillPattern())
439   bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
440   if(state->getStrokePattern())
441   bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
442  
443   if(state->getFillOpacity()!=1.0)
444   bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
445   if(state->getStrokeOpacity()!=1.0)
446   bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
447
448   bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
449  
450   double * dash;
451   int length;
452   double start;
453   state->getLineDash(&dash, &length, &start);
454   int t;
455   if(length)
456   {
457       bufpos+=sprintf(bufpos,"DASH%.1f[",start);
458       for(t=0;t<length;t++) {
459           bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
460       }
461       bufpos+=sprintf(bufpos,"]");
462   }
463
464   if(state->getFlatness()!=1)
465   bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
466   if(state->getLineJoin()!=0)
467   bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
468   if(state->getLineJoin()!=0)
469   bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
470   if(state->getLineJoin()!=0)
471   bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
472
473   if(state->getFont() && getFontID(state->getFont()))
474   bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
475   bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
476   bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
477                                    state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
478   if(state->getCharSpace())
479   bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
480   if(state->getWordSpace())
481   bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
482   if(state->getHorizScaling()!=1.0)
483   bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
484   if(state->getLeading())
485   bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
486   if(state->getRise())
487   bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
488   if(state->getRender())
489   bufpos+=sprintf(bufpos,"R%d ", state->getRender());
490   bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
491   bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
492   bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
493   if(state->getLineX())
494   bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
495   if(state->getLineY())
496   bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
497   bufpos+=sprintf(bufpos," ");
498   return mybuf;
499 }
500
501 static void dumpFontInfo(char*loglevel, GfxFont*font);
502 static int lastdumps[1024];
503 static int lastdumppos = 0;
504 /* nr = 0  unknown
505    nr = 1  substituting
506    nr = 2  type 3
507  */
508 static void showFontError(GfxFont*font, int nr) 
509 {  
510     Ref*r=font->getID();
511     int t;
512     for(t=0;t<lastdumppos;t++)
513         if(lastdumps[t] == r->num)
514             break;
515     if(t < lastdumppos)
516       return;
517     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
518     lastdumps[lastdumppos++] = r->num;
519     if(nr == 0)
520       msg("<warning> The following font caused problems:");
521     else if(nr == 1)
522       msg("<warning> The following font caused problems (substituting):");
523     else if(nr == 2)
524       msg("<warning> The following Type 3 Font will be rendered as bitmap:");
525     dumpFontInfo("<warning>", font);
526 }
527
528 static void dumpFontInfo(char*loglevel, GfxFont*font)
529 {
530   char* id = getFontID(font);
531   char* name = getFontName(font);
532   Ref* r=font->getID();
533   msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
534
535   GString*gstr  = font->getTag();
536    
537   msg("%s| Tag: %s\n", loglevel, id);
538   
539   if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
540
541   GfxFontType type=font->getType();
542   switch(type) {
543     case fontUnknownType:
544      msg("%s| Type: unknown\n",loglevel);
545     break;
546     case fontType1:
547      msg("%s| Type: 1\n",loglevel);
548     break;
549     case fontType1C:
550      msg("%s| Type: 1C\n",loglevel);
551     break;
552     case fontType3:
553      msg("%s| Type: 3\n",loglevel);
554     break;
555     case fontTrueType:
556      msg("%s| Type: TrueType\n",loglevel);
557     break;
558     case fontCIDType0:
559      msg("%s| Type: CIDType0\n",loglevel);
560     break;
561     case fontCIDType0C:
562      msg("%s| Type: CIDType0C\n",loglevel);
563     break;
564     case fontCIDType2:
565      msg("%s| Type: CIDType2\n",loglevel);
566     break;
567   }
568   
569   Ref embRef;
570   GBool embedded = font->getEmbeddedFontID(&embRef);
571   char*embeddedName=0;
572   if(font->getEmbeddedFontName()) {
573     embeddedName = font->getEmbeddedFontName()->getCString();
574   }
575   if(embedded)
576    msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
577
578   gstr = font->getExtFontFile();
579   if(gstr)
580    msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
581
582   // Get font descriptor flags.
583   if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
584   if(font->isSerif()) msg("%s| is serif\n", loglevel);
585   if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
586   if(font->isItalic()) msg("%s| is italic\n", loglevel);
587   if(font->isBold()) msg("%s| is bold\n", loglevel);
588
589   free(id);
590   free(name);
591 }
592
593 //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");}
594 //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");}
595
596
597 void dump_outline(gfxline_t*line)
598 {
599     while(line) {
600         if(line->type == gfx_moveTo) {
601             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
602         } else if(line->type == gfx_lineTo) {
603             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
604         } else if(line->type == gfx_splineTo) {
605             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
606         }
607         line = line->next;
608     }
609 }
610
611 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
612 {
613     int num = path->getNumSubpaths();
614     int s,t;
615     int cpos = 0;
616     double lastx=0,lasty=0,posx=0,posy=0;
617     int needsfix=0;
618     if(!num) {
619         msg("<warning> empty path");
620         return 0;
621     }
622     gfxdrawer_t draw;
623     gfxdrawer_target_gfxline(&draw);
624
625     for(t = 0; t < num; t++) {
626         GfxSubpath *subpath = path->getSubpath(t);
627         int subnum = subpath->getNumPoints();
628         double bx=0,by=0,cx=0,cy=0;
629
630         for(s=0;s<subnum;s++) {
631            double x,y;
632            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
633            if(s==0) {
634                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
635                     draw.lineTo(&draw, lastx, lasty);
636                 }
637                 draw.moveTo(&draw, x,y);
638                 posx = lastx = x; 
639                 posy = lasty = y;
640                 cpos = 0;
641                 needsfix = 0;
642            } else if(subpath->getCurve(s) && cpos==0) {
643                 bx = x;
644                 by = y;
645                 cpos = 1;
646            } else if(subpath->getCurve(s) && cpos==1) {
647                 cx = x;
648                 cy = y;
649                 cpos = 2;
650            } else {
651                 posx = x;
652                 posy = y;
653                 if(cpos==0) {
654                     draw.lineTo(&draw, x,y);
655                 } else {
656                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y);
657                 }
658                 needsfix = 1;
659                 cpos = 0;
660            }
661         }
662     }
663     /* fix non-closed lines */
664     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
665         draw.lineTo(&draw, lastx, lasty);
666     }
667     gfxline_t*result = (gfxline_t*)draw.result(&draw);
668     return result;
669 }
670
671 /*----------------------------------------------------------------------------
672  * Primitive Graphic routines
673  *----------------------------------------------------------------------------*/
674
675 void SWFOutputDev::stroke(GfxState *state) 
676 {
677     GfxPath * path = state->getPath();
678     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
679     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
680     double miterLimit = state->getMiterLimit();
681     double width = state->getTransformedLineWidth();
682
683     GfxRGB rgb;
684     double opaq = state->getStrokeOpacity();
685     if(type3active)
686         state->getFillRGB(&rgb);
687     else
688         state->getStrokeRGB(&rgb);
689     gfxcolor_t col;
690     col.r = (unsigned char)(rgb.r*255);
691     col.g = (unsigned char)(rgb.g*255);
692     col.b = (unsigned char)(rgb.b*255);
693     col.a = (unsigned char)(opaq*255);
694    
695     gfx_capType capType = gfx_capRound;
696     if(lineCap == 0) capType = gfx_capButt;
697     else if(lineCap == 1) capType = gfx_capRound;
698     else if(lineCap == 2) capType = gfx_capSquare;
699
700     gfx_joinType joinType = gfx_joinRound;
701     if(lineJoin == 0) joinType = gfx_joinMiter;
702     else if(lineJoin == 1) joinType = gfx_joinRound;
703     else if(lineJoin == 2) joinType = gfx_joinBevel;
704
705     gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
706     
707     int dashnum = 0;
708     double dashphase = 0;
709     double * ldash = 0;
710     state->getLineDash(&ldash, &dashnum, &dashphase);
711
712     if(dashnum && ldash) {
713         float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
714         int t;
715         double cut = 0;
716         int fixzero = 0;
717         msg("<trace> %d dashes", dashnum);
718         msg("<trace> |  phase: %f", dashphase);
719         for(t=0;t<dashnum;t++) {
720             dash[t] = ldash[t];
721             msg("<trace> |  d%-3d: %f", t, ldash[t]);
722         }
723         dash[dashnum] = -1;
724         if(getLogLevel() >= LOGLEVEL_TRACE) {
725             dump_outline(line);
726         }
727
728         gfxline_t*line2 = gfxtool_dash_line(line, dash, dashphase);
729         gfxline_free(line);
730         line = line2;
731         msg("<trace> After dashing:");
732     }
733     
734     if(getLogLevel() >= LOGLEVEL_TRACE)  {
735         double gray;
736         state->getStrokeGray(&gray);
737         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
738                 width,
739                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
740                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
741                 dashnum,
742                 col.r,col.g,col.b,col.a,
743                 gray
744                 );
745         dump_outline(line);
746     }
747    
748     swfoutput_drawgfxline(&output, line, width, &col, capType, joinType, miterLimit);
749     gfxline_free(line);
750 }
751 void SWFOutputDev::fill(GfxState *state) 
752 {
753     GfxPath * path = state->getPath();
754     double opaq = state->getFillOpacity();
755     GfxRGB rgb;
756     state->getFillRGB(&rgb);
757     gfxcolor_t col;
758     col.r = (unsigned char)(rgb.r*255);
759     col.g = (unsigned char)(rgb.g*255);
760     col.b = (unsigned char)(rgb.b*255);
761     col.a = (unsigned char)(opaq*255);
762
763     gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
764
765     if(getLogLevel() >= LOGLEVEL_TRACE)  {
766         msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
767         dump_outline(line);
768     }
769
770     swfoutput_fillgfxline(&output, line, &col);
771     gfxline_free(line);
772 }
773 void SWFOutputDev::eoFill(GfxState *state) 
774 {
775     GfxPath * path = state->getPath();
776     double opaq = state->getFillOpacity();
777     GfxRGB rgb;
778     state->getFillRGB(&rgb);
779     gfxcolor_t col;
780     col.r = (unsigned char)(rgb.r*255);
781     col.g = (unsigned char)(rgb.g*255);
782     col.b = (unsigned char)(rgb.b*255);
783     col.a = (unsigned char)(opaq*255);
784
785     gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
786
787     if(getLogLevel() >= LOGLEVEL_TRACE)  {
788         msg("<trace> eofill\n");
789         dump_outline(line);
790     }
791
792     swfoutput_fillgfxline(&output, line, &col);
793     gfxline_free(line);
794 }
795 void SWFOutputDev::clip(GfxState *state) 
796 {
797     GfxPath * path = state->getPath();
798     gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
799
800     if(getLogLevel() >= LOGLEVEL_TRACE)  {
801         msg("<trace> clip\n");
802         dump_outline(line);
803     }
804
805     swfoutput_startclip(&output, line);
806     states[statepos].clipping++;
807     gfxline_free(line);
808 }
809 void SWFOutputDev::eoClip(GfxState *state) 
810 {
811     GfxPath * path = state->getPath();
812     gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
813
814     if(getLogLevel() >= LOGLEVEL_TRACE)  {
815         msg("<trace> eoclip\n");
816         dump_outline(line);
817     }
818
819     swfoutput_startclip(&output, line);
820     states[statepos].clipping++;
821     gfxline_free(line);
822 }
823
824 /* pass through functions for swf_output */
825 int SWFOutputDev::save(char*filename)
826 {
827     return swfoutput_save(&output, filename);
828 }
829 void SWFOutputDev::pagefeed()
830 {
831     swfoutput_pagefeed(&output);
832 }
833 void* SWFOutputDev::getSWF()
834 {
835     return (void*)swfoutput_get(&output);
836 }
837
838 SWFOutputDev::~SWFOutputDev() 
839 {
840     swfoutput_destroy(&output);
841     outputstarted = 0;
842 };
843 GBool SWFOutputDev::upsideDown() 
844 {
845     msg("<debug> upsidedown? yes");
846     return gTrue;
847 };
848 GBool SWFOutputDev::useDrawChar() 
849 {
850     return gTrue;
851 }
852 GBool SWFOutputDev::useGradients()
853 {
854     if(!gradientinfo)
855     {
856         msg("<notice> File contains gradients");
857         gradientinfo = 1;
858     }
859     return gTrue;
860 }
861
862 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
863                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
864
865 static char tmp_printstr[4096];
866 char* makeStringPrintable(char*str)
867 {
868     int len = strlen(str);
869     int dots = 0;
870     if(len>=80) {
871         len = 80;
872         dots = 1;
873     }
874     int t;
875     for(t=0;t<len;t++) {
876         char c = str[t];
877         if(c<32 || c>124) {
878             c = '.';
879         }
880         tmp_printstr[t] = c;
881     }
882     if(dots) {
883         tmp_printstr[len++] = '.';
884         tmp_printstr[len++] = '.';
885         tmp_printstr[len++] = '.';
886     }
887     tmp_printstr[len] = 0;
888     return tmp_printstr;
889 }
890
891 void SWFOutputDev::beginString(GfxState *state, GString *s) 
892
893     int render = state->getRender();
894     msg("<trace> beginString(%s) render=%d", s->getCString(), render);
895     double m11,m21,m12,m22;
896 //    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
897     state->getFontTransMat(&m11, &m12, &m21, &m22);
898     m11 *= state->getHorizScaling();
899     m21 *= state->getHorizScaling();
900     swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22);
901     if(render != 3 && render != 0)
902         msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));
903     states[statepos].textRender = render;
904 }
905
906 static int textCount = 0;
907
908 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
909                         double dx, double dy,
910                         double originX, double originY,
911                         CharCode c, Unicode *_u, int uLen)
912 {
913     textCount++;
914
915     int render = state->getRender();
916     // check for invisible text -- this is used by Acrobat Capture
917     if (render == 3)
918         return;
919
920     if(states[statepos].textRender != render)
921         msg("<error> Internal error: drawChar.render!=beginString.render");
922
923     GfxRGB rgb;
924     double opaq = state->getFillOpacity();
925     state->getFillRGB(&rgb);
926     gfxcolor_t col;
927     col.r = (unsigned char)(rgb.r*255);
928     col.g = (unsigned char)(rgb.g*255);
929     col.b = (unsigned char)(rgb.b*255);
930     col.a = (unsigned char)(opaq*255);
931
932     Gushort *CIDToGIDMap = 0;
933     GfxFont*font = state->getFont();
934
935     if(font->getType() == fontType3) {
936         /* type 3 chars are passed as graphics */
937         msg("<debug> type3 char at %f/%f", x, y);
938         return;
939     }
940     double x1,y1;
941     x1 = x;
942     y1 = y;
943     state->transform(x, y, &x1, &y1);
944     
945     Unicode u=0;
946     char*name=0;
947
948     if(_u && uLen) {
949         u = *_u;
950         if (u) {
951             int t;
952             /* find out char name from unicode index 
953                TODO: should be precomputed
954              */
955             for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
956                 if(nameToUnicodeTab[t].u == u) {
957                     name = nameToUnicodeTab[t].name;
958                     break;
959                 }
960             }
961         }
962     }
963
964     if(font->isCIDFont()) {
965         GfxCIDFont*cfont = (GfxCIDFont*)font;
966
967         if(font->getType() == fontCIDType2)
968             CIDToGIDMap = cfont->getCIDToGID();
969     } else {
970         Gfx8BitFont*font8;
971         font8 = (Gfx8BitFont*)font;
972         char**enc=font8->getEncoding();
973         if(enc && enc[c])
974            name = enc[c];
975     }
976  
977     if (CIDToGIDMap) {
978         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);
979         swfoutput_drawchar(&output, x1, y1, name, CIDToGIDMap[c], u, &col);
980     } else {
981         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);
982         swfoutput_drawchar(&output, x1, y1, name, c, u, &col);
983     }
984 }
985
986 void SWFOutputDev::endString(GfxState *state) 
987
988     msg("<trace> endString()");
989 }    
990
991 void SWFOutputDev::endTextObject(GfxState *state)
992 {
993     msg("<trace> endTextObject()");
994 }
995
996 /* the logic seems to be as following:
997    first, beginType3Char is called, with the charcode and the coordinates.
998    if this function returns true, it already knew about the char and has now drawn it.
999    if the function returns false, it's a new char, and type3D1 is called with some parameters-
1000    the all draw operations until endType3Char are part of the char (which in this moment is
1001    at the position first passed to beginType3Char). the char ends with endType3Char.
1002
1003    The drawing operations between beginType3Char and endType3Char are somewhat different to
1004    the normal ones. For example, the fillcolor equals the stroke color.
1005 */
1006
1007 GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1008 {
1009     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1010     type3active = 1;
1011     /* the character itself is going to be passed using the draw functions */
1012     return gFalse; /* gTrue= is_in_cache? */
1013 }
1014
1015 void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) {
1016     msg("<debug> type3D0 width=%f height=%f", wx, wy);
1017 }
1018 void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1019     msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1020             llx,lly,urx,ury);
1021 }
1022
1023 void SWFOutputDev::endType3Char(GfxState *state)
1024 {
1025     type3active = 0;
1026     msg("<debug> endType3Char");
1027 }
1028
1029 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1030 {
1031     this->currentpage = pageNum;
1032     double x1,y1,x2,y2;
1033     int rot = doc->getPageRotate(1);
1034     laststate = state;
1035     msg("<verbose> startPage %d (%f,%f,%f,%f)\n", pageNum, crop_x1, crop_y1, crop_x2, crop_y2);
1036     if(rot!=0)
1037         msg("<verbose> page is rotated %d degrees\n", rot);
1038
1039     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1040     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1041     Use CropBox, not MediaBox, as page size
1042     */
1043     
1044     /*x1 = crop_x1;
1045     y1 = crop_y1;
1046     x2 = crop_x2;
1047     y2 = crop_y2;*/
1048     state->transform(crop_x1,crop_y1,&x1,&y1);
1049     state->transform(crop_x2,crop_y2,&x2,&y2);
1050
1051     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1052     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1053
1054     /* apply user clip box */
1055     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1056         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1057         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1058         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1059         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1060     }
1061
1062     if(!outputstarted) {
1063         msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
1064         swfoutput_init(&output);
1065         outputstarted = 1;
1066     }
1067       
1068     swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2);
1069 }
1070
1071 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
1072 {
1073     msg("<debug> drawlink\n");
1074     double x1, y1, x2, y2, w;
1075     GfxRGB rgb;
1076     swfcoord points[5];
1077     int x, y;
1078
1079 #ifdef XPDF_101
1080     link->getBorder(&x1, &y1, &x2, &y2, &w);
1081 #else
1082     link->getRect(&x1, &y1, &x2, &y2);
1083 #endif
1084     rgb.r = 0;
1085     rgb.g = 0;
1086     rgb.b = 1;
1087     cvtUserToDev(x1, y1, &x, &y);
1088     points[0].x = points[4].x = (int)x;
1089     points[0].y = points[4].y = (int)y;
1090     cvtUserToDev(x2, y1, &x, &y);
1091     points[1].x = (int)x;
1092     points[1].y = (int)y;
1093     cvtUserToDev(x2, y2, &x, &y);
1094     points[2].x = (int)x;
1095     points[2].y = (int)y;
1096     cvtUserToDev(x1, y2, &x, &y);
1097     points[3].x = (int)x;
1098     points[3].y = (int)y;
1099
1100     LinkAction*action=link->getAction();
1101     char buf[128];
1102     char*s = 0;
1103     char*type = "-?-";
1104     char*url = 0;
1105     char*named = 0;
1106     int page = -1;
1107     switch(action->getKind())
1108     {
1109         case actionGoTo: {
1110             type = "GoTo";
1111             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1112             LinkDest *dest=NULL;
1113             if (ha->getDest()==NULL) 
1114                 dest=catalog->findDest(ha->getNamedDest());
1115             else dest=ha->getDest();
1116             if (dest){ 
1117               if (dest->isPageRef()){
1118                 Ref pageref=dest->getPageRef();
1119                 page=catalog->findPage(pageref.num,pageref.gen);
1120               }
1121               else  page=dest->getPageNum();
1122               sprintf(buf, "%d", page);
1123               s = strdup(buf);
1124             }
1125         }
1126         break;
1127         case actionGoToR: {
1128             type = "GoToR";
1129             LinkGoToR*l = (LinkGoToR*)action;
1130             GString*g = l->getNamedDest();
1131             if(g)
1132              s = strdup(g->getCString());
1133         }
1134         break;
1135         case actionNamed: {
1136             type = "Named";
1137             LinkNamed*l = (LinkNamed*)action;
1138             GString*name = l->getName();
1139             if(name) {
1140                 s = strdup(name->lowerCase()->getCString());
1141                 named = name->getCString();
1142                 if(!strchr(s,':')) 
1143                 {
1144                     if(strstr(s, "next") || strstr(s, "forward"))
1145                     {
1146                         page = currentpage + 1;
1147                     }
1148                     else if(strstr(s, "prev") || strstr(s, "back"))
1149                     {
1150                         page = currentpage - 1;
1151                     }
1152                     else if(strstr(s, "last") || strstr(s, "end"))
1153                     {
1154                         page = pagepos>0?pages[pagepos-1]:0;
1155                     }
1156                     else if(strstr(s, "first") || strstr(s, "top"))
1157                     {
1158                         page = 1;
1159                     }
1160                 }
1161             }
1162         }
1163         break;
1164         case actionLaunch: {
1165             type = "Launch";
1166             LinkLaunch*l = (LinkLaunch*)action;
1167             GString * str = new GString(l->getFileName());
1168             str->append(l->getParams());
1169             s = strdup(str->getCString());
1170             delete str;
1171         }
1172         break;
1173         case actionURI: {
1174             type = "URI";
1175             LinkURI*l = (LinkURI*)action;
1176             GString*g = l->getURI();
1177             if(g) {
1178              url = g->getCString();
1179              s = strdup(url);
1180             }
1181         }
1182         break;
1183         case actionUnknown: {
1184             type = "Unknown";
1185             LinkUnknown*l = (LinkUnknown*)action;
1186             s = strdup("");
1187         }
1188         break;
1189         default: {
1190             msg("<error> Unknown link type!\n");
1191             break;
1192         }
1193     }
1194     if(!s) s = strdup("-?-");
1195
1196     if(!linkinfo && (page || url))
1197     {
1198         msg("<notice> File contains links");
1199         linkinfo = 1;
1200     }
1201     if(page>0)
1202     {
1203         int t;
1204         for(t=0;t<pagepos;t++)
1205             if(pages[t]==page)
1206                 break;
1207         if(t!=pagepos)
1208             swfoutput_linktopage(&output, t, points);
1209     }
1210     else if(url)
1211     {
1212         swfoutput_linktourl(&output, url, points);
1213     }
1214     else if(named)
1215     {
1216         swfoutput_namedlink(&output, named, points);
1217     }
1218     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1219     free(s);s=0;
1220 }
1221
1222 void SWFOutputDev::saveState(GfxState *state) {
1223   msg("<trace> saveState\n");
1224   updateAll(state);
1225   if(statepos>=64) {
1226     msg("<error> Too many nested states in pdf.");
1227     return;
1228   }
1229   statepos ++;
1230   states[statepos].clipping = 0; //? shouldn't this be the current value?
1231   states[statepos].textRender = states[statepos-1].textRender;
1232 };
1233
1234 void SWFOutputDev::restoreState(GfxState *state) {
1235   msg("<trace> restoreState\n");
1236   updateAll(state);
1237   while(states[statepos].clipping) {
1238       swfoutput_endclip(&output);
1239       states[statepos].clipping--;
1240   }
1241   statepos--;
1242 }
1243
1244 char* SWFOutputDev::searchFont(char*name) 
1245 {       
1246     int i;
1247     char*filename=0;
1248     int is_standard_font = 0;
1249         
1250     msg("<verbose> SearchFont(%s)", name);
1251
1252     /* see if it is a pdf standard font */
1253     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
1254     {
1255         if(!strcmp(name, pdf2t1map[i].pdffont))
1256         {
1257             name = pdf2t1map[i].filename;
1258             is_standard_font = 1;
1259             break;
1260         }
1261     }
1262     /* look in all font files */
1263     for(i=0;i<fontnum;i++) 
1264     {
1265         if(strstr(fonts[i].filename, name))
1266         {
1267             if(!fonts[i].used) {
1268
1269                 fonts[i].used = 1;
1270                 if(!is_standard_font)
1271                     msg("<notice> Using %s for %s", fonts[i].filename, name);
1272             }
1273             return strdup(fonts[i].filename);
1274         }
1275     }
1276     return 0;
1277 }
1278
1279 void SWFOutputDev::updateLineWidth(GfxState *state)
1280 {
1281     double width = state->getTransformedLineWidth();
1282     //swfoutput_setlinewidth(&output, width);
1283 }
1284
1285 void SWFOutputDev::updateLineCap(GfxState *state)
1286 {
1287     int c = state->getLineCap();
1288 }
1289
1290 void SWFOutputDev::updateLineJoin(GfxState *state)
1291 {
1292     int j = state->getLineJoin();
1293 }
1294
1295 void SWFOutputDev::updateFillColor(GfxState *state) 
1296 {
1297     GfxRGB rgb;
1298     double opaq = state->getFillOpacity();
1299     state->getFillRGB(&rgb);
1300
1301     //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1302 }
1303
1304 void SWFOutputDev::updateStrokeColor(GfxState *state) 
1305 {
1306     GfxRGB rgb;
1307     double opaq = state->getStrokeOpacity();
1308     state->getStrokeRGB(&rgb);
1309     //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1310 }
1311
1312 void FoFiWrite(void *stream, char *data, int len)
1313 {
1314    fwrite(data, len, 1, (FILE*)stream);
1315 }
1316
1317 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1318 {
1319     char*tmpFileName = NULL;
1320     FILE *f;
1321     int c;
1322     char *fontBuf;
1323     int fontLen;
1324     Ref embRef;
1325     Object refObj, strObj;
1326     char namebuf[512];
1327     tmpFileName = mktmpname(namebuf);
1328     int ret;
1329
1330     ret = font->getEmbeddedFontID(&embRef);
1331     if(!ret) {
1332         msg("<verbose> Didn't get embedded font id");
1333         /* not embedded- the caller should now search the font
1334            directories for this font */
1335         return 0;
1336     }
1337
1338     f = fopen(tmpFileName, "wb");
1339     if (!f) {
1340       msg("<error> Couldn't create temporary Type 1 font file");
1341         return 0;
1342     }
1343
1344     /*if(font->isCIDFont()) {
1345         GfxCIDFont* cidFont = (GfxCIDFont *)font;
1346         GString c = cidFont->getCollection();
1347         msg("<notice> Collection: %s", c.getCString());
1348     }*/
1349
1350     if (font->getType() == fontType1C) {
1351       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1352         fclose(f);
1353         msg("<error> Couldn't read embedded font file");
1354         return 0;
1355       }
1356 #ifdef XPDF_101
1357       Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
1358       if(!cvt) return 0;
1359       cvt->convertToType1(f);
1360 #else
1361       FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1362       if(!cvt) return 0;
1363       cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1364 #endif
1365       //cvt->convertToCIDType0("test", f);
1366       //cvt->convertToType0("test", f);
1367       delete cvt;
1368       gfree(fontBuf);
1369     } else if(font->getType() == fontTrueType) {
1370       msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1371       if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1372         fclose(f);
1373         msg("<error> Couldn't read embedded font file");
1374         return 0;
1375       }
1376 #ifdef XPDF_101
1377       TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
1378       cvt->writeTTF(f);
1379 #else
1380       FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1381       cvt->writeTTF(FoFiWrite, f);
1382 #endif
1383       delete cvt;
1384       gfree(fontBuf);
1385     } else {
1386       font->getEmbeddedFontID(&embRef);
1387       refObj.initRef(embRef.num, embRef.gen);
1388       refObj.fetch(ref, &strObj);
1389       refObj.free();
1390       strObj.streamReset();
1391       int f4[4];
1392       char f4c[4];
1393       int t;
1394       for(t=0;t<4;t++) {
1395           f4[t] = strObj.streamGetChar();
1396           f4c[t] = (char)f4[t];
1397           if(f4[t] == EOF)
1398               break;
1399       }
1400       if(t==4) {
1401           if(!strncmp(f4c, "true", 4)) {
1402               /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1403                  Change this on the fly */
1404               f4[0] = f4[2] = f4[3] = 0;
1405               f4[1] = 1;
1406           }
1407           fputc(f4[0], f);
1408           fputc(f4[1], f);
1409           fputc(f4[2], f);
1410           fputc(f4[3], f);
1411
1412           while ((c = strObj.streamGetChar()) != EOF) {
1413             fputc(c, f);
1414           }
1415       }
1416       strObj.streamClose();
1417       strObj.free();
1418     }
1419     fclose(f);
1420
1421     return strdup(tmpFileName);
1422 }
1423     
1424 char* searchForSuitableFont(GfxFont*gfxFont)
1425 {
1426     char*name = getFontName(gfxFont);
1427     char*fontname = 0;
1428     char*filename = 0;
1429
1430     if(!config_use_fontconfig)
1431         return 0;
1432     
1433 #ifdef HAVE_FONTCONFIG
1434     FcPattern *pattern, *match;
1435     FcResult result;
1436     FcChar8 *v;
1437
1438     static int fcinitcalled = false; 
1439         
1440     msg("<debug> searchForSuitableFont(%s)", name);
1441     
1442     // call init ony once
1443     if (!fcinitcalled) {
1444         msg("<debug> Initializing FontConfig...");
1445         fcinitcalled = true;
1446         if(!FcInit()) {
1447             msg("<debug> FontConfig Initialization failed. Disabling.");
1448             config_use_fontconfig = 0;
1449             return 0;
1450         }
1451         msg("<debug> ...initialized FontConfig");
1452     }
1453    
1454     msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1455     pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1456     if (gfxFont->isItalic()) // check for italic
1457         msg("<debug> FontConfig: Adding Italic Slant");
1458         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1459     if (gfxFont->isBold()) // check for bold
1460         msg("<debug> FontConfig: Adding Bold Weight");
1461         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1462
1463     msg("<debug> FontConfig: Try to match...");
1464     // configure and match using the original font name 
1465     FcConfigSubstitute(0, pattern, FcMatchPattern); 
1466     FcDefaultSubstitute(pattern);
1467     match = FcFontMatch(0, pattern, &result);
1468     
1469     if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1470         msg("<debug> FontConfig: family=%s", (char*)v);
1471         // if we get an exact match
1472         if (strcmp((char *)v, name) == 0) {
1473             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1474                 filename = strdup((char*)v); // mem leak
1475                 char *nfn = strrchr(filename, '/');
1476                 if(nfn) fontname = strdup(nfn+1);
1477                 else    fontname = filename;
1478             }
1479             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1480         } else {
1481             // initialize patterns
1482             FcPatternDestroy(pattern);
1483             FcPatternDestroy(match);
1484
1485             // now match against serif etc.
1486             if (gfxFont->isSerif()) {
1487                 msg("<debug> FontConfig: Create Serif Family Pattern");
1488                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1489             } else if (gfxFont->isFixedWidth()) {
1490                 msg("<debug> FontConfig: Create Monospace Family Pattern");
1491                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1492             } else {
1493                 msg("<debug> FontConfig: Create Sans Family Pattern");
1494                 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1495             }
1496
1497             // check for italic
1498             if (gfxFont->isItalic()) {
1499                 msg("<debug> FontConfig: Adding Italic Slant");
1500                 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1501             }
1502             // check for bold
1503             if (gfxFont->isBold()) {
1504                 msg("<debug> FontConfig: Adding Bold Weight");
1505                 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1506             }
1507
1508             msg("<debug> FontConfig: Try to match... (2)");
1509             // configure and match using serif etc
1510             FcConfigSubstitute (0, pattern, FcMatchPattern);
1511             FcDefaultSubstitute (pattern);
1512             match = FcFontMatch (0, pattern, &result);
1513             
1514             if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1515                 filename = strdup((char*)v); // mem leak
1516                 char *nfn = strrchr(filename, '/');
1517                 if(nfn) fontname = strdup(nfn+1);
1518                 else    fontname = filename;
1519             }
1520             msg("<debug> FontConfig: Returning \"%s\"", fontname);
1521         }        
1522     }
1523
1524     //printf("FONTCONFIG: pattern");
1525     //FcPatternPrint(pattern);
1526     //printf("FONTCONFIG: match");
1527     //FcPatternPrint(match);
1528  
1529     FcPatternDestroy(pattern);
1530     FcPatternDestroy(match);
1531
1532     pdfswf_addfont(filename);
1533     return fontname;
1534 #else
1535     return 0;
1536 #endif
1537 }
1538
1539 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1540 {
1541     char*fontname = 0, *filename = 0;
1542     msg("<notice> subsituteFont(%s)", oldname);
1543
1544     if(!(fontname = searchForSuitableFont(gfxFont))) {
1545         fontname = "Times-Roman";
1546     }
1547     filename = searchFont(fontname);
1548     if(!filename) {
1549         msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1550         return 0;
1551     }
1552
1553     if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1554         msg("<fatal> Too many fonts in file.");
1555         exit(1);
1556     }
1557     if(oldname) {
1558         substitutesource[substitutepos] = strdup(oldname); //mem leak
1559         substitutetarget[substitutepos] = fontname;
1560         msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1561         substitutepos ++;
1562     }
1563     return strdup(filename); //mem leak
1564 }
1565
1566 void unlinkfont(char* filename)
1567 {
1568     int l;
1569     if(!filename)
1570         return;
1571     l=strlen(filename);
1572     unlink(filename);
1573     if(!strncmp(&filename[l-4],".afm",4)) {
1574         memcpy(&filename[l-4],".pfb",4);
1575         unlink(filename);
1576         memcpy(&filename[l-4],".pfa",4);
1577         unlink(filename);
1578         memcpy(&filename[l-4],".afm",4);
1579         return;
1580     } else 
1581     if(!strncmp(&filename[l-4],".pfa",4)) {
1582         memcpy(&filename[l-4],".afm",4);
1583         unlink(filename);
1584         memcpy(&filename[l-4],".pfa",4);
1585         return;
1586     } else 
1587     if(!strncmp(&filename[l-4],".pfb",4)) {
1588         memcpy(&filename[l-4],".afm",4);
1589         unlink(filename);
1590         memcpy(&filename[l-4],".pfb",4);
1591         return;
1592     }
1593 }
1594
1595 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1596 {
1597     this->doc = doc;
1598     this->xref = xref;
1599 }
1600
1601
1602 void SWFOutputDev::updateFont(GfxState *state) 
1603 {
1604     GfxFont*gfxFont = state->getFont();
1605       
1606     if (!gfxFont) {
1607         return;
1608     }  
1609     char * fontid = getFontID(gfxFont);
1610     
1611     int t;
1612     /* first, look if we substituted this font before-
1613        this way, we don't initialize the T1 Fonts
1614        too often */
1615     for(t=0;t<substitutepos;t++) {
1616         if(!strcmp(fontid, substitutesource[t])) {
1617             free(fontid);fontid=0;
1618             fontid = strdup(substitutetarget[t]);
1619             break;
1620         }
1621     }
1622
1623     /* second, see if swfoutput already has this font
1624        cached- if so, we are done */
1625     if(swfoutput_queryfont(&output, fontid))
1626     {
1627         swfoutput_setfont(&output, fontid, 0);
1628         
1629         msg("<debug> updateFont(%s) [cached]", fontid);
1630         free(fontid);
1631         return;
1632     }
1633
1634     // look for Type 3 font
1635     if (gfxFont->getType() == fontType3) {
1636         if(!type3Warning) {
1637             type3Warning = gTrue;
1638             showFontError(gfxFont, 2);
1639         }
1640         free(fontid);
1641         return;
1642     }
1643
1644     /* now either load the font, or find a substitution */
1645
1646     Ref embRef;
1647     GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1648
1649     char*fileName = 0;
1650     int del = 0;
1651     if(embedded &&
1652        (gfxFont->getType() == fontType1 ||
1653         gfxFont->getType() == fontType1C ||
1654        (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1655         gfxFont->getType() == fontTrueType ||
1656         gfxFont->getType() == fontCIDType2
1657        ))
1658     {
1659       fileName = writeEmbeddedFontToFile(xref, gfxFont);
1660       if(!fileName) showFontError(gfxFont,0);
1661       else del = 1;
1662     } else {
1663       char * fontname = getFontName(gfxFont);
1664       fileName = searchFont(fontname);
1665       if(!fileName) showFontError(gfxFont,0);
1666     }
1667     if(!fileName) {
1668         char * fontname = getFontName(gfxFont);
1669         msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1670         msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname);
1671         fileName = substituteFont(gfxFont, fontid);
1672         if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1673         msg("<notice> Font is now %s (%s)", fontid, fileName);
1674     }
1675
1676     if(!fileName) {
1677         msg("<error> Couldn't set font %s\n", fontid);
1678         free(fontid);
1679         return;
1680     }
1681         
1682     msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
1683     dumpFontInfo("<verbose>", gfxFont);
1684
1685     swfoutput_setfont(&output, fontid, fileName);
1686    
1687     if(fileName && del)
1688         unlinkfont(fileName);
1689     if(fileName)
1690         free(fileName);
1691     free(fontid);
1692 }
1693
1694 #define SQR(x) ((x)*(x))
1695
1696 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1697 {
1698     if((newwidth<2 || newheight<2) ||
1699        (width<=newwidth || height<=newheight))
1700         return 0;
1701     unsigned char*newdata;
1702     int x,y;
1703     newdata= (unsigned char*)malloc(newwidth*newheight);
1704     int t;
1705     double fx = (double)(width)/newwidth;
1706     double fy = (double)(height)/newheight;
1707     double px = 0;
1708     int blocksize = (int)(8192/(fx*fy));
1709     int r = 8192*256/palettesize;
1710     for(x=0;x<newwidth;x++) {
1711         double ex = px + fx;
1712         int fromx = (int)px;
1713         int tox = (int)ex;
1714         int xweight1 = (int)(((fromx+1)-px)*256);
1715         int xweight2 = (int)((ex-tox)*256);
1716         double py =0;
1717         for(y=0;y<newheight;y++) {
1718             double ey = py + fy;
1719             int fromy = (int)py;
1720             int toy = (int)ey;
1721             int yweight1 = (int)(((fromy+1)-py)*256);
1722             int yweight2 = (int)((ey-toy)*256);
1723             int a = 0;
1724             int xx,yy;
1725             for(xx=fromx;xx<=tox;xx++)
1726             for(yy=fromy;yy<=toy;yy++) {
1727                 int b = 1-data[width*yy+xx];
1728                 int weight=256;
1729                 if(xx==fromx) weight = (weight*xweight1)/256;
1730                 if(xx==tox) weight = (weight*xweight2)/256;
1731                 if(yy==fromy) weight = (weight*yweight1)/256;
1732                 if(yy==toy) weight = (weight*yweight2)/256;
1733                 a+=b*weight;
1734             }
1735             //if(a) a=(palettesize-1)*r/blocksize;
1736             newdata[y*newwidth+x] = (a*blocksize)/r;
1737             py = ey;
1738         }
1739         px = ex;
1740     }
1741     return newdata;
1742 }
1743
1744 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1745                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1746                                    GBool inlineImg, int mask, int*maskColors)
1747 {
1748   FILE *fi;
1749   int c;
1750   char fileName[128];
1751   double x1,y1,x2,y2,x3,y3,x4,y4;
1752   ImageStream *imgStr;
1753   Guchar pixBuf[4];
1754   GfxRGB rgb;
1755   int ncomps = 1;
1756   int bits = 1;
1757                                  
1758   if(colorMap) {
1759     ncomps = colorMap->getNumPixelComps();
1760     bits = colorMap->getBits();
1761   }
1762   imgStr = new ImageStream(str, width, ncomps,bits);
1763   imgStr->reset();
1764
1765   if(!width || !height || (height<=1 && width<=1))
1766   {
1767       msg("<verbose> Ignoring %d by %d image", width, height);
1768       unsigned char buf[8];
1769       int x,y;
1770       for (y = 0; y < height; ++y)
1771       for (x = 0; x < width; ++x) {
1772           imgStr->getPixel(buf);
1773       }
1774       delete imgStr;
1775       return;
1776   }
1777   
1778   state->transform(0, 1, &x1, &y1);
1779   state->transform(0, 0, &x2, &y2);
1780   state->transform(1, 0, &x3, &y3);
1781   state->transform(1, 1, &x4, &y4);
1782
1783   if(!pbminfo && !(str->getKind()==strDCT)) {
1784       if(!type3active) {
1785           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1786           pbminfo = 1;
1787       }
1788       if(mask)
1789       msg("<verbose> drawing %d by %d masked picture\n", width, height);
1790   }
1791   if(!jpeginfo && (str->getKind()==strDCT)) {
1792       msg("<notice> file contains jpeg pictures");
1793       jpeginfo = 1;
1794   }
1795
1796   if(mask) {
1797       int i,j;
1798       unsigned char buf[8];
1799       int x,y;
1800       unsigned char*pic = new unsigned char[width*height];
1801       RGBA pal[256];
1802       GfxRGB rgb;
1803       state->getFillRGB(&rgb);
1804
1805       memset(pal,255,sizeof(pal));
1806       pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
1807       pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
1808       pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
1809       pal[0].a = 255;              pal[1].a = 0;
1810
1811       int numpalette = 2;
1812       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1813       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1814       for (y = 0; y < height; ++y)
1815       for (x = 0; x < width; ++x)
1816       {
1817             imgStr->getPixel(buf);
1818             if(invert) 
1819                 buf[0]=1-buf[0];
1820             pic[width*y+x] = buf[0];
1821       }
1822       
1823       /* the size of the drawn image is added to the identifier
1824          as the same image may require different bitmaps if displayed
1825          at different sizes (due to antialiasing): */
1826       int t,found = -1;
1827       if(type3active) {
1828           unsigned char*pic2 = 0;
1829           numpalette = 16;
1830           
1831           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1832
1833           if(!pic2) {
1834             delete pic;
1835             delete imgStr;
1836             return;
1837           }
1838
1839           width = realwidth;
1840           height = realheight;
1841           free(pic);
1842           pic = pic2;
1843           
1844           /* make a black/white palette */
1845           GfxRGB rgb2;
1846           rgb2.r = 1 - rgb.r;
1847           rgb2.g = 1 - rgb.g;
1848           rgb2.b = 1 - rgb.b;
1849           float r = 255/(numpalette-1);
1850           int t;
1851           for(t=0;t<numpalette;t++) {
1852               pal[t].r = (U8)(255*rgb.r);
1853               pal[t].g = (U8)(255*rgb.g);
1854               pal[t].b = (U8)(255*rgb.b);
1855               pal[t].a = (U8)(t*r);
1856           }
1857       }
1858
1859       RGBA*pic2 = new RGBA[width*height];
1860       for (y = 0; y < height; ++y) {
1861         for (x = 0; x < width; ++x) {
1862           pic2[width*y+x] = pal[pic[y*width+x]];
1863         }
1864       }
1865       swfoutput_drawimagelossless(&output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1866       free(pic2);
1867       free(pic);
1868       delete imgStr;
1869       return;
1870   } 
1871
1872   int x,y;
1873
1874   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1875       RGBA*pic=new RGBA[width*height];
1876       for (y = 0; y < height; ++y) {
1877         for (x = 0; x < width; ++x) {
1878           imgStr->getPixel(pixBuf);
1879           colorMap->getRGB(pixBuf, &rgb);
1880           pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5);
1881           pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5);
1882           pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5);
1883           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1884         }
1885       }
1886       if(str->getKind()==strDCT)
1887           swfoutput_drawimagejpeg(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1888       else
1889           swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1890       delete pic;
1891       delete imgStr;
1892       return;
1893   } else {
1894       RGBA*pic=new RGBA[width*height];
1895       RGBA pal[256];
1896       int t;
1897       for(t=0;t<256;t++) {
1898           pixBuf[0] = t;
1899           colorMap->getRGB(pixBuf, &rgb);
1900           /*if(maskColors && *maskColors==t) {
1901               msg("<notice> Color %d is transparent", t);
1902               if (imgData->maskColors) {
1903                 *alpha = 0;
1904                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1905                   if (pix[i] < imgData->maskColors[2*i] ||
1906                       pix[i] > imgData->maskColors[2*i+1]) {
1907                     *alpha = 1;
1908                     break;
1909                   }
1910                 }
1911               } else {
1912                 *alpha = 1;
1913               }
1914               if(!*alpha) {
1915                     pal[t].r = 0;
1916                     pal[t].g = 0;
1917                     pal[t].b = 0;
1918                     pal[t].a = 0;
1919               }
1920           } else*/ {
1921               pal[t].r = (U8)(rgb.r * 255 + 0.5);
1922               pal[t].g = (U8)(rgb.g * 255 + 0.5);
1923               pal[t].b = (U8)(rgb.b * 255 + 0.5);
1924               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1925           }
1926       }
1927       for (y = 0; y < height; ++y) {
1928         for (x = 0; x < width; ++x) {
1929           imgStr->getPixel(pixBuf);
1930           pic[width*y+x] = pal[pixBuf[0]];
1931         }
1932       }
1933       swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1934
1935       delete pic;
1936       delete imgStr;
1937       return;
1938   }
1939 }
1940
1941 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1942                                    int width, int height, GBool invert,
1943                                    GBool inlineImg) 
1944 {
1945   if(states[statepos].textRender & 4) //clipped
1946       return;
1947   msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1948   drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
1949 }
1950
1951 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1952                          int width, int height, GfxImageColorMap *colorMap,
1953                          int *maskColors, GBool inlineImg)
1954 {
1955   if(states[statepos].textRender & 4) //clipped
1956       return;
1957
1958   msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height, 
1959           colorMap?"colorMap":"no colorMap", 
1960           maskColors?"maskColors":"no maskColors",
1961           inlineImg);
1962   if(colorMap)
1963       msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1964               colorMap->getBits(),colorMap->getColorSpace()->getMode());
1965   drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
1966 }
1967
1968 SWFOutputDev*output = 0; 
1969
1970 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
1971   Object obj;
1972   GString *s1, *s2;
1973   int i;
1974
1975   if (infoDict->lookup(key, &obj)->isString()) {
1976     s1 = obj.getString();
1977     if ((s1->getChar(0) & 0xff) == 0xfe &&
1978         (s1->getChar(1) & 0xff) == 0xff) {
1979       s2 = new GString();
1980       for (i = 2; i < obj.getString()->getLength(); i += 2) {
1981         if (s1->getChar(i) == '\0') {
1982           s2->append(s1->getChar(i+1));
1983         } else {
1984           delete s2;
1985           s2 = new GString("<unicode>");
1986           break;
1987         }
1988       }
1989       printf(fmt, s2->getCString());
1990       delete s2;
1991     } else {
1992       printf(fmt, s1->getCString());
1993     }
1994   }
1995   obj.free();
1996 }
1997
1998 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
1999   Object obj;
2000   char *s;
2001
2002   if (infoDict->lookup(key, &obj)->isString()) {
2003     s = obj.getString()->getCString();
2004     if (s[0] == 'D' && s[1] == ':') {
2005       s += 2;
2006     }
2007     printf(fmt, s);
2008   }
2009   obj.free();
2010 }
2011
2012 int jpeg_dpi = 0;
2013 int ppm_dpi = 0;
2014
2015 void pdfswf_setparameter(char*name, char*value)
2016 {
2017     msg("<verbose> setting parameter %s to \"%s\"", name, value);
2018     if(!strcmp(name, "caplinewidth")) {
2019         caplinewidth = atof(value);
2020     } else if(!strcmp(name, "zoom")) {
2021         char buf[80];
2022         zoom = atoi(value);
2023         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2024         swfoutput_setparameter("jpegsubpixels", buf);
2025         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2026         swfoutput_setparameter("ppmsubpixels", buf);
2027     } else if(!strcmp(name, "jpegdpi")) {
2028         char buf[80];
2029         jpeg_dpi = atoi(value);
2030         sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2031         swfoutput_setparameter("jpegsubpixels", buf);
2032     } else if(!strcmp(name, "ppmdpi")) {
2033         char buf[80];
2034         ppm_dpi = atoi(value);
2035         sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2036         swfoutput_setparameter("ppmsubpixels", buf);
2037     } else if(!strcmp(name, "forceType0Fonts")) {
2038         forceType0Fonts = atoi(value);
2039     } else if(!strcmp(name, "fontdir")) {
2040         pdfswf_addfontdir(value);
2041     } else if(!strcmp(name, "languagedir")) {
2042         pdfswf_addlanguagedir(value);
2043     } else if(!strcmp(name, "fontconfig")) {
2044         config_use_fontconfig = atoi(value);
2045     } else {
2046         swfoutput_setparameter(name, value);
2047     }
2048 }
2049 void pdfswf_addfont(char*filename)
2050 {
2051     fontfile_t f;
2052     memset(&f, 0, sizeof(fontfile_t));
2053     f.filename = filename;
2054     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2055         fonts[fontnum++] = f;
2056     } else {
2057         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2058     }
2059 }
2060
2061 static char* dirseparator()
2062 {
2063 #ifdef WIN32
2064     return "\\";
2065 #else
2066     return "/";
2067 #endif
2068 }
2069
2070 void pdfswf_addlanguagedir(char*dir)
2071 {
2072     if(!globalParams)
2073         globalParams = new GlobalParams("");
2074     
2075     msg("<notice> Adding %s to language pack directories", dir);
2076
2077     int l;
2078     FILE*fi = 0;
2079     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2080     strcpy(config_file, dir);
2081     strcat(config_file, dirseparator());
2082     strcat(config_file, "add-to-xpdfrc");
2083
2084     fi = fopen(config_file, "rb");
2085     if(!fi) {
2086         msg("<error> Could not open %s", config_file);
2087         return;
2088     }
2089     globalParams->parseFile(new GString(config_file), fi);
2090     fclose(fi);
2091 }
2092
2093 void pdfswf_addfontdir(char*dirname)
2094 {
2095 #ifdef HAVE_DIRENT_H
2096     msg("<notice> Adding %s to font directories", dirname);
2097     DIR*dir = opendir(dirname);
2098     if(!dir) {
2099         msg("<warning> Couldn't open directory %s\n", dirname);
2100         return;
2101     }
2102     struct dirent*ent;
2103     while(1) {
2104         ent = readdir (dir);
2105         if (!ent) 
2106             break;
2107         int l;
2108         char*name = ent->d_name;
2109         char type = 0;
2110         if(!name) continue;
2111         l=strlen(name);
2112         if(l<4)
2113             continue;
2114         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2115             type=1;
2116         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2117             type=3;
2118         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2119             type=2;
2120         if(type)
2121         {
2122             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2123             strcpy(fontname, dirname);
2124             strcat(fontname, dirseparator());
2125             strcat(fontname, name);
2126             msg("<verbose> Adding %s to fonts", fontname);
2127             pdfswf_addfont(fontname);
2128         }
2129     }
2130     closedir(dir);
2131 #else
2132     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2133 #endif
2134 }
2135
2136
2137 typedef struct _pdf_doc_internal
2138 {
2139     int protect;
2140     PDFDoc*doc;
2141 } pdf_doc_internal_t;
2142 typedef struct _pdf_page_internal
2143 {
2144 } pdf_page_internal_t;
2145 typedef struct _swf_output_internal
2146 {
2147     SWFOutputDev*outputDev;
2148 } swf_output_internal_t;
2149
2150 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2151 {
2152     pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2153     memset(pdf_doc, 0, sizeof(pdf_doc_t));
2154     pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2155     memset(i, 0, sizeof(pdf_doc_internal_t));
2156     pdf_doc->internal = i;
2157     
2158     GString *fileName = new GString(filename);
2159     GString *userPW;
2160     Object info;
2161
2162     // read config file
2163     if(!globalParams)
2164         globalParams = new GlobalParams("");
2165
2166     // open PDF file
2167     if (userPassword && userPassword[0]) {
2168       userPW = new GString(userPassword);
2169     } else {
2170       userPW = NULL;
2171     }
2172     i->doc = new PDFDoc(fileName, userPW);
2173     if (userPW) {
2174       delete userPW;
2175     }
2176     if (!i->doc->isOk()) {
2177         return 0;
2178     }
2179
2180     // print doc info
2181     i->doc->getDocInfo(&info);
2182     if (info.isDict() &&
2183       (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2184       printInfoString(info.getDict(), "Title",        "Title:        %s\n");
2185       printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
2186       printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
2187       printInfoString(info.getDict(), "Author",       "Author:       %s\n");
2188       printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
2189       printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
2190       printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
2191       printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
2192       printf("Pages:        %d\n", i->doc->getNumPages());
2193       printf("Linearized:   %s\n", i->doc->isLinearized() ? "yes" : "no");
2194       printf("Encrypted:    ");
2195       if (i->doc->isEncrypted()) {
2196         printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2197                i->doc->okToPrint() ? "yes" : "no",
2198                i->doc->okToCopy() ? "yes" : "no",
2199                i->doc->okToChange() ? "yes" : "no",
2200                i->doc->okToAddNotes() ? "yes" : "no");
2201       } else {
2202         printf("no\n");
2203       }
2204     }
2205     info.free();
2206                    
2207     pdf_doc->num_pages = i->doc->getNumPages();
2208     i->protect = 0;
2209     if (i->doc->isEncrypted()) {
2210           if(!i->doc->okToCopy()) {
2211               printf("PDF disallows copying.\n");
2212               return 0;
2213           }
2214           if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2215               i->protect = 1;
2216     }
2217    
2218     return pdf_doc;
2219 }
2220
2221 void pdfswf_preparepage(int page)
2222 {
2223     /*FIXME*/
2224     if(!pages) {
2225         pages = (int*)malloc(1024*sizeof(int));
2226         pagebuflen = 1024;
2227     } else {
2228         if(pagepos == pagebuflen)
2229         {
2230             pagebuflen+=1024;
2231             pages = (int*)realloc(pages, pagebuflen);
2232         }
2233     }
2234     pages[pagepos++] = page;
2235 }
2236
2237 class MemCheck
2238 {
2239     public: ~MemCheck()
2240     {
2241         delete globalParams;globalParams=0;
2242         Object::memCheck(stderr);
2243         gMemReport(stderr);
2244     }
2245 } myMemCheck;
2246
2247 void pdf_destroy(pdf_doc_t*pdf_doc)
2248 {
2249     pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2250
2251     msg("<debug> pdfswf.cc: pdfswf_close()");
2252     delete i->doc; i->doc=0;
2253     
2254     free(pages); pages = 0; //FIXME
2255
2256     free(pdf_doc->internal);pdf_doc->internal=0;
2257     free(pdf_doc);pdf_doc=0;
2258 }
2259
2260 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2261 {
2262     pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2263
2264     if(page < 1 || page > pdf_doc->num_pages)
2265         return 0;
2266     
2267     pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2268     pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2269     memset(pi, 0, sizeof(pdf_page_internal_t));
2270     pdf_page->internal = pi;
2271
2272     pdf_page->parent = pdf_doc;
2273     pdf_page->nr = page;
2274     return pdf_page;
2275 }
2276
2277 void pdf_page_destroy(pdf_page_t*pdf_page)
2278 {
2279     pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2280     free(pdf_page->internal);pdf_page->internal = 0;
2281     free(pdf_page);pdf_page=0;
2282 }
2283
2284 swf_output_t* swf_output_init() 
2285 {
2286     swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2287     memset(swf_output, 0, sizeof(swf_output_t));
2288     swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2289     memset(i, 0, sizeof(swf_output_internal_t));
2290     swf_output->internal = i;
2291
2292     i->outputDev = new SWFOutputDev();
2293     return swf_output;
2294 }
2295
2296 void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
2297 {
2298     /* FIXME */
2299     pdfswf_setparameter(name, value);
2300 }
2301
2302 void swf_output_pagefeed(swf_output_t*swf)
2303 {
2304     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2305     i->outputDev->pagefeed();
2306     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2307 }
2308
2309 int swf_output_save(swf_output_t*swf, char*filename)
2310 {
2311     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2312     int ret = i->outputDev->save(filename);
2313     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2314     return ret;
2315 }
2316
2317 void* swf_output_get(swf_output_t*swf)
2318 {
2319     swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2320     void* ret = i->outputDev->getSWF();
2321     i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2322     return ret;
2323 }
2324
2325 void swf_output_destroy(swf_output_t*output)
2326 {
2327     swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2328     delete i->outputDev; i->outputDev=0;
2329     free(output->internal);output->internal=0;
2330     free(output);
2331 }
2332
2333 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2334 {
2335     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2336     swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2337
2338     if(pi->protect) {
2339         swfoutput_setparameter("protect", "1");
2340     }
2341     si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2342 #ifdef XPDF_101
2343     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2344 #else
2345     pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2346 #endif
2347     si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2348 }
2349
2350 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2351 {
2352     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2353     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2354
2355     si->outputDev->setMove(x,y);
2356     if((x1|y1|x2|y2)==0) x2++;
2357     si->outputDev->setClip(x1,y1,x2,y2);
2358
2359     pdf_page_render2(page, output);
2360 }
2361 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2362 {
2363     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2364     swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2365     
2366     si->outputDev->setMove(0,0);
2367     si->outputDev->setClip(0,0,0,0);
2368     
2369     pdf_page_render2(page, output);
2370 }
2371
2372
2373 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2374 {
2375     pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2376     pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2377     pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2378     memset(info, 0, sizeof(pdf_page_info_t));
2379
2380     InfoOutputDev*output = new InfoOutputDev;
2381     
2382 #ifdef XPDF_101
2383     pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2384 #else
2385     pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2386 #endif
2387
2388     info->xMin = output->x1;
2389     info->yMin = output->y1;
2390     info->xMax = output->x2;
2391     info->yMax = output->y2;
2392     info->number_of_images = output->num_images;
2393     info->number_of_links = output->num_links;
2394     info->number_of_fonts = output->num_fonts;
2395
2396     delete output;
2397
2398     return info;
2399 }
2400
2401 void pdf_page_info_destroy(pdf_page_info_t*info)
2402 {
2403     free(info);
2404 }