write fontinfo tags
[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 #define standardEncodingSize 335
31 extern char *standardEncodingNames[standardEncodingSize];
32
33 int opennewwindow=0;
34 int ignoredraworder=0;
35 int drawonlyshapes=0;
36 int jpegquality=85;
37 static int flag_protected = 0;
38
39 typedef unsigned char u8;
40 typedef unsigned short int u16;
41 typedef unsigned long int u32;
42
43 static int fi;
44 static char* filename = 0;
45 static SWF swf;
46 static TAG *tag;
47 static int currentswfid = 0;
48 static int depth = 1;
49 static int startdepth = 1;
50
51 static SHAPE* shape;
52 static int shapeid = -1;
53 static int textid = -1;
54
55 static int drawmode = -1;
56 static char storefont = 0;
57 static int fillstyleid;
58 static int linestyleid;
59 static int swflastx=0;
60 static int swflasty=0;
61 static int lastwasfill = 0;
62 static char fill = 0;
63 static int sizex;
64 static int sizey;
65 TAG* cliptags[128];
66 int clipshapes[128];
67 u32 clipdepths[128];
68 int clippos = 0;
69
70 int CHARMIDX = 0;
71 int CHARMIDY = 0;
72
73 void startshape(struct swfoutput* obj);
74 void starttext(struct swfoutput* obj);
75 void endshape();
76 void endtext();
77
78 // matrix multiplication. changes p0
79 void transform (plotxy*p0,struct swfmatrix*m)
80 {
81     double x,y;
82     x = m->m11*p0->x+m->m12*p0->y;
83     y = m->m21*p0->x+m->m22*p0->y;
84     p0->x = x + m->m13;
85     p0->y = y + m->m23;
86 }
87
88 // write a move-to command into the swf
89 void moveto(TAG*tag, plotxy p0)
90 {
91     int rx = (int)(p0.x*20);
92     int ry = (int)(p0.y*20);
93     if(rx!=swflastx || ry!=swflasty) {
94       swf_ShapeSetMove (tag, shape, rx,ry);
95     }
96     swflastx=rx;
97     swflasty=ry;
98 }
99
100 // write a line-to command into the swf
101 void lineto(TAG*tag, plotxy p0)
102 {
103     int rx = ((int)(p0.x*20)-swflastx);
104     int ry = ((int)(p0.y*20)-swflasty);
105     /* we can't skip this for rx=0,ry=0, those
106        are plots */
107     swf_ShapeSetLine (tag, shape, rx,ry);
108     swflastx+=rx;
109     swflasty+=ry;
110 }
111
112 // write a spline-to command into the swf
113 void splineto(TAG*tag, plotxy control,plotxy end)
114 {
115     int cx = ((int)(control.x*20)-swflastx);
116     int cy = ((int)(control.y*20)-swflasty);
117     swflastx += cx;
118     swflasty += cy;
119     int ex = ((int)(end.x*20)-swflastx);
120     int ey = ((int)(end.y*20)-swflasty);
121     swflastx += ex;
122     swflasty += ey;
123     swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
124 }
125
126 /* write a line, given two points and the transformation
127    matrix. */
128 void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
129 {
130     transform(&p0,m);
131     transform(&p1,m);
132     moveto(tag, p0);
133     lineto(tag, p1);
134 }
135
136 /* write a cubic (!) spline. This involves calling the approximate()
137    function out of spline.cc to convert it to a quadratic spline.  */
138 void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
139 {
140     double d;
141     struct qspline q[16];
142     int num;
143     int t;
144     transform(&p0,m);
145     transform(&p1,m);
146     transform(&p2,m);
147     transform(&p3,m);
148
149     num = approximate(p0,p1,p2,p3,q);
150     for(t=0;t<num;t++) {
151         moveto(tag,q[t].start);
152         splineto(tag,q[t].control, q[t].end);
153     }
154 }
155
156 /* draw a T1 outline. These are generated by pdf2swf and by t1lib.
157    (representing characters) */
158 void drawpath(TAG*tag, T1_OUTLINE*outline, struct swfmatrix*m)
159 {
160     if(tag->id != ST_DEFINEFONT &&
161         tag->id != ST_DEFINESHAPE &&
162         tag->id != ST_DEFINESHAPE2 &&
163         tag->id != ST_DEFINESHAPE3)
164     {
165         logf("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
166         exit(1);
167     }
168     int log = 0;
169
170     double x=0,y=0;
171     double lastx=0,lasty=0;
172     double firstx=0,firsty=0;
173     int init=1;
174
175     while (outline)
176     {
177         x += (outline->dest.x/(float)0xffff);
178         y += (outline->dest.y/(float)0xffff);
179         if(outline->type == T1_PATHTYPE_MOVE)
180         {
181             if(((int)(lastx*20) != (int)(firstx*20) ||
182                 (int)(lasty*20) != (int)(firsty*20)) &&
183                      fill && !init)
184             {
185                 plotxy p0;
186                 plotxy p1;
187                 p0.x=lastx;
188                 p0.y=lasty;
189                 p1.x=firstx;
190                 p1.y=firsty;
191                 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
192                 line(tag, p0, p1, m);
193             }
194             firstx=x;
195             firsty=y;
196             init = 0;
197         }
198         else if(outline->type == T1_PATHTYPE_LINE) 
199         {
200             plotxy p0;
201             plotxy p1;
202             p0.x=lastx;
203             p0.y=lasty;
204             p1.x=x;
205             p1.y=y;
206             if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
207             line(tag, p0,p1,m);
208         }
209         else if(outline->type == T1_PATHTYPE_BEZIER)
210         {
211             plotxy p0;
212             plotxy p1;
213             plotxy p2;
214             plotxy p3;
215             T1_BEZIERSEGMENT*o2 = (T1_BEZIERSEGMENT*)outline;
216             p0.x=x; 
217             p0.y=y;
218             p1.x=o2->C.x/(float)0xffff+lastx;
219             p1.y=o2->C.y/(float)0xffff+lasty;
220             p2.x=o2->B.x/(float)0xffff+lastx;
221             p2.y=o2->B.y/(float)0xffff+lasty;
222             p3.x=lastx;
223             p3.y=lasty;
224             if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
225             spline(tag,p0,p1,p2,p3,m);
226         } 
227         else {
228          logf("<error> drawpath: unknown outline type:%d\n", outline->type);
229         }
230         lastx=x;
231         lasty=y;
232         outline = outline->link;
233     }
234     if(((int)(lastx*20) != (int)(firstx*20) ||
235         (int)(lasty*20) != (int)(firsty*20)) &&
236              fill)
237     {
238         plotxy p0;
239         plotxy p1;
240         p0.x=lastx;
241         p0.y=lasty;
242         p1.x=firstx;
243         p1.y=firsty;
244         if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
245         line(tag, p0, p1, m);
246     }
247 }
248
249 int colorcompare(RGBA*a,RGBA*b)
250 {
251
252     if(a->r!=b->r ||
253        a->g!=b->g ||
254        a->b!=b->b ||
255        a->a!=b->a) {
256         return 0;
257     }
258     return 1;
259 }
260
261 static const int CHARDATAMAX = 1024;
262 struct chardata {
263     int charid;
264     int fontid;
265     int x;
266     int y;
267     int size;
268     RGBA color;
269 } chardata[CHARDATAMAX];
270 int chardatapos = 0;
271
272 void putcharacters(TAG*tag)
273 {
274     int t;
275     SWFFONT font;
276     RGBA color;
277     color.r = chardata[0].color.r^255;
278     color.g = 0;
279     color.b = 0;
280     color.a = 0;
281     int lastfontid;
282     int lastx;
283     int lasty;
284     int lastsize;
285     int charids[128];
286     int charadvance[128];
287     int charstorepos;
288     int pass;
289     int glyphbits=1; //TODO: can this be zero?
290     int advancebits=1;
291
292     if(tag->id != ST_DEFINETEXT &&
293         tag->id != ST_DEFINETEXT2) {
294         logf("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
295         exit(1);
296     }
297     if(!chardatapos) {
298         logf("<warning> putcharacters called with zero characters");
299     }
300
301     for(pass = 0; pass < 2; pass++)
302     {
303         charstorepos = 0;
304         lastfontid = -1;
305         lastx = CHARMIDX;
306         lasty = CHARMIDY;
307         lastsize = -1;
308
309         if(pass==1)
310         {
311             advancebits++; // add sign bit
312             swf_SetU8(tag, glyphbits);
313             swf_SetU8(tag, advancebits);
314         }
315
316         for(t=0;t<=chardatapos;t++)
317         {
318             if(lastfontid != chardata[t].fontid || 
319                     lastx!=chardata[t].x ||
320                     lasty!=chardata[t].y ||
321                     !colorcompare(&color, &chardata[t].color) ||
322                     charstorepos==127 ||
323                     lastsize != chardata[t].size ||
324                     t == chardatapos)
325             {
326                 if(charstorepos && pass==0)
327                 {
328                     int s;
329                     for(s=0;s<charstorepos;s++)
330                     {
331                         while(charids[s]>=(1<<glyphbits))
332                             glyphbits++;
333                         while(charadvance[s]>=(1<<advancebits))
334                             advancebits++;
335                     }
336                 }
337                 if(charstorepos && pass==1)
338                 {
339                     tag->writeBit = 0; // Q&D
340                     swf_SetBits(tag, 0, 1); // GLYPH Record
341                     swf_SetBits(tag, charstorepos, 7); // number of glyphs
342                     int s;
343                     for(s=0;s<charstorepos;s++)
344                     {
345                         swf_SetBits(tag, charids[s], glyphbits);
346                         swf_SetBits(tag, charadvance[s], advancebits);
347                     }
348                 }
349                 charstorepos = 0;
350
351                 if(pass == 1 && t<chardatapos)
352                 {
353                     RGBA*newcolor=0;
354                     SWFFONT*newfont=0;
355                     int newx = 0;
356                     int newy = 0;
357                     if(lastx != chardata[t].x ||
358                        lasty != chardata[t].y)
359                     {
360                         newx=chardata[t].x;
361                         newy=chardata[t].y;
362                     }
363                     if(!colorcompare(&color, &chardata[t].color)) 
364                     {
365                         color = chardata[t].color;
366                         newcolor = &color;
367                     }
368                     font.id = chardata[t].fontid;
369                     if(lastfontid != chardata[t].fontid || lastsize != chardata[t].size)
370                         newfont = &font;
371
372                     tag->writeBit = 0; // Q&D
373                     swf_TextSetInfoRecord(tag, newfont, chardata[t].size, newcolor, newx,newy);
374                 }
375
376                 lastfontid = chardata[t].fontid;
377                 lastx = chardata[t].x;
378                 lasty = chardata[t].y;
379                 lastsize = chardata[t].size;
380             }
381
382             if(t==chardatapos)
383                     break;
384
385             int advance;
386             int nextt = t==chardatapos-1?t:t+1;
387             int rel = chardata[nextt].x-chardata[t].x;
388             if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
389                advance = rel;
390                lastx=chardata[nextt].x;
391             }
392             else {
393                advance = 0;
394                lastx=chardata[t].x;
395             }
396             charids[charstorepos] = chardata[t].charid;
397             charadvance[charstorepos] = advance;
398             charstorepos ++;
399         }
400     }
401     chardatapos = 0;
402 }
403
404 void putcharacter(struct swfoutput*obj, int fontid, int charid, 
405                     int x,int y, int size)
406 {
407     if(chardatapos == CHARDATAMAX)
408     {
409         endtext();
410         starttext(obj);
411     }
412     chardata[chardatapos].fontid = fontid;
413     chardata[chardatapos].charid = charid;
414     chardata[chardatapos].x = x;
415     chardata[chardatapos].y = y;
416     chardata[chardatapos].color = obj->fillrgb;
417     chardata[chardatapos].size = size;
418     chardatapos++;
419 }
420
421
422 /* process a character. */
423 void drawchar(struct swfoutput*obj, SWFFont*font, char*character, swfmatrix*m)
424 {
425     int usefonts=1;
426     if(m->m12!=0 || m->m21!=0)
427         usefonts=0;
428     if(m->m11 != m->m22)
429         usefonts=0;
430
431     if(usefonts && ! drawonlyshapes)
432     {
433         int charid = font->getSWFCharID(character);
434         if(shapeid>=0)
435             endshape();
436         if(textid<0)
437             starttext(obj);
438         putcharacter(obj, font->swfid, charid,(int)(m->m13*20),(int)(m->m23*20),
439                 (int)(m->m11*20/2+0.5)); //where does the /2 come from?
440     }
441     else
442     {
443         T1_OUTLINE*outline = font->getOutline(character);
444         char* charname = character;
445
446         if(!outline) {
447          logf("<warning> Didn't find %s in current charset (%s)", 
448                  character,font->getName());
449          return;
450         }
451         
452         swfmatrix m2=*m;    
453         m2.m11/=100;
454         m2.m21/=100;
455         m2.m12/=100;
456         m2.m22/=100;
457
458         if(textid>=0)
459             endtext();
460         if(shapeid<0)
461             startshape(obj);
462
463         if(!lastwasfill)
464          swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
465         lastwasfill = 1;
466
467         int lf = fill;
468         fill = 1;
469         drawpath(tag, outline, &m2);
470         fill = lf;
471     }
472 }
473
474 /* draw a curved polygon. */
475 void swfoutput_drawpath(swfoutput*output, T1_OUTLINE*outline, 
476                             struct swfmatrix*m)
477 {
478     if(textid>=0)
479         endtext();
480     if(shapeid<0)
481         startshape(output);
482
483     if(lastwasfill && !fill)
484     {
485      swf_ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
486      lastwasfill = 0;
487     }
488     if(!lastwasfill && fill)
489     {
490      swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
491      lastwasfill = 1;
492     }
493
494     drawpath(tag, outline,m); 
495 }
496
497 /* SWFFont: copy all t1 font outlines to a local 
498    array. */
499 SWFFont::SWFFont(char*name, int id, char*filename)
500 {
501     if(!T1_GetFontName(id))
502         T1_LoadFont(id);
503
504     this->name = strdup(T1_GetFontFileName(id));
505     this->fontid = strdup(name);
506     this->t1id = id;
507
508     char**a= T1_GetAllCharNames(id);
509     int t=0, outlinepos=0;
510     char*map[256];
511     while(a[t])
512         t++;
513  
514     this->charnum = t;
515     if(!t) 
516         return;
517     logf("<verbose> Font %s(%d): Storing %d outlines.\n", name, id, t);
518     
519     outline = (T1_OUTLINE**)malloc(t*sizeof(T1_OUTLINE*));
520     charname = (char**)malloc(t*sizeof(char*));
521     used = (char*)malloc(t*sizeof(char));
522     char2swfcharid = (U16*)malloc(t*2);
523     swfcharid2char = (U16*)malloc(t*2);
524     swfcharpos = 0;
525
526     memset(used,0,t*sizeof(char));
527
528     this->swfid = ++currentswfid;
529
530     
531     t=0;
532     while(*a)
533     {
534         map[t] = *a;
535         a++;
536         t++;
537         if(t==256 || !*a) {
538             int s;
539             for(s=t;s<256;s++)
540                 map[s] = ".notdef";
541
542             int ret = T1_ReencodeFont(id, map);
543             if(ret) {
544              T1_DeleteFont(id);
545              T1_LoadFont(id);
546              int ret = T1_ReencodeFont(id, map);
547              if(ret)
548                fprintf(stderr,"Can't reencode font: (%s) ret:%d\n",filename, ret);
549             }
550
551             // parsecharacters
552             for(s=0;s<t;s++)
553             {
554                 this->outline[outlinepos] = T1_CopyOutline(T1_GetCharOutline(id, s, 100.0, 0));
555                 this->charname[outlinepos] = strdup(T1_GetCharName(id, s));
556                 outlinepos++;
557             }
558             t=0;
559         }
560     }
561 }
562
563 /* free all tables, write out definefont tags */
564 SWFFont::~SWFFont()
565 {
566     int t,usednum=0;
567     int*ptr = (int*)malloc(swfcharpos*sizeof(int));
568
569     for(t=0;t<charnum;t++)
570         if(used[t]) usednum++;
571
572     if(usednum && !drawonlyshapes)
573     {
574         logf("<verbose> Font %s has %d used characters",fontid, usednum);
575         TAG*ftag = swf_InsertTag(swf.firstTag,ST_DEFINEFONT);
576         swf_SetU16(ftag, this->swfid);
577         int initpos = swf_GetDataSize(ftag);
578         swfmatrix m;
579         m.m11 = m.m22 = 1;
580         m.m21 = m.m12 = 0;
581         m.m13 = CHARMIDX;
582         m.m23 = CHARMIDY;
583         for(t=0;t<swfcharpos;t++) 
584         {
585             ptr[t] = swf_GetDataSize(ftag);
586             swf_SetU16(ftag, 0x1234);
587         }
588         for(t=0;t<swfcharpos;t++)
589         {
590             *(U16*)&ftag->data[ptr[t]] = swf_GetDataSize(ftag)-initpos;
591             swflastx=0;
592             swflasty=0;
593             swf_SetU8(ftag,0x10); //0 fill bits, 0 linestyle bits
594             SHAPE s;
595             s.bits.fill = 1;
596             s.bits.line = 0;
597             swf_ShapeSetStyle(ftag,&s,0,1,0);
598             int lastfill = fill;
599             fill = 1;
600             storefont = 1;
601             drawpath(ftag, outline[swfcharid2char[t]],&m);
602             storefont = 0;
603             fill = lastfill;
604             swf_ShapeSetEnd(ftag);
605         }
606         ftag = swf_InsertTag(ftag,ST_DEFINEFONTINFO);
607         swf_SetU16(ftag, this->swfid);
608         swf_SetU8(ftag, strlen(this->fontid));
609         swf_SetBlock(ftag, (U8*)this->fontid, strlen(this->fontid));
610         swf_SetU8(ftag, 0);
611         for(t=0;t<swfcharpos;t++)
612         {
613             int s;
614             char * name = this->charname[this->swfcharid2char[t]];
615             for(s=0;s<256;s++) {
616                 if(standardEncodingNames[s] && 
617                         !strcasecmp(name,standardEncodingNames[s]))
618                     break;
619             }
620             swf_SetU8(ftag, (U8)s);
621         }
622     }
623
624     free(ptr);
625     free(outline);
626     for(t=0;t<charnum;t++)
627         free(charname[t]);
628     free(charname);
629     free(used);
630     free(swfcharid2char);
631     free(char2swfcharid);
632 }
633
634 T1_OUTLINE*SWFFont::getOutline(char*name)
635 {
636     int t;
637     for(t=0;t<this->charnum;t++) {
638         if(!strcmp(this->charname[t],name)) {
639             if(!used[t])
640             {
641                 swfcharid2char[swfcharpos] = t;
642                 char2swfcharid[t] = swfcharpos;
643                 swfcharpos++;
644                 used[t] = 1;
645             }
646             return outline[t];
647         }
648     }
649     return 0;
650 }
651
652 int SWFFont::getSWFCharID(char*name)
653 {
654     int t;
655     for(t=0;t<this->charnum;t++) {
656         if(!strcmp(this->charname[t],name)) {
657             if(!used[t])
658             {
659                 swfcharid2char[swfcharpos] = t;
660                 char2swfcharid[t] = swfcharpos++;
661                 used[t] = 1;
662             }
663             return char2swfcharid[t];
664         }
665     }
666     logf("<warning> Didn't find character '%s' in font '%s'", name, this->name);
667     return 0;
668 }
669
670 char*SWFFont::getName()
671 {
672     return this->name;
673 }
674
675 struct fontlist_t 
676 {
677     SWFFont * font;
678     fontlist_t*next;
679 } *fontlist = 0;
680
681 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
682 void swfoutput_setfont(struct swfoutput*obj, char*fontid, int t1id, char*filename)
683 {
684     fontlist_t*last=0,*iterator;
685     if(obj->font && !strcmp(obj->font->fontid,fontid))
686         return;
687
688     iterator = fontlist;
689     while(iterator) {
690         if(!strcmp(iterator->font->fontid,fontid))
691             break;
692         last = iterator;
693         iterator = iterator->next;
694     }
695     if(iterator) 
696     {
697         obj->font = iterator->font;
698         return ;
699     }
700
701     if(t1id<0) {
702         logf("<error> internal error: t1id:%d, fontid:%s\n", t1id,fontid);
703     }
704     
705     SWFFont*font = new SWFFont(fontid, t1id, filename);
706     iterator = new fontlist_t;
707     iterator->font = font;
708     iterator->next = 0;
709
710     if(last) 
711         last->next = iterator;
712     else 
713         fontlist = iterator;
714     obj->font = font;
715 }
716
717 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
718 {
719     fontlist_t *iterator = fontlist;
720     while(iterator) {
721         if(!strcmp(iterator->font->fontid,fontid))
722             return 1;
723         iterator = iterator->next;
724     }
725     return 0;
726 }
727
728 /* set's the matrix which is to be applied to characters drawn by
729    swfoutput_drawchar() */
730 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
731                                                   double m21,double m22)
732 {
733     if(obj->fontm11 == m11 &&
734        obj->fontm12 == m12 &&
735        obj->fontm21 == m21 &&
736        obj->fontm22 == m22)
737         return;
738 //    if(textid>=0)
739 //      endtext();
740     obj->fontm11 = m11;
741     obj->fontm12 = m12;
742     obj->fontm21 = m21;
743     obj->fontm22 = m22;
744 }
745
746 /* draws a character at x,y. */
747 void swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character) 
748 {
749     swfmatrix m;
750     m.m11 = obj->fontm11;
751     m.m12 = obj->fontm12;
752     m.m21 = obj->fontm21;
753     m.m22 = obj->fontm22;
754     m.m13 = x;
755     m.m23 = y;
756     drawchar(obj, obj->font, character, &m);
757 }
758
759 /* initialize the swf writer */
760 void swfoutput_init(struct swfoutput* obj, char*_filename, int _sizex, int _sizey) 
761 {
762   GLYPH *glyph;
763   RGBA rgb;
764   SRECT r;
765   memset(obj, 0, sizeof(struct swfoutput));
766   filename = _filename;
767   sizex = _sizex;
768   sizey = _sizey;
769
770   logf("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
771
772   obj->font = 0;
773   
774   memset(&swf,0x00,sizeof(SWF));
775
776   swf.fileVersion    = 4;
777   swf.frameRate      = 0x0040; // 1 frame per 4 seconds
778   swf.movieSize.xmax = 20*sizex;
779   swf.movieSize.ymax = 20*sizey;
780   
781   swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
782   tag = swf.firstTag;
783   rgb.r = 0xff;
784   rgb.g = 0xff;
785   rgb.b = 0xff;
786   swf_SetRGB(tag,&rgb);
787   if(flag_protected)  // good practice! /r
788     tag = swf_InsertTag(tag, ST_PROTECT);
789   depth = 1;
790   startdepth = depth;
791 }
792
793 void swfoutput_setprotected() //write PROTECT tag
794 {
795   flag_protected = 1;
796 }
797
798 void startshape(struct swfoutput*obj)
799 {
800   RGBA rgb;
801   SRECT r;
802
803   if(textid>=0)
804       endtext();
805
806   tag = swf_InsertTag(tag,ST_DEFINESHAPE);
807
808   swf_ShapeNew(&shape);
809   linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
810   rgb.r = obj->fillrgb.r;
811   rgb.g = obj->fillrgb.g;
812   rgb.b = obj->fillrgb.b;
813   fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
814
815   shapeid = ++currentswfid;
816   swf_SetU16(tag,shapeid);  // ID
817
818   r.xmin = 0;
819   r.ymin = 0;
820   r.xmax = 20*sizex;
821   r.ymax = 20*sizey;
822   
823   swf_SetRect(tag,&r);
824
825   swf_SetShapeStyles(tag,shape);
826   swf_ShapeCountBits(shape,NULL,NULL);
827   swf_SetShapeBits(tag,shape);
828
829   swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
830   swflastx=swflasty=0;
831   lastwasfill = 0;
832 }
833
834 void starttext(struct swfoutput*obj)
835 {
836   SRECT r;
837   MATRIX m;
838   if(shapeid>=0)
839       endshape();
840   tag = swf_InsertTag(tag,ST_DEFINETEXT);
841   textid = ++currentswfid;
842   swf_SetU16(tag, textid);
843
844   r.xmin = 0;
845   r.ymin = 0;
846   r.xmax = 20*sizex;
847   r.ymax = 20*sizey;
848   
849   swf_SetRect(tag,&r);
850
851   m.sx = 65536;
852   m.sy = 65536;
853   m.r0 = 0;
854   m.r1 = 0;
855   m.tx = 0;
856   m.ty = 0;
857  
858   swf_SetMatrix(tag,&m);
859   swflastx=swflasty=0;
860 }
861
862 void endshape()
863 {
864     if(shapeid<0) 
865         return;
866     swf_ShapeSetEnd(tag);
867     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
868     swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
869     shapeid = -1;
870 }
871
872 void endtext()
873 {
874     if(textid<0)
875         return;
876     putcharacters(tag);
877     swf_SetU8(tag,0);
878     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
879     swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
880     textid = -1;
881 }
882
883 void endpage(struct swfoutput*obj)
884 {
885     if(shapeid>=0)
886       endshape();
887     if(textid>=0)
888       endtext();
889     while(clippos)
890         swfoutput_endclip(obj);
891     tag = swf_InsertTag(tag,ST_SHOWFRAME);
892 }
893
894 void swfoutput_newpage(struct swfoutput*obj)
895 {
896     endpage(obj);
897
898     for(depth--;depth>=startdepth;depth--) {
899         tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
900         swf_SetU16(tag,depth);
901     }
902
903     depth = 1;
904     startdepth = depth;
905 }
906
907 /* "destroy" like in (oo-terminology) "destructor". Perform cleaning
908    up, complete the swf, and write it out. */
909 void swfoutput_destroy(struct swfoutput* obj) 
910 {
911     endpage(obj);
912     fontlist_t *tmp,*iterator = fontlist;
913     while(iterator) {
914         delete iterator->font;
915         iterator->font = 0;
916         tmp = iterator;
917         iterator = iterator->next;
918         delete tmp;
919     }
920
921     T1_CloseLib();
922     if(!filename) 
923         return;
924     if(filename)
925      fi = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0777);
926     else
927      fi = 1; // stdout
928     
929     if(fi<=0) {
930      logf("<fatal> Could not create \"%s\". ", filename);
931      exit(1);
932     }
933  
934     tag = swf_InsertTag(tag,ST_END);
935
936     if FAILED(swf_WriteSWF(fi,&swf)) 
937      logf("<error> WriteSWF() failed.\n");
938     if(filename)
939      close(fi);
940     logf("<notice> SWF written\n");
941 }
942
943 void swfoutput_setdrawmode(swfoutput* obj, int mode)
944 {
945     drawmode = mode;
946     if(mode == DRAWMODE_FILL)
947      fill = 1;
948     else if(mode == DRAWMODE_EOFILL)
949      fill = 1;
950     else if(mode == DRAWMODE_STROKE)
951      fill = 0;
952     else if(mode == DRAWMODE_CLIP)
953      fill = 1;
954     else if(mode == DRAWMODE_EOCLIP)
955      fill = 1;
956 }
957
958 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
959 {
960     if(obj->fillrgb.r == r &&
961        obj->fillrgb.g == g &&
962        obj->fillrgb.b == b &&
963        obj->fillrgb.a == a) return;
964
965     if(shapeid>=0)
966      endshape();
967     obj->fillrgb.r = r;
968     obj->fillrgb.g = g;
969     obj->fillrgb.b = b;
970     obj->fillrgb.a = a;
971 }
972
973 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
974 {
975     if(obj->strokergb.r == r &&
976        obj->strokergb.g == g &&
977        obj->strokergb.b == b &&
978        obj->strokergb.a == a) return;
979
980     if(shapeid>=0)
981      endshape();
982     obj->strokergb.r = r;
983     obj->strokergb.g = g;
984     obj->strokergb.b = b;
985     obj->strokergb.a = a;
986 }
987
988 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
989 {
990     if(obj->linewidth == (u16)(linewidth*20))
991         return;
992
993     if(shapeid>=0)
994      endshape();
995     obj->linewidth = (u16)(linewidth*20);
996 }
997
998
999 void swfoutput_startclip(swfoutput*obj, T1_OUTLINE*outline, struct swfmatrix*m)
1000 {
1001     if(textid>=0)
1002      endtext();
1003     if(shapeid>=0)
1004      endshape();
1005
1006     if(clippos >= 127)
1007     {
1008         logf("<warning> Too many clip levels.");
1009         clippos --;
1010     } 
1011     
1012     startshape(obj);
1013     int olddrawmode = drawmode;
1014     swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1015     swfoutput_drawpath(obj, outline, m);
1016     swf_ShapeSetEnd(tag);
1017     swfoutput_setdrawmode(obj, olddrawmode);
1018
1019     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1020     cliptags[clippos] = tag;
1021     clipshapes[clippos] = shapeid;
1022     clipdepths[clippos] = depth++;
1023     clippos++;
1024     shapeid = -1;
1025 }
1026
1027 void swfoutput_endclip(swfoutput*obj)
1028 {
1029     if(textid>=0)
1030      endtext();
1031     if(shapeid>=0)
1032      endshape();
1033
1034     if(!clippos) {
1035         logf("<error> Invalid end of clipping region");
1036         return;
1037     }
1038     clippos--;
1039     swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1040 }
1041
1042 void drawlink(struct swfoutput*obj, ActionTAG*, swfcoord*points);
1043
1044 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1045 {
1046     ActionTAG* actions;
1047     
1048     if(shapeid>=0)
1049      endshape();
1050     if(textid>=0)
1051      endtext();
1052     
1053     actions = swf_ActionStart();
1054     if(opennewwindow)
1055       action_GetUrl(url, "_parent");
1056     else
1057       action_GetUrl(url, "_this");
1058       action_End();
1059     swf_ActionEnd();
1060     
1061     drawlink(obj, actions, points);
1062 }
1063 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1064 {
1065     ActionTAG* actions;
1066
1067     if(shapeid>=0)
1068      endshape();
1069     if(textid>=0)
1070      endtext();
1071    
1072     actions = swf_ActionStart();
1073       action_GotoFrame(page);
1074       action_End();
1075     swf_ActionEnd();
1076
1077     drawlink(obj, actions, points);
1078 }
1079
1080 void drawlink(struct swfoutput*obj, ActionTAG*actions, swfcoord*points)
1081 {
1082     RGBA rgb;
1083     SRECT r;
1084     int lsid=0;
1085     int fsid;
1086     struct plotxy p1,p2,p3,p4;
1087     int myshapeid;
1088     int myshapeid2;
1089     double xmin,ymin;
1090     double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1091     int t;
1092     int buttonid = ++currentswfid;
1093     for(t=1;t<4;t++)
1094     {
1095         if(points[t].x>xmax) xmax=points[t].x;
1096         if(points[t].y>ymax) ymax=points[t].y;
1097         if(points[t].x<xmin) xmin=points[t].x;
1098         if(points[t].y<ymin) ymin=points[t].y;
1099     }
1100     p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y; 
1101     p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1102     
1103     /* shape */
1104     myshapeid = ++currentswfid;
1105     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1106     swf_ShapeNew(&shape);
1107     rgb.r = rgb.b = rgb.a = rgb.g = 0; 
1108     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1109     swf_SetU16(tag, myshapeid);
1110     r.xmin = (int)(xmin*20);
1111     r.ymin = (int)(ymin*20);
1112     r.xmax = (int)(xmax*20);
1113     r.ymax = (int)(ymax*20);
1114     swf_SetRect(tag,&r);
1115     swf_SetShapeStyles(tag,shape);
1116     swf_ShapeCountBits(shape,NULL,NULL);
1117     swf_SetShapeBits(tag,shape);
1118     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1119     swflastx = swflasty = 0;
1120     moveto(tag, p1);
1121     lineto(tag, p2);
1122     lineto(tag, p3);
1123     lineto(tag, p4);
1124     lineto(tag, p1);
1125     swf_ShapeSetEnd(tag);
1126
1127     /* shape2 */
1128     myshapeid2 = ++currentswfid;
1129     tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1130     swf_ShapeNew(&shape);
1131     rgb.r = rgb.b = rgb.a = rgb.g = 255;
1132     rgb.a = 40;
1133     fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1134     swf_SetU16(tag, myshapeid2);
1135     r.xmin = (int)(xmin*20);
1136     r.ymin = (int)(ymin*20);
1137     r.xmax = (int)(xmax*20);
1138     r.ymax = (int)(ymax*20);
1139     swf_SetRect(tag,&r);
1140     swf_SetShapeStyles(tag,shape);
1141     swf_ShapeCountBits(shape,NULL,NULL);
1142     swf_SetShapeBits(tag,shape);
1143     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1144     swflastx = swflasty = 0;
1145     moveto(tag, p1);
1146     lineto(tag, p2);
1147     lineto(tag, p3);
1148     lineto(tag, p4);
1149     lineto(tag, p1);
1150     swf_ShapeSetEnd(tag);
1151
1152     tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1153     swf_SetU16(tag,buttonid); //id
1154     swf_ButtonSetFlags(tag, 0); //menu=no
1155     swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1156     swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1157     swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1158     swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1159     swf_SetU8(tag,0);
1160     swf_SetActions(tag,actions);
1161     swf_SetU8(tag,0);
1162     
1163     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1164     swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1165 }
1166
1167 void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey, 
1168         double x1,double y1,
1169         double x2,double y2,
1170         double x3,double y3,
1171         double x4,double y4)
1172 {
1173     RGBA rgb;
1174     SRECT r;
1175     int lsid=0;
1176     int fsid;
1177     struct plotxy p1,p2,p3,p4;
1178     int myshapeid;
1179     double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1180     if(x2>xmax) xmax=x2;
1181     if(y2>ymax) ymax=y2;
1182     if(x2<xmin) xmin=x2;
1183     if(y2<ymin) ymin=y2;
1184     if(x3>xmax) xmax=x3;
1185     if(y3>ymax) ymax=y3;
1186     if(x3<xmin) xmin=x3;
1187     if(y3<ymin) ymin=y3;
1188     if(x4>xmax) xmax=x4;
1189     if(y4>ymax) ymax=y4;
1190     if(x4<xmin) xmin=x4;
1191     if(y4<ymin) ymin=y4;
1192     p1.x=x1;
1193     p1.y=y1;
1194     p2.x=x2;
1195     p2.y=y2;
1196     p3.x=x3;
1197     p3.y=y3;
1198     p4.x=x4;
1199     p4.y=y4;
1200     
1201     MATRIX m;
1202     m.sx = (int)(65536*20*(x4-x1))/sizex;
1203     m.r1 = -(int)(65536*20*(y4-y1))/sizex;
1204     m.r0 = (int)(65536*20*(x1-x2))/sizey;
1205     m.sy = -(int)(65536*20*(y1-y2))/sizey;
1206
1207     m.tx = (int)(x1*20);
1208     m.ty = (int)(y1*20);
1209   
1210     /* shape */
1211     myshapeid = ++currentswfid;
1212     tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1213     swf_ShapeNew(&shape);
1214     //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1215     //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1216     fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,0);
1217     swf_SetU16(tag, myshapeid);
1218     r.xmin = (int)(xmin*20);
1219     r.ymin = (int)(ymin*20);
1220     r.xmax = (int)(xmax*20);
1221     r.ymax = (int)(ymax*20);
1222     swf_SetRect(tag,&r);
1223     swf_SetShapeStyles(tag,shape);
1224     swf_ShapeCountBits(shape,NULL,NULL);
1225     swf_SetShapeBits(tag,shape);
1226     swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1227     swflastx = swflasty = 0;
1228     moveto(tag, p1);
1229     lineto(tag, p2);
1230     lineto(tag, p3);
1231     lineto(tag, p4);
1232     lineto(tag, p1);
1233     /*
1234     ShapeMoveTo  (tag, shape, (int)(x1*20),(int)(y1*20));
1235     ShapeSetLine (tag, shape, (int)(x1*20);
1236     ShapeSetLine (tag, shape, x*20,0);
1237     ShapeSetLine (tag, shape, 0,-y*20);
1238     ShapeSetLine (tag, shape, -x*20,0);*/
1239     swf_ShapeSetEnd(tag);
1240
1241     /* instance */
1242     tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1243     swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1244 }
1245
1246 int swfoutput_drawimagejpeg(struct swfoutput*obj, char*filename, int sizex,int sizey, 
1247         double x1,double y1,
1248         double x2,double y2,
1249         double x3,double y3,
1250         double x4,double y4)
1251 {
1252     if(shapeid>=0)
1253      endshape();
1254     if(textid>=0)
1255      endtext();
1256
1257     int bitid = ++currentswfid;
1258     tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1259     swf_SetU16(tag, bitid);
1260     swf_SetJPEGBits(tag, filename, jpegquality);
1261
1262     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1263     return bitid;
1264 }
1265
1266 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey, 
1267         double x1,double y1,
1268         double x2,double y2,
1269         double x3,double y3,
1270         double x4,double y4)
1271 {
1272     if(shapeid>=0)
1273      endshape();
1274     if(textid>=0)
1275      endtext();
1276
1277     int bitid = ++currentswfid;
1278     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
1279     swf_SetU16(tag, bitid);
1280     swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT);
1281     
1282     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1283     return bitid;
1284 }
1285
1286 int swfoutput_drawimagelossless256(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey, 
1287         double x1,double y1,
1288         double x2,double y2,
1289         double x3,double y3,
1290         double x4,double y4)
1291 {
1292     if(shapeid>=0)
1293      endshape();
1294     if(textid>=0)
1295      endtext();
1296
1297     int bitid = ++currentswfid;
1298     tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
1299     swf_SetU16(tag, bitid);
1300     swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, 256);
1301   
1302     drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1303     return bitid;
1304 }
1305
1306 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey, 
1307         double x1,double y1,
1308         double x2,double y2,
1309         double x3,double y3,
1310         double x4,double y4)
1311 {
1312     if(shapeid>=0)
1313      endshape();
1314     if(textid>=0)
1315      endtext();
1316
1317     drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1318 }
1319