c473211b5ceba1a9cba34f5a76ba63845137957e
[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     int nr = swf_FontUseGetPair(f, char1, char2);
971     if(!nr) {
972         if(u->num_neighbors == u->neighbors_size) {
973             u->neighbors_size += 4096;
974             u->neighbors = rfx_realloc(u->neighbors, sizeof(SWFGLYPHPAIR)*u->neighbors_size);
975         }
976         u->neighbors[u->num_neighbors].char1 = char1;
977         u->neighbors[u->num_neighbors].char2 = char2;
978         u->neighbors[u->num_neighbors].num = 1;
979         hashadd(u, char1, char2, u->num_neighbors);
980         u->num_neighbors++;
981     } else {
982         u->neighbors[nr-1].num++;
983     }
984 }
985
986 int swf_FontUseGlyph(SWFFONT * f, int glyph, U16 size)
987 {
988     if (!f->use)
989         swf_FontInitUsage(f);
990     if(glyph < 0 || glyph >= f->numchars)
991         return -1;
992     if(!f->use->chars[glyph])
993         f->use->used_glyphs++;
994     f->use->chars[glyph] = 1;
995     if(size && size < f->use->smallest_size)
996         f->use->smallest_size = size;
997     return 0;
998 }
999
1000 int swf_FontSetDefine(TAG * t, SWFFONT * f)
1001 {
1002     U16 *ofs = (U16 *) rfx_alloc(f->numchars * 2);
1003     int p, i, j;
1004
1005     if ((!t) || (!f))
1006         return -1;
1007     swf_ResetWriteBits(t);
1008     swf_SetU16(t, f->id);
1009
1010     p = 0;
1011     j = 0;
1012     for (i = 0; i < f->numchars; i++)
1013         if (f->glyph[i].shape) {
1014             ofs[j++] = p;
1015             p += swf_SetSimpleShape(NULL, f->glyph[i].shape);
1016         }
1017
1018     for (i = 0; i < j; i++)
1019         swf_SetU16(t, ofs[i] + j * 2);
1020     if (!j) {
1021         fprintf(stderr, "rfxswf: warning: Font is empty\n");
1022         swf_SetU16(t, 0);
1023     }
1024
1025     for (i = 0; i < f->numchars; i++)
1026         if (f->glyph[i].shape)
1027             swf_SetSimpleShape(t, f->glyph[i].shape);
1028
1029     swf_ResetWriteBits(t);
1030     rfx_free(ofs);
1031     return 0;
1032 }
1033
1034 static inline int fontSize(SWFFONT * font)
1035 {
1036     int t;
1037     int size = 0;
1038     for (t = 0; t < font->numchars; t++) {
1039         int l = 0;
1040         if(font->glyph[t].shape)
1041             l = (font->glyph[t].shape->bitlen + 7) / 8;
1042         else
1043             l = 8;
1044         size += l + 1;
1045     }
1046     return size + (font->numchars + 1) * 2;
1047 }
1048
1049 int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
1050 {
1051     U8 flags = 0;
1052     int t;
1053     int pos;
1054     swf_SetU16(tag, f->id);
1055
1056     if (f->layout) flags |= 128;                // haslayout
1057     if (f->numchars > 256)
1058         flags |= 4;             // widecodes
1059     if (f->style & FONT_STYLE_BOLD)
1060         flags |= 1;             // bold
1061     if (f->style & FONT_STYLE_ITALIC)
1062         flags |= 2;             // italic
1063     if (f->maxascii >= 256)
1064         flags |= 4;             //wide codecs
1065     if (fontSize(f) > 65535)
1066         flags |= 8;             //wide offsets
1067     flags |= 8 | 4;             //FIXME: the above check doesn't work
1068
1069     if (f->encoding & FONT_ENCODING_ANSI)
1070         flags |= 16;            // ansi
1071     if (f->encoding & FONT_ENCODING_UNICODE)
1072         flags |= 32;            // unicode
1073     if (f->encoding & FONT_ENCODING_SHIFTJIS)
1074         flags |= 64;            // shiftjis
1075
1076     swf_SetU8(tag, flags);
1077     swf_SetU8(tag, 0);          //reserved flags
1078     if (f->name) {
1079         /* font name */
1080         swf_SetU8(tag, strlen((const char*)f->name)+1);
1081         swf_SetBlock(tag, f->name, strlen((const char*)f->name)+1);
1082     } else {
1083         /* font name (="") */
1084         swf_SetU8(tag, 1);
1085         swf_SetU8(tag, 0);
1086     }
1087     /* number of glyphs */
1088     swf_SetU16(tag, f->numchars);
1089     /* font offset table */
1090     pos = tag->len;
1091     for (t = 0; t <= f->numchars; t++) {
1092         if (flags & 8)
1093             swf_SetU32(tag, /* fontoffset */ 0);        /*placeholder */
1094         else
1095             swf_SetU16(tag, /* fontoffset */ 0);        /*placeholder */
1096     }
1097
1098     for (t = 0; t <= f->numchars; t++) {
1099         if (flags & 8) {
1100             tag->data[pos + t * 4] = (tag->len - pos);
1101             tag->data[pos + t * 4 + 1] = (tag->len - pos) >> 8;
1102             tag->data[pos + t * 4 + 2] = (tag->len - pos) >> 16;
1103             tag->data[pos + t * 4 + 3] = (tag->len - pos) >> 24;
1104         } else {
1105             if (tag->len - pos > 65535) {
1106                 fprintf(stderr, "Internal error: Font too big and WideOffsets flag not set\n");
1107                 exit(1);
1108             }
1109             tag->data[pos + t * 2] = (tag->len - pos);
1110             tag->data[pos + t * 2 + 1] = (tag->len - pos) >> 8;
1111         }
1112         if (t < f->numchars) {
1113             if(f->glyph[t].shape) {
1114                 swf_SetSimpleShape(tag, f->glyph[t].shape);
1115             } else {
1116                 swf_SetU8(tag, 0); //non-edge(1) + edge flags(5)
1117             }
1118         }
1119     }
1120
1121
1122     /* font code table */
1123     for (t = 0; t < f->numchars; t++) {
1124         if (flags & 4) {                /* wide codes */
1125             if(f->glyph2ascii[t]) {
1126                 swf_SetU16(tag, f->glyph2ascii[t]);
1127             } else {
1128                 swf_SetU16(tag, 0);
1129             }
1130         } else {
1131             if(f->glyph2ascii[t]) {
1132                 swf_SetU8(tag, f->glyph2ascii[t]);
1133             } else {
1134                 swf_SetU8(tag, 0);
1135             }
1136         }
1137     }
1138
1139     if (f->layout) {
1140         swf_SetU16(tag, f->layout->ascent);
1141         swf_SetU16(tag, f->layout->descent);
1142         swf_SetU16(tag, f->layout->leading);
1143         for (t = 0; t < f->numchars; t++)
1144             swf_SetU16(tag, f->glyph[t].advance);
1145         for (t = 0; t < f->numchars; t++) {
1146             swf_ResetWriteBits(tag);
1147             swf_SetRect(tag, &f->layout->bounds[t]);
1148         }
1149         swf_SetU16(tag, f->layout->kerningcount);
1150         for (t = 0; t < f->layout->kerningcount; t++) {
1151             if (flags & 4) {    /* wide codes */
1152                 swf_SetU16(tag, f->layout->kerning[t].char1);
1153                 swf_SetU16(tag, f->layout->kerning[t].char2);
1154             } else {
1155                 swf_SetU8(tag, f->layout->kerning[t].char1);
1156                 swf_SetU8(tag, f->layout->kerning[t].char2);
1157             }
1158             swf_SetU16(tag, f->layout->kerning[t].adjustment);
1159         }
1160     }
1161     return 0;
1162 }
1163
1164 void swf_FontAddLayout(SWFFONT * f, int ascent, int descent, int leading)
1165 {
1166     f->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
1167     f->layout->ascent = ascent;
1168     f->layout->descent = descent;
1169     f->layout->leading = leading;
1170     f->layout->kerningcount = 0;
1171     f->layout->kerning = 0;
1172     f->layout->bounds = (SRECT *) rfx_calloc(sizeof(SRECT) * f->numchars);
1173 }
1174
1175 int swf_FontSetInfo(TAG * t, SWFFONT * f)
1176 {
1177     int l, i;
1178     U8 wide = 0;
1179     U8 flags = 0;
1180     if ((!t) || (!f))
1181         return -1;
1182     swf_ResetWriteBits(t);
1183     swf_SetU16(t, f->id);
1184     l = f->name ? strlen((const char *)f->name) : 0;
1185     if (l > 255)
1186         l = 255;
1187     swf_SetU8(t, l);
1188     if (l)
1189         swf_SetBlock(t, f->name, l);
1190     if (f->numchars >= 256)
1191         wide = 1;
1192
1193     if (f->style & FONT_STYLE_BOLD)
1194         flags |= 2;
1195     if (f->style & FONT_STYLE_ITALIC)
1196         flags |= 4;
1197     if (f->style & FONT_ENCODING_ANSI)
1198         flags |= 8;
1199     if (f->style & FONT_ENCODING_SHIFTJIS)
1200         flags |= 16;
1201     if (f->style & FONT_ENCODING_UNICODE)
1202         flags |= 32;
1203
1204     swf_SetU8(t, (flags & 0xfe) | wide);
1205
1206     for (i = 0; i < f->numchars; i++) {
1207         if (f->glyph[i].shape) {
1208             int g2a = f->glyph2ascii?f->glyph2ascii[i]:0;
1209             wide ? swf_SetU16(t, g2a) : swf_SetU8(t, g2a);
1210         }
1211     }
1212
1213     return 0;
1214 }
1215
1216 int swf_TextPrintDefineText(TAG * t, SWFFONT * f)
1217 {
1218     int id = swf_GetTagID(t);
1219     if ((id == ST_DEFINETEXT) || (id == ST_DEFINETEXT2))
1220         swf_FontExtract_DefineText(f->id, f, t, FEDTJ_PRINT);
1221     else
1222         return -1;
1223     return 0;
1224 }
1225
1226 void swf_FontFree(SWFFONT * f)
1227 {
1228     int i;
1229     if (!f)
1230         return;
1231
1232     if (f->glyph)
1233     {
1234         for (i = 0; i < f->numchars; i++)
1235             if (f->glyph[i].shape)
1236             {
1237                 swf_ShapeFree(f->glyph[i].shape);
1238                 f->glyph[i].shape = NULL;
1239             }
1240             rfx_free(f->glyph);
1241             f->glyph = NULL;
1242     }
1243     if (f->ascii2glyph)
1244     {
1245         rfx_free(f->ascii2glyph);
1246         f->ascii2glyph = NULL;
1247     }
1248     if (f->glyph2ascii)
1249     {
1250         rfx_free(f->glyph2ascii);
1251         f->glyph2ascii = NULL;
1252     }
1253     font_freename(f);
1254     font_freelayout(f);
1255     font_freeglyphnames(f);
1256     font_freeusage(f);
1257
1258     rfx_free(f);
1259 }
1260
1261 int swf_TextSetInfoRecord(TAG * t, SWFFONT * font, U16 size, RGBA * color, int x, int y)
1262 {
1263     U8 flags;
1264     if (!t)
1265         return -1;
1266
1267     flags = TF_TEXTCONTROL | (font ? TF_HASFONT : 0) | (color ? TF_HASCOLOR : 0) | (x ? TF_HASXOFFSET : 0)
1268         | (y ? TF_HASYOFFSET : 0);
1269
1270     swf_SetU8(t, flags);
1271     if (font)
1272         swf_SetU16(t, font->id);
1273     if (color) {
1274         if (swf_GetTagID(t) == ST_DEFINETEXT2)
1275             swf_SetRGBA(t, color);
1276         else
1277             swf_SetRGB(t, color);
1278     }
1279     if (x) {
1280         if(x != SET_TO_ZERO) {
1281             if(x>32767 || x<-32768)
1282                 fprintf(stderr, "Warning: Horizontal char position overflow: %d\n", x);
1283             swf_SetS16(t, x);
1284         } else {
1285             swf_SetS16(t, 0);
1286         }
1287     }
1288     if (y) {
1289         if(y != SET_TO_ZERO) {
1290             if(y>32767 || y<-32768)
1291                 fprintf(stderr, "Warning: Vertical char position overflow: %d\n", y);
1292             swf_SetS16(t, y);
1293         } else {
1294             swf_SetS16(t, 0);
1295         }
1296     }
1297     if (font)
1298         swf_SetU16(t, size);
1299
1300     return 0;
1301 }
1302
1303 static int swf_TextCountBits2(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits, char *encoding)
1304 {
1305     U16 g, a;
1306     char utf8 = 0;
1307     if ((!s) || (!font) || ((!gbits) && (!abits)) || (!font->ascii2glyph))
1308         return -1;
1309     g = a = 0;
1310
1311     if (!strcmp(encoding, "UTF8"))
1312         utf8 = 1;
1313     else if (!strcmp(encoding, "iso-8859-1"))
1314         utf8 = 0;
1315     else
1316         fprintf(stderr, "Unknown encoding: %s", encoding);
1317
1318     while (*s) {
1319         int glyph = -1, c;
1320
1321         if (!utf8)
1322             c = *s++;
1323         else
1324             c = readUTF8char(&s);
1325
1326         if (c < font->maxascii)
1327             glyph = font->ascii2glyph[c];
1328         if (glyph >= 0) {
1329             g = swf_CountUBits(glyph, g);
1330             a = swf_CountBits(((((U32) font->glyph[glyph].advance) * scale) / 20) / 100, a);
1331         }
1332     }
1333
1334     if (gbits)
1335         gbits[0] = (U8) g;
1336     if (abits)
1337         abits[0] = (U8) a;
1338     return 0;
1339 }
1340
1341 static int swf_TextSetCharRecord2(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits, char *encoding)
1342 {
1343     int l = 0, pos;
1344     char utf8 = 0;
1345
1346     if ((!t) || (!font) || (!s) || (!font->ascii2glyph))
1347         return -1;
1348
1349     if (!strcmp(encoding, "UTF8"))
1350         utf8 = 1;
1351     else if (!strcmp(encoding, "iso-8859-1"))
1352         utf8 = 0;
1353     else
1354         fprintf(stderr, "Unknown encoding: %s", encoding);
1355
1356     pos = t->len;
1357     swf_SetU8(t, l);            //placeholder
1358
1359     while (*s) {
1360         int g = -1, c;
1361
1362         if (!utf8)
1363             c = *s++;
1364         else
1365             c = readUTF8char(&s);
1366
1367         if (c < font->maxascii)
1368             g = font->ascii2glyph[c];
1369         if (g >= 0) {
1370             swf_SetBits(t, g, gbits);
1371             swf_SetBits(t, ((((U32) font->glyph[g].advance) * scale) / 20) / 100, abits);
1372             l++;
1373             /* We split into 127 characters per text field.
1374                We could do 255, by the (formerly wrong) flash specification,
1375                but some SWF parsing code out there still assumes that char blocks
1376                are at max 127 characters, and it would save only a few bits.
1377             */
1378             if (l == 0x7f)
1379                 break;
1380         }
1381     }
1382
1383     PUT8(&t->data[pos], l);
1384
1385     swf_ResetWriteBits(t);
1386     return 0;
1387 }
1388
1389 int swf_TextCountBits(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1390 {
1391     return swf_TextCountBits2(font, s, scale, gbits, abits, "iso-8859-1");
1392 }
1393
1394 int swf_TextSetCharRecord(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1395 {
1396     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "iso-8859-1");
1397 }
1398
1399 int swf_TextCountBitsUTF8(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
1400 {
1401     return swf_TextCountBits2(font, s, scale, gbits, abits, "UTF8");
1402 }
1403
1404 int swf_TextSetCharRecordUTF8(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
1405 {
1406     return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "UTF8");
1407 }
1408
1409 U32 swf_TextGetWidth(SWFFONT * font, U8 * s, int scale)
1410 {
1411     U32 res = 0;
1412
1413     if (font && s) {
1414         while (s[0]) {
1415             int g = -1;
1416             if (*s < font->maxascii)
1417                 g = font->ascii2glyph[*s];
1418             if (g >= 0)
1419                 res += font->glyph[g].advance / 20;
1420             s++;
1421         }
1422         if (scale)
1423             res = (res * scale) / 100;
1424     }
1425     return res;
1426 }
1427
1428 SRECT swf_TextCalculateBBoxUTF8(SWFFONT * font, U8 * s, int scale)
1429 {
1430     int xpos = 0;
1431     int ypos = 0;
1432     SRECT r;
1433     swf_GetRect(0, &r);
1434     while (*s) {
1435         int c = readUTF8char(&s);
1436         if(c==13 || c==10) {
1437             if(s[0] == 10) {
1438                 s++;
1439             }
1440             xpos=0;
1441             ypos+=font->layout->leading;
1442             continue;
1443         }
1444         if (c < font->maxascii) {
1445             int g = font->ascii2glyph[c];
1446             if (g >= 0) {
1447                 SRECT rn = font->layout->bounds[g];
1448                 rn.xmin = (rn.xmin * scale) / 20 / 100 + xpos;
1449                 rn.xmax = (rn.xmax * scale) / 20 / 100 + xpos;
1450                 rn.ymin = (rn.ymin * scale) / 20 / 100 + ypos;
1451                 rn.ymax = (rn.ymax * scale) / 20 / 100 + ypos;
1452                 swf_ExpandRect2(&r, &rn);
1453                 xpos += (font->glyph[g].advance * scale) / 20 / 100;
1454             }
1455         }
1456     }
1457     return r;
1458 }
1459
1460
1461 SWFFONT *swf_ReadFont(const char *filename)
1462 {
1463     int f;
1464     SWF swf;
1465     if (!filename)
1466         return 0;
1467     f = open(filename, O_RDONLY|O_BINARY);
1468
1469     if (f < 0 || swf_ReadSWF(f, &swf) < 0) {
1470         fprintf(stderr, "%s is not a valid SWF font file or contains errors.\n", filename);
1471         close(f);
1472         return 0;
1473     } else {
1474         SWFFONT *font;
1475         close(f);
1476         if (swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
1477             return 0;
1478         swf_FreeTags(&swf);
1479         return font;
1480     }
1481 }
1482
1483 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)
1484 {
1485     swf_SetRect(tag, &r);
1486     swf_ResetWriteBits(tag);
1487
1488     flags &= ~(ET_HASTEXT | ET_HASTEXTCOLOR | ET_HASMAXLENGTH | ET_HASFONT | ET_HASLAYOUT);
1489     if (text)
1490         flags |= ET_HASTEXT;
1491     if (color)
1492         flags |= ET_HASTEXTCOLOR;
1493     if (maxlength)
1494         flags |= ET_HASMAXLENGTH;
1495     if (font)
1496         flags |= ET_HASFONT;
1497     if (layout)
1498         flags |= ET_HASLAYOUT;
1499
1500     swf_SetBits(tag, flags, 16);
1501
1502     if (flags & ET_HASFONT) {
1503         swf_SetU16(tag, font);  //font
1504         swf_SetU16(tag, height);        //fontheight
1505     }
1506     if (flags & ET_HASTEXTCOLOR) {
1507         swf_SetRGBA(tag, color);
1508     }
1509     if (flags & ET_HASMAXLENGTH) {
1510         swf_SetU16(tag, maxlength);     //maxlength
1511     }
1512     if (flags & ET_HASLAYOUT) {
1513         swf_SetU8(tag, layout->align);  //align
1514         swf_SetU16(tag, layout->leftmargin);    //left margin
1515         swf_SetU16(tag, layout->rightmargin);   //right margin
1516         swf_SetU16(tag, layout->indent);        //indent
1517         swf_SetU16(tag, layout->leading);       //leading
1518     }
1519     swf_SetString(tag, variable);
1520     if (flags & ET_HASTEXT)
1521         swf_SetString(tag, text);
1522 }
1523
1524 SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, const char *text, int scale)
1525 {
1526     SRECT r;
1527     U8 gbits, abits;
1528     U8 *utext = (U8 *) strdup(text);
1529     U8 *upos = utext;
1530     int x = 0, y = 0;
1531     int pos = 0;
1532     int ystep = 0;
1533     if (font->layout) {
1534         r = swf_TextCalculateBBoxUTF8(font, (U8*)text, scale * 20);
1535         ystep = font->layout->leading;
1536     } else {
1537         fprintf(stderr, "No layout information- can't compute text bbox accurately");
1538         /* Hm, without layout information, we can't compute a bounding
1539            box. We could call swf_FontCreateLayout to create a layout,
1540            but the caller probably doesn't want us to mess up his font
1541            structure.
1542          */
1543         r.xmin = r.ymin = 0;
1544         r.xmax = r.ymax = 1024 * 20;
1545         ystep = 100;
1546     }
1547
1548     swf_SetRect(tag, &r);
1549
1550     /* The text matrix is pretty boring, as it doesn't apply to
1551        individual characters, but rather whole text objects (or
1552        at least whole char records- haven't tested).
1553        So it can't do anything which we can't already do with
1554        the placeobject tag we use for placing the text on the scene.
1555      */
1556     swf_SetMatrix(tag, 0);
1557
1558     swf_TextCountBitsUTF8(font, (U8*)text, scale * 20, &gbits, &abits);
1559     swf_SetU8(tag, gbits);
1560     swf_SetU8(tag, abits);
1561
1562     while(*upos) {
1563         U8*next = upos;
1564         int count = 0;
1565
1566         swf_TextSetInfoRecord(tag, font, (scale * 1024) / 100, rgb, x, y);      //scale
1567         x = 0;
1568
1569         while(*next && *next!=13 && *next!=10 && count<127) {
1570             readUTF8char(&next);
1571             count++;
1572         }
1573         if(next[0] == 13 || next[0] == 10) {
1574             x = SET_TO_ZERO;
1575             y += ystep;
1576         }
1577
1578         if(next[0] == 13 && next[1] == 10)
1579             next++;
1580
1581         if(next[0] == 13 || next[0] == 10) {
1582             *next = 0;
1583             next++;
1584         }
1585
1586         /* now set the text params- notice that a font size of
1587            1024 (or 1024*20 for definefont3) means that the glyphs will 
1588            be displayed exactly as they would be in/with a defineshape.
1589            This is not documented in the specs.
1590          */
1591
1592         /* set the actual text- notice that we just pass our scale
1593            parameter over, as TextSetCharRecord calculates with
1594            percent, too */
1595         swf_TextSetCharRecordUTF8(tag, font, upos, scale * 20, gbits, abits);
1596
1597         upos= next;
1598     }
1599     free(utext);
1600
1601     swf_SetU8(tag, 0);
1602     return r;
1603 }
1604
1605 void swf_FontCreateLayout(SWFFONT * f)
1606 {
1607     S16 leading = 0;
1608     int t;
1609     if (f->layout)
1610         return;
1611     if (!f->numchars)
1612         return;
1613
1614     f->layout = (SWFLAYOUT *) rfx_calloc(sizeof(SWFLAYOUT));
1615     f->layout->bounds = (SRECT *) rfx_alloc(f->numchars * sizeof(SRECT));
1616     f->layout->ascent = 0;
1617     f->layout->descent = 0;
1618
1619     for (t = 0; t < f->numchars; t++) {
1620         SHAPE2 *shape2;
1621         SRECT bbox;
1622         int width;
1623         shape2 = swf_ShapeToShape2(f->glyph[t].shape);
1624         if (!shape2) {
1625             fprintf(stderr, "Shape parse error\n");
1626             exit(1);
1627         }
1628         bbox = swf_GetShapeBoundingBox(shape2);
1629         swf_Shape2Free(shape2);
1630         f->layout->bounds[t] = bbox;
1631
1632         width = (bbox.xmax);
1633
1634         /* The following is a heuristic- it may be that extractfont_DefineText
1635            has already found out some widths for individual characters (from the way
1636            they are used)- we now have to guess whether that width might be possible,
1637            which is the case if it isn't either much too big or much too small */
1638         if (width > f->glyph[t].advance * 3 / 2 || width < f->glyph[t].advance / 2)
1639             f->glyph[t].advance = width;
1640
1641         if (-bbox.ymin > f->layout->ascent)
1642             f->layout->ascent = -bbox.ymin;
1643         if (bbox.ymax > f->layout->descent)
1644             f->layout->descent = bbox.ymax;
1645     }
1646 }
1647
1648 void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, const char *text)
1649 {
1650     U8 *s = (U8 *) text;
1651     int advance = 0;
1652     while (*s) {
1653         SHAPE *shape;
1654         SHAPE2 *shape2;
1655         SHAPELINE *l;
1656         U32 c = readUTF8char(&s);
1657         int g = font->ascii2glyph[c];
1658         shape = font->glyph[g].shape;
1659         if (((int) g) < 0) {
1660             fprintf(stderr, "No char %d in font %s\n", c, font->name ? (char *) font->name : "?");
1661             continue;
1662         }
1663         shape2 = swf_ShapeToShape2(shape);
1664         l = shape2->lines;
1665         while (l) {
1666             if (l->type == moveTo) {
1667                 FPOINT to;
1668                 to.x = l->x * size / 100.0 / 20.0 + advance;
1669                 to.y = l->y * size / 100.0 / 20.0;
1670                 draw->moveTo(draw, &to);
1671             } else if (l->type == lineTo) {
1672                 FPOINT to;
1673                 to.x = l->x * size / 100.0 / 20.0 + advance;
1674                 to.y = l->y * size / 100.0 / 20.0;
1675                 draw->lineTo(draw, &to);
1676             } else if (l->type == splineTo) {
1677                 FPOINT mid, to;
1678                 mid.x = l->sx * size / 100.0 / 20.0 + advance;
1679                 mid.y = l->sy * size / 100.0 / 20.0;
1680                 to.x = l->x * size / 100.0 / 20.0 + advance;
1681                 to.y = l->y * size / 100.0 / 20.0;
1682                 draw->splineTo(draw, &mid, &to);
1683             }
1684             l = l->next;
1685         }
1686         swf_Shape2Free(shape2);
1687         advance += font->glyph[g].advance * size / 100.0 / 20.0;
1688     }
1689 }
1690
1691 void swf_WriteFont_AS3(SWFFONT * font, char *filename)
1692 {
1693     if(!font->layout) 
1694         swf_FontCreateLayout(font);
1695     
1696     SWF swf;
1697     memset(&swf, 0, sizeof(SWF));
1698     swf.fileVersion = 9;
1699     swf.frameRate = 0x4000;
1700     swf.movieSize.xmax = 200;
1701     swf.movieSize.ymax = 200;
1702     
1703     if(!font->id) font->id=1;
1704
1705     TAG *tag;
1706     swf.firstTag = tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1707     swf_FontSetDefine2(tag, font);
1708
1709     char*name = font->name?(char*)font->name:"font";
1710
1711     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1712     swf_SetU16(tag, font->id);
1713     swf_SetString(tag, name);
1714     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1715     swf_SetU16(tag, 1);
1716     swf_SetU16(tag, font->id);
1717     swf_SetString(tag, name);
1718     tag = swf_AddAS3FontDefine(tag, font->id, (char*)font->name);
1719     
1720     tag = swf_InsertTag(tag, ST_END);
1721     swf_SaveSWF(&swf, filename);
1722     swf_FreeTags(&swf);
1723 }
1724
1725 void swf_WriteFont(SWFFONT * font, char *filename)
1726 {
1727     if(!font->layout)
1728         swf_FontCreateLayout(font);
1729
1730     char viewer = 1;
1731     U16 id = 1;
1732     U16 depth = 1;
1733
1734     font->id = id++;
1735     
1736     SWF swf;
1737     memset(&swf, 0, sizeof(SWF));
1738     swf.fileVersion = 8;
1739     swf.frameRate = 0x4000;
1740     swf.movieSize.xmax = 1024*20;
1741     swf.movieSize.ymax = 768*20;
1742     
1743     TAG *tag;
1744     swf.firstTag = tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
1745     swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xff);
1746
1747     tag = swf_InsertTag(tag, ST_DEFINEFONT3);
1748     swf_FontSetDefine2(tag, font);
1749
1750     if(font->glyphnames) {
1751         int c;
1752         tag = swf_InsertTag(tag, ST_GLYPHNAMES);
1753         swf_SetU16(tag, font->id);
1754         swf_SetU16(tag, font->numchars);
1755         for (c = 0; c < font->numchars; c++) {
1756             if (font->glyphnames[c])
1757                 swf_SetString(tag, font->glyphnames[c]);
1758             else
1759                 swf_SetString(tag, "");
1760         }
1761     }
1762
1763     if(viewer)
1764     {
1765         RGBA white = {255,255,255,255};
1766         RGBA black = {255,0,0,0};
1767         RGBA gray50 = {255,128,128,128};
1768         RGBA green = {255,0,255,0};
1769         int t;
1770         SCOORD miny = SCOORD_MAX;
1771         SCOORD maxy = SCOORD_MIN;
1772         double width = 0;
1773         U16 max_advance = 0;
1774         char*flags = rfx_calloc(font->numchars);
1775         double*xmin = rfx_calloc(sizeof(double)*(font->numchars+1));
1776         double*xmax = rfx_calloc(sizeof(double)*(font->numchars+1));
1777         int*xpos = rfx_calloc(sizeof(int)*(font->numchars+1));
1778         for(t=0;t<font->numchars;t++) {
1779             SHAPE*s = font->glyph[t].shape;
1780             SHAPE2*s2 = swf_ShapeToShape2(s);
1781             SRECT r = swf_GetShapeBoundingBox(s2);
1782
1783             // inside a definefont3, everything is 20x the resolution:
1784             double rx1 = r.xmin / 20.0;
1785             double ry1 = r.ymin / 20.0;
1786             double rx2 = r.xmax / 20.0;
1787             double ry2 = r.ymax / 20.0;
1788             
1789             xmin[t]= rx1;
1790             xmax[t]= rx2;
1791
1792             if(ry1<miny) {miny=ry1;}
1793             if(ry2>maxy) {maxy=ry2;}
1794             swf_Shape2Free(s2);free(s2);
1795             width += font->glyph[t].advance;
1796             if(font->glyph[t].advance>max_advance)
1797                 max_advance = font->glyph[t].advance;
1798         }
1799
1800         if(miny==SCOORD_MAX) miny=maxy=0;
1801         if(miny==maxy) maxy=miny+1;
1802
1803         /* scale the font so that it's 256 pixels high */
1804         double scale = (int)((256.0*1024.0/(maxy-miny))*20.0);
1805         double overlarge_factor;
1806         int fontsize;
1807         if(scale > 32767) {
1808             fontsize = 32767;
1809             overlarge_factor = scale / 32767.0;
1810         } else {
1811             fontsize = scale;
1812             overlarge_factor = 1.0;
1813         }
1814
1815         int textid = id++;
1816         int spriteid = id++;
1817         SRECT r;
1818         r.xmin = 0;
1819         r.ymin = miny*fontsize/1024;
1820         r.xmax = width*fontsize/20480;
1821         r.ymax = maxy*fontsize/1024;
1822         tag = swf_InsertTag(tag, ST_DEFINETEXT);
1823         swf_SetU16(tag, textid);
1824         swf_SetRect(tag, &r);
1825         swf_SetMatrix(tag, NULL);
1826
1827         U8 abits = 15;
1828         U8 gbits = swf_CountBits(font->numchars, 0);
1829         swf_SetU8(tag, gbits);
1830         swf_SetU8(tag, abits);
1831
1832         RGBA rgb = {255,0,0,0};
1833
1834         swf_TextSetInfoRecord(tag, font, fontsize, &rgb, SET_TO_ZERO, SET_TO_ZERO);
1835         ActionTAG*array = 0;
1836         double x=0;
1837         array = action_PushString(array, "xpos");
1838         for(t=0;t<font->numchars;t++) {
1839             swf_SetU8(tag, 1);
1840             int width = abs((xmax[t] - xmin[t+1])*fontsize/1024) + 60;
1841             array = action_PushInt(array, x/20 +(xmin[t]*scale/1024)/20);
1842             x += width * overlarge_factor;
1843             swf_SetBits(tag, t, gbits);
1844             swf_SetBits(tag, width, abits);
1845             swf_SetU8(tag, 128);
1846         }
1847         array = action_PushInt(array, x/20);
1848         array = action_PushInt(array, font->numchars+1);
1849         array = action_InitArray(array);
1850         array = action_SetVariable(array);
1851         swf_SetU8(tag, 0);
1852
1853         if(font->layout) {
1854             tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1855             SHAPE* s;
1856             swf_ShapeNew(&s);
1857             int ls = swf_ShapeAddLineStyle(s,20,&white);
1858             int shapeid = id++;
1859             swf_SetU16(tag,shapeid);
1860             SRECT r;
1861             r.xmin = 0;
1862             r.xmax = 1024*20;
1863             r.ymin = 0;
1864             r.ymax = 256*20;
1865             swf_SetRect(tag,&r);
1866             swf_SetShapeHeader(tag,s);
1867             swf_ShapeSetAll(tag,s,0,0,ls,0,0);
1868
1869             /* Ç and Â are good chars to test ascent/descent extend */
1870             int y1 = (-font->layout->ascent-miny*20.0)*256.0/(maxy-miny);
1871             int y2 = (font->layout->descent-miny*20.0)*256.0/(maxy-miny);
1872
1873             swf_ShapeSetMove(tag,s,0,y1);
1874             swf_ShapeSetLine(tag,s,width,0);
1875             swf_ShapeSetMove(tag,s,0,y2);
1876             swf_ShapeSetLine(tag,s,width,0);
1877
1878             swf_ShapeSetEnd(tag);
1879             swf_ShapeFree(s);
1880             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1881             swf_ObjectPlace(tag, shapeid, depth++, NULL, NULL, NULL);
1882         }
1883
1884         /* shapes */
1885         
1886         for(t=0;t<font->numchars;t++) {
1887             tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1888             SHAPE* s;
1889             swf_ShapeNew(&s);
1890             int ls = swf_ShapeAddLineStyle(s,20*2,&black);
1891             int ls2 = swf_ShapeAddLineStyle(s,20*2,&green);
1892             int fs = swf_ShapeAddSolidFillStyle(s, &gray50);
1893             int shapeid = id++;
1894             swf_SetU16(tag,shapeid);
1895             SRECT r;
1896             r.xmin = 0;
1897             r.xmax = 1024*20;
1898             r.ymin = 0;
1899             r.ymax = 512*20;
1900             swf_SetRect(tag,&r);
1901             swf_SetShapeHeader(tag,s);
1902             swf_ShapeSetAll(tag,s,0,0,ls,fs,0);
1903             SHAPE2*s2 = swf_ShapeToShape2(font->glyph[t].shape);
1904             SHAPELINE*l = s2->lines;
1905             int lastx=0,lasty=0;
1906
1907             double x1 = (1024*20 - (xmax[t] - xmin[t])*20*2*scale/20480.0)/2;
1908             double y1 = -miny*20*scale*2/20480.0;
1909             double scalex = scale*2/20480.0;
1910             double scaley = scale*2/20480.0;
1911
1912             while(l) {
1913                 int lx = (l->x)*scalex+x1;
1914                 int ly = (l->y)*scaley+y1;
1915                 int sx = (l->sx)*scalex+x1;
1916                 int sy = (l->sy)*scaley+y1;
1917                 if(l->type == moveTo) {
1918                     swf_ShapeSetMove(tag,s,lx,ly);
1919                 } else if(l->type == lineTo) {
1920                     swf_ShapeSetLine(tag,s,lx-lastx,ly-lasty);
1921                 } else if(l->type == splineTo) {
1922                     swf_ShapeSetCurve(tag,s,sx-lastx,sy-lasty,lx-sx,ly-sy);
1923                 }
1924                 lastx = lx;
1925                 lasty = ly;
1926                 l = l->next;
1927             }
1928             
1929             if(font->alignzones) {
1930                 ALIGNZONE*zone = &font->alignzones[t];
1931                 swf_ShapeSetAll(tag,s,0,0,ls2,SET_TO_ZERO,SET_TO_ZERO);
1932                 if((zone->x&zone->dx)!=0xffff) {
1933                     double x = F16toFloat(zone->x)*20480.0*scalex+x1;
1934                     double dx = (F16toFloat(zone->x)+F16toFloat(zone->dx))*20480.0*scalex+x1;
1935                     swf_ShapeSetMove(tag,s,x,0);
1936                     swf_ShapeSetLine(tag,s,0,1024*20);
1937                     swf_ShapeSetMove(tag,s,dx,0);
1938                     swf_ShapeSetLine(tag,s,0,1024*20);
1939                 }
1940                 if((zone->y&zone->dy)!=0xffff) {
1941                     double y = -F16toFloat(zone->y)*20480.0*scaley+y1;
1942                     double dy = -(F16toFloat(zone->y)+F16toFloat(zone->dy))*20480.0*scaley+y1;
1943                     swf_ShapeSetMove(tag,s,0,y);
1944                     swf_ShapeSetLine(tag,s,1024*20,0);
1945                     swf_ShapeSetMove(tag,s,0,dy);
1946                     swf_ShapeSetLine(tag,s,1024*20,0);
1947                 }
1948             }
1949
1950             swf_ShapeSetEnd(tag);
1951             swf_ShapeFree(s);
1952         
1953             tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1954             U16 spriteid=id++;
1955             swf_SetU16(tag, spriteid);
1956             swf_SetU16(tag, 1);
1957             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1958             swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
1959             tag = swf_InsertTag(tag, ST_END);
1960             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1961             MATRIX m;
1962             swf_GetMatrix(0, &m);
1963             m.ty = 20000;
1964             char txt[80];
1965             sprintf(txt, "char%d", font->numchars-t);
1966             swf_ObjectPlace(tag, spriteid, depth++, &m, NULL, txt);
1967         }
1968         
1969         /* marker */
1970         tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
1971         int shapeid=id++;
1972         RGBA blue = {0xff,0xc0,0xc0,0xff};
1973         swf_ShapeSetRectangle(tag, shapeid, 20, 20, &blue);
1974         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1975         U16 spriteid2=id++;
1976         swf_SetU16(tag, spriteid2);
1977         swf_SetU16(tag, 1);
1978         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1979         swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
1980         tag = swf_InsertTag(tag, ST_END);
1981         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1982         swf_ObjectPlace(tag, spriteid2, depth++, NULL, NULL, "marker");
1983         
1984         /* textbar */
1985         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1986         swf_SetU16(tag, spriteid);
1987         swf_SetU16(tag, 1);
1988         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1989         MATRIX m;
1990         swf_GetMatrix(0, &m);
1991         m.sx = 65536 * overlarge_factor;
1992         m.sy = 65536 * overlarge_factor;
1993         m.tx = 0;
1994         m.ty = -miny*256*20/(maxy-miny);
1995         swf_ObjectPlace(tag, textid, 1, &m, NULL, NULL);
1996         tag = swf_InsertTag(tag, ST_END);
1997         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1998         swf_ObjectPlace(tag, spriteid, depth++, NULL, NULL, "textbar");
1999         
2000         /* marker2 */
2001         RGBA blue2 = {0x80,0x80,0xff,0x80};
2002         tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
2003         int shapeid2=id++;
2004         swf_ShapeSetRectangleWithBorder(tag, shapeid2, 20, 20, &blue2, 0, &white);
2005         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2006         U16 spriteid3=id++;
2007         swf_SetU16(tag, spriteid3);
2008         swf_SetU16(tag, 1);
2009         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2010         swf_ObjectPlace(tag, shapeid2, 1, NULL, NULL, NULL);
2011         tag = swf_InsertTag(tag, ST_END);
2012         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2013         swf_ObjectPlace(tag, spriteid3, depth++, NULL, NULL, "marker2");
2014
2015
2016 char*data = 
2017 " var mouseListener = new Object();"
2018 " var speed = 0;"
2019 " var myx = 0;"
2020 " var currentMouseOver, currentChar;"
2021 " mouseListener.onMouseDown = function() { "
2022 "     eval(\"_root.char\"+currentChar)._y = 20000;"
2023 "     currentChar = currentMouseOver;"
2024 "     var i = currentMouseOver;"
2025 "     eval(\"_root.char\"+i)._y = 256;"
2026 "     _root.marker2._yscale=256*100;"
2027 "     _root.marker2._xscale=(xpos[i-1]-xpos[i])*100;"
2028 "     _root.marker2._x=xpos[i]+myx;"
2029 " };"
2030 " mouseListener.onMouseMove = function() { "
2031 "     if(_ymouse<256) {"
2032 "          speed = Math.abs(_xmouse-512)>256?(512-_xmouse)/8:0;"
2033 "     } else {"
2034 "          speed = 0;"
2035 "     }; "
2036 " }; "
2037 " setInterval( function(){ "
2038 "     if(_ymouse<256) {"
2039 "         var i, x=_xmouse-_root.textbar._x;"
2040 "         for(i=xpos.length-1;i>0;i--) {"
2041 "             if(x<xpos[i-1]) break;"
2042 "         }"
2043 "         currentMouseOver = i;"
2044 "         _root.marker._yscale=256*100;"
2045 "         _root.marker._xscale=(xpos[i-1]-xpos[i])*100;"
2046 "         _root.marker._x=xpos[i]+myx;"
2047 "         _root.textbar._x += 0.05;"
2048 "     }"
2049 "     if(myx+speed>0) {"
2050 "         speed=0;"
2051 "     } else if(myx+speed<-xpos[0]+1024) {"
2052 "         speed=0;"
2053 "     }"
2054 "     myx+=speed;"
2055 "     _root.textbar._x = myx;"
2056 "     _root.marker._x += speed;"
2057 "     _root.marker2._x += speed;"
2058 " }, 20);"
2059 " Mouse.addListener(mouseListener);"
2060 ;
2061         ActionTAG* atag = swf_ActionCompile(data, 6);
2062
2063         tag = swf_InsertTag(tag, ST_DOACTION);
2064         swf_ActionSet(tag, array);
2065         swf_ActionSet(tag, atag);
2066         swf_SetU8(tag, 0);
2067         swf_ActionFree(atag);
2068
2069         tag = swf_InsertTag(tag, ST_SHOWFRAME);
2070
2071         free(flags);
2072         free(xmin);
2073         free(xmax);
2074     }
2075
2076     tag = swf_InsertTag(tag, ST_END);
2077
2078     swf.compressed = -1;
2079     swf_SaveSWF(&swf, filename);
2080     swf_FreeTags(&swf);
2081 }