root/modules/ft_font/ft_font.c

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

DEFINITIONS

This source file includes following definitions.
  1. isBestFontFor
  2. setBestFont
  3. ft_enum_fonts
  4. ft_enum_fonts_dir
  5. ft_rescan_fonts
  6. ft_init_font_engine
  7. ft_shutdown_font_engine
  8. ft_check_face
  9. ft_font_in_cache
  10. ft_set_font
  11. ft_get_font_info
  12. ft_get_glyphs
  13. ft_move_to
  14. ft_line_to
  15. ft_conic_to
  16. ft_cubic_to
  17. ft_load_glyph
  18. ft_load
  19. ft_delete
  20. QueryInterfaces
  21. LoadInterface
  22. ShutdownInterface

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / FreeType font engine module
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <gpac/modules/font.h>
#include <gpac/list.h>
#include <gpac/utf.h>
#include <gpac/tools.h>

#if !defined(__GNUC__)
# if defined(_WIN32_WCE)
#  pragma comment(lib, "freetype")
# elif defined (WIN32)
#  pragma comment(lib, "freetype")
# endif
#endif


#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
/*TrueType tables*/
#include FT_TRUETYPE_TABLES_H

typedef struct
{
        FT_Library library;
        FT_Face active_face;

        GF_List *font_dirs;

        GF_List *loaded_fonts;

        /*default fonts*/
        char *font_serif, *font_sans, *font_fixed, *font_default;
} FTBuilder;

static const char * BEST_FIXED_FONTS[] = {
        "Courier New",
        "Courier",
        "Monaco",
        "Bitstream Vera Monospace",
        "Droid Sans Mono",
        NULL
};

static const char * BEST_SERIF_FONTS[] = {
        "Times New Roman",
        "Bitstream Vera",
        "Times",
        "Droid Serif",
        NULL
};

static const char * BEST_SANS_FONTS[] = {
        "Arial",
        "Tahoma",
        "Verdana",
        "Helvetica",
        "Bitstream Vera Sans",
        "Frutiger",
        "Droid Sans",
        NULL
};

/**
 * Choose the best font in the list of fonts
 */
static Bool isBestFontFor(const char * listOfFonts[], const char * currentBestFont, const char * fontName) {
        u32 i;
        assert( fontName );
        assert( listOfFonts );
        for (i = 0 ; listOfFonts[i]; i++) {
                const char * best = listOfFonts[i];
                if (!stricmp(best, fontName))
                        return GF_TRUE;
                if (currentBestFont && !stricmp(best, currentBestFont))
                        return GF_FALSE;
        }
        /* Nothing has been found, the font is the best if none has been choosen before */
        return currentBestFont == NULL;
}

void setBestFont(const char * listOfFonts[], char ** currentBestFont, const char * fontName) {
        if (isBestFontFor(listOfFonts, *currentBestFont, fontName)) {
                if (*currentBestFont)
                        gf_free(*currentBestFont);
                *currentBestFont = NULL;
        }
        if (! (*currentBestFont)) {
                *currentBestFont = gf_strdup(fontName);
        }
}

static Bool ft_enum_fonts(void *cbck, char *file_name, char *file_path, GF_FileEnumInfo *file_info)
{
        char *szfont;
        FT_Face face;
        u32 num_faces, i;
        GF_FontReader *dr = cbck;
        FTBuilder *ftpriv = dr->udta;

        GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Enumerating font %s (%s)\n", file_name, file_path));

        if (FT_New_Face(ftpriv->library, file_path, 0, & face )) return 0;
        if (!face || !face->family_name) return 0;

        num_faces = (u32) face->num_faces;
        /*locate right font in collection if several*/
        for (i=0; i<num_faces; i++) {

                /*only scan scalable fonts*/
                if (face->face_flags & FT_FACE_FLAG_SCALABLE) {
                        Bool bold, italic;
                        szfont = gf_malloc(sizeof(char)* (strlen(face->family_name)+100));
                        if (!szfont) continue;
                        strcpy(szfont, face->family_name);

                        /*remember first font found which looks like a alphabetical one*/
                        if (!ftpriv->font_default) {
                                u32 gidx;
                                FT_Select_Charmap(face, FT_ENCODING_UNICODE);
                                gidx = FT_Get_Char_Index(face, (u32) 'a');
                                if (gidx) gidx = FT_Get_Char_Index(face, (u32) 'z');
                                if (gidx) gidx = FT_Get_Char_Index(face, (u32) '1');
                                if (gidx) gidx = FT_Get_Char_Index(face, (u32) '@');
                                if (gidx) ftpriv->font_default = gf_strdup(szfont);
                        }

                        bold = italic = 0;

                        if (face->style_name) {
                                char *name = gf_strdup(face->style_name);
                                strupr(name);
                                if (strstr(name, "BOLD")) bold = 1;
                                if (strstr(name, "ITALIC")) italic = 1;
                                /*if font is not regular style, append all styles blindly*/
                                if (!strstr(name, "REGULAR")) {
                                        strcat(szfont, " ");
                                        strcat(szfont, face->style_name);
                                }
                                gf_free(name);
                        } else {
                                if (face->style_flags & FT_STYLE_FLAG_BOLD) bold = 1;
                                if (face->style_flags & FT_STYLE_FLAG_ITALIC) italic = 1;

                                if (bold) strcat(szfont, " Bold");
                                if (italic) strcat(szfont, " Italic");
                        }
                        gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", szfont, file_path);

                        /*try to assign default fixed fonts*/
                        if (!bold && !italic) {
                                strcpy(szfont, face->family_name);
                                strlwr(szfont);

                                if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
                                        setBestFont(BEST_FIXED_FONTS, &(ftpriv->font_fixed), face->family_name);
                                }
                                setBestFont(BEST_SERIF_FONTS, &(ftpriv->font_serif), face->family_name);
                                setBestFont(BEST_SANS_FONTS, &(ftpriv->font_sans), face->family_name);
                        }
                        gf_free(szfont);
                }

                FT_Done_Face(face);
                if (i+1==num_faces) return 0;

                /*load next font in collection*/
                if (FT_New_Face(ftpriv->library, file_path, i+1, & face )) return 0;
                if (!face) return 0;
        }
        return 0;
}

static Bool ft_enum_fonts_dir(void *cbck, char *file_name, char *file_path, GF_FileEnumInfo *file_info)
{
        GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Scanning directory %s (%s)\n", file_name, file_path));
        gf_enum_directory(file_path, 0, ft_enum_fonts, cbck, "ttf;ttc");
        return (gf_enum_directory(file_path, 1, ft_enum_fonts_dir, cbck, NULL)==GF_OK) ? GF_FALSE : GF_TRUE;
}


static void ft_rescan_fonts(GF_FontReader *dr)
{
        u32 i, count;
        GF_Config *cfg = gf_modules_get_config((GF_BaseInterface *)dr);
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;

        GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[FreeType] Rescaning %d font directories\n", gf_list_count(ftpriv->font_dirs) ));

        count = gf_cfg_get_key_count(cfg, "FontEngine");
        for (i=0; i<count; i++) {
                const char *key = gf_cfg_get_key_name(cfg, "FontEngine", i);
                if (!strcmp(key, "FontReader")) continue;
                if (!strcmp(key, "FontDirectory")) continue;
                if (!strcmp(key, "RescanFonts")) continue;
                /*any other persistent options should go here*/

                gf_cfg_set_key(cfg, "FontEngine", key, NULL);
                count--;
                i--;
        }
        gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "RescanFonts", "no");

        if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed);
        ftpriv->font_fixed = NULL;
        if (ftpriv->font_sans) gf_free(ftpriv->font_sans);
        ftpriv->font_sans = NULL;
        if (ftpriv->font_serif) gf_free(ftpriv->font_serif);
        ftpriv->font_serif = NULL;

        if (ftpriv->font_default) gf_free(ftpriv->font_default);
        ftpriv->font_default = NULL;


        count = gf_list_count(ftpriv->font_dirs);
        for (i=0; i<count; i++) {
                char *font_dir = gf_list_get(ftpriv->font_dirs, i);

                if (gf_dir_exists(font_dir)) {
                        gf_enum_directory(font_dir, 0, ft_enum_fonts, dr, "ttf;ttc");
                        gf_enum_directory(font_dir, 1, ft_enum_fonts_dir, dr, NULL);
                }
        }

        if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed);
        ftpriv->font_fixed = NULL;
        if (ftpriv->font_sans) gf_free(ftpriv->font_sans);
        ftpriv->font_sans = NULL;
        if (ftpriv->font_serif) gf_free(ftpriv->font_serif);
        ftpriv->font_serif = NULL;

        /* let's check we have fonts that match our default Bold/Italic/BoldItalic conventions*/
        count = gf_cfg_get_key_count(cfg, "FontEngine");
        for (i=0; i<count; i++) {
                const char *opt;
                char fkey[GF_MAX_PATH];
                const char *key = gf_cfg_get_key_name(cfg, "FontEngine", i);
                opt = gf_cfg_get_key(cfg, "FontEngine", key);
                if (!strchr(opt, '/') && !strchr(opt, '\\')) continue;
                if (!strcmp(key, "FontDirectory")) continue;

                if (strstr(key, "Bold")) continue;
                if (strstr(key, "Italic")) continue;

                strcpy(fkey, key);
                strcat(fkey, " Italic");
                opt = gf_cfg_get_key(cfg, "FontEngine", fkey);
                if (!opt) continue;

                strcpy(fkey, key);
                strcat(fkey, " Bold");
                opt = gf_cfg_get_key(cfg, "FontEngine", fkey);
                if (!opt) continue;

                strcpy(fkey, key);
                strcat(fkey, " Bold Italic");
                opt = gf_cfg_get_key(cfg, "FontEngine", fkey);
                if (!opt) continue;

                strcpy(fkey, key);
                strlwr(fkey);

                /*this font is suited for our case*/
                if (isBestFontFor(BEST_FIXED_FONTS, ftpriv->font_fixed, key) || (!ftpriv->font_fixed && (strstr(fkey, "fixed") || strstr(fkey, "mono")) ) ) {
                        if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed);
                        ftpriv->font_fixed = gf_strdup(key);
                }

                if (isBestFontFor(BEST_SANS_FONTS, ftpriv->font_sans, key) || (!ftpriv->font_sans && strstr(fkey, "sans")) ) {
                        if (ftpriv->font_sans) gf_free(ftpriv->font_sans);
                        ftpriv->font_sans = gf_strdup(key);
                }

                if (isBestFontFor(BEST_SERIF_FONTS, ftpriv->font_serif, key) || (!ftpriv->font_serif && strstr(fkey, "serif")) ) {
                        if (ftpriv->font_serif) gf_free(ftpriv->font_serif);
                        ftpriv->font_serif = gf_strdup(key);
                }
        }

        if (!ftpriv->font_serif) ftpriv->font_serif = gf_strdup(ftpriv->font_default ? ftpriv->font_default : "");
        if (!ftpriv->font_sans) ftpriv->font_sans = gf_strdup(ftpriv->font_default ? ftpriv->font_default : "");
        if (!ftpriv->font_fixed) ftpriv->font_fixed = gf_strdup(ftpriv->font_default ? ftpriv->font_default : "");

        gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed", ftpriv->font_fixed);
        gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif", ftpriv->font_serif);
        gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSans", ftpriv->font_sans);

        GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[FreeType] Font directories scanned\n"));
}



static GF_Err ft_init_font_engine(GF_FontReader *dr)
{
        const char *sOpt;
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;

        sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontDirectory");
        if (!sOpt) return GF_BAD_PARAM;

        /*inits freetype*/
        if (FT_Init_FreeType(&ftpriv->library) ) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[FreeType] Cannot initialize FreeType\n"));
                return GF_IO_ERR;
        }

        while (sOpt) {
                char dir[GF_MAX_PATH];
                char *sep = (char *) strchr(sOpt, ',');
                if (sep) sep[0] = 0;

                strcpy(dir, sOpt);
                while ( (dir[strlen(dir)-1] == '\n') || (dir[strlen(dir)-1] == '\r') )
                        dir[strlen(dir)-1] = 0;

                if (dir[strlen(dir)-1] != GF_PATH_SEPARATOR) {
                        char ext[2];
                        ext[0] = GF_PATH_SEPARATOR;
                        ext[1] = 0;
                        strcat(dir, ext);
                }

                gf_list_add(ftpriv->font_dirs, gf_strdup(dir) );

                if (!sep) break;
                sep[0] = ',';
                sOpt = sep+1;
        }

        sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "RescanFonts");
        if (!sOpt || !strcmp(sOpt, "yes") )
                ft_rescan_fonts(dr);

        if (!ftpriv->font_serif) {
                sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif");
                ftpriv->font_serif = gf_strdup(sOpt ? sOpt : "");
        }

        if (!ftpriv->font_sans) {
                sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSans");
                ftpriv->font_sans = gf_strdup(sOpt ? sOpt : "");
        }

        if (!ftpriv->font_fixed) {
                sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed");
                ftpriv->font_fixed = gf_strdup(sOpt ? sOpt : "");
        }
        GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Init OK - %d font directory (first %s)\n", gf_list_count(ftpriv->font_dirs), gf_list_get(ftpriv->font_dirs, 0) ));

        return GF_OK;
}

static GF_Err ft_shutdown_font_engine(GF_FontReader *dr)
{
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;

        ftpriv->active_face = NULL;
        /*reset loaded fonts*/
        while (gf_list_count(ftpriv->loaded_fonts)) {
                FT_Face face = gf_list_get(ftpriv->loaded_fonts, 0);
                gf_list_rem(ftpriv->loaded_fonts, 0);
                FT_Done_Face(face);
        }

        /*exit FT*/
        if (ftpriv->library) FT_Done_FreeType(ftpriv->library);
        ftpriv->library = NULL;
        return GF_OK;
}


static Bool ft_check_face(FT_Face font, const char *fontName, u32 styles)
{
        u32 ft_style, loc_styles;
        char *name;

        if (fontName && stricmp(font->family_name, fontName)) return 0;
        ft_style = 0;
        if (font->style_name) {
                name = gf_strdup(font->style_name);
                strupr(name);
                if (strstr(name, "BOLD")) ft_style |= GF_FONT_WEIGHT_BOLD;
                if (strstr(name, "ITALIC")) ft_style |= GF_FONT_ITALIC;
                gf_free(name);
        } else {
                if (font->style_flags & FT_STYLE_FLAG_BOLD) ft_style |= GF_FONT_WEIGHT_BOLD;
                if (font->style_flags & FT_STYLE_FLAG_ITALIC) ft_style |= GF_FONT_ITALIC;
        }
        name = gf_strdup(font->family_name);
        strupr(name);
        if (strstr(name, "BOLD")) ft_style |= GF_FONT_WEIGHT_BOLD;
        if (strstr(name, "ITALIC")) ft_style |= GF_FONT_ITALIC;
        gf_free(name);

        loc_styles = styles & GF_FONT_WEIGHT_MASK;
        if (loc_styles>=GF_FONT_WEIGHT_BOLD)
                styles = (styles & 0x00000007) | GF_FONT_WEIGHT_BOLD;
        else
                styles = (styles & 0x00000007);

        if (ft_style==styles)
                return 1;
        return 0;
}

static FT_Face ft_font_in_cache(FTBuilder *ft, const char *fontName, u32 styles)
{
        u32 i=0;
        FT_Face font;

        while ((font = gf_list_enum(ft->loaded_fonts, &i))) {
                if (ft_check_face(font, fontName, styles)) return font;
        }
        return NULL;
}



static GF_Err ft_set_font(GF_FontReader *dr, const char *OrigFontName, u32 styles)
{
        char *fname;
        char *fontName;
        const char *opt;
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;

        fontName = (char *) OrigFontName;
        ftpriv->active_face = NULL;

        if (!fontName || !strlen(fontName) || !stricmp(fontName, "SERIF")) {
                fontName = ftpriv->font_serif;
        }
        else if (!stricmp(fontName, "SANS") || !stricmp(fontName, "sans-serif")) {
                fontName = ftpriv->font_sans;
        }
        else if (!stricmp(fontName, "TYPEWRITER") || !stricmp(fontName, "monospace")) {
                fontName = ftpriv->font_fixed;
        }

        /*first look in loaded fonts*/
        ftpriv->active_face = ft_font_in_cache(ftpriv, fontName, styles);
        if (ftpriv->active_face) return GF_OK;

        /*check cfg file - gf_free(type is slow at loading fonts so we keep the (font name + styles)=fontfile associations
        in the cfg file*/
        if (!fontName || !strlen(fontName)) return GF_NOT_SUPPORTED;
        fname = gf_malloc(sizeof(char) * (strlen(fontName) + 50));

        {
                int checkStyles = (styles & GF_FONT_WEIGHT_BOLD) | (styles & GF_FONT_ITALIC);
checkFont:
                strcpy(fname, fontName);
                if (styles & GF_FONT_WEIGHT_BOLD & checkStyles) strcat(fname, " Bold");
                if (styles & GF_FONT_ITALIC & checkStyles) strcat(fname, " Italic");

                opt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", fname);

                if (opt) {
                        FT_Face face;
                        gf_free(fname);
                        if (FT_New_Face(ftpriv->library, opt, 0, & face )) return GF_IO_ERR;
                        if (!face) return GF_IO_ERR;
                        gf_list_add(ftpriv->loaded_fonts, face);
                        ftpriv->active_face = face;
                        return GF_OK;
                }
                if (checkStyles) {
                        /* If we tried font + bold + italic -> we will try font + [bold | italic]
                           If we tried font + [bold | italic] -> we try font
                             */
                        if (checkStyles == (GF_FONT_WEIGHT_BOLD | GF_FONT_ITALIC))
                                checkStyles = GF_FONT_WEIGHT_BOLD;
                        else if (checkStyles == GF_FONT_WEIGHT_BOLD && (styles & GF_FONT_ITALIC))
                                checkStyles = GF_FONT_ITALIC;
                        else if (checkStyles == GF_FONT_WEIGHT_BOLD || checkStyles == GF_FONT_ITALIC)
                                checkStyles = 0;
                        goto checkFont;
                }
        }

        GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Font '%s' (%s) not found\n", fontName, fname));
        gf_free(fname);
        return GF_NOT_SUPPORTED;
}

static GF_Err ft_get_font_info(GF_FontReader *dr, char **font_name, u32 *em_size, s32 *ascent, s32 *descent, s32 *underline, s32 *line_spacing, s32 *max_advance_h, s32 *max_advance_v)
{
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;
        if (!ftpriv->active_face) return GF_BAD_PARAM;

        *em_size = ftpriv->active_face->units_per_EM;
        *ascent = ftpriv->active_face->ascender;
        *descent = ftpriv->active_face->descender;
        *underline = ftpriv->active_face->underline_position;
        *line_spacing = ftpriv->active_face->height;
        *font_name = gf_strdup(ftpriv->active_face->family_name);
        *max_advance_h = ftpriv->active_face->max_advance_width;
        *max_advance_v = ftpriv->active_face->max_advance_height;
        return GF_OK;
}


static GF_Err ft_get_glyphs(GF_FontReader *dr, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *xml_lang, Bool *is_rtl)
{
        size_t _len;
        u32 len;
        u32 i;
        u16 *conv;
        char *utf8 = (char*) utf_string;
        FTBuilder *ftpriv = (FTBuilder *)dr->udta;

        if (!ftpriv->active_face) return GF_BAD_PARAM;

        /*TODO: glyph substitution / ligature */

        len = utf_string ? (u32) strlen(utf_string) : 0;
        if (!len) {
                *io_glyph_buffer_size = 0;
                return GF_OK;
        }
        if (*io_glyph_buffer_size < len+1) {
                *io_glyph_buffer_size = len+1;
                return GF_BUFFER_TOO_SMALL;
        }
        _len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char **) &utf8);
        if (_len==(size_t)-1) return GF_IO_ERR;
        len = (u32) _len;
        if (utf8) return GF_IO_ERR;

        /*perform bidi relayout*/
        conv = (u16*) glyph_buffer;
        *is_rtl = gf_utf8_reorder_bidi(conv, len);
        /*move 16bit buffer to 32bit*/
        for (i=len; i>0; i--) {
                glyph_buffer[i-1] = (u32) conv[i-1];
        }
        *io_glyph_buffer_size = len;
        return GF_OK;
}





typedef struct
{
        FTBuilder *ftpriv;
        GF_Path *path;
        s32 last_x, last_y;
} ft_outliner;

#if defined(GPAC_IPHONE) || defined(GPAC_ANDROID)
#define FTCST
#else
#define FTCST const
#endif



static int ft_move_to(FTCST FT_Vector *to, void *user)
{
        ft_outliner *ftol = (ft_outliner *)user;
        gf_path_add_move_to(ftol->path, INT2FIX(to->x), INT2FIX(to->y) );
        ftol->last_x = (s32) to->x;
        ftol->last_y = (s32) to->y;
        return 0;
}

static int ft_line_to(FTCST FT_Vector *to, void *user)
{
        ft_outliner *ftol = (ft_outliner *)user;
        if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) {
                gf_path_close(ftol->path);
        } else {
                gf_path_add_line_to(ftol->path, INT2FIX(to->x), INT2FIX(to->y) );
        }
        return 0;
}

static int ft_conic_to(FTCST FT_Vector * control, FTCST FT_Vector *to, void *user)
{
        ft_outliner *ftol = (ft_outliner *)user;
        gf_path_add_quadratic_to(ftol->path, INT2FIX(control->x), INT2FIX(control->y), INT2FIX(to->x), INT2FIX(to->y) );
        if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) gf_path_close(ftol->path);
        return 0;
}

static int ft_cubic_to(FTCST FT_Vector *c1, FTCST FT_Vector *c2, FTCST FT_Vector *to, void *user)
{
        ft_outliner *ftol = (ft_outliner *)user;
        gf_path_add_cubic_to(ftol->path, INT2FIX(c1->x), INT2FIX(c1->y), INT2FIX(c2->x), INT2FIX(c2->y), INT2FIX(to->x), INT2FIX(to->y) );
        if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) gf_path_close(ftol->path);
        return 0;
}


static GF_Glyph *ft_load_glyph(GF_FontReader *dr, u32 glyph_name)
{
        GF_Glyph *glyph;
        u32 glyph_idx;
        FT_BBox bbox;
        FT_OutlineGlyph outline;
        ft_outliner outl;
        FT_Outline_Funcs ft_outl_funcs;

        FTBuilder *ftpriv = (FTBuilder *)dr->udta;
        if (!ftpriv->active_face || !glyph_name) return NULL;

        FT_Select_Charmap(ftpriv->active_face, FT_ENCODING_UNICODE);

        glyph_idx = FT_Get_Char_Index(ftpriv->active_face, glyph_name);
        /*missing glyph*/
        if (!glyph_idx) {
                GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Glyph not found for char %d in font %s (style %s)\n", glyph_name, ftpriv->active_face->family_name, ftpriv->active_face->style_name));
                return NULL;
        }

        /*work in design units*/
        FT_Load_Glyph(ftpriv->active_face, glyph_idx, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);

        FT_Get_Glyph(ftpriv->active_face->glyph, (FT_Glyph*)&outline);

#ifdef FT_GLYPH_FORMAT_OUTLINE
        /*oops not vectorial...*/
        if (outline->root.format != FT_GLYPH_FORMAT_OUTLINE) return NULL;
#endif


        GF_SAFEALLOC(glyph, GF_Glyph);
        if (!glyph) return NULL;
        GF_SAFEALLOC(glyph->path, GF_Path);
        if (!glyph->path) {
                gf_free(glyph);
                return NULL;
        }
        /*setup outliner*/
        ft_outl_funcs.shift = 0;
        ft_outl_funcs.delta = 0;
        ft_outl_funcs.move_to = ft_move_to;
        ft_outl_funcs.line_to = ft_line_to;
        ft_outl_funcs.conic_to = ft_conic_to;
        ft_outl_funcs.cubic_to = ft_cubic_to;
        outl.path = glyph->path;
        outl.ftpriv = ftpriv;

        /*FreeType is marvelous and gives back the right advance on space char !!!*/
        FT_Outline_Decompose(&outline->outline, &ft_outl_funcs, &outl);

        FT_Glyph_Get_CBox((FT_Glyph) outline, ft_glyph_bbox_unscaled, &bbox);

        glyph->ID = glyph_name;
        glyph->utf_name = glyph_name;
        glyph->horiz_advance = (s32) ftpriv->active_face->glyph->metrics.horiAdvance;
        glyph->vert_advance = (s32) ftpriv->active_face->glyph->metrics.vertAdvance;
        /*
                glyph->x = bbox.xMin;
                glyph->y = bbox.yMax;
        */
        glyph->width = (u32) ftpriv->active_face->glyph->metrics.width;
        glyph->height = (u32) ftpriv->active_face->glyph->metrics.height;
        FT_Done_Glyph((FT_Glyph) outline);
        return glyph;
}


static GF_FontReader *ft_load()
{
        GF_FontReader *dr;
        FTBuilder *ftpriv;
        dr = gf_malloc(sizeof(GF_FontReader));
        memset(dr, 0, sizeof(GF_FontReader));
        GF_REGISTER_MODULE_INTERFACE(dr, GF_FONT_READER_INTERFACE, "FreeType Font Reader", "gpac distribution");

        ftpriv = gf_malloc(sizeof(FTBuilder));
        memset(ftpriv, 0, sizeof(FTBuilder));

        ftpriv->font_dirs = gf_list_new();

        ftpriv->loaded_fonts = gf_list_new();

        dr->udta = ftpriv;


        dr->init_font_engine = ft_init_font_engine;
        dr->shutdown_font_engine = ft_shutdown_font_engine;
        dr->set_font = ft_set_font;
        dr->get_font_info = ft_get_font_info;
        dr->get_glyphs = ft_get_glyphs;
        dr->load_glyph = ft_load_glyph;
        return dr;
}


static void ft_delete(GF_BaseInterface *ifce)
{
        GF_FontReader *dr = (GF_FontReader *) ifce;
        FTBuilder *ftpriv = dr->udta;

        while (gf_list_count(ftpriv->font_dirs)) {
                char *font = gf_list_pop_back(ftpriv->font_dirs);
                if (font)
                        gf_free(font);
        }

        gf_list_del(ftpriv->font_dirs);

        if (ftpriv->font_serif) gf_free(ftpriv->font_serif);
        if (ftpriv->font_sans) gf_free(ftpriv->font_sans);
        if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed);
        if (ftpriv->font_default) gf_free(ftpriv->font_default);
        assert(!gf_list_count(ftpriv->loaded_fonts) );

        gf_list_del(ftpriv->loaded_fonts);

        gf_free(dr->udta);
        gf_free(dr);
}

#ifndef GPAC_STANDALONE_RENDER_2D

GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
        static u32 si [] = {
                GF_FONT_READER_INTERFACE,
                0
        };
        return si;
}

GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
        if (InterfaceType == GF_FONT_READER_INTERFACE) return (GF_BaseInterface *)ft_load();
        return NULL;
}

GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
        switch (ifce->InterfaceType) {
        case GF_FONT_READER_INTERFACE:
                ft_delete(ifce);
                break;
        }
}

GPAC_MODULE_STATIC_DECLARATION( ftfont )

#endif


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