root/lib/modules/swftext.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. readUTF8char
  2. swf_FontIsItalic
  3. swf_FontIsBold
  4. swf_FontEnumerate
  5. swf_FontExtract_DefineFont
  6. swf_FontExtract_DefineFontInfo
  7. swf_FontExtract_GlyphNames
  8. swf_FontExtract_DefineFont2
  9. swf_FontExtract_DefineFontAlignZones
  10. swf_FontExtract_DefineTextCallback
  11. swf_ParseDefineText
  12. swf_FontExtract_DefineText
  13. updateusage
  14. swf_FontUpdateUsage
  15. swf_FontExtract
  16. swf_FontSetID
  17. swf_LayoutFree
  18. font_freeglyphnames
  19. font_freeusage
  20. font_freelayout
  21. font_freename
  22. swf_FontReduce_old
  23. swf_FontReduce_swfc
  24. swf_FontReduce
  25. cmp_chars
  26. swf_FontSort
  27. swf_FontPrepareForEditText
  28. swf_FontInitUsage
  29. swf_FontClearUsage
  30. swf_FontUse
  31. swf_FontUseUTF8
  32. swf_FontUseAll
  33. hash2
  34. hashadd
  35. swf_FontUseGetPair
  36. swf_FontUsePair
  37. swf_FontUseGlyph
  38. swf_FontSetDefine
  39. fontSize
  40. swf_FontSetDefine2
  41. swf_FontAddLayout
  42. swf_FontSetInfo
  43. swf_TextPrintDefineText
  44. font_freealignzones
  45. swf_FontFree
  46. swf_TextSetInfoRecord
  47. swf_TextCountBits2
  48. swf_TextSetCharRecord2
  49. swf_TextCountBits
  50. swf_TextSetCharRecord
  51. swf_TextCountBitsUTF8
  52. swf_TextSetCharRecordUTF8
  53. swf_TextGetWidth
  54. swf_TextCalculateBBoxUTF8
  55. swf_ReadFont
  56. swf_SetEditText
  57. swf_SetDefineText
  58. swf_FontCreateLayout
  59. swf_DrawText
  60. swf_WriteFont_AS3
  61. swf_WriteFont

/* swftext.c

   Text and font routines

   Extension module for the rfxswf library.
   Part of the swftools package.

   Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
   Copyright (c) 2003,2004,2005,2006,2007,2008,2009 Matthias Kramm

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "../rfxswf.h"

U32 readUTF8char(U8 ** text)
{
    U32 c = 0;
    if (!(*(*text) & 0x80))
        return *((*text)++);

    /* 0000 0080-0000 07FF   110xxxxx 10xxxxxx */
    if (((*text)[0] & 0xe0) == 0xc0 && (*text)[1]) {
        c = ((*text)[0] & 0x1f) << 6 | ((*text)[1] & 0x3f);
        (*text) += 2;
        return c;
    }
    /* 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx */
    if (((*text)[0] & 0xf0) == 0xe0 && (*text)[1] && (*text)[2]) {
        c = ((*text)[0] & 0x0f) << 12 | ((*text)[1] & 0x3f) << 6 | ((*text)[2] & 0x3f);
        (*text) += 3;
        return c;
    }
    /* 0001 0000-001F FFFF   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
    if (((*text)[0] & 0xf8) == 0xf0 && (*text)[1] && (*text)[2]
        && (*text)[3]) {
        c = ((*text)[0] & 0x07) << 18 | ((*text)[1] & 0x3f) << 12 | ((*text)[2] & 0x3f) << 6 | ((*text)[3] & 0x3f);
        (*text) += 4;
        return c;
    }
    /* 0020 0000-03FF FFFF   111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
    if (((*text)[0] & 0xfc) == 0xf8 && (*text)[1] && (*text)[2]
        && (*text)[3]
        && (*text)[4]) {
        c = ((*text)[0] & 0x03) << 24 | ((*text)[1] & 0x3f) << 18 | ((*text)[2] & 0x3f) << 12 | ((*text)[3] & 0x3f) << 6 | ((*text)[4] & 0x3f);
        (*text) += 5;
        return c;
    }
    /* 0400 0000-7FFF FFFF   1111110x 10xxxxxx ... 10xxxxxx */
    if (((*text)[0] & 0xfe) == 0xfc && (*text)[1] && (*text)[2]
        && (*text)[3]
        && (*text)[4] && (*text)[5]) {
        c = ((*text)[0] & 0x01) << 30 | ((*text)[1] & 0x3f) << 24 |
            ((*text)[2] & 0x3f) << 18 | ((*text)[3] & 0x3f) << 12 | ((*text)[4] & 0x3f) << 6 | ((*text)[5] & 0x3f) << 6;
        (*text) += 6;
        return c;
    }
    return *((*text)++);
}

#define TF_TEXTCONTROL  0x80
#define TF_HASFONT      0x08
#define TF_HASCOLOR     0x04
#define TF_HASYOFFSET   0x02
#define TF_HASXOFFSET   0x01

#define FF_WIDECODES    0x01
#define FF_BOLD         0x02
#define FF_ITALIC       0x04
#define FF_ANSI         0x08
#define FF_SHIFTJIS     0x10
#define FF_UNICODE      0x20

#define FF2_BOLD         0x01
#define FF2_ITALIC       0x02
#define FF2_WIDECODES    0x04
#define FF2_WIDEOFFSETS  0x08
#define FF2_ANSI         0x10
#define FF2_UNICODE      0x20
#define FF2_SHIFTJIS     0x40
#define FF2_LAYOUT       0x80

int swf_FontIsItalic(SWFFONT * f)
{
    return f->style & FONT_STYLE_ITALIC;
}

int swf_FontIsBold(SWFFONT * f)
{
    return f->style & FONT_STYLE_BOLD;
}

static const int WRITEFONTID = 0x4e46;  // font id for WriteFont and ReadFont

int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*self)
{
    int n;
    TAG *t;
    if (!swf)
        return -1;
    t = swf->firstTag;
    n = 0;

    while (t) {
        if (swf_isFontTag(t)) {
            n++;
            if (FontCallback) {
                U16 id;
                int l;
                U8 s[257];
                s[0] = 0;
                swf_SetTagPos(t, 0);

                id = swf_GetU16(t);
                if (swf_GetTagID(t) == ST_DEFINEFONT2 || swf_GetTagID(t) == ST_DEFINEFONTINFO || swf_GetTagID(t) == ST_DEFINEFONTINFO2) {
                    swf_GetU16(t);
                    l = swf_GetU8(t);
                    swf_GetBlock(t, s, l);
                    s[l] = 0;
                }

                (FontCallback) (self, id, s);
            }
        }
        t = swf_NextTag(t);
    }
    return n;
}

int swf_FontExtract_DefineFont(int id, SWFFONT * f, TAG * t)
{
    U16 fid;
    swf_SetTagPos(t, 0);

    fid = swf_GetU16(t);
    if ((!id) || (id == fid)) {
        U16 of;
        int n, i;

        id = fid;
        f->version = 1;
        f->id = fid;

        of = swf_GetU16(t);
        n = of / 2;
        f->numchars = n;
        f->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH) * n);

        for (i = 1; i < n; i++)
            swf_GetU16(t);
        for (i = 0; i < n; i++)
            swf_GetSimpleShape(t, &f->glyph[i].shape);
    }
    return id;
}

int swf_FontExtract_DefineFontInfo(int id, SWFFONT * f, TAG * t)
{
    U16 fid;
    U16 maxcode;
    U8 flags;
    swf_SetTagPos(t, 0);

    fid = swf_GetU16(t);
    if (fid == id) {
        U8 l = swf_GetU8(t);
        int i;

        if (f->version > 1) {
            /* Especially with Flash MX, DefineFont2 may have FontInfo fields,
               too. However, they only add little information to what's already
               inside the DefineFont2 tag */
            return id;
        }

        if (f->name)
            rfx_free(f->name);

        f->name = (U8 *) rfx_alloc(l + 1);
        swf_GetBlock(t, f->name, l);
        f->name[l] = 0;

        flags = swf_GetU8(t);
        if (flags & 2)
            f->style |= FONT_STYLE_BOLD;
        if (flags & 4)
            f->style |= FONT_STYLE_ITALIC;
        if (flags & 8)
            f->encoding |= FONT_ENCODING_ANSI;
        if (flags & 16)
            f->encoding |= FONT_ENCODING_SHIFTJIS;
        if (flags & 32)
            f->encoding |= FONT_ENCODING_UNICODE;

        if (t->id == ST_DEFINEFONTINFO2) {
            f->language = swf_GetU8(t);
        }

        f->glyph2ascii = (U16 *) rfx_alloc(sizeof(U16) * f->numchars);
        maxcode = 0;
        for (i = 0; i < f->numchars; i++) {
            f->glyph2ascii[i] = ((flags & FF_WIDECODES) ? swf_GetU16(t) : swf_GetU8(t));
            if (f->glyph2ascii[i] > maxcode)
                maxcode = f->glyph2ascii[i];
        }
        maxcode++;
        if (maxcode < 256)
            maxcode = 256;
        f->maxascii = maxcode;
        f->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
        memset(f->ascii2glyph, -1, sizeof(int) * maxcode);

        for (i = 0; i < f->numchars; i++)
            f->ascii2glyph[f->glyph2ascii[i]] = i;
    }
    return id;
}

int swf_FontExtract_GlyphNames(int id, SWFFONT * f, TAG * tag)
{
    U16 fid;
    swf_SetTagPos(tag, 0);

    fid = swf_GetU16(tag);

    if (fid == id) {
        int num = swf_GetU16(tag);
        int t;
        f->glyphnames = (char**)rfx_alloc(sizeof(char *) * num);
        for (t = 0; t < num; t++) {
            f->glyphnames[t] = strdup(swf_GetString(tag));
        }
    }
    return id;
}


int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
{
    int t, glyphcount;
    int maxcode;
    int fid;
    U32 offset_start;
    U32 *offset;
    U8 flags1, langcode, namelen;
    swf_SetTagPos(tag, 0);
    font->version = tag->id==ST_DEFINEFONT3?3:2;
    fid = swf_GetU16(tag);
    if (id && id != fid)
        return id;
    font->id = fid;
    flags1 = swf_GetU8(tag);
    langcode = swf_GetU8(tag);  //reserved flags

    if (flags1 & 1)
        font->style |= FONT_STYLE_BOLD;
    if (flags1 & 2)
        font->style |= FONT_STYLE_ITALIC;
    if (flags1 & 16)
        font->encoding |= FONT_ENCODING_ANSI;
    if (flags1 & 32)
        font->encoding |= FONT_ENCODING_UNICODE;
    if (flags1 & 64)
        font->encoding |= FONT_ENCODING_SHIFTJIS;

    namelen = swf_GetU8(tag);
    font->name = (U8 *) rfx_alloc(namelen + 1);
    font->name[namelen] = 0;
    swf_GetBlock(tag, font->name, namelen);
    glyphcount = swf_GetU16(tag);
    font->numchars = glyphcount;

    font->glyph = (SWFGLYPH *) rfx_calloc(sizeof(SWFGLYPH) * glyphcount);
    font->glyph2ascii = (U16 *) rfx_calloc(sizeof(U16) * glyphcount);

    offset = (U32*)rfx_calloc(sizeof(U32)*(glyphcount+1));
    offset_start = tag->pos;

    if (flags1 & 8) {           // wide offsets
        for (t = 0; t < glyphcount; t++)
            offset[t] = swf_GetU32(tag);        //offset[t]

        if (glyphcount)         /* this _if_ is not in the specs */
            offset[glyphcount] = swf_GetU32(tag);       // fontcodeoffset
        else
            offset[glyphcount] = tag->pos;
    } else {
        for (t = 0; t < glyphcount; t++)
            offset[t] = swf_GetU16(tag);        //offset[t]

        if (glyphcount)         /* this _if_ is not in the specs */
            offset[glyphcount] = swf_GetU16(tag);       // fontcodeoffset
        else
            offset[glyphcount] = tag->pos;
    }
    for (t = 0; t < glyphcount; t++) {
        swf_SetTagPos(tag, offset[t]+offset_start);
        swf_GetSimpleShape(tag, &(font->glyph[t].shape));
    }

    if(glyphcount)
        swf_SetTagPos(tag, offset[glyphcount]+offset_start);

    free(offset);

    maxcode = 0;
    for (t = 0; t < glyphcount; t++) {
        int code;
        if (flags1 & 4)         // wide codes (always on for definefont3)
            code = swf_GetU16(tag);
        else
            code = swf_GetU8(tag);
        font->glyph2ascii[t] = code;
        if (code > maxcode)
            maxcode = code;
    }
    maxcode++;
    if (maxcode < 256)
        maxcode = 256;
    font->maxascii = maxcode;
    font->ascii2glyph = (int *) rfx_alloc(sizeof(int) * maxcode);
    memset(font->ascii2glyph, -1, sizeof(int) * maxcode);
    for (t = 0; t < glyphcount; t++) {
        font->ascii2glyph[font->glyph2ascii[t]] = t;
    }

    if (flags1 & 128) {         // has layout
        U16 kerningcount;
        font->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
        font->layout->ascent = swf_GetU16(tag);
        font->layout->descent = swf_GetU16(tag);
        font->layout->leading = swf_GetU16(tag);
        for (t = 0; t < glyphcount; t++) {
            S16 advance = swf_GetS16(tag);
            font->glyph[t].advance = advance;
        }
        font->layout->bounds = (SRECT*)rfx_alloc(glyphcount * sizeof(SRECT));
        for (t = 0; t < glyphcount; t++) {
            swf_ResetReadBits(tag);
            swf_GetRect(tag, &font->layout->bounds[t]);
            SRECT b = font->layout->bounds[t];
            if((b.xmin|b.xmax|b.ymin|b.ymax) == 0) {
                // recalculate bounding box
                SHAPE2 *shape2 = swf_ShapeToShape2(font->glyph[t].shape);
                font->layout->bounds[t] = swf_GetShapeBoundingBox(shape2);
                swf_Shape2Free(shape2);free(shape2);
            }
        }

        kerningcount = swf_GetU16(tag);
        font->layout->kerningcount = kerningcount;

        font->layout->kerning = (SWFKERNING *) rfx_alloc(sizeof(SWFKERNING) * kerningcount);
        if (kerningcount) {
            font->layout->kerning = (SWFKERNING*)rfx_alloc(sizeof(*font->layout->kerning) * kerningcount);
            for (t = 0; t < kerningcount; t++) {
                if (flags1 & 4) {       // wide codes
                    font->layout->kerning[t].char1 = swf_GetU16(tag);
                    font->layout->kerning[t].char2 = swf_GetU16(tag);
                } else {
                    font->layout->kerning[t].char1 = swf_GetU8(tag);
                    font->layout->kerning[t].char2 = swf_GetU8(tag);
                }
                font->layout->kerning[t].adjustment = swf_GetS16(tag);
            }
        }
    }
    return font->id;
}

int swf_FontExtract_DefineFontAlignZones(int id, SWFFONT * font, TAG * tag)
{
    U16 fid;
    swf_SetTagPos(tag, 0);
    fid = swf_GetU16(tag);
    
    if (fid == id) {
        font->alignzone_flags = swf_GetU8(tag);
        font->alignzones = rfx_calloc(sizeof(ALIGNZONE)*font->numchars);
        int i=0;
        while(tag->pos < tag->len) {
            if(i>=font->numchars)
                break;
            int nr = swf_GetU8(tag); // should be 2
            if(nr!=1 && nr!=2) {
                fprintf(stderr, "rfxswf: Can't parse alignzone tags with %d zones", nr);
                break;
            }
            U16 x = swf_GetU16(tag);
            U16 y = swf_GetU16(tag);
            U16 dx = (nr==2)?swf_GetU16(tag):0xffff;
            U16 dy = (nr==2)?swf_GetU16(tag):0xffff;
            U8 xy = swf_GetU8(tag);

#ifdef DEBUG_RFXSWF
            if((!(xy&1) && (x!=0 || (dx!=0 && dx!=0xffff))) ||
               (!(xy&2) && (y!=0 || (dy!=0 && dy!=0xffff)))) {
                fprintf(stderr, "Warning: weird combination of alignzone bits and values (%d x:%04x-%04x y:%04x-%04x)\n", xy,
                        x,dx,y,dy);
            }
#endif
            if(!(xy&1)) {
                x = 0xffff;
                dx = 0xffff;
            } else if(!(xy&2)) {
                y = 0xffff;
                dy = 0xffff;
            }
            font->alignzones[i].x = x;
            font->alignzones[i].y = y;
            font->alignzones[i].dx = dx;
            font->alignzones[i].dy = dy;
            i++;
        }
    }
    return id;
}


#define FEDTJ_PRINT  0x01
#define FEDTJ_MODIFY 0x02
#define FEDTJ_CALLBACK 0x04

static int
swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
                                   void (*callback) (void *self,
                                                     int *chars, int *xpos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
{
    U16 cid;
    SRECT r;
    MATRIX m;
    U8 gbits, abits;
    int fid = -1;
    RGBA color;
    int x = 0, y = 0;
    int fontsize = 0;

    memset(&color, 0, sizeof(color));

    swf_SetTagPos(t, 0);

    cid = swf_GetU16(t);
    swf_GetRect(t, &r);
    swf_GetMatrix(t, &m);
    gbits = swf_GetU8(t);
    abits = swf_GetU8(t);

    while (1) {
        int flags, num;
        flags = swf_GetU8(t);
        if (!flags)
            break;

        if (flags & TF_TEXTCONTROL) {
            if (flags & TF_HASFONT)
                fid = swf_GetU16(t);
            if (flags & TF_HASCOLOR) {
                color.r = swf_GetU8(t); // rgb
                color.g = swf_GetU8(t);
                color.b = swf_GetU8(t);
                if (swf_GetTagID(t) == ST_DEFINETEXT2)
                    color.a = swf_GetU8(t);
                else
                    color.a = 255;
            }
            if (flags & TF_HASXOFFSET)
                x = swf_GetS16(t);
            if (flags & TF_HASYOFFSET)
                y = swf_GetS16(t);
            if (flags & TF_HASFONT)
                fontsize = swf_GetU16(t);
        }

        num = swf_GetU8(t);
        if (!num)
            break;

        {
            int i;
            int buf[256];
            int advance[256];
            int xpos = 0;
            for (i = 0; i < num; i++) {
                int glyph;
                int adv = 0;
                advance[i] = xpos;
                glyph = swf_GetBits(t, gbits);
                adv = swf_GetBits(t, abits);
                xpos += adv;

                if (id == fid) {
                    if (jobs & FEDTJ_PRINT) {
                        int code = f->glyph2ascii[glyph];
                        printf("%lc", code);
                    }
                    if (jobs & FEDTJ_MODIFY)
                        f->glyph[glyph].advance = adv * 20;     //?
                }

                buf[i] = glyph;
            }
            if ((id == fid) && (jobs & FEDTJ_PRINT))
                printf("\n");
            if (jobs & FEDTJ_CALLBACK)
                callback(self, buf, advance, num, fid, fontsize, x, y, &color);
            x += xpos;
        }
    }

    return id;
}

int swf_ParseDefineText(TAG * tag,
                    void (*callback) (void *self, int *chars, int *xpos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
{
    return swf_FontExtract_DefineTextCallback(-1, 0, tag, FEDTJ_CALLBACK, callback, self);
}

int swf_FontExtract_DefineText(int id, SWFFONT * f, TAG * t, int jobs)
{
    return swf_FontExtract_DefineTextCallback(id, f, t, jobs, 0, 0);
}

typedef struct _usagetmp {
    SWFFONT*font;
    int lastx,lasty;
    int last;
} usagetmp_t;
static void updateusage(void *self, int *chars, int *xpos, int nr, 
                        int fontid, int fontsize, int xstart, int ystart, RGBA * color)
{
    usagetmp_t*u = (usagetmp_t*)self;
    if(!u->font->use) {
        swf_FontInitUsage(u->font);
    }
    if(fontid!=u->font->id)
        return;

    int t;
    for(t=0;t<nr;t++) {
        int x=xpos[t];
        int y=ystart;
        int c = chars[t];
        if(c<0 || c>u->font->numchars)
            continue;
        swf_FontUseGlyph(u->font, c, fontsize);
        if(u->lasty == y && x>=u->lastx-200 && abs(u->lastx-x)<200 &&
           u->last!=c && !swf_ShapeIsEmpty(u->font->glyph[u->last].shape) && 
           !swf_ShapeIsEmpty(u->font->glyph[c].shape)) 
        {
            swf_FontUsePair(u->font, u->last, c);
        }
        u->lasty = y;
        /* FIXME: do we still need to divide advance by 20 for definefont3? */
        u->lastx = x + (u->font->glyph[c].advance*fontsize/20480);
        u->last = c;
    }
}

void swf_FontUpdateUsage(SWFFONT*f, TAG* tag)
{
    usagetmp_t u;
    u.font = f;
    u.lastx = -0x80000000;
    u.lasty = -0x80000000;
    u.last = 0;
    swf_ParseDefineText(tag, updateusage, &u);
}

int swf_FontExtract(SWF * swf, int id, SWFFONT * *font)
{
    TAG *t;
    SWFFONT *f;

    if ((!swf) || (!font))
        return -1;

    f = (SWFFONT *) rfx_calloc(sizeof(SWFFONT));

    t = swf->firstTag;

    while (t) {
        int nid = 0;
        switch (swf_GetTagID(t)) {
        case ST_DEFINEFONT:
            nid = swf_FontExtract_DefineFont(id, f, t);
            break;

        case ST_DEFINEFONT2:
        case ST_DEFINEFONT3:
            nid = swf_FontExtract_DefineFont2(id, f, t);
            break;

        case ST_DEFINEFONTALIGNZONES:
            nid = swf_FontExtract_DefineFontAlignZones(id, f, t);
            break;

        case ST_DEFINEFONTINFO:
        case ST_DEFINEFONTINFO2:
            nid = swf_FontExtract_DefineFontInfo(id, f, t);
            break;

        case ST_DEFINETEXT:
        case ST_DEFINETEXT2:
            if(!f->layout) {
                nid = swf_FontExtract_DefineText(id, f, t, FEDTJ_MODIFY);
            }
            if(f->version>=3 && f->layout) 
                swf_FontUpdateUsage(f, t);
            break;

        case ST_GLYPHNAMES:
            nid = swf_FontExtract_GlyphNames(id, f, t);
            break;
        }
        if (nid > 0)
            id = nid;
        t = swf_NextTag(t);
    }
    if (f->id != id) {
        rfx_free(f);
        f = 0;
    }
    font[0] = f;
    return 0;
}

int swf_FontSetID(SWFFONT * f, U16 id)
{
    if (!f)
        return -1;
    f->id = id;
    return 0;
}

void swf_LayoutFree(SWFLAYOUT * l)
{
    if (l) {
        if (l->kerning)
            rfx_free(l->kerning);
        l->kerning = NULL;
        if (l->bounds)
            rfx_free(l->bounds);
        l->bounds = NULL;
    }
    rfx_free(l);
}


static void font_freeglyphnames(SWFFONT*f)
{
    if (f->glyphnames)
    {
        int t;
        for (t = 0; t < f->numchars; t++)
        {
            if (f->glyphnames[t])
            {
                rfx_free(f->glyphnames[t]);
                f->glyphnames[t] = 0;
            }
        }
        rfx_free(f->glyphnames);
        f->glyphnames = 0;
        }
}
static void font_freeusage(SWFFONT*f)
{
    if (f->use) {
        if(f->use->chars) {
            rfx_free(f->use->chars);f->use->chars = 0;
        }
        if(f->use->neighbors) {
            rfx_free(f->use->neighbors);f->use->neighbors = 0;
        }
        if(f->use->neighbors_hash) {
            rfx_free(f->use->neighbors_hash);f->use->neighbors_hash = 0;
        }
        rfx_free(f->use); f->use = 0;
    }
}
static void font_freelayout(SWFFONT*f)
{
    if (f->layout) {
        swf_LayoutFree(f->layout);
        f->layout = 0;
    }
}
static void font_freename(SWFFONT*f)
{
    if (f->name) {
        rfx_free(f->name);
        f->name = 0;
    }
}

int swf_FontReduce_old(SWFFONT * f)
{
    int i, j;
    int max_unicode = 0;
    if ((!f) || (!f->use) || f->use->is_reduced)
        return -1;

    j = 0;

    for (i = 0; i < f->numchars; i++) {
        if (f->glyph[i].shape && f->use->chars[i]) {
            f->glyph2ascii[j] = f->glyph2ascii[i];
            f->glyph[j] = f->glyph[i];
            f->use->chars[i] = j;
            j++;
        } else {
            f->glyph2ascii[i] = 0;
            if(f->glyph[i].shape) {
                swf_ShapeFree(f->glyph[i].shape);
                f->glyph[i].shape = 0;
                f->glyph[i].advance = 0;
            }
            f->use->chars[i] = -1;
            j++; //TODO: remove
        }
    }
    for (i = 0; i < f->maxascii; i++) {
        if(f->use->chars[f->ascii2glyph[i]]<0) {
            f->ascii2glyph[i] = -1;
        } else {
            f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
            max_unicode = i;
        }
    }
    f->maxascii = max_unicode;
    f->use->is_reduced = 1;
    f->numchars = j;
    font_freelayout(f);
    font_freeglyphnames(f);
    font_freename(f);
    return j;
}

int swf_FontReduce_swfc(SWFFONT * f)
{
    int i, j;
    int max_unicode = 0;
    if ((!f) || (!f->use) || f->use->is_reduced)
        return -1;

    font_freeglyphnames(f);

    j = 0;
    for (i = 0; i < f->numchars; i++) {
        if (f->glyph[i].shape && f->use->chars[i]) {
            f->glyph2ascii[j] = f->glyph2ascii[i];
            if (f->layout)
                f->layout->bounds[j] = f->layout->bounds[i];
            f->glyph[j] = f->glyph[i];
            f->use->chars[i] = j;
            j++;
        } else {
            f->glyph2ascii[i] = 0;
            if(f->glyph[i].shape) {
                swf_ShapeFree(f->glyph[i].shape);
                f->glyph[i].shape = 0;
                f->glyph[i].advance = 0;
            }
            f->use->chars[i] = -1;
        }
    }
    f->use->used_glyphs = j;
    for (i = 0; i < f->maxascii; i++) {
        if(f->ascii2glyph[i] > -1) {
            if (f->use->chars[f->ascii2glyph[i]]<0) {
                f->use->chars[f->ascii2glyph[i]] = 0;
                f->ascii2glyph[i] = -1;
            } else {
                f->ascii2glyph[i] = f->use->chars[f->ascii2glyph[i]];
                f->use->chars[f->ascii2glyph[i]] = 1;
                max_unicode = i + 1;
            }
        }
    }
    f->maxascii = max_unicode;
    f->use->is_reduced = 1;
    f->numchars = j;
    font_freename(f);
    return j;
}

int swf_FontReduce(SWFFONT * f)
{
    int i;
    int max_unicode = 0;
    int max_glyph = 0;
    if ((!f) || (!f->use) || f->use->is_reduced)
        return -1;

    font_freelayout(f);
    font_freeglyphnames(f);

    f->use->used_glyphs= 0;
    for (i = 0; i < f->numchars; i++) {
        if(!f->use->chars[i]) {
            if(f->glyph2ascii) {
                f->glyph2ascii[i] = 0;
            }
            if(f->glyph[i].shape) {
                swf_ShapeFree(f->glyph[i].shape);
                f->glyph[i].shape = 0;
                f->glyph[i].advance = 0;
            }
//          f->use->used_glyphs++;
        } else {
            f->use->used_glyphs++;
            max_glyph = i+1;
        }
    }
    for (i = 0; i < f->maxascii; i++) {
        if(f->ascii2glyph[i] > -1 && !f->use->chars[f->ascii2glyph[i]]) {
            if(f->ascii2glyph) {
                f->ascii2glyph[i] = -1;
            }
        } else {
            max_unicode = i+1;
        }
    }
    f->maxascii = max_unicode;
    f->numchars = max_glyph;
    font_freename(f);
    return 0;
}

static SWFFONT* font_to_sort;
int cmp_chars(const void*a, const void*b)
{
    int x = *(const int*)a;
    int y = *(const int*)b;
    return 0;
}

void swf_FontSort(SWFFONT * font)
{
    int i, j;
    int *newplace;
    int *newpos;
    if (!font)
        return;

    newplace = (int*)rfx_alloc(sizeof(int) * font->numchars);

    for (i = 0; i < font->numchars; i++) {
        newplace[i] = i;
    }
    //qsort(newplace, sizeof(newplace[0]), font->numchars, cmp_chars);

    for (i = 0; i < font->numchars; i++)
        for (j = 0; j < i; j++) {
            if (font->glyph2ascii[i] < font->glyph2ascii[j]) {
                int n1, n2;
                char *c1, *c2;
                SWFGLYPH g1, g2;
                SRECT r1, r2;
                n1 = newplace[i];
                n2 = newplace[j];
                newplace[j] = n1;
                newplace[i] = n2;
                n1 = font->glyph2ascii[i];
                n2 = font->glyph2ascii[j];
                font->glyph2ascii[j] = n1;
                font->glyph2ascii[i] = n2;
                g1 = font->glyph[i];
                g2 = font->glyph[j];
                font->glyph[j] = g1;
                font->glyph[i] = g2;
                if (font->glyphnames) {
                    c1 = font->glyphnames[i];
                    c2 = font->glyphnames[j];
                    font->glyphnames[j] = c1;
                    font->glyphnames[i] = c2;
                }
                if (font->layout) {
                    r1 = font->layout->bounds[i];
                    r2 = font->layout->bounds[j];
                    font->layout->bounds[j] = r1;
                    font->layout->bounds[i] = r2;
                }
            }
        }
    newpos = (int*)rfx_alloc(sizeof(int) * font->numchars);
    for (i = 0; i < font->numchars; i++) {
        newpos[newplace[i]] = i;
    }
    for (i = 0; i < font->maxascii; i++) {
        if (font->ascii2glyph[i] >= 0)
            font->ascii2glyph[i] = newpos[font->ascii2glyph[i]];
    }

    rfx_free(newplace);
    font->glyph2glyph = newpos;
}

void swf_FontPrepareForEditText(SWFFONT * font)
{
    if (!font->layout)
        swf_FontCreateLayout(font);
    swf_FontSort(font);
}

int swf_FontInitUsage(SWFFONT * f)
{
    if (!f)
        return -1;
    if(f->use) {
        fprintf(stderr, "Usage initialized twice");
        return -1;
    }
    f->use = (FONTUSAGE*)rfx_calloc(sizeof(FONTUSAGE));
    f->use->smallest_size = 0xffff;
    f->use->chars = (int*)rfx_calloc(sizeof(f->use->chars[0]) * f->numchars);
    return 0;
}

void swf_FontClearUsage(SWFFONT * f)
{
    if (!f || !f->use)
        return;
    rfx_free(f->use->chars); f->use->chars = 0;
    rfx_free(f->use); f->use = 0;
}

int swf_FontUse(SWFFONT * f, U8 * s)
{
    if( (!s))
        return -1;
    while (*s) {
        if(*s < f->maxascii && f->ascii2glyph[*s]>=0)
            swf_FontUseGlyph(f, f->ascii2glyph[*s], /*FIXME*/0xffff);
        s++;
    }
    return 0;
}

int swf_FontUseUTF8(SWFFONT * f, const U8 * s, U16 size)
{
    if( (!s))
        return -1;
    int ascii;
    while (*s)
    {
        ascii = readUTF8char((U8**)&s);
        if(ascii < f->maxascii && f->ascii2glyph[ascii]>=0)
            swf_FontUseGlyph(f, f->ascii2glyph[ascii], size);
    }
    return 0;
}

int swf_FontUseAll(SWFFONT* f)
{
    int i;

    if (!f->use)
        swf_FontInitUsage(f);
    for (i = 0; i < f->numchars; i++)
        f->use->chars[i] = 1;
    f->use->used_glyphs = f->numchars;
    return 0;
}

static unsigned hash2(int char1, int char2)
{
    unsigned hash = char1^(char2<<8);
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}
static void hashadd(FONTUSAGE*u, int char1, int char2, int nr)
{
    unsigned hash = hash2(char1, char2);
    while(1) {
        hash = hash%u->neighbors_hash_size;
        if(!u->neighbors_hash[hash]) {
           u->neighbors_hash[hash] = nr+1;
           return;
        }
        hash++;
    }
}
int swf_FontUseGetPair(SWFFONT * f, int char1, int char2)
{
    FONTUSAGE*u = f->use;
    if(!u || !u->neighbors_hash_size) 
        return 0;
    unsigned hash = hash2(char1, char2);
    while(1) {
        hash = hash%u->neighbors_hash_size;
        int pos = u->neighbors_hash[hash];
        if(!pos)
            return 0;
        if(pos && 
           u->neighbors[pos-1].char1 == char1 &&
           u->neighbors[pos-1].char2 == char2) {
            return pos;
        }
        hash++;
    }

}
void swf_FontUsePair(SWFFONT * f, int char1, int char2)
{
    if (!f->use)
        swf_FontInitUsage(f);
    FONTUSAGE*u = f->use;

    if(u->num_neighbors*3 >= u->neighbors_hash_size*2) {
        if(u->neighbors_hash) {
            free(u->neighbors_hash);
        }
        u->neighbors_hash_size = u->neighbors_hash_size?u->neighbors_hash_size*2:1024;
        u->neighbors_hash = rfx_calloc(u->neighbors_hash_size*sizeof(int));
        int t;
        for(t=0;t<u->num_neighbors;t++) {
            hashadd(u, u->neighbors[t].char1, u->neighbors[t].char2, t);
        }
    }

    int nr = swf_FontUseGetPair(f, char1, char2);
    if(!nr) {
        if(u->num_neighbors == u->neighbors_size) {
            u->neighbors_size += 4096;
            u->neighbors = rfx_realloc(u->neighbors, sizeof(SWFGLYPHPAIR)*u->neighbors_size);
        }
        u->neighbors[u->num_neighbors].char1 = char1;
        u->neighbors[u->num_neighbors].char2 = char2;
        u->neighbors[u->num_neighbors].num = 1;
        hashadd(u, char1, char2, u->num_neighbors);
        u->num_neighbors++;
    } else {
        u->neighbors[nr-1].num++;
    }
}

int swf_FontUseGlyph(SWFFONT * f, int glyph, U16 size)
{
    if (!f->use)
        swf_FontInitUsage(f);
    if(glyph < 0 || glyph >= f->numchars)
        return -1;
    if(!f->use->chars[glyph])
        f->use->used_glyphs++;
    f->use->chars[glyph] = 1;
    if(size && size < f->use->smallest_size)
        f->use->smallest_size = size;
    return 0;
}

int swf_FontSetDefine(TAG * t, SWFFONT * f)
{
    U16 *ofs = (U16 *) rfx_alloc(f->numchars * 2);
    int p, i, j;

    if ((!t) || (!f))
        return -1;
    swf_ResetWriteBits(t);
    swf_SetU16(t, f->id);

    p = 0;
    j = 0;
    for (i = 0; i < f->numchars; i++)
        if (f->glyph[i].shape) {
            ofs[j++] = p;
            p += swf_SetSimpleShape(NULL, f->glyph[i].shape);
        }

    for (i = 0; i < j; i++)
        swf_SetU16(t, ofs[i] + j * 2);
    if (!j) {
        fprintf(stderr, "rfxswf: warning: Font is empty\n");
        swf_SetU16(t, 0);
    }

    for (i = 0; i < f->numchars; i++)
        if (f->glyph[i].shape)
            swf_SetSimpleShape(t, f->glyph[i].shape);

    swf_ResetWriteBits(t);
    rfx_free(ofs);
    return 0;
}

static inline int fontSize(SWFFONT * font)
{
    int t;
    int size = 0;
    for (t = 0; t < font->numchars; t++) {
        int l = 0;
        if(font->glyph[t].shape)
            l = (font->glyph[t].shape->bitlen + 7) / 8;
        else
            l = 8;
        size += l + 1;
    }
    return size + (font->numchars + 1) * 2;
}

int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
{
    U8 flags = 0;
    int t;
    int pos;
    swf_SetU16(tag, f->id);

    if (f->layout) flags |= 128;                // haslayout
    if (f->numchars > 256)
        flags |= 4;             // widecodes
    if (f->style & FONT_STYLE_BOLD)
        flags |= 1;             // bold
    if (f->style & FONT_STYLE_ITALIC)
        flags |= 2;             // italic
    if (f->maxascii >= 256)
        flags |= 4;             //wide codecs
    if (fontSize(f) > 65535)
        flags |= 8;             //wide offsets
    flags |= 8 | 4;             //FIXME: the above check doesn't work

    if (f->encoding & FONT_ENCODING_ANSI)
        flags |= 16;            // ansi
    if (f->encoding & FONT_ENCODING_UNICODE)
        flags |= 32;            // unicode
    if (f->encoding & FONT_ENCODING_SHIFTJIS)
        flags |= 64;            // shiftjis

    swf_SetU8(tag, flags);
    swf_SetU8(tag, 0);          //reserved flags
    if (f->name) {
        /* font name */
        swf_SetU8(tag, strlen((const char*)f->name)+1);
        swf_SetBlock(tag, f->name, strlen((const char*)f->name)+1);
    } else {
        /* font name (="") */
        swf_SetU8(tag, 1);
        swf_SetU8(tag, 0);
    }
    /* number of glyphs */
    swf_SetU16(tag, f->numchars);
    /* font offset table */
    pos = tag->len;
    for (t = 0; t <= f->numchars; t++) {
        if (flags & 8)
            swf_SetU32(tag, /* fontoffset */ 0);        /*placeholder */
        else
            swf_SetU16(tag, /* fontoffset */ 0);        /*placeholder */
    }

    for (t = 0; t <= f->numchars; t++) {
        if (flags & 8) {
            tag->data[pos + t * 4] = (tag->len - pos);
            tag->data[pos + t * 4 + 1] = (tag->len - pos) >> 8;
            tag->data[pos + t * 4 + 2] = (tag->len - pos) >> 16;
            tag->data[pos + t * 4 + 3] = (tag->len - pos) >> 24;
        } else {
            if (tag->len - pos > 65535) {
                fprintf(stderr, "Internal error: Font too big and WideOffsets flag not set\n");
                exit(1);
            }
            tag->data[pos + t * 2] = (tag->len - pos);
            tag->data[pos + t * 2 + 1] = (tag->len - pos) >> 8;
        }
        if (t < f->numchars) {
            if(f->glyph[t].shape) {
                swf_SetSimpleShape(tag, f->glyph[t].shape);
            } else {
                swf_SetU8(tag, 0); //non-edge(1) + edge flags(5)
            }
        }
    }


    /* font code table */
    for (t = 0; t < f->numchars; t++) {
        if (flags & 4) {                /* wide codes */
            if(f->glyph2ascii[t]) {
                swf_SetU16(tag, f->glyph2ascii[t]);
            } else {
                swf_SetU16(tag, 0);
            }
        } else {
            if(f->glyph2ascii[t]) {
                swf_SetU8(tag, f->glyph2ascii[t]);
            } else {
                swf_SetU8(tag, 0);
            }
        }
    }

    if (f->layout) {
        swf_SetU16(tag, f->layout->ascent);
        swf_SetU16(tag, f->layout->descent);
        swf_SetU16(tag, 0); // flash ignores leading

        for (t = 0; t < f->numchars; t++)
            swf_SetU16(tag, f->glyph[t].advance);
        for (t = 0; t < f->numchars; t++) {
            swf_ResetWriteBits(tag);
            /* not used by flash, so leave this empty */
            SRECT b = {0,0,0,0};
            swf_SetRect(tag, &b);
        }
        swf_SetU16(tag, f->layout->kerningcount);
        for (t = 0; t < f->layout->kerningcount; t++) {
            if (flags & 4) {    /* wide codes */
                swf_SetU16(tag, f->layout->kerning[t].char1);
                swf_SetU16(tag, f->layout->kerning[t].char2);
            } else {
                swf_SetU8(tag, f->layout->kerning[t].char1);
                swf_SetU8(tag, f->layout->kerning[t].char2);
            }
            swf_SetU16(tag, f->layout->kerning[t].adjustment);
        }
    }
    return 0;
}

void swf_FontAddLayout(SWFFONT * f, int ascent, int descent, int leading)
{
    f->layout = (SWFLAYOUT *) rfx_alloc(sizeof(SWFLAYOUT));
    f->layout->ascent = ascent;
    f->layout->descent = descent;
    f->layout->leading = leading;
    f->layout->kerningcount = 0;
    f->layout->kerning = 0;
    f->layout->bounds = (SRECT *) rfx_calloc(sizeof(SRECT) * f->numchars);
}

int swf_FontSetInfo(TAG * t, SWFFONT * f)
{
    int l, i;
    U8 wide = 0;
    U8 flags = 0;
    if ((!t) || (!f))
        return -1;
    swf_ResetWriteBits(t);
    swf_SetU16(t, f->id);
    l = f->name ? strlen((const char *)f->name) : 0;
    if (l > 255)
        l = 255;
    swf_SetU8(t, l);
    if (l)
        swf_SetBlock(t, f->name, l);
    if (f->numchars >= 256)
        wide = 1;

    if (f->style & FONT_STYLE_BOLD)
        flags |= 2;
    if (f->style & FONT_STYLE_ITALIC)
        flags |= 4;
    if (f->style & FONT_ENCODING_ANSI)
        flags |= 8;
    if (f->style & FONT_ENCODING_SHIFTJIS)
        flags |= 16;
    if (f->style & FONT_ENCODING_UNICODE)
        flags |= 32;

    swf_SetU8(t, (flags & 0xfe) | wide);

    for (i = 0; i < f->numchars; i++) {
        if (f->glyph[i].shape) {
            int g2a = f->glyph2ascii?f->glyph2ascii[i]:0;
            wide ? swf_SetU16(t, g2a) : swf_SetU8(t, g2a);
        }
    }

    return 0;
}

int swf_TextPrintDefineText(TAG * t, SWFFONT * f)
{
    int id = swf_GetTagID(t);
    if ((id == ST_DEFINETEXT) || (id == ST_DEFINETEXT2))
        swf_FontExtract_DefineText(f->id, f, t, FEDTJ_PRINT);
    else
        return -1;
    return 0;
}

static void font_freealignzones(SWFFONT * f)
{
    if(f->alignzones)
        free(f->alignzones);
    f->alignzones = 0;
}

void swf_FontFree(SWFFONT * f)
{
    int i;
    if (!f)
        return;

    if (f->glyph)
    {
        for (i = 0; i < f->numchars; i++)
            if (f->glyph[i].shape)
            {
                swf_ShapeFree(f->glyph[i].shape);
                f->glyph[i].shape = NULL;
            }
            rfx_free(f->glyph);
            f->glyph = NULL;
    }
    if (f->ascii2glyph)
    {
        rfx_free(f->ascii2glyph);
        f->ascii2glyph = NULL;
    }
    if (f->glyph2ascii)
    {
        rfx_free(f->glyph2ascii);
        f->glyph2ascii = NULL;
    }
    if (f->glyph2glyph) {
        rfx_free(f->glyph2glyph);
        f->glyph2glyph = NULL;
    }
    font_freename(f);
    font_freelayout(f);
    font_freeglyphnames(f);
    font_freeusage(f);
    font_freealignzones(f);

    rfx_free(f);
}

int swf_TextSetInfoRecord(TAG * t, SWFFONT * font, U16 size, RGBA * color, int x, int y)
{
    U8 flags;
    if (!t)
        return -1;

    flags = TF_TEXTCONTROL | (font ? TF_HASFONT : 0) | (color ? TF_HASCOLOR : 0) | (x ? TF_HASXOFFSET : 0)
        | (y ? TF_HASYOFFSET : 0);

    swf_SetU8(t, flags);
    if (font)
        swf_SetU16(t, font->id);
    if (color) {
        if (swf_GetTagID(t) == ST_DEFINETEXT2)
            swf_SetRGBA(t, color);
        else
            swf_SetRGB(t, color);
    }
    if (x) {
        if(x != SET_TO_ZERO) {
            if(x>32767 || x<-32768)
                fprintf(stderr, "Warning: Horizontal char position overflow: %d\n", x);
            swf_SetS16(t, x);
        } else {
            swf_SetS16(t, 0);
        }
    }
    if (y) {
        if(y != SET_TO_ZERO) {
            if(y>32767 || y<-32768)
                fprintf(stderr, "Warning: Vertical char position overflow: %d\n", y);
            swf_SetS16(t, y);
        } else {
            swf_SetS16(t, 0);
        }
    }
    if (font)
        swf_SetU16(t, size);

    return 0;
}

static int swf_TextCountBits2(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits, char *encoding)
{
    U16 g, a;
    char utf8 = 0;
    if ((!s) || (!font) || ((!gbits) && (!abits)) || (!font->ascii2glyph))
        return -1;
    g = a = 0;

    if (!strcmp(encoding, "UTF8"))
        utf8 = 1;
    else if (!strcmp(encoding, "iso-8859-1"))
        utf8 = 0;
    else
        fprintf(stderr, "Unknown encoding: %s", encoding);

    while (*s) {
        int glyph = -1, c;

        if (!utf8)
            c = *s++;
        else
            c = readUTF8char(&s);

        if (c < font->maxascii)
            glyph = font->ascii2glyph[c];
        if (glyph >= 0) {
            g = swf_CountUBits(glyph, g);
            a = swf_CountBits(((((U32) font->glyph[glyph].advance) * scale) / 20) / 100, a);
        }
    }

    if (gbits)
        gbits[0] = (U8) g;
    if (abits)
        abits[0] = (U8) a;
    return 0;
}

static int swf_TextSetCharRecord2(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits, char *encoding)
{
    int l = 0, pos;
    char utf8 = 0;

    if ((!t) || (!font) || (!s) || (!font->ascii2glyph))
        return -1;

    if (!strcmp(encoding, "UTF8"))
        utf8 = 1;
    else if (!strcmp(encoding, "iso-8859-1"))
        utf8 = 0;
    else
        fprintf(stderr, "Unknown encoding: %s", encoding);

    pos = t->len;
    swf_SetU8(t, l);            //placeholder

    while (*s) {
        int g = -1, c;

        if (!utf8)
            c = *s++;
        else
            c = readUTF8char(&s);

        if (c < font->maxascii)
            g = font->ascii2glyph[c];
        if (g >= 0) {
            swf_SetBits(t, g, gbits);
            swf_SetBits(t, ((((U32) font->glyph[g].advance) * scale) / 20) / 100, abits);
            l++;
            /* We split into 127 characters per text field.
               We could do 255, by the (formerly wrong) flash specification,
               but some SWF parsing code out there still assumes that char blocks
               are at max 127 characters, and it would save only a few bits.
            */
            if (l == 0x7f)
                break;
        }
    }

    PUT8(&t->data[pos], l);

    swf_ResetWriteBits(t);
    return 0;
}

int swf_TextCountBits(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
{
    return swf_TextCountBits2(font, s, scale, gbits, abits, "iso-8859-1");
}

int swf_TextSetCharRecord(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
{
    return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "iso-8859-1");
}

int swf_TextCountBitsUTF8(SWFFONT * font, U8 * s, int scale, U8 * gbits, U8 * abits)
{
    return swf_TextCountBits2(font, s, scale, gbits, abits, "UTF8");
}

int swf_TextSetCharRecordUTF8(TAG * t, SWFFONT * font, U8 * s, int scale, U8 gbits, U8 abits)
{
    return swf_TextSetCharRecord2(t, font, s, scale, gbits, abits, "UTF8");
}

U32 swf_TextGetWidth(SWFFONT * font, U8 * s, int scale)
{
    U32 res = 0;

    if (font && s) {
        while (s[0]) {
            int g = -1;
            if (*s < font->maxascii)
                g = font->ascii2glyph[*s];
            if (g >= 0)
                res += font->glyph[g].advance / 20;
            s++;
        }
        if (scale)
            res = (res * scale) / 100;
    }
    return res;
}

SRECT swf_TextCalculateBBoxUTF8(SWFFONT * font, U8 * s, int scale)
{
    int xpos = 0;
    int ypos = 0;
    SRECT r;
    swf_GetRect(0, &r);
    while (*s) {
        int c = readUTF8char(&s);
        if(c==13 || c==10) {
            if(s[0] == 10) {
                s++;
            }
            xpos=0;
            ypos+=font->layout->leading;
            continue;
        }
        if (c < font->maxascii) {
            int g = font->ascii2glyph[c];
            if (g >= 0) {
                SRECT rn = font->layout->bounds[g];
                rn.xmin = (rn.xmin * scale) / 20 / 100 + xpos;
                rn.xmax = (rn.xmax * scale) / 20 / 100 + xpos;
                rn.ymin = (rn.ymin * scale) / 20 / 100 + ypos;
                rn.ymax = (rn.ymax * scale) / 20 / 100 + ypos;
                swf_ExpandRect2(&r, &rn);
                xpos += (font->glyph[g].advance * scale) / 20 / 100;
            }
        }
    }
    return r;
}


SWFFONT *swf_ReadFont(const char *filename)
{
    int f;
    SWF swf;
    if (!filename)
        return 0;
    f = open(filename, O_RDONLY|O_BINARY);

    if (f < 0 || swf_ReadSWF(f, &swf) < 0) {
        fprintf(stderr, "%s is not a valid SWF font file or contains errors.\n", filename);
        close(f);
        return 0;
    } else {
        SWFFONT *font;
        close(f);
        if (swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
            return 0;
        swf_FreeTags(&swf);
        return font;
    }
}

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)
{
    swf_SetRect(tag, &r);
    swf_ResetWriteBits(tag);

    flags &= ~(ET_HASTEXT | ET_HASTEXTCOLOR | ET_HASMAXLENGTH | ET_HASFONT | ET_HASLAYOUT);
    if (text)
        flags |= ET_HASTEXT;
    if (color)
        flags |= ET_HASTEXTCOLOR;
    if (maxlength)
        flags |= ET_HASMAXLENGTH;
    if (font)
        flags |= ET_HASFONT;
    if (layout)
        flags |= ET_HASLAYOUT;

    swf_SetBits(tag, flags, 16);

    if (flags & ET_HASFONT) {
        swf_SetU16(tag, font);  //font
        swf_SetU16(tag, height);        //fontheight
    }
    if (flags & ET_HASTEXTCOLOR) {
        swf_SetRGBA(tag, color);
    }
    if (flags & ET_HASMAXLENGTH) {
        swf_SetU16(tag, maxlength);     //maxlength
    }
    if (flags & ET_HASLAYOUT) {
        swf_SetU8(tag, layout->align);  //align
        swf_SetU16(tag, layout->leftmargin);    //left margin
        swf_SetU16(tag, layout->rightmargin);   //right margin
        swf_SetU16(tag, layout->indent);        //indent
        swf_SetU16(tag, layout->leading);       //leading
    }
    swf_SetString(tag, variable);
    if (flags & ET_HASTEXT)
        swf_SetString(tag, text);
}

SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, const char *text, int scale)
{
    SRECT r;
    U8 gbits, abits;
    U8 *utext = (U8 *) strdup(text);
    U8 *upos = utext;
    int x = 0, y = 0;
    int pos = 0;
    int ystep = 0;
    if (font->layout) {
        r = swf_TextCalculateBBoxUTF8(font, (U8*)text, scale * 20);
        ystep = font->layout->leading;
    } else {
        fprintf(stderr, "No layout information- can't compute text bbox accurately");
        /* Hm, without layout information, we can't compute a bounding
           box. We could call swf_FontCreateLayout to create a layout,
           but the caller probably doesn't want us to mess up his font
           structure.
         */
        r.xmin = r.ymin = 0;
        r.xmax = r.ymax = 1024 * 20;
        ystep = 100;
    }

    swf_SetRect(tag, &r);

    /* The text matrix is pretty boring, as it doesn't apply to
       individual characters, but rather whole text objects (or
       at least whole char records- haven't tested).
       So it can't do anything which we can't already do with
       the placeobject tag we use for placing the text on the scene.
     */
    swf_SetMatrix(tag, 0);

    swf_TextCountBitsUTF8(font, (U8*)text, scale * 20, &gbits, &abits);
    swf_SetU8(tag, gbits);
    swf_SetU8(tag, abits);

    while(*upos) {
        U8*next = upos;
        int count = 0;

        swf_TextSetInfoRecord(tag, font, (scale * 1024) / 100, rgb, x, y);      //scale
        x = 0;

        while(*next && *next!=13 && *next!=10 && count<127) {
            readUTF8char(&next);
            count++;
        }
        if(next[0] == 13 || next[0] == 10) {
            x = SET_TO_ZERO;
            y += ystep;
        }

        if(next[0] == 13 && next[1] == 10)
            next++;

        if(next[0] == 13 || next[0] == 10) {
            *next = 0;
            next++;
        }

        /* now set the text params- notice that a font size of
           1024 (or 1024*20 for definefont3) means that the glyphs will 
           be displayed exactly as they would be in/with a defineshape.
           This is not documented in the specs.
         */

        /* set the actual text- notice that we just pass our scale
           parameter over, as TextSetCharRecord calculates with
           percent, too */
        swf_TextSetCharRecordUTF8(tag, font, upos, scale * 20, gbits, abits);

        upos= next;
    }
    free(utext);

    swf_SetU8(tag, 0);
    return r;
}

void swf_FontCreateLayout(SWFFONT * f)
{
    S16 leading = 0;
    int t;
    if (f->layout)
        return;
    if (!f->numchars)
        return;

    f->layout = (SWFLAYOUT *) rfx_calloc(sizeof(SWFLAYOUT));
    f->layout->bounds = (SRECT *) rfx_alloc(f->numchars * sizeof(SRECT));
    f->layout->ascent = 0;
    f->layout->descent = 0;

    for (t = 0; t < f->numchars; t++) {
        SHAPE2 *shape2;
        SRECT bbox;
        int width;
        shape2 = swf_ShapeToShape2(f->glyph[t].shape);
        if (!shape2) {
            fprintf(stderr, "Shape parse error\n");
            exit(1);
        }
        bbox = swf_GetShapeBoundingBox(shape2);
        swf_Shape2Free(shape2);
        f->layout->bounds[t] = bbox;

        width = (bbox.xmax);

        /* The following is a heuristic- it may be that extractfont_DefineText
           has already found out some widths for individual characters (from the way
           they are used)- we now have to guess whether that width might be possible,
           which is the case if it isn't either much too big or much too small */
        if (width > f->glyph[t].advance * 3 / 2 || width < f->glyph[t].advance / 2)
            f->glyph[t].advance = width;

        if (-bbox.ymin > f->layout->ascent)
            f->layout->ascent = -bbox.ymin;
        if (bbox.ymax > f->layout->descent)
            f->layout->descent = bbox.ymax;
    }
}

void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, const char *text)
{
    U8 *s = (U8 *) text;
    int advance = 0;
    while (*s) {
        SHAPE *shape;
        SHAPE2 *shape2;
        SHAPELINE *l;
        U32 c = readUTF8char(&s);
        int g = font->ascii2glyph[c];
        shape = font->glyph[g].shape;
        if (((int) g) < 0) {
            fprintf(stderr, "No char %d in font %s\n", c, font->name ? (char *) font->name : "?");
            continue;
        }
        shape2 = swf_ShapeToShape2(shape);
        l = shape2->lines;
        while (l) {
            if (l->type == moveTo) {
                FPOINT to;
                to.x = l->x * size / 100.0 / 20.0 + advance;
                to.y = l->y * size / 100.0 / 20.0;
                draw->moveTo(draw, &to);
            } else if (l->type == lineTo) {
                FPOINT to;
                to.x = l->x * size / 100.0 / 20.0 + advance;
                to.y = l->y * size / 100.0 / 20.0;
                draw->lineTo(draw, &to);
            } else if (l->type == splineTo) {
                FPOINT mid, to;
                mid.x = l->sx * size / 100.0 / 20.0 + advance;
                mid.y = l->sy * size / 100.0 / 20.0;
                to.x = l->x * size / 100.0 / 20.0 + advance;
                to.y = l->y * size / 100.0 / 20.0;
                draw->splineTo(draw, &mid, &to);
            }
            l = l->next;
        }
        swf_Shape2Free(shape2);
        advance += font->glyph[g].advance * size / 100.0 / 20.0;
    }
}

void swf_WriteFont_AS3(SWFFONT * font, char *filename)
{
    if(!font->layout) 
        swf_FontCreateLayout(font);
    
    SWF swf;
    memset(&swf, 0, sizeof(SWF));
    swf.fileVersion = 9;
    swf.frameRate = 0x4000;
    swf.movieSize.xmax = 200;
    swf.movieSize.ymax = 200;
    
    if(!font->id) font->id=1;

    TAG *tag;
    swf.firstTag = tag = swf_InsertTag(tag, ST_DEFINEFONT3);
    swf_FontSetDefine2(tag, font);

    char*name = font->name?(char*)font->name:"font";

    tag = swf_InsertTag(tag, ST_NAMECHARACTER);
    swf_SetU16(tag, font->id);
    swf_SetString(tag, name);
    tag = swf_InsertTag(tag, ST_EXPORTASSETS);
    swf_SetU16(tag, 1);
    swf_SetU16(tag, font->id);
    swf_SetString(tag, name);
    tag = swf_AddAS3FontDefine(tag, font->id, (char*)font->name);
    
    tag = swf_InsertTag(tag, ST_END);
    swf_SaveSWF(&swf, filename);
    swf_FreeTags(&swf);
}

void swf_WriteFont(SWFFONT * font, char *filename)
{
    if(!font->layout)
        swf_FontCreateLayout(font);

    char viewer = 1;
    U16 id = 1;
    U16 depth = 1;

    font->id = id++;
    
    SWF swf;
    memset(&swf, 0, sizeof(SWF));
    swf.fileVersion = 8;
    swf.frameRate = 0x4000;
    swf.movieSize.xmax = 1024*20;
    swf.movieSize.ymax = 768*20;
    
    TAG *tag;
    swf.firstTag = tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
    swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xe0);swf_SetU8(tag, 0xff);

    tag = swf_InsertTag(tag, ST_DEFINEFONT3);
    swf_FontSetDefine2(tag, font);

    if(font->glyphnames) {
        int c;
        tag = swf_InsertTag(tag, ST_GLYPHNAMES);
        swf_SetU16(tag, font->id);
        swf_SetU16(tag, font->numchars);
        for (c = 0; c < font->numchars; c++) {
            if (font->glyphnames[c])
                swf_SetString(tag, font->glyphnames[c]);
            else
                swf_SetString(tag, "");
        }
    }

    if(viewer)
    {
        RGBA white = {255,255,255,255};
        RGBA black = {255,0,0,0};
        RGBA gray50 = {255,128,128,128};
        RGBA green = {255,0,255,0};
        int t;
        SCOORD miny = SCOORD_MAX;
        SCOORD maxy = SCOORD_MIN;
        double width = 0;
        U16 max_advance = 0;
        char*flags = rfx_calloc(font->numchars);
        double*xmin = rfx_calloc(sizeof(double)*(font->numchars+1));
        double*xmax = rfx_calloc(sizeof(double)*(font->numchars+1));
        int*xpos = rfx_calloc(sizeof(int)*(font->numchars+1));
        for(t=0;t<font->numchars;t++) {
            SHAPE*s = font->glyph[t].shape;
            SHAPE2*s2 = swf_ShapeToShape2(s);
            SRECT r = swf_GetShapeBoundingBox(s2);

            // inside a definefont3, everything is 20x the resolution:
            double rx1 = r.xmin / 20.0;
            double ry1 = r.ymin / 20.0;
            double rx2 = r.xmax / 20.0;
            double ry2 = r.ymax / 20.0;
            
            xmin[t]= rx1;
            xmax[t]= rx2;

            if(ry1<miny) {miny=ry1;}
            if(ry2>maxy) {maxy=ry2;}
            swf_Shape2Free(s2);free(s2);
            width += font->glyph[t].advance;
            if(font->glyph[t].advance>max_advance)
                max_advance = font->glyph[t].advance;
        }

        if(miny==SCOORD_MAX) miny=maxy=0;
        if(miny==maxy) maxy=miny+1;

        /* scale the font so that it's 256 pixels high */
        double scale = (int)((256.0*1024.0/(maxy-miny))*20.0);
        double overlarge_factor;
        int fontsize;
        if(scale > 32767) {
            fontsize = 32767;
            overlarge_factor = scale / 32767.0;
        } else {
            fontsize = scale;
            overlarge_factor = 1.0;
        }

        int textid = id++;
        int spriteid = id++;
        SRECT r;
        r.xmin = 0;
        r.ymin = miny*fontsize/1024;
        r.xmax = width*fontsize/20480;
        r.ymax = maxy*fontsize/1024;
        tag = swf_InsertTag(tag, ST_DEFINETEXT);
        swf_SetU16(tag, textid);
        swf_SetRect(tag, &r);
        swf_SetMatrix(tag, NULL);

        U8 abits = 15;
        U8 gbits = swf_CountBits(font->numchars, 0);
        swf_SetU8(tag, gbits);
        swf_SetU8(tag, abits);

        RGBA rgb = {255,0,0,0};

        swf_TextSetInfoRecord(tag, font, fontsize, &rgb, SET_TO_ZERO, SET_TO_ZERO);
        ActionTAG*array = 0;
        double x=0;
        array = action_PushString(array, "xpos");
        for(t=0;t<font->numchars;t++) {
            swf_SetU8(tag, 1);
            int width = abs((xmax[t] - xmin[t+1])*fontsize/1024) + 60;
            array = action_PushInt(array, x/20 +(xmin[t]*scale/1024)/20);
            x += width * overlarge_factor;
            swf_SetBits(tag, t, gbits);
            swf_SetBits(tag, width, abits);
            swf_SetU8(tag, 128);
        }
        array = action_PushInt(array, x/20);
        array = action_PushInt(array, font->numchars+1);
        array = action_InitArray(array);
        array = action_SetVariable(array);
        swf_SetU8(tag, 0);

        if(font->layout) {
            tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
            SHAPE* s;
            swf_ShapeNew(&s);
            int ls = swf_ShapeAddLineStyle(s,20,&white);
            int shapeid = id++;
            swf_SetU16(tag,shapeid);
            SRECT r;
            r.xmin = 0;
            r.xmax = 1024*20;
            r.ymin = 0;
            r.ymax = 256*20;
            swf_SetRect(tag,&r);
            swf_SetShapeHeader(tag,s);
            swf_ShapeSetAll(tag,s,0,0,ls,0,0);

            /* Ç and  are good chars to test ascent/descent extend */
            int y1 = (-font->layout->ascent-miny*20.0)*256.0/(maxy-miny);
            int y2 = (font->layout->descent-miny*20.0)*256.0/(maxy-miny);

            swf_ShapeSetMove(tag,s,0,y1);
            swf_ShapeSetLine(tag,s,width,0);
            swf_ShapeSetMove(tag,s,0,y2);
            swf_ShapeSetLine(tag,s,width,0);

            swf_ShapeSetEnd(tag);
            swf_ShapeFree(s);
            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
            swf_ObjectPlace(tag, shapeid, depth++, NULL, NULL, NULL);
        }

        /* shapes */
        
        for(t=0;t<font->numchars;t++) {
            tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
            SHAPE* s;
            swf_ShapeNew(&s);
            int ls = swf_ShapeAddLineStyle(s,20*2,&black);
            int ls2 = swf_ShapeAddLineStyle(s,20*2,&green);
            int fs = swf_ShapeAddSolidFillStyle(s, &gray50);
            int shapeid = id++;
            swf_SetU16(tag,shapeid);
            SRECT r;
            r.xmin = 0;
            r.xmax = 1024*20;
            r.ymin = 0;
            r.ymax = 512*20;
            swf_SetRect(tag,&r);
            swf_SetShapeHeader(tag,s);
            swf_ShapeSetAll(tag,s,0,0,ls,fs,0);
            SHAPE2*s2 = swf_ShapeToShape2(font->glyph[t].shape);
            SHAPELINE*l = s2->lines;
            int lastx=0,lasty=0;

            double x1 = (1024*20 - (xmax[t] - xmin[t])*20*2*scale/20480.0)/2;
            double y1 = -miny*20*scale*2/20480.0;
            double scalex = scale*2/20480.0;
            double scaley = scale*2/20480.0;

            while(l) {
                int lx = (l->x)*scalex+x1;
                int ly = (l->y)*scaley+y1;
                int sx = (l->sx)*scalex+x1;
                int sy = (l->sy)*scaley+y1;
                if(l->type == moveTo) {
                    swf_ShapeSetMove(tag,s,lx,ly);
                } else if(l->type == lineTo) {
                    swf_ShapeSetLine(tag,s,lx-lastx,ly-lasty);
                } else if(l->type == splineTo) {
                    swf_ShapeSetCurve(tag,s,sx-lastx,sy-lasty,lx-sx,ly-sy);
                }
                lastx = lx;
                lasty = ly;
                l = l->next;
            }
            
            if(font->alignzones) {
                ALIGNZONE*zone = &font->alignzones[t];
                swf_ShapeSetAll(tag,s,0,0,ls2,SET_TO_ZERO,SET_TO_ZERO);
                if((zone->x&zone->dx)!=0xffff) {
                    double x = F16toFloat(zone->x)*20480.0*scalex+x1;
                    double dx = (F16toFloat(zone->x)+F16toFloat(zone->dx))*20480.0*scalex+x1;
                    swf_ShapeSetMove(tag,s,x,0);
                    swf_ShapeSetLine(tag,s,0,1024*20);
                    swf_ShapeSetMove(tag,s,dx,0);
                    swf_ShapeSetLine(tag,s,0,1024*20);
                }
                if((zone->y&zone->dy)!=0xffff) {
                    double y = -F16toFloat(zone->y)*20480.0*scaley+y1;
                    double dy = -(F16toFloat(zone->y)+F16toFloat(zone->dy))*20480.0*scaley+y1;
                    swf_ShapeSetMove(tag,s,0,y);
                    swf_ShapeSetLine(tag,s,1024*20,0);
                    swf_ShapeSetMove(tag,s,0,dy);
                    swf_ShapeSetLine(tag,s,1024*20,0);
                }
            }

            swf_ShapeSetEnd(tag);
            swf_ShapeFree(s);
        
            tag = swf_InsertTag(tag, ST_DEFINESPRITE);
            U16 spriteid=id++;
            swf_SetU16(tag, spriteid);
            swf_SetU16(tag, 1);
            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
            swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
            tag = swf_InsertTag(tag, ST_END);
            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
            MATRIX m;
            swf_GetMatrix(0, &m);
            m.ty = 20000;
            char txt[80];
            sprintf(txt, "char%d", font->numchars-t);
            swf_ObjectPlace(tag, spriteid, depth++, &m, NULL, txt);
        }
        
        /* marker */
        tag = swf_InsertTag(tag, ST_DEFINESHAPE2);
        int shapeid=id++;
        RGBA blue = {0xff,0xc0,0xc0,0xff};
        swf_ShapeSetRectangle(tag, shapeid, 20, 20, &blue);
        tag = swf_InsertTag(tag, ST_DEFINESPRITE);
        U16 spriteid2=id++;
        swf_SetU16(tag, spriteid2);
        swf_SetU16(tag, 1);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        swf_ObjectPlace(tag, shapeid, 1, NULL, NULL, NULL);
        tag = swf_InsertTag(tag, ST_END);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        swf_ObjectPlace(tag, spriteid2, depth++, NULL, NULL, "marker");
        
        /* textbar */
        tag = swf_InsertTag(tag, ST_DEFINESPRITE);
        swf_SetU16(tag, spriteid);
        swf_SetU16(tag, 1);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        MATRIX m;
        swf_GetMatrix(0, &m);
        m.sx = 65536 * overlarge_factor;
        m.sy = 65536 * overlarge_factor;
        m.tx = 0;
        m.ty = -miny*256*20/(maxy-miny);
        swf_ObjectPlace(tag, textid, 1, &m, NULL, NULL);
        tag = swf_InsertTag(tag, ST_END);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        swf_ObjectPlace(tag, spriteid, depth++, NULL, NULL, "textbar");
        
        /* marker2 */
        RGBA blue2 = {0x80,0x80,0xff,0x80};
        tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
        int shapeid2=id++;
        swf_ShapeSetRectangleWithBorder(tag, shapeid2, 20, 20, &blue2, 0, &white);
        tag = swf_InsertTag(tag, ST_DEFINESPRITE);
        U16 spriteid3=id++;
        swf_SetU16(tag, spriteid3);
        swf_SetU16(tag, 1);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        swf_ObjectPlace(tag, shapeid2, 1, NULL, NULL, NULL);
        tag = swf_InsertTag(tag, ST_END);
        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
        swf_ObjectPlace(tag, spriteid3, depth++, NULL, NULL, "marker2");


char*data = 
" var mouseListener = new Object();"
" var speed = 0;"
" var myx = 0;"
" var currentMouseOver, currentChar;"
" mouseListener.onMouseDown = function() { "
"     eval(\"_root.char\"+currentChar)._y = 20000;"
"     currentChar = currentMouseOver;"
"     var i = currentMouseOver;"
"     eval(\"_root.char\"+i)._y = 256;"
"     _root.marker2._yscale=256*100;"
"     _root.marker2._xscale=(xpos[i-1]-xpos[i])*100;"
"     _root.marker2._x=xpos[i]+myx;"
" };"
" mouseListener.onMouseMove = function() { "
"     if(_ymouse<256) {"
"          speed = Math.abs(_xmouse-512)>256?(512-_xmouse)/8:0;"
"     } else {"
"          speed = 0;"
"     }; "
" }; "
" setInterval( function(){ "
"     if(_ymouse<256) {"
"         var i, x=_xmouse-_root.textbar._x;"
"         for(i=xpos.length-1;i>0;i--) {"
"             if(x<xpos[i-1]) break;"
"         }"
"         currentMouseOver = i;"
"         _root.marker._yscale=256*100;"
"         _root.marker._xscale=(xpos[i-1]-xpos[i])*100;"
"         _root.marker._x=xpos[i]+myx;"
"         _root.textbar._x += 0.05;"
"     }"
"     if(myx+speed>0) {"
"         speed=0;"
"     } else if(myx+speed<-xpos[0]+1024) {"
"         speed=0;"
"     }"
"     myx+=speed;"
"     _root.textbar._x = myx;"
"     _root.marker._x += speed;"
"     _root.marker2._x += speed;"
" }, 20);"
" Mouse.addListener(mouseListener);"
;
        ActionTAG* atag = swf_ActionCompile(data, 6);

        tag = swf_InsertTag(tag, ST_DOACTION);
        swf_ActionSet(tag, array);
        swf_ActionSet(tag, atag);
        swf_SetU8(tag, 0);
        swf_ActionFree(atag);

        tag = swf_InsertTag(tag, ST_SHOWFRAME);

        free(flags);
        free(xmin);
        free(xmax);
    }

    tag = swf_InsertTag(tag, ST_END);

    swf.compressed = -1;
    swf_SaveSWF(&swf, filename);
    swf_FreeTags(&swf);
}

/* [<][>][^][v][top][bottom][index][help] */