Initial revision
[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 //xpdf header files
25 #include "GString.h"
26 #include "gmem.h"
27 #include "Object.h"
28 #include "Stream.h"
29 #include "Array.h"
30 #include "Dict.h"
31 #include "XRef.h"
32 #include "Catalog.h"
33 #include "Page.h"
34 #include "PDFDoc.h"
35 #include "Params.h"
36 #include "Error.h"
37 #include "config.h"
38 #include "OutputDev.h"
39 #include "GfxState.h"
40 #include "GfxFont.h"
41 #include "FontFile.h"
42 //swftools header files
43 #include "swfoutput.h"
44 extern "C" {
45 #include "../lib/log.h"
46 }
47
48 static char* filename = 0;
49
50 static void printInfoString(Dict *infoDict, char *key, char *fmt);
51 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
52
53 static char userPassword[33] = "";
54 static GBool printVersion = gFalse;
55 static GBool printHelp = gFalse;
56
57 double fontsizes[] = 
58 {
59  0.833,0.833,0.889,0.889,0.788,0.722,0.833,0.778,0.600,0.600,0.600,0.600,0.576,0.576,0.576,0.576
60 };
61 char*fontnames[]={
62 "Helvetica",             
63 "Helvetica-Bold",        
64 "Helvetica-BoldOblique", 
65 "Helvetica-Oblique",     
66 "Times-Roman",           
67 "Times-Bold",            
68 "Times-BoldItalic",      
69 "Times-Italic",          
70 "Courier",               
71 "Courier-Bold",          
72 "Courier-BoldOblique",   
73 "Courier-Oblique",       
74 "Symbol",                
75 "Symbol",                
76 "Symbol",                
77 "Symbol",
78 "ZapfDingBats"
79 };
80
81 struct mapping {
82     char*pdffont;
83     char*filename;
84     int id;
85 } pdf2t1map[] ={
86 {"Times-Roman",           "n021003l.pfb"},
87 {"Times-Italic",          "n021023l.pfb"},
88 {"Times-Bold",            "n021004l.pfb"},
89 {"Times-BoldItalic",      "n021024l.pfb"},
90 {"Helvetica",             "n019003l.pfb"},
91 {"Helvetica-Oblique",     "n019023l.pfb"},
92 {"Helvetica-Bold",        "n019004l.pfb"},
93 {"Helvetica-BoldOblique", "n019024l.pfb"},
94 {"Courier",               "n022003l.pfb"},
95 {"Courier-Oblique",       "n022023l.pfb"},
96 {"Courier-Bold",          "n022004l.pfb"},
97 {"Courier-BoldOblique",   "n022024l.pfb"},
98 {"Symbol",                "s050000l.pfb"},
99 {"ZapfDingbats",          "d050000l.pfb"}};
100
101 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
102   Object obj;
103   GString *s1, *s2;
104   int i;
105
106   if (infoDict->lookup(key, &obj)->isString()) {
107     s1 = obj.getString();
108     if ((s1->getChar(0) & 0xff) == 0xfe &&
109         (s1->getChar(1) & 0xff) == 0xff) {
110       s2 = new GString();
111       for (i = 2; i < obj.getString()->getLength(); i += 2) {
112         if (s1->getChar(i) == '\0') {
113           s2->append(s1->getChar(i+1));
114         } else {
115           delete s2;
116           s2 = new GString("<unicode>");
117           break;
118         }
119       }
120       printf(fmt, s2->getCString());
121       delete s2;
122     } else {
123       printf(fmt, s1->getCString());
124     }
125   }
126   obj.free();
127 }
128
129 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
130   Object obj;
131   char *s;
132
133   if (infoDict->lookup(key, &obj)->isString()) {
134     s = obj.getString()->getCString();
135     if (s[0] == 'D' && s[1] == ':') {
136       s += 2;
137     }
138     printf(fmt, s);
139   }
140   obj.free();
141 }
142
143 class GfxState;
144 class GfxImageColorMap;
145
146 class SWFOutputDev:  public OutputDev {
147   struct swfoutput output;
148   int outputstarted;
149 public:
150
151   // Constructor.
152   SWFOutputDev();
153
154   // Destructor.
155   virtual ~SWFOutputDev() ;
156
157   //----- get info about output device
158
159   // Does this device use upside-down coordinates?
160   // (Upside-down means (0,0) is the top left corner of the page.)
161   virtual GBool upsideDown();
162
163   // Does this device use drawChar() or drawString()?
164   virtual GBool useDrawChar();
165
166   //----- initialization and control
167
168   // Start a page.
169   virtual void startPage(int pageNum, GfxState *state) ;
170
171   //----- link borders
172   virtual void drawLink(Link *link, Catalog *catalog) ;
173
174   //----- save/restore graphics state
175   virtual void saveState(GfxState *state) ;
176   virtual void restoreState(GfxState *state) ;
177
178   //----- update graphics state
179
180   virtual void updateFont(GfxState *state);
181   virtual void updateFillColor(GfxState *state);
182   virtual void updateStrokeColor(GfxState *state);
183   virtual void updateLineWidth(GfxState *state);
184   
185   virtual void updateAll(GfxState *state) 
186   {
187       updateFont(state);
188       updateFillColor(state);
189       updateStrokeColor(state);
190       updateLineWidth(state);
191   };
192
193   //----- path painting
194   virtual void stroke(GfxState *state) ;
195   virtual void fill(GfxState *state) ;
196   virtual void eoFill(GfxState *state) ;
197
198   //----- path clipping
199   virtual void clip(GfxState *state) ;
200   virtual void eoClip(GfxState *state) ;
201
202   //----- text drawing
203   virtual void beginString(GfxState *state, GString *s) ;
204   virtual void endString(GfxState *state) ;
205   virtual void drawChar(GfxState *state, double x, double y,
206                         double dx, double dy, Guchar c) ;
207   virtual void drawChar16(GfxState *state, double x, double y,
208                           double dx, double dy, int c) ;
209
210   //----- image drawing
211   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
212                              int width, int height, GBool invert,
213                              GBool inlineImg);
214   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
215                          int width, int height, GfxImageColorMap *colorMap,
216                          GBool inlineImg);
217
218   private:
219   int clipping[32];
220   int clippos;
221
222   int setT1Font(char*name,FontEncoding*enc);
223   int initT1Font(int id, FontEncoding*encoding);
224   int t1id;
225 };
226
227 char mybuf[1024];
228 char* gfxstate2str(GfxState *state)
229 {
230   char*bufpos = mybuf;
231   GfxRGB rgb;
232   bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
233                                     state->getCTM()[0],
234                                     state->getCTM()[1],
235                                     state->getCTM()[2],
236                                     state->getCTM()[3],
237                                     state->getCTM()[4],
238                                     state->getCTM()[5]);
239   if(state->getX1()!=0.0)
240   bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
241   if(state->getY1()!=0.0)
242   bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
243   bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
244   bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
245   bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
246   bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
247   /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
248           state->getFillColor()->c[0], state->getFillColor()->c[1]);
249   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
250           state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
251 /*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
252           state->getFillColor()->c[0], state->getFillColor()->c[1],
253           state->getFillColor()->c[2], state->getFillColor()->c[3],
254           state->getFillColor()->c[4], state->getFillColor()->c[5],
255           state->getFillColor()->c[6], state->getFillColor()->c[7]);
256   bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
257           state->getStrokeColor()->c[0], state->getFillColor()->c[1],
258           state->getStrokeColor()->c[2], state->getFillColor()->c[3],
259           state->getStrokeColor()->c[4], state->getFillColor()->c[5],
260           state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
261   state->getFillRGB(&rgb);
262   if(rgb.r || rgb.g || rgb.b)
263   bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
264   state->getStrokeRGB(&rgb);
265   if(rgb.r || rgb.g || rgb.b)
266   bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
267   if(state->getFillColorSpace()->getNComps()>1)
268   bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
269   if(state->getStrokeColorSpace()->getNComps()>1)
270   bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
271   if(state->getFillPattern())
272   bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
273   if(state->getStrokePattern())
274   bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
275  
276   if(state->getFillOpacity()!=1.0)
277   bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
278   if(state->getStrokeOpacity()!=1.0)
279   bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
280
281   bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
282  
283   double * dash;
284   int length;
285   double start;
286   state->getLineDash(&dash, &length, &start);
287   int t;
288   if(length)
289   {
290       bufpos+=sprintf(bufpos,"DASH%.1f[",start);
291       for(t=0;t<length;t++) {
292           bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
293       }
294       bufpos+=sprintf(bufpos,"]");
295   }
296
297   if(state->getFlatness()!=1)
298   bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
299   if(state->getLineJoin()!=0)
300   bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
301   if(state->getLineJoin()!=0)
302   bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
303   if(state->getLineJoin()!=0)
304   bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
305
306   if(state->getFont() && state->getFont()->getName() && state->getFont()->getName()->getCString())
307   bufpos+=sprintf(bufpos,"F\"%s\" ",((state->getFont())->getName())->getCString());
308   bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
309   bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
310                                    state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
311   if(state->getCharSpace())
312   bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
313   if(state->getWordSpace())
314   bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
315   if(state->getHorizScaling()!=1.0)
316   bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
317   if(state->getLeading())
318   bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
319   if(state->getRise())
320   bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
321   if(state->getRender())
322   bufpos+=sprintf(bufpos,"R%d ", state->getRender());
323   bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
324   bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
325   bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
326   if(state->getLineX())
327   bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
328   if(state->getLineY())
329   bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
330   bufpos+=sprintf(bufpos," ");
331   return mybuf;
332 }
333
334 void dumpFontInfo(char*loglevel, GfxFont*font);
335 int lastdumps[1024];
336 int lastdumppos = 0;
337 /* nr = 0  unknown
338    nr = 1  substituting
339    nr = 2  type 3
340  */
341 void showFontError(GfxFont*font, int nr) 
342 {  
343     Ref r=font->getID();
344     int t;
345     for(t=0;t<lastdumppos;t++)
346         if(lastdumps[t] == r.num)
347             break;
348     if(t < lastdumppos)
349       return;
350     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
351     lastdumps[lastdumppos++] = r.num;
352     if(nr == 0)
353       logf("<error> The following font caused problems:");
354     else if(nr == 1)
355       logf("<error> The following font caused problems (substituting):");
356     else if(nr == 2)
357       logf("<error> This document contains Type 3 Fonts: (some text may be incorrectly displayed)");
358
359     dumpFontInfo("<error>", font);
360 }
361
362 void dumpFontInfo(char*loglevel, GfxFont*font)
363 {
364   GString *gstr;
365   char*name;
366   gstr = font->getName();
367   Ref r=font->getID();
368   logf("%s=========== %s (ID:%d) ==========\n", loglevel, gstr?gstr->getCString():"(unknown font)", r.num);
369
370   gstr  = font->getTag();
371   if(gstr) 
372    logf("%sTag: %s\n", loglevel, gstr->getCString());
373   if(font->is16Bit()) logf("%sis 16 bit\n", loglevel);
374
375   GfxFontType type=font->getType();
376   switch(type) {
377     case fontUnknownType:
378      logf("%sType: unknown\n",loglevel);
379     break;
380     case fontType0:
381      logf("%sType: 0\n",loglevel);
382     break;
383     case fontType1:
384      logf("%sType: 1\n",loglevel);
385     break;
386     case fontType1C:
387      logf("%sType: 1C\n",loglevel);
388     break;
389     case fontType3:
390      logf("%sType: 3\n",loglevel);
391     break;
392     case fontTrueType:
393      logf("%sType: TrueType\n",loglevel);
394     break;
395   }
396   name = font->getEmbeddedFontName();
397   if(name)
398    logf("%sEmbedded name: %s\n",loglevel, name);
399
400   gstr = font->getExtFontFile();
401   if(gstr)
402    logf("%sExternal Font file: %s\n", loglevel, gstr->getCString());
403
404   // Get font descriptor flags.
405   if(font->isFixedWidth()) logf("%sis fixed width\n", loglevel);
406   if(font->isSerif()) logf("%sis serif\n", loglevel);
407   if(font->isSymbolic()) logf("%sis symbolic\n", loglevel);
408   if(font->isItalic()) logf("%sis italic\n", loglevel);
409   if(font->isBold()) logf("%sis bold\n", loglevel);
410 }
411
412 //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");}
413 //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");}
414
415 SWFOutputDev::SWFOutputDev() 
416 {
417     clippos = 0;
418     clipping[clippos] = 0;
419     outputstarted = 0;
420 //    printf("SWFOutputDev::SWFOutputDev() \n");
421 };
422
423 T1_OUTLINE* gfxPath_to_T1_OUTLINE(GfxState*state, GfxPath*path)
424 {
425     int num = path->getNumSubpaths();
426     int s,t;
427     bezierpathsegment*start,*last;
428     bezierpathsegment*outline = start = new bezierpathsegment();
429     int cpos = 0;
430     double lastx=0,lasty=0;
431     for(t = 0; t < num; t++) {
432         GfxSubpath *subpath = path->getSubpath(t);
433         int subnum = subpath->getNumPoints();
434
435         for(s=0;s<subnum;s++) {
436            double nx,ny;
437            state->transform(subpath->getX(s),subpath->getY(s),&nx,&ny);
438            int x = (int)((nx-lastx)*0xffff);
439            int y = (int)((ny-lasty)*0xffff);
440            if(s==0) 
441            {
442                 last = outline;
443                 outline->type = T1_PATHTYPE_MOVE;
444                 outline->dest.x = x;
445                 outline->dest.y = y;
446                 outline->link = (T1_OUTLINE*)new bezierpathsegment();
447                 outline = (bezierpathsegment*)outline->link;
448                 cpos = 0;
449                 lastx = nx;
450                 lasty = ny;
451            }
452            else if(subpath->getCurve(s) && !cpos)
453            {
454                 outline->B.x = x;
455                 outline->B.y = y;
456                 cpos = 1;
457            } 
458            else if(subpath->getCurve(s) && cpos)
459            {
460                 outline->C.x = x;
461                 outline->C.y = y;
462                 cpos = 2;
463            }
464            else
465            {
466                 last = outline;
467                 outline->dest.x = x;
468                 outline->dest.y = y;
469                 outline->type = cpos?T1_PATHTYPE_BEZIER:T1_PATHTYPE_LINE;
470                 outline->link = 0;
471                 outline->link = (T1_OUTLINE*)new bezierpathsegment();
472                 outline = (bezierpathsegment*)outline->link;
473                 cpos = 0;
474                 lastx = nx;
475                 lasty = ny;
476            }
477         }
478     }
479     last->link = 0;
480     return (T1_OUTLINE*)start;
481 }
482 /*----------------------------------------------------------------------------
483  * Primitive Graphic routines
484  *----------------------------------------------------------------------------*/
485
486 void SWFOutputDev::stroke(GfxState *state) 
487 {
488     logf("<debug> %s stroke\n",gfxstate2str(state));
489     GfxPath * path = state->getPath();
490     struct swfmatrix m;
491     m.m11 = 1; m.m21 = 0; m.m22 = 1;
492     m.m21 = 0; m.m13 = 0; m.m23 = 0;
493     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
494     swfoutput_setdrawmode(&output, DRAWMODE_STROKE);
495     swfoutput_drawpath(&output, outline, &m);
496 }
497 void SWFOutputDev::fill(GfxState *state) 
498 {
499     logf("<debug> %s fill\n",gfxstate2str(state));
500     GfxPath * path = state->getPath();
501     struct swfmatrix m;
502     m.m11 = 1; m.m21 = 0; m.m22 = 1;
503     m.m21 = 0; m.m13 = 0; m.m23 = 0;
504     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
505     swfoutput_setdrawmode(&output, DRAWMODE_FILL);
506     swfoutput_drawpath(&output, outline, &m);
507 }
508 void SWFOutputDev::eoFill(GfxState *state) 
509 {
510     logf("<debug> %s eofill\n",gfxstate2str(state));
511     GfxPath * path = state->getPath();
512     struct swfmatrix m;
513     m.m11 = 1; m.m21 = 0; m.m22 = 1;
514     m.m21 = 0; m.m13 = 0; m.m23 = 0;
515     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
516     swfoutput_setdrawmode(&output, DRAWMODE_EOFILL);
517     swfoutput_drawpath(&output, outline, &m);
518 }
519 void SWFOutputDev::clip(GfxState *state) 
520 {
521     logf("<debug> %s clip\n",gfxstate2str(state));
522     GfxPath * path = state->getPath();
523     struct swfmatrix m;
524     m.m11 = 1; m.m21 = 0; m.m22 = 1;
525     m.m21 = 0; 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] = 1;
529 }
530 void SWFOutputDev::eoClip(GfxState *state) 
531 {
532     logf("<debug> %s eoclip\n",gfxstate2str(state));
533     GfxPath * path = state->getPath();
534     struct swfmatrix m;
535     m.m11 = 1; m.m21 = 0; m.m22 = 1;
536     m.m21 = 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] = 1;
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,-m12,m21,-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> %s drawChar(%f,%f,%f,%f,'%c')\n",gfxstate2str(state), x,y,dx,dy,c);
572     // check for invisible text -- this is used by Acrobat Capture
573     if ((state->getRender() & 3) != 3)
574     {
575        FontEncoding*enc=state->getFont()->getEncoding();
576
577        double x1,y1;
578        x1 = x;
579        y1 = y;
580        state->transform(x, y, &x1, &y1);
581
582        swfoutput_drawchar(&output, x1, y1, c);
583     }
584 }
585
586 void SWFOutputDev::drawChar16(GfxState *state, double x, double y, double dx, double dy, int c) 
587 {
588     printf("<error> %s drawChar16(%f,%f,%f,%f,%08x)\n",gfxstate2str(state), x,y,dx,dy,c);
589     exit(1);
590 }
591
592 void SWFOutputDev::endString(GfxState *state) 
593
594     logf("<debug> %s endstring\n", gfxstate2str(state));
595 }    
596
597 void SWFOutputDev::startPage(int pageNum, GfxState *state) 
598 {
599   double x1,y1,x2,y2;
600   logf("<debug> %s, startPage %d\n", gfxstate2str(state), pageNum);
601   logf("<notice> processing page %d", pageNum);
602
603   state->transform(state->getX1(),state->getY1(),&x1,&y1);
604   state->transform(state->getX2(),state->getY2(),&x2,&y2);
605   if(!outputstarted) {
606     swfoutput_init(&output, filename, abs((int)(x2-x1)),abs((int)(y2-y1)));
607     outputstarted = 1;
608   }
609   else
610     swfoutput_newpage(&output);
611 }
612
613 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
614 {
615   logf("<debug> drawlink\n");
616   double x1, y1, x2, y2, w;
617   GfxRGB rgb;
618   swfcoord points[5];
619   int x, y;
620
621   link->getBorder(&x1, &y1, &x2, &y2, &w);
622   if (w > 0) {
623     rgb.r = 0;
624     rgb.g = 0;
625     rgb.b = 1;
626     cvtUserToDev(x1, y1, &x, &y);
627     points[0].x = points[4].x = x;
628     points[0].y = points[4].y = y;
629     cvtUserToDev(x2, y1, &x, &y);
630     points[1].x = x;
631     points[1].y = y;
632     cvtUserToDev(x2, y2, &x, &y);
633     points[2].x = x;
634     points[2].y = y;
635     cvtUserToDev(x1, y2, &x, &y);
636     points[3].x = x;
637     points[3].y = y;
638     //PDF: draw rect
639     LinkAction*action=link->getAction();
640     char*s;
641     switch(action->getKind())
642     {
643         case actionGoTo: {
644             LinkGoTo*l = (LinkGoTo*)action;
645             s = l->getNamedDest()->getCString();
646         }
647         break;
648         case actionGoToR: {
649             LinkGoToR*l = (LinkGoToR*)action;
650             s = l->getNamedDest()->getCString();
651         }
652         break;
653         case actionLaunch: {
654             LinkLaunch*l = (LinkLaunch*)action;
655             GString * str = new GString(l->getFileName());
656             str->append(l->getParams());
657             s = str->getCString();
658         }
659         break;
660         case actionURI: {
661             LinkURI*l = (LinkURI*)action;
662             s = l->getURI()->getCString();
663         }
664         break;
665         case actionNamed: {
666             LinkNamed*l = (LinkNamed*)action;
667             s = l->getName()->getCString();
668         }
669         break;
670         case actionUnknown: {
671             LinkUnknown*l = (LinkUnknown*)action;
672             s = "";
673         }
674         break;
675     }
676     logf("<verbose> link to \"%s\"\n", s);
677   }
678 }
679
680 void SWFOutputDev::saveState(GfxState *state) {
681   logf("<debug> %s saveState\n", gfxstate2str(state));
682   updateAll(state);
683   clippos ++;
684   clipping[clippos] = 0;
685 };
686
687 void SWFOutputDev::restoreState(GfxState *state) {
688   logf("<debug> %s restoreState\n", gfxstate2str(state));
689   updateAll(state);
690   if(clipping[clippos])
691       swfoutput_endclip(&output);
692   clippos--;
693 }
694
695 char type3Warning=0;
696
697 int SWFOutputDev::setT1Font(char*name, FontEncoding*encoding) 
698 {       
699     int i;
700     
701     int id=-1;
702     int mapid=-1;
703     char*filename=0;
704     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
705     {
706         if(!strcmp(name, pdf2t1map[i].pdffont))
707         {
708             filename = pdf2t1map[i].filename;
709             mapid = i;
710         }
711     }
712     if(filename)
713     for(i=0; i<T1_Get_no_fonts(); i++)
714     {
715         char*fontfilename = T1_GetFontFileName (i);
716         if(strstr(fontfilename, filename))
717         {
718                 id = i;
719                 pdf2t1map[i].id = mapid;
720         }
721     }
722     if(id<0)
723      return 0;
724
725     initT1Font(id, encoding);
726 }
727
728 int SWFOutputDev::initT1Font(int id, FontEncoding*encoding)
729 {
730     int encStrSize;
731     char *encPtr;
732     int i;
733     T1_DeleteFont(id);
734     T1_LoadFont(id);
735     /* reencode the font: 
736      * This is the only way to get the unmapped characters
737      * from t1lib
738      */
739     encStrSize = 0;
740     for (i = 0; i < 256 && i < encoding->getSize(); ++i) {
741       if (encoding->getCharName(i)) {
742         encStrSize += strlen(encoding->getCharName(i)) + 1;
743       }
744     }
745     char**enc = (char **)gmalloc(257 * sizeof(char *));
746     char*encStr = (char *)gmalloc(encStrSize * sizeof(char));
747     encPtr = encStr;
748     for (i = 0; i < 256 && i < encoding->getSize(); ++i) {
749       if (encoding->getCharName(i)) {
750         strcpy(encPtr, encoding->getCharName(i));
751         enc[i] = encPtr;
752         encPtr += strlen(encPtr) + 1;
753       } else {
754         enc[i] = ".notdef";
755       }
756     }
757     for (; i < 256; ++i) {
758       enc[i] = ".notdef";
759     }
760     enc[256] = "custom";
761     int ret=T1_ReencodeFont(id, enc);
762     t1id = id;
763     return 1;
764 }
765
766 void SWFOutputDev::updateLineWidth(GfxState *state)
767 {
768     double width = state->getLineWidth();
769     swfoutput_setlinewidth(&output, width);
770 }
771
772 void SWFOutputDev::updateFillColor(GfxState *state) 
773 {
774     GfxRGB rgb;
775     double opaq = state->getFillOpacity();
776     state->getFillRGB(&rgb);
777
778     swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
779                                     (char)(rgb.b*255), (char)(opaq*255));
780 }
781
782 void SWFOutputDev::updateStrokeColor(GfxState *state) 
783 {
784     GfxRGB rgb;
785     double opaq = state->getStrokeOpacity();
786     state->getStrokeRGB(&rgb);
787
788     swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
789                                       (char)(rgb.b*255), (char)(opaq*255));
790 }
791
792 void SWFOutputDev::updateFont(GfxState *state) {
793   double m11, m12, m21, m22;
794   char * fontname = 0;
795   GfxFont*gfxFont = state->getFont();
796   char * filename;
797
798   if (!gfxFont) {
799     return;
800   }  
801   // look for Type 3 font
802   if (!type3Warning && gfxFont->getType() == fontType3) {
803     type3Warning = gTrue;
804     showFontError(gfxFont, 2);
805   }
806   //dumpFontInfo (gfxFont);
807   
808
809   Ref embRef;
810   GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
811   if(embedded) {
812     char*tmpFileName = NULL;
813     char*fileName = NULL;
814     FILE *f;
815     char *fontBuf;
816     int fontLen;
817     Type1CFontConverter *cvt;
818     Ref embRef;
819     Object refObj, strObj;
820     int c;
821     if (!gfxFont->is16Bit() &&
822         (gfxFont->getType() == fontType1 ||
823          gfxFont->getType() == fontType1C) &&
824         gfxFont->getEmbeddedFontID(&embRef)) {
825       tmpFileName = "tmpfont";
826       f = fopen(tmpFileName, "wb");
827       if (!f) {
828         logf("<error> Couldn't create temporary Type 1 font file");
829         return;
830       }
831       if (gfxFont->getType() == fontType1C) {
832         if (!(fontBuf = gfxFont->readEmbFontFile(&fontLen))) {
833           fclose(f);
834           logf("<error> Couldn't read embedded font file");
835           return ;
836         }
837         cvt = new Type1CFontConverter(fontBuf, fontLen, f);
838         cvt->convert();
839         delete cvt;
840         gfree(fontBuf);
841       } else {
842         gfxFont->getEmbeddedFontID(&embRef);
843         refObj.initRef(embRef.num, embRef.gen);
844         refObj.fetch(&strObj);
845         refObj.free();
846         strObj.streamReset();
847         while ((c = strObj.streamGetChar()) != EOF) {
848           fputc(c, f);
849         }
850         strObj.streamClose();
851         strObj.free();
852       }
853       fclose(f);
854       fileName = tmpFileName;
855       if(!fileName) {
856           logf("<error> Embedded font writer didn't create a file");
857           return ;
858       }
859     }
860     else {
861         showFontError(gfxFont,0);
862         return ;
863     }
864     t1id = T1_AddFont(fileName);
865     initT1Font(t1id, gfxFont->getEncoding());
866   } else {
867     fontname = NULL;
868     if(gfxFont->getName()) {
869       fontname = gfxFont->getName()->getCString();
870       //logf("<notice> Processing font %s", fontname);
871     }
872     if(!fontname || !setT1Font(state->getFont()->getName()->getCString(), gfxFont->getEncoding()))
873     { //substitute font
874       int index;
875       int code;
876       double w,w1,w2;
877       double*fm;
878       double v;
879       showFontError(gfxFont, 1);
880       if (!gfxFont->is16Bit()) {
881         if (gfxFont->isFixedWidth()) {
882           index = 8;
883         } else if (gfxFont->isSerif()) {
884           index = 4;
885         } else {
886           index = 0;
887         }
888         if (gfxFont->isBold())
889           index += 2;
890         if (gfxFont->isItalic())
891           index += 1;
892         fontname = fontnames[index];
893         // get width of 'm' in real font and substituted font
894         if ((code = gfxFont->getCharCode("m")) >= 0)
895           w1 = gfxFont->getWidth(code);
896         else
897           w1 = 0;
898         w2 = fontsizes[index];
899         if (gfxFont->getType() == fontType3) {
900           // This is a hack which makes it possible to substitute for some
901           // Type 3 fonts.  The problem is that it's impossible to know what
902           // the base coordinate system used in the font is without actually
903           // rendering the font.  This code tries to guess by looking at the
904           // width of the character 'm' (which breaks if the font is a
905           // subset that doesn't contain 'm').
906           if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
907             w1 /= w2;
908             m11 *= w1;
909             m12 *= w1;
910             m21 *= w1;
911             m22 *= w1;
912           }
913           fm = gfxFont->getFontMatrix();
914           v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
915           m21 *= v;
916           m22 *= v;
917         } else if (!gfxFont->isSymbolic()) {
918           // if real font is substantially narrower than substituted
919           // font, reduce the font size accordingly
920           if (w1 > 0.01 && w1 < 0.9 * w2) {
921             w1 /= w2;
922             if (w1 < 0.8) {
923               w1 = 0.8;
924             }
925             m11 *= w1;
926             m12 *= w1;
927             m21 *= w1;
928             m22 *= w1;
929           }
930         }
931       }
932       if(fontname)
933         setT1Font(fontname, gfxFont->getEncoding());
934     }
935   }
936
937   swfoutput_setfont(&output,t1id);
938 }
939
940 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
941                                    int width, int height, GBool invert,
942                                    GBool inlineImg) {
943   FILE *fi;
944   int c;
945   char fileName[128];
946   double x1,y1,x2,y2,x3,y3,x4,y4;
947   state->transform(0, 1, &x1, &y1);
948   state->transform(0, 0, &x2, &y2);
949   state->transform(1, 0, &x3, &y3);
950   state->transform(1, 1, &x4, &y4);
951
952   if (str->getKind() == strDCT) {
953     sprintf(fileName, "/tmp/tmp%08x.jpg",lrand48());
954     logf("<notice> Found picture. Temporary storage is %s", fileName);
955     if (!(fi = fopen(fileName, "wb"))) {
956       logf("<error> Couldn't open temporary image file '%s'", fileName);
957       return;
958     }
959     str = ((DCTStream *)str)->getRawStream();
960     str->reset();
961     while ((c = str->getChar()) != EOF)
962       fputc(c, fi);
963     fclose(fi);
964     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
965   } else {
966       logf("<notice> File contains pbm pictures.");
967   }
968 }
969
970 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
971                                int width, int height,
972                                GfxImageColorMap *colorMap, GBool inlineImg) {
973   FILE *fi;
974   int c;
975   char fileName[128];
976   double x1,y1,x2,y2,x3,y3,x4,y4;
977   state->transform(0, 1, &x1, &y1);
978   state->transform(0, 0, &x2, &y2);
979   state->transform(1, 0, &x3, &y3);
980   state->transform(1, 1, &x4, &y4);
981
982   if (str->getKind() == strDCT &&
983       colorMap->getNumPixelComps() == 3) {
984     sprintf(fileName, "/tmp/tmp%08x.jpg", lrand48());
985     logf("<notice> Found picture. Temporary storage is %s", fileName);
986     if (!(fi = fopen(fileName, "wb"))) {
987       error(-1, "Couldn't open temporary image file '%s'", fileName);
988       return;
989     }
990     str = ((DCTStream *)str)->getRawStream();
991     str->reset();
992     while ((c = str->getChar()) != EOF)
993       fputc(c, fi);
994     fclose(fi);
995     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
996   } else {
997       logf("<notice> File contains pbm pictures.");
998   }
999 }
1000
1001 PDFDoc*doc = 0;
1002 SWFOutputDev*output = 0; 
1003
1004 void pdfswf_init(char*filename, char*userPassword) 
1005 {
1006   GString *fileName = new GString(filename);
1007   GString *userPW;
1008   Object info;
1009   // init error file
1010   errorInit();
1011
1012   // read config file
1013   initParams(xpdfConfigFile);
1014
1015   // open PDF file
1016   xref = NULL;
1017   if (userPassword && userPassword[0]) {
1018     userPW = new GString(userPassword);
1019   } else {
1020     userPW = NULL;
1021   }
1022   doc = new PDFDoc(fileName, userPW);
1023   if (userPW) {
1024     delete userPW;
1025   }
1026   if (!doc->isOk()) {
1027     exit(1);
1028   }
1029
1030   // print doc info
1031   doc->getDocInfo(&info);
1032   if (info.isDict()) {
1033     printInfoString(info.getDict(), "Title",        "Title:        %s\n");
1034     printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
1035     printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
1036     printInfoString(info.getDict(), "Author",       "Author:       %s\n");
1037     printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
1038     printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
1039     printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
1040     printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
1041   }
1042   info.free();
1043
1044   // print page count
1045   printf("Pages:        %d\n", doc->getNumPages());
1046   
1047   // print linearization info
1048   printf("Linearized:   %s\n", doc->isLinearized() ? "yes" : "no");
1049
1050   // print encryption info
1051   printf("Encrypted:    ");
1052   if (doc->isEncrypted()) {
1053     printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
1054            doc->okToPrint() ? "yes" : "no",
1055            doc->okToCopy() ? "yes" : "no",
1056            doc->okToChange() ? "yes" : "no",
1057            doc->okToAddNotes() ? "yes" : "no");
1058         /*ERROR: This pdf is encrypted, and disallows copying.
1059           Due to the DMCA, paragraph 1201, (2) A-C, circumventing
1060           a technological measure that efficively controls access to
1061           a protected work is violating American law. 
1062           See www.eff.org for more information about DMCA issues.
1063          */
1064         if(!doc->okToCopy()) {
1065             printf("PDF disallows copying. Bailing out.\n");
1066             exit(1); //bail out
1067         }
1068         if(!doc->okToChange() || !doc->okToAddNotes())
1069             swfoutput_setprotected();
1070     }
1071   else {
1072     printf("no\n");
1073   }
1074
1075
1076   output = new SWFOutputDev();
1077 }
1078
1079 void pdfswf_setoutputfilename(char*_filename)
1080 {
1081     filename = _filename;
1082 }
1083
1084 void pdfswf_convertpage(int page)
1085 {
1086     doc->displayPage((OutputDev*)output, page, /*zoom*/100, /*rotate*/0, /*doLinks*/(int)1);
1087 }
1088
1089 int pdfswf_numpages()
1090 {
1091   return doc->getNumPages();
1092 }
1093
1094 void pdfswf_close()
1095 {
1096     delete doc;
1097     delete output;
1098     
1099     freeParams();
1100     // check for memory leaks
1101     Object::memCheck(stderr);
1102     gMemReport(stderr);
1103 }
1104