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