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