* shapecount is now named currentswfid
[swftools.git] / pdf2swf / swfoutput.cc
1 /* swfoutput.cc
2    Implements generation of swf files using the rfxswf lib. The routines
3    in this file are called from pdf2swf.
4
5    This file is part of swftools.
6
7    Swftools is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    Swftools is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with swftools; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <string.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include "swfoutput.h"
25 #include "spline.h"
26 extern "C" {
27 #include "../lib/log.h"
28 #include "../lib/rfxswf.h"
29 }
30
31 typedef unsigned char u8;
32 typedef unsigned short int u16;
33 typedef unsigned long int u32;
34
35 static int fi;
36 static int flag_protected;
37 static SWF swf;
38 static TAG *tag;
39 static int shapeid = -1;
40 static int currentswfid = 0;
41 static SHAPE* shape;
42 static int fillstyleid;
43 static int linestyleid;
44 static int swflastx=0;
45 static int swflasty=0;
46 static int lastwasfill = 0;
47 static char* filename = 0;
48 static int sizex;
49 static int sizey;
50 static char fill = 0;
51 static int depth = 1;
52 static int startdepth = 1;
53 TAG* cliptags[128];
54 int clipshapes[128];
55 u32 clipdepths[128];
56 int clippos = 0;
57
58 void startshape(struct swfoutput* obj);
59
60 // matrix multiplication. changes p0
61 void transform (plotxy*p0,struct swfmatrix*m)
62 {
63     double x,y;
64     x = m->m11*p0->x+m->m12*p0->y;
65     y = m->m21*p0->x+m->m22*p0->y;
66     p0->x = x + m->m13;
67     p0->y = y + m->m23;
68 }
69
70 // write a move-to command into the swf
71 void moveto(plotxy p0)
72 {
73     int rx = (int)(p0.x*20);
74     int ry = (int)(p0.y*20);
75     if(rx!=swflastx || ry!=swflasty) {
76       ShapeSetMove (tag, shape, rx,ry);
77     }
78     swflastx=rx;
79     swflasty=ry;
80 }
81
82 // write a line-to command into the swf
83 void lineto(plotxy p0)
84 {
85     int rx = ((int)(p0.x*20)-swflastx);
86     int ry = ((int)(p0.y*20)-swflasty);
87     /* we can't skip this for rx=0,ry=0, those
88        are plots */
89     ShapeSetLine (tag, shape, rx,ry);
90     swflastx+=rx;
91     swflasty+=ry;
92 }
93
94 // write a spline-to command into the swf
95 void splineto(plotxy control,plotxy end)
96 {
97     int cx = ((int)(control.x*20)-swflastx);
98     int cy = ((int)(control.y*20)-swflasty);
99     swflastx += cx;
100     swflasty += cy;
101     int ex = ((int)(end.x*20)-swflastx);
102     int ey = ((int)(end.y*20)-swflasty);
103     swflastx += ex;
104     swflasty += ey;
105     ShapeSetCurve(tag, shape, cx,cy,ex,ey);
106 }
107
108 /* write a line, given two points and the transformation
109    matrix. */
110 void line(plotxy p0, plotxy p1, struct swfmatrix*m)
111 {
112     transform(&p0,m);
113     transform(&p1,m);
114     moveto(p0);
115     lineto(p1);
116 }
117
118 /* write a cubic (!) spline. This involves calling the approximate()
119    function out of spline.cc to convert it to a quadratic spline.  */
120 void spline(plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
121 {
122     double d;
123     struct qspline q[16];
124     int num;
125     int t;
126     transform(&p0,m);
127     transform(&p1,m);
128     transform(&p2,m);
129     transform(&p3,m);
130
131     num = approximate(p0,p1,p2,p3,q);
132     for(t=0;t<num;t++) {
133         moveto(q[t].start);
134         splineto(q[t].control, q[t].end);
135     }
136 }
137
138 /* Adds an outline to a font. Applies only the 2x2 component of the transformation matrix. 
139  */
140 void addtofont(T1_OUTLINE*outline, struct swfmatrix*m, char*namehint)
141 {
142 }
143
144 /* draw a T1 outline. These are generated by pdf2swf and by t1lib.
145    (representing characters) */
146 void drawpath(T1_OUTLINE*outline, struct swfmatrix*m, char*namehint)
147 {
148     double x=0,y=0;
149     double lastx=0,lasty=0;
150
151     while (outline)
152     {
153         logf("<debug> Pathtype:%s",outline->type == T1_PATHTYPE_MOVE?"MOVE":
154                                                     (outline->type == T1_PATHTYPE_LINE?"LINE"
155                                                                                       :"BEZIER"));
156         logf("<debug> relative coordinates: %08x,%08x", outline->dest.x, outline->dest.y);
157         x += (outline->dest.x/(float)0xffff);
158         y += (outline->dest.y/(float)0xffff);
159         logf("<debug> coordinates: %f,%f", x, y);
160         if(outline->type == T1_PATHTYPE_MOVE)
161         {
162         }
163         else if(outline->type == T1_PATHTYPE_LINE) 
164         {
165             plotxy p0;
166             plotxy p1;
167             p0.x=lastx;
168             p0.y=lasty;
169             p1.x=x;
170             p1.y=y;
171             line(p0,p1,m);
172         }
173         else if(outline->type == T1_PATHTYPE_BEZIER)
174         {
175             plotxy p0;
176             plotxy p1;
177             plotxy p2;
178             plotxy p3;
179             T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
180             p0.x=x; 
181             p0.y=y;
182             p1.x=o2->C.x/(float)0xffff+lastx;
183             p1.y=o2->C.y/(float)0xffff+lasty;
184             p2.x=o2->B.x/(float)0xffff+lastx;
185             p2.y=o2->B.y/(float)0xffff+lasty;
186             p3.x=lastx;
187             p3.y=lasty;
188             spline(p0,p1,p2,p3,m);
189         } 
190         else {
191          logf("<error> drawpath: unknown outline type:%d\n", outline->type);
192         }
193         lastx=x;
194         lasty=y;
195         outline = outline->link;
196     }
197 }
198     //logf("<debug> Font name is %s", T1_GetFontFileName(t1fontindex));
199     //logf("<debug> char 0x%02x is named %s\n",character,charname);
200     //logf("<debug> bbox: %d %d %d %d\n",bbox.llx,bbox.lly,bbox.urx,bbox.ury);
201     //char*charname = T1_GetCharName(t1fontindex, character);
202
203 /* process a character. */
204 void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
205 {
206     T1_OUTLINE*outline = font->getOutline(character);
207     char* charname = character;
208
209     if(!outline) {
210      logf("Didn't find %s in current charset", character);
211      return;
212     }
213     
214     swfmatrix m2=*m;    
215     m2.m11/=100;
216     m2.m21/=100;
217     m2.m12/=100;
218     m2.m22/=100;
219
220     if(shapeid<0)
221         startshape(obj);
222
223     if(!lastwasfill)
224      ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
225     lastwasfill = 1;
226
227     drawpath(outline, &m2, charname);
228 }
229
230 /* draw a curved polygon. */
231 void swfoutput_drawpath(swfoutput*output, T1_OUTLINE*outline, struct swfmatrix*m)
232 {
233     if(shapeid<0)
234         startshape(output);
235
236     if(lastwasfill && !fill)
237     {
238      ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
239      lastwasfill = 0;
240     }
241     if(!lastwasfill && fill)
242     {
243      ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
244      lastwasfill = 1;
245     }
246
247     drawpath(outline,m, 0); 
248 }
249
250 SWFFont::SWFFont(char*name, int id, char*filename)
251 {
252     if(!T1_GetFontName(id))
253         T1_LoadFont(id);
254
255     this->name = strdup(T1_GetFontFileName(id));
256     this->fontid = strdup(name);
257     this->t1id = id;
258
259     char**a= T1_GetAllCharNames(id);
260     int t=0, outlinepos=0;
261     char*map[256];
262     while(a[t])
263         t++;
264  
265     this->charnum = t;
266     if(!t) 
267         return;
268     logf("<notice> Font %s(%d): Storing %d outlines.\n", name, id, t);
269     
270     outline = (T1_OUTLINE**)malloc(t*sizeof(T1_OUTLINE*));
271     charname = (char**)malloc(t*sizeof(char*));
272     used = (char*)malloc(t*sizeof(char));
273     memset(used,0,t*sizeof(char));
274
275     /*
276     tag = InsertTag(tag,ST_DEFINEFONT);
277     SetU16(tag, ++currentswfid);
278     this->swfid = currentswfid;
279     */
280     
281     t=0;
282     while(*a)
283     {
284         map[t] = *a;
285         a++;
286         t++;
287         if(t==256 || !*a) {
288             int s;
289             for(s=t;s<256;s++)
290                 map[s] = ".notdef";
291
292             int ret = T1_ReencodeFont(id, map);
293             if(ret) {
294              T1_DeleteFont(id);
295              T1_LoadFont(id);
296              int ret = T1_ReencodeFont(id, map);
297              if(ret)
298                fprintf(stderr,"Can't reencode font: (%s) ret:%d\n",filename, ret);
299             }
300
301             // parsecharacters
302             for(s=0;s<t;s++)
303             {
304                 this->outline[outlinepos] = T1_CopyOutline(T1_GetCharOutline(id, s, 100.0, 0));
305                 this->charname[outlinepos] = strdup(T1_GetCharName(id, s));
306                 outlinepos++;
307             }
308             t=0;
309         }
310     }
311 }
312
313 SWFFont::~SWFFont()
314 {
315     int t,s=0;
316     for(t=0;t<charnum;t++)
317         if(used[t]) s++;
318     logf("<notice> Font %s has %d used characters",fontid, s);
319 }
320
321 T1_OUTLINE*SWFFont::getOutline(char*name)
322 {
323     int t;
324     for(t=0;t<this->charnum;t++) {
325         if(!strcmp(this->charname[t],name)) {
326             used[t] = 1;
327             return outline[t];
328         }
329     }
330     return 0;
331 }
332
333 char*SWFFont::getName()
334 {
335     return this->name;
336 }
337
338 struct fontlist_t 
339 {
340     SWFFont * font;
341     fontlist_t*next;
342 } *fontlist = 0;
343
344 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
345 void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filename)
346 {
347     fontlist_t*last=0,*iterator;
348     if(obj->font && !strcmp(obj->font->fontid,fontid))
349         return;
350
351     iterator = fontlist;
352     while(iterator) {
353         if(!strcmp(iterator->font->fontid,fontid))
354             break;
355         last = iterator;
356         iterator = iterator->next;
357     }
358     if(iterator) 
359     {
360         obj->font = iterator->font;
361         return ;
362     }
363
364     if(t1id<0) {
365         logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,fontid);
366     }
367     
368     SWFFont*font = new SWFFont(fontid, t1id, filename);
369     iterator = new fontlist_t;
370     iterator->font = font;
371     iterator->next = 0;
372
373     if(last) 
374         last->next = iterator;
375     else 
376         fontlist = iterator;
377     obj->font = font;
378 }
379
380 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
381 {
382     fontlist_t *iterator = fontlist;
383     while(iterator) {
384         if(!strcmp(iterator->font->fontid,fontid))
385             return 1;
386         iterator = iterator->next;
387     }
388     return 0;
389 }
390
391 /* set's the matrix which is to be applied to characters drawn by
392    swfoutput_drawchar() */
393 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
394                                                   double m21,double m22)
395 {
396     obj->fontm11 = m11;
397     obj->fontm12 = m12;
398     obj->fontm21 = m21;
399     obj->fontm22 = m22;
400 }
401
402 /* draws a character at x,y. */
403 void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character) 
404 {
405     swfmatrix m;
406     m.m11 = obj->fontm11;
407     m.m12 = obj->fontm12;
408     m.m21 = obj->fontm21;
409     m.m22 = obj->fontm22;
410     m.m13 = x;
411     m.m23 = y;
412     drawchar(obj, obj->font, character, &m);
413 }
414
415 /* initialize the swf writer */
416 void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _sizey) 
417 {
418   GLYPH *glyph;
419   RGBA rgb;
420   SRECT r;
421   memset(obj, 0, sizeof(struct swfoutput));
422   filename = _filename;
423   sizex = _sizex;
424   sizey = _sizey;
425
426   logf("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
427
428   obj->font = 0;
429   
430   memset(&swf,0x00,sizeof(SWF));
431
432   swf.FileVersion    = 4;
433 //  swf.FrameRate      = 0x1900;
434   swf.FrameRate      = 0x0040; // 1 frame per 4 seconds
435   swf.MovieSize.xmax = 20*sizex;
436   swf.MovieSize.ymax = 20*sizey;
437   
438   swf.FirstTag = InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
439   tag = swf.FirstTag;
440   rgb.r = 0xff;
441   rgb.g = 0xff;
442   rgb.b = 0xff;
443   SetRGB(tag,&rgb);
444   if(flag_protected)
445     tag = InsertTag(tag, ST_PROTECT);
446   depth = 1;
447   startdepth = depth;
448 }
449
450 void swfoutput_setprotected() //write PROTECT tag
451 {
452   flag_protected = 1;
453 }
454
455 void startshape(struct swfoutput*obj)
456 {
457   RGBA rgb;
458   SRECT r;
459   tag = InsertTag(tag,ST_DEFINESHAPE);
460
461   NewShape(&shape);
462   linestyleid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
463   rgb.r = obj->fillrgb.r;
464   rgb.g = obj->fillrgb.g;
465   rgb.b = obj->fillrgb.b;
466   fillstyleid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
467
468   shapeid = ++currentswfid;
469   SetU16(tag,shapeid);  // ID
470
471   r.xmin = 0;
472   r.ymin = 0;
473   r.xmax = 20*sizex;
474   r.ymax = 20*sizey;
475   
476   SetRect(tag,&r);
477
478   SetShapeStyles(tag,shape);
479   ShapeCountBits(shape,NULL,NULL);
480   SetShapeBits(tag,shape);
481
482   ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
483   swflastx=swflasty=0;
484   lastwasfill = 0;
485 }
486
487 void endshape()
488 {
489     if(shapeid<0) 
490         return;
491     ShapeSetEnd(tag);
492     tag = InsertTag(tag,ST_PLACEOBJECT2);
493     ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
494     shapeid = -1;
495 }
496
497 void endpage(struct swfoutput*obj)
498 {
499     if(shapeid>=0)
500       endshape();
501     while(clippos)
502         swfoutput_endclip(obj);
503     tag = InsertTag(tag,ST_SHOWFRAME);
504 }
505
506 void swfoutput_newpage(struct swfoutput*obj)
507 {
508     endpage(obj);
509
510     for(depth--;depth>=startdepth;depth--) {
511         tag = InsertTag(tag,ST_REMOVEOBJECT2);
512         SetU16(tag,depth);
513     }
514
515     depth = 1;
516     startdepth = depth;
517 }
518
519 /* "destroy" like in (oo-terminology) "destructor". Perform cleaning
520    up, complete the swf, and write it out. */
521 void swfoutput_destroy(struct swfoutput* obj) 
522 {
523     endpage(obj);
524     fontlist_t *iterator = fontlist;
525     while(iterator) {
526         delete iterator->font;
527         iterator->font = 0;
528         iterator = iterator->next;
529     }
530
531     T1_CloseLib();
532     if(!filename) 
533         return;
534     if(filename)
535      fi = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0777);
536     else
537      fi = 1; // stdout
538     
539     if(fi<=0) {
540      logf("<fatal> Could not create \"%s\". ", filename);
541      exit(1);
542     }
543  
544     tag = InsertTag(tag,ST_END);
545
546     if FAILED(WriteSWF(fi,&swf)) 
547      logf("<error> WriteSWF() failed.\n");
548     if(filename)
549      close(fi);
550     printf("SWF written\n");
551 }
552
553 void swfoutput_setdrawmode(swfoutput* obj, int mode)
554 {
555     if(mode == DRAWMODE_FILL)
556      fill = 1;
557     else if(mode == DRAWMODE_EOFILL)
558      fill = 1;
559     else if(mode == DRAWMODE_STROKE)
560      fill = 0;
561     else if(mode == DRAWMODE_CLIP)
562      fill = 1;
563     else if(mode == DRAWMODE_EOCLIP)
564      fill = 1;
565 }
566
567 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
568 {
569     if(obj->fillrgb.r == r &&
570        obj->fillrgb.g == g &&
571        obj->fillrgb.b == b &&
572        obj->fillrgb.a == a) return;
573
574     if(shape>=0)
575      endshape();
576     obj->fillrgb.r = r;
577     obj->fillrgb.g = g;
578     obj->fillrgb.b = b;
579     obj->fillrgb.a = a;
580 }
581
582 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
583 {
584     if(obj->strokergb.r == r &&
585        obj->strokergb.g == g &&
586        obj->strokergb.b == b &&
587        obj->strokergb.a == a) return;
588
589     if(shape>=0)
590      endshape();
591     obj->strokergb.r = r;
592     obj->strokergb.g = g;
593     obj->strokergb.b = b;
594     obj->strokergb.a = a;
595 }
596
597 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
598 {
599     if(obj->linewidth == (u16)(linewidth*20))
600         return;
601
602     if(shape>=0)
603      endshape();
604     obj->linewidth = (u16)(linewidth*20);
605 }
606
607
608 void swfoutput_startclip(swfoutput*obj, T1_OUTLINE*outline, struct swfmatrix*m)
609 {
610     if(shape>=0)
611      endshape();
612
613     if(clippos >= 127)
614     {
615         logf("<warning> Too many clip levels.");
616         clippos --;
617     } 
618
619     startshape(obj);
620
621     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
622     swfoutput_drawpath(obj, outline, m);
623
624     ShapeSetEnd(tag);
625     tag = InsertTag(tag,ST_PLACEOBJECT2);
626     cliptags[clippos] = tag;
627     clipshapes[clippos] = shapeid;
628     clipdepths[clippos] = depth++;
629     clippos++;
630     shapeid = -1;
631 }
632
633 void swfoutput_endclip(swfoutput*obj)
634 {
635     if(shape>=0)
636      endshape();
637
638     if(!clippos) {
639         logf("<error> Invalid end of clipping region");
640         return;
641     }
642     clippos--;
643     PlaceObject(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
644 }
645
646 void swfoutput_drawimagefile(struct swfoutput*, char*filename, int sizex,int sizey, 
647         double x1,double y1,
648         double x2,double y2,
649         double x3,double y3,
650         double x4,double y4)
651 {
652     RGBA rgb;
653     SRECT r;
654     int lsid=0;
655     int fsid;
656     int bitid;
657     struct plotxy p1,p2,p3,p4;
658     int myshapeid;
659     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
660     if(x2>xmax) xmax=x2;
661     if(y2>ymax) ymax=y2;
662     if(x2<xmin) xmin=x2;
663     if(y2<ymin) ymin=y2;
664     if(x3>xmax) xmax=x3;
665     if(y3>ymax) ymax=y3;
666     if(x3<xmin) xmin=x3;
667     if(y3<ymin) ymin=y3;
668     if(x4>xmax) xmax=x4;
669     if(y4>ymax) ymax=y4;
670     if(x4<xmin) xmin=x4;
671     if(y4<ymin) ymin=y4;
672     p1.x=x1;
673     p1.y=y1;
674     p2.x=x2;
675     p2.y=y2;
676     p3.x=x3;
677     p3.y=y3;
678     p4.x=x4;
679     p4.y=y4;
680     
681     MATRIX m;
682     m.sx = (int)(65536*20*(x4-x1))/sizex;
683     m.r1 = -(int)(65536*20*(y4-y1))/sizex;
684     m.r0 = (int)(65536*20*(x1-x2))/sizey;
685     m.sy = -(int)(65536*20*(y1-y2))/sizey;
686
687     m.tx = (int)(x1*20);
688     m.ty = (int)(y1*20);
689     
690     if(shape>=0)
691      endshape();
692
693     bitid = ++currentswfid;
694   
695     /* bitmap */
696     tag = InsertTag(tag,ST_DEFINEBITSJPEG2);
697     SetU16(tag, bitid);
698     SetJPEGBits(tag, filename, 85);
699
700     /* shape */
701     myshapeid = ++currentswfid;
702     tag = InsertTag(tag,ST_DEFINESHAPE);
703     NewShape(&shape);
704     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
705     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
706     fsid = ShapeAddBitmapFillStyle(shape,&m,bitid,0);
707     SetU16(tag, myshapeid);
708     r.xmin = (int)(xmin*20);
709     r.ymin = (int)(ymin*20);
710     r.xmax = (int)(xmax*20);
711     r.ymax = (int)(ymax*20);
712     SetRect(tag,&r);
713     SetShapeStyles(tag,shape);
714     ShapeCountBits(shape,NULL,NULL);
715     SetShapeBits(tag,shape);
716     ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
717     swflastx = swflasty = 0;
718     moveto(p1);
719     lineto(p2);
720     lineto(p3);
721     lineto(p4);
722     lineto(p1);
723     /*
724     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
725     ShapeSetLine (tag, shape, (int)(x1*20);
726     ShapeSetLine (tag, shape, x*20,0);
727     ShapeSetLine (tag, shape, 0,-y*20);
728     ShapeSetLine (tag, shape, -x*20,0);*/
729     ShapeSetEnd(tag);
730
731     /* instance */
732     tag = InsertTag(tag,ST_PLACEOBJECT2);
733     ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
734 }
735