fixed bug in unicode relocation
[swftools.git] / lib / gfxfont.c
index 3aa2a8d..760db9a 100644 (file)
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
+#include <assert.h>
 #include "../config.h"
 #include "gfxdevice.h"
 #include "gfxtools.h"
+#include "gfxfont.h"
+#include "ttf.h"
+#include "mem.h"
+#include "log.h"
 
 static int loadfont_scale = 64;
 static int full_unicode = 1;
@@ -60,7 +65,7 @@ typedef struct _gfxdrawinfo_t {
     double quality;
 } gfxdrawinfo_t;
 
-static int ft_move_to(FT_Vector* _to, void* user) 
+static int ft_move_to(const FT_Vector* _to, void* user) 
 {
     gfxdrawinfo_t* info = (gfxdrawinfo_t*)user;
     gfxdrawer_t* draw = info->draw;
@@ -69,7 +74,7 @@ static int ft_move_to(FT_Vector* _to, void* user)
     draw->moveTo(draw, x,y);
     return 0;
 }
-static int ft_line_to(FT_Vector* _to, void* user) 
+static int ft_line_to(const FT_Vector* _to, void* user) 
 {
     gfxdrawinfo_t* info = (gfxdrawinfo_t*)user;
     gfxdrawer_t* draw = info->draw;
@@ -78,7 +83,7 @@ static int ft_line_to(FT_Vector* _to, void* user)
     draw->lineTo(draw, x,y);
     return 0;
 }
-static int ft_cubic_to(FT_Vector* _c1, FT_Vector* _c2, FT_Vector* _to, void* user)
+static int ft_cubic_to(const FT_Vector* _c1, const FT_Vector* _c2, const FT_Vector* _to, void* user)
 {
     gfxdrawinfo_t* info = (gfxdrawinfo_t*)user;
     gfxdrawer_t* draw = info->draw;
@@ -91,7 +96,7 @@ static int ft_cubic_to(FT_Vector* _c1, FT_Vector* _c2, FT_Vector* _to, void* use
     gfxdraw_cubicTo(draw, c1x, c1y, c2x, c2y, tox, toy, info->quality);
     return 0;
 }
-static int ft_conic_to(FT_Vector* _c, FT_Vector* _to, void* user) 
+static int ft_conic_to(const FT_Vector* _c, const FT_Vector* _to, void* user) 
 {
     gfxdrawinfo_t* info = (gfxdrawinfo_t*)user;
     gfxdrawer_t* draw = info->draw;
@@ -129,14 +134,16 @@ static void glyph_clear(gfxglyph_t*g)
 {
     gfxline_t*line;
     if(g->name) {
-       free(g->name); g->name = 0;
+       free((void*)g->name); g->name = 0;
     }
     gfxline_free(g->line);g->line = 0;
 }
 
 static int errorno = 0;
 
-gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
+//#define DEBUG 1
+
+gfxfont_t* gfxfont_load(char*id, char*filename, unsigned int flags, double quality)
 {
     FT_Face face;
     FT_Error error;
@@ -147,7 +154,6 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
     int t;
     int*glyph2glyph = 0;
     int*glyph2unicode = 0;
-    FT_Size size;
     int max_unicode = 0;
     int charmap = -1;
     int isunicode = 1;
@@ -162,17 +168,20 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
     }
     error = FT_New_Face(ftlibrary, filename, 0, &face);
     FT_Set_Pixel_Sizes (face, 16*loadfont_scale, 16*loadfont_scale);
+#ifdef DEBUG
+    printf("gfxfont_load(%s, %s, %f)\n", id, filename, quality);
+#endif
 
     if(error) {
        fprintf(stderr, "Couldn't load file %s- not a TTF file? (error=%02x)\n", filename, error);
        return 0;
     }
     if(face->num_glyphs <= 0) {
-       fprintf(stderr, "File %s contains %d glyphs\n", face->num_glyphs);
+       fprintf(stderr, "File %s contains %d glyphs\n", filename, (int)face->num_glyphs);
        return 0;
     }
 
-    font = rfx_calloc(sizeof(gfxfont_t));
+    font = (gfxfont_t*)rfx_calloc(sizeof(gfxfont_t));
     //font->style =  ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0) |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0);
     //font->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin;
     //font->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax;
@@ -181,9 +190,9 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
     font->max_unicode = 0;
     font->id = strdup(id);
     
-    font->glyphs = rfx_calloc(face->num_glyphs*sizeof(gfxglyph_t));
-    glyph2unicode = rfx_calloc(face->num_glyphs*sizeof(int));
-    glyph2glyph = rfx_calloc(face->num_glyphs*sizeof(int));
+    font->glyphs = (gfxglyph_t*)rfx_calloc(face->num_glyphs*sizeof(gfxglyph_t));
+    glyph2unicode = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
+    glyph2glyph = (int*)rfx_calloc(face->num_glyphs*sizeof(int));
 
     if(FT_HAS_GLYPH_NAMES(face)) {
        //font->glyphnames = rfx_calloc(face->num_glyphs*sizeof(char*));
@@ -191,9 +200,22 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
 
     fontname = FT_Get_Postscript_Name(face);
 
+#ifdef DEBUG
+    for(t=0;t<face->num_charmaps;t++) {
+        printf("possible encoding: %c%c%c%c (%d of %d)\n", 
+                (face->charmaps[t]->encoding >> 24)&255,
+                (face->charmaps[t]->encoding >> 16)&255,
+                (face->charmaps[t]->encoding >> 8)&255,
+                (face->charmaps[t]->encoding >> 0)&255,
+                t+1, face->num_charmaps
+                );
+    }
+#endif
+
     while(1) 
     {
        charcode = FT_Get_First_Char(face, &gindex);
+
        while(gindex != 0)
        {
            if(gindex >= 0 && gindex<face->num_glyphs) {
@@ -207,13 +229,31 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
            charcode = FT_Get_Next_Char(face, charcode, &gindex);
        }
 
+#ifdef DEBUG
+        if(face->charmap) {
+            printf("ENCODING: %c%c%c%c (%d of %d) max_unicode=%d\n", 
+                    (face->charmap->encoding >> 24)&255,
+                    (face->charmap->encoding >> 16)&255,
+                    (face->charmap->encoding >> 8)&255,
+                    (face->charmap->encoding >> 0)&255,
+                    charmap, face->num_charmaps, font->max_unicode
+                    );
+        } else {
+            printf("ENCODING: NONE (%d of %d) max_unicode=%d\n",
+                    charmap, face->num_charmaps, font->max_unicode
+                    );
+        }
+#endif
+
        /* if we didn't find a single encoding character, try
           the font's charmaps instead. That usually means that
           the encoding is no longer unicode. 
           TODO: find a way to convert the encoding to unicode
         */
-       if(font->max_unicode == 0 && charmap < face->num_charmaps-1 && 
-               face->charmaps[charmap+1]->encoding != 0x41444243 /* custom */)
+       if(font->max_unicode == 0 && charmap < face->num_charmaps-1
+               && face->charmaps[charmap+1]->encoding != 0x41444243 /* adbc, custom */
+               && face->charmaps[charmap+1]->encoding != 0x61726d6e    /* armn */
+                )
                {
            charmap++;
            FT_Set_Charmap(face, face->charmaps[charmap]);
@@ -221,14 +261,13 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
        } else
            break;
     }
-
     /* TODO: if isunicode is 1, we now need to permutate the character
              order so that each character is at it's encoding position */
 
     if(full_unicode)
        font->max_unicode = 65535;
     
-    font->unicode2glyph = rfx_calloc(font->max_unicode*sizeof(int));
+    font->unicode2glyph = (int*)rfx_calloc(font->max_unicode*sizeof(int));
     
     for(t=0;t<font->max_unicode;t++) {
        int g = FT_Get_Char_Index(face, t);
@@ -236,6 +275,9 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
            g = -1;
        font->unicode2glyph[t] = g;
        if(g>=0) {
+#ifdef DEBUG
+           printf("u%d ->%d\n", t, g);
+#endif
            max_unicode = t+1;
            if(!glyph2unicode[g]) {
                glyph2unicode[g] = t;
@@ -260,12 +302,9 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
 
     for(t=0; t < face->num_glyphs; t++) {
        FT_Glyph glyph;
-       FT_BBox bbox;
-       FT_Matrix matrix;
        char name[128];
        gfxdrawer_t draw;
        gfxdrawinfo_t info;
-       int ret;
        char hasname = 0;
        int omit = 0;
        name[0]=0;
@@ -298,9 +337,9 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
            error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP);
            if(error) {
                if(hasname)
-                   fprintf(stderr, "Warning: glyph %d/%d (unicode %d, name %s) has return code %d\n", t, face->num_glyphs, glyph2unicode[t], name, error);
+                   fprintf(stderr, "Warning: glyph %d/%d (unicode %d, name %s) has return code %d\n", t, (int)face->num_glyphs, glyph2unicode[t], name, error);
                else
-                   fprintf(stderr, "Warning: glyph %d/%d (unicode %d) has return code %d\n", t, face->num_glyphs, glyph2unicode[t], error);
+                   fprintf(stderr, "Warning: glyph %d/%d (unicode %d) has return code %d\n", t, (int)face->num_glyphs, glyph2unicode[t], error);
                omit = 2;
 
 #if 0
@@ -323,7 +362,7 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
        if(!omit) {
            error = FT_Get_Glyph(face->glyph, &glyph);
            if(error) {
-               fprintf(stderr, "Couldn't get glyph %d/%d, error:%d\n", t, face->num_glyphs, error);
+               fprintf(stderr, "Couldn't get glyph %d/%d, error:%d\n", t, (int)face->num_glyphs, error);
                omit = 3;
            }
        }
@@ -344,7 +383,7 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
                FT_Done_Glyph(glyph);
                omit = 4;
            } else {
-               font->glyphs[font->num_glyphs].advance = (glyph->advance.x*20)/65536;
+               font->glyphs[font->num_glyphs].advance = glyph->advance.x/65536;
                font->glyphs[font->num_glyphs].line = (gfxline_t*)draw.result(&draw);
            }
            l = font->glyphs[font->num_glyphs].line;
@@ -367,7 +406,7 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
                    */
                font->glyphs[font->num_glyphs].unicode = 0;
                if(font->glyphs[font->num_glyphs].name) {
-                   free(font->glyphs[font->num_glyphs].name);
+                   free((void*)font->glyphs[font->num_glyphs].name);
                    font->glyphs[font->num_glyphs].name = 0;
                }
                FT_Done_Glyph(glyph);
@@ -404,7 +443,7 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
           so that the encoding equals the char position, and
           remove the unicode table */
        int t;
-       gfxglyph_t*newglyphs = rfx_calloc(font->max_unicode*sizeof(gfxglyph_t));
+       gfxglyph_t*newglyphs = (gfxglyph_t*)rfx_calloc(font->max_unicode*sizeof(gfxglyph_t));
 
        for(t=0;t<max_unicode;t++) {
            int c = font->unicode2glyph[t];
@@ -453,9 +492,10 @@ gfxfont_t* gfxfont_load(char*id, char*filename, double quality)
 }
 #else
 
-gfxfont_t* gfxfont_load(char*filename)
+gfxfont_t* gfxfont_load(char*id, char*filename, unsigned int flags, double quality)
 {
     fprintf(stderr, "No freetype support compiled in! Not able to load %s\n", filename);
+    return 0;
 }
 
 #endif
@@ -473,6 +513,223 @@ void gfxfont_free(gfxfont_t*font)
     if(font->unicode2glyph) {
        free(font->unicode2glyph);font->unicode2glyph = 0;
     }
+    if(font->id) {
+       free((void*)font->id);font->id=0;
+    }
+    if(font->kerning) {
+       free(font->kerning);font->kerning=0;
+    }
+
     free(font);
 }
 
+static inline int invalid_unicode(int u)
+{
+    return (u<32 || (u>=0xe000 && u<0xf900));
+}
+void gfxfont_fix_unicode(gfxfont_t*font)
+{
+    int t;
+
+    /* find the current maximum unicode2glyph */
+    int max = 0;
+    for(t=0;t<font->num_glyphs;t++) {
+       int u = font->glyphs[t].unicode;
+       if(u > max)
+           max = u;
+    }
+    char*used = rfx_calloc(max+1);
+
+    /* now, remap all duplicates (and invalid characters) and
+       calculate the new maximum */
+    int remap_pos=0;
+    max = 0;
+    for(t=0;t<font->num_glyphs;t++) {
+       int u = font->glyphs[t].unicode;
+       if(u>=0) {
+           if(used[u] || invalid_unicode(u)) {
+               u = font->glyphs[t].unicode = 0xe000 + remap_pos++;
+           } else {
+               used[u] = 1;
+           }
+       }
+       if(u > max)
+           max = u;
+    }
+    free(used);
+    
+    if(!font->unicode2glyph) {
+       /* (re)generate unicode2glyph-to-glyph mapping table by reverse mapping
+          the glyph unicode2glyph's indexes into the mapping table. For collisions,
+          we prefer the smaller unicode2glyph value.*/
+       font->max_unicode = max+1;
+       font->unicode2glyph = malloc(sizeof(font->unicode2glyph[0])*(font->max_unicode));
+       memset(font->unicode2glyph, -1, sizeof(font->unicode2glyph[0])*(font->max_unicode));
+       
+       for(t=0;t<font->num_glyphs;t++) {
+           int u = font->glyphs[t].unicode;
+           if(u>=0) {
+               assert(font->unicode2glyph[u]<0); // we took care of duplicates, right?
+               assert(u<font->max_unicode);
+               font->unicode2glyph[u] = t;
+           }
+       }
+    } else {
+       /* add the new glyph indexes (most probably, that's only the remapped values
+          at 0xe000) to the unicode2glyph table. Notice: Unlike glyph2unicode, we don't
+          care about collisions in the unicode2glyph table */
+       int new_max_unicode = max+1;
+       if(font->max_unicode < new_max_unicode) {
+           font->unicode2glyph = rfx_realloc(font->unicode2glyph, sizeof(font->unicode2glyph[0])*(font->max_unicode));
+           memset(font->unicode2glyph+font->max_unicode, -1, sizeof(font->unicode2glyph[0])*(new_max_unicode - font->max_unicode));
+       }
+       for(t=0;t<font->num_glyphs;t++) {
+           int u = font->glyphs[t].unicode;
+           if(u>=0 && font->unicode2glyph[u]<0) {
+               font->unicode2glyph[u] = t;
+           }
+       }
+       font->max_unicode = new_max_unicode;
+    }
+}
+
+ttf_t* gfxfont_to_ttf(gfxfont_t*font)
+{
+    ttf_t*ttf = ttf_new();
+    int num_glyphs = font->num_glyphs;
+    int offset = 0;
+    int t;
+    char has_nondef_glyph = 
+       font->num_glyphs && font->glyphs[0].unicode==-1 && 
+       (!font->glyphs[0].line || !font->glyphs[0].line->next);
+
+    if(!has_nondef_glyph) {
+       /* insert a new .nondef glyph at the start of the font */
+       offset++;
+       num_glyphs++;
+    }
+    ttf->num_glyphs = num_glyphs;
+    ttf->glyphs = rfx_calloc(num_glyphs*sizeof(ttfglyph_t));
+    double scale = 1.0;
+    int max_unicode = font->max_unicode;
+    int remap_pos=0;
+    for(t=0;t<font->num_glyphs;t++) {
+       gfxglyph_t*src = &font->glyphs[t];
+       ttfglyph_t*dest = &ttf->glyphs[t+offset];
+       gfxline_t*line = src->line;
+       int count = 0;
+       while(line) {
+           count++;
+           if(line->type == gfx_splineTo) 
+               count++;
+           line=line->next;
+       }
+       dest->num_points = count;
+       dest->points = rfx_calloc(count*sizeof(ttfpoint_t));
+       count = 0;
+       line = src->line;
+       while(line) {
+           if(line->type == gfx_splineTo) {
+               dest->points[count].x = line->sx*scale;
+               dest->points[count].y = line->sy*scale;
+               count++;
+           }
+           dest->points[count].x = line->x*scale;
+           dest->points[count].y = line->y*scale;
+           dest->points[count].flags |= GLYPH_ON_CURVE;
+           if(line->type == gfx_moveTo) {
+               dest->points[count].flags |= GLYPH_CONTOUR_START;
+               if(count)
+                   dest->points[count-1].flags |= GLYPH_CONTOUR_END;
+           }
+           count++;
+           line=line->next;
+       }
+       if(count)
+           dest->points[count-1].flags |= GLYPH_CONTOUR_END;
+
+       /* compute bounding box */
+       int s;
+       if(count) {
+           dest->xmin = dest->xmax = dest->points[0].x;
+           dest->ymin = dest->ymax = dest->points[0].y;
+           for(s=1;s<count;s++) {
+               if(dest->points[s].x < dest->xmin) 
+                   dest->xmin = dest->points[s].x;
+               if(dest->points[s].y < dest->ymin) 
+                   dest->ymin = dest->points[s].y;
+               if(dest->points[s].x > dest->xmax) 
+                   dest->xmax = dest->points[s].x;
+               if(dest->points[s].y > dest->ymax) 
+                   dest->ymax = dest->points[s].y;
+           }
+       }
+
+       dest->advance = src->advance*scale;
+
+       int u = font->glyphs[t].unicode;
+       if(u > max_unicode)
+           max_unicode = u;
+    }
+    ttf->unicode_size = max_unicode+1;
+    ttf->unicode = rfx_calloc(sizeof(unicode_t)*ttf->unicode_size);
+    
+    if(!font->unicode2glyph) {
+       for(t=0;t<font->num_glyphs;t++) {
+           gfxglyph_t*src = &font->glyphs[t];
+           int u = font->glyphs[t].unicode;
+           if(u<=0)
+               continue;
+           if(u<32) {
+               msg("<warning> gfxfont_to_ttf: glyph %d has an invalid unicode (%d)", t, u);
+               continue;
+           } else if(ttf->unicode[u]) {
+               msg("<warning> gfxfont_to_ttf: glyph %d has a duplicate unicode (%d)", t, u);
+               continue;
+           }
+           if(u<ttf->unicode_size)
+               ttf->unicode[u] = t+offset;
+       }
+    } else {
+       int u;
+       for(u=1;u<font->max_unicode;u++) {
+           int g = font->unicode2glyph[u];
+           if(g>=0 && u<32) {
+               msg("<warning> gfxfont_to_ttf: Font contains an invalid unicode (%d)", u);
+               continue;
+           }
+           if(g>=0 && g<font->num_glyphs && !ttf->unicode[u]) {
+               ttf->unicode[u] = g+offset;
+           }
+       }
+    }
+       
+    ttf->ascent = font->ascent;
+    ttf->descent = -font->descent;
+    ttf->lineGap = 0;
+
+    ttf->full_name = strdup(font->id);
+    ttf->family_name = strdup(font->id);
+    ttf->subfamily_name = strdup(font->id);
+    ttf->postscript_name = strdup(font->id);
+    ttf->version_string = strdup("Version 1.0");
+    ttf->font_uid = strdup(font->id);
+
+    ttf_create_truetype_tables(ttf);
+    return ttf;
+}
+
+void gfxfont_save(gfxfont_t*font, const char*filename)
+{
+    ttf_t*ttf = gfxfont_to_ttf(font);
+    ttf_save(ttf, filename);
+    ttf_destroy(ttf);
+}
+
+void gfxfont_save_eot(gfxfont_t*font, const char*filename)
+{
+    ttf_t*ttf = gfxfont_to_ttf(font);
+    ttf_save_eot(ttf, filename);
+    ttf_destroy(ttf);
+}
+