more horizontal refactoring
[swftools.git] / lib / modules / swftext.c
index 194ec72..62546db 100644 (file)
@@ -6,7 +6,7 @@
    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
@@ -255,7 +255,7 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
     U32 *offset;
     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;
@@ -278,7 +278,6 @@ 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;
 
@@ -350,6 +349,13 @@ int swf_FontExtract_DefineFont2(int id, SWFFONT * font, TAG * tag)
        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);
@@ -516,7 +522,7 @@ swf_FontExtract_DefineTextCallback(int id, SWFFONT * f, TAG * t, int jobs,
 }
 
 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);
 }
@@ -526,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;
@@ -561,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:
@@ -625,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;
     }
 }
@@ -777,6 +839,14 @@ 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;
@@ -790,6 +860,8 @@ void swf_FontSort(SWFFONT * font)
     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]) {
@@ -832,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)
@@ -851,12 +923,9 @@ int swf_FontInitUsage(SWFFONT * f)
        fprintf(stderr, "Usage initialized twice");
        return -1;
     }
-    f->use = (FONTUSAGE*)rfx_alloc(sizeof(FONTUSAGE));
-    f->use->is_reduced = 0;
+    f->use = (FONTUSAGE*)rfx_calloc(sizeof(FONTUSAGE));
     f->use->smallest_size = 0xffff;
-    f->use->used_glyphs = 0;
     f->use->chars = (int*)rfx_calloc(sizeof(f->use->chars[0]) * f->numchars);
-    f->use->glyphs_specified = 0;
     return 0;
 }
 
@@ -880,14 +949,14 @@ int swf_FontUse(SWFFONT * f, U8 * s)
     return 0;
 }
 
-int swf_FontUseUTF8(SWFFONT * f, U8 * s, U16 size)
+int swf_FontUseUTF8(SWFFONT * f, const U8 * s, U16 size)
 {
     if( (!s))
        return -1;
     int ascii;
     while (*s)
     {
-       ascii = readUTF8char(&s);
+       ascii = readUTF8char((U8**)&s);
        if(ascii < f->maxascii && f->ascii2glyph[ascii]>=0)
            swf_FontUseGlyph(f, f->ascii2glyph[ascii], size);
     }
@@ -906,6 +975,80 @@ int swf_FontUseAll(SWFFONT* f)
     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)
@@ -1062,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++) {
@@ -1146,6 +1292,13 @@ 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;
@@ -1173,10 +1326,15 @@ void swf_FontFree(SWFFONT * f)
         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);
 }