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