more horizontal refactoring
[swftools.git] / lib / modules / swftext.c
index 1612e63..62546db 100644 (file)
@@ -1,13 +1,13 @@
 /* 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 Matthias Kramm
+   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
@@ -22,7 +22,9 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-static U32 readUTF8char(U8 ** text)
+#include "../rfxswf.h"
+
+U32 readUTF8char(U8 ** text)
 {
     U32 c = 0;
     if (!(*(*text) & 0x80))
@@ -111,14 +113,13 @@ int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*s
     n = 0;
 
     while (t) {
-       if (swf_GetTagID(t) == ST_DEFINEFONT2 || swf_GetTagID(t) == ST_DEFINEFONT) {
+       if (swf_isFontTag(t)) {
            n++;
            if (FontCallback) {
                U16 id;
                int l;
                U8 s[257];
                s[0] = 0;
-               swf_SaveTagPos(t);
                swf_SetTagPos(t, 0);
 
                id = swf_GetU16(t);
@@ -130,8 +131,6 @@ int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*s
                }
 
                (FontCallback) (self, id, s);
-
-               swf_RestoreTagPos(t);
            }
        }
        t = swf_NextTag(t);
@@ -142,7 +141,6 @@ int swf_FontEnumerate(SWF * swf, void (*FontCallback) (void*, U16, U8 *), void*s
 int swf_FontExtract_DefineFont(int id, SWFFONT * f, TAG * t)
 {
     U16 fid;
-    swf_SaveTagPos(t);
     swf_SetTagPos(t, 0);
 
     fid = swf_GetU16(t);
@@ -157,15 +155,13 @@ int swf_FontExtract_DefineFont(int id, SWFFONT * f, TAG * t)
        of = swf_GetU16(t);
        n = of / 2;
        f->numchars = n;
-       f->glyph = rfx_calloc(sizeof(SWFGLYPH) * 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);
     }
-
-    swf_RestoreTagPos(t);
     return id;
 }
 
@@ -174,7 +170,6 @@ int swf_FontExtract_DefineFontInfo(int id, SWFFONT * f, TAG * t)
     U16 fid;
     U16 maxcode;
     U8 flags;
-    swf_SaveTagPos(t);
     swf_SetTagPos(t, 0);
 
     fid = swf_GetU16(t);
@@ -229,17 +224,12 @@ int swf_FontExtract_DefineFontInfo(int id, SWFFONT * f, TAG * t)
        for (i = 0; i < f->numchars; i++)
            f->ascii2glyph[f->glyph2ascii[i]] = i;
     }
-
-    swf_RestoreTagPos(t);
     return id;
 }
 
 int swf_FontExtract_GlyphNames(int id, SWFFONT * f, TAG * tag)
 {
     U16 fid;
-    U16 maxcode;
-    U8 flags;
-    swf_SaveTagPos(tag);
     swf_SetTagPos(tag, 0);
 
     fid = swf_GetU16(tag);
@@ -247,13 +237,11 @@ int swf_FontExtract_GlyphNames(int id, SWFFONT * f, TAG * tag)
     if (fid == id) {
        int num = swf_GetU16(tag);
        int t;
-       f->glyphnames = rfx_alloc(sizeof(char *) * num);
+       f->glyphnames = (char**)rfx_alloc(sizeof(char *) * num);
        for (t = 0; t < num; t++) {
            f->glyphnames[t] = strdup(swf_GetString(tag));
        }
     }
-
-    swf_RestoreTagPos(tag);
     return id;
 }
 
@@ -265,16 +253,15 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
     int fid;
     U32 offset_start;
     U32 *offset;
-    U8 flags1, flags2, namelen;
-    swf_SaveTagPos(tag);
+    U8 flags1, langcode, namelen;
     swf_SetTagPos(tag, 0);
-    font->version = 2;
+    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);
-    flags2 = swf_GetU8(tag);   //reserved flags
+    langcode = swf_GetU8(tag); //reserved flags
 
     if (flags1 & 1)
        font->style |= FONT_STYLE_BOLD;
@@ -291,14 +278,13 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
     font->name = (U8 *) rfx_alloc(namelen + 1);
     font->name[namelen] = 0;
     swf_GetBlock(tag, font->name, namelen);
-    font->version = 2;
     glyphcount = swf_GetU16(tag);
     font->numchars = glyphcount;
 
     font->glyph = (SWFGLYPH *) rfx_calloc(sizeof(SWFGLYPH) * glyphcount);
     font->glyph2ascii = (U16 *) rfx_calloc(sizeof(U16) * glyphcount);
 
-    offset = rfx_calloc(sizeof(U32)*(glyphcount+1));
+    offset = (U32*)rfx_calloc(sizeof(U32)*(glyphcount+1));
     offset_start = tag->pos;
 
     if (flags1 & 8) {          // wide offsets
@@ -323,14 +309,15 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
        swf_GetSimpleShape(tag, &(font->glyph[t].shape));
     }
 
-    swf_SetTagPos(tag, offset[glyphcount]+offset_start);
+    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
+       if (flags1 & 4)         // wide codes (always on for definefont3)
            code = swf_GetU16(tag);
        else
            code = swf_GetU8(tag);
@@ -358,10 +345,17 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
            S16 advance = swf_GetS16(tag);
            font->glyph[t].advance = advance;
        }
-       font->layout->bounds = rfx_alloc(glyphcount * sizeof(SRECT));
+       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);
@@ -369,7 +363,7 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
 
        font->layout->kerning = (SWFKERNING *) rfx_alloc(sizeof(SWFKERNING) * kerningcount);
        if (kerningcount) {
-           font->layout->kerning = rfx_alloc(sizeof(*font->layout->kerning) * 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);
@@ -382,10 +376,57 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
            }
        }
     }
-    swf_RestoreTagPos(t);
     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
@@ -400,14 +441,13 @@ swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
     SRECT r;
     MATRIX m;
     U8 gbits, abits;
-    int fid = 0;
+    int fid = -1;
     RGBA color;
     int x = 0, y = 0;
     int fontsize = 0;
 
     memset(&color, 0, sizeof(color));
 
-    swf_SaveTagPos(t);
     swf_SetTagPos(t, 0);
 
     cid = swf_GetU16(t);
@@ -459,7 +499,6 @@ swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
                adv = swf_GetBits(t, abits);
                xpos += adv;
 
-               // <deprecated>
                if (id == fid) {
                    if (jobs & FEDTJ_PRINT) {
                        int code = f->glyph2ascii[glyph];
@@ -467,12 +506,7 @@ swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
                    }
                    if (jobs & FEDTJ_MODIFY)
                        f->glyph[glyph].advance = adv * 20;     //?
-               } else {
-                   if (jobs & FEDTJ_PRINT) {
-                       printf("?");
-                   }
                }
-               // </deprecated>
 
                buf[i] = glyph;
            }
@@ -484,12 +518,11 @@ swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
        }
     }
 
-    swf_RestoreTagPos(t);
     return id;
 }
 
 int swf_ParseDefineText(TAG * tag,
-                   void (*callback) (void *self, int *chars, int *ypos, int nr, int fontid, int fontsize, int xstart, int ystart, RGBA * color), void *self)
+                   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);
 }
@@ -499,6 +532,52 @@ 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;
@@ -519,9 +598,14 @@ int swf_FontExtract(SWF * swf, int id, SWFFONT * *font)
            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);
@@ -529,7 +613,11 @@ int swf_FontExtract(SWF * swf, int id, SWFFONT * *font)
 
        case ST_DEFINETEXT:
        case ST_DEFINETEXT2:
-           nid = swf_FontExtract_DefineText(id, f, t, f->layout ? 0 : FEDTJ_MODIFY);
+           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:
@@ -572,18 +660,20 @@ void swf_LayoutFree(SWFLAYOUT * 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;
-           }
+    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;
        }
-       rfx_free(f->glyphnames);
-       f->glyphnames = 0;
-    }
-
 }
 static void font_freeusage(SWFFONT*f)
 {
@@ -591,6 +681,12 @@ static void font_freeusage(SWFFONT*f)
        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;
     }
 }
@@ -652,6 +748,54 @@ int swf_FontReduce_old(SWFFONT * 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;
@@ -659,7 +803,7 @@ int swf_FontReduce(SWFFONT * f)
     int max_glyph = 0;
     if ((!f) || (!f->use) || f->use->is_reduced)
        return -1;
-    
+
     font_freelayout(f);
     font_freeglyphnames(f);
 
@@ -674,13 +818,14 @@ int swf_FontReduce(SWFFONT * f)
                f->glyph[i].shape = 0;
                f->glyph[i].advance = 0;
            }
-           f->use->used_glyphs++;
+//         f->use->used_glyphs++;
        } else {
+           f->use->used_glyphs++;
            max_glyph = i+1;
        }
     }
     for (i = 0; i < f->maxascii; i++) {
-       if(!f->use->chars[f->ascii2glyph[i]]) {
+       if(f->ascii2glyph[i] > -1 && !f->use->chars[f->ascii2glyph[i]]) {
            if(f->ascii2glyph) {
                f->ascii2glyph[i] = -1;
            }
@@ -694,19 +839,29 @@ int swf_FontReduce(SWFFONT * 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, k;
+    int i, j;
     int *newplace;
     int *newpos;
     if (!font)
        return;
-    
-    newplace = rfx_alloc(sizeof(int) * font->numchars);
+
+    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]) {
@@ -740,7 +895,7 @@ void swf_FontSort(SWFFONT * font)
                }
            }
        }
-    newpos = rfx_alloc(sizeof(int) * font->numchars);
+    newpos = (int*)rfx_alloc(sizeof(int) * font->numchars);
     for (i = 0; i < font->numchars; i++) {
        newpos[newplace[i]] = i;
     }
@@ -749,8 +904,8 @@ void swf_FontSort(SWFFONT * font)
            font->ascii2glyph[i] = newpos[font->ascii2glyph[i]];
     }
 
-    rfx_free(newpos);
     rfx_free(newplace);
+    font->glyph2glyph = newpos;
 }
 
 void swf_FontPrepareForEditText(SWFFONT * font)
@@ -768,9 +923,9 @@ int swf_FontInitUsage(SWFFONT * f)
        fprintf(stderr, "Usage initialized twice");
        return -1;
     }
-    f->use = rfx_alloc(sizeof(FONTUSAGE));
-    f->use->is_reduced = 0;
-    f->use->chars = rfx_calloc(sizeof(f->use->chars[0]) * f->numchars);
+    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;
 }
 
@@ -788,21 +943,123 @@ int swf_FontUse(SWFFONT * f, U8 * s)
        return -1;
     while (*s) {
        if(*s < f->maxascii && f->ascii2glyph[*s]>=0)
-           swf_FontUseGlyph(f, f->ascii2glyph[*s]);
+           swf_FontUseGlyph(f, f->ascii2glyph[*s], /*FIXME*/0xffff);
        s++;
     }
     return 0;
 }
 
-int swf_FontUseGlyph(SWFFONT * f, int glyph)
+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) 
+    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;
 }
 
@@ -846,7 +1103,7 @@ static inline int fontSize(SWFFONT * font)
     int size = 0;
     for (t = 0; t < font->numchars; t++) {
        int l = 0;
-       if(font->glyph[t].shape) 
+       if(font->glyph[t].shape)
            l = (font->glyph[t].shape->bitlen + 7) / 8;
        else
            l = 8;
@@ -860,7 +1117,6 @@ int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
     U8 flags = 0;
     int t;
     int pos;
-    int pos2;
     swf_SetU16(tag, f->id);
 
     if (f->layout) flags |= 128;               // haslayout
@@ -887,10 +1143,11 @@ int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
     swf_SetU8(tag, 0);         //reserved flags
     if (f->name) {
        /* font name */
-       swf_SetU8(tag, strlen(f->name));
-       swf_SetBlock(tag, f->name, strlen(f->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 */
@@ -948,12 +1205,15 @@ int swf_FontSetDefine2(TAG * tag, SWFFONT * f)
     if (f->layout) {
        swf_SetU16(tag, f->layout->ascent);
        swf_SetU16(tag, f->layout->descent);
-       swf_SetU16(tag, f->layout->leading);
+       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);
-           swf_SetRect(tag, &f->layout->bounds[t]);
+           /* 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++) {
@@ -990,7 +1250,7 @@ int swf_FontSetInfo(TAG * t, SWFFONT * f)
        return -1;
     swf_ResetWriteBits(t);
     swf_SetU16(t, f->id);
-    l = f->name ? strlen(f->name) : 0;
+    l = f->name ? strlen((const char *)f->name) : 0;
     if (l > 255)
        l = 255;
     swf_SetU8(t, l);
@@ -1032,45 +1292,61 @@ int swf_TextPrintDefineText(TAG * t, SWFFONT * f)
     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;
+        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->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->ascii2glyph)
+    {
+        rfx_free(f->ascii2glyph);
+        f->ascii2glyph = NULL;
+    }
+    if (f->glyph2ascii)
+    {
+        rfx_free(f->glyph2ascii);
+        f->glyph2ascii = 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 dx, int dy)
+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) | (dx ? TF_HASXOFFSET : 0)
-       | (dy ? TF_HASYOFFSET : 0);
+    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)
@@ -1081,20 +1357,20 @@ int swf_TextSetInfoRecord(TAG * t, SWFFONT * font, U16 size, RGBA * color, int d
        else
            swf_SetRGB(t, color);
     }
-    if (dx) {
-       if(dx != SET_TO_ZERO) {
-           if(dx>32767 || dx<-32768)
-               fprintf(stderr, "Warning: Horizontal char position overflow: %d\n", dx);
-           swf_SetS16(t, dx);
+    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 (dy) {
-       if(dy != SET_TO_ZERO) {
-           if(dy>32767 || dy<-32768)
-               fprintf(stderr, "Warning: Vertical char position overflow: %d\n", dy);
-           swf_SetS16(t, dy);
+    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);
        }
@@ -1263,7 +1539,7 @@ SRECT swf_TextCalculateBBoxUTF8(SWFFONT * font, U8 * s, int scale)
 }
 
 
-SWFFONT *swf_ReadFont(char *filename)
+SWFFONT *swf_ReadFont(const char *filename)
 {
     int f;
     SWF swf;
@@ -1285,173 +1561,7 @@ SWFFONT *swf_ReadFont(char *filename)
     }
 }
 
-void swf_WriteFont(SWFFONT * font, char *filename)
-{
-    SWF swf;
-    TAG *t;
-    SRECT r;
-    RGBA rgb;
-    int f;
-    int useDefineFont2 = 0;
-    int storeGlyphNames = 1;
-
-    if (font->layout)
-       useDefineFont2 = 1;     /* the only thing new in definefont2 
-                                  is layout information. */
-
-    font->id = WRITEFONTID;    //"FN"
-
-    memset(&swf, 0x00, sizeof(SWF));
-
-    swf.fileVersion = 4;
-    swf.frameRate = 0x4000;
-
-    /* if we use DefineFont1 to store the characters,
-       we have to build a textfield to store the
-       advance values. While at it, we can also
-       make the whole .swf viewable */
-
-    /* we now always create viewable swfs, even if we
-       did use definefont2 -mk */
-    t = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
-    swf.firstTag = t;
-    rgb.r = 0xef;
-    rgb.g = 0xef;
-    rgb.b = 0xff;
-    swf_SetRGB(t, &rgb);
-    if (!useDefineFont2) {
-       t = swf_InsertTag(t, ST_DEFINEFONT);
-       swf_FontSetDefine(t, font);
-       t = swf_InsertTag(t, ST_DEFINEFONTINFO);
-       swf_FontSetInfo(t, font);
-    } else {
-       t = swf_InsertTag(t, ST_DEFINEFONT2);
-       swf_FontSetDefine2(t, font);
-    }
-
-    if (storeGlyphNames && font->glyphnames) {
-       int c;
-       t = swf_InsertTag(t, ST_GLYPHNAMES);
-       swf_SetU16(t, WRITEFONTID);
-       swf_SetU16(t, font->numchars);
-       for (c = 0; c < font->numchars; c++) {
-           if (font->glyphnames[c])
-               swf_SetString(t, font->glyphnames[c]);
-           else
-               swf_SetString(t, "");
-       }
-    }
-
-    if (1)                     //neccessary only for df1, but pretty to look at anyhow, so do it always
-    {
-       int textscale = 400;
-       int s;
-       int xmax = 0;
-       int ymax = 0;
-       int ypos = 1;
-       U8 gbits, abits;
-       int x, y, c;
-       int range = font->maxascii;
-
-       c = 0;
-       if (useDefineFont2 && range > 256) {
-           range = 256;
-       }
-
-       for (s = 0; s < range; s++) {
-           int g = font->ascii2glyph[s];
-           if (g >= 0) {
-               if ((font->glyph[g].advance * textscale / 20) / 64 > xmax) {
-                   xmax = (font->glyph[g].advance * textscale / 20) / 64;
-               }
-               c++;
-           }
-           if ((s & 15) == 0) {
-               if (c) {
-                   ypos++;
-               }
-               c = 0;
-           }
-       }
-       ymax = ypos * textscale * 2;
-
-       swf.movieSize.xmax = xmax * 20;
-       swf.movieSize.ymax = ymax;
-
-       t = swf_InsertTag(t, ST_DEFINETEXT);
-
-       swf_SetU16(t, font->id + 1);    // ID
-
-       r.xmin = 0;
-       r.ymin = 0;
-       r.xmax = swf.movieSize.xmax;
-       r.ymax = swf.movieSize.ymax;
-
-       swf_SetRect(t, &r);
-
-       swf_SetMatrix(t, NULL);
-
-       abits = swf_CountBits(xmax * 16, 0);
-       gbits = 8;
-
-       swf_SetU8(t, gbits);
-       swf_SetU8(t, abits);
-
-       rgb.r = 0x00;
-       rgb.g = 0x00;
-       rgb.b = 0x00;
-       ypos = 1;
-       for (y = 0; y < ((range + 15) / 16); y++) {
-           int c = 0, lastx = -1;
-           for (x = 0; x < 16; x++) {
-               int g = (y * 16 + x < range) ? font->ascii2glyph[y * 16 + x] : -1;
-               if (g >= 0 && font->glyph[g].shape) {
-                   c++;
-                   if (lastx < 0)
-                       lastx = x * xmax;
-               }
-           }
-           if (c) {
-               swf_TextSetInfoRecord(t, font, textscale, &rgb, lastx + 1, textscale * ypos * 2);
-               for (x = 0; x < 16; x++) {
-                   int g = (y * 16 + x < range) ? font->ascii2glyph[y * 16 + x] : -1;
-                   if (g >= 0 && font->glyph[g].shape) {
-                       if (lastx != x * xmax) {
-                           swf_TextSetInfoRecord(t, 0, 0, 0, x * xmax + 1, 0);
-                       }
-                       swf_SetU8(t, 1);
-                       swf_SetBits(t, g, gbits);
-                       swf_SetBits(t, font->glyph[g].advance / 20, abits);
-                       lastx = x * xmax + (font->glyph[g].advance / 20);
-                       swf_ResetWriteBits(t);
-                   }
-               }
-               ypos++;
-           }
-       }
-       swf_SetU8(t, 0);
-
-
-       t = swf_InsertTag(t, ST_PLACEOBJECT2);
-
-       swf_ObjectPlace(t, font->id + 1, 1, NULL, NULL, NULL);
-
-       t = swf_InsertTag(t, ST_SHOWFRAME);
-
-    }
-
-    t = swf_InsertTag(t, ST_END);
-
-    f = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
-    if FAILED
-       (swf_WriteSWF(f, &swf)) fprintf(stderr, "WriteSWF() failed in writeFont().\n");
-    close(f);
-
-    swf_FreeTags(&swf);
-}
-
-
-void swf_SetEditText(TAG * tag, U16 flags, SRECT r, char *text, RGBA * color, int maxlength, U16 font, U16 height, EditTextLayout * layout, char *variable)
+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);
@@ -1492,7 +1602,7 @@ void swf_SetEditText(TAG * tag, U16 flags, SRECT r, char *text, RGBA * color, in
        swf_SetString(tag, text);
 }
 
-SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, char *text, int scale)
+SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, const char *text, int scale)
 {
     SRECT r;
     U8 gbits, abits;
@@ -1502,7 +1612,7 @@ SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, char *text, int s
     int pos = 0;
     int ystep = 0;
     if (font->layout) {
-       r = swf_TextCalculateBBoxUTF8(font, text, scale * 20);
+       r = swf_TextCalculateBBoxUTF8(font, (U8*)text, scale * 20);
        ystep = font->layout->leading;
     } else {
        fprintf(stderr, "No layout information- can't compute text bbox accurately");
@@ -1526,14 +1636,14 @@ SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, char *text, int s
      */
     swf_SetMatrix(tag, 0);
 
-    swf_TextCountBitsUTF8(font, text, scale * 20, &gbits, &abits);
+    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;
 
@@ -1548,19 +1658,20 @@ SRECT swf_SetDefineText(TAG * tag, SWFFONT * font, RGBA * rgb, char *text, int s
 
        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 means that the glyphs will be displayed exactly
-          as they would be in/with a defineshape. (Try to find
-          *that* in the flash specs)
+          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 
+          parameter over, as TextSetCharRecord calculates with
           percent, too */
        swf_TextSetCharRecordUTF8(tag, font, upos, scale * 20, gbits, abits);
 
@@ -1583,8 +1694,8 @@ void swf_FontCreateLayout(SWFFONT * f)
 
     f->layout = (SWFLAYOUT *) rfx_calloc(sizeof(SWFLAYOUT));
     f->layout->bounds = (SRECT *) rfx_alloc(f->numchars * sizeof(SRECT));
-    f->layout->ascent = -32767;
-    f->layout->descent = -32767;
+    f->layout->ascent = 0;
+    f->layout->descent = 0;
 
     for (t = 0; t < f->numchars; t++) {
        SHAPE2 *shape2;
@@ -1609,13 +1720,13 @@ void swf_FontCreateLayout(SWFFONT * f)
            f->glyph[t].advance = width;
 
        if (-bbox.ymin > f->layout->ascent)
-           f->layout->ascent = bbox.ymin;
+           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, char *text)
+void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, const char *text)
 {
     U8 *s = (U8 *) text;
     int advance = 0;
@@ -1657,3 +1768,395 @@ void swf_DrawText(drawer_t * draw, SWFFONT * font, int size, char *text)
        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);
+}