swfoutput_setfont now takes the font id as parameter
[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 {
794   double m11, m12, m21, m22;
795   char * fontname = 0;
796   GfxFont*gfxFont = state->getFont();
797   char * filename;
798
799   if (!gfxFont) {
800     return;
801   }  
802   // look for Type 3 font
803   if (!type3Warning && gfxFont->getType() == fontType3) {
804     type3Warning = gTrue;
805     showFontError(gfxFont, 2);
806   }
807   //dumpFontInfo (gfxFont);
808   
809
810   Ref embRef;
811   GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
812   if(embedded) {
813     char*tmpFileName = NULL;
814     char*fileName = NULL;
815     FILE *f;
816     char *fontBuf;
817     int fontLen;
818     Type1CFontConverter *cvt;
819     Ref embRef;
820     Object refObj, strObj;
821     int c;
822     if (!gfxFont->is16Bit() &&
823         (gfxFont->getType() == fontType1 ||
824          gfxFont->getType() == fontType1C) &&
825         gfxFont->getEmbeddedFontID(&embRef)) {
826       tmpFileName = "tmpfont";
827       f = fopen(tmpFileName, "wb");
828       if (!f) {
829         logf("<error> Couldn't create temporary Type 1 font file");
830         return;
831       }
832       if (gfxFont->getType() == fontType1C) {
833         if (!(fontBuf = gfxFont->readEmbFontFile(&fontLen))) {
834           fclose(f);
835           logf("<error> Couldn't read embedded font file");
836           return ;
837         }
838         cvt = new Type1CFontConverter(fontBuf, fontLen, f);
839         cvt->convert();
840         delete cvt;
841         gfree(fontBuf);
842       } else {
843         gfxFont->getEmbeddedFontID(&embRef);
844         refObj.initRef(embRef.num, embRef.gen);
845         refObj.fetch(&strObj);
846         refObj.free();
847         strObj.streamReset();
848         while ((c = strObj.streamGetChar()) != EOF) {
849           fputc(c, f);
850         }
851         strObj.streamClose();
852         strObj.free();
853       }
854       fclose(f);
855       fileName = tmpFileName;
856       if(!fileName) {
857           logf("<error> Embedded font writer didn't create a file");
858           return ;
859       }
860     }
861     else {
862         showFontError(gfxFont,0);
863         return ;
864     }
865     t1id = T1_AddFont(fileName);
866     initT1Font(t1id, gfxFont->getEncoding());
867   } else {
868     fontname = NULL;
869     if(gfxFont->getName()) {
870       fontname = gfxFont->getName()->getCString();
871       //logf("<notice> Processing font %s", fontname);
872     }
873     if(!fontname || !setT1Font(state->getFont()->getName()->getCString(), gfxFont->getEncoding()))
874     { //substitute font
875       int index;
876       int code;
877       double w,w1,w2;
878       double*fm;
879       double v;
880       showFontError(gfxFont, 1);
881       if (!gfxFont->is16Bit()) {
882         if (gfxFont->isFixedWidth()) {
883           index = 8;
884         } else if (gfxFont->isSerif()) {
885           index = 4;
886         } else {
887           index = 0;
888         }
889         if (gfxFont->isBold())
890           index += 2;
891         if (gfxFont->isItalic())
892           index += 1;
893         fontname = fontnames[index];
894         // get width of 'm' in real font and substituted font
895         if ((code = gfxFont->getCharCode("m")) >= 0)
896           w1 = gfxFont->getWidth(code);
897         else
898           w1 = 0;
899         w2 = fontsizes[index];
900         if (gfxFont->getType() == fontType3) {
901           // This is a hack which makes it possible to substitute for some
902           // Type 3 fonts.  The problem is that it's impossible to know what
903           // the base coordinate system used in the font is without actually
904           // rendering the font.  This code tries to guess by looking at the
905           // width of the character 'm' (which breaks if the font is a
906           // subset that doesn't contain 'm').
907           if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
908             w1 /= w2;
909             m11 *= w1;
910             m12 *= w1;
911             m21 *= w1;
912             m22 *= w1;
913           }
914           fm = gfxFont->getFontMatrix();
915           v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
916           m21 *= v;
917           m22 *= v;
918         } else if (!gfxFont->isSymbolic()) {
919           // if real font is substantially narrower than substituted
920           // font, reduce the font size accordingly
921           if (w1 > 0.01 && w1 < 0.9 * w2) {
922             w1 /= w2;
923             if (w1 < 0.8) {
924               w1 = 0.8;
925             }
926             m11 *= w1;
927             m12 *= w1;
928             m21 *= w1;
929             m22 *= w1;
930           }
931         }
932       }
933       if(fontname)
934         setT1Font(fontname, gfxFont->getEncoding());
935     }
936   }
937
938   swfoutput_setfont(&output,gfxFont->getID().num,t1id);
939 }
940
941 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
942                                    int width, int height, GBool invert,
943                                    GBool inlineImg) {
944   FILE *fi;
945   int c;
946   char fileName[128];
947   double x1,y1,x2,y2,x3,y3,x4,y4;
948   state->transform(0, 1, &x1, &y1);
949   state->transform(0, 0, &x2, &y2);
950   state->transform(1, 0, &x3, &y3);
951   state->transform(1, 1, &x4, &y4);
952
953   if (str->getKind() == strDCT) {
954     sprintf(fileName, "/tmp/tmp%08x.jpg",lrand48());
955     logf("<notice> Found picture. Temporary storage is %s", fileName);
956     if (!(fi = fopen(fileName, "wb"))) {
957       logf("<error> Couldn't open temporary image file '%s'", fileName);
958       return;
959     }
960     str = ((DCTStream *)str)->getRawStream();
961     str->reset();
962     while ((c = str->getChar()) != EOF)
963       fputc(c, fi);
964     fclose(fi);
965     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
966   } else {
967       logf("<notice> File contains pbm pictures.");
968   }
969 }
970
971 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
972                                int width, int height,
973                                GfxImageColorMap *colorMap, GBool inlineImg) {
974   FILE *fi;
975   int c;
976   char fileName[128];
977   double x1,y1,x2,y2,x3,y3,x4,y4;
978   state->transform(0, 1, &x1, &y1);
979   state->transform(0, 0, &x2, &y2);
980   state->transform(1, 0, &x3, &y3);
981   state->transform(1, 1, &x4, &y4);
982
983   if (str->getKind() == strDCT &&
984       colorMap->getNumPixelComps() == 3) {
985     sprintf(fileName, "/tmp/tmp%08x.jpg", lrand48());
986     logf("<notice> Found picture. Temporary storage is %s", fileName);
987     if (!(fi = fopen(fileName, "wb"))) {
988       error(-1, "Couldn't open temporary image file '%s'", fileName);
989       return;
990     }
991     str = ((DCTStream *)str)->getRawStream();
992     str->reset();
993     while ((c = str->getChar()) != EOF)
994       fputc(c, fi);
995     fclose(fi);
996     swfoutput_drawimagefile(&output, fileName, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
997   } else {
998       logf("<notice> File contains pbm pictures.");
999   }
1000 }
1001
1002 PDFDoc*doc = 0;
1003 SWFOutputDev*output = 0; 
1004
1005 void pdfswf_init(char*filename, char*userPassword) 
1006 {
1007   GString *fileName = new GString(filename);
1008   GString *userPW;
1009   Object info;
1010   // init error file
1011   errorInit();
1012
1013   // read config file
1014   initParams(xpdfConfigFile);
1015
1016   // open PDF file
1017   xref = NULL;
1018   if (userPassword && userPassword[0]) {
1019     userPW = new GString(userPassword);
1020   } else {
1021     userPW = NULL;
1022   }
1023   doc = new PDFDoc(fileName, userPW);
1024   if (userPW) {
1025     delete userPW;
1026   }
1027   if (!doc->isOk()) {
1028     exit(1);
1029   }
1030
1031   // print doc info
1032   doc->getDocInfo(&info);
1033   if (info.isDict()) {
1034     printInfoString(info.getDict(), "Title",        "Title:        %s\n");
1035     printInfoString(info.getDict(), "Subject",      "Subject:      %s\n");
1036     printInfoString(info.getDict(), "Keywords",     "Keywords:     %s\n");
1037     printInfoString(info.getDict(), "Author",       "Author:       %s\n");
1038     printInfoString(info.getDict(), "Creator",      "Creator:      %s\n");
1039     printInfoString(info.getDict(), "Producer",     "Producer:     %s\n");
1040     printInfoDate(info.getDict(),   "CreationDate", "CreationDate: %s\n");
1041     printInfoDate(info.getDict(),   "ModDate",      "ModDate:      %s\n");
1042   }
1043   info.free();
1044
1045   // print page count
1046   printf("Pages:        %d\n", doc->getNumPages());
1047   
1048   // print linearization info
1049   printf("Linearized:   %s\n", doc->isLinearized() ? "yes" : "no");
1050
1051   // print encryption info
1052   printf("Encrypted:    ");
1053   if (doc->isEncrypted()) {
1054     printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
1055            doc->okToPrint() ? "yes" : "no",
1056            doc->okToCopy() ? "yes" : "no",
1057            doc->okToChange() ? "yes" : "no",
1058            doc->okToAddNotes() ? "yes" : "no");
1059         /*ERROR: This pdf is encrypted, and disallows copying.
1060           Due to the DMCA, paragraph 1201, (2) A-C, circumventing
1061           a technological measure that efficively controls access to
1062           a protected work is violating American law. 
1063           See www.eff.org for more information about DMCA issues.
1064          */
1065         if(!doc->okToCopy()) {
1066             printf("PDF disallows copying. Bailing out.\n");
1067             exit(1); //bail out
1068         }
1069         if(!doc->okToChange() || !doc->okToAddNotes())
1070             swfoutput_setprotected();
1071     }
1072   else {
1073     printf("no\n");
1074   }
1075
1076
1077   output = new SWFOutputDev();
1078 }
1079
1080 void pdfswf_setoutputfilename(char*_filename)
1081 {
1082     filename = _filename;
1083 }
1084
1085 void pdfswf_convertpage(int page)
1086 {
1087     doc->displayPage((OutputDev*)output, page, /*zoom*/100, /*rotate*/0, /*doLinks*/(int)1);
1088 }
1089
1090 int pdfswf_numpages()
1091 {
1092   return doc->getNumPages();
1093 }
1094
1095 void pdfswf_close()
1096 {
1097     delete doc;
1098     delete output;
1099     
1100     freeParams();
1101     // check for memory leaks
1102     Object::memCheck(stderr);
1103     gMemReport(stderr);
1104 }
1105