/** \file lvfreetypefontman.cpp
    \brief FreeType font manager implementation

    CoolReader Engine


    (c) Vadim Lopatin, 2000-2006
    This source code is distributed under the terms of
    GNU General Public License.

    See LICENSE file for details.

*/

#include "lvfreetypefontman.h"

#if (USE_FREETYPE == 1)

#include "lvfreetypeface.h"
#include "lvfontboldtransform.h"
#include "lvcontainer.h"
#include "crlog.h"

#include FT_LCD_FILTER_H
#include FT_CONFIG_OPTIONS_H

#if (USE_FONTCONFIG == 1)
#include <fontconfig/fontconfig.h>
#endif

#include "crlocaledata.h"
#if USE_LOCALE_DATA==1
#include "fc-lang-data.h"
#endif

inline int myabs(int n) { return n < 0 ? -n : n; }

lString8 familyName(FT_Face face) {
    lString8 faceName(face->family_name);
    if ( face->style_name ) {
        if (faceName == "Arial" && !strcmp(face->style_name, "Narrow"))
            faceName << " " << face->style_name;
        else if (strstr(face->style_name, "ExtraCondensed"))
            faceName << " " << "ExtraCondensed";
        else if (strstr(face->style_name, "SemiCondensed"))
            faceName << " " << "SemiCondensed";
        else if (strstr(face->style_name, "Condensed"))
            faceName << " " << "Condensed";
    }
    return faceName;
}

int getFontWeight(FT_Face face) {
    if (!face)
        return -1;
    int weight = -1;
    bool bold_flag = (face->style_flags & FT_STYLE_FLAG_BOLD) != 0;
    lString32 style32(face->style_name);
    style32 = style32.lowercase();
    if (style32.pos("extrablack") >= 0 || style32.pos("ultrablack") >= 0
          || style32.pos("extra black") >= 0 || style32.pos("ultra black") >= 0)
        weight = 950;
    else if (style32.pos("extrabold") >= 0 || style32.pos("ultrabold") >= 0
          || style32.pos("extra bold") >= 0 || style32.pos("ultra bold") >= 0)
        weight = 800;
    else if (style32.pos("demibold") >= 0 || style32.pos("semibold") >= 0
          || style32.pos("demi bold") >= 0 || style32.pos("semi bold") >= 0)
        weight = 600;
    else if (style32.pos("extralight") >= 0 || style32.pos("ultralight") >= 0
          || style32.pos("extra light") >= 0 || style32.pos("ultra light") >= 0)
        weight = 200;
    else if (style32.pos("demilight") >= 0 || style32.pos("light") >= 0
          || style32.pos("demi light") >= 0)
        weight = 300;
    else if (style32.pos("regular") >= 0 || style32.pos("normal") >= 0 || style32.pos("book") >= 0 || style32.pos("text") >= 0)
        weight = 400;
    else if (style32.pos("thin") >= 0)
        weight = 100;
    else if (style32.pos("medium") >= 0)
        weight = 500;
    else if (style32.pos("bold") >= 0)
        weight = 700;
    else if (style32.pos("black") >= 0 || style32.pos("heavy") >= 0)
        weight = 900;

    if (-1 == weight)
        weight = bold_flag ? 700 : 400;
    else if (weight <= 400 && bold_flag)
        weight = 700;
    return weight;
}

#if USE_LOCALE_DATA==1
LVHashTable<lString8, font_lang_compat>* getSupportedLangs(FT_Face face) {
    LVHashTable<lString8, font_lang_compat>* langs = new LVHashTable<lString8, font_lang_compat>(16);
#define FC_LANG_START_INTERVAL_CODE     0xF0F0FFFF
    bool fullSupport;
    bool partialSupport;
    unsigned int codePoint = 0;
    unsigned int tmp;
    unsigned int first, second;
    bool inRange;
    const struct fc_lang_rec* rec = get_fc_lang_data();
    for (unsigned int l = 0; l < get_fc_lang_data_size(); l++) {
        CRLocaleData loc(rec->lang_code);
        if (loc.isValid()) {
            // Check compatibility
            codePoint = 0;
            second = 0;
            inRange = false;
            FT_UInt glyphIndex;
            partialSupport = false;
            fullSupport = true;
            for (int i = 0;;) {
                // get next codePoint
                if (inRange && codePoint < second) {
                    codePoint++;
                } else {
                    if (i >= rec->char_set_sz)
                        break;
                    tmp = rec->char_set[i];
                    if (FC_LANG_START_INTERVAL_CODE == tmp) {       // code of start interval
                        if (i + 2 < rec->char_set_sz) {
                            i++;
                            first = rec->char_set[i];
                            i++;
                            second = rec->char_set[i];
                            inRange = true;
                            codePoint = first;
                            i++;
                        } else {
                            // broken language char set
                            fullSupport = false;
                            break;
                        }
                    } else {
                        codePoint = tmp;
                        inRange = false;
                        i++;
                    }
                }
                // check codePoint in this font
                glyphIndex = FT_Get_Char_Index(face, codePoint);
                if (0 == glyphIndex) {
                    fullSupport = false;
                } else {
                    partialSupport = true;
                }
            }
            if (fullSupport)
                langs->set(loc.langTag(), font_lang_compat_full);
            else if (partialSupport)
                langs->set(loc.langTag(), font_lang_compat_partial);
        } else {
            CRLog::warn("getSupportedLangs(): invalid lang tag: %s", rec->lang_code);
        }
        // to next language
        rec++;
    }
    langs->compact();
    return langs;
}
#endif

lUInt32 LVFreeTypeFontManager::GetFontListHash(int documentId) {
    FONT_MAN_GUARD
    return _cache.GetFontListHash(documentId) * 75 + _fallbackFontFaces.getHash();
}

bool LVFreeTypeFontManager::SetFallbackFontFaces(lString8 facesStr)
{
    FONT_MAN_GUARD
    lString8Collection faces = lString8Collection(facesStr, lString8(";"));
    int count = 0;
    if (faces != _fallbackFontFaces) {
        _fallbackFontFaces.clear();
        for (int i = 0; i < faces.length(); i++) {
            lString8 face = faces[i];
            face.trim();
            CRLog::trace("Looking for fallback font %s", face.c_str());
            LVFontCacheItem *item = _cache.findFallback(face, -1);
            if (item) {
                bool exist = false;
                for (int j = 0; j < _fallbackFontFaces.length(); j++)
                    if (_fallbackFontFaces[j] == face) {
                        exist = true;
                        break;
                    }
                if (!exist) {
                    _fallbackFontFaces.add(face);
                    count++;
                    if (8*sizeof(lUInt32) == count) {
                        CRLog::warn("Too many fallback fonts specified, skipping.");
                        break;
                    }
                }
            }
        }
        _cache.clearFallbackFonts();
        // Somehow, with Fedra Serif (only!), changing the fallback font does
        // not prevent glyphs from previous fallback font to be re-used...
        // So let's clear glyphs caches too.
        gc();
        clearGlyphCache();
    }
    return count > 0;
}

lString8 LVFreeTypeFontManager::GetFallbackFontFace(int index) {
    if (index >= 0 && index < _fallbackFontFaces.length())
        return _fallbackFontFaces[index];
    return lString8::empty_str;
}

lString8 LVFreeTypeFontManager::GetFallbackFontFaces() {
    lString8 faces;
    if (_fallbackFontFaces.length() > 0) {
        for (int i = 0; i < _fallbackFontFaces.length() - 1; i++) {
            faces.append(_fallbackFontFaces[i]);
            faces.append("; ");
        }
        faces.append(_fallbackFontFaces[_fallbackFontFaces.length() - 1]);
    }
    return faces;
}

int LVFreeTypeFontManager::GetFallbackFontCount()
{
    return _fallbackFontFaces.length();
}

LVFontRef LVFreeTypeFontManager::GetFallbackFont(int size, int index) {
    FONT_MAN_GUARD
    if ( _fallbackFontFaces.empty() )
        return LVFontRef();
    if ( index < 0 || index >= _fallbackFontFaces.length() )
        return LVFontRef();
    // reduce number of possible distinct sizes for fallback font
    if (size > 40)
        size &= 0xFFF8;
    else if (size > 28)
        size &= 0xFFFC;
    else if (size > 16)
        size &= 0xFFFE;
    LVFontCacheItem *item = _cache.findFallback(_fallbackFontFaces[index], size);
    lUInt32 fallbackMask = 1 << index;
    if (!item->getFont().isNull()) {
        item->getFont()->setFallbackMask(fallbackMask);
        return item->getFont();
    }
    LVFontRef fontRef = GetFont(size, 400, false, css_ff_sans_serif, _fallbackFontFaces[index], 0, -1, false);
    if (!fontRef.isNull())
        fontRef->setFallbackMask(fallbackMask);
    return fontRef;
}

LVFontRef LVFreeTypeFontManager::GetFallbackFont(int size, int weight, bool italic, int index)
{
    FONT_MAN_GUARD
    if ( _fallbackFontFaces.empty() )
        return LVFontRef();
    if ( index < 0 || index >= _fallbackFontFaces.length() )
        return LVFontRef();
    // reduce number of possible distinct sizes for fallback font
    if ( size>40 )
        size &= 0xFFF8;
    else if ( size>28 )
        size &= 0xFFFC;
    else if ( size>16 )
        size &= 0xFFFE;
    // We don't use/extend findFallback(), which was made to work
    // assuming the fallback font is a standalone regular font
    // without any bold/italic sibling.
    // GetFont() works just as fine when we need specified weigh and italic.
    LVFontRef fontRef = GetFont(size, weight, italic, css_ff_sans_serif, _fallbackFontFaces[index], 0, -1, false);
    if (!fontRef.isNull())
        fontRef->setFallbackMask(1 << index);
    return fontRef;
}

bool LVFreeTypeFontManager::isBitmapModeForSize(int size) {
    bool isBitmap = false;
    switch (_antialiasMode) {
        case font_aa_none:
            isBitmap = true;
            break;
        case font_aa_big:
            isBitmap = size < 20 ? true : false;
            break;
        case font_aa_all:
        default:
            isBitmap = false;
            break;
    }
    return isBitmap;
}

void LVFreeTypeFontManager::SetAntialiasMode(font_antialiasing_t mode) {
    _antialiasMode = mode;
    int error;
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    // ClearType-style LCD rendering
    switch (mode)
    {
    case font_aa_lcd_rgb:
    case font_aa_lcd_bgr:
    case font_aa_lcd_v_rgb:
    case font_aa_lcd_v_bgr:
        error = FT_Library_SetLcdFilter(_library, FT_LCD_FILTER_DEFAULT);
        if (FT_Err_Ok != error)
            CRLog::debug("FT_Library_SetLcdFilter() failed, error=%d", error);
        break;
    case font_aa_lcd_pentile:
    case font_aa_lcd_pentile_m:
    case font_aa_lcd_v_pentile:
    case font_aa_lcd_v_pentile_m:
        CRLog::warn("Pentile unavailable with ClearType-style LCD rendering");
        CRLog::warn("Recompile FreeType without FT_CONFIG_OPTION_SUBPIXEL_RENDERING");
        break;
    default:
        error = FT_Library_SetLcdFilter(_library, FT_LCD_FILTER_NONE);
        if (FT_Err_Ok != error)
            CRLog::debug("FT_Library_SetLcdFilter() failed, error=%d", error);
        break;
    }
#else
    // Harmony LCD rendering
    switch (mode) {
        case font_aa_lcd_rgb:
        case font_aa_lcd_v_rgb: {
            // {{-⅓, 0}, {0, 0}, {⅓, 0}}
            FT_Vector sub[3] = {{-21, 0}, {0, 0}, { 21, 0}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
            break;
        }
        case font_aa_lcd_bgr:
        case font_aa_lcd_v_bgr: {
            // {{⅓, 0}, {0, 0}, {-⅓, 0}}
            FT_Vector sub[3] = {{21, 0}, {0, 0}, {-21, 0}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
            break;
        }
        case font_aa_lcd_pentile: {
            // {{-⅙, ¼}, {-⅙, -¼}, {⅓, 0}}
            FT_Vector sub[3] = {{-11, 16}, {-11, -16}, { 22, 0}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
            break;
        }
        case font_aa_lcd_pentile_m: {
            // TODO: test & fix this
            FT_Vector sub[3] = {{ -22, 0}, {11, 16}, {11, -16}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
                break;
            }
        case font_aa_lcd_v_pentile: {
            // TODO: test & fix this
            FT_Vector sub[3] = {{ 16, -11}, {-16, -11}, {0, 22}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
            break;
        }
        case font_aa_lcd_v_pentile_m: {
            // TODO: test & fix this
            FT_Vector sub[3] = {{ 22, 0}, {-11, -16}, {-11, 16}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
            break;
        }
        default: {
            FT_Vector sub[3] = {{0, 0},
                                {0, 0},
                                {0, 0}};
            error = FT_Library_SetLcdGeometry(_library, sub);
            if (FT_Err_Ok != error)
                CRLog::debug("FT_Library_SetLcdGeometry() failed, error=%d", error);
        }
    }
#endif
    gc();
    clearGlyphCache();
    FONT_MAN_GUARD
    LVPtrVector<LVFontCacheItem> *fonts = _cache.getInstances();
    for (int i = 0; i < fonts->length(); i++) {
        LVFontRef font = fonts->get(i)->getFont();
        font->SetAntialiasMode(_antialiasMode);
        font->setBitmapMode(isBitmapModeForSize(font->getHeight()));
    }
}

void LVFreeTypeFontManager::SetHintingMode(hinting_mode_t mode) {
    FONT_MAN_GUARD
    CRLog::debug("Hinting mode is changed: %d", (int) mode);
    _hintingMode = mode;
    gc();
    clearGlyphCache();
    LVPtrVector<LVFontCacheItem> *fonts = _cache.getInstances();
    for (int i = 0; i < fonts->length(); i++) {
        fonts->get(i)->getFont()->setHintingMode(mode);
    }
}

void LVFreeTypeFontManager::SetKerning(bool kerningEnabled)
{
    FONT_MAN_GUARD
    CRLog::debug("Kerning mode is changed: %d", (int) kerningEnabled);
    _allowKerning = kerningEnabled;
    gc();
    clearGlyphCache();
    LVPtrVector<LVFontCacheItem> *fonts = _cache.getInstances();
    for (int i = 0; i < fonts->length(); i++) {
        fonts->get(i)->getFont()->setKerning(kerningEnabled);
    }
}

void LVFreeTypeFontManager::SetShapingMode( shaping_mode_t mode )
{
    FONT_MAN_GUARD
    CRLog::debug("Shaping mode is changed: %d", (int) mode);
    _shapingMode = mode;
    gc();
    clearGlyphCache();
    LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances();
    for ( int i=0; i<fonts->length(); i++ ) {
        fonts->get(i)->getFont()->setShapingMode( mode );
    }
}

void LVFreeTypeFontManager::clearGlyphCache() {
    FONT_MAN_GUARD
    _globalCache.clear();
    #if USE_HARFBUZZ==1
    // needs to clear each font _glyph_cache2 (for Gamma change, which
    // does not call any individual font method)
    LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances();
    for ( int i=0; i<fonts->length(); i++ ) {
        fonts->get(i)->getFont()->clearCache();
    }
    #endif
}

bool LVFreeTypeFontManager::initSystemFonts() {
#if (DEBUG_FONT_SYNTHESIS == 1)
    fontMan->RegisterFont(lString8("/usr/share/fonts/liberation/LiberationSans-Regular.ttf"));
    CRLog::debug("fonts:");
    LVFontRef fnt4 = dumpFontRef( fontMan->GetFont(24, 200, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    LVFontRef fnt1 = dumpFontRef( fontMan->GetFont(18, 200, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    LVFontRef fnt2 = dumpFontRef( fontMan->GetFont(20, 400, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    LVFontRef fnt3 = dumpFontRef( fontMan->GetFont(22, 600, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    LVFontRef fnt5 = dumpFontRef( fontMan->GetFont(26, 400, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    LVFontRef fnt6 = dumpFontRef( fontMan->GetFont(28, 600, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) );
    CRLog::debug("end of font testing");
#elif (USE_FONTCONFIG == 1)
    {
        CRLog::info("Reading list of system fonts using FONTCONFIG");
        lString32Collection fonts;
        
        int facesFound = 0;
        
        FcFontSet *fontset;
        
        FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_WEIGHT, FC_FAMILY,
                                           FC_SLANT, FC_SPACING, FC_INDEX,
                                           FC_STYLE, FC_SCALABLE,
#ifdef FC_COLOR
                                           FC_COLOR,
#endif
                                           NULL);
        FcPattern *pat = FcPatternCreate();
        //FcBool b = 1;
        FcPatternAddBool(pat, FC_SCALABLE, 1);
        
        fontset = FcFontList(NULL, pat, os);
        
        FcPatternDestroy(pat);
        FcObjectSetDestroy(os);
        
        // load fonts from file
        CRLog::debug("FONTCONFIG: %d font files found", fontset->nfont);
        for(int i = 0; i < fontset->nfont; i++) {
            FcChar8 *s=(FcChar8*)"";
            FcChar8 *family=(FcChar8*)"";
            FcChar8 *style=(FcChar8*)"";
            //FcBool b;
            FcResult res;

            //FC_SCALABLE
            //res = FcPatternGetBool( fontset->fonts[i], FC_OUTLINE, 0, (FcBool*)&b);
            //if(res != FcResultMatch)
            //    continue;
            //if ( !b )
            //    continue; // skip non-scalable fonts
            res = FcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8 **)&s);
            if(res != FcResultMatch) {
                continue;
            }
            lString8 fn( (const char *)s );
            lString32 fn32( fn.c_str() );
            fn32.lowercase();
            if (!fn32.endsWith(".ttf") && !fn32.endsWith(".odf") && !fn32.endsWith(".otf") && !fn32.endsWith(".pfb") && !fn32.endsWith(".pfa") && !fn32.endsWith(".ttc") ) {
                continue;
            }
            int weight = FC_WEIGHT_REGULAR;
            res = FcPatternGetInteger(fontset->fonts[i], FC_WEIGHT, 0, &weight);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_WEIGHT for %s", s);
                //continue;
            }
            switch ( weight ) {
            case FC_WEIGHT_THIN:          //    0
                weight = 100;
                break;
            case FC_WEIGHT_EXTRALIGHT:    //    40
            case 45:                      // TODO: find what is it...
                //case FC_WEIGHT_ULTRALIGHT        FC_WEIGHT_EXTRALIGHT
                weight = 200;
                break;
            case FC_WEIGHT_LIGHT:         //    50
#ifdef FC_WEIGHT_DEMILIGHT
            case FC_WEIGHT_DEMILIGHT:     //    55
#endif
                weight = 300;
                break;
            case FC_WEIGHT_BOOK:          //    75
            case FC_WEIGHT_REGULAR:       //    80
                //case FC_WEIGHT_NORMAL:            FC_WEIGHT_REGULAR
                weight = 400;
                break;
            case FC_WEIGHT_MEDIUM:        //    100
                weight = 500;
                break;
            case FC_WEIGHT_DEMIBOLD:      //    180
                //case FC_WEIGHT_SEMIBOLD:          FC_WEIGHT_DEMIBOLD
                weight = 600;
                break;
            case FC_WEIGHT_BOLD:          //    200
                weight = 700;
                break;
            case FC_WEIGHT_EXTRABOLD:     //    205
                //case FC_WEIGHT_ULTRABOLD:         FC_WEIGHT_EXTRABOLD
                weight = 800;
                break;
            case FC_WEIGHT_BLACK:         //    210
                //case FC_WEIGHT_HEAVY:             FC_WEIGHT_BLACK
                weight = 900;
                break;
#ifdef FC_WEIGHT_EXTRABLACK
            case FC_WEIGHT_EXTRABLACK:    //    215
                //case FC_WEIGHT_ULTRABLACK:        FC_WEIGHT_EXTRABLACK
                weight = 950;
                break;
#endif
            default:
                weight = 400;
                break;
            }
            FcBool scalable = FcFalse;
            res = FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_SCALABLE for %s", s);
            }
            int index = 0;
            res = FcPatternGetInteger(fontset->fonts[i], FC_INDEX, 0, &index);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_INDEX for %s", s);
                //continue;
            }
            res = FcPatternGetString(fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_FAMILY for %s", s);
                continue;
            }
            res = FcPatternGetString(fontset->fonts[i], FC_STYLE, 0, (FcChar8 **)&style);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_STYLE for %s", s);
                style = (FcChar8*)"";
                //continue;
            }
            int slant = FC_SLANT_ROMAN;
            res = FcPatternGetInteger(fontset->fonts[i], FC_SLANT, 0, &slant);
            if(res != FcResultMatch) {
                CRLog::debug("no FC_SLANT for %s", s);
                //continue;
            }
            int spacing = 0;
            res = FcPatternGetInteger(fontset->fonts[i], FC_SPACING, 0, &spacing);
            if(res != FcResultMatch) {
                //CRLog::debug("no FC_SPACING for %s", s);
                //continue;
            }
            //                int cr_weight;
            //                switch(weight) {
            //                    case FC_WEIGHT_LIGHT: cr_weight = 200; break;
            //                    case FC_WEIGHT_MEDIUM: cr_weight = 300; break;
            //                    case FC_WEIGHT_DEMIBOLD: cr_weight = 500; break;
            //                    case FC_WEIGHT_BOLD: cr_weight = 700; break;
            //                    case FC_WEIGHT_BLACK: cr_weight = 800; break;
            //                    default: cr_weight=300; break;
            //                }
            css_font_family_t fontFamily = css_ff_sans_serif;
            lString32 face32((const char *)family);
            face32.lowercase();
            if ( spacing==FC_MONO )
                fontFamily = css_ff_monospace;
            else if (face32.pos("sans") >= 0)
                fontFamily = css_ff_sans_serif;
            else if (face32.pos("serif") >= 0)
                fontFamily = css_ff_serif;
            
            //css_ff_inherit,
            //css_ff_serif,
            //css_ff_sans_serif,
            //css_ff_cursive,
            //css_ff_fantasy,
            //css_ff_monospace,
            bool italic = (slant!=FC_SLANT_ROMAN);
            
            lString8 face((const char*)family);
            lString8 style8((const char*)style);
            style8.lowercase();
            if (style8.pos("extracondensed") >= 0)
                face << " ExtraCondensed";
            else if (style8.pos("semicondensed") >= 0)
                face << " SemiCondensed";
            else if (style8.pos("condensed") >= 0)
                face << " Condensed";
            
            LVFontDef def(
                        lString8((const char*)s),
                        -1, // height==-1 for scalable fonts
                        weight,
                        italic,
                        -1, // OpenType features = -1 for not yet instantiated fonts
                        fontFamily,
                        face,
                        index
                        );
            CRLog::debug("FONTCONFIG: Font family:%s style:%s weight:%d slant:%d spacing:%d file:%s", family, style, weight, slant, spacing, s);
            if ( _cache.findDuplicate( &def ) ) {
                CRLog::debug("is duplicate, skipping");
                continue;
            }
            _cache.update( &def, LVFontRef(NULL) );
            
            if ( scalable != FcFalse && !def.getItalic() ) {
                LVFontDef newDef( def );
                newDef.setItalic(2); // can italicize
                if ( !_cache.findDuplicate( &newDef ) )
                    _cache.update( &newDef, LVFontRef(NULL) );
            }
            
            facesFound++;
            
            
        }
        
        FcFontSetDestroy(fontset);
        CRLog::info("FONTCONFIG: %d fonts registered", facesFound);
        
        const char * fallback_faces = "Arial Unicode MS; AR PL ShanHeiSun Uni; Liberation Sans; Roboto; DejaVu Sans; Noto Sans; Droid Sans";
        SetFallbackFontFaces(lString8(fallback_faces));
        
        return facesFound > 0;
    }
#elif (CR3_OSX == 1)

    int facesFound = 0;
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Unicode.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Bold.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Italic.ttf"));
    facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold Italic.ttf"));
    
    return facesFound > 0;

#else
    return false;
#endif
}

LVFreeTypeFontManager::~LVFreeTypeFontManager() {
    FONT_MAN_GUARD
    LVHashTable<lString8, LVHashTable<lString8, font_lang_compat>* >::iterator it = _supportedLangs.forwardIterator();
    LVHashTable<lString8, LVHashTable<lString8, font_lang_compat>* >::pair* p;
    while ((p = it.next()) != NULL) {
        LVHashTable<lString8, font_lang_compat>* langTable = p->value;
        if (langTable)
            delete langTable;
    }
    _globalCache.clear();
    _cache.clear();
    if (_library)
        FT_Done_FreeType(_library);
#if (DEBUG_FONT_MAN == 1)
    if ( _log ) {
        fclose(_log);
    }
#endif
}

LVFreeTypeFontManager::LVFreeTypeFontManager()
        : _library(NULL), _globalCache(GLYPH_CACHE_SIZE), _supportedLangs(16) {
    FONT_MAN_GUARD
    int error = FT_Init_FreeType(&_library);
    if (error) {
        // error
        CRLog::error("Error while initializing freetype library");
    }
#if (DEBUG_FONT_MAN == 1)
    _log = fopen(DEBUG_FONT_MAN_LOG_FILE, "at");
    if ( _log ) {
        fprintf(_log, "=========================== LOGGING STARTED ===================\n");
    }
#endif
    // _requiredChars = U"azAZ09";//\x0410\x042F\x0430\x044F";
    // Some fonts come without any of these (ie. NotoSansMyanmar.ttf), there's
    // no reason to prevent them from being used.
    // So, check only for the presence of the space char, hoping it's there in any font.
    _requiredChars = U" ";
}

void LVFreeTypeFontManager::gc() // garbage collector
{
    FONT_MAN_GUARD
    _cache.gc();
}

lString8 LVFreeTypeFontManager::makeFontFileName(lString8 name) {
    lString8 filename = _path;
    if (!filename.empty() && _path[_path.length() - 1] != PATH_SEPARATOR_CHAR)
        filename << PATH_SEPARATOR_CHAR;
    filename << name;
    return filename;
}

void LVFreeTypeFontManager::getFaceList(lString32Collection &list) {
    FONT_MAN_GUARD
    _cache.getFaceList(list);
}

void LVFreeTypeFontManager::getFontFileNameList(lString32Collection &list) {
    FONT_MAN_GUARD
    _cache.getFontFileNameList(list);
}

bool LVFreeTypeFontManager::SetAlias(lString8 alias, lString8 facename, int id, bool bold, bool italic) {
    FONT_MAN_GUARD
    lString8 fontname=lString8("\0");
    LVFontDef def(
        fontname,
        -1,
        bold?700:400,
        italic,
        -1, // OpenType features = -1 for not yet instantiated fonts
        css_ff_inherit,
        facename,
        -1,
        id
    );
    LVFontCacheItem * item = _cache.find( &def);
    LVFontDef def1(
        fontname,
        -1,
        bold?700:400,
        italic,
        -1, // OpenType features = -1 for not yet instantiated fonts
        css_ff_inherit,
        alias,
        -1,
        id
    );

    int index = 0;

    FT_Face face = NULL;

    // for all faces in file
    for ( ;; index++ ) {
        int error = FT_New_Face( _library, item->getDef()->getName().c_str(), index, &face ); /* create face object */
        if ( error ) {
            if (index == 0) {
                CRLog::error("FT_New_Face returned error %d", error);
            }
            break;
        }
        int num_faces = face->num_faces;

        css_font_family_t fontFamily = css_ff_sans_serif;
        if ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH )
            fontFamily = css_ff_monospace;
        //lString8 familyName(!facename.empty() ? facename : ::familyName(face));
        // We don't need this here and in other places below: all fonts (except
        // monospaces) will be marked as sans-serif, and elements with
        // style {font-family:serif;} will use the default font too.
        // (we don't ship any Times, and someone having unluckily such
        // a font among his would see it used for {font-family:serif;}
        // elements instead of his default font)
        /*
        if ( familyName=="Times" || familyName=="Times New Roman" )
            fontFamily = css_ff_serif;
        */

        int weight = !facename.empty() ? (bold ? 700 : 400) : getFontWeight(face);
        bool italicFlag = !facename.empty() ? italic : (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0;

        LVFontDef def2(
                item->getDef()->getName(),
                -1, // height==-1 for scalable fonts
                weight,
                italicFlag,
                -1, // OpenType features = -1 for not yet instantiated fonts
                fontFamily,
                alias,
                index,
                id
        );

        if ( face ) {
            FT_Done_Face( face );
            face = NULL;
        }

        if ( _cache.findDuplicate( &def2 ) ) {
            CRLog::trace("font definition is duplicate");
            return false;
        }
        _cache.update( &def2, LVFontRef(NULL) );
        if (!def.getItalic()) {
            LVFontDef newDef( def2 );
            newDef.setItalic(2); // can italicize
            if ( !_cache.findDuplicate( &newDef ) )
                _cache.update( &newDef, LVFontRef(NULL) );
        }
        if ( index>=num_faces-1 )
            break;
    }
    item = _cache.find( &def1);
    if (item->getDef()->getTypeFace()==alias ) {
        return true;
    }
    else {
        return false;
    }
}

LVFontRef LVFreeTypeFontManager::GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface,
                                         int features, int documentId, bool useBias) {
    FONT_MAN_GUARD
#if (DEBUG_FONT_MAN == 1)
    if ( _log ) {
fprintf(_log, "GetFont(size=%d, weight=%d, italic=%d, family=%d, typeface='%s')\n",
        size, weight, italic?1:0, (int)family, typeface.c_str() );
    }
#endif
    lString8 fontname;
    LVFontDef def(
            fontname,
            size,
            weight,
            italic,
            features,
            family,
            typeface,
            -1,
            documentId
    );
#if (DEBUG_FONT_MAN == 1)
    if ( _log )
        fprintf( _log, "GetFont: %s %d %s %s\n",
                 typeface.c_str(),
                 size,
                 weight>400?"bold":"",
                 italic?"italic":"" );
#endif
    LVFontCacheItem *item = _cache.find( &def, useBias );
#if (DEBUG_FONT_MAN == 1)
    if ( item && _log ) { //_log &&
        fprintf(_log, "   found item: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s, weightDelta=%d) FontRef=%d\n",
                item->getDef()->getName().c_str(), item->getDef()->getIndex(), item->getDef()->getSize(), item->getDef()->getWeight(), item->getDef()->getItalic()?1:0,
                (int)item->getDef()->getFamily(), item->getDef()->getTypeFace().c_str(),
                weight - item->getDef()->getWeight(), item->getFont().isNull()?0:item->getFont()->getHeight()
                                                                               );
    }
#endif
    bool italicize = false;

    if (NULL == item)
    {
        CRLog::error("_cache.find() return NULL: size=%d, weight=%d, italic=%d, family=%d, typeface=%s", size, weight, italic, family, typeface.c_str());
        CRLog::error("possible font cache cleared!");
        return LVFontRef(NULL);
    }

    LVFontDef newDef(*item->getDef());

    if (!item->getFont().isNull()) {
        if ( item->getDef()->getFeatures() != features ) {
            // Be sure we ignore any instantiated font found in cache that
            // has features different than the ones requested.
        }
        else {
#if USE_FT_EMBOLDEN
            int deltaWeight = myabs(weight - item->getDef()->getWeight());
            if (deltaWeight >= 25) {
                // This instantiated cached font has a too different weight
                // when USE_FT_EMBOLDEN, ignore this other-weight cached font instance
                // and go loading from the font file again to apply embolden.
            }
#else
            int deltaWeight = weight - item->getDef()->getWeight();
            if (deltaWeight >= 200) {
                // This instantiated cached font has a too low weight
                // embolden using LVFontBoldTransform
                CRLog::debug("font: apply Embolding to increase weight from %d to %d",
                                    newDef.getWeight(), newDef.getWeight() + 200 );
                newDef.setWeight( newDef.getWeight() + 200 );
                LVFontRef ref = LVFontRef( new LVFontBoldTransform( item->getFont(), &_globalCache ) );
                _cache.update( &newDef, ref );
                return ref;
            }
#endif
            else {
                //fprintf(_log, "    : fount existing\n");
                return item->getFont();
            }
        }
    }
    lString8 fname = item->getDef()->getName();
#if (DEBUG_FONT_MAN == 1)
    if ( _log ) {
        int index = item->getDef()->getIndex();
        fprintf(_log, "   no instance: adding new one for filename=%s, index = %d\n", fname.c_str(), index );
    }
#endif
    LVFreeTypeFace *font = new LVFreeTypeFace(_lock, _library, &_globalCache);
    lString8 pathname = makeFontFileName(fname);
    //def.setName( fname );
    //def.setIndex( index );

    //if ( fname.empty() || pathname.empty() ) {
    //    pathname = lString8("arial.ttf");
    //}

    if (!item->getDef()->isRealItalic() && italic) {
        //CRLog::debug("font: fake italic");
        newDef.setItalic(2);
        italicize = true;
    }

    // Use the family of the font we found in the cache (it may be different
    // from the requested family).
    // Assigning the requested familly to this new font could be wrong, and
    // may cause a style or font mismatch when loading from cache, forcing a
    // full re-rendering).
    family = item->getDef()->getFamily();

    //printf("going to load font file %s\n", fname.c_str());
    bool loaded = false;
    if (item->getDef()->getBuf().isNull())
        loaded = font->loadFromFile(pathname.c_str(), item->getDef()->getIndex(), size, family,
                                    isBitmapModeForSize(size), italicize, item->getDef()->getWeight());
    else
        loaded = font->loadFromBuffer(item->getDef()->getBuf(), item->getDef()->getIndex(), size,
                                      family, isBitmapModeForSize(size), italicize, item->getDef()->getWeight());
    if (loaded) {
        //fprintf(_log, "    : loading from file %s : %s %d\n", item->getDef()->getName().c_str(),
        //    item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() );
        LVFontRef ref(font);
        // Instantiate this font with the requested OpenType features
        newDef.setFeatures( features );
        font->setFeatures( features ); // followup setKerningMode() will create/update hb_features if needed
        font->setKerning( GetKerning() );
        font->setShapingMode( GetShapingMode() );
        font->setFaceName(item->getDef()->getTypeFace());
        newDef.setSize(size);
        //item->setFont( ref );
        //_cache.update( def, ref );
#if USE_FT_EMBOLDEN
        int deltaWeight = myabs(weight - newDef.getWeight());
        if (deltaWeight >= 25) {
            // embolden
            // Will make some of this font's methods do embolden the glyphs and widths
            font->setSynthWeight(weight);
            newDef.setWeight( weight, false );
            // Now newDef contains fake/synthetic weight
        }
#else
        int deltaWeight = weight - newDef.getWeight();
        if (deltaWeight >= 200) {
            CRLog::debug("font: apply Embolding to increase weight from %d to %d",
                                newDef.getWeight(), newDef.getWeight() + 200 );
            // Create a wrapper with LVFontBoldTransform which will bolden the glyphs
            newDef.setWeight( newDef.getWeight() + 200 );
            ref = LVFontRef( new LVFontBoldTransform( ref, &_globalCache ) );
        }
#endif
        // build fallback chain
        for (int i = 0; i < _fallbackFontFaces.length(); i++) {
            if (item->getDef()->getTypeFace() == _fallbackFontFaces[i]) {
                ref->setFallbackMask(1 << i);
                break;
            }
        }
        // filling in the language support table if it is empty
#if USE_LOCALE_DATA==1
        LVHashTable<lString8, font_lang_compat>* langTable = NULL;
        if (!_supportedLangs.get(font->getTypeFace(), langTable) || NULL == langTable) {
            FT_Face face = (FT_Face) font->GetHandle();
            if (NULL != face) {
                langTable = getSupportedLangs(face);
                _supportedLangs.set(font->getTypeFace(), langTable);
            }
        }
#endif
        // add to cache
        _cache.update( &newDef, ref );
        //            int rsz = ref->getSize();
        //            if ( rsz!=size ) {
        //                size++;
        //            }
        //delete def;
        return ref;
    } else {
        //printf("    not found!\n");
    }
    //delete def;
    delete font;
    return LVFontRef(NULL);
}

void LVFreeTypeFontManager::GetAvailableFontWeights(LVArray<int>& weights, lString8 typeface) {
    _cache.getAvailableFontWeights(weights, typeface);
}

bool LVFreeTypeFontManager::checkCharSet(FT_Face face) {
    // TODO: check existance of required characters (e.g. cyrillic)
    if (face == NULL)
        return false; // invalid face
    for (int i = 0; i < _requiredChars.length(); i++) {
        lChar32 ch = _requiredChars[i];
        FT_UInt ch_glyph_index = FT_Get_Char_Index(face, ch);
        if ( ch_glyph_index == 0 ) {
            CRLog::debug("Required char not found in font: %04x", ch);
            return false; // no required char!!!
        }
    }
    return true;
}

font_lang_compat LVFreeTypeFontManager::checkFontLangCompat(const lString8 &typeface, const lString8 &langTag) {
    font_lang_compat res = font_lang_compat_invalid_tag;
#if USE_LOCALE_DATA==1
    CRLocaleData loc(langTag);
    if (loc.isValid()) {
        LVHashTable<lString8, font_lang_compat>* langTable = NULL;
        res = font_lang_compat_none;
        if (!_supportedLangs.get(typeface, langTable) || NULL == langTable) {
            if (NULL != langTable) {
                delete langTable;
                langTable = NULL;
                _supportedLangs.remove(typeface);
            }
            LVFontRef fntRef = GetFont(-1, 400, false, css_ff_inherit, typeface, -1);
            if (!fntRef.isNull()) {
                FT_Face face = (FT_Face) fntRef->GetHandle();
                if (NULL != face) {
                    langTable = getSupportedLangs(face);
                    _supportedLangs.set(typeface, langTable);
                }
            }
        }
        if (NULL != langTable) {
            lString8 best_lang;
            LVHashTable<lString8, font_lang_compat>::iterator it = langTable->forwardIterator();
            LVHashTable<lString8, font_lang_compat>::pair* p;
            int max_match = 0;
            font_lang_compat compat;
            while ((p = it.next()) != NULL) {
                lString8 lang = p->key;
                compat = p->value;
                CRLocaleData fc_loc(lang);
                if (fc_loc.isValid()) {
                    int match = loc.calcMatch(fc_loc);
                    if (match > max_match) {
                        max_match = match;
                        best_lang = lang;
                    }
                }
            }
            if (!best_lang.empty()) {
                if (langTable->get(best_lang, compat))
                    res = compat;
            }
        }
    } else {
        CRLog::warn("checkFontLangCompat(): invalid langTag: %s", langTag.c_str());
    }
#endif
    return res;
}

/*
bool LVFreeTypeFontManager::isMonoSpaced( FT_Face face )
{
    // TODO: check existance of required characters (e.g. cyrillic)
    if (face==NULL)
        return false; // invalid face
    lChar32 ch1 = 'i';
    FT_UInt ch_glyph_index1 = FT_Get_Char_Index( face, ch1 );
    if ( ch_glyph_index1==0 )
        return false; // no required char!!!
    int w1, w2;
    int error1 = FT_Load_Glyph( face,  //    handle to face object
            ch_glyph_index1,           //    glyph index
            FT_LOAD_DEFAULT );         //   load flags, see below
    if ( error1 )
        w1 = 0;
    else
        w1 = (face->glyph->metrics.horiAdvance >> 6);
    int error2 = FT_Load_Glyph( face,  //     handle to face object
            ch_glyph_index2,           //     glyph index
            FT_LOAD_DEFAULT );         //     load flags, see below
    if ( error2 )
        w2 = 0;
    else
        w2 = (face->glyph->metrics.horiAdvance >> 6);

    lChar32 ch2 = 'W';
    FT_UInt ch_glyph_index2 = FT_Get_Char_Index( face, ch2 );
    if ( ch_glyph_index2==0 )
        return false; // no required char!!!
    return w1==w2;
}
*/

// Note: publishers can specify font-variant/font-feature-settings/font-variation-settings
// in the @font-face declaration.
// TODO: parse it and pass it here, and set it on the non-instantiated font (instead of -1)
bool LVFreeTypeFontManager::RegisterDocumentFont(int documentId, LVContainerRef container,
                                                 lString32 name, lString8 faceName, bool bold,
                                                 bool italic) {
    FONT_MAN_GUARD
    lString8 name8 = UnicodeToUtf8(name);
    CRLog::debug("RegisterDocumentFont(documentId=%d, path=%s)", documentId, name8.c_str());
    if (_cache.findDocumentFontDuplicate(documentId, name8)) {
        return false;
    }
    LVStreamRef stream = container->OpenStream(name.c_str(), LVOM_READ);
    if (stream.isNull())
        return false;
    lUInt32 size = (lUInt32) stream->GetSize();
    if (size < 100 || size > 5000000)
        return false;
    LVByteArrayRef buf(new LVByteArray(size, 0));
    lvsize_t bytesRead = 0;
    if (stream->Read(buf->get(), size, &bytesRead) != LVERR_OK || bytesRead != size)
        return false;
    bool res = false;

    int index = 0;

    FT_Face face = NULL;

    // for all faces in file
    for (;; index++) {
        int error = FT_New_Memory_Face(_library, buf->get(), buf->length(), index,
                                       &face); /* create face object */
        if (error) {
            if (index == 0) {
                CRLog::error("FT_New_Memory_Face returned error %d", error);
            }
            break;
        }
        //            bool scal = FT_IS_SCALABLE( face );
        //            bool charset = checkCharSet( face );
        //            //bool monospaced = isMonoSpaced( face );
        //            if ( !scal || !charset ) {
        //    //#if (DEBUG_FONT_MAN==1)
        //     //           if ( _log ) {
        //                CRLog::debug("    won't register font %s: %s",
        //                    name.c_str(), !charset?"no mandatory characters in charset" : "font is not scalable"
        //                    );
        //    //            }
        //    //#endif
        //                if ( face ) {
        //                    FT_Done_Face( face );
        //                    face = NULL;
        //                }
        //                break;
        //            }
        int num_faces = face->num_faces;

        css_font_family_t fontFamily = css_ff_sans_serif;
        if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH)
            fontFamily = css_ff_monospace;
        lString8 familyName(!faceName.empty() ? faceName : ::familyName(face));
        if (familyName == "Times" || familyName == "Times New Roman")
            fontFamily = css_ff_serif;

        int weight = !faceName.empty() ? (bold ? 700 : 400) : getFontWeight(face);
        bool italicFlag = !faceName.empty() ? italic : (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0;

        LVFontDef def(
                name8,
                -1, // height==-1 for scalable fonts
                weight,
                italicFlag,
                -1, // OpenType features = -1 for not yet instantiated fonts
                fontFamily,
                familyName,
                index,
                documentId,
                buf
        );
#if (DEBUG_FONT_MAN == 1)
        if ( _log ) {
            fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n",
                    def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str()
                    );
        }
#endif
        if (face) {
            FT_Done_Face(face);
            face = NULL;
        }

        if (_cache.findDuplicate(&def)) {
            CRLog::trace("font definition is duplicate");
            return false;
        }
        _cache.update(&def, LVFontRef(NULL));
        if (!def.getItalic()) {
            LVFontDef newDef(def);
            newDef.setItalic(2); // can italicize
            if (!_cache.findDuplicate(&newDef))
                _cache.update(&newDef, LVFontRef(NULL));
        }
        res = true;

        if (index >= num_faces - 1)
            break;
    }

    return res;
}

void LVFreeTypeFontManager::UnregisterDocumentFonts(int documentId) {
    _cache.removeDocumentFonts(documentId);
}

bool LVFreeTypeFontManager::RegisterExternalFont(int documentId, lString32 name, lString8 family_name, bool bold,
                                                 bool italic) {
    if (name.startsWithNoCase(lString32("res://")))
        name = name.substr(6);
    else if (name.startsWithNoCase(lString32("file://")))
        name = name.substr(7);
    lString8 fname = UnicodeToUtf8(name);
    CRLog::debug("RegisterExternalFont(documentId=%d, path=%s)", documentId, fname.c_str());
    if (_cache.findDocumentFontDuplicate(documentId, fname)) {
        return false;
    }

    bool res = false;
    int index = 0;

    FT_Face face = NULL;

    // for all faces in file
    for (;; index++) {
        int error = FT_New_Face(_library, fname.c_str(), index, &face); /* create face object */
        if (error) {
            if (index == 0) {
                CRLog::error("FT_New_Face returned error %d", error);
            }
            break;
        }
        bool scal = FT_IS_SCALABLE(face);
        bool color = FT_HAS_COLOR(face) != 0;
        bool charset = checkCharSet(face);
        if (!charset) {
            if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) // returns 0 on success
                // If no unicode charmap found, try symbol charmap
                if (!FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL))
                    // It has a symbol charmap: consider it valid
                    charset = true;
        }
        //bool monospaced = isMonoSpaced( face );
        if ((!scal && !color) || !charset) {
            CRLog::debug("    won't register font %s: %s",
                         name.c_str(),
                         !charset ? "no mandatory characters in charset" : "font nor scalable nor color"
            );
            if (face) {
                FT_Done_Face(face);
                face = NULL;
            }
            break;
        }
        int num_faces = face->num_faces;

        css_font_family_t fontFamily = css_ff_sans_serif;
        if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH)
            fontFamily = css_ff_monospace;
        //lString8 familyName(::familyName(face));
        /*
        if (familyName == "Times" || familyName == "Times New Roman")
            fontFamily = css_ff_serif;
         */

        LVFontDef def(
                fname,
                -1, // height==-1 for scalable fonts
                bold ? 700 : 400,
                italic,
                -1, // OpenType features = -1 for not yet instantiated fonts
                fontFamily,
                family_name,
                index,
                documentId
        );
#if (DEBUG_FONT_MAN == 1)
        if ( _log ) {
            fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n",
                    def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str()
                    );
        }
#endif
        if ( _cache.findDuplicate(&def) ) {
            CRLog::trace("font definition is duplicate");
            return false;
        }
        _cache.update(&def, LVFontRef(NULL));
        if ( scal && !def.getItalic() ) {
            LVFontDef newDef(def);
            newDef.setItalic(2); // can italicize
            if ( !_cache.findDuplicate(&newDef) )
                _cache.update(&newDef, LVFontRef(NULL));
        }
        res = true;

        if ( face ) {
            FT_Done_Face(face);
            face = NULL;
        }

        if ( index >= num_faces - 1 )
            break;
    }

    return res;
}

bool LVFreeTypeFontManager::RegisterFont(lString8 name) {
    FONT_MAN_GUARD
#ifdef LOAD_TTF_FONTS_ONLY
    if ( name.pos( cs8(".ttf") ) < 0 && name.pos( cs8(".TTF") ) < 0 )
    return false; // load ttf fonts only
#endif
    //CRLog::trace("RegisterFont(%s)", name.c_str());
    lString8 fname = makeFontFileName(name);
    //CRLog::trace("font file name : %s", fname.c_str());
#if (DEBUG_FONT_MAN == 1)
    if ( _log ) {
        fprintf(_log, "RegisterFont( %s ) path=%s\n",
                name.c_str(), fname.c_str()
                );
    }
#endif
    bool res = false;

    int index = 0;

    FT_Face face = NULL;

    // for all faces in file
    for (;; index++) {
        int error = FT_New_Face(_library, fname.c_str(), index, &face); /* create face object */
        if (error) {
            if (index == 0) {
                CRLog::error("FT_New_Face returned error %d", error);
            }
            break;
        }
        bool scal = FT_IS_SCALABLE(face) != 0;
        bool color = FT_HAS_COLOR(face) != 0;
        bool charset = checkCharSet(face);
        if (!charset) {
            if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) // returns 0 on success
                // If no unicode charmap found, try symbol charmap
                if (!FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL))
                    // It has a symbol charmap: consider it valid
                    charset = true;
        }
        //bool monospaced = isMonoSpaced( face );
        if ((!scal && !color) || !charset) {
            CRLog::debug("    won't register font %s: %s",
                         name.c_str(),
                         !charset ? "no mandatory characters in charset" : "font nor scalable nor color"
            );
            if (face) {
                FT_Done_Face(face);
                face = NULL;
            }
            break;
        }
        int num_faces = face->num_faces;

        css_font_family_t fontFamily = css_ff_sans_serif;
        if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH)
            fontFamily = css_ff_monospace;
        lString8 familyName(::familyName(face));
        /*
        if (familyName == "Times" || familyName == "Times New Roman")
            fontFamily = css_ff_serif;
         */
        LVFontDef def(
                name,
                -1, // height==-1 for scalable fonts
                getFontWeight(face),
                (face->style_flags & FT_STYLE_FLAG_ITALIC) ? true : false,
                -1, // OpenType features = -1 for not yet instantiated fonts
                fontFamily,
                familyName,
                index
        );
#if (DEBUG_FONT_MAN == 1)
        if ( _log ) {
            fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n",
                    def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str()
                    );
        }
#endif

        if (face) {
            FT_Done_Face(face);
            face = NULL;
        }

        if (_cache.findDuplicate(&def)) {
            CRLog::trace("font definition is duplicate");
            return false;
        }
        _cache.update(&def, LVFontRef(NULL));
        if (scal && !def.getItalic()) {
            // If this font is not italic, create another definition
            // with italic=2 (=fake italic) as we can italicize it.
            // A real italic font (italic=1) will be found first
            // when italic is requested.
            // (Strange that italic and embolden are managed differently...
            // maybe it makes the 2x2 combinations easier to manage?)
            LVFontDef newDef( def );
            newDef.setItalic(2); // can italicize
            if ( !_cache.findDuplicate( &newDef ) )
                _cache.update( &newDef, LVFontRef(NULL) );
        }
        res = true;

        if ( index >= num_faces - 1 )
            break;
    }

    return res;
}

bool LVFreeTypeFontManager::Init(lString8 path) {
    _path = path;
    initSystemFonts();
    return (_library != NULL);
}

bool LVFreeTypeFontManager::SetAsPreferredFontWithBias(lString8 face, int bias, bool clearOthersBias)
{
    FONT_MAN_GUARD
    return _cache.setAsPreferredFontWithBias(face, bias, clearOthersBias);
}

#endif  // (USE_FREETYPE==1)
