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