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