added glyph pairing to align zone detector
[swftools.git] / lib / modules / swftext.c
1 /* swftext.c
2
3    Text and font routines
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9    Copyright (c) 2003,2004 Matthias Kramm
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
24
25 #include "../rfxswf.h"
26
27 U32 readUTF8char(U8 ** text)
28 {
29     U32 c = 0;
30     if (!(*(*text) & 0x80))
31         return *((*text)++);
32
33     /* 0000 0080-0000 07FF   110xxxxx 10xxxxxx */
34     if (((*text)[0] & 0xe0) == 0xc0 && (*text)[1]) {
35         c = ((*text)[0] & 0x1f) << 6 | ((*text)[1] & 0x3f);
36         (*text) += 2;
37         return c;
38     }
39     /* 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx */
40     if (((*text)[0] & 0xf0) == 0xe0 && (*text)[1] && (*text)[2]) {
41         c = ((*text)[0] & 0x0f) << 12 | ((*text)[1] & 0x3f) << 6 | ((*text)[2] & 0x3f);
42         (*text) += 3;
43         return c;
44     }
45     /* 0001 0000-001F FFFF   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
46     if (((*text)[0] & 0xf8) == 0xf0 && (*text)[1] && (*text)[2]
47         && (*text)[3]) {
48         c = ((*text)[0] & 0x07) << 18 | ((*text)[1] & 0x3f) << 12 | ((*text)[2] & 0x3f) << 6 | ((*text)[3] & 0x3f);
49         (*text) += 4;
50         return c;
51     }
52     /* 0020 0000-03FF FFFF   111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
53     if (((*text)[0] & 0xfc) == 0xf8 && (*text)[1] && (*text)[2]
54         && (*text)[3]
55         && (*text)[4]) {
56         c = ((*text)[0] & 0x03) << 24 | ((*text)[1] & 0x3f) << 18 | ((*text)[2] & 0x3f) << 12 | ((*text)[3] & 0x3f) << 6 | ((*text)[4] & 0x3f);
57         (*text) += 5;
58         return c;
59     }
60     /* 0400 0000-7FFF FFFF   1111110x 10xxxxxx ... 10xxxxxx */
61     if (((*text)[0] & 0xfe) == 0xfc && (*text)[1] && (*text)[2]
62         && (*text)[3]
63         && (*text)[4] && (*text)[5]) {
64         c = ((*text)[0] & 0x01) << 30 | ((*text)[1] & 0x3f) << 24 |
65             ((*text)[2] & 0x3f) << 18 | ((*text)[3] & 0x3f) << 12 | ((*text)[4] & 0x3f) << 6 | ((*text)[5] & 0x3f) << 6;
66         (*text) += 6;
67         return c;
68     }
69     return *((*text)++);
70 }
71
72 #define TF_TEXTCONTROL  0x80
73 #define TF_HASFONT      0x08
74 #define TF_HASCOLOR     0x04
75 #define TF_HASYOFFSET   0x02
76 #define TF_HASXOFFSET   0x01
77
78 #define FF_WIDECODES    0x01
79 #define FF_BOLD         0x02
80 #define FF_ITALIC       0x04
81 #define FF_ANSI         0x08
82 #define FF_SHIFTJIS     0x10
83 #define FF_UNICODE      0x20
84
85 #define FF2_BOLD         0x01
86 #define FF2_ITALIC       0x02
87 #define FF2_WIDECODES    0x04
88 #define FF2_WIDEOFFSETS  0x08
89 #define FF2_ANSI         0x10
90 #define FF2_UNICODE      0x20
91 #define FF2_SHIFTJIS     0x40
92 #define FF2_LAYOUT       0x80
93
94 int swf_FontIsItalic(SWFFONT * f)
95 {
96     return f->style & FONT_STYLE_ITALIC;
97 }
98
99 int swf_FontIsBold(SWFFONT * f)
100 {
101     return f->style & FONT_STYLE_BOLD;
102 }
103
104 static const int WRITEFONTID = 0x4e46;  // font id for WriteFont and ReadFont
105
106 int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*self)
107 {
108     int n;
109     TAG *t;
110     if (!swf)
111         return -1;
112     t = swf->firstTag;
113     n = 0;
114
115     while (t) {
116         if (swf_isFontTag(t)) {
117             n++;
118             if (FontCallback) {
119                 U16 id;
120                 int l;
121                 U8 s[257];
122                 s[0] = 0;
123                 swf_SetTagPos(t, 0);
124
125                 id = swf_GetU16(t);
126                 if (swf_GetTagID(t) == ST_DEFINEFONT2 || swf_GetTagID(t) == ST_DEFINEFONTINFO || swf_GetTagID(t) == ST_DEFINEFONTINFO2) {
127                     swf_GetU16(t);
128                     l = swf_GetU8(t);
129                     swf_GetBlock(t, s, l);
130                     s[l] = 0;
131                 }
132
133                 (FontCallback) (self, id, s);
134             }
135         }
136         t = swf_NextTag(t);
137     }
138     return n;
139 }
140
141 int swf_FontExtract_DefineFont(int id, SWFFONT * f, TAG * t)
142 {
143     U16 fid;
144     swf_SetTagPos(t, 0);
145
146     fid = swf_GetU16(t);
147     if ((!id) || (id == fid)) {
148         U16 of;
149         int n, i;
150
151         id = fid;
152         f->version = 1;
153         f->id = fid;
154
155         of = swf_GetU16(t);
156         n = of / 2;
157         f->numchars = n;
158         f->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH) * n);
159
160         for (i = 1; i < n; i++)
161             swf_GetU16(t);
162         for (i = 0; i < n; i++)
163             swf_GetSimpleShape(t, &f->glyph[i].shape);
164     }
165     return id;
166 }
167
168 int swf_FontExtract_DefineFontInfo(int id, SWFFONT * f, TAG * t)
169 {
170     U16 fid;
171     U16 maxcode;
172     U8 flags;
173     swf_SetTagPos(t, 0);
174
175     fid = swf_GetU16(t);
176     if (fid == id) {
177         U8 l = swf_GetU8(t);
178         int i;
179
180         if (f->version > 1) {
181             /* Especially with Flash MX, DefineFont2 may have FontInfo fields,
182                too. However, they only add little information to what's already
183                inside the DefineFont2 tag */
184             return id;
185         }
186
187         if (f->name)
188             rfx_free(f->name);
189
190         f->name = (U8 *) rfx_alloc(l + 1);
191         swf_GetBlock(t, f->name, l);
192         f->name[l] = 0;
193
194         flags = swf_GetU8(t);
195         if (flags & 2)
196             f->style |= FONT_STYLE_BOLD;
197         if (flags & 4)
198             f->style |= FONT_STYLE_ITALIC;
199         if (flags & 8)
200             f->encoding |= FONT_ENCODING_ANSI;
201         if (flags & 16)
202             f->encoding |= FONT_ENCODING_SHIFTJIS;
203         if (flags & 32)
204             f->encoding |= FONT_ENCODING_UNICODE;
205
206         if (t->id == ST_DEFINEFONTINFO2) {
207             f->language = swf_GetU8(t);
208         }
209
210         f->glyph2ascii = (U16 *) rfx_alloc(sizeof(U16) * f->numchars);
211         maxcode = 0;
212         for (i = 0; i < f->numchars; i++) {
213             f->glyph2ascii[i] = ((flags & FF_WIDECODES) ? swf_GetU16(t) : swf_GetU8(t));
214             if (f->glyph2ascii[i] > maxcode)
215                 maxcode = f->glyph2ascii[i];
216         }
217         maxcode++;
218         if (maxcode < 256)
219             maxcode = 256;
220         f->maxascii = maxcode;
221         f->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
222         memset(f->ascii2glyph, -1, sizeof(int) * maxcode);
223
224         for (i = 0; i < f->numchars; i++)
225             f->ascii2glyph[f->glyph2ascii[i]] = i;
226     }
227     return id;
228 }
229
230 int swf_FontExtract_GlyphNames(int id, SWFFONT * f, TAG * tag)
231 {
232     U16 fid;
233     swf_SetTagPos(tag, 0);
234
235     fid = swf_GetU16(tag);
236
237     if (fid == id) {
238         int num = swf_GetU16(tag);
239         int t;
240         f->glyphnames = (char**)rfx_alloc(sizeof(char *) * num);
241         for (t = 0; t < num; t++) {
242             f->glyphnames[t] = strdup(swf_GetString(tag));
243         }
244     }
245     return id;
246 }
247
248
249 int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
250 {
251     int t, glyphcount;
252     int maxcode;
253     int fid;
254     U32 offset_start;
255     U32 *offset;
256     U8 flags1, langcode, namelen;
257     swf_SetTagPos(tag, 0);
258     font->version = 2;
259     fid = swf_GetU16(tag);
260     if (id && id != fid)
261         return id;
262     font->id = fid;
263     flags1 = swf_GetU8(tag);
264     langcode = swf_GetU8(tag);  //reserved flags
265
266     if (flags1 & 1)
267         font->style |= FONT_STYLE_BOLD;
268     if (flags1 & 2)
269         font->style |= FONT_STYLE_ITALIC;
270     if (flags1 & 16)
271         font->encoding |= FONT_ENCODING_ANSI;
272     if (flags1 & 32)
273         font->encoding |= FONT_ENCODING_UNICODE;
274     if (flags1 & 64)
275         font->encoding |= FONT_ENCODING_SHIFTJIS;
276
277     namelen = swf_GetU8(tag);
278     font->name = (U8 *) rfx_alloc(namelen + 1);
279     font->name[namelen] = 0;
280     swf_GetBlock(tag, font->name, namelen);
281     font->version = 2;
282     glyphcount = swf_GetU16(tag);
283     font->numchars = glyphcount;
284
285     font->glyph = (SWFGLYPH *) rfx_calloc(sizeof(SWFGLYPH) * glyphcount);
286     font->glyph2ascii = (U16 *) rfx_calloc(sizeof(U16) * glyphcount);
287
288     offset = (U32*)rfx_calloc(sizeof(U32)*(glyphcount+1));
289     offset_start = tag->pos;
290
291     if (flags1 & 8) {           // wide offsets
292         for (t = 0; t < glyphcount; t++)
293             offset[t] = swf_GetU32(tag);        //offset[t]
294
295         if (glyphcount)         /* this _if_ is not in the specs */
296             offset[glyphcount] = swf_GetU32(tag);       // fontcodeoffset
297         else
298             offset[glyphcount] = tag->pos;
299     } else {
300         for (t = 0; t < glyphcount; t++)
301             offset[t] = swf_GetU16(tag);        //offset[t]
302
303         if (glyphcount)         /* this _if_ is not in the specs */
304             offset[glyphcount] = swf_GetU16(tag);       // fontcodeoffset
305         else
306             offset[glyphcount] = tag->pos;
307     }
308     for (t = 0; t < glyphcount; t++) {
309         swf_SetTagPos(tag, offset[t]+offset_start);
310         swf_GetSimpleShape(tag, &(font->glyph[t].shape));
311     }
312
313     if(glyphcount)
314         swf_SetTagPos(tag, offset[glyphcount]+offset_start);
315
316     free(offset);
317
318     maxcode = 0;
319     for (t = 0; t < glyphcount; t++) {
320         int code;
321         if (flags1 & 4)         // wide codes (always on for definefont3)
322             code = swf_GetU16(tag);
323         else
324             code = swf_GetU8(tag);
325         font->glyph2ascii[t] = code;
326         if (code > maxcode)
327             maxcode = code;
328     }
329     maxcode++;
330     if (maxcode < 256)
331         maxcode = 256;
332     font->maxascii = maxcode;
333     font->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
334     memset(font->ascii2glyph, -1, sizeof(int) * maxcode);
335     for (t = 0; t < glyphcount; t++) {
336         font->ascii2glyph[font->glyph2ascii[t]] = t;
337     }
338
339     if (flags1 & 128) {         // has layout
340         U16 kerningcount;
341         font->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
342         font->layout->ascent = swf_GetU16(tag);
343         font->layout->descent = swf_GetU16(tag);
344         font->layout->leading = swf_GetU16(tag);
345         for (t = 0; t < glyphcount; t++) {
346             S16 advance = swf_GetS16(tag);
347             font->glyph[t].advance = advance;
348         }
349         font->layout->bounds = (SRECT*)rfx_alloc(glyphcount * sizeof(SRECT));
350         for (t = 0; t < glyphcount; t++) {
351             swf_ResetReadBits(tag);
352             swf_GetRect(tag, &font->layout->bounds[t]);
353         }
354
355         kerningcount = swf_GetU16(tag);
356         font->layout->kerningcount = kerningcount;
357
358         font->layout->kerning = (SWFKERNING *) rfx_alloc(sizeof(SWFKERNING) * kerningcount);
359         if (kerningcount) {
360             font->layout->kerning = (SWFKERNING*)rfx_alloc(sizeof(*font->layout->kerning) * kerningcount);
361             for (t = 0; t < kerningcount; t++) {
362                 if (flags1 & 4) {       // wide codes
363                     font->layout->kerning[t].char1 = swf_GetU16(tag);
364                     font->layout->kerning[t].char2 = swf_GetU16(tag);
365                 } else {
366                     font->layout->kerning[t].char1 = swf_GetU8(tag);
367                     font->layout->kerning[t].char2 = swf_GetU8(tag);
368                 }
369                 font->layout->kerning[t].adjustment = swf_GetS16(tag);
370             }
371         }
372     }
373     return font->id;
374 }
375
376 int swf_FontExtract_DefineFontAlignZones(int id, SWFFONT * font, TAG * tag)
377 {
378     U16 fid;
379     swf_SetTagPos(tag, 0);
380     fid = swf_GetU16(tag);
381     
382     if (fid == id) {
383         font->alignzone_flags = swf_GetU8(tag);
384         font->alignzones = rfx_calloc(sizeof(ALIGNZONE)*font->numchars);
385         int i=0;
386         while(tag->pos < tag->len) {
387             if(i>=font->numchars)
388                 break;
389             int nr = swf_GetU8(tag); // should be 2
390             if(nr!=1 && nr!=2) {
391                 fprintf(stderr, "rfxswf: Can't parse alignzone tags with %d zones", nr);
392                 break;
393             }
394             U16 x = swf_GetU16(tag);
395             U16 y = swf_GetU16(tag);
396             U16 dx = (nr==2)?swf_GetU16(tag):0xffff;
397             U16 dy = (nr==2)?swf_GetU16(tag):0xffff;
398             U8 xy = swf_GetU8(tag);
399
400 #ifdef DEBUG_RFXSWF
401             if((!(xy&1) && (x!=0 || (dx!=0 && dx!=0xffff))) ||
402                (!(xy&2) && (y!=0 || (dy!=0 && dy!=0xffff)))) {
403                 fprintf(stderr, "Warning: weird combination of alignzone bits and values (%d x:%04x-%04x y:%04x-%04x)\n", xy,
404                         x,dx,y,dy);
405             }
406 #endif
407             if(!(xy&1)) {
408                 x = 0xffff;
409                 dx = 0xffff;
410             } else if(!(xy&2)) {
411                 y = 0xffff;
412                 dy = 0xffff;
413             }
414             font->alignzones[i].x = x;
415             font->alignzones[i].y = y;
416             font->alignzones[i].dx = dx;
417             font->alignzones[i].dy = dy;
418             i++;
419         }
420     }
421     return id;
422 }
423
424
425 #define FEDTJ_PRINT  0x01
426 #define FEDTJ_MODIFY 0x02
427 #define FEDTJ_CALLBACK 0x04
428
429 static int
430 swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
431                                    void (*callback) (void *self,
432                                                      int *chars, int *xpos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
433 {
434     U16 cid;
435     SRECT r;
436     MATRIX m;
437     U8 gbits, abits;
438     int fid = -1;
439     RGBA color;
440     int x = 0, y = 0;
441     int fontsize = 0;
442
443     memset(&color, 0, sizeof(color));
444
445     swf_SetTagPos(t, 0);
446
447     cid = swf_GetU16(t);
448     swf_GetRect(t, &r);
449     swf_GetMatrix(t, &m);
450     gbits = swf_GetU8(t);
451     abits = swf_GetU8(t);
452
453     while (1) {
454         int flags, num;
455         flags = swf_GetU8(t);
456         if (!flags)
457             break;
458
459         if (flags & TF_TEXTCONTROL) {
460             if (flags & TF_HASFONT)
461                 fid = swf_GetU16(t);
462             if (flags & TF_HASCOLOR) {
463                 color.r = swf_GetU8(t); // rgb
464                 color.g = swf_GetU8(t);
465                 color.b = swf_GetU8(t);
466                 if (swf_GetTagID(t) == ST_DEFINETEXT2)
467                     color.a = swf_GetU8(t);
468                 else
469                     color.a = 255;
470             }
471             if (flags & TF_HASXOFFSET)
472                 x = swf_GetS16(t);
473             if (flags & TF_HASYOFFSET)
474                 y = swf_GetS16(t);
475             if (flags & TF_HASFONT)
476                 fontsize = swf_GetU16(t);
477         }
478
479         num = swf_GetU8(t);
480         if (!num)
481             break;
482
483         {
484             int i;
485             int buf[256];
486             int advance[256];
487             int xpos = 0;
488             for (i = 0; i < num; i++) {
489                 int glyph;
490                 int adv = 0;
491                 advance[i] = xpos;
492                 glyph = swf_GetBits(t, gbits);
493                 adv = swf_GetBits(t, abits);
494                 xpos += adv;
495
496                 if (id == fid) {
497                     if (jobs & FEDTJ_PRINT) {
498                         int code = f->glyph2ascii[glyph];
499                         printf("%lc", code);
500                     }
501                     if (jobs & FEDTJ_MODIFY)
502                         f->glyph[glyph].advance = adv * 20;     //?
503                 }
504
505                 buf[i] = glyph;
506             }
507             if ((id == fid) && (jobs & FEDTJ_PRINT))
508                 printf("\n");
509             if (jobs & FEDTJ_CALLBACK)
510                 callback(self, buf, advance, num, fid, fontsize, x, y, &color);
511             x += xpos;
512         }
513     }
514
515     return id;
516 }
517
518 int swf_ParseDefineText(TAG * tag,
519                     void (*callback) (void *self, int *chars, int *ypos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
520 {
521     return swf_FontExtract_DefineTextCallback(-1, 0, tag, FEDTJ_CALLBACK, callback, self);
522 }
523
524 int swf_FontExtract_DefineText(int id, SWFFONT * f, TAG * t, int jobs)
525 {
526     return swf_FontExtract_DefineTextCallback(id, f, t, jobs, 0, 0);
527 }
528
529 int swf_FontExtract(SWF * swf, int id, SWFFONT * *font)
530 {
531     TAG *t;
532     SWFFONT *f;
533
534     if ((!swf) || (!font))
535         return -1;
536
537     f = (SWFFONT *) rfx_calloc(sizeof(SWFFONT));
538
539     t = swf->firstTag;
540
541     while (t) {
542         int nid = 0;
543         switch (swf_GetTagID(t)) {
544         case ST_DEFINEFONT:
545             nid = swf_FontExtract_DefineFont(id, f, t);
546             break;
547
548         case ST_DEFINEFONT2:
549         case ST_DEFINEFONT3:
550             nid = swf_FontExtract_DefineFont2(id, f, t);
551             break;
552
553         case ST_DEFINEFONTALIGNZONES:
554             nid = swf_FontExtract_DefineFontAlignZones(id, f, t);
555             break;
556
557         case ST_DEFINEFONTINFO:
558         case ST_DEFINEFONTINFO2:
559             nid = swf_FontExtract_DefineFontInfo(id, f, t);
560             break;
561
562         case ST_DEFINETEXT:
563         case ST_DEFINETEXT2:
564             nid = swf_FontExtract_DefineText(id, f, t, f->layout ? 0 : FEDTJ_MODIFY);
565             break;
566
567         case ST_GLYPHNAMES:
568             nid = swf_FontExtract_GlyphNames(id, f, t);
569             break;
570         }
571         if (nid > 0)
572             id = nid;
573         t = swf_NextTag(t);
574     }
575     if (f->id != id) {
576         rfx_free(f);
577         f = 0;
578     }
579     font[0] = f;
580     return 0;
581 }
582
583 int swf_FontSetID(SWFFONT * f, U16 id)
584 {
585     if (!f)
586         return -1;
587     f->id = id;
588     return 0;
589 }
590
591 void swf_LayoutFree(SWFLAYOUT * l)
592 {
593     if (l) {
594         if (l->kerning)
595             rfx_free(l->kerning);
596         l->kerning = NULL;
597         if (l->bounds)
598             rfx_free(l->bounds);
599         l->bounds = NULL;
600     }
601     rfx_free(l);
602 }
603
604
605 static void font_freeglyphnames(SWFFONT*f)
606 {
607     if (f->glyphnames)
608     {
609         int t;
610         for (t = 0; t < f->numchars; t++)
611         {
612             if (f->glyphnames[t])
613             {
614                 rfx_free(f->glyphnames[t]);
615                 f->glyphnames[t] = 0;
616             }
617         }
618         rfx_free(f->glyphnames);
619         f->glyphnames = 0;
620         }
621 }
622 static void font_freeusage(SWFFONT*f)
623 {
624     if (f->use) {
625         if(f->use->chars) {
626             rfx_free(f->use->chars);f->use->chars = 0;
627         }
628         if(f->use->neighbors) {
629             rfx_free(f->use->neighbors);f->use->neighbors = 0;
630         }
631         if(f->use->neighbors_hash) {
632             rfx_free(f->use->neighbors_hash);f->use->neighbors_hash = 0;
633         }
634         rfx_free(f->use); f->use = 0;
635     }
636 }
637 static void font_freelayout(SWFFONT*f)
638 {
639     if (f->layout) {
640         swf_LayoutFree(f->layout);
641         f->layout = 0;
642     }
643 }
644 static void font_freename(SWFFONT*f)
645 {
646     if (f->name) {
647         rfx_free(f->name);
648         f->name = 0;
649     }
650 }
651
652 int swf_FontReduce_old(SWFFONT * f)
653 {
654     int i, j;
655     int max_unicode = 0;
656     if ((!f) || (!f->use) || f->use->is_reduced)
657         return -1;
658
659     j = 0;
660
661     for (i = 0; i < f->numchars; i++) {
662         if (f->glyph[i].shape && f->use->chars[i]) {
663             f->glyph2ascii[j] = f->glyph2ascii[i];
664             f->glyph[j] = f->glyph[i];
665             f->use->chars[i] = j;
666             j++;
667         } else {
668             f->glyph2ascii[i] = 0;
669             if(f->glyph[i].shape) {
670                 swf_ShapeFree(f->glyph[i].shape);
671                 f->glyph[i].shape = 0;
672                 f->glyph[i].advance = 0;
673             }
674             f->use->chars[i] = -1;
675             j++; //TODO: remove
676         }
677     }
678     for (i = 0; i < f->maxascii; i++) {
679         if(f->use->chars[f->ascii2glyph[i]]<0) {
680             f->ascii2glyph[i] = -1;
681         } else {
682             f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
683             max_unicode = i;
684         }
685     }
686     f->maxascii = max_unicode;
687     f->use->is_reduced = 1;
688     f->numchars = j;
689     font_freelayout(f);
690     font_freeglyphnames(f);
691     font_freename(f);
692     return j;
693 }
694
695 int swf_FontReduce_swfc(SWFFONT * f)
696 {
697     int i, j;
698     int max_unicode = 0;
699     if ((!f) || (!f->use) || f->use->is_reduced)
700         return -1;
701
702     font_freeglyphnames(f);
703
704     j = 0;
705     for (i = 0; i < f->numchars; i++) {
706         if (f->glyph[i].shape && f->use->chars[i]) {
707             f->glyph2ascii[j] = f->glyph2ascii[i];
708             if (f->layout)
709                 f->layout->bounds[j] = f->layout->bounds[i];
710             f->glyph[j] = f->glyph[i];
711             f->use->chars[i] = j;
712             j++;
713         } else {
714             f->glyph2ascii[i] = 0;
715             if(f->glyph[i].shape) {
716                 swf_ShapeFree(f->glyph[i].shape);
717                 f->glyph[i].shape = 0;
718                 f->glyph[i].advance = 0;
719             }
720             f->use->chars[i] = -1;
721         }
722     }
723     f->use->used_glyphs = j;
724     for (i = 0; i < f->maxascii; i++) {
725         if(f->ascii2glyph[i] > -1) {
726             if (f->use->chars[f->ascii2glyph[i]]<0) {
727                 f->use->chars[f->ascii2glyph[i]] = 0;
728                 f->ascii2glyph[i] = -1;
729             } else {
730                 f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
731                 f->use->chars[f->ascii2glyph[i]] = 1;
732                 max_unicode = i + 1;
733             }
734         }
735     }
736     f->maxascii = max_unicode;
737     f->use->is_reduced = 1;
738     f->numchars = j;
739     font_freename(f);
740     return j;
741 }
742
743 int swf_FontReduce(SWFFONT * f)
744 {
745     int i;
746     int max_unicode = 0;
747     int max_glyph = 0;
748     if ((!f) || (!f->use) || f->use->is_reduced)
749         return -1;
750
751     font_freelayout(f);
752     font_freeglyphnames(f);
753
754     f->use->used_glyphs= 0;
755     for (i = 0; i < f->numchars; i++) {
756         if(!f->use->chars[i]) {
757             if(f->glyph2ascii) {
758                 f->glyph2ascii[i] = 0;
759             }
760             if(f->glyph[i].shape) {
761                 swf_ShapeFree(f->glyph[i].shape);
762                 f->glyph[i].shape = 0;
763                 f->glyph[i].advance = 0;
764             }
765 //          f->use->used_glyphs++;
766         } else {
767             f->use->used_glyphs++;
768             max_glyph = i+1;
769         }
770     }
771     for (i = 0; i < f->maxascii; i++) {
772         if(f->ascii2glyph[i] > -1 && !f->use->chars[f->ascii2glyph[i]]) {
773             if(f->ascii2glyph) {
774                 f->ascii2glyph[i] = -1;
775             }
776         } else {
777             max_unicode = i+1;
778         }
779     }
780     f->maxascii = max_unicode;
781     f->numchars = max_glyph;
782     font_freename(f);
783     return 0;
784 }
785
786 void swf_FontSort(SWFFONT * font)
787 {
788     int i, j;
789     int *newplace;
790     int *newpos;
791     if (!font)
792         return;
793
794     newplace = (int*)rfx_alloc(sizeof(int) * font->numchars);
795
796     for (i = 0; i < font->numchars; i++) {
797         newplace[i] = i;
798     }
799     for (i = 0; i < font->numchars; i++)
800         for (j = 0; j < i; j++) {
801             if (font->glyph2ascii[i] < font->glyph2ascii[j]) {
802                 int n1, n2;
803                 char *c1, *c2;
804                 SWFGLYPH g1, g2;
805                 SRECT r1, r2;
806                 n1 = newplace[i];
807                 n2 = newplace[j];
808                 newplace[j] = n1;
809                 newplace[i] = n2;
810                 n1 = font->glyph2ascii[i];
811                 n2 = font->glyph2ascii[j];
812                 font->glyph2ascii[j] = n1;
813                 font->glyph2ascii[i] = n2;
814                 g1 = font->glyph[i];
815                 g2 = font->glyph[j];
816                 font->glyph[j] = g1;
817                 font->glyph[i] = g2;
818                 if (font->glyphnames) {
819                     c1 = font->glyphnames[i];
820                     c2 = font->glyphnames[j];
821                     font->glyphnames[j] = c1;
822                     font->glyphnames[i] = c2;
823                 }
824                 if (font->layout) {
825                     r1 = font->layout->bounds[i];
826                     r2 = font->layout->bounds[j];
827                     font->layout->bounds[j] = r1;
828                     font->layout->bounds[i] = r2;
829                 }
830             }
831         }
832     newpos = (int*)rfx_alloc(sizeof(int) * font->numchars);
833     for (i = 0; i < font->numchars; i++) {
834         newpos[newplace[i]] = i;
835     }
836     for (i = 0; i < font->maxascii; i++) {
837         if (font->ascii2glyph[i] >= 0)
838             font->ascii2glyph[i] = newpos[font->ascii2glyph[i]];
839     }
840
841     rfx_free(newpos);
842     rfx_free(newplace);
843 }
844
845 void swf_FontPrepareForEditText(SWFFONT * font)
846 {
847     if (!font->layout)
848         swf_FontCreateLayout(font);
849     swf_FontSort(font);
850 }
851
852 int swf_FontInitUsage(SWFFONT * f)
853 {
854     if (!f)
855         return -1;
856     if(f->use) {
857         fprintf(stderr, "Usage initialized twice");
858         return -1;
859     }
860     f->use = (FONTUSAGE*)rfx_calloc(sizeof(FONTUSAGE));
861     f->use->smallest_size = 0xffff;
862     f->use->chars = (int*)rfx_calloc(sizeof(f->use->chars[0]) * f->numchars);
863     return 0;
864 }
865
866 void swf_FontClearUsage(SWFFONT * f)
867 {
868     if (!f || !f->use)
869         return;
870     rfx_free(f->use->chars); f->use->chars = 0;
871     rfx_free(f->use); f->use = 0;
872 }
873
874 int swf_FontUse(SWFFONT * f, U8 * s)
875 {
876     if( (!s))
877         return -1;
878     while (*s) {
879         if(*s < f->maxascii && f->ascii2glyph[*s]>=0)
880             swf_FontUseGlyph(f, f->ascii2glyph[*s], /*FIXME*/0xffff);
881         s++;
882     }
883     return 0;
884 }
885
886 int swf_FontUseUTF8(SWFFONT * f, U8 * s, U16 size)
887 {
888     if( (!s))
889         return -1;
890     int ascii;
891     while (*s)
892     {
893         ascii = readUTF8char(&s);
894         if(ascii < f->maxascii && f->ascii2glyph[ascii]>=0)
895             swf_FontUseGlyph(f, f->ascii2glyph[ascii], size);
896     }
897     return 0;
898 }
899
900 int swf_FontUseAll(SWFFONT* f)
901 {
902     int i;
903
904     if (!f->use)
905         swf_FontInitUsage(f);
906     for (i = 0; i < f->numchars; i++)
907         f->use->chars[i] = 1;
908     f->use->used_glyphs = f->numchars;
909     return 0;
910 }
911
912 static unsigned hash2(int char1, int char2)
913 {
914     unsigned hash = char1^(char2<<8);
915     hash += (hash << 3);
916     hash ^= (hash >> 11);
917     hash += (hash << 15);
918     return hash;
919 }
920 static void hashadd(FONTUSAGE*u, int char1, int char2, int nr)
921 {
922     unsigned hash = hash2(char1, char2);
923     while(1) {
924         hash = hash%u->neighbors_hash_size;
925         if(!u->neighbors_hash[hash]) {
926            u->neighbors_hash[hash] = nr+1;
927            return;
928         }
929         hash++;
930     }
931 }
932 int swf_FontUseGetPair(SWFFONT * f, int char1, int char2)
933 {
934     FONTUSAGE*u = f->use;
935     if(!u || !u->neighbors_hash_size) 
936         return 0;
937     unsigned hash = hash2(char1, char2);
938     while(1) {
939         hash = hash%u->neighbors_hash_size;
940         int pos = u->neighbors_hash[hash];
941         if(!pos)
942             return 0;
943         if(pos && 
944            u->neighbors[pos-1].char1 == char1 &&
945            u->neighbors[pos-1].char2 == char2) {
946             return pos;
947         }
948         hash++;
949     }
950
951 }
952 void swf_FontUsePair(SWFFONT * f, int char1, int char2)
953 {
954     if (!f->use)
955         swf_FontInitUsage(f);
956     FONTUSAGE*u = f->use;
957
958     if(u->num_neighbors*3 >= u->neighbors_hash_size*2) {
959         if(u->neighbors_hash) {
960             free(u->neighbors_hash);
961         }
962         u->neighbors_hash_size = u->neighbors_hash_size?u->neighbors_hash_size*2:1024;
963         u->neighbors_hash = rfx_calloc(u->neighbors_hash_size*sizeof(int));
964         int t;
965         for(t=0;t<u->num_neighbors;t++) {
966             hashadd(u, u->neighbors[t].char1, u->neighbors[t].char2, t);
967         }
968     }
969
970     if(!swf_FontUseGetPair(f, char1, char2)) {
971         if(u->num_neighbors == u->neighbors_size) {
972             u->neighbors_size += 4096;
973             u->neighbors = rfx_realloc(u->neighbors, sizeof(SWFGLYPHPAIR)*u->neighbors_size);
974         }
975         u->neighbors[u->num_neighbors].char1 = char1;
976         u->neighbors[u->num_neighbors].char2 = char2;
977         hashadd(u, char1, char2, u->num_neighbors);
978         u->num_neighbors++;
979     } else {
980         // increase?
981     }
982 }
983
984 int swf_FontUseGlyph(SWFFONT * f, int glyph, U16 size)
985 {
986     if (!f->use)
987         swf_FontInitUsage(f);
988     if(glyph < 0 || glyph >= f->numchars)
989         return -1;
990     if(!f->use->chars[glyph])
991         f->use->used_glyphs++;
992     f->use->chars[glyph] = 1;
993     if(size && size < f->use->smallest_size)
994         f->use->smallest_size = size;
995     return 0;
996 }
997
998 int swf_FontSetDefine(TAG * t, SWFFONT * f)
999 {
1000     U16 *ofs = (U16 *) rfx_alloc(f->numchars * 2);
1001     int p, i, j;
1002
1003     if ((!t) || (!f))
1004         return -1;
1005     swf_ResetWriteBits(t);
1006     swf_SetU16(t, f->id);
1007
1008     p = 0;
1009     j = 0;
1010     for (i = 0; i < f->numchars; i++)
1011         if (f->glyph[i].shape) {
1012             ofs[j++] = p;
1013             p += swf_SetSimpleShape(NULL, f->glyph[i].shape);
1014         }
1015
1016     for (i = 0; i < j; i++)
1017         swf_SetU16(t, ofs[i] + j * 2);
1018     if (!j) {
1019         fprintf(stderr, "rfxswf: warning: Font is empty\n");
1020         swf_SetU16(t, 0);
1021     }
1022
1023     for (i = 0; i < f->numchars; i++)
1024         if (f->glyph[i].shape)
1025             swf_SetSimpleShape(t, f->glyph[i].shape);
1026
1027     swf_ResetWriteBits(t);
1028     rfx_free(ofs);
1029     return 0;
1030 }
1031
1032 static inline int fontSize(SWFFONT * font)
1033 {
1034     int t;
1035     int size = 0;
1036     for (t = 0; t < font->numchars; t++) {
1037         int l = 0;
1038         if(font->glyph[t].shape)
1039             l = (font->glyph[t].shape->bitlen + 7) / 8;
1040         else
1041             l = 8;
1042         size += l + 1;
1043     }
1044     return size + (font->numchars + 1) * 2;
1045 }
1046
1047 int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
1048 {
1049     U8 flags = 0;
1050     int t;
1051     int pos;
1052     swf_SetU16(tag, f->id);
1053
1054     if (f->layout) flags |= 128;                // haslayout
1055     if (f->numchars > 256)
1056         flags |= 4;             // widecodes
1057     if (f->style & FONT_STYLE_BOLD)
1058         flags |= 1;             // bold
1059     if (f->style & FONT_STYLE_ITALIC)
1060         flags |= 2;             // italic
1061     if (f->maxascii >= 256)
1062         flags |= 4;             //wide codecs
1063     if (fontSize(f) > 65535)
1064         flags |= 8;             //wide offsets
1065     flags |= 8 | 4;             //FIXME: the above check doesn't work
1066
1067     if (f->encoding & FONT_ENCODING_ANSI)
1068         flags |= 16;            // ansi
1069     if (f->encoding & FONT_ENCODING_UNICODE)
1070         flags |= 32;            // unicode
1071     if (f->encoding & FONT_ENCODING_SHIFTJIS)
1072         flags |= 64;            // shiftjis
1073
1074     swf_SetU8(tag, flags);
1075     swf_SetU8(tag, 0);          //reserved flags
1076     if (f->name) {
1077         /* font name */
1078         swf_SetU8(tag, strlen((const char*)f->name)+1);
1079         swf_SetBlock(tag, f->name, strlen((const char*)f->name)+1);
1080     } else {
1081         /* font name (="") */
1082         swf_SetU8(tag, 1);
1083         swf_SetU8(tag, 0);
1084     }
1085     /* number of glyphs */
1086     swf_SetU16(tag, f->numchars);
1087     /* font offset table */
1088     pos = tag->len;
1089     for (t = 0; t <= f->numchars; t++) {
1090         if (flags & 8)
1091             swf_SetU32(tag, /* fontoffset */ 0);        /*placeholder */
1092         else
1093             swf_SetU16(tag, /* fontoffset */ 0);        /*placeholder */
1094     }
1095
1096     for (t = 0; t <= f->numchars; t++) {
1097         if (flags & 8) {
1098             tag->data[pos + t * 4] = (tag->len - pos);
1099             tag->data[pos + t * 4 + 1] = (tag->len - pos) >> 8;
1100             tag->data[pos + t * 4 + 2] = (tag->len - pos) >> 16;
1101             tag->data[pos + t * 4 + 3] = (tag->len - pos) >> 24;
1102         } else {
1103             if (tag->len - pos > 65535) {
1104                 fprintf(stderr, "Internal error: Font too big and WideOffsets flag not set\n");
1105                 exit(1);
1106             }
1107             tag->data[pos + t * 2] = (tag->len - pos);
1108             tag->data[pos + t * 2 + 1] = (tag->len - pos) >> 8;
1109         }
1110         if (t < f->numchars) {
1111             if(f->glyph[t].shape) {
1112                 swf_SetSimpleShape(tag, f->glyph[t].shape);
1113             } else {
1114                 swf_SetU8(tag, 0); //non-edge(1) + edge flags(5)
1115             }
1116         }
1117     }
1118
1119
1120     /* font code table */
1121     for (t = 0; t < f->numchars; t++) {
1122         if (flags & 4) {                /* wide codes */
1123             if(f->glyph2ascii[t]) {
1124                 swf_SetU16(tag, f->glyph2ascii[t]);
1125             } else {
1126                 swf_SetU16(tag, 0);
1127             }
1128         } else {
1129             if(f->glyph2ascii[t]) {
1130                 swf_SetU8(tag, f->glyph2ascii[t]);
1131             } else {
1132                 swf_SetU8(tag, 0);
1133             }
1134         }
1135     }
1136
1137     if (f->layout) {
1138         swf_SetU16(tag, f->layout->ascent);
1139         swf_SetU16(tag, f->layout->descent);
1140         swf_SetU16(tag, f->layout->leading);
1141         for (t = 0; t < f->numchars; t++)
1142             swf_SetU16(tag, f->glyph[t].advance);
1143         for (t = 0; t < f->numchars; t++) {
1144             swf_ResetWriteBits(tag);
1145             swf_SetRect(tag, &f->layout->bounds[t]);
1146         }
1147         swf_SetU16(tag, f->layout->kerningcount);
1148         for (t = 0; t < f->layout->kerningcount; t++) {
1149             if (flags & 4) {    /* wide codes */
1150                 swf_SetU16(tag, f->layout->kerning[t].char1);
1151                 swf_SetU16(tag, f->layout->kerning[t].char2);
1152             } else {
1153                 swf_SetU8(tag, f->layout->kerning[t].char1);
1154                 swf_SetU8(tag, f->layout->kerning[t].char2);
1155             }
1156             swf_SetU16(tag, f->layout->kerning[t].adjustment);
1157         }
1158     }
1159     return 0;
1160 }
1161
1162 void swf_FontAddLayout(SWFFONT * f, int ascent, int descent, int leading)
1163 {
1164     f->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
1165     f->layout->ascent = ascent;
1166     f->layout->descent = descent;
1167     f->layout->leading = leading;
1168     f->layout->kerningcount = 0;
1169     f->layout->kerning = 0;
1170     f->layout->bounds = (SRECT *) rfx_calloc(sizeof(SRECT) * f->numchars);
1171 }
1172
1173 int swf_FontSetInfo(TAG * t, SWFFONT * f)
1174 {
1175     int l, i;
1176     U8 wide = 0;
1177     U8 flags = 0;
1178     if ((!t) || (!f))
1179         return -1;
1180     swf_ResetWriteBits(t);
1181     swf_SetU16(t, f->id);
1182     l = f->name ? strlen((const char *)f->name) : 0;
1183     if (l > 255)
1184         l = 255;
1185     swf_SetU8(t, l);
1186     if (l)
1187         swf_SetBlock(t, f->name, l);
1188     if (f->numchars >= 256)
1189         wide = 1;
1190
1191     if (f->style & FONT_STYLE_BOLD)
1192         flags |= 2;
1193     if (f->style & FONT_STYLE_ITALIC)
1194         flags |= 4;
1195     if (f->style & FONT_ENCODING_ANSI)
1196         flags |= 8;
1197     if (f->style & FONT_ENCODING_SHIFTJIS)
1198         flags |= 16;
1199     if (f->style & FONT_ENCODING_UNICODE)
1200         flags |= 32;
1201
1202     swf_SetU8(t, (flags & 0xfe) | wide);
1203
1204     for (i = 0; i < f->numchars; i++) {
1205         if (f->glyph[i].shape) {
1206             int g2a = f->glyph2ascii?f->glyph2ascii[i]:0;
1207             wide ? swf_SetU16(t, g2a) : swf_SetU8(t, g2a);
1208         }
1209     }
1210
1211     return 0;
1212 }
1213
1214 int swf_TextPrintDefineText(TAG * t, SWFFONT * f)
1215 {
1216     int id = swf_GetTagID(t);
1217     if ((id == ST_DEFINETEXT) || (id == ST_DEFINETEXT2))
1218         swf_FontExtract_DefineText(f->id, f, t, FEDTJ_PRINT);
1219     else
1220         return -1;
1221     return 0;
1222 }
1223
1224 void swf_FontFree(SWFFONT * f)
1225 {
1226     int i;
1227     if (!f)
1228         return;
1229
1230     if (f->glyph)
1231     {
1232         for (i = 0; i < f->numchars; i++)
1233             if (f->glyph[i].shape)
1234             {
1235                 swf_ShapeFree(f->glyph[i].shape);
1236                 f->glyph[i].shape = NULL;
1237             }
1238             rfx_free(f->glyph);
1239             f->glyph = NULL;
1240     }
1241     if (f->ascii2glyph)
1242     {
1243         rfx_free(f->ascii2glyph);
1244         f->ascii2glyph = NULL;
1245     }
1246     if (f->glyph2ascii)
1247     {
1248         rfx_free(f->glyph2ascii);
1249         f->glyph2ascii = NULL;
1250     }
1251     font_freename(f);
1252     font_freelayout(f);
1253     font_freeglyphnames(f);
1254     font_freeusage(f);
1255
1256     rfx_free(f);
1257 }
1258
1259 int swf_TextSetInfoRecord(TAG * t, SWFFONT * font, U16 size, RGBA * color, int x, int y)
1260 {
1261     U8 flags;
1262     if (!t)
1263         return -1;
1264
1265     flags = TF_TEXTCONTROL | (font ? TF_HASFONT : 0) | (color ? TF_HASCOLOR : 0) | (x ? TF_HASXOFFSET : 0)
1266         | (y ? TF_HASYOFFSET : 0);
1267
1268     swf_SetU8(t, flags);
1269     if (font)
1270         swf_SetU16(t, font->id);
1271     if (color) {
1272         if (swf_GetTagID(t) == ST_DEFINETEXT2)
1273             swf_SetRGBA(t, color);
1274         else
1275             swf_SetRGB(t, color);
1276     }
1277     if (x) {
1278         if(x != SET_TO_ZERO) {
1279             if(x>32767 || x<-32768)
1280                 fprintf(stderr, "Warning: Horizontal char position overflow: %d\n", x);
1281             swf_SetS16(t, x);
1282         } else {
1283             swf_SetS16(t, 0);
1284         }
1285     }
1286     if (y) {
1287         if(y != SET_TO_ZERO) {
1288             if(y>32767 || y<-32768)
1289                 fprintf(stderr, "Warning: Vertical char position overflow: %d\n", y);
1290             swf_SetS16(t, y);
1291         } else {
1292             swf_SetS16(t, 0);
1293         }
1294     }
1295     if (font)
1296         swf_SetU16(t, size);
1297
1298     return 0;
1299 }
1300
1301 static int swf_TextCountBits2(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits, char *encoding)
1302 {
1303     U16 g, a;
1304     char utf8 = 0;
1305     if ((!s) || (!font) || ((!gbits) && (!abits)) || (!font->ascii2glyph))
1306         return -1;
1307     g = a = 0;
1308
1309     if (!strcmp(encoding, "UTF8"))
1310         utf8 = 1;
1311     else if (!strcmp(encoding, "iso-8859-1"))
1312         utf8 = 0;
1313     else
1314         fprintf(stderr, "Unknown encoding: %s", encoding);
1315
1316     while (*s) {
1317         int glyph = -1, c;
1318
1319         if (!utf8)
1320             c = *s++;
1321         else
1322             c = readUTF8char(&s);
1323
1324         if (c < font->maxascii)
1325             glyph = font->ascii2glyph[c];
1326         if (glyph >= 0) {
1327             g = swf_CountUBits(glyph, g);
1328             a = swf_CountBits(((((U32) font->glyph[glyph].advance) * scale) / 20) / 100, a);
1329         }
1330     }
1331
1332     if (gbits)
1333         gbits[0] = (U8) g;
1334     if (abits)
1335         abits[0] = (U8) a;
1336     return 0;
1337 }
1338
1339 static int swf_TextSetCharRecord2(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits, char *encoding)
1340 {
1341     int l = 0, pos;
1342     char utf8 = 0;
1343
1344     if ((!t) || (!font) || (!s) || (!font->ascii2glyph))
1345         return -1;
1346
1347     if (!strcmp(encoding, "UTF8"))
1348         utf8 = 1;
1349     else if (!strcmp(encoding, "iso-8859-1"))
1350         utf8 = 0;
1351     else
1352         fprintf(stderr, "Unknown encoding: %s", encoding);
1353
1354     pos = t->len;
1355     swf_SetU8(t, l);            //placeholder
1356
1357     while (*s) {
1358         int g = -1, c;
1359
1360         if (!utf8)
1361             c = *s++;
1362         else
1363             c = readUTF8char(&s);
1364
1365         if (c < font->maxascii)
1366             g = font->ascii2glyph[c];
1367         if (g >= 0) {
1368             swf_SetBits(t, g, gbits);
1369             swf_SetBits(t, ((((U32) font->glyph[g].advance) * scale) / 20) / 100, abits);
1370             l++;
1371             /* We split into 127 characters per text field.
1372                We could do 255, by the (formerly wrong) flash specification,
1373                but some SWF parsing code out there still assumes that char blocks
1374                are at max 127 characters, and it would save only a few bits.
1375             */
1376             if (l == 0x7f)
1377                 break;
1378         }
1379     }
1380
1381     PUT8(&t->data[pos], l);
1382
1383     swf_ResetWriteBits(t);
1384     return 0;
1385 }
1386
1387 int swf_TextCountBits(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1388 {
1389     return swf_TextCountBits2(font, s, scale, gbits, abits, "iso-8859-1");
1390 }
1391
1392 int swf_TextSetCharRecord(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1393 {
1394     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "iso-8859-1");
1395 }
1396
1397 int swf_TextCountBitsUTF8(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1398 {
1399     return swf_TextCountBits2(font, s, scale, gbits, abits, "UTF8");
1400 }
1401
1402 int swf_TextSetCharRecordUTF8(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1403 {
1404     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "UTF8");
1405 }
1406
1407 U32 swf_TextGetWidth(SWFFONT * font, U8 * s, int scale)
1408 {
1409     U32 res = 0;
1410
1411     if (font && s) {
1412         while (s[0]) {
1413             int g = -1;
1414             if (*s < font->maxascii)
1415                 g = font->ascii2glyph[*s];
1416             if (g >= 0)
1417                 res += font->glyph[g].advance / 20;
1418             s++;
1419         }
1420         if (scale)
1421             res = (res * scale) / 100;
1422     }
1423     return res;
1424 }
1425
1426 SRECT swf_TextCalculateBBoxUTF8(SWFFONT * font, U8 * s, int scale)
1427 {
1428     int xpos = 0;
1429     int ypos = 0;
1430     SRECT r;
1431     swf_GetRect(0, &r);
1432     while (*s) {
1433         int c = readUTF8char(&s);
1434         if(c==13 || c==10) {
1435             if(s[0] == 10) {
1436                 s++;
1437             }
1438             xpos=0;
1439             ypos+=font->layout->leading;
1440             continue;
1441         }
1442         if (c < font->maxascii) {
1443             int g = font->ascii2glyph[c];
1444             if (g >= 0) {
1445                 SRECT rn = font->layout->bounds[g];
1446                 rn.xmin = (rn.xmin * scale) / 20 / 100 + xpos;
1447                 rn.xmax = (rn.xmax * scale) / 20 / 100 + xpos;
1448                 rn.ymin = (rn.ymin * scale) / 20 / 100 + ypos;
1449                 rn.ymax = (rn.ymax * scale) / 20 / 100 + ypos;
1450                 swf_ExpandRect2(&r, &rn);
1451                 xpos += (font->glyph[g].advance * scale) / 20 / 100;
1452             }
1453         }
1454     }
1455     return r;
1456 }
1457
1458
1459 SWFFONT *swf_ReadFont(const char *filename)
1460 {
1461     int f;
1462     SWF swf;
1463     if (!filename)
1464         return 0;
1465     f = open(filename, O_RDONLY|O_BINARY);
1466
1467     if (f < 0 || swf_ReadSWF(f, &swf) < 0) {
1468         fprintf(stderr, "%s is not a valid SWF font file or contains errors.\n", filename);
1469         close(f);
1470         return 0;
1471     } else {
1472         SWFFONT *font;
1473         close(f);
1474         if (swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
1475             return 0;
1476         swf_FreeTags(&swf);
1477         return font;
1478     }
1479 }
1480
1481 void swf_SetEditText(TAG * tag, U16 flags, SRECT r, const char *text, RGBA * color, int maxlength, U16 font, U16 height, EditTextLayout * layout, const char *variable)
1482 {
1483     swf_SetRect(tag, &r);
1484     swf_ResetWriteBits(tag);
1485
1486     flags &= ~(ET_HASTEXT | ET_HASTEXTCOLOR | ET_HASMAXLENGTH | ET_HASFONT | ET_HASLAYOUT);
1487     if (text)
1488         flags |= ET_HASTEXT;
1489     if (color)
1490         flags |= ET_HASTEXTCOLOR;
1491     if (maxlength)
1492         flags |= ET_HASMAXLENGTH;
1493     if (font)
1494         flags |= ET_HASFONT;
1495     if (layout)
1496         flags |= ET_HASLAYOUT;
1497
1498     swf_SetBits(tag, flags, 16);
1499
1500     if (flags & ET_HASFONT) {
1501         swf_SetU16(tag, font);  //font
1502         swf_SetU16(tag, height);        //fontheight
1503     }
1504     if (flags & ET_HASTEXTCOLOR) {
1505         swf_SetRGBA(tag, color);
1506     }
1507     if (flags & ET_HASMAXLENGTH) {
1508         swf_SetU16(tag, maxlength);     //maxlength
1509     }
1510     if (flags & ET_HASLAYOUT) {
1511         swf_SetU8(tag, layout->align);  //align
1512         swf_SetU16(tag, layout->leftmargin);    //left margin
1513         swf_SetU16(tag, layout->rightmargin);   //right margin
1514         swf_SetU16(tag, layout->indent);        //indent
1515         swf_SetU16(tag, layout->leading);       //leading
1516     }
1517     swf_SetString(tag, variable);
1518     if (flags & ET_HASTEXT)
1519         swf_SetString(tag, text);
1520 }
1521
1522 SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, const char *text, int scale)
1523 {
1524     SRECT r;
1525     U8 gbits, abits;
1526     U8 *utext = (U8 *) strdup(text);
1527     U8 *upos = utext;
1528     int x = 0, y = 0;
1529     int pos = 0;
1530     int ystep = 0;
1531     if (font->layout) {
1532         r = swf_TextCalculateBBoxUTF8(font, (U8*)text, scale * 20);
1533         ystep = font->layout->leading;
1534     } else {
1535         fprintf(stderr, "No layout information- can't compute text bbox accurately");
1536         /* Hm, without layout information, we can't compute a bounding
1537            box. We could call swf_FontCreateLayout to create a layout,
1538            but the caller probably doesn't want us to mess up his font
1539            structure.
1540          */
1541         r.xmin = r.ymin = 0;
1542         r.xmax = r.ymax = 1024 * 20;
1543         ystep = 100;
1544     }
1545
1546     swf_SetRect(tag, &r);
1547
1548     /* The text matrix is pretty boring, as it doesn't apply to
1549        individual characters, but rather whole text objects (or
1550        at least whole char records- haven't tested).
1551        So it can't do anything which we can't already do with
1552        the placeobject tag we use for placing the text on the scene.
1553      */
1554     swf_SetMatrix(tag, 0);
1555
1556     swf_TextCountBitsUTF8(font, (U8*)text, scale * 20, &gbits, &abits);
1557     swf_SetU8(tag, gbits);
1558     swf_SetU8(tag, abits);
1559
1560     while(*upos) {
1561         U8*next = upos;
1562         int count = 0;
1563
1564         swf_TextSetInfoRecord(tag, font, (scale * 1024) / 100, rgb, x, y);      //scale
1565         x = 0;
1566
1567         while(*next && *next!=13 && *next!=10 && count<127) {
1568             readUTF8char(&next);
1569             count++;
1570         }
1571         if(next[0] == 13 || next[0] == 10) {
1572             x = SET_TO_ZERO;
1573             y += ystep;
1574         }
1575
1576         if(next[0] == 13 && next[1] == 10)
1577             next++;
1578
1579         if(next[0] == 13 || next[0] == 10) {
1580             *next = 0;
1581             next++;
1582         }
1583
1584         /* now set the text params- notice that a font size of
1585            1024 (or 1024*20 for definefont3) means that the glyphs will 
1586            be displayed exactly as they would be in/with a defineshape.
1587            This is not documented in the specs.
1588          */
1589
1590         /* set the actual text- notice that we just pass our scale
1591            parameter over, as TextSetCharRecord calculates with
1592            percent, too */
1593         swf_TextSetCharRecordUTF8(tag, font, upos, scale * 20, gbits, abits);
1594
1595         upos= next;
1596     }
1597     free(utext);
1598
1599     swf_SetU8(tag, 0);
1600     return r;
1601 }
1602
1603 void swf_FontCreateLayout(SWFFONT * f)
1604 {
1605     S16 leading = 0;
1606     int t;
1607     if (f->layout)
1608         return;
1609     if (!f->numchars)
1610         return;
1611
1612     f->layout = (SWFLAYOUT *) rfx_calloc(sizeof(SWFLAYOUT));
1613     f->layout->bounds = (SRECT *) rfx_alloc(f->numchars * sizeof(SRECT));
1614     f->layout->ascent = 0;
1615     f->layout->descent = 0;
1616
1617     for (t = 0; t < f->numchars; t++) {
1618         SHAPE2 *shape2;
1619         SRECT bbox;
1620         int width;
1621         shape2 = swf_ShapeToShape2(f->glyph[t].shape);
1622         if (!shape2) {
1623             fprintf(stderr, "Shape parse error\n");
1624             exit(1);
1625         }
1626         bbox = swf_GetShapeBoundingBox(shape2);
1627         swf_Shape2Free(shape2);
1628         f->layout->bounds[t] = bbox;
1629
1630         width = (bbox.xmax);
1631
1632         /* The following is a heuristic- it may be that extractfont_DefineText
1633            has already found out some widths for individual characters (from the way
1634            they are used)- we now have to guess whether that width might be possible,
1635            which is the case if it isn't either much too big or much too small */
1636         if (width > f->glyph[t].advance * 3 / 2 || width < f->glyph[t].advance / 2)
1637             f->glyph[t].advance = width;
1638
1639         if (-bbox.ymin > f->layout->ascent)
1640             f->layout->ascent = -bbox.ymin;
1641         if (bbox.ymax > f->layout->descent)
1642             f->layout->descent = bbox.ymax;
1643     }
1644 }
1645
1646 void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, const char *text)
1647 {
1648     U8 *s = (U8 *) text;
1649     int advance = 0;
1650     while (*s) {
1651         SHAPE *shape;
1652         SHAPE2 *shape2;
1653         SHAPELINE *l;
1654         U32 c = readUTF8char(&s);
1655         int g = font->ascii2glyph[c];
1656         shape = font->glyph[g].shape;
1657         if (((int) g) < 0) {
1658             fprintf(stderr, "No char %d in font %s\n", c, font->name ? (char *) font->name : "?");
1659             continue;
1660         }
1661         shape2 = swf_ShapeToShape2(shape);
1662         l = shape2->lines;
1663         while (l) {
1664             if (l->type == moveTo) {
1665                 FPOINT to;
1666                 to.x = l->x * size / 100.0 / 20.0 + advance;
1667                 to.y = l->y * size / 100.0 / 20.0;
1668                 draw->moveTo(draw, &to);
1669             } else if (l->type == lineTo) {
1670                 FPOINT to;
1671                 to.x = l->x * size / 100.0 / 20.0 + advance;
1672                 to.y = l->y * size / 100.0 / 20.0;
1673                 draw->lineTo(draw, &to);
1674             } else if (l->type == splineTo) {
1675                 FPOINT mid, to;
1676                 mid.x = l->sx * size / 100.0 / 20.0 + advance;
1677                 mid.y = l->sy * size / 100.0 / 20.0;
1678                 to.x = l->x * size / 100.0 / 20.0 + advance;
1679                 to.y = l->y * size / 100.0 / 20.0;
1680                 draw->splineTo(draw, &mid, &to);
1681             }
1682             l = l->next;
1683         }
1684         swf_Shape2Free(shape2);
1685         advance += font->glyph[g].advance * size / 100.0 / 20.0;
1686     }
1687 }
1688
1689 void swf_WriteFont_AS3(SWFFONT * font, char *filename)
1690 {
1691     if(!font->layout) 
1692         swf_FontCreateLayout(font);
1693     
1694     SWF swf;
1695     memset(&swf, 0, sizeof(SWF));
1696     swf.fileVersion = 9;
1697     swf.frameRate = 0x4000;
1698     swf.movieSize.xmax = 200;
1699     swf.movieSize.ymax = 200;
1700     
1701     if(!font->id) font->id=1;
1702
1703     TAG *tag;
1704     swf.firstTag = tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1705     swf_FontSetDefine2(tag, font);
1706
1707     char*name = font->name?(char*)font->name:"font";
1708
1709     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1710     swf_SetU16(tag, font->id);
1711     swf_SetString(tag, name);
1712     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1713     swf_SetU16(tag, 1);
1714     swf_SetU16(tag, font->id);
1715     swf_SetString(tag, name);
1716     tag = swf_AddAS3FontDefine(tag, font->id, (char*)font->name);
1717     
1718     tag = swf_InsertTag(tag, ST_END);
1719     swf_SaveSWF(&swf, filename);
1720     swf_FreeTags(&swf);
1721 }
1722
1723 void swf_WriteFont(SWFFONT * font, char *filename)
1724 {
1725     if(!font->layout)
1726         swf_FontCreateLayout(font);
1727
1728     char viewer = 1;
1729     U16 id = 1;
1730     U16 depth = 1;
1731
1732     font->id = id++;
1733     
1734     SWF swf;
1735     memset(&swf, 0, sizeof(SWF));
1736     swf.fileVersion = 8;
1737     swf.frameRate = 0x4000;
1738     swf.movieSize.xmax = 1024*20;
1739     swf.movieSize.ymax = 768*20;
1740     
1741     TAG *tag;
1742     swf.firstTag = tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
1743     swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xff);
1744
1745     tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1746     swf_FontSetDefine2(tag, font);
1747
1748     if(font->glyphnames) {
1749         int c;
1750         tag = swf_InsertTag(tag, ST_GLYPHNAMES);
1751         swf_SetU16(tag, font->id);
1752         swf_SetU16(tag, font->numchars);
1753         for (c = 0; c < font->numchars; c++) {
1754             if (font->glyphnames[c])
1755                 swf_SetString(tag, font->glyphnames[c]);
1756             else
1757                 swf_SetString(tag, "");
1758         }
1759     }
1760
1761     if(viewer)
1762     {
1763         RGBA white = {255,255,255,255};
1764         RGBA black = {255,0,0,0};
1765         RGBA gray50 = {255,128,128,128};
1766         RGBA green = {255,0,255,0};
1767         int t;
1768         SCOORD miny = SCOORD_MAX;
1769         SCOORD maxy = SCOORD_MIN;
1770         double width = 0;
1771         U16 max_advance = 0;
1772         char*flags = rfx_calloc(font->numchars);
1773         double*xmin = rfx_calloc(sizeof(double)*(font->numchars+1));
1774         double*xmax = rfx_calloc(sizeof(double)*(font->numchars+1));
1775         int*xpos = rfx_calloc(sizeof(int)*(font->numchars+1));
1776         for(t=0;t<font->numchars;t++) {
1777             SHAPE*s = font->glyph[t].shape;
1778             SHAPE2*s2 = swf_ShapeToShape2(s);
1779             SRECT r = swf_GetShapeBoundingBox(s2);
1780
1781             // inside a definefont3, everything is 20x the resolution:
1782             double rx1 = r.xmin / 20.0;
1783             double ry1 = r.ymin / 20.0;
1784             double rx2 = r.xmax / 20.0;
1785             double ry2 = r.ymax / 20.0;
1786             
1787             xmin[t]= rx1;
1788             xmax[t]= rx2;
1789
1790             if(ry1<miny) {miny=ry1;}
1791             if(ry2>maxy) {maxy=ry2;}
1792             swf_Shape2Free(s2);free(s2);
1793             width += font->glyph[t].advance;
1794             if(font->glyph[t].advance>max_advance)
1795                 max_advance = font->glyph[t].advance;
1796         }
1797
1798         if(miny==SCOORD_MAX) miny=maxy=0;
1799         if(miny==maxy) maxy=miny+1;
1800
1801         /* scale the font so that it's 256 pixels high */
1802         double scale = (int)((256.0*1024.0/(maxy-miny))*20.0);
1803         double overlarge_factor;
1804         int fontsize;
1805         if(scale > 32767) {
1806             fontsize = 32767;
1807             overlarge_factor = scale / 32767.0;
1808         } else {
1809             fontsize = scale;
1810             overlarge_factor = 1.0;
1811         }
1812
1813         int textid = id++;
1814         int spriteid = id++;
1815         SRECT r;
1816         r.xmin = 0;
1817         r.ymin = miny*fontsize/1024;
1818         r.xmax = width*fontsize/20480;
1819         r.ymax = maxy*fontsize/1024;
1820         tag = swf_InsertTag(tag, ST_DEFINETEXT);
1821         swf_SetU16(tag, textid);
1822         swf_SetRect(tag, &r);
1823         swf_SetMatrix(tag, NULL);
1824
1825         U8 abits = 15;
1826         U8 gbits = swf_CountBits(font->numchars, 0);
1827         swf_SetU8(tag, gbits);
1828         swf_SetU8(tag, abits);
1829
1830         RGBA rgb = {255,0,0,0};
1831
1832         swf_TextSetInfoRecord(tag, font, fontsize, &rgb, SET_TO_ZERO, SET_TO_ZERO);
1833         ActionTAG*array = 0;
1834         double x=0;
1835         array = action_PushString(array, "xpos");
1836         for(t=0;t<font->numchars;t++) {
1837             swf_SetU8(tag, 1);
1838             int width = abs((xmax[t] - xmin[t+1])*fontsize/1024) + 60;
1839             array = action_PushInt(array, x/20 +(xmin[t]*scale/1024)/20);
1840             x += width * overlarge_factor;
1841             swf_SetBits(tag, t, gbits);
1842             swf_SetBits(tag, width, abits);
1843             swf_SetU8(tag, 128);
1844         }
1845         array = action_PushInt(array, x/20);
1846         array = action_PushInt(array, font->numchars+1);
1847         array = action_InitArray(array);
1848         array = action_SetVariable(array);
1849         swf_SetU8(tag, 0);
1850
1851         if(font->layout) {
1852             tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1853             SHAPE* s;
1854             swf_ShapeNew(&s);
1855             int ls = swf_ShapeAddLineStyle(s,20,&white);
1856             int shapeid = id++;
1857             swf_SetU16(tag,shapeid);
1858             SRECT r;
1859             r.xmin = 0;
1860             r.xmax = 1024*20;
1861             r.ymin = 0;
1862             r.ymax = 256*20;
1863             swf_SetRect(tag,&r);
1864             swf_SetShapeHeader(tag,s);
1865             swf_ShapeSetAll(tag,s,0,0,ls,0,0);
1866
1867             /* Ç and Â are good chars to test ascent/descent extend */
1868             int y1 = (-font->layout->ascent-miny*20.0)*256.0/(maxy-miny);
1869             int y2 = (font->layout->descent-miny*20.0)*256.0/(maxy-miny);
1870
1871             swf_ShapeSetMove(tag,s,0,y1);
1872             swf_ShapeSetLine(tag,s,width,0);
1873             swf_ShapeSetMove(tag,s,0,y2);
1874             swf_ShapeSetLine(tag,s,width,0);
1875
1876             swf_ShapeSetEnd(tag);
1877             swf_ShapeFree(s);
1878             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1879             swf_ObjectPlace(tag, shapeid, depth++, NULL, NULL, NULL);
1880         }
1881
1882         /* shapes */
1883         
1884         for(t=0;t<font->numchars;t++) {
1885             tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1886             SHAPE* s;
1887             swf_ShapeNew(&s);
1888             int ls = swf_ShapeAddLineStyle(s,20*2,&black);
1889             int ls2 = swf_ShapeAddLineStyle(s,20*2,&green);
1890             int fs = swf_ShapeAddSolidFillStyle(s, &gray50);
1891             int shapeid = id++;
1892             swf_SetU16(tag,shapeid);
1893             SRECT r;
1894             r.xmin = 0;
1895             r.xmax = 1024*20;
1896             r.ymin = 0;
1897             r.ymax = 512*20;
1898             swf_SetRect(tag,&r);
1899             swf_SetShapeHeader(tag,s);
1900             swf_ShapeSetAll(tag,s,0,0,ls,fs,0);
1901             SHAPE2*s2 = swf_ShapeToShape2(font->glyph[t].shape);
1902             SHAPELINE*l = s2->lines;
1903             int lastx=0,lasty=0;
1904
1905             double x1 = (1024*20 - (xmax[t] - xmin[t])*20*2*scale/20480.0)/2;
1906             double y1 = -miny*20*scale*2/20480.0;
1907             double scalex = scale*2/20480.0;
1908             double scaley = scale*2/20480.0;
1909
1910             while(l) {
1911                 int lx = (l->x)*scalex+x1;
1912                 int ly = (l->y)*scaley+y1;
1913                 int sx = (l->sx)*scalex+x1;
1914                 int sy = (l->sy)*scaley+y1;
1915                 if(l->type == moveTo) {
1916                     swf_ShapeSetMove(tag,s,lx,ly);
1917                 } else if(l->type == lineTo) {
1918                     swf_ShapeSetLine(tag,s,lx-lastx,ly-lasty);
1919                 } else if(l->type == splineTo) {
1920                     swf_ShapeSetCurve(tag,s,sx-lastx,sy-lasty,lx-sx,ly-sy);
1921                 }
1922                 lastx = lx;
1923                 lasty = ly;
1924                 l = l->next;
1925             }
1926             
1927             if(font->alignzones) {
1928                 ALIGNZONE*zone = &font->alignzones[t];
1929                 swf_ShapeSetAll(tag,s,0,0,ls2,SET_TO_ZERO,SET_TO_ZERO);
1930                 if((zone->x&zone->dx)!=0xffff) {
1931                     double x = F16toFloat(zone->x)*20480.0*scalex+x1;
1932                     double dx = (F16toFloat(zone->x)+F16toFloat(zone->dx))*20480.0*scalex+x1;
1933                     swf_ShapeSetMove(tag,s,x,0);
1934                     swf_ShapeSetLine(tag,s,0,1024*20);
1935                     swf_ShapeSetMove(tag,s,dx,0);
1936                     swf_ShapeSetLine(tag,s,0,1024*20);
1937                 }
1938                 if((zone->y&zone->dy)!=0xffff) {
1939                     double y = -F16toFloat(zone->y)*20480.0*scaley+y1;
1940                     double dy = -(F16toFloat(zone->y)+F16toFloat(zone->dy))*20480.0*scaley+y1;
1941                     swf_ShapeSetMove(tag,s,0,y);
1942                     swf_ShapeSetLine(tag,s,1024*20,0);
1943                     swf_ShapeSetMove(tag,s,0,dy);
1944                     swf_ShapeSetLine(tag,s,1024*20,0);
1945                 }
1946             }
1947
1948             swf_ShapeSetEnd(tag);
1949             swf_ShapeFree(s);
1950         
1951             tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1952             U16 spriteid=id++;
1953             swf_SetU16(tag, spriteid);
1954             swf_SetU16(tag, 1);
1955             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1956             swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
1957             tag = swf_InsertTag(tag, ST_END);
1958             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1959             MATRIX m;
1960             swf_GetMatrix(0, &m);
1961             m.ty = 20000;
1962             char txt[80];
1963             sprintf(txt, "char%d", font->numchars-t);
1964             swf_ObjectPlace(tag, spriteid, depth++, &m, NULL, txt);
1965         }
1966         
1967         /* marker */
1968         tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1969         int shapeid=id++;
1970         RGBA blue = {0xff,0xc0,0xc0,0xff};
1971         swf_ShapeSetRectangle(tag, shapeid, 20, 20, &blue);
1972         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1973         U16 spriteid2=id++;
1974         swf_SetU16(tag, spriteid2);
1975         swf_SetU16(tag, 1);
1976         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1977         swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
1978         tag = swf_InsertTag(tag, ST_END);
1979         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1980         swf_ObjectPlace(tag, spriteid2, depth++, NULL, NULL, "marker");
1981         
1982         /* textbar */
1983         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1984         swf_SetU16(tag, spriteid);
1985         swf_SetU16(tag, 1);
1986         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1987         MATRIX m;
1988         swf_GetMatrix(0, &m);
1989         m.sx = 65536 * overlarge_factor;
1990         m.sy = 65536 * overlarge_factor;
1991         m.tx = 0;
1992         m.ty = -miny*256*20/(maxy-miny);
1993         swf_ObjectPlace(tag, textid, 1, &m, NULL, NULL);
1994         tag = swf_InsertTag(tag, ST_END);
1995         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1996         swf_ObjectPlace(tag, spriteid, depth++, NULL, NULL, "textbar");
1997         
1998         /* marker2 */
1999         RGBA blue2 = {0x80,0x80,0xff,0x80};
2000         tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
2001         int shapeid2=id++;
2002         swf_ShapeSetRectangleWithBorder(tag, shapeid2, 20, 20, &blue2, 0, &white);
2003         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2004         U16 spriteid3=id++;
2005         swf_SetU16(tag, spriteid3);
2006         swf_SetU16(tag, 1);
2007         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2008         swf_ObjectPlace(tag, shapeid2, 1, NULL, NULL, NULL);
2009         tag = swf_InsertTag(tag, ST_END);
2010         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2011         swf_ObjectPlace(tag, spriteid3, depth++, NULL, NULL, "marker2");
2012
2013
2014 char*data = 
2015 " var mouseListener = new Object();"
2016 " var speed = 0;"
2017 " var myx = 0;"
2018 " var currentMouseOver, currentChar;"
2019 " mouseListener.onMouseDown = function() { "
2020 "     eval(\"_root.char\"+currentChar)._y = 20000;"
2021 "     currentChar = currentMouseOver;"
2022 "     var i = currentMouseOver;"
2023 "     eval(\"_root.char\"+i)._y = 256;"
2024 "     _root.marker2._yscale=256*100;"
2025 "     _root.marker2._xscale=(xpos[i-1]-xpos[i])*100;"
2026 "     _root.marker2._x=xpos[i]+myx;"
2027 " };"
2028 " mouseListener.onMouseMove = function() { "
2029 "     if(_ymouse<256) {"
2030 "          speed = Math.abs(_xmouse-512)>256?(512-_xmouse)/8:0;"
2031 "     } else {"
2032 "          speed = 0;"
2033 "     }; "
2034 " }; "
2035 " setInterval( function(){ "
2036 "     if(_ymouse<256) {"
2037 "         var i, x=_xmouse-_root.textbar._x;"
2038 "         for(i=xpos.length-1;i>0;i--) {"
2039 "             if(x<xpos[i-1]) break;"
2040 "         }"
2041 "         currentMouseOver = i;"
2042 "         _root.marker._yscale=256*100;"
2043 "         _root.marker._xscale=(xpos[i-1]-xpos[i])*100;"
2044 "         _root.marker._x=xpos[i]+myx;"
2045 "         _root.textbar._x += 0.05;"
2046 "     }"
2047 "     if(myx+speed>0) {"
2048 "         speed=0;"
2049 "     } else if(myx+speed<-xpos[0]+1024) {"
2050 "         speed=0;"
2051 "     }"
2052 "     myx+=speed;"
2053 "     _root.textbar._x = myx;"
2054 "     _root.marker._x += speed;"
2055 "     _root.marker2._x += speed;"
2056 " }, 20);"
2057 " Mouse.addListener(mouseListener);"
2058 ;
2059         ActionTAG* atag = swf_ActionCompile(data, 6);
2060
2061         tag = swf_InsertTag(tag, ST_DOACTION);
2062         swf_ActionSet(tag, array);
2063         swf_ActionSet(tag, atag);
2064         swf_SetU8(tag, 0);
2065         swf_ActionFree(atag);
2066
2067         tag = swf_InsertTag(tag, ST_SHOWFRAME);
2068
2069         free(flags);
2070         free(xmin);
2071         free(xmax);
2072     }
2073
2074     tag = swf_InsertTag(tag, ST_END);
2075
2076     swf.compressed = -1;
2077     swf_SaveSWF(&swf, filename);
2078     swf_FreeTags(&swf);
2079 }