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