fixed bug in unicode relocation
[swftools.git] / lib / gfxfont.c
index ee4b9cb..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*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,11 +154,11 @@ gfxfont_t* gfxfont_load(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;
     int has_had_errors = 0;
+    int num_names = 0;
 
     if(ftlibrary == 0) {
        if(FT_Init_FreeType(&ftlibrary)) {
@@ -161,27 +168,31 @@ gfxfont_t* gfxfont_load(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?\n", filename);
+       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;
     //font->leading = font->layout->ascent + font->layout->descent;
     //font->encoding = FONT_ENCODING_UNICODE;
     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*));
@@ -189,9 +200,22 @@ gfxfont_t* gfxfont_load(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) {
@@ -205,27 +229,45 @@ gfxfont_t* gfxfont_load(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) {
+       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]);
-           //font->encoding = 0;//anything but unicode FIXME
            isunicode = 0;
-       } else 
+       } 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);
@@ -233,6 +275,9 @@ gfxfont_t* gfxfont_load(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;
@@ -243,14 +288,23 @@ gfxfont_t* gfxfont_load(char*filename, double quality)
 
     font->num_glyphs = 0;
 
+
+    for(t=0; t < face->num_glyphs; t++) {
+       if(FT_HAS_GLYPH_NAMES(face)) {
+           char name[128];
+           error = FT_Get_Glyph_Name(face, t, name, 127);
+           if(!error && name[0] && !strstr(name, "notdef")) {
+               num_names++;
+           }
+       }
+    }
+
+
     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;
@@ -260,27 +314,33 @@ gfxfont_t* gfxfont_load(char*filename, double quality)
        font->glyphs[font->num_glyphs].unicode = glyph2unicode[t];
        font->glyphs[font->num_glyphs].name = 0;
 
-       if(FT_HAS_GLYPH_NAMES(face)) {
+       if(FT_HAS_GLYPH_NAMES(face) && (num_names >= face->num_glyphs/10 || num_names > 2)) {
+           name[0] = 0;
            error = FT_Get_Glyph_Name(face, t, name, 127);
            if(!error && name[0] && !strstr(name, "notdef")) {
                font->glyphs[font->num_glyphs].name = strdup(name);
                hasname = 1;
            }
        }
-       if(has_had_errors && (isunicode && !glyph2unicode[t]) && !hasname) {
+
+#if 0 // some cantonese pdfs fail to work if this is activated
+
+       if(has_had_errors && (isunicode && !glyph2unicode[t]) && !hasname && t>=256) {
            /* some freetype versions crash or corrupt memory if we try to load
               characters (without unicode index or name) above 256 for some fonts.
               So skip those characters once the first error occured */
            omit = 1;
        }
+#endif
+
        if(!omit) {
            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);
-               omit = 1;
+                   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
                if(!has_had_errors) {
@@ -302,12 +362,14 @@ gfxfont_t* gfxfont_load(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);
-               omit = 1;
+               fprintf(stderr, "Couldn't get glyph %d/%d, error:%d\n", t, (int)face->num_glyphs, error);
+               omit = 3;
            }
        }
 
        if(!omit) {
+           gfxline_t*l;
+           int ok=0;
            gfxdrawer_target_gfxline(&draw);
            info.draw = &draw;
            info.quality = quality;
@@ -319,16 +381,44 @@ gfxfont_t* gfxfont_load(char*filename, double quality)
                fprintf(stderr, "Couldn't decompose glyph %d\n", t);
                gfxline_free((gfxline_t*)draw.result(&draw));
                FT_Done_Glyph(glyph);
-               omit = 1;
+               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;
+           while(l) {
+               if(l->type != gfx_moveTo) {
+                   ok = 1;
+               }
+               l = l->next;
+           }
+           if(!ok && !name) {
+               gfxline_free(font->glyphs[font->num_glyphs].line);
+               font->glyphs[font->num_glyphs].line = 0;
+               font->glyphs[font->num_glyphs].advance = 0;
+
+               /* Some PDFs created e.g. by InDesign tend to create
+                  fonts with reduced (empty) characters, which still
+                  have unicode indices attached to them.
+                  Remove that information, in order to not confuse
+                  any converter applications.
+                   */
+               font->glyphs[font->num_glyphs].unicode = 0;
+               if(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);
+               omit = 5;
+           }
        }
 
        if(!omit) {
            FT_Done_Glyph(glyph);
+           font->glyphs[font->num_glyphs].unicode = glyph2unicode[t];
        }
+
        glyph2glyph[t] = font->num_glyphs;
        font->num_glyphs++;
     }
@@ -347,13 +437,13 @@ gfxfont_t* gfxfont_load(char*filename, double quality)
 
     FT_Done_Face(face);
     FT_Done_FreeType(ftlibrary);ftlibrary=0;
-  
-    if(!isunicode && font->num_glyphs>0) {
+    if(!isunicode && font->num_glyphs>0 && font->max_unicode) {
        /* if the encoding isn't unicode, remap the font
           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];
@@ -402,9 +492,10 @@ gfxfont_t* gfxfont_load(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
@@ -422,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);
+}
+