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