implemented two levels of font cache
[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   
397   Ref embRef;
398   GBool embedded = font->getEmbeddedFontID(&embRef);
399   name = font->getEmbeddedFontName();
400   if(embedded)
401    logf("%sEmbedded name: %s id: %d\n",loglevel, name, embRef.num);
402
403   gstr = font->getExtFontFile();
404   if(gstr)
405    logf("%sExternal Font file: %s\n", loglevel, gstr->getCString());
406
407   // Get font descriptor flags.
408   if(font->isFixedWidth()) logf("%sis fixed width\n", loglevel);
409   if(font->isSerif()) logf("%sis serif\n", loglevel);
410   if(font->isSymbolic()) logf("%sis symbolic\n", loglevel);
411   if(font->isItalic()) logf("%sis italic\n", loglevel);
412   if(font->isBold()) logf("%sis bold\n", loglevel);
413 }
414
415 //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");}
416 //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");}
417
418 SWFOutputDev::SWFOutputDev() 
419 {
420     clippos = 0;
421     clipping[clippos] = 0;
422     outputstarted = 0;
423 //    printf("SWFOutputDev::SWFOutputDev() \n");
424 };
425
426 T1_OUTLINE* gfxPath_to_T1_OUTLINE(GfxState*state, GfxPath*path)
427 {
428     int num = path->getNumSubpaths();
429     int s,t;
430     bezierpathsegment*start,*last;
431     bezierpathsegment*outline = start = new bezierpathsegment();
432     int cpos = 0;
433     double lastx=0,lasty=0;
434     for(t = 0; t < num; t++) {
435         GfxSubpath *subpath = path->getSubpath(t);
436         int subnum = subpath->getNumPoints();
437
438         for(s=0;s<subnum;s++) {
439            double nx,ny;
440            state->transform(subpath->getX(s),subpath->getY(s),&nx,&ny);
441            int x = (int)((nx-lastx)*0xffff);
442            int y = (int)((ny-lasty)*0xffff);
443            if(s==0) 
444            {
445                 last = outline;
446                 outline->type = T1_PATHTYPE_MOVE;
447                 outline->dest.x = x;
448                 outline->dest.y = y;
449                 outline->link = (T1_OUTLINE*)new bezierpathsegment();
450                 outline = (bezierpathsegment*)outline->link;
451                 cpos = 0;
452                 lastx = nx;
453                 lasty = ny;
454            }
455            else if(subpath->getCurve(s) && !cpos)
456            {
457                 outline->B.x = x;
458                 outline->B.y = y;
459                 cpos = 1;
460            } 
461            else if(subpath->getCurve(s) && cpos)
462            {
463                 outline->C.x = x;
464                 outline->C.y = y;
465                 cpos = 2;
466            }
467            else
468            {
469                 last = outline;
470                 outline->dest.x = x;
471                 outline->dest.y = y;
472                 outline->type = cpos?T1_PATHTYPE_BEZIER:T1_PATHTYPE_LINE;
473                 outline->link = 0;
474                 outline->link = (T1_OUTLINE*)new bezierpathsegment();
475                 outline = (bezierpathsegment*)outline->link;
476                 cpos = 0;
477                 lastx = nx;
478                 lasty = ny;
479            }
480         }
481     }
482     last->link = 0;
483     return (T1_OUTLINE*)start;
484 }
485 /*----------------------------------------------------------------------------
486  * Primitive Graphic routines
487  *----------------------------------------------------------------------------*/
488
489 void SWFOutputDev::stroke(GfxState *state) 
490 {
491     logf("<debug> %s stroke\n",gfxstate2str(state));
492     GfxPath * path = state->getPath();
493     struct swfmatrix m;
494     m.m11 = 1; m.m21 = 0; m.m22 = 1;
495     m.m21 = 0; m.m13 = 0; m.m23 = 0;
496     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
497     swfoutput_setdrawmode(&output, DRAWMODE_STROKE);
498     swfoutput_drawpath(&output, outline, &m);
499 }
500 void SWFOutputDev::fill(GfxState *state) 
501 {
502     logf("<debug> %s fill\n",gfxstate2str(state));
503     GfxPath * path = state->getPath();
504     struct swfmatrix m;
505     m.m11 = 1; m.m21 = 0; m.m22 = 1;
506     m.m21 = 0; m.m13 = 0; m.m23 = 0;
507     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
508     swfoutput_setdrawmode(&output, DRAWMODE_FILL);
509     swfoutput_drawpath(&output, outline, &m);
510 }
511 void SWFOutputDev::eoFill(GfxState *state) 
512 {
513     logf("<debug> %s eofill\n",gfxstate2str(state));
514     GfxPath * path = state->getPath();
515     struct swfmatrix m;
516     m.m11 = 1; m.m21 = 0; m.m22 = 1;
517     m.m21 = 0; m.m13 = 0; m.m23 = 0;
518     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
519     swfoutput_setdrawmode(&output, DRAWMODE_EOFILL);
520     swfoutput_drawpath(&output, outline, &m);
521 }
522 void SWFOutputDev::clip(GfxState *state) 
523 {
524     logf("<debug> %s clip\n",gfxstate2str(state));
525     GfxPath * path = state->getPath();
526     struct swfmatrix m;
527     m.m11 = 1; m.m21 = 0; m.m22 = 1;
528     m.m21 = 0; m.m13 = 0; m.m23 = 0;
529     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
530     swfoutput_startclip(&output, outline, &m);
531     clipping[clippos] = 1;
532 }
533 void SWFOutputDev::eoClip(GfxState *state) 
534 {
535     logf("<debug> %s eoclip\n",gfxstate2str(state));
536     GfxPath * path = state->getPath();
537     struct swfmatrix m;
538     m.m11 = 1; m.m21 = 0; m.m22 = 1;
539     m.m21 = 0; m.m13 = 0; m.m23 = 0;
540     T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path);
541     swfoutput_startclip(&output, outline, &m);
542     clipping[clippos] = 1;
543 }
544
545 SWFOutputDev::~SWFOutputDev() 
546 {
547     swfoutput_destroy(&output);
548     outputstarted = 0;
549 };
550 GBool SWFOutputDev::upsideDown() 
551 {
552     logf("<debug> upsidedown?");
553     return gTrue;
554 };
555 GBool SWFOutputDev::useDrawChar() 
556 {
557     logf("<debug> usedrawchar?");
558     return gTrue;
559 }
560
561 void SWFOutputDev::beginString(GfxState *state, GString *s) 
562
563     double m11,m21,m12,m22;
564     logf("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
565     state->getFontTransMat(&m11, &m12, &m21, &m22);
566     m11 *= state->getHorizScaling();
567     m21 *= state->getHorizScaling();
568     swfoutput_setfontmatrix(&output, m11, -m12, m21, -m22);
569 }
570
571 int charcounter = 0;
572 void SWFOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, Guchar c) 
573 {
574     logf("<debug> %s drawChar(%f,%f,%f,%f,'%c')\n",gfxstate2str(state), x,y,dx,dy,c);
575     // check for invisible text -- this is used by Acrobat Capture
576     if ((state->getRender() & 3) != 3)
577     {
578        FontEncoding*enc=state->getFont()->getEncoding();
579
580        double x1,y1;
581        x1 = x;
582        y1 = y;
583        state->transform(x, y, &x1, &y1);
584
585        swfoutput_drawchar(&output, x1, y1, c);
586     }
587 }
588
589 void SWFOutputDev::drawChar16(GfxState *state, double x, double y, double dx, double dy, int c) 
590 {
591     printf("<error> %s drawChar16(%f,%f,%f,%f,%08x)\n",gfxstate2str(state), x,y,dx,dy,c);
592     exit(1);
593 }
594
595 void SWFOutputDev::endString(GfxState *state) 
596
597     logf("<debug> %s endstring\n", gfxstate2str(state));
598 }    
599
600 void SWFOutputDev::startPage(int pageNum, GfxState *state) 
601 {
602   double x1,y1,x2,y2;
603   logf("<debug> %s, startPage %d\n", gfxstate2str(state), pageNum);
604   logf("<notice> processing page %d", pageNum);
605
606   state->transform(state->getX1(),state->getY1(),&x1,&y1);
607   state->transform(state->getX2(),state->getY2(),&x2,&y2);
608   if(!outputstarted) {
609     swfoutput_init(&output, filename, abs((int)(x2-x1)),abs((int)(y2-y1)));
610     outputstarted = 1;
611   }
612   else
613     swfoutput_newpage(&output);
614 }
615
616 void SWFOutputDev::drawLink(Link *link, Catalog *catalog) 
617 {
618   logf("<debug> drawlink\n");
619   double x1, y1, x2, y2, w;
620   GfxRGB rgb;
621   swfcoord points[5];
622   int x, y;
623
624   link->getBorder(&x1, &y1, &x2, &y2, &w);
625   if (w > 0) {
626     rgb.r = 0;
627     rgb.g = 0;
628     rgb.b = 1;
629     cvtUserToDev(x1, y1, &x, &y);
630     points[0].x = points[4].x = x;
631     points[0].y = points[4].y = y;
632     cvtUserToDev(x2, y1, &x, &y);
633     points[1].x = x;
634     points[1].y = y;
635     cvtUserToDev(x2, y2, &x, &y);
636     points[2].x = x;
637     points[2].y = y;
638     cvtUserToDev(x1, y2, &x, &y);
639     points[3].x = x;
640     points[3].y = y;
641     //PDF: draw rect
642     LinkAction*action=link->getAction();
643     char*s;
644     switch(action->getKind())
645     {
646         case actionGoTo: {
647             LinkGoTo*l = (LinkGoTo*)action;
648             s = l->getNamedDest()->getCString();
649         }
650         break;
651         case actionGoToR: {
652             LinkGoToR*l = (LinkGoToR*)action;
653             s = l->getNamedDest()->getCString();
654         }
655         break;
656         case actionLaunch: {
657             LinkLaunch*l = (LinkLaunch*)action;
658             GString * str = new GString(l->getFileName());
659             str->append(l->getParams());
660             s = str->getCString();
661         }
662         break;
663         case actionURI: {
664             LinkURI*l = (LinkURI*)action;
665             s = l->getURI()->getCString();
666         }
667         break;
668         case actionNamed: {
669             LinkNamed*l = (LinkNamed*)action;
670             s = l->getName()->getCString();
671         }
672         break;
673         case actionUnknown: {
674             LinkUnknown*l = (LinkUnknown*)action;
675             s = "";
676         }
677         break;
678     }
679     logf("<verbose> link to \"%s\"\n", s);
680   }
681 }
682
683 void SWFOutputDev::saveState(GfxState *state) {
684   logf("<debug> %s saveState\n", gfxstate2str(state));
685   updateAll(state);
686   clippos ++;
687   clipping[clippos] = 0;
688 };
689
690 void SWFOutputDev::restoreState(GfxState *state) {
691   logf("<debug> %s restoreState\n", gfxstate2str(state));
692   updateAll(state);
693   if(clipping[clippos])
694       swfoutput_endclip(&output);
695   clippos--;
696 }
697
698 char type3Warning=0;
699
700 int SWFOutputDev::setT1Font(char*name, FontEncoding*encoding) 
701 {       
702     int i;
703     
704     int id=-1;
705     int mapid=-1;
706     char*filename=0;
707     for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
708     {
709         if(!strcmp(name, pdf2t1map[i].pdffont))
710         {
711             filename = pdf2t1map[i].filename;
712             mapid = i;
713         }
714     }
715     if(filename)
716     for(i=0; i<T1_Get_no_fonts(); i++)
717     {
718         char*fontfilename = T1_GetFontFileName (i);
719         if(strstr(fontfilename, filename))
720         {
721                 id = i;
722                 pdf2t1map[i].id = mapid;
723         }
724     }
725     if(id<0)
726      return 0;
727
728     /* T1 allows us to recode only once. Therefore, remove
729        and reload the font to reset it */
730     T1_DeleteFont(id);
731     T1_LoadFont(id);
732     initT1Font(id, encoding);
733 }
734
735 int SWFOutputDev::initT1Font(int id, FontEncoding*encoding)
736 {
737     int encStrSize;
738     char *encPtr;
739     int i;
740
741     if(!T1_GetFontName(id))
742         T1_LoadFont(id);
743
744     /* reencode the font: 
745      * This is the only way to get the unmapped characters
746      * from t1lib
747      */
748     encStrSize = 0;
749     for (i = 0; i < 256 && i < encoding->getSize(); ++i) {
750       if (encoding->getCharName(i)) {
751         encStrSize += strlen(encoding->getCharName(i)) + 1;
752       }
753     }
754     char**enc = (char **)gmalloc(257 * sizeof(char *));
755     char*encStr = (char *)gmalloc(encStrSize * sizeof(char));
756     encPtr = encStr;
757     for (i = 0; i < 256 && i < encoding->getSize(); ++i) {
758       if (encoding->getCharName(i)) {
759         strcpy(encPtr, encoding->getCharName(i));
760         enc[i] = encPtr;
761         encPtr += strlen(encPtr) + 1;
762       } else {
763         enc[i] = ".notdef";
764       }
765     }
766     for (; i < 256; ++i) {
767       enc[i] = ".notdef";
768     }
769     enc[256] = "custom";
770     int ret=T1_ReencodeFont(id, enc);
771     t1id = id;
772     return 1;
773 }
774
775 void SWFOutputDev::updateLineWidth(GfxState *state)
776 {
777     double width = state->getLineWidth();
778     swfoutput_setlinewidth(&output, width);
779 }
780
781 void SWFOutputDev::updateFillColor(GfxState *state) 
782 {
783     GfxRGB rgb;
784     double opaq = state->getFillOpacity();
785     state->getFillRGB(&rgb);
786
787     swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
788                                     (char)(rgb.b*255), (char)(opaq*255));
789 }
790
791 void SWFOutputDev::updateStrokeColor(GfxState *state) 
792 {
793     GfxRGB rgb;
794     double opaq = state->getStrokeOpacity();
795     state->getStrokeRGB(&rgb);
796
797     swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), 
798                                       (char)(rgb.b*255), (char)(opaq*255));
799 }
800
801 char*writeEmbeddedFontToFile(GfxFont*font)
802 {
803       char*tmpFileName = NULL;
804       char*fileName = NULL;
805       FILE *f;
806       int c;
807       char *fontBuf;
808       int fontLen;
809       Type1CFontConverter *cvt;
810       Ref embRef;
811       Object refObj, strObj;
812       tmpFileName = "tmpfont";
813       font->getEmbeddedFontID(&embRef);
814
815       f = fopen(tmpFileName, "wb");
816       if (!f) {
817         logf("<error> Couldn't create temporary Type 1 font file");
818         return 0;
819       }
820       if (font->getType() == fontType1C) {
821         if (!(fontBuf = font->readEmbFontFile(&fontLen))) {
822           fclose(f);
823           logf("<error> Couldn't read embedded font file");
824           return 0;
825         }
826         cvt = new Type1CFontConverter(fontBuf, fontLen, f);
827         cvt->convert();
828         delete cvt;
829         gfree(fontBuf);
830       } else {
831         font->getEmbeddedFontID(&embRef);
832         refObj.initRef(embRef.num, embRef.gen);
833         refObj.fetch(&strObj);
834         refObj.free();
835         strObj.streamReset();
836         while ((c = strObj.streamGetChar()) != EOF) {
837           fputc(c, f);
838         }
839         strObj.streamClose();
840         strObj.free();
841       }
842       fclose(f);
843       fileName = tmpFileName;
844       if(!fileName) {
845           logf("<error> Embedded font writer didn't create a file");
846           return 0;
847       }
848       return fileName;
849 }
850
851 int embeddedids[128];
852 int embeddedt1ids[128];
853 int embedded_mappos = 0;
854 int embedded_maxpos = 128;
855
856 void SWFOutputDev::updateFont(GfxState *state) 
857 {
858   double m11, m12, m21, m22;
859   char * fontname = 0;
860   GfxFont*gfxFont = state->getFont();
861
862   if (!gfxFont) {
863     return;
864   }  
865
866   if(swfoutput_queryfont(&output, gfxFont->getID().num))
867   {
868       swfoutput_setfont(&output, gfxFont->getID().num, -1);
869       return;
870   }
871
872   // look for Type 3 font
873   if (!type3Warning && gfxFont->getType() == fontType3) {
874     type3Warning = gTrue;
875     showFontError(gfxFont, 2);
876   }
877   //dumpFontInfo ("<notice>", gfxFont);
878
879   Ref embRef;
880   GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
881   if(embedded) {
882     int t;
883     for(t=0;t<embedded_mappos;t++)
884         if(embeddedids[t] == embRef.num)
885             break;
886     if(t==embedded_mappos)
887     {
888         char*fileName;
889         if (!gfxFont->is16Bit() &&
890             (gfxFont->getType() == fontType1 ||
891              gfxFont->getType() == fontType1C)) {
892             
893             fileName = writeEmbeddedFontToFile(gfxFont);
894             if(!fileName)
895               return ;
896         }
897         else {
898             showFontError(gfxFont,0);
899             return ;
900         }
901         t1id = T1_AddFont(fileName);
902         embeddedids[embedded_mappos] = embRef.num;
903         embeddedt1ids[embedded_mappos] = t1id;
904         if(embedded_mappos < embedded_maxpos-1)
905             embedded_mappos++;
906     }
907     else 
908     {
909         t1id = embeddedt1ids[t];
910     }
911     initT1Font(t1id, gfxFont->getEncoding());
912   } else {
913     fontname = NULL;
914     if(gfxFont->getName()) {
915       fontname = gfxFont->getName()->getCString();
916       //logf("<notice> Processing font %s", fontname);
917     }
918     if(!fontname || !setT1Font(state->getFont()->getName()->getCString(), gfxFont->getEncoding()))
919     { //substitute font
920       int index;
921       int code;
922       double w,w1,w2;
923       double*fm;
924       double v;
925       showFontError(gfxFont, 1);
926       if (!gfxFont->is16Bit()) {
927         if (gfxFont->isFixedWidth()) {
928           index = 8;
929         } else if (gfxFont->isSerif()) {
930           index = 4;
931         } else {
932           index = 0;
933         }
934         if (gfxFont->isBold())
935           index += 2;
936         if (gfxFont->isItalic())
937           index += 1;
938         fontname = fontnames[index];
939         // get width of 'm' in real font and substituted font
940         if ((code = gfxFont->getCharCode("m")) >= 0)
941           w1 = gfxFont->getWidth(code);
942         else
943           w1 = 0;
944         w2 = fontsizes[index];
945         if (gfxFont->getType() == fontType3) {
946           // This is a hack which makes it possible to substitute for some
947           // Type 3 fonts.  The problem is that it's impossible to know what
948           // the base coordinate system used in the font is without actually
949           // rendering the font.  This code tries to guess by looking at the
950           // width of the character 'm' (which breaks if the font is a
951           // subset that doesn't contain 'm').
952           if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
953             w1 /= w2;
954             m11 *= w1;
955             m12 *= w1;
956             m21 *= w1;
957             m22 *= w1;
958           }
959           fm = gfxFont->getFontMatrix();
960           v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
961           m21 *= v;
962           m22 *= v;
963         } else if (!gfxFont->isSymbolic()) {
964           // if real font is substantially narrower than substituted
965           // font, reduce the font size accordingly
966           if (w1 > 0.01 && w1 < 0.9 * w2) {
967             w1 /= w2;
968             if (w1 < 0.8) {
969               w1 = 0.8;
970             }
971             m11 *= w1;
972             m12 *= w1;
973             m21 *= w1;
974             m22 *= w1;
975           }
976         }
977       }
978       if(fontname)
979         setT1Font(fontname, gfxFont->getEncoding());
980     }
981   }
982
983   swfoutput_setfont(&output,gfxFont->getID().num,t1id);
984 }
985
986 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
987                                    int width, int height, GBool invert,
988                                    GBool inlineImg) {
989   FILE *fi;
990   int c;
991   char fileName[128];
992   double x1,y1,x2,y2,x3,y3,x4,y4;
993   state->transform(0, 1, &x1, &y1);
994   state->transform(0, 0, &x2, &y2);
995   state->transform(1, 0, &x3, &y3);
996   state->transform(1, 1, &x4, &y4);
997
998   if (str->getKind() == strDCT) {
999     sprintf(fileName, "/tmp/tmp%08x.jpg",lrand48());
1000     logf("<notice> Found picture. Temporary storage is %s", fileName);
1001     if (!(fi = fopen(fileName, "wb"))) {
1002       logf("<error> Couldn't open temporary image file '%s'", fileName);
1003       return;
1004     }
1005     str = ((DCTStream *)str)->getRawStream();
1006     str->reset();
1007     while ((c = str->getChar()) != EOF)
1008       fputc(c, fi);
1009     fclose(fi);
1010     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1011   } else {
1012       logf("<notice> File contains pbm pictures.");
1013   }
1014 }
1015
1016 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1017                                int width, int height,
1018                                GfxImageColorMap *colorMap, GBool inlineImg) {
1019   FILE *fi;
1020   int c;
1021   char fileName[128];
1022   double x1,y1,x2,y2,x3,y3,x4,y4;
1023   state->transform(0, 1, &x1, &y1);
1024   state->transform(0, 0, &x2, &y2);
1025   state->transform(1, 0, &x3, &y3);
1026   state->transform(1, 1, &x4, &y4);
1027
1028   if (str->getKind() == strDCT &&
1029       colorMap->getNumPixelComps() == 3) {
1030     sprintf(fileName, "/tmp/tmp%08x.jpg", lrand48());
1031     logf("<notice> Found picture. Temporary storage is %s", fileName);
1032     if (!(fi = fopen(fileName, "wb"))) {
1033       error(-1, "Couldn't open temporary image file '%s'", fileName);
1034       return;
1035     }
1036     str = ((DCTStream *)str)->getRawStream();
1037     str->reset();
1038     while ((c = str->getChar()) != EOF)
1039       fputc(c, fi);
1040     fclose(fi);
1041     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1042   } else {
1043       logf("<notice> File contains pbm pictures.");
1044   }
1045 }
1046
1047 PDFDoc*doc = 0;
1048 SWFOutputDev*output = 0; 
1049
1050 void pdfswf_init(char*filename, char*userPassword) 
1051 {
1052   GString *fileName = new GString(filename);
1053   GString *userPW;
1054   Object info;
1055   // init error file
1056   errorInit();
1057
1058   // read config file
1059   initParams(xpdfConfigFile);
1060
1061   // open PDF file
1062   xref = NULL;
1063   if (userPassword && userPassword[0]) {
1064     userPW = new GString(userPassword);
1065   } else {
1066     userPW = NULL;
1067   }
1068   doc = new PDFDoc(fileName, userPW);
1069   if (userPW) {
1070     delete userPW;
1071   }
1072   if (!doc->isOk()) {
1073     exit(1);
1074   }
1075
1076   // print doc info
1077   doc->getDocInfo(&info);
1078   if (info.isDict()) {
1079     printInfoString(info.getDict(), "Title",        "Title:        %s\n");
1080     printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
1081     printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
1082     printInfoString(info.getDict(), "Author",       "Author:       %s\n");
1083     printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
1084     printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
1085     printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
1086     printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
1087   }
1088   info.free();
1089
1090   // print page count
1091   printf("Pages:        %d\n", doc->getNumPages());
1092   
1093   // print linearization info
1094   printf("Linearized:   %s\n", doc->isLinearized() ? "yes" : "no");
1095
1096   // print encryption info
1097   printf("Encrypted:    ");
1098   if (doc->isEncrypted()) {
1099     printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
1100            doc->okToPrint() ? "yes" : "no",
1101            doc->okToCopy() ? "yes" : "no",
1102            doc->okToChange() ? "yes" : "no",
1103            doc->okToAddNotes() ? "yes" : "no");
1104         /*ERROR: This pdf is encrypted, and disallows copying.
1105           Due to the DMCA, paragraph 1201, (2) A-C, circumventing
1106           a technological measure that efficively controls access to
1107           a protected work is violating American law. 
1108           See www.eff.org for more information about DMCA issues.
1109          */
1110         if(!doc->okToCopy()) {
1111             printf("PDF disallows copying. Bailing out.\n");
1112             exit(1); //bail out
1113         }
1114         if(!doc->okToChange() || !doc->okToAddNotes())
1115             swfoutput_setprotected();
1116     }
1117   else {
1118     printf("no\n");
1119   }
1120
1121
1122   output = new SWFOutputDev();
1123 }
1124
1125 void pdfswf_setoutputfilename(char*_filename)
1126 {
1127     filename = _filename;
1128 }
1129
1130 void pdfswf_convertpage(int page)
1131 {
1132     doc->displayPage((OutputDev*)output, page, /*zoom*/100, /*rotate*/0, /*doLinks*/(int)1);
1133 }
1134
1135 int pdfswf_numpages()
1136 {
1137   return doc->getNumPages();
1138 }
1139
1140 void pdfswf_close()
1141 {
1142     delete doc;
1143     delete output;
1144     
1145     freeParams();
1146     // check for memory leaks
1147     Object::memCheck(stderr);
1148     gMemReport(stderr);
1149 }
1150