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