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