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