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