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