// MLFLink.cpp : Implementation of CMLFLink
#include "private.h"
#include "mlmain.h"
#include "codepage.h"

#ifdef UNIX
inline WORD READWINTELWORD(WORD w)
{
  return ( w << 8 | w >> 8 );
}

inline DWORD READWINTELDWORD(DWORD dw)
{
  return READWINTELWORD( (WORD)(dw >> 16 )) | ((DWORD)READWINTELWORD( dw & 0xffff)) << 16;
}
#else
#define READWINTELWORD
#define READWINTELDWORD
#endif

IMLangFontLink *g_pMLFLink = NULL;

CMLFLink::CCodePagesCache* CMLFLink::m_pCodePagesCache = NULL;
CMLFLink::CFontMappingCache* CMLFLink::m_pFontMappingCache = NULL;
CMLFLink2::CFontMappingCache2* CMLFLink2::m_pFontMappingCache2 = NULL;

// Strings to identify regular font
const char szRegular[]    = "Regular";
const char szNormal[]     = "Normal";

// font table
FONTINFO *g_pfont_table = NULL;

// Unicode range table for non Windows code page code points
// Data is provided by NT international group.
URANGEFONT g_urange_table[] = {
{0x0108, 0x010B, 0},
{0x0114, 0x0115, 0},
{0x011C, 0x011D, 0},
{0x0120, 0x0121, 0},
{0x0124, 0x0125, 0},
{0x0128, 0x0129, 0},
{0x012C, 0x012D, 0},
{0x0134, 0x0135, 0},
{0x014E, 0x014F, 0},
{0x015C, 0x015D, 0},
{0x0168, 0x0169, 0},
{0x016C, 0x016D, 0},
{0x0174, 0x0177, 0},
{0x017F, 0x0191, 0},
{0x0193, 0x019F, 0},
{0x01A2, 0x01AE, 0},
{0x01B1, 0x01CD, 0},
{0x01CF, 0x01CF, 0},
{0x01D1, 0x01D1, 0},
{0x01D3, 0x01D3, 0},
{0x01D5, 0x01D5, 0},
{0x01D7, 0x01D7, 0},
{0x01D9, 0x01D9, 0},
{0x01DB, 0x01DB, 0},
{0x01DD, 0x01F5, 0},
{0x01FA, 0x0217, 0},
{0x0250, 0x0250, 0},
{0x0252, 0x0260, 0},
{0x0262, 0x02A8, 0},
{0x02B0, 0x02C5, 0},
{0x02C8, 0x02C8, 0},
{0x02CC, 0x02CC, 0},
{0x02CE, 0x02CF, 0},
{0x02D1, 0x02D7, 0},
{0x02DE, 0x02DE, 0},
{0x02E0, 0x02E9, 0},
{0x0302, 0x0302, 0},
{0x0304, 0x0308, 0},
{0x030A, 0x0322, 0},
{0x0324, 0x0345, 0},
{0x0360, 0x0361, 0},
{0x0374, 0x0375, 0},
{0x037A, 0x037A, 0},
{0x037E, 0x037E, 0},
{0x0387, 0x0387, 0},
{0x03D0, 0x03D6, 0},
{0x03DA, 0x03DA, 0},
{0x03DC, 0x03DC, 0},
{0x03DE, 0x03DE, 0},
{0x03E0, 0x03E0, 0},
{0x03E2, 0x03F3, 0},
{0x0460, 0x0486, 0},
{0x0492, 0x04C4, 0},
{0x04C7, 0x04C8, 0},
{0x04CB, 0x04CC, 0},
{0x04D0, 0x04EB, 0},
{0x04EE, 0x04F5, 0},
{0x04F8, 0x04F9, 0},
{0x0531, 0x0556, 0},
{0x0559, 0x055F, 0},
{0x0561, 0x0587, 0},
{0x0589, 0x0589, 0},
{0x0591, 0x05A1, 0},
{0x05A3, 0x05AF, 0},
{0x05C4, 0x05C4, 0},
{0x0660, 0x066D, 0},
{0x0670, 0x067D, 0},
{0x067F, 0x0685, 0},
{0x0687, 0x0697, 0},
{0x0699, 0x06AE, 0},
{0x06B0, 0x06B7, 0},
{0x06BA, 0x06BE, 0},
{0x06C0, 0x06CE, 0},
{0x06D0, 0x06ED, 0},
{0x06F0, 0x06F9, 0},
{0x0901, 0x0903, 0},
{0x0905, 0x0939, 0},
{0x093C, 0x094D, 0},
{0x0950, 0x0954, 0},
{0x0958, 0x0970, 0},
{0x0981, 0x0983, 0},
{0x0985, 0x098C, 0},
{0x098F, 0x0990, 0},
{0x0993, 0x09A8, 0},
{0x09AA, 0x09B0, 0},
{0x09B2, 0x09B2, 0},
{0x09B6, 0x09B9, 0},
{0x09BC, 0x09BC, 0},
{0x09BE, 0x09C4, 0},
{0x09C7, 0x09C8, 0},
{0x09CB, 0x09CD, 0},
{0x09D7, 0x09D7, 0},
{0x09DC, 0x09DD, 0},
{0x09DF, 0x09E3, 0},
{0x09E6, 0x09FA, 0},
{0x0A02, 0x0A02, 0},
{0x0A05, 0x0A0A, 0},
{0x0A0F, 0x0A10, 0},
{0x0A13, 0x0A28, 0},
{0x0A2A, 0x0A30, 0},
{0x0A32, 0x0A33, 0},
{0x0A35, 0x0A36, 0},
{0x0A38, 0x0A39, 0},
{0x0A3C, 0x0A3C, 0},
{0x0A3E, 0x0A42, 0},
{0x0A47, 0x0A48, 0},
{0x0A4B, 0x0A4D, 0},
{0x0A59, 0x0A5C, 0},
{0x0A5E, 0x0A5E, 0},
{0x0A66, 0x0A74, 0},
{0x0A81, 0x0A83, 0},
{0x0A85, 0x0A8B, 0},
{0x0A8D, 0x0A8D, 0},
{0x0A8F, 0x0A91, 0},
{0x0A93, 0x0AA8, 0},
{0x0AAA, 0x0AB0, 0},
{0x0AB2, 0x0AB3, 0},
{0x0AB5, 0x0AB9, 0},
{0x0ABC, 0x0AC5, 0},
{0x0AC7, 0x0AC9, 0},
{0x0ACB, 0x0ACD, 0},
{0x0AD0, 0x0AD0, 0},
{0x0AE0, 0x0AE0, 0},
{0x0AE6, 0x0AEF, 0},
{0x0B01, 0x0B03, 0},
{0x0B05, 0x0B0C, 0},
{0x0B0F, 0x0B10, 0},
{0x0B13, 0x0B28, 0},
{0x0B2A, 0x0B30, 0},
{0x0B32, 0x0B33, 0},
{0x0B36, 0x0B39, 0},
{0x0B3C, 0x0B43, 0},
{0x0B47, 0x0B48, 0},
{0x0B4B, 0x0B4D, 0},
{0x0B56, 0x0B57, 0},
{0x0B5C, 0x0B5D, 0},
{0x0B5F, 0x0B61, 0},
{0x0B66, 0x0B70, 0},
{0x0B82, 0x0B83, 0},
{0x0B85, 0x0B8A, 0},
{0x0B8E, 0x0B90, 0},
{0x0B92, 0x0B95, 0},
{0x0B99, 0x0B9A, 0},
{0x0B9C, 0x0B9C, 0},
{0x0B9E, 0x0B9F, 0},
{0x0BA3, 0x0BA4, 0},
{0x0BA8, 0x0BAA, 0},
{0x0BAE, 0x0BB5, 0},
{0x0BB7, 0x0BB9, 0},
{0x0BBE, 0x0BC2, 0},
{0x0BC6, 0x0BC8, 0},
{0x0BCA, 0x0BCD, 0},
{0x0BD7, 0x0BD7, 0},
{0x0BE7, 0x0BF2, 0},
{0x0C01, 0x0C03, 0},
{0x0C05, 0x0C0C, 0},
{0x0C0E, 0x0C10, 0},
{0x0C12, 0x0C28, 0},
{0x0C2A, 0x0C33, 0},
{0x0C35, 0x0C39, 0},
{0x0C3E, 0x0C44, 0},
{0x0C46, 0x0C48, 0},
{0x0C4A, 0x0C4D, 0},
{0x0C55, 0x0C56, 0},
{0x0C60, 0x0C61, 0},
{0x0C66, 0x0C6F, 0},
{0x0C82, 0x0C83, 0},
{0x0C85, 0x0C8C, 0},
{0x0C8E, 0x0C90, 0},
{0x0C92, 0x0CA8, 0},
{0x0CAA, 0x0CB3, 0},
{0x0CB5, 0x0CB9, 0},
{0x0CBE, 0x0CC4, 0},
{0x0CC6, 0x0CC8, 0},
{0x0CCA, 0x0CCD, 0},
{0x0CD5, 0x0CD6, 0},
{0x0CDE, 0x0CDE, 0},
{0x0CE0, 0x0CE1, 0},
{0x0CE6, 0x0CEF, 0},
{0x0D02, 0x0D03, 0},
{0x0D05, 0x0D0C, 0},
{0x0D0E, 0x0D10, 0},
{0x0D12, 0x0D28, 0},
{0x0D2A, 0x0D39, 0},
{0x0D3E, 0x0D43, 0},
{0x0D46, 0x0D48, 0},
{0x0D4A, 0x0D4D, 0},
{0x0D57, 0x0D57, 0},
{0x0D60, 0x0D61, 0},
{0x0D66, 0x0D6F, 0},
{0x0E81, 0x0E82, 0},
{0x0E84, 0x0E84, 0},
{0x0E87, 0x0E88, 0},
{0x0E8A, 0x0E8A, 0},
{0x0E8D, 0x0E8D, 0},
{0x0E94, 0x0E97, 0},
{0x0E99, 0x0E9F, 0},
{0x0EA1, 0x0EA3, 0},
{0x0EA5, 0x0EA5, 0},
{0x0EA7, 0x0EA7, 0},
{0x0EAA, 0x0EAB, 0},
{0x0EAD, 0x0EB9, 0},
{0x0EBB, 0x0EBD, 0},
{0x0EC0, 0x0EC4, 0},
{0x0EC6, 0x0EC6, 0},
{0x0EC8, 0x0ECD, 0},
{0x0ED0, 0x0ED9, 0},
{0x0EDC, 0x0EDD, 0},
{0x0F00, 0x0F47, 0},
{0x0F49, 0x0F69, 0},
{0x0F71, 0x0F8B, 0},
{0x0F90, 0x0F95, 0},
{0x0F97, 0x0F97, 0},
{0x0F99, 0x0FAD, 0},
{0x0FB1, 0x0FB7, 0},
{0x0FB9, 0x0FB9, 0},
{0x10A0, 0x10C5, 0},
{0x10D0, 0x10F6, 0},
{0x10FB, 0x10FB, 0},
{0x1100, 0x1159, 0},
{0x115F, 0x11A2, 0},
{0x11A8, 0x11F9, 0},
{0x1E00, 0x1E9B, 0},
{0x1EA0, 0x1EF9, 0},
{0x1F00, 0x1F15, 0},
{0x1F18, 0x1F1D, 0},
{0x1F20, 0x1F45, 0},
{0x1F48, 0x1F4D, 0},
{0x1F50, 0x1F57, 0},
{0x1F59, 0x1F59, 0},
{0x1F5B, 0x1F5B, 0},
{0x1F5D, 0x1F5D, 0},
{0x1F5F, 0x1F7D, 0},
{0x1F80, 0x1FB4, 0},
{0x1FB6, 0x1FC4, 0},
{0x1FC6, 0x1FD3, 0},
{0x1FD6, 0x1FDB, 0},
{0x1FDD, 0x1FEF, 0},
{0x1FF2, 0x1FF4, 0},
{0x1FF6, 0x1FFE, 0},
{0x2000, 0x200B, 0},
{0x2011, 0x2012, 0},
{0x2017, 0x2017, 0},
{0x201B, 0x201B, 0},
{0x201F, 0x201F, 0},
{0x2023, 0x2024, 0},
{0x2028, 0x202E, 0},
{0x2031, 0x2031, 0},
{0x2034, 0x2034, 0},
{0x2036, 0x2038, 0},
{0x203C, 0x2046, 0},
{0x206A, 0x2070, 0},
{0x2075, 0x207E, 0},
{0x2080, 0x2080, 0},
{0x2085, 0x208E, 0},
{0x20A0, 0x20A9, 0},
{0x20D0, 0x20E1, 0},
{0x2100, 0x2102, 0},
{0x2104, 0x2104, 0},
{0x2106, 0x2108, 0},
{0x210A, 0x2112, 0},
{0x2114, 0x2115, 0},
{0x2117, 0x2120, 0},
{0x2123, 0x2125, 0},
{0x2127, 0x212A, 0},
{0x212C, 0x2138, 0},
{0x2155, 0x215A, 0},
{0x215F, 0x215F, 0},
{0x216C, 0x216F, 0},
{0x217A, 0x2182, 0},
{0x219A, 0x21D1, 0},
{0x21D3, 0x21D3, 0},
{0x21D5, 0x21EA, 0},
{0x2201, 0x2201, 0},
{0x2204, 0x2206, 0},
{0x2209, 0x220A, 0},
{0x220C, 0x220E, 0},
{0x2210, 0x2210, 0},
{0x2212, 0x2214, 0},
{0x2216, 0x2219, 0},
{0x221B, 0x221C, 0},
{0x2221, 0x2222, 0},
{0x2224, 0x2224, 0},
{0x2226, 0x2226, 0},
{0x222D, 0x222D, 0},
{0x222F, 0x2233, 0},
{0x2238, 0x223B, 0},
{0x223E, 0x2247, 0},
{0x2249, 0x224B, 0},
{0x224D, 0x2251, 0},
{0x2253, 0x225F, 0},
{0x2262, 0x2263, 0},
{0x2268, 0x2269, 0},
{0x226C, 0x226D, 0},
{0x2270, 0x2281, 0},
{0x2284, 0x2285, 0},
{0x2288, 0x2294, 0},
{0x2296, 0x2298, 0},
{0x229A, 0x22A4, 0},
{0x22A6, 0x22BE, 0},
{0x22C0, 0x22F1, 0},
{0x2300, 0x2300, 0},
{0x2302, 0x2311, 0},
{0x2313, 0x237A, 0},
{0x2400, 0x2424, 0},
{0x2440, 0x244A, 0},
{0x24B6, 0x24CF, 0},
{0x24EA, 0x24EA, 0},
{0x254C, 0x254F, 0},
{0x2575, 0x2580, 0},
{0x2590, 0x2591, 0},
{0x25A2, 0x25A2, 0},
{0x25AA, 0x25B1, 0},
{0x25B4, 0x25B5, 0},
{0x25B8, 0x25BB, 0},
{0x25BE, 0x25BF, 0},
{0x25C2, 0x25C5, 0},
{0x25C9, 0x25CA, 0},
{0x25CC, 0x25CD, 0},
{0x25D2, 0x25E1, 0},
{0x25E6, 0x25EE, 0},
{0x2600, 0x2604, 0},
{0x2607, 0x2608, 0},
{0x260A, 0x260D, 0},
{0x2610, 0x2613, 0},
{0x261A, 0x261B, 0},
{0x261D, 0x261D, 0},
{0x261F, 0x263F, 0},
{0x2641, 0x2641, 0},
{0x2643, 0x265F, 0},
{0x2662, 0x2662, 0},
{0x2666, 0x2666, 0},
{0x266B, 0x266B, 0},
{0x266E, 0x266E, 0},
{0x2701, 0x2704, 0},
{0x2706, 0x2709, 0},
{0x270C, 0x2727, 0},
{0x2729, 0x274B, 0},
{0x274D, 0x274D, 0},
{0x274F, 0x2752, 0},
{0x2756, 0x2756, 0},
{0x2758, 0x275E, 0},
{0x2761, 0x2767, 0},
{0x2776, 0x2794, 0},
{0x2798, 0x27AF, 0},
{0x27B1, 0x27BE, 0},
{0x3004, 0x3004, 0},
{0x3018, 0x301C, 0},
{0x3020, 0x3020, 0},
{0x302A, 0x3037, 0},
{0x303F, 0x303F, 0},
{0x3094, 0x3094, 0},
{0x3099, 0x309A, 0},
{0x30F7, 0x30FA, 0},
{0x312A, 0x312C, 0},
{0x3190, 0x319F, 0},
{0x322A, 0x3230, 0},
{0x3233, 0x3238, 0},
{0x323A, 0x3243, 0},
{0x3280, 0x32A2, 0},
{0x32A9, 0x32B0, 0},
{0x32C0, 0x32CB, 0},
{0x32D0, 0x32FE, 0},
{0x3300, 0x3302, 0},
{0x3304, 0x330C, 0},
{0x330E, 0x3313, 0},
{0x3315, 0x3317, 0},
{0x3319, 0x3321, 0},
{0x3324, 0x3325, 0},
{0x3328, 0x332A, 0},
{0x332C, 0x3335, 0},
{0x3337, 0x333A, 0},
{0x333C, 0x3348, 0},
{0x334B, 0x334C, 0},
{0x334E, 0x3350, 0},
{0x3352, 0x3356, 0},
{0x3358, 0x3376, 0},
{0x337F, 0x337F, 0},
{0x3385, 0x3387, 0},
{0x33CB, 0x33CC, 0},
{0x33D4, 0x33D4, 0},
{0x33D7, 0x33D7, 0},
{0x33D9, 0x33DA, 0},
{0x33E0, 0x33FE, 0},
{0xFB00, 0xFB06, 0},
{0xFB13, 0xFB17, 0},
{0xFB1E, 0xFB36, 0},
{0xFB38, 0xFB3C, 0},
{0xFB3E, 0xFB3E, 0},
{0xFB40, 0xFB41, 0},
{0xFB43, 0xFB44, 0},
{0xFB46, 0xFBB1, 0},
{0xFBD3, 0xFD3F, 0},
{0xFD50, 0xFD8F, 0},
{0xFD92, 0xFDC7, 0},
{0xFDF0, 0xFDFB, 0},
{0xFE20, 0xFE23, 0},
{0xFE32, 0xFE32, 0},
{0xFE58, 0xFE58, 0},
{0xFE70, 0xFE72, 0},
{0xFE74, 0xFE74, 0},
{0xFE76, 0xFEFC, 0},
{0xFEFF, 0xFEFF, 0},
{0xFFA0, 0xFFBE, 0},
{0xFFC2, 0xFFC7, 0},
{0xFFCA, 0xFFCF, 0},
{0xFFD2, 0xFFD7, 0},
{0xFFDA, 0xFFDC, 0},
{0xFFE8, 0xFFEE, 0},
{0xFFFD, 0xFFFD, 0}
};

const struct {
    int         nCharSet;
    UINT        uCodePage;
    DWORD       dwCodePages;
    SCRIPT_ID   sid[3];
} g_CharSetTransTable[] = 
{
    ANSI_CHARSET,        1252, FS_LATIN1,   sidAsciiLatin,  sidLatin,   sidDefault,  
    EASTEUROPE_CHARSET,  1250, FS_LATIN2,   sidAsciiLatin,  sidLatin,   sidDefault,  
    RUSSIAN_CHARSET,     1251, FS_CYRILLIC, sidCyrillic,    sidDefault, sidDefault,
    GREEK_CHARSET,       1253, FS_GREEK,    sidGreek,       sidDefault, sidDefault,
    TURKISH_CHARSET,     1254, FS_TURKISH,  sidAsciiLatin,  sidLatin,   sidDefault,
    HEBREW_CHARSET,      1255, FS_HEBREW,   sidHebrew,      sidDefault, sidDefault,
    ARABIC_CHARSET,      1256, FS_ARABIC,   sidArabic,      sidDefault, sidDefault,
    BALTIC_CHARSET,      1257, FS_BALTIC,   sidAsciiLatin,  sidLatin,   sidDefault,
    VIETNAMESE_CHARSET,  1258, FS_VIETNAMESE, sidAsciiLatin,  sidLatin, sidDefault,
    THAI_CHARSET,         874, FS_THAI ,    sidThai,        sidDefault, sidDefault,
    SHIFTJIS_CHARSET,     932, FS_JISJAPAN, sidKana,        sidDefault, sidDefault, //sidKana,        sidHan,     sidDefault,
    GB2312_CHARSET,       936, FS_CHINESESIMP,sidHan,       sidDefault, sidDefault, //sidKana,     sidHan,     sidBopomofo,
    HANGEUL_CHARSET,      949, FS_WANSUNG,  sidHangul,      sidDefault, sidDefault, //sidHangul,   sidKana,    sidHan,
    CHINESEBIG5_CHARSET,  950, FS_CHINESETRAD, sidBopomofo, sidDefault, sidDefault, //sidKana,     sidHan,     sidBopomofo,     
    JOHAB_CHARSET,       1361, FS_JOHAB,    sidHangul,     sidDefault, sidDefault,
    DEFAULT_CHARSET,        0, 0,           sidDefault,     sidDefault, sidDefault,
};

//
// Extended code page table
//
const struct {
    int         nCharSet;
    UINT        uCodePage;
    DWORD       dwCodePages;
} g_CharSetTransTableExt[] = 
{
    ANSI_CHARSET,        28591, FS_MLANG_28591,
    EASTEUROPE_CHARSET,  28592, FS_MLANG_28592,
    RUSSIAN_CHARSET,     28595, FS_MLANG_28595,
    GREEK_CHARSET,       28597, FS_MLANG_28597,
    TURKISH_CHARSET,     28593, FS_MLANG_28593,
    HEBREW_CHARSET,      28598, FS_MLANG_28598,
    HEBREW_CHARSET,      38598, FS_MLANG_38598,
    ARABIC_CHARSET,      28596, FS_MLANG_28596,
    BALTIC_CHARSET,      28594, FS_MLANG_28594,
    VIETNAMESE_CHARSET,  28599, FS_MLANG_28599,
    THAI_CHARSET,        28605, FS_MLANG_28605,
    ANSI_CHARSET,        20127, FS_MLANG_20127,
    ANSI_CHARSET,        50220, FS_MLANG_50220,
    ANSI_CHARSET,        51932, FS_MLANG_51932,
    ANSI_CHARSET,        51949, FS_MLANG_51949,
    ANSI_CHARSET,        50225, FS_MLANG_50225,
    ANSI_CHARSET,        52936, FS_MLANG_52936,
    ANSI_CHARSET,        65000, FS_MLANG_65000,
    ANSI_CHARSET,        65001, FS_MLANG_65001,
    ANSI_CHARSET,        1200,  FS_MLANG_1200,
    ANSI_CHARSET,        20866, FS_MLANG_20866,
    ANSI_CHARSET,        21866, FS_MLANG_21866,
    ANSI_CHARSET,        50221, FS_MLANG_50221,
    ANSI_CHARSET,        50222, FS_MLANG_50222,
    DEFAULT_CHARSET,     0,     0, 
};

// Primary chars for scripts
// Pre-sorted by Unicode characters to speed up CMAP search.
const struct {
    WCHAR       wch;    //Can be extended to a character list
    SCRIPT_ID   sid;
} g_wCharToScript[] =
{
        0x0531, sidArmenian,
        0x0710, sidSyriac,
        0x0780, sidThaana,
        0x0905, sidDevanagari,
        0x0985, sidBengali,
        0x0a05, sidGurmukhi,
        0x0a85, sidGujarati,
        0x0b05, sidOriya,
        0x0b85, sidTamil,
        0x0c05, sidTelugu,
        0x0c85, sidKannada,
        0x0d05, sidMalayalam,
        0x0d85, sidSinhala,
        0x0e81, sidLao,
        0x0f40, sidTibetan,
        0x10a0, sidGeorgian,
        0x10d0, sidGeorgian,
        0x1300, sidEthiopic,
        0x1401, sidCanSyllabic,
        0x13a0, sidCherokee,
        0xa000, sidYi,
        0x1680, sidOgham,
        0x16a0, sidRunic,
        0x1700, sidBurmese,
        0x1780, sidKhmer,
        0x2800, sidBraille,
//      0x0020, sidUserDefined
    };

// Script tables ported from Trident
static SCRIPT_ID s_asidUnicodeSubRangeScriptMapping[] =
{
    sidAsciiLatin, sidLatin,      sidLatin,      sidLatin,        // 128-131
    sidLatin,      sidLatin,      0,             sidGreek,        // 132-135
    sidGreek,      sidCyrillic,   sidArmenian,   sidHebrew,       // 136-139
    sidHebrew,     sidArabic,     sidArabic,     sidDevanagari,   // 140-143
    sidBengali,    sidGurmukhi,   sidGujarati,   sidOriya,        // 144-147
    sidTamil,      sidTelugu,     sidKannada,    sidMalayalam,    // 148-151
    sidThai,       sidLao,        sidGeorgian,   sidGeorgian,     // 152-155
    sidHangul,     sidLatin,      sidGreek,      0,               // 156-159
    0,             0,             0,             0,               // 160-163
    0,             0,             0,             0,               // 164-167
    0,             0,             0,             0,               // 168-171
    0,             0,             0,             0,               // 172-175
    sidHan,        sidKana,       sidKana,       sidBopomofo,     // 176-179
    sidHangul,     0,             0,             0,               // 180-183
    sidHangul,     sidHangul,     sidHangul,     sidHan,          // 184-187
    0,             sidHan,        0,             0,               // 188-191
    0,             0,             0,             0,               // 192-195
    0,             0,             sidHangul,     0,               // 196-199
};


// Script table (raw data from MichelSu)
// Rendered by script ID
const SCRIPT ScriptTable[] = 
{

    {sidDefault,    IDS_SIDDEFAULT,     0,  0,  0,  0, SCRIPTCONTF_SCRIPT_SYSTEM},      // 0
    {sidMerge,      IDS_SIDMERGE,       0,  0,  0,  0, SCRIPTCONTF_SCRIPT_SYSTEM},      // 1
    {sidAsciiSym,   IDS_SIDASCIISYM,    0,  0,  0,  0, SCRIPTCONTF_SCRIPT_SYSTEM},      // 2
    {sidAsciiLatin, IDS_SIDASCIILATIN,  1252, 0,      IDS_FONT_WESTERN_FIXED, IDS_FONT_WESTERN_PROP2, SCRIPTCONTF_SCRIPT_USER},  // 3
    {sidLatin,      IDS_SIDLATIN,       1252, 0,      IDS_FONT_WESTERN_FIXED, IDS_FONT_WESTERN_PROP2, SCRIPTCONTF_SCRIPT_HIDE},  // 4
    {sidGreek,      IDS_SIDGREEK,       1253, 0x03AC, IDS_FONT_WESTERN_FIXED, IDS_FONT_WESTERN_PROP2, SCRIPTCONTF_SCRIPT_USER},  // 5
    {sidCyrillic,   IDS_SIDCYRILLIC,    1251, 0x0401, IDS_FONT_WESTERN_FIXED, IDS_FONT_WESTERN_PROP2, SCRIPTCONTF_SCRIPT_USER},  // 6
    {sidArmenian,   IDS_SIDARMENIAN,    0,    0x0531, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 7
/**/{sidHebrew,     IDS_SIDHEBREW,      1255, 0x05D4, IDS_FONT_HEBREW_FIXED,  IDS_FONT_HEBREW_PROP, SCRIPTCONTF_SCRIPT_USER},    // 8
    {sidArabic,     IDS_SIDARABIC,      1256, 0x0627, IDS_FONT_ARABIC_FIXED,  IDS_FONT_ARABIC_PROP, SCRIPTCONTF_SCRIPT_USER},    // 9
    {sidDevanagari, IDS_SIDDEVANAGARI,  0,    0x0905, IDS_FONT_DEVANAGARI_FIXED,IDS_FONT_DEVANAGARI_PROP, SCRIPTCONTF_SCRIPT_USER}, // 10
    {sidBengali,    IDS_SIDBENGALI,     0,    0x0985, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 11
    {sidGurmukhi,   IDS_SIDGURMUKHI,    0,    0x0A05, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 12
    {sidGujarati,   IDS_SIDGUJARATI,    0,    0x0A85, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 13
    {sidOriya,      IDS_SIDORIYA,       0,    0x0B05, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 14
    {sidTamil,      IDS_SIDTAMIL,       0,    0x0B85, IDS_FONT_TAMIL_FIXED, IDS_FONT_TAMIL_PROP, SCRIPTCONTF_SCRIPT_USER},       // 15
    {sidTelugu,     IDS_SIDTELUGU,      0,    0x0C05, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 16
    {sidKannada,    IDS_SIDKANNADA,     0,    0x0C85, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 17
    {sidMalayalam,  IDS_SIDMALAYALAM,   0,    0x0D05, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 18
    {sidThai,       IDS_SIDTHAI,        874,  0x0E01, IDS_FONT_THAI_FIXED2, IDS_FONT_THAI_PROP2, SCRIPTCONTF_SCRIPT_USER},       // 19
    {sidLao,        IDS_SIDLAO,         0,    0x0E81, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 20
    {sidTibetan,    IDS_SIDTIBETAN,     0,    0x0F40, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 21
    {sidGeorgian,   IDS_SIDGEORGIAN,    0,    0x10D0, 0, 0, SCRIPTCONTF_SCRIPT_USER},                                            // 22
    {sidHangul,     IDS_SIDHANGUL,      949,  0,      IDS_FONT_KOREAN_FIXED,   IDS_FONT_KOREAN_PROP, SCRIPTCONTF_SCRIPT_USER},   // 23
    {sidKana,       IDS_SIDKANA,        932,  0,      IDS_FONT_JAPANESE_FIXED, IDS_FONT_JAPANESE_PROP, SCRIPTCONTF_SCRIPT_USER}, // 24
    {sidBopomofo,   IDS_SIDBOPOMOFO,    950,  0,      IDS_FONT_TAIWAN_FIXED,   IDS_FONT_TAIWAN_PROP, SCRIPTCONTF_SCRIPT_USER},   // 25
    {sidHan,        IDS_SIDHAN,         936,  0,      IDS_FONT_CHINESE_FIXED,  IDS_FONT_CHINESE_PROP, SCRIPTCONTF_SCRIPT_USER},  // 26
    {sidEthiopic,   IDS_SIDETHIOPIC,    0,    0x1300, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 27
    {sidCanSyllabic,IDS_SIDCANSYLLABIC, 0,    0x1401, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 28
    {sidCherokee,   IDS_SIDCHEROKEE,    0,    0x13A0, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 29
    {sidYi,         IDS_SIDYI,          0,    0xA000, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 30
    {sidBraille,    IDS_SIDBRAILLE,     0,    0x2800, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 31
    {sidRunic,      IDS_SIDRUNIC,       0,    0x16A0, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 32
    {sidOgham,      IDS_SIDOGHAM,       0,    0x1680, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 33
    {sidSinhala,    IDS_SIDSINHALA,     0,    0x0D85, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 34
    {sidSyriac,     IDS_SIDSYRIAC,      0,    0x0710, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 35
    {sidBurmese,    IDS_SIDBURMESE,     0,    0x1700, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 36
    {sidKhmer,      IDS_SIDKHMER,       0,    0x1780, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 37
    {sidThaana,     IDS_SIDTHAANA,      0,    0x0780, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 38
    {sidMongolian,  IDS_SIDMONGOLIAN,   0,    0,      0, 0, SCRIPTCONTF_SCRIPT_USER},      // 39
    {sidUserDefined,IDS_SIDUSERDEFINED, 0,    0x0020, 0, 0, SCRIPTCONTF_SCRIPT_USER},      // 40
};



UINT g_cScript = ARRAYSIZE(ScriptTable);

/////////////////////////////////////////////////////////////////////////////
// CMLFLink Free Global Objects

void CMLangFontLink_FreeGlobalObjects()
{
    if (g_pMLFLink)
        g_pMLFLink->Release();
    if (CMLFLink::m_pCodePagesCache)
        delete CMLFLink::m_pCodePagesCache;
    if (CMLFLink::m_pFontMappingCache)
        delete CMLFLink::m_pFontMappingCache;
    if (CMLFLink2::m_pFontMappingCache2)
        delete CMLFLink2::m_pFontMappingCache2;
}

/////////////////////////////////////////////////////////////////////////////
// CMLFLink

CMLFLink::CMLFLink()
{
    DllAddRef();
    EnterCriticalSection(&g_cs);
    if (!m_pCodePagesCache)
        m_pCodePagesCache = new CCodePagesCache;
    if (!m_pFontMappingCache)
        m_pFontMappingCache = new CFontMappingCache;
    LeaveCriticalSection(&g_cs);
    m_pFlinkTable = NULL;
}

STDMETHODIMP CMLFLink::GetCharCodePages(WCHAR chSrc, DWORD* pdwCodePages)
{
    return ::GetCharCodePagesEx(chSrc, pdwCodePages, CPBITS_WINDOWS);
}


/////////////////////////////////////////////////////////////////////////////
// CMLFLink : IMLangCodePages

HRESULT GetCharCodePagesEx(WCHAR chSrc, DWORD* pdwCodePages, DWORD dwFlags)
{
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);

    HRESULT hr = S_OK;
    int nLen;
    int iCmd = 0;
    int nPickOffset;
    int nBitOffset = 0;
    int nBitCount = 32;
    DWORD dwDiff = 0xffffffff;
    DWORD dwOr = 0;
    DWORD dwCodePages;
    DWORD adwBitsMap[32];
    const CCodePagesHeader* pHeader;
    const WORD* pwTable;
    int nBlock;
    int nEndLen;
    const BYTE* pbBlock;
    BYTE *pBuffer = NULL;

    if (!CMLFLink::m_pCodePagesCache)
        CMLFLink::m_pCodePagesCache = new CMLFLink::CCodePagesCache;

    if (CMLFLink::m_pCodePagesCache)
        hr = CMLFLink::m_pCodePagesCache->Load();
    else
    {
        hr = E_FAIL;
    }

    if (SUCCEEDED(hr))
    {
        pBuffer = CMLFLink::m_pCodePagesCache->GetCodePageBits(dwFlags & CPBITS_WINDOWS? FALSE:TRUE);
        pHeader = (CCodePagesHeader*) pBuffer;
        pwTable = (WORD*)(pBuffer + READWINTELDWORD(pHeader->m_dwTableOffset));
        nBlock = chSrc / READWINTELDWORD(pHeader->m_dwBlockSize);
        nEndLen = chSrc % READWINTELDWORD(pHeader->m_dwBlockSize);
        pbBlock = pBuffer + READWINTELWORD(pwTable[nBlock]);
    }

    for (int nDoneLen = 0; SUCCEEDED(hr) && nDoneLen < (int)READWINTELDWORD(pHeader->m_dwBlockSize); nDoneLen += nLen)
    {
        BYTE bCmd = pbBlock[--iCmd];
        if (bCmd < pHeader->m_abCmdCode[1])
        {
            // Flat
            nLen = bCmd + 1;
            nPickOffset = nBitOffset + nBitCount * (nEndLen - nDoneLen);
            nBitOffset += nBitCount * nLen;
        }
        else if (bCmd < pHeader->m_abCmdCode[2])
        {
            // Pack
            nLen = bCmd - pHeader->m_abCmdCode[1] + 2;
            nPickOffset = nBitOffset;
            nBitOffset += nBitCount;
        }
        else if (bCmd < pHeader->m_abCmdCode[4])
        {
            // Diff & Or
            nLen = 0;

            DWORD dw = pbBlock[--iCmd];
            dw <<= 8;
            dw |= pbBlock[--iCmd];
            dw <<= 8;
            dw |= pbBlock[--iCmd];
            dw <<= 8;
            dw |= pbBlock[--iCmd];

            if (bCmd < pHeader->m_abCmdCode[3])
            {
                // Diff
                dwDiff = dw;
                DWORD dwShift = 1;
                nBitCount = 0;
                for (int nBit = 0; nBit < 32; nBit++)
                {
                    if (dwDiff & (1 << nBit))
                    {
                        adwBitsMap[nBit] = dwShift;
                        dwShift <<= 1;
                        nBitCount++;
                    }
                    else
                    {
                        adwBitsMap[nBit] = 0;
                    }
                }
            }
            else
            {
                // Or
                dwOr = dw;
            }
        }
        else
        {
            // Big Pack
            nLen = (bCmd - pHeader->m_abCmdCode[4]) * 0x100 + pbBlock[--iCmd] + pHeader->m_abCmdCode[2] - pHeader->m_abCmdCode[1] + 1 + 1;
            nPickOffset = nBitOffset;
            nBitOffset += nBitCount;
        }

        if (nEndLen < nDoneLen + nLen)
            break;
    }

    if (SUCCEEDED(hr) &&
        nDoneLen < (int)READWINTELDWORD(pHeader->m_dwBlockSize))
    {
        const BYTE* const pbBuf = &pbBlock[nPickOffset / 8];
        DWORD dwCompBits = pbBuf[0] | (DWORD(pbBuf[1]) << 8) | (DWORD(pbBuf[2]) << 16) | (DWORD(pbBuf[3]) << 24);
        dwCompBits >>= nPickOffset % 8;
        if (nBitOffset % 8)
            dwCompBits |= pbBuf[4] << (32 - nBitOffset % 8);

        if (nBitCount < 32)
        {
            dwCompBits &= (1 << nBitCount) - 1;

            dwCodePages = 0;
            for (int nBit = 0; nBit < 32; nBit++)
            {
                if (dwCompBits & adwBitsMap[nBit])
                    dwCodePages |= (1 << nBit);
            }
        }
        else
        {
            dwCodePages = dwCompBits;
        }

        dwCodePages |= dwOr;
    }
    else
    {
        hr = E_FAIL; // Probably Code Pages data is broken.
    }

    if (pdwCodePages)
    {
        if (SUCCEEDED(hr))
        {
            if (dwFlags & CPBITS_WINDOWS)
            {
                // 04/07/00 WEIWU
                // Need to match latest NLS file (Currently W2K RTM) if we're in strict mode for outbound encoding detection
                // For backward compatibilities reasons, we don't want to change our raw Windows CP data.
                // We patch up major differences between Win95/NT4 NLS files and current NLS files here
                // For non-Windows CP data, we'll modify them directly since it doesn't affect text rendering
                if (dwFlags & CPBITS_STRICT)
                {
                    // Add Euro support for 936,950, 949 if we're in strict mode
                    if (chSrc == 0x20AC)
                    {
                        dwCodePages |=  FS_WANSUNG|FS_CHINESESIMP|FS_CHINESETRAD;
                    }
                    else if (chSrc == 0x00AE)
                    {
                        dwCodePages |= FS_WANSUNG;
                    }
                    // Clear K1_HANJA bits if we're in strict mode
                    dwCodePages &= ~FS_MLANG_K1HANJA;
                }
                else
                {
                    // We introduce this new internal charset bit, FS_MLANG_K1HANJA, to support Korean K1 Hanja
                    // K1 Hanja is defined in KSC 5657-1991, it contains non-cp949 DBCS characters
                    // Currenly, Korean fonts shipped with NT5 and Win98 support K1 Hanja glyphs 
                    // and we don't want to switch font to other DBCS fonts in this case.
                    if (dwCodePages & FS_MLANG_K1HANJA)
                    {
                        // Assume Korean font supports K1_HANJA on Win98 and NT5 
                        if (g_bIsNT5 || (g_bIsWin98 && CP_KOR_5601 == g_uACP))
                            dwCodePages |= FS_WANSUNG;
                        dwCodePages &= ~FS_MLANG_K1HANJA;
                    }            

                }
            }
            *pdwCodePages = dwCodePages;
        }
        else
        {
            *pdwCodePages = 0;
        }
    }

    return hr;
}   



HRESULT GetStrCodePagesEx(const WCHAR* pszSrc, long cchSrc, DWORD dwPriorityCodePages, DWORD* pdwCodePages, long* pcchCodePages, DWORD dwFlags)
{
    ASSERT_READ_BLOCK(pszSrc, cchSrc);
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);
    ASSERT_WRITE_PTR_OR_NULL(pcchCodePages);

    HRESULT hr = S_OK;
    long cchCodePages = 0;
    DWORD dwStrCodePages = (DWORD)~0;
    BOOL fInit = FALSE;
    BOOL fNoPri = FALSE;

    if (!pszSrc || cchSrc <= 0) // We can't make dwStrCodePages when cchSrc is zero
        hr = E_INVALIDARG;

    while (SUCCEEDED(hr) && cchSrc > 0)
    {
        DWORD dwCharCodePages;

        if (SUCCEEDED(hr = GetCharCodePagesEx(*pszSrc, &dwCharCodePages, dwFlags)))
        {
            if (!fInit)
            {
                fInit = TRUE;
                fNoPri = !(dwPriorityCodePages & dwCharCodePages);
            }
            else if (fNoPri != !(dwPriorityCodePages & dwCharCodePages))
            {
                break;
            }
            if (!fNoPri)
                dwPriorityCodePages &= dwCharCodePages;

            if (dwCharCodePages & dwStrCodePages)
                dwStrCodePages &= dwCharCodePages;
            // Don't break if dwCharCodePages is zero and we're not in strict mode
            else if (dwCharCodePages || dwFlags & CPBITS_STRICT) 
                     break;

            pszSrc++;
            cchSrc--;
            cchCodePages++;
        }
    }

    // Codepage bits defines don't take full 32 bits. 
    // If no bits are flipped, we don't have any candidate code pages, we should clear the bits
    if (dwStrCodePages == (DWORD)~0)
        dwStrCodePages = 0;


    if (SUCCEEDED(hr))
    {
        if (pcchCodePages)
            *pcchCodePages = cchCodePages;
        if (pdwCodePages)
            *pdwCodePages = dwStrCodePages;
    }
    else
    {
        if (pcchCodePages)
            *pcchCodePages = 0;
        if (pdwCodePages)
            *pdwCodePages = 0;
    }

    return hr;
}

STDMETHODIMP CMLFLink::GetStrCodePages(const WCHAR* pszSrc, long cchSrc, DWORD dwPriorityCodePages, DWORD* pdwCodePages, long* pcchCodePages)
{
    return ::GetStrCodePagesEx(pszSrc, cchSrc, dwPriorityCodePages, pdwCodePages, pcchCodePages, CPBITS_WINDOWS);
}

HRESULT CodePageToCodePagesEx(UINT uCodePage, DWORD* pdwCodePages, DWORD* pdwCodePagesExt)
{
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);

    int iCharSet;

    if (pdwCodePages)
        *pdwCodePages = 0;

    if (pdwCodePagesExt)
        *pdwCodePagesExt = 0;


    for (iCharSet = 0; g_CharSetTransTable[iCharSet].uCodePage; iCharSet++)
    {
        if (uCodePage == g_CharSetTransTable[iCharSet].uCodePage)
        {
            if (pdwCodePages)
                *pdwCodePages = g_CharSetTransTable[iCharSet].dwCodePages;
            return S_OK;
        }
    }

    for (iCharSet = 0; g_CharSetTransTableExt[iCharSet].uCodePage; iCharSet++)
    {
        if (uCodePage == g_CharSetTransTableExt[iCharSet].uCodePage)
        {
            if (pdwCodePages)
                *pdwCodePagesExt = g_CharSetTransTableExt[iCharSet].dwCodePages;
            return S_OK;
        }
    }

    return E_FAIL; // Unknown code page
}

STDMETHODIMP CMLFLink::CodePageToCodePages(UINT uCodePage, DWORD* pdwCodePages)
{
    ASSERT_THIS;
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);

    for (int iCharSet = 0; g_CharSetTransTable[iCharSet].uCodePage; iCharSet++)
    {
        if (uCodePage == g_CharSetTransTable[iCharSet].uCodePage)
        {
            if (pdwCodePages)
                *pdwCodePages = g_CharSetTransTable[iCharSet].dwCodePages;
            return S_OK;
        }
    }

    if (pdwCodePages)
        *pdwCodePages = 0;

    return E_FAIL; // Unknown code page
}

STDMETHODIMP CMLFLink::CodePagesToCodePage(DWORD dwCodePages, UINT uDefaultCodePage, UINT* puCodePage)
{
    return ::CodePagesToCodePageEx(dwCodePages, uDefaultCodePage, puCodePage, 0);
}

HRESULT CodePagesToCodePageEx(DWORD dwCodePages, UINT uDefaultCodePage, UINT* puCodePage, BOOL bCodePagesExt)
{
    ASSERT_WRITE_PTR_OR_NULL(puCodePage);

    HRESULT hr = E_FAIL; // Unknown code pages
    DWORD dwDefaultCodePages;

    if (uDefaultCodePage &&
        SUCCEEDED(hr = CodePageToCodePagesEx(uDefaultCodePage, &dwDefaultCodePages, NULL)) &&
        (dwDefaultCodePages & dwCodePages))
    {
        hr = S_OK;
    }
    else
    {
        if (bCodePagesExt)
        {
            for (int iCharSet = 0; g_CharSetTransTableExt[iCharSet].dwCodePages; iCharSet++)
            {
                if (dwCodePages & g_CharSetTransTableExt[iCharSet].dwCodePages)
                {
                    uDefaultCodePage = g_CharSetTransTableExt[iCharSet].uCodePage;
                    hr = S_OK;
                    break;
                }
            }

        }
        else
        {

            for (int iCharSet = 0; g_CharSetTransTable[iCharSet].dwCodePages; iCharSet++)
            {
                if (dwCodePages & g_CharSetTransTable[iCharSet].dwCodePages)
                {
                    uDefaultCodePage = g_CharSetTransTable[iCharSet].uCodePage;
                    hr = S_OK;
                    break;
                }
            }
        }
    }

    if (puCodePage)
    {
        if (SUCCEEDED(hr))
            *puCodePage = uDefaultCodePage;
        else
            *puCodePage = 0;
    }

    return hr;
}


#define REGSTR_PATH_FONTLINK TSZMICROSOFTPATH TEXT("\\Windows NT\\CurrentVersion\\FontLink\\SystemLink")

void CMLFLink::FreeFlinkTable(void)
{
    if (m_pFlinkTable)
    {
        for (UINT i=0; i<m_uiFLinkFontNum; i++)
            if (m_pFlinkTable[i].pmszFaceName)
                LocalFree(m_pFlinkTable[i].pmszFaceName);
        LocalFree(m_pFlinkTable);
        m_pFlinkTable = NULL;
        m_uiFLinkFontNum = 0;
    }
}

#define MAX_FONTLINK_BUFFER_SIZE    1024

HRESULT CMLFLink::CreateNT5FontLinkTable(void)
{
    HKEY    hKey = NULL;
    HKEY    hKeyFont = NULL;
    ULONG   ulFonts = 0;
    ULONG   ulFLinkFonts = 0;
    DWORD   dwIndex = 0;
    WCHAR   szFaceName[MAX_PATH];
    LPWSTR  pNewFaceName = NULL;
    DWORD   dwOffset = 0;
    DWORD   dwOffset2 = 0;
    DWORD   dwValue;
    DWORD   dwData;
    WCHAR   szFontFile[LF_FACESIZE];
    DWORD   dwType;
    WCHAR   szFlinkFont[MAX_FONTLINK_BUFFER_SIZE];
    

    // Internal temperate data
    struct tagFontTable
    {
        WCHAR szFontFile[LF_FACESIZE];
        WCHAR szFaceName[LF_FACESIZE];
    }* tmpFontTable = NULL;

    HRESULT hr;

    // Open system font and fontlink key
    if ((ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_FONTLINK, 0, KEY_READ, &hKey)) ||
        (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGFONTKEYNT, 0, KEY_READ, &hKeyFont)))
    {
        hr = E_FAIL;
        goto TABLE_DONE;
    }


    // Get number of items
    if ((ERROR_SUCCESS != RegQueryInfoKey(hKey, NULL, NULL, 0, NULL, 
         NULL, NULL, &ulFLinkFonts, NULL, NULL, NULL, NULL) || 0 == ulFLinkFonts) ||            
        (ERROR_SUCCESS != RegQueryInfoKey(hKeyFont, NULL, NULL, 0, NULL, 
            NULL, NULL, &ulFonts, NULL, NULL, NULL, NULL) || 0 == ulFonts))
    {
        hr = E_FAIL;
        goto TABLE_DONE;
    }

    tmpFontTable = (struct tagFontTable *)LocalAlloc(LPTR, sizeof(struct tagFontTable)*ulFonts);

    if (NULL == tmpFontTable)
    {
        hr = E_OUTOFMEMORY;
        goto TABLE_DONE;
    }

    dwValue = dwData = LF_FACESIZE;
    dwType = REG_SZ;
    dwIndex = 0;
    ulFonts = 0;    

    while (ERROR_NO_MORE_ITEMS != RegEnumValueW(
                      hKeyFont,
                      dwIndex++,
                      szFaceName,
                      &dwValue,
                      NULL,
                      &dwType,
                      (LPBYTE)szFontFile,
                      &dwData ))
    {
        //  TTF fonts only, TTC fonts already have face name under fontlink
        if (pNewFaceName = wcsstr(szFaceName, L" & "))
            break;
        pNewFaceName = wcsstr(szFaceName, L" (TrueType)");
        if(pNewFaceName)
        {
           *pNewFaceName = 0;
           MLStrCpyNW(tmpFontTable[ulFonts].szFaceName, szFaceName, LF_FACESIZE);
           MLStrCpyNW(tmpFontTable[ulFonts++].szFontFile, szFontFile, LF_FACESIZE);
        }

        dwValue = dwData = LF_FACESIZE;
        dwType = REG_SZ;
    }

    
    m_pFlinkTable = (PFLINKFONT) LocalAlloc(LPTR, sizeof(FLINKFONT)*ulFLinkFonts);
    if (NULL == m_pFlinkTable)
    {
        hr = E_OUTOFMEMORY;
        goto TABLE_DONE;
    }

    dwValue = LF_FACESIZE;
    dwData = MAX_FONTLINK_BUFFER_SIZE;
    dwType = REG_MULTI_SZ;

    dwIndex = 0;

    while (ERROR_NO_MORE_ITEMS != RegEnumValueW(
                      hKey,
                      dwIndex,
                      m_pFlinkTable[dwIndex].szFaceName,
                      &dwValue,
                      NULL,
                      &dwType,
                      (LPBYTE)szFlinkFont,
                      &dwData ))
    {
        m_pFlinkTable[dwIndex].pmszFaceName = (LPWSTR) LocalAlloc(LPTR, MAX_FONTLINK_BUFFER_SIZE); 
        if (!m_pFlinkTable[dwIndex].pmszFaceName)
        {
            hr = E_OUTOFMEMORY;
            goto TABLE_DONE;
        }
        while (TRUE)
        {
            pNewFaceName = wcsstr(&szFlinkFont[dwOffset], L",");
            
            if (pNewFaceName)   // TTC font, get face name from registry
            {
                MLStrCpyNW(&(m_pFlinkTable[dwIndex].pmszFaceName[dwOffset2]), ++pNewFaceName, LF_FACESIZE);
                dwOffset2 += lstrlenW(pNewFaceName)+1;
            }
            else                // TTF font, search font table for face name            
            {
                if (szFlinkFont[dwOffset])
                    for (UINT i=0; i<ulFonts; i++)
                    {
                        if (!MLStrCmpNIW(&szFlinkFont[dwOffset], tmpFontTable[i].szFontFile, LF_FACESIZE))
                        {
                            MLStrCpyNW(&(m_pFlinkTable[dwIndex].pmszFaceName[dwOffset2]), tmpFontTable[i].szFaceName, LF_FACESIZE);
                            dwOffset2 += lstrlenW(tmpFontTable[i].szFaceName)+1;
                            break;
                        }
                    }
                else            // End of multiple string, break out                    
                    break;
            }

            dwOffset += lstrlenW(&szFlinkFont[dwOffset])+1;

            // Prevent infinitive loop, shouldn't happen
            if (dwOffset >= MAX_FONTLINK_BUFFER_SIZE) 
            {
                break;
            }
        }

        dwValue = LF_FACESIZE;
        dwData = MAX_FONTLINK_BUFFER_SIZE;
        dwType = REG_MULTI_SZ;
        dwOffset = dwOffset2 = 0;
        dwIndex++;
    }

    m_uiFLinkFontNum = ulFLinkFonts;

    hr = S_OK;

TABLE_DONE:
    if (hKey)
        RegCloseKey(hKey);
    if (hKeyFont)
        RegCloseKey(hKeyFont);
    if (tmpFontTable)
        LocalFree(tmpFontTable);
    if ((hr != S_OK) && m_pFlinkTable)
        FreeFlinkTable();

    return hr;
}


HRESULT CMLFLink::GetNT5FLinkFontCodePages(HDC hDC, LOGFONTW* plfEnum, DWORD * lpdwCodePages)
{
    HRESULT hr = S_OK;
    UINT    i;

    if (!EnumFontFamiliesExW(hDC, plfEnum, GetFontCodePagesEnumFontProcW, (LPARAM)lpdwCodePages, 0))
        return E_FAIL;

    if (NULL == m_pFlinkTable)
        CreateNT5FontLinkTable();

    if (m_pFlinkTable)
    {
        for (i=0; i<m_uiFLinkFontNum;i++)
        {
            if (!MLStrCmpNIW(plfEnum->lfFaceName, m_pFlinkTable[i].szFaceName, LF_FACESIZE))
            {
                DWORD dwOffset=0;
                // Internal buffer, we're sure it'll end
                while(TRUE)
                {
                    MLStrCpyNW(plfEnum->lfFaceName, &m_pFlinkTable[i].pmszFaceName[dwOffset], LF_FACESIZE);
                    EnumFontFamiliesExW(hDC, plfEnum, GetFontCodePagesEnumFontProcW, (LPARAM)lpdwCodePages, 0);
                    dwOffset += lstrlenW(&m_pFlinkTable[i].pmszFaceName[dwOffset])+1;
                    // End of multiple string ?
                    if (m_pFlinkTable[i].pmszFaceName[dwOffset] == 0)
                        break;                        
                }
                break;
            }
        }
    }

    return hr;
}


/////////////////////////////////////////////////////////////////////////////
// CMLFLink : IMLangFontLink
// 1/29/99 - Change HR return
//        Now, we always return S_OK unless system error, caller will
//        check code pages bits in dwCodePages for font code page coverage
STDMETHODIMP CMLFLink::GetFontCodePages(HDC hDC, HFONT hFont, DWORD* pdwCodePages)
{
    ASSERT_THIS;
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);

    HRESULT hr = S_OK;
    LOGFONT lfFont;
    DWORD dwCodePages = 0;


    if (!::GetObject(hFont, sizeof(lfFont), &lfFont))
        hr = E_FAIL; // Invalid hFont

    if (SUCCEEDED(hr))
    {
        LOGFONT lfEnum;

        // Enumerates all character sets of given font's facename
        // Then, combines them in dwCodePages

        ::memset(&lfEnum, 0, sizeof(lfEnum));
        lfEnum.lfCharSet = DEFAULT_CHARSET;
        _tcsncpy(lfEnum.lfFaceName, lfFont.lfFaceName, ARRAYSIZE(lfEnum.lfFaceName));


        if (g_bIsNT5)
        {
            LOGFONTW lfEnumW = {0};
            lfEnumW.lfCharSet = DEFAULT_CHARSET;

            if (MultiByteToWideChar(g_uACP, 0, lfFont.lfFaceName, LF_FACESIZE, lfEnumW.lfFaceName, LF_FACESIZE))
                hr = GetNT5FLinkFontCodePages(hDC, &lfEnumW, &dwCodePages);
            else
                if (!::EnumFontFamiliesEx(hDC, &lfEnum, GetFontCodePagesEnumFontProc, (LPARAM)&dwCodePages, 0))
                    hr = E_FAIL; // Invalid hDC

        }
        else
        {
            if (!::EnumFontFamiliesEx(hDC, &lfEnum, GetFontCodePagesEnumFontProc, (LPARAM)&dwCodePages, 0))
                hr = E_FAIL; // Invalid hDC
        }

    }

//############################
//######  MingLiU HACK  ######
//## Fix the bogus font !!! ##
//############################
    if (SUCCEEDED(hr) && ::CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lfFont.lfFaceName, -1, _T("MingLiU"), -1) == 2)
    {
        dwCodePages &= ~FS_LATIN1; // Actually it doesn't have the characters of ANSI_CHARSET.
    }
//############################

// We should use following logic to replace above hack code 
// But, there is another DBCS<->Western font size mapping issue, we should disable this code until that issue is resolved,
#if 0

    // If font claims FE and 1252 and 1250, believe it can do 1252.
    // If font claims FE and 1252 and not 1250, don't believe it can do 1252.
    // This lets full Unicode fonts pass but blocks bad FE fonts.
    if (SUCCEEDED(hr) && (dwCodePages & (FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD)) && (dwCodePages & FS_LATIN1) && !(dwCodePages & FS_LATIN2))
    {
        dwCodePages &= ~FS_LATIN1;
    }
#endif

#ifdef UNICODE
#define PRC_DEFAULT_GUI_FONT L"\x5b8b\x4f53"
#else
#define PRC_DEFAULT_GUI_FONT "\xcb\xce\xcc\xe5"
#endif

    // PRC Win95 DEFAULT_GUI_FONT HACK !!!
    if (SUCCEEDED(hr) && lfFont.lfCharSet == ANSI_CHARSET && ::CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lfFont.lfFaceName, -1, PRC_DEFAULT_GUI_FONT, -1) == 2)
    {
        dwCodePages &= ~FS_CHINESESIMP; // Actually it doesn't have the characters of GB2321_CHARSET.
    }

    if (pdwCodePages)
    {
        if (SUCCEEDED(hr))
            *pdwCodePages = dwCodePages;
        else
            *pdwCodePages = 0;
    }

    return hr;
}

int CALLBACK CMLFLink::GetFontCodePagesEnumFontProc(const LOGFONT* plf, const TEXTMETRIC*, DWORD FontType, LPARAM lParam)
{
    for (int iCharSet = 0; g_CharSetTransTable[iCharSet].nCharSet != DEFAULT_CHARSET; iCharSet++)
    {
        if (plf->lfCharSet == g_CharSetTransTable[iCharSet].nCharSet)
        {
            if ((FontType == TRUETYPE_FONTTYPE) || 
                (g_CharSetTransTable[iCharSet].uCodePage == g_uACP))
            {
                *((DWORD*)lParam) |= g_CharSetTransTable[iCharSet].dwCodePages;
                break;
            }
        }
    }

    return TRUE;
}

int CALLBACK CMLFLink::GetFontCodePagesEnumFontProcW(const LOGFONTW* plf, const TEXTMETRICW*, DWORD FontType, LPARAM lParam)
{
    for (int iCharSet = 0; g_CharSetTransTable[iCharSet].nCharSet != DEFAULT_CHARSET; iCharSet++)
    {
        if (plf->lfCharSet == g_CharSetTransTable[iCharSet].nCharSet)
        {
            if ((FontType == TRUETYPE_FONTTYPE) || 
                (g_CharSetTransTable[iCharSet].uCodePage == g_uACP))
            {
                *((DWORD*)lParam) |= g_CharSetTransTable[iCharSet].dwCodePages;
                break;
            }
        }
    }

    return TRUE;
}


STDMETHODIMP CMLFLink::MapFont(HDC hDC, DWORD dwCodePages, HFONT hSrcFont, HFONT* phDestFont)
{
    ASSERT_THIS;
    ASSERT_WRITE_PTR_OR_NULL(phDestFont);

    HRESULT hr = S_OK;

    CFontMappingInfo fm; // To accelerate internal subroutine calls

    fm.hDC = hDC;

    // Font mapping cache works only for Display
    BOOL fDisplay = (::GetDeviceCaps(hDC, TECHNOLOGY) == DT_RASDISPLAY);

    dwCodePages &= ~FS_SYMBOL; // We don't map symbol font.

    if (!::GetObject(hSrcFont, sizeof(fm.lfSrcFont), &fm.lfSrcFont))
        hr = E_FAIL; // Invalid hSrcFont

    // Do two things at same time
    // (1) Find given font in the font mapping cache
    // (2) Build m_auCodePage[] and m_adwCodePages[]
    if (SUCCEEDED(hr))
    {
        if (fDisplay)
        {
            BYTE nCharSet = fm.lfSrcFont.lfCharSet;
            fm.lfSrcFont.lfCharSet = DEFAULT_CHARSET;
            EnumFontFamiliesEx(hDC, &fm.lfSrcFont, (FONTENUMPROC)VerifyFontSizeEnumFontProc, (LPARAM)&fm.lfSrcFont, 0);
            fm.lfSrcFont.lfCharSet = nCharSet;
        }

        hr = S_FALSE; // hr == S_FALSE means that we didn't find the font in the cache
        for (int n = 0; n < 32 && dwCodePages; n++)
        {
            hr = CodePagesToCodePage(dwCodePages, 0, &fm.auCodePage[n]); // Pick one of CodePages

            if (SUCCEEDED(hr))
                hr = CodePageToCodePages(fm.auCodePage[n], &fm.adwCodePages[n]);

            if (SUCCEEDED(hr))
            {
                if (fDisplay && m_pFontMappingCache)
                    hr = m_pFontMappingCache->FindEntry(fm.auCodePage[n], fm.lfSrcFont, &fm.hDestFont);
                else
                    hr = S_FALSE;
            }

            if (hr != S_FALSE)
                break;

            dwCodePages &= ~fm.adwCodePages[n];
        }
        fm.auCodePage[n] = NULL; // End mark
        fm.adwCodePages[n] = 0;
    }

    if (hr == S_FALSE) // Not exist in cache
    {

        hr = MapFontCodePages(fm, GetFaceNameRegistry);

        if (hr == MLSTR_E_FACEMAPPINGFAILURE)
            hr = MapFontCodePages(fm, GetFaceNameGDI);

        // Handle font link failure case for NT5
        if (hr == MLSTR_E_FACEMAPPINGFAILURE && g_bIsNT5)
            hr = MapFontCodePages(fm, GetFaceNameMIME);

        if (SUCCEEDED(hr) && fDisplay && m_pFontMappingCache)
            hr = m_pFontMappingCache->AddEntry(fm.auCodePage[fm.iCP], fm.lfSrcFont, fm.hDestFont);
    }

    if (phDestFont)
    {
        if (SUCCEEDED(hr))
        {            
            *phDestFont = fm.hDestFont;
            fm.hDestFont = NULL; // Avoid being deleted it in destructor
        }
        else
        {
            *phDestFont = NULL;
        }
    }

    return hr;
}

STDMETHODIMP CMLFLink::ReleaseFont(HFONT hFont)
{
    ASSERT_THIS;

    HRESULT hr = S_OK;

    if (!m_pFontMappingCache || FAILED(hr = m_pFontMappingCache->UnlockEntry(hFont)))
    {
        // For non display DC
        if (::DeleteObject(hFont))
            hr = S_OK;
        else
            hr = E_FAIL; // Invalid hFont
    }
    return hr;
}

STDMETHODIMP CMLFLink::ResetFontMapping(void)
{
    ASSERT_THIS;
    HRESULT hr = S_OK;

    if (m_pFontMappingCache)
        hr =  m_pFontMappingCache->FlushEntries();

    return hr;
}

STDMETHODIMP CMLFLink2::ResetFontMapping(void)
{
    ASSERT_THIS;
    HRESULT hr = S_OK;

    if (m_pIMLFLnk)
        hr =  m_pIMLFLnk->ResetFontMapping();

    if (m_pFontMappingCache2)
        hr = (S_OK == m_pFontMappingCache2->EnsureFontTable(FALSE)? hr : E_FAIL);

    return hr;
}

HRESULT CMLFLink::MapFontCodePages(CFontMappingInfo& fm, PFNGETFACENAME pfnGetFaceName)
{
    HRESULT hr = MLSTR_E_FACEMAPPINGFAILURE;    

    for (fm.iCP = 0; fm.auCodePage[fm.iCP]; fm.iCP++)
    {
        fm.lfDestFont.lfCharSet = DEFAULT_CHARSET;

        hr = (this->*pfnGetFaceName)(fm);

        if (SUCCEEDED(hr))
        {
            LOGFONT lf = {0};

            // If face name is from registry or MIMEDB, we set charset to codepage charset.
            if (fm.lfDestFont.lfCharSet == DEFAULT_CHARSET)
            {
                for (int iCharSet = 0; g_CharSetTransTable[iCharSet].uCodePage; iCharSet++)
                {
                    if (fm.auCodePage[fm.iCP] == g_CharSetTransTable[iCharSet].uCodePage)
                    {
                        fm.lfDestFont.lfCharSet = (BYTE)g_CharSetTransTable[iCharSet].nCharSet;
                        break;
                    }
                }
            }

            lf.lfCharSet = DEFAULT_CHARSET;
            MLStrCpyN(lf.lfFaceName, fm.szFaceName, LF_FACESIZE);

            // Retrieve LOGFONT from gotten facename
            fm.lfDestFont.lfFaceName[0] = _T('\0');

            if (!::EnumFontFamiliesEx(fm.hDC, &lf, MapFontEnumFontProc, (LPARAM)&fm.lfDestFont, 0))
                hr = E_FAIL; // Invalid hDC
            else if (fm.lfDestFont.lfFaceName[0] == _T('\0'))
                hr = MLSTR_E_FACEMAPPINGFAILURE;
        }

        if (SUCCEEDED(hr))
        {
            fm.lfDestFont.lfHeight      = fm.lfSrcFont.lfHeight;
            fm.lfDestFont.lfWidth       = fm.lfSrcFont.lfWidth;
            fm.lfDestFont.lfEscapement  = fm.lfSrcFont.lfEscapement;
            fm.lfDestFont.lfOrientation = fm.lfSrcFont.lfOrientation;
            fm.lfDestFont.lfWeight      = fm.lfSrcFont.lfWeight;
            fm.lfDestFont.lfItalic      = fm.lfSrcFont.lfItalic;
            fm.lfDestFont.lfUnderline   = fm.lfSrcFont.lfUnderline;
            fm.lfDestFont.lfStrikeOut   = fm.lfSrcFont.lfStrikeOut;


            HRESULT hrTemp = VerifyFaceMap(fm);
            if (hrTemp == MLSTR_E_FACEMAPPINGFAILURE && fm.lfDestFont.lfWidth)
            {
                fm.lfDestFont.lfWidth = 0; // To recover non-scalable font
                hr = VerifyFaceMap(fm);
            }
            else
            {
                hr = hrTemp;
            }
        }

        if (hr != MLSTR_E_FACEMAPPINGFAILURE)
            break;
    }

    return hr;
}
    
int CALLBACK CMLFLink::MapFontEnumFontProc(const LOGFONT* plfFont, const TEXTMETRIC*, DWORD, LPARAM lParam)
{
    LOGFONT* plfDestFont = (LOGFONT*)lParam;
    
    if (!plfDestFont->lfFaceName[0] )
    {
        if (plfDestFont->lfCharSet != DEFAULT_CHARSET)
        {
            if (plfDestFont->lfCharSet == plfFont->lfCharSet)
                *plfDestFont = *plfFont;
        }
        else
              *plfDestFont = *plfFont;
    }

    return TRUE;
}

HRESULT CMLFLink::GetFaceNameRegistry(CFontMappingInfo& fm)
{
    static const TCHAR szRootKey[]       = _T("Software\\Microsoft\\Internet Explorer");
    static const TCHAR szIntlKey[]       = _T("International\\%d");
    static const TCHAR szPropFontName[]  = _T("IEPropFontName");
    static const TCHAR szFixedFontName[] = _T("IEFixedFontName");

    HRESULT hr = S_OK;
    HKEY hKeyRoot;

    if (::RegOpenKeyEx(HKEY_CURRENT_USER, szRootKey, 0, KEY_READ, &hKeyRoot) == ERROR_SUCCESS)
    {
        TCHAR szCodePageKey[ARRAYSIZE(szIntlKey) + 10];
        HKEY hKeySub;

        ::wsprintf(szCodePageKey, szIntlKey, fm.auCodePage[fm.iCP]);
        if (::RegOpenKeyEx(hKeyRoot, szCodePageKey, 0, KEY_READ, &hKeySub) == ERROR_SUCCESS)
        {
            const TCHAR* pszFontNameValue;
            DWORD dwType;
            DWORD dwSize = sizeof(fm.szFaceName);

            if ((fm.lfSrcFont.lfPitchAndFamily & 0x03) == FIXED_PITCH)
                pszFontNameValue = szFixedFontName;
            else
                pszFontNameValue = szPropFontName;

            if (::RegQueryValueEx(hKeySub, pszFontNameValue, 0, &dwType, (LPBYTE)fm.szFaceName, &dwSize) != ERROR_SUCCESS)
                hr = MLSTR_E_FACEMAPPINGFAILURE;

            if (::RegCloseKey(hKeySub) != ERROR_SUCCESS && SUCCEEDED(hr))
                hr = MLSTR_E_FACEMAPPINGFAILURE;
        }
        else
        {
            hr = MLSTR_E_FACEMAPPINGFAILURE;
        }

        if (::RegCloseKey(hKeyRoot) != ERROR_SUCCESS && SUCCEEDED(hr))
            hr = MLSTR_E_FACEMAPPINGFAILURE;
    }
    else
    {
        hr = MLSTR_E_FACEMAPPINGFAILURE;
    }
    return hr;
}

HRESULT CMLFLink::GetFaceNameGDI(CFontMappingInfo& fm)
{
    HRESULT hr = S_OK;

    for (int iCharSet = 0; g_CharSetTransTable[iCharSet].uCodePage; iCharSet++)
    {
        if (fm.auCodePage[fm.iCP] == g_CharSetTransTable[iCharSet].uCodePage)
            break;
    }

    if (g_CharSetTransTable[iCharSet].uCodePage)
    {
        ::memset(&fm.lfDestFont, 0, sizeof(fm.lfDestFont));

        // Specify font weight as NORMAL to avoid NT GDI font mapping bugs
        fm.lfDestFont.lfWeight = FW_NORMAL;
        fm.lfDestFont.lfCharSet = (BYTE)g_CharSetTransTable[iCharSet].nCharSet;
        hr = GetFaceNameRealizeFont(fm);
    }
    else
    {
        hr = E_FAIL; // Unknown code page
    }

    if (SUCCEEDED(hr))
    {
        // Height, CharSet, Pitch and Family
        fm.lfDestFont.lfHeight = fm.lfSrcFont.lfHeight;
        fm.lfDestFont.lfPitchAndFamily = fm.lfSrcFont.lfPitchAndFamily;
        hr = GetFaceNameRealizeFont(fm);

        if (FAILED(hr))
        {
            // CharSet, Pitch and Family
            fm.lfDestFont.lfHeight = 0;
            hr = GetFaceNameRealizeFont(fm);
        }

        if (FAILED(hr))
        {
            // CharSet and Pitch
            fm.lfDestFont.lfPitchAndFamily &= 0x03; // Pitch Mask
            hr = GetFaceNameRealizeFont(fm);
        }

        if (FAILED(hr))
        {
            // CharSet only
            fm.lfDestFont.lfPitchAndFamily = 0;
            hr = GetFaceNameRealizeFont(fm);
        }
    }

    return hr;
}

HRESULT CMLFLink::GetFaceNameMIME(CFontMappingInfo& fm)
{
    HRESULT hr = E_FAIL;
    MIMECPINFO cpInfo;    

    if (fm.auCodePage[fm.iCP] == 936)
    {
        MLStrCpyN(fm.szFaceName, TEXT("SimSun"), LF_FACESIZE);
        return S_OK;
    }

    if (!g_pMimeDatabase)
        BuildGlobalObjects();


    if (NULL != g_pMimeDatabase)
    {
        if (SUCCEEDED(g_pMimeDatabase->GetCodePageInfo(fm.auCodePage[fm.iCP], 0x409, &cpInfo)))
        {
            TCHAR szFontFaceName[LF_FACESIZE];
            szFontFaceName[0] = 0;

            if ((fm.lfSrcFont.lfPitchAndFamily & 0x03) == FIXED_PITCH && cpInfo.wszFixedWidthFont[0])
            {
#ifdef UNICODE
                MLStrCpyNW(szFontFaceName, cpInfo.wszFixedWidthFont, LF_FACESIZE);
#else
                WideCharToMultiByte(CP_ACP, 0, cpInfo.wszFixedWidthFont, -1, szFontFaceName, LF_FACESIZE, NULL, NULL);
#endif
            }
            else
                if (cpInfo.wszProportionalFont[0])
                {
#ifdef UNICODE
                    MLStrCpyNW(szFontFaceName, cpInfo.wszProportionalFont, LF_FACESIZE);
#else
                    WideCharToMultiByte(CP_ACP, 0, cpInfo.wszProportionalFont, -1, szFontFaceName, LF_FACESIZE, NULL, NULL);
#endif
                }

            if (szFontFaceName[0])
            {
                MLStrCpyN(fm.szFaceName, szFontFaceName, LF_FACESIZE);
                hr = S_OK;
            }
        }
        else
            hr = MLSTR_E_FACEMAPPINGFAILURE;
    }

    return hr;
}

HRESULT CMLFLink::GetFaceNameRealizeFont(CFontMappingInfo& fm)
{
    HRESULT hr = S_OK;
    HFONT hFont = NULL;
    HFONT hOldFont;
    DWORD dwFontCodePages;

    // First let's get a facename based on the given lfDestFont
    // Then verify if the font of the found facename has the code pages we want.

    hFont = ::CreateFontIndirect(&fm.lfDestFont);
    if (!hFont)
        hr = E_FAIL; // Out of memory or GDI resource

    if (SUCCEEDED(hr))
    {
        hOldFont = (HFONT)::SelectObject(fm.hDC, hFont);
        if (!hOldFont)
            hr = E_FAIL; // Out of memory or GDI resource
    }

    if (SUCCEEDED(hr))
    {
        if (!::GetTextFace(fm.hDC, ARRAYSIZE(fm.szFaceName), fm.szFaceName))
            hr = E_FAIL; // Out of memory or GDI resource

        if (!::SelectObject(fm.hDC, hOldFont) && SUCCEEDED(hr))
            hr = E_FAIL; // Out of memory or GDI resource
    }

    if (hFont)
        ::DeleteObject(hFont);

    if (SUCCEEDED(hr))
    {
        LOGFONT lfTemp;

        lfTemp = fm.lfDestFont;
        _tcsncpy(lfTemp.lfFaceName, fm.szFaceName, ARRAYSIZE(lfTemp.lfFaceName));

        hFont = ::CreateFontIndirect(&lfTemp);
        if (!hFont)
            hr = E_FAIL; // Out of memory or GDI resource

        if (SUCCEEDED(hr = GetFontCodePages(fm.hDC, hFont, &dwFontCodePages)) && !(dwFontCodePages & fm.adwCodePages[fm.iCP]))
                hr = MLSTR_E_FACEMAPPINGFAILURE;

        if (hFont)
            ::DeleteObject(hFont);
    }

    return hr;
}

HRESULT CMLFLink::VerifyFaceMap(CFontMappingInfo& fm)
{
    HRESULT hr = S_OK;
    HFONT hOldFont;

    if (fm.hDestFont)
        ::DeleteObject(fm.hDestFont);

    fm.hDestFont = ::CreateFontIndirect(&fm.lfDestFont);
    if (!fm.hDestFont)
        hr = E_FAIL; // Out of memory or GDI resource

    if (SUCCEEDED(hr))
    {
        hOldFont = (HFONT)::SelectObject(fm.hDC, fm.hDestFont);
        if (!hOldFont)
            hr = E_FAIL; // Out of memory or GDI resource
    }

    if (SUCCEEDED(hr))
    {
        TCHAR szFaceName[LF_FACESIZE];

        if (!::GetTextFace(fm.hDC, ARRAYSIZE(szFaceName), szFaceName))
            hr = E_FAIL; // Out of memory or GDI resource

        if (SUCCEEDED(hr))
        {
            int nRet = ::CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, fm.lfDestFont.lfFaceName, -1, szFaceName, -1);
            if (!nRet)
                hr = E_FAIL; // Unexpected error
            else if (nRet != 2) // Not Equal
                hr = MLSTR_E_FACEMAPPINGFAILURE;
        }

        if (!::SelectObject(fm.hDC, hOldFont) && SUCCEEDED(hr))
            hr = E_FAIL; // Out of memory or GDI resource
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////////
// CMLFLink::CFontMappingCache

CMLFLink::CFontMappingCache::CFontMappingCache(void) :
    m_pEntries(NULL),
    m_pFree(NULL),
    m_cEntries(0)
{
    ::InitializeCriticalSection(&m_cs);
}

CMLFLink::CFontMappingCache::~CFontMappingCache(void)
{
    FlushEntries();
    DeleteCriticalSection(&m_cs);
}

HRESULT CMLFLink::CFontMappingCache::FindEntry(UINT uCodePage, const LOGFONT& lfSrcFont, HFONT* phDestFont)
{
    HRESULT hr = S_FALSE;

    ::EnterCriticalSection(&m_cs);

    if (m_pEntries)
    {
        CFontMappingCacheEntry* pEntry = m_pEntries;

        while ((pEntry = pEntry->m_pPrev) != m_pEntries)
        {
            if (uCodePage == pEntry->m_uSrcCodePage &&
                lfSrcFont.lfPitchAndFamily == pEntry->m_bSrcPitchAndFamily &&
                lfSrcFont.lfHeight == pEntry->m_lSrcHeight &&
                lfSrcFont.lfWidth == pEntry->m_lSrcWidth &&
                lfSrcFont.lfEscapement == pEntry->m_lSrcEscapement &&
                lfSrcFont.lfOrientation == pEntry->m_lSrcOrientation &&
                lfSrcFont.lfWeight == pEntry->m_lSrcWeight &&
                lfSrcFont.lfItalic == pEntry->m_bSrcItalic &&
                lfSrcFont.lfUnderline == pEntry->m_bSrcUnderline &&
                lfSrcFont.lfStrikeOut == pEntry->m_bSrcStrikeOut)
            {
                int nRet = ::CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lfSrcFont.lfFaceName, -1, pEntry->m_szSrcFaceName, -1);
                if (!nRet)
                {
                    hr = E_FAIL; // Unexpected error
                    break;
                }
                else if (nRet == 2) // Equal
                {
                    if (phDestFont)
                        *phDestFont = pEntry->m_hDestFont;
                    pEntry->m_nLockCount++;
                    hr = S_OK;
                    break;
                }
            }
        }
    }

    ::LeaveCriticalSection(&m_cs);

    if (phDestFont && hr != S_OK)
        *phDestFont = NULL;

    return hr;
}

HRESULT CMLFLink::CFontMappingCache::UnlockEntry(HFONT hDestFont)
{
    HRESULT hr = E_FAIL; // hDestFont is not found in the cache

    ::EnterCriticalSection(&m_cs);

    if (m_pEntries)
    {
        CFontMappingCacheEntry* pEntry = m_pEntries;

        while ((pEntry = pEntry->m_pPrev) != m_pEntries)
        {
            if (hDestFont == pEntry->m_hDestFont)
            {
                if (pEntry->m_nLockCount - 1 >= 0)
                {
                    pEntry->m_nLockCount--;
                    hr = S_OK;
                }
                break;
            }
        }
    }

    ::LeaveCriticalSection(&m_cs);

    return hr;
}

HRESULT CMLFLink::CFontMappingCache::AddEntry(UINT uCodePage, const LOGFONT& lfSrcFont, HFONT hDestFont)
{
    HRESULT hr = S_OK;

    ::EnterCriticalSection(&m_cs);

    if (!m_pEntries) // Need to allocate all the entries
    {
        CFontMappingCacheEntry* pEntries;

        pEntries = new CFontMappingCacheEntry[NUMFONTMAPENTRIES + 1]; // +1 for sentinel

        if (pEntries)
        {
            // Init sentinel
            pEntries[0].m_pPrev = &pEntries[0];
            pEntries[0].m_pNext = &pEntries[0];

            // Init free entries
            for (int n = 0; n < NUMFONTMAPENTRIES; n++)
            {
                const nEnt = n + 1; // + 1 for sentinel

                if (n < NUMFONTMAPENTRIES - 1)
                    pEntries[nEnt].m_pNext = &pEntries[nEnt + 1];
                else
                    pEntries[nEnt].m_pNext = NULL;
            }

            m_pEntries = &pEntries[0];
            m_pFree = &pEntries[1];
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr) && !m_pFree) // Need to delete oldest entry
    {
        CFontMappingCacheEntry* pOldestEntry = m_pEntries->m_pPrev;

        while (pOldestEntry->m_nLockCount > 0 && pOldestEntry != m_pEntries) // Entry is locked
            pOldestEntry = pOldestEntry->m_pPrev;

        if (pOldestEntry != m_pEntries)
        {
            if (pOldestEntry->m_hDestFont)
                ::DeleteObject(pOldestEntry->m_hDestFont);

            // Delete it from m_pEntries list
            pOldestEntry->m_pPrev->m_pNext = pOldestEntry->m_pNext;
            pOldestEntry->m_pNext->m_pPrev = pOldestEntry->m_pPrev;

            // Insert it into m_pFree list
            pOldestEntry->m_pNext = m_pFree;
            m_pFree = pOldestEntry;
        }
        else // No entry available
        {
            hr = E_FAIL; // Out of cache entries
        }
    }

    if (SUCCEEDED(hr)) // Create new entry and fill it
    {
        CFontMappingCacheEntry* pNewEntry;

        // Delete it from m_pFree list
        pNewEntry = m_pFree; // shouldn't be NULL
        m_pFree = pNewEntry->m_pNext;

        // Insert it into m_pEntries list
        pNewEntry->m_pNext = m_pEntries->m_pNext;
        pNewEntry->m_pPrev = m_pEntries;
        m_pEntries->m_pNext->m_pPrev = pNewEntry;
        m_pEntries->m_pNext = pNewEntry;

        // Fill it
        pNewEntry->m_nLockCount = 1;
        pNewEntry->m_uSrcCodePage = uCodePage;
        pNewEntry->m_lSrcHeight = lfSrcFont.lfHeight;
        pNewEntry->m_lSrcWidth = lfSrcFont.lfWidth;
        pNewEntry->m_lSrcEscapement = lfSrcFont.lfEscapement;
        pNewEntry->m_lSrcOrientation = lfSrcFont.lfOrientation;
        pNewEntry->m_lSrcWeight = lfSrcFont.lfWeight;
        pNewEntry->m_bSrcItalic = lfSrcFont.lfItalic;
        pNewEntry->m_bSrcUnderline = lfSrcFont.lfUnderline;
        pNewEntry->m_bSrcStrikeOut = lfSrcFont.lfStrikeOut;
        pNewEntry->m_bSrcPitchAndFamily = lfSrcFont.lfPitchAndFamily;
        _tcsncpy(pNewEntry->m_szSrcFaceName, lfSrcFont.lfFaceName, ARRAYSIZE(pNewEntry->m_szSrcFaceName));
        pNewEntry->m_hDestFont = hDestFont;
    }

    ::LeaveCriticalSection(&m_cs);

    return hr;
}

HRESULT CMLFLink::CFontMappingCache::FlushEntries(void)
{
    ::EnterCriticalSection(&m_cs);

    if (m_pEntries)
    {
        CFontMappingCacheEntry* pEntry = m_pEntries;

        while ((pEntry = pEntry->m_pPrev) != m_pEntries)
        {
            if (pEntry->m_hDestFont)
                ::DeleteObject(pEntry->m_hDestFont);
        }

        delete[] m_pEntries;

        m_pEntries = NULL;
        m_cEntries = 0;
    }

    ::LeaveCriticalSection(&m_cs);

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CMLFLink::CCodePagesCache

CMLFLink::CCodePagesCache::CCodePagesCache(void) :
    m_pbBuf(NULL),m_pbBufExt(NULL)
{
    ::InitializeCriticalSection(&m_cs);
}

CMLFLink::CCodePagesCache::~CCodePagesCache(void)
{
    DeleteCriticalSection(&m_cs);
}

HRESULT CMLFLink::CCodePagesCache::RealLoad(void)
{
    HRESULT hr = S_OK;

    ::EnterCriticalSection(&m_cs);

    if (!m_pbBuf && !m_pbBufExt)
    {
        HRSRC hrCodePages;
        HGLOBAL hgCodePages;
        HRSRC hrCodePagesExt;
        HGLOBAL hgCodePagesExt;


        if (SUCCEEDED(hr))
        {
            hrCodePages = ::FindResource(g_hInst, MAKEINTRESOURCE(IDR_CODEPAGES), _T("CODEPAGES"));
            hrCodePagesExt = ::FindResource(g_hInst, MAKEINTRESOURCE(IDR_CODEPAGESEXT), _T("CODEPAGESEXT"));
            if (!hrCodePages || !hrCodePagesExt)
                hr = E_FAIL; // Build error?
        }
        if (SUCCEEDED(hr))
        {
            hgCodePages = ::LoadResource(g_hInst, hrCodePages);
            hgCodePagesExt = ::LoadResource(g_hInst, hrCodePagesExt);
            if (!hgCodePages && !hgCodePagesExt)
                hr = E_FAIL; // Unexpected error
        }
        if (SUCCEEDED(hr))
        {
            m_pbBuf = (BYTE*)::LockResource(hgCodePages);
            m_pbBufExt = (BYTE*)::LockResource(hgCodePagesExt);
            if (!m_pbBuf || !m_pbBufExt)
                hr = E_FAIL; // Unexpected error
        }
    }

    ::LeaveCriticalSection(&m_cs);

    return hr;
}

extern "C" HRESULT GetGlobalFontLinkObject(IMLangFontLink **ppMLFontLink)
{
    HRESULT hr = E_INVALIDARG;

    if (NULL != ppMLFontLink)
    {
        if (NULL == g_pMLFLink)
        {
            EnterCriticalSection(&g_cs);
            if (NULL == g_pMLFLink)
                CComCreator< CComPolyObject< CMLFLink > >::CreateInstance(NULL, IID_IMLangFontLink, (void **)&g_pMLFLink);
            LeaveCriticalSection(&g_cs);
        }
        *ppMLFontLink = g_pMLFLink;
        if (g_pMLFLink)
        {
            g_pMLFLink->AddRef();
            hr = S_OK;
        }
        else
            hr = E_FAIL;
    }
    return hr;
}

HRESULT CMLFLink2::CFontMappingCache2::MapFontFromCMAP(HDC hDC, WCHAR wchar, HFONT hSrcFont, HFONT *phDestFont)
{
    BOOL    bFont = FALSE;
    HRESULT hr = E_FAIL;
    int     i,j,k;
    LOGFONT LogFont;

    if (!phDestFont)
        return E_INVALIDARG;

    if (!GetObject(hSrcFont, sizeof(LOGFONT), &LogFont))
        return hr;

    if (!g_pfont_table || !g_pfont_table[0].szFaceName[0])
    {        
        if (FAILED(LoadFontDataFile()))
        {
            return hr;
        }
    }

    i=0;
    j=ARRAYSIZE(g_urange_table);
    k = j/2;

    while (i<=j)
    {

        if (wchar >= g_urange_table[k].wcFrom && wchar <= g_urange_table[k].wcTo)
           break;
        else
           if (wchar < g_urange_table[k].wcFrom)
           {
               j = k -1;
           }
           else
           {
               i = k + 1;
           }
           k = (i+j)/2;
    }

    if (i<=j && g_urange_table[k].nFonts)
    {
        TCHAR szFaceName[LF_FACESIZE];
        GetTextFace(hDC, LF_FACESIZE, szFaceName);

        // Check if it supports the character
        for (i=0; i<g_urange_table[k].nFonts; i++)
        {
            if (!MLStrCmpI(szFaceName,g_pfont_table[*(g_urange_table[k].pFontIndex+i)].lf.lfFaceName))
                break;
        }

        // Current font doesn't support this character
        if (i == g_urange_table[k].nFonts)
        {
            for (i=0; i<g_urange_table[k].nFonts; i++)
            {
                if (LogFont.lfCharSet == g_pfont_table[*(g_urange_table[k].pFontIndex+i)].lf.lfCharSet)
                    break;
            }

            // No font available for current CharSet, then return the first one in the list            
            if (i >= g_urange_table[k].nFonts)
            {
                i = fetchCharSet((BYTE *) &(LogFont.lfCharSet), k);
            }

            MLStrCpyN(LogFont.lfFaceName, g_pfont_table[*(g_urange_table[k].pFontIndex+i)].lf.lfFaceName, LF_FACESIZE);
        }

        if (i < g_urange_table[k].nFonts)
        {
            MLStrCpyN(LogFont.lfFaceName, g_pfont_table[*(g_urange_table[k].pFontIndex+i)].lf.lfFaceName, LF_FACESIZE);
        }
        
        bFont = TRUE;
    } 

    if (bFont && (*phDestFont = CreateFontIndirect(&LogFont)))
    {
        hr = S_OK;       
    }
    else
    {
        *phDestFont = NULL;
    }


    return hr;
}


HRESULT CMLFLink2::CFontMappingCache2::UnicodeRanges(
    LPTSTR  szFont,
    UINT    *puiRanges, 
    UNICODERANGE* pURanges
    )

{
    HRESULT hr = E_FAIL;
    UINT    nURange = 0;
    DWORD   cmap    = 0;
    DWORD   name    = 0;
    HANDLE  hTTF;    
    TCHAR   szFontPath[MAX_PATH];
    static TCHAR s_szFontDir[MAX_PATH] = {0};


    HANDLE  hTTFMap;
    DWORD   dwFileSize;
    LPVOID  lpvFile = NULL;
    LPBYTE  lp, lp1, lp2, lpMax = NULL;
    DWORD   Num;
    WORD    i, j, Len;

    if (!szFont[0])
        return hr;

    if (!s_szFontDir[0])
    {
        MLGetWindowsDirectory(s_szFontDir, MAX_PATH);
        MLPathCombine(s_szFontDir, s_szFontDir, FONT_FOLDER);
    }

    MLPathCombine(szFontPath, s_szFontDir, szFont);

    hTTF = CreateFile(  szFontPath,             // pointer to name of the file
                        GENERIC_READ,           // access (read-write) mode
                        FILE_SHARE_READ,        // share mode
                        NULL,                   // pointer to security attributes
                        OPEN_EXISTING,          // how to create
                        FILE_ATTRIBUTE_NORMAL,  // file attributes
                        NULL);                  // handle to file with attributes to copy;

    if (INVALID_HANDLE_VALUE == hTTF)
        return hr;

    dwFileSize = GetFileSize(hTTF, NULL);


    hTTFMap = CreateFileMapping(
                  hTTF,
                  NULL,
                  PAGE_READONLY,
                  0,
                  dwFileSize,
                  NULL
              );

    if(hTTFMap == NULL)
    {
        goto CloseHandle0;
    }

    lpvFile = MapViewOfFile(
                  hTTFMap,
                  FILE_MAP_READ,
                  0,
                  0,
                  0
              );

    if(lpvFile == NULL)
    {
        goto CloseHandle;
    }

    lp = (LPBYTE)lpvFile;
    // The maximum boundary we can go
    lpMax = (LPBYTE)lpvFile + dwFileSize;

    // Font table name uses ASCII
    if(strncmp(((TTC_HEAD*)lp)->TTCTag, "ttcf", 4) == 0)   // TTC format
    {
        lp += FOUR_BYTE_NUM(((TTC_HEAD*)lp)->OffsetTTF1);  // points to first TTF
    }

    Num = TWO_BYTE_NUM(((TTF_HEAD*)lp)->NumTables);        // Number of Tables
    lp += sizeof(TTF_HEAD);

    if (lp+Num*sizeof(TABLE_DIR) >= lpMax) // Not a valid TrueType file if table size >= TTF file size
        goto CloseHandle;

    for(i = 0; i < Num ; i++)   // go thru all tables to find cmap and name
    {
        if(strncmp( ((TABLE_DIR*)lp)->Tag, "cmap", 4) == 0)
        {
            cmap = FOUR_BYTE_NUM(((TABLE_DIR*)lp)->Offset);
            if (name) break;
        }
        else if(strncmp( ((TABLE_DIR*)lp)->Tag, "name", 4) == 0)
        {
            name = FOUR_BYTE_NUM(((TABLE_DIR*)lp)->Offset);
            if (cmap) break;
        }
        lp += sizeof(TABLE_DIR);

     }

    if((!cmap) || (!name))    // Can't find cmap or name
    {
        goto CloseHandle;
    }

    // Read thru all name records
    // to see if font subfamily name is "Regular"

    lp  = (LPBYTE)lpvFile + name;                   // point to name table
    Num = TWO_BYTE_NUM(((NAME_TABLE*)lp)->NumRec);  // # of name record

    if (lp + sizeof(NAME_TABLE)*Num >= lpMax)
        goto CloseHandle;

    lp1 = lp  + sizeof(NAME_TABLE);                 // point to name record


    for(i = 0; i < Num; i++)
    {
        if(FONT_SUBFAMILY_NAME == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->NameID))
        {
            lp2 = lp +                              // point to string store
                  TWO_BYTE_NUM(((NAME_TABLE* )lp )->Offset) +
                  TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Offset);

            // Invalid TTF file
            if (lp2 >= lpMax)
                break;

            Len = TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Length);

            if(((MICROSOFT_PLATFORM == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Platform)) && 
                (UNICODE_INDEXING == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Encoding)))  ||
               ((APPLE_UNICODE_PLATFORM == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Platform)) && 
                (APPLE_UNICODE_INDEXING == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Encoding))))  
            {
                Len >>= 1;
                const char *pStr = szRegular;

                if (Len == sizeof(szNormal) -1)
                    pStr = szNormal;
                else 
                    if (Len != sizeof(szRegular)-1)
                    {
                        lp1 += sizeof(NAME_RECORD);
                        continue;
                    }

                while(--Len > 0)
                {
                    if(*(lp2+(Len<<1)+1) != pStr[Len])
                    break;
                }

                if (!Len)
                    break;
                else
                {
                    lp1 += sizeof(NAME_RECORD);
                    continue;
                }
            }
            else
            {
                if(strncmp((char*)lp2, szRegular, sizeof(szRegular)-1) != 0 && 
                   strncmp((char*)lp2, szNormal, sizeof(szNormal)-1) != 0)
                {
                    lp1 += sizeof(NAME_RECORD);
                    continue;
                }
                else
                    break;
            }
        }
        lp1 += sizeof(NAME_RECORD);
    }

    // If no regular font, exit
    if (i == Num)
        goto CloseHandle;

    // all non-regular fonts have already been eliminated
    lp1  = (LPBYTE)lpvFile + cmap;                     // point to cmap table

    if (lp1 + sizeof(CMAP_TABLE)*Num >= lpMax)
        goto CloseHandle;

    Num  = TWO_BYTE_NUM(((CMAP_HEAD*)lp1)->NumTables);

    lp1 += sizeof(CMAP_HEAD);


    while(Num >0)
    {

        if(TWO_BYTE_NUM(((CMAP_TABLE*)lp1)->Platform) == MICROSOFT_PLATFORM && 
           (TWO_BYTE_NUM(((CMAP_TABLE*)lp1)->Encoding) == UNICODE_INDEXING ||
           TWO_BYTE_NUM(((CMAP_TABLE*)lp1)->Encoding) == UNICODE_SYMBOL_INDEXING))
        {
            lp = (LPBYTE)lpvFile
                 + cmap
                 + FOUR_BYTE_NUM(((CMAP_TABLE*)lp1)->Offset);

            if(TWO_BYTE_NUM(((CMAP_FORMAT*)lp)->Format) == CMAP_FORMAT_FOUR)
            {
                break;
            }
        }
        Num--;
        lp1 += sizeof(CMAP_TABLE);
    }

    if(Num == 0)                            // can't find Platform:3/Encoding:1 (Unicode)
        goto CloseHandle;

    Num  = TWO_BYTE_NUM(((CMAP_FORMAT*)lp)->SegCountX2);
    lp2  = lp  + sizeof(CMAP_FORMAT);       // lp2 -> first WCHAR of wcTo
    lp1  = lp2 + Num + 2;                   // lp1 -> first WCHAR of wcFrom

    if (lp1 + Num >= lpMax)
        goto CloseHandle;

    Num /= 2;

    

    if (pURanges == NULL)
    {
        *puiRanges = Num;
    }
    else
    {
        if (Num > *puiRanges)
            Num = *puiRanges;
        else
            *puiRanges = Num;

        for(i=0, j=0; i < Num; i++, j++, j++)
        {
            pURanges[i].wcFrom = TWO_BYTE_NUM((lp1+j));
            pURanges[i].wcTo   = TWO_BYTE_NUM((lp2+j));
        }
    }

    hr = S_OK;

CloseHandle:
    UnmapViewOfFile(lpvFile);
CloseHandle0:
    CloseHandle(hTTFMap);
    CloseHandle(hTTF);

    return hr;
}

int CMLFLink2::CFontMappingCache2::fetchCharSet(BYTE *pCharset, int iURange)
{
    int i,j;

    //Check if current charset valid for the font
    for (i=0; i<g_urange_table[iURange].nFonts; i++)
    {
        for (j=0;(j<32) && g_CharSetTransTable[j].uCodePage;j++)
        {
            if (g_pfont_table[*(g_urange_table[iURange].pFontIndex+i)].dwCodePages[0] & g_CharSetTransTable[j].dwCodePages)
                if (*pCharset == g_CharSetTransTable[j].nCharSet)
                    return i;
        }
    }

    //If invalid, fetch first valid one.
    for (i=0;(i<32) && g_CharSetTransTable[i].uCodePage;i++)
    {
        if (g_pfont_table[*(g_urange_table[iURange].pFontIndex)].dwCodePages[0] & g_CharSetTransTable[i].dwCodePages)
        {
           *pCharset = (BYTE)g_CharSetTransTable[i].nCharSet;
           break;
        }
    }

    return 0;
}


BOOL CMLFLink2::CFontMappingCache2::GetNonCpFontUnicodeRanges(TCHAR *szFontName, int iFontIndex)
{
    LONG    nURange = 0;
    DWORD   cmap    = 0;
    DWORD   name    = 0;
    DWORD   os2     = 0;

    HANDLE  hTTFMap;
    DWORD   dwFileSize;
    LPVOID  lpvFile;
    LPBYTE  lp, lp1, lp2;
    DWORD   Num;
    int     i, j, k, m;
    WORD    Len;
    HANDLE  hTTF;
    BOOL    bRet = FALSE;


    hTTF = CreateFile(  szFontName,             // pointer to name of the file
                        GENERIC_READ,           // access (read-write) mode
                        FILE_SHARE_READ,        // share mode
                        NULL,                   // pointer to security attributes
                        OPEN_EXISTING,          // how to create
                        FILE_ATTRIBUTE_NORMAL,  // file attributes
                        NULL);                  // handle to file with attributes to copy;

    if (hTTF == INVALID_HANDLE_VALUE)
        return FALSE;

    dwFileSize = GetFileSize(hTTF, NULL);

    hTTFMap = CreateFileMapping(
                  hTTF,
                  NULL,
                  PAGE_READONLY,
                  0,
                  dwFileSize,
                  NULL
              );

    if(hTTFMap == NULL)
    {
        goto CloseHandle01;
    }

    lpvFile = MapViewOfFile(
                  hTTFMap,
                  FILE_MAP_READ,
                  0,
                  0,
                  0
              );

    if(lpvFile == NULL)
    {
        goto CloseHandle00;
    }

    lp = (LPBYTE)lpvFile;

    if(strncmp(((TTC_HEAD*)lp)->TTCTag, "ttcf", 4) == 0)   // TTC format
    {
        lp += FOUR_BYTE_NUM(((TTC_HEAD*)lp)->OffsetTTF1);  // points to first TTF
    }

    Num = TWO_BYTE_NUM(((TTF_HEAD*)lp)->NumTables);        // Number of Tables
    {
      // if SearchRange != (Maximum power of 2 <= Num)*16,
      // then this is not a TTF file
      DWORD  wTmp = 1;

      while(wTmp <= Num)
      {
        wTmp <<= 1;
      }
      wTmp <<= 3;          // (wTmp/2)*16

      if(wTmp != (DWORD)TWO_BYTE_NUM(((TTF_HEAD*)lp)->SearchRange))
      {
        goto CloseHandle00;
      }

      // if RangeShift != (Num*16) - SearchRange,
      // then this is not a TTF file
      wTmp = (Num<<4) - wTmp;
      if(wTmp != (DWORD)TWO_BYTE_NUM(((TTF_HEAD*)lp)->RangeShift))
      {
        goto CloseHandle00;
      }
    }

    lp += sizeof(TTF_HEAD);

    for(i = 0; i < (int)Num; i++)   // go thru all tables to find cmap and name
    {
        if(strncmp( ((TABLE_DIR*)lp)->Tag, "cmap", 4) == 0)
        {
            cmap = FOUR_BYTE_NUM(((TABLE_DIR*)lp)->Offset);
            if (name && os2) break;
        }
        else if(strncmp( ((TABLE_DIR*)lp)->Tag, "name", 4) == 0)
        {
            name = FOUR_BYTE_NUM(((TABLE_DIR*)lp)->Offset);
            if (cmap && os2) break;
        }
        else if(strncmp( ((TABLE_DIR*)lp)->Tag, "OS/2", 4) == 0)
        {
            os2 = FOUR_BYTE_NUM(((TABLE_DIR*)lp)->Offset);
            if (cmap && name) break;
        }

        lp += sizeof(TABLE_DIR);
     }

    if((!cmap) || (!name) || (!os2))    // Can't find cmap or name
    {
        goto CloseHandle00;
    }

    // Read thru all name records
    // to see if font subfamily name is "Regular"

    lp  = (LPBYTE)lpvFile + name;                   // point to name table
    Num = TWO_BYTE_NUM(((NAME_TABLE*)lp)->NumRec);  // # of name record
    lp1 = lp  + sizeof(NAME_TABLE);                 // point to name record

    for(i = 0; i < (int)Num; i++)
    {
        if(FONT_SUBFAMILY_NAME == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->NameID))
        {
            lp2 = lp +                              // point to string store
                  TWO_BYTE_NUM(((NAME_TABLE* )lp )->Offset) +
                  TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Offset);

            Len = TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Length);

            if(UNICODE_INDEXING == TWO_BYTE_NUM(((NAME_RECORD*)lp1)->Encoding))
            {
                Len >>= 1;
                while(--Len > 0)
                {
                    if(*(lp2+(Len<<1)+1) != szRegular[Len])
                    goto CloseHandle00;
                }
                break;
            }
            else
            {
                if(strncmp((char*)lp2, szRegular, Len) != 0)
                    goto CloseHandle00;
                else
                    break;
            }
        } 

        lp1 += sizeof(NAME_RECORD);
    }

    // all non-regular fonts have already been eliminated

    lp1  = (LPBYTE)lpvFile + cmap;                     // point to cmap table
    Num  = TWO_BYTE_NUM(((CMAP_HEAD*)lp)->NumTables);
    lp1 += sizeof(CMAP_HEAD);

    while(Num >0)
    {
        if(TWO_BYTE_NUM(((CMAP_TABLE*)lp1)->Platform) == MICROSOFT_PLATFORM && 
           TWO_BYTE_NUM(((CMAP_TABLE*)lp1)->Encoding) == UNICODE_INDEXING)
        {
            lp = (LPBYTE)lpvFile
                 + cmap
                 + FOUR_BYTE_NUM(((CMAP_TABLE*)lp1)->Offset);

            if(TWO_BYTE_NUM(((CMAP_FORMAT*)lp)->Format) == CMAP_FORMAT_FOUR)
            {
                break;
            }
        }
        Num--;
        lp1 += sizeof(CMAP_TABLE);
    }

    if(Num == 0)                   // can't find Platform:3/Encoding:1 (Unicode)
        goto CloseHandle00;


    Num  = TWO_BYTE_NUM(((CMAP_FORMAT*)lp)->SegCountX2) ;

    m = ARRAYSIZE(g_urange_table);

    lp2  = lp  + sizeof(CMAP_FORMAT);     // lp2 -> first WCHAR of wcTo
    lp1  = lp2 + Num + 2;                 // lp1 -> first WCHAR of wcFrom


    // Fast parse !!!
    while (--m)
    {
        // URANGE binary search
        i=0;
        j= (int) Num - 2;
        k=j/2;
        while (i<=j)
        {
            if (k % 2) 
                k++;

            if (g_urange_table[m].wcFrom >= TWO_BYTE_NUM((lp1+k)) && g_urange_table[m].wcTo <= TWO_BYTE_NUM((lp2+k)))
            {
                EnterCriticalSection(&g_cs);
                if (!g_urange_table[m].pFontIndex)
                    g_urange_table[m].pFontIndex = (int *)LocalAlloc(LPTR, sizeof(int)* MAX_FONT_INDEX);
                if (!g_urange_table[m].pFontIndex)
                {
                    goto CloseHandle00;
                }

                if (g_urange_table[m].nFonts >= MAX_FONT_INDEX)
                {
                    break;
                }

                g_urange_table[m].pFontIndex[g_urange_table[m].nFonts] = iFontIndex;
                g_urange_table[m].nFonts++;

                // Fill in font code page signature
                g_pfont_table[iFontIndex].dwCodePages[0] = FOUR_BYTE_NUM(((BYTE *)lpvFile+os2+OFFSET_OS2CPRANGE));
                g_pfont_table[iFontIndex].dwCodePages[1] = FOUR_BYTE_NUM(((BYTE *)lpvFile+os2+OFFSET_OS2CPRANGE+1));                
                LeaveCriticalSection(&g_cs);
                break;
            }
            else
            {
                if (g_urange_table[m].wcFrom < TWO_BYTE_NUM((lp1+k)))
                {
                    j = k-2;
                }
                else
                {
                    i = k+2;
                }
                k = (i+j)/2;
            }
        }
    }
    
    bRet = TRUE;

CloseHandle00:
    UnmapViewOfFile(lpvFile);
CloseHandle01:
    CloseHandle(hTTF);
    CloseHandle(hTTFMap);

    return bRet;
}

HRESULT GetRegFontKey(HKEY *phKey, DWORD *pdwValues)
{
    HRESULT hr = E_FAIL;

    if (ERROR_SUCCESS == (g_bIsNT? 
        RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGFONTKEYNT, 0, KEY_READ, phKey):
        RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGFONTKEY95, 0, KEY_READ, phKey)))
    {
        if (ERROR_SUCCESS == RegQueryInfoKey(*phKey, NULL, NULL, 0, NULL, 
            NULL, NULL, pdwValues, NULL, NULL, NULL, NULL))
        {
            hr = S_OK;
        }        
    }

    return hr;
}

BOOL CMLFLink2::CFontMappingCache2::GetFontURangeBits(TCHAR *szFontFile, DWORD * pdwURange)
{
    // We can make use of font Unicode range signature if needed.
    return TRUE;    
}

BOOL CMLFLink2::CFontMappingCache2::SetFontScripts(void)
{

    LOGFONT lf;
    int     i,j;
    HWND    hWnd = GetTopWindow(GetDesktopWindow());
    HDC     hDC = GetDC(hWnd);


    if (!g_pfont_table)
        return FALSE;

    // Process code page based scripts (g_CharSetTransTable.sid)
    for (i = 0; g_CharSetTransTable[i].nCharSet != DEFAULT_CHARSET; i++)
    {
        j = 0;
        ZeroMemory(&lf, sizeof(lf));
        lf.lfCharSet = (BYTE)g_CharSetTransTable[i].nCharSet;

        while (g_CharSetTransTable[i].sid[j] != sidDefault)
        {
            EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)SetFontScriptsEnumFontProc, (LPARAM)g_CharSetTransTable[i].sid[j], 0);
            j++;
        }
    }


    if (hDC)
        ReleaseDC(hWnd, hDC);

    // Process Unicode subrange based scripts (not implemented)
    // Skip this part since we need to access font CMAP anyway

    // Process char based scripts (g_wCharToScript)

    for (i=1; i<= (int)g_pfont_table[0].dwCodePages[0]; i++)
    {
        UINT uiRanges = 0;
        UNICODERANGE* pURanges = NULL;
        SCRIPT_IDS  scripts;

        if (SUCCEEDED(m_pFontMappingCache2->UnicodeRanges(g_pfont_table[i].szFileName, &uiRanges, pURanges)))
        {
            if (uiRanges)
            {
                int         l, m, n;                

                pURanges = (UNICODERANGE *)LocalAlloc(LPTR, sizeof(UNICODERANGE) * uiRanges);

                if (!pURanges)
                    return FALSE;

                m_pFontMappingCache2->UnicodeRanges(g_pfont_table[i].szFileName, &uiRanges, pURanges);
                for (j=0; j< ARRAYSIZE(g_wCharToScript); j++)
                {

                    l = 0;
                    m = uiRanges;
                    n = m/2;
                    while (l <= m)
                    {
                        if ((g_wCharToScript[j].wch >= pURanges[n].wcFrom) && (g_wCharToScript[j].wch <= pURanges[n].wcTo))
                        {
                            scripts = 1;
                            scripts <<= g_wCharToScript[j].sid;
                            g_pfont_table[i].scripts |= scripts;
                            break;
                        }
                        else
                        {
                            if (g_wCharToScript[j].wch < pURanges[n].wcFrom)
                                m = n-1;
                            else
                                l = n+1;
                            n = (m+l)/2;
                        }
                    }
                }

                LocalFree(pURanges);
                pURanges = NULL;
            }
        }

        // sidUserDefined should contain all valid regular TrueType fonts
        if (!MLStrStr(g_pfont_table[i].szFaceName, TEXT("Bold")) && !MLStrStr(g_pfont_table[i].szFaceName, TEXT("Italic")))
        {
            scripts = 1;
            scripts <<= sidUserDefined;
            g_pfont_table[i].scripts |= scripts;
        }
    }

    //GetFontScriptFromCMAP(szFont, &(g_pfont_table[i].scripts));

    return TRUE;
}

BOOL CMLFLink2::CFontMappingCache2::IsFontUpdated(void)
{
    HKEY    hkey;
    DWORD   dwFonts = 0;
    BOOL    bRet = FALSE;


    if (g_pfont_table)
    {
        if (S_OK == GetRegFontKey(&hkey, &dwFonts))
        {
            if (g_pfont_table[0].dwCodePages[1] != dwFonts)
                bRet = TRUE;
            RegCloseKey(hkey);
        }
    }
    else
    {
        // font table not created yet, need to update
        bRet = TRUE;
    }

    return bRet;
}
    
// Make sure we have font data table available and it is updated
HRESULT CMLFLink2::CFontMappingCache2::EnsureFontTable(BOOL bUpdateURangeTable)    
{
    BOOL bRet;
    
    if (IsFontUpdated())
    {
        // Need to guard the whole font creation procedure by critical sections
        EnterCriticalSection(&g_cs);

        if (IsFontUpdated())
        {
            if (g_pfont_table)
            {
                if (g_pfont_table[0].szFaceName[0])
                {
                    bUpdateURangeTable = TRUE;
                }
                if (g_pfont_table)
                {
                    LocalFree(g_pfont_table);
                    g_pfont_table = NULL;
                }
            }

            bRet = SetFontTable();

            if (!bRet)
                return E_OUTOFMEMORY;

        }
        LeaveCriticalSection(&g_cs);        
    }

    if (bUpdateURangeTable)
    {
        EnterCriticalSection(&g_cs);
        for (int i = 0; i < ARRAYSIZE(g_urange_table); i++)
        {
            if (g_urange_table[i].nFonts)
            {
                LocalFree(g_urange_table[i].pFontIndex);
                g_urange_table[i].pFontIndex = NULL;
                g_urange_table[i].nFonts = 0;
            }            
        }
        LeaveCriticalSection(&g_cs);        

        if (S_OK != SetFontUnicodeRanges())
            return E_OUTOFMEMORY;

        SaveFontDataFile();
    }

    // All tables created successfully
    return S_OK;
}


#ifdef UNIX
typedef struct tagTable_info{
    int count;
    int table_size;
    } Table_info;

int UnixGetAllFontsProc(ENUMLOGFONTEX* plfFont, NEWTEXTMETRICEX* lpntm, int iFontType, LPARAM lParam)
{
    LOGFONT *lplf;
    int *pcount = &((Table_info*)lParam)->count;
    int *ptable_size = &((Table_info*)lParam)->table_size;

    lplf = &(plfFont->elfLogFont);
    // We don't use non TrueType fonts
    if (iFontType == DEVICE_FONTTYPE || iFontType == RASTER_FONTTYPE)
        return 1;   // keep going but don't use this font

    // We don't use the SYMBOL, Mac Charset fonts
    if(lplf->lfCharSet == SYMBOL_CHARSET || lplf->lfCharSet == MAC_CHARSET)
        return 1;

    // We don't handle vertical fonts
    if (TEXT('@') == lplf->lfFaceName[0])
        return 1;

    // Now update the font-table
    // Does UNIX use TTF? // if (FontType == TRUETYPE_FONTTYPE)
    {
        CopyMemory(&g_pfont_table[*pcount].lf, lplf, sizeof(LOGFONT));
        MLStrCpyN(g_pfont_table[*pcount].szFaceName, lplf->lfFaceName, LF_FACESIZE);
        (*pcount)++;
    }

    if (*pcount >= *ptable_size)
    {
        FONTINFO * pfont_table = NULL;

        *ptable_size += FONT_TABLE_INIT_SIZE;
        pfont_table = (FONTINFO *) LocalReAlloc(g_pfont_table, 
                                        sizeof(FONTINFO) * *ptable_size,
                                        LMEM_MOVEABLE | LMEM_ZEROINIT);
        if (NULL == pfont_table)
        {
            return 0; // Stop enum. 
        }
        else
        {
            g_pfont_table = pfont_table;
        }
    }
 
    return 1;       // Keep enum. 
}
#endif

BOOL CMLFLink2::CFontMappingCache2::SetFontTable(void)
{
    BOOL    bRet = TRUE;
    TCHAR   szFaceName[MAX_PATH];

    DWORD   dwValue;
    TCHAR   szFontFile[MAX_FONT_FILE_NAME];
    DWORD   dwData;
    DWORD   dwType = REG_SZ;
    DWORD   dwFonts;
    int     i, table_size = FONT_TABLE_INIT_SIZE;
    LPTSTR  pNewFaceName = NULL;
    HKEY    hkey = NULL;
    static int count;

    HDC     hDC = NULL;
    HWND    hWnd = NULL;

    count = 1;
    
    if (!g_pfont_table)
    {
        g_pfont_table = (FONTINFO *)LocalAlloc(LPTR, sizeof(FONTINFO) * FONT_TABLE_INIT_SIZE);
        if (!g_pfont_table)
        {
            bRet = FALSE;
            goto SETFONT_DONE;
        }        
    }
    else
    {
        goto SETFONT_DONE;
    }
    
#ifndef UNIX
    if (S_OK != GetRegFontKey(&hkey, &dwFonts))
    {
        bRet = FALSE;
        goto SETFONT_DONE;
    }    

    hWnd = GetTopWindow(GetDesktopWindow());
    hDC = GetDC(hWnd);

    for (i=0; ;i++)
    {
        dwValue = sizeof(szFaceName);
        dwData  = sizeof(szFontFile);

        if (ERROR_NO_MORE_ITEMS == RegEnumValue(
                      hkey,
                      i,
                      szFaceName,
                      &dwValue,
                      NULL,
                      &dwType,
                      (LPBYTE)szFontFile,
                      &dwData ))
        {
            break;
        }
        DWORD dwOffset = 0;
FIND_NEXT_FACENAME:        
        pNewFaceName = MLStrStr(&szFaceName[dwOffset], TEXT(" & "));
        if (pNewFaceName)
        {
           *pNewFaceName = 0;
           // Skip " & ", look for next font face name
           pNewFaceName+=3;
        }
        else
        {
            pNewFaceName = MLStrStr(&szFaceName[dwOffset], TEXT("(TrueType)"));
            if(pNewFaceName)
            {
                // Ignor the space between face name and "(TrueTye)" signature
                if ((pNewFaceName > szFaceName) && (*(pNewFaceName-1) == 0x20))
                    pNewFaceName--;
                *pNewFaceName = 0;
            }
        }

        if (pNewFaceName && !EnumFontFamilies(hDC, &szFaceName[dwOffset], MapFontExEnumFontProc, (LPARAM)&count))   //TrueType font
        {
            int nSize;
            LPTSTR pFontFile;

            if (count >= table_size)
            {
                FONTINFO * _pfont_table = NULL;

                table_size += FONT_TABLE_INIT_SIZE;
                _pfont_table = (FONTINFO *) LocalReAlloc(g_pfont_table, sizeof(FONTINFO) * table_size,
                                    LMEM_MOVEABLE | LMEM_ZEROINIT);
                if (NULL == _pfont_table)
                {
                    bRet = FALSE;
                    goto SETFONT_DONE;
                }
                else
                {
                    g_pfont_table = _pfont_table;
                }
            }

            nSize = lstrlen(szFontFile);

            if (!MLStrCmpNI(&szFontFile[nSize-3], "fot", 3))
                MLStrCpyN(&szFontFile[nSize-3], "ttf", 3);

            //
            // Trim off path 
            //
            // #335900, some third party apps write font file names to registry directly
            // and the names they used could have redundant font path 
            //
            pFontFile = szFontFile;

            while (nSize)
            {
                // Font file name contains only ASCII characters,
                // So, we can safely trim the path by backward searching '\'
                if (szFontFile[nSize] == TEXT('\\'))
                {
                    pFontFile = &szFontFile[nSize];
                    pFontFile++;
                    break;
                }
                nSize--;
            }

            GetFontURangeBits(szFontFile, &(g_pfont_table[count-1].dwUniSubRanges[0]));
            MLStrCpyN(g_pfont_table[count-1].szFaceName, &szFaceName[dwOffset], LF_FACESIZE);
            MLStrCpyN(g_pfont_table[count-1].szFileName, pFontFile, LF_FACESIZE);

        }
        if (pNewFaceName && (*pNewFaceName))
        {
            dwOffset = (DWORD)(pNewFaceName - &szFaceName[0]);
            goto FIND_NEXT_FACENAME;
        }
    }
#else
    // For UNIX, we don't have registry font information,
    // Let's create font table through EnumFontFamiliesEx.
    Table_info table_info;
    table_info.count = 1;
    table_info.table_size = table_size;

    int iRet;
    LOGFONT lf;
    lf.lfCharSet = DEFAULT_CHARSET; // give me all fonts
    lf.lfFaceName[0] = _T('\0');
    lf.lfPitchAndFamily = 0;
        
    hWnd = GetTopWindow(GetDesktopWindow());
    hDC = GetDC(hWnd);

    iRet = EnumFontFamiliesEx(hDC, // Enum all fonts
                     &lf,
                     (FONTENUMPROC)UnixGetAllFontsProc,
                     (LPARAM)&table_info,
                     0);
    count = table_info.count;
    if (iRet == 0) // abort
    {
        bRet = FALSE;
        goto SETFONT_DONE;    
    }
#endif // UNIX

    // Release un-used memory
    g_pfont_table = (FONTINFO *)LocalReAlloc(g_pfont_table, (count)*sizeof(FONTINFO), LMEM_MOVEABLE);

    // Save TrueType font number
    g_pfont_table[0].dwCodePages[0] = count-1;

#ifndef UNIX
    // Unix doesn't have this number.
    // Save total font number for font change verification 
    g_pfont_table[0].dwCodePages[1] = dwFonts;

    RegCloseKey(hkey);
#endif

    if (count > 1)
        SetFontScripts();

SETFONT_DONE:    

    if (hDC)
        ReleaseDC(hWnd, hDC);

    if (count <= 1)
    {
        if (g_pfont_table)
        {
            LocalFree(g_pfont_table);
            g_pfont_table = NULL;
        }

        bRet = FALSE;
    }

    return bRet;

}


HRESULT CMLFLink2::CFontMappingCache2::SaveFontDataFile(void)
{
    FONTDATAHEADER fileHeader;
    HRESULT hr = E_FAIL;
    int     *pTmpBuf = NULL;
    HANDLE  hFile = NULL;
    int     i, j, Count = 0;
    DWORD   dwSize;
    FONTDATATABLE fontInfoTable, fontIndexTable;


    hFile = CreateFile( szFontDataFilePath,         
                        GENERIC_WRITE,          
                        0,                      
                        NULL,                   
                        CREATE_ALWAYS,          
                        FILE_ATTRIBUTE_HIDDEN,
                        NULL);                 

    if (hFile == INVALID_HANDLE_VALUE)
    {
        goto SAVE_FONT_DATA_DONE;
    }


    for (i = 0; i < ARRAYSIZE(g_urange_table); i++)
    {
        Count += (g_urange_table[i].nFonts+1);
    }


    // Create file header
    lstrcpyA(fileHeader.FileSig, FONT_DATA_SIGNATURE);
    fileHeader.dwVersion = 0x00010000;

    // Use file size as CheckSum
    fileHeader.dwCheckSum = sizeof(FONTINFO)*(g_pfont_table[0].dwCodePages[0]+1)+Count*sizeof(int)+
         + sizeof(FONTDATAHEADER) + sizeof(FONTDATATABLE)*FONTDATATABLENUM;
    fileHeader.nTable = FONTDATATABLENUM;


    pTmpBuf = (int *)LocalAlloc(LPTR, Count*sizeof(int));

    if (!pTmpBuf)
         goto SAVE_FONT_DATA_DONE;

    // Get font index data
    for (i = 0; i < ARRAYSIZE(g_urange_table); i++)
    {
        *pTmpBuf++ = g_urange_table[i].nFonts;

        if (g_urange_table[i].nFonts)
        {
            for (j = 0; j< g_urange_table[i].nFonts; j++)
            {
                *pTmpBuf++ = *(g_urange_table[i].pFontIndex+j);
            }
        }
    }

    pTmpBuf -= Count;

    // Create Dir tables
    lstrcpyA(fontInfoTable.szName, "fnt");
    fontInfoTable.dwOffset = sizeof(FONTDATAHEADER) + sizeof(FONTDATATABLE)*FONTDATATABLENUM;
    fontInfoTable.dwSize = sizeof(FONTINFO)*(g_pfont_table[0].dwCodePages[0]+1);

    lstrcpyA(fontIndexTable.szName, "idx");
    fontIndexTable.dwOffset = fontInfoTable.dwSize+fontInfoTable.dwOffset;
    fontIndexTable.dwSize = Count*sizeof(int);

    if (WriteFile(hFile, &fileHeader, sizeof(FONTDATAHEADER), &dwSize, NULL) &&
        WriteFile(hFile, &fontInfoTable, sizeof(FONTDATATABLE), &dwSize, NULL) &&
        WriteFile(hFile, &fontIndexTable, sizeof(FONTDATATABLE), &dwSize, NULL) &&
        WriteFile(hFile, g_pfont_table, fontInfoTable.dwSize, &dwSize, NULL) &&
        WriteFile(hFile, pTmpBuf, fontIndexTable.dwSize, &dwSize, NULL))
    {
        hr = S_OK;
    }

SAVE_FONT_DATA_DONE:
    if (hFile)
        CloseHandle(hFile);
    if (pTmpBuf)
        LocalFree(pTmpBuf);

    return hr;
}

HRESULT CMLFLink2::CFontMappingCache2::LoadFontDataFile(void)
{
    HANDLE  hFontData = NULL;
    HANDLE  hFileMap = NULL;
    LPVOID  lpvFile = NULL;
    int *   lp;
    HRESULT hr = E_FAIL;
    DWORD   dwFileSize;

    int     i, j;
    HKEY    hKey = NULL;
    DWORD   nFonts;
    FONTDATAHEADER *pHeader;
    FONTDATATABLE *pfTable;


    hFontData = CreateFile(szFontDataFilePath,  
                        GENERIC_READ,           
                        FILE_SHARE_READ,        
                        NULL,                   
                        OPEN_EXISTING,          
                        FILE_ATTRIBUTE_NORMAL,  
                        NULL);                  

    if (hFontData == INVALID_HANDLE_VALUE)
        return EnsureFontTable(TRUE);

    dwFileSize = GetFileSize(hFontData, NULL);

    hFileMap = CreateFileMapping(
                  hFontData,
                  NULL,
                  PAGE_READONLY,
                  0,
                  dwFileSize,
                  NULL
              );

    if(hFileMap == NULL)
    {
        goto Load_File_Done;
    }

    lpvFile = MapViewOfFile(
                  hFileMap,
                  FILE_MAP_READ,
                  0,
                  0,
                  0
              );

    if (lpvFile == NULL)
    {        
        goto Load_File_Done;
    }

    pHeader = (FONTDATAHEADER *)lpvFile;

    // Check mlang font cache file by signature and checksum
    if (lstrcmpA(pHeader->FileSig, FONT_DATA_SIGNATURE) || pHeader->dwCheckSum != dwFileSize)
    {
        goto Load_File_Done;
    }


    if (S_OK != GetRegFontKey(&hKey, &nFonts))
    {
        goto Load_File_Done;
    }

    pfTable = (FONTDATATABLE *) ((LPBYTE)lpvFile + sizeof(FONTDATAHEADER));

    // Check if there is any font change (no guarantee, but works in most cases)
    if (nFonts != ((FONTINFO*)((LPBYTE)lpvFile + pfTable[0].dwOffset))->dwCodePages[1])
    {
        // If there is a change in system font number, we reload everything
        UnmapViewOfFile(lpvFile);
        CloseHandle(hFileMap);
        CloseHandle(hFontData);
        RegCloseKey(hKey);
        return EnsureFontTable(TRUE);
    }

    EnterCriticalSection(&g_cs);
    // Reset cache information
    if (g_pfont_table)
    {
        
        LocalFree(g_pfont_table);
        g_pfont_table = NULL;
        for (i = 0; i < ARRAYSIZE(g_urange_table); i++)
        {
            if (g_urange_table[i].nFonts)
            {
                LocalFree(g_urange_table[i].pFontIndex);
                g_urange_table[i].pFontIndex = NULL;
                g_urange_table[i].nFonts = 0;
            }
        }        
    }


    if(!(g_pfont_table = (FONTINFO *) (LocalAlloc(LPTR, pfTable[0].dwSize))))
    {
        hr = E_OUTOFMEMORY;
        goto Load_File_Done;
    }

    CopyMemory(g_pfont_table, (LPBYTE)lpvFile + pfTable[0].dwOffset, pfTable[0].dwSize);

    lp = (int *)((LPBYTE)lpvFile + pfTable[1].dwOffset);

    for (i = 0; i < ARRAYSIZE(g_urange_table); i++)
    {
        if (g_urange_table[i].nFonts = *lp++)
        {
            //g_urange_table[i].nFonts = *lp++;
            g_urange_table[i].pFontIndex = (int *)LocalAlloc(LPTR, sizeof(int)*g_urange_table[i].nFonts);
            for (j = 0; j<  g_urange_table[i].nFonts; j++)
            {
                g_urange_table[i].pFontIndex[j] = *lp++;
            }
        }
    }

    LeaveCriticalSection(&g_cs);

    hr = S_OK;

Load_File_Done:
    if (lpvFile)
        UnmapViewOfFile(lpvFile);
    if (hFileMap)
        CloseHandle(hFileMap);
    if (hFontData)
        CloseHandle(hFontData);
    if (hKey)
        RegCloseKey(hKey);

    return hr;

}


HRESULT CMLFLink2::CFontMappingCache2::SetFontUnicodeRanges(void)
{
    TCHAR   szFontPath[MAX_PATH];
    TCHAR   szFont[MAX_PATH];
    HRESULT hr = S_OK;
    int     i;
    

    EnterCriticalSection(&g_cs);
    g_pfont_table[0].szFaceName[0] = 1;
    LeaveCriticalSection(&g_cs);
    
    MLGetWindowsDirectory(szFontPath, MAX_PATH);
    MLPathCombine(szFontPath, szFontPath, FONT_FOLDER);

    for (i=1; i<= (int)g_pfont_table[0].dwCodePages[0]; i++)
    {
        MLPathCombine(szFont, szFontPath, g_pfont_table[i].szFileName);
        GetNonCpFontUnicodeRanges(szFont, i);
    }

    // Release un-used memory
    for (i=0; i< ARRAYSIZE(g_urange_table); i++)
    {
        if (g_urange_table[i].nFonts)
            g_urange_table[i].pFontIndex = (int *)LocalReAlloc(g_urange_table[i].pFontIndex, g_urange_table[i].nFonts*sizeof(int), LMEM_MOVEABLE);
    }

    return hr;
}

STDMETHODIMP CMLFLink2::GetStrCodePages(const WCHAR* pszSrc, long cchSrc, DWORD dwPriorityCodePages, DWORD* pdwCodePages, long* pcchCodePages)
{
    ASSERT_THIS;
    ASSERT_READ_BLOCK(pszSrc, cchSrc);
    ASSERT_WRITE_PTR_OR_NULL(pdwCodePages);
    ASSERT_WRITE_PTR_OR_NULL(pcchCodePages);

    HRESULT hr = S_OK;
    long cchCodePages = 0;
    DWORD dwStrCodePages = (DWORD)~0;
    BOOL fInit = FALSE;
    BOOL fNoPri = FALSE;

    if (!pszSrc || cchSrc <= 0) // We can't make dwStrCodePages when cchSrc is zero
        hr = E_INVALIDARG;

    if (!m_pIMLFLnk)
        return E_OUTOFMEMORY;

    while (SUCCEEDED(hr) && cchSrc > 0)
    {
        DWORD dwCharCodePages;

        if (SUCCEEDED(hr = m_pIMLFLnk->GetCharCodePages(*pszSrc, &dwCharCodePages)))
        {
            if (!fInit)
            {
                fInit = TRUE;
                fNoPri = !(dwPriorityCodePages & dwCharCodePages);
            }
            else if (fNoPri != !(dwPriorityCodePages & dwCharCodePages))
            {
                break;
            }
            if (!fNoPri)
                dwPriorityCodePages &= dwCharCodePages;

            if (dwCharCodePages && (dwCharCodePages & dwStrCodePages))
                dwStrCodePages &= dwCharCodePages;
            else
                break;

            pszSrc++;
            cchSrc--;
            cchCodePages++;
        }
    }

    if (SUCCEEDED(hr))
    {
        if (!cchCodePages)
        {
            dwStrCodePages = 0;
            cchCodePages++;
        }
        if (pcchCodePages)
            *pcchCodePages = cchCodePages;
        if (pdwCodePages)
            *pdwCodePages = dwStrCodePages;
    }
    else
    {
        if (pcchCodePages)
            *pcchCodePages = 0;
        if (pdwCodePages)
            *pdwCodePages = 0;
    }

    return hr;
}

STDMETHODIMP CMLFLink2::MapFont(HDC hDC, DWORD dwCodePages, WCHAR wchar, HFONT* phDestFont)
{
    HFONT hSrcFont = NULL;

    if (NULL == (hSrcFont = (HFONT) GetCurrentObject(hDC, OBJ_FONT)))
        return E_FAIL;

    if (dwCodePages)
    {
        if (m_pIMLFLnk)
            return m_pIMLFLnk->MapFont(hDC, dwCodePages, hSrcFont, phDestFont);
        return E_OUTOFMEMORY;
    }
    else
    {
        if (!m_pFontMappingCache2)
            m_pFontMappingCache2 = new CFontMappingCache2;
        if (m_pFontMappingCache2)
            return m_pFontMappingCache2->MapFontFromCMAP(hDC, wchar, hSrcFont, phDestFont);
        else
            return E_OUTOFMEMORY;
    }
}

STDMETHODIMP CMLFLink2::GetFontUnicodeRanges(HDC hDC, UINT *puiRanges, UNICODERANGE* pURanges)
{
    int     i;
    LOGFONT lf;
    HRESULT hr = E_FAIL;
    HFONT   hFont = NULL;

    if (!puiRanges)
        return E_INVALIDARG;

    if (!m_pFontMappingCache2)
        m_pFontMappingCache2 = new CFontMappingCache2;
    if (!m_pFontMappingCache2)
        return E_OUTOFMEMORY;

    if (!(hFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT)))
        return hr;

    if (FAILED(m_pFontMappingCache2->EnsureFontTable(FALSE)))
        return hr;

    if (!GetObject(hFont, sizeof(LOGFONT), &lf))
        return hr;

    for (i=1; i<= (int) g_pfont_table[0].dwCodePages[0]; i++)
    {
        if (!lstrcmp(lf.lfFaceName, g_pfont_table[i].szFaceName))
            break;
    }

    if (i > (int) g_pfont_table[0].dwCodePages[0])
        return hr;

    return m_pFontMappingCache2->UnicodeRanges(g_pfont_table[i].szFileName, puiRanges, pURanges);

}

STDMETHODIMP CMLFLink2::GetScriptFontInfo(SCRIPT_ID sid, DWORD dwFlags, UINT *puiFonts, SCRIPTFONTINFO* pScriptFont)
{
    HRESULT hr = E_FAIL;
    UINT    uiNum;
    BYTE    bPitch = dwFlags & SCRIPTCONTF_FIXED_FONT? FIXED_PITCH:VARIABLE_PITCH;


    if (!m_pFontMappingCache2)
        m_pFontMappingCache2 = new CFontMappingCache2;

    if (m_pFontMappingCache2)
        m_pFontMappingCache2->EnsureFontTable(FALSE);

    if (!g_pfont_table)
        return hr;


    if (!pScriptFont)
    {
        uiNum = g_pfont_table[0].dwCodePages[0];
    }
    else
    {
        uiNum = *puiFonts;    
    }

    *puiFonts = 0;

    // Binary search font table to match script id.
    for (UINT i=1; i<= g_pfont_table[0].dwCodePages[0]; i++)
    {
        // Check font pitch
        if (!(g_pfont_table[i].lf.lfPitchAndFamily & bPitch))
            continue;

        // Get sid bit mask
        SCRIPT_IDS sids = 1;
        sids <<= sid;

        if (sids & g_pfont_table[i].scripts)
        {
            // Bail out is required number reached
            if (*puiFonts >= uiNum)
            {
                break;
            }
            if (pScriptFont)
            {
                MultiByteToWideChar(CP_ACP, 0, g_pfont_table[i].szFaceName, -1, (pScriptFont + *puiFonts)->wszFont, MAX_MIMEFACE_NAME);
                (pScriptFont + *puiFonts)->scripts = g_pfont_table[i].scripts;
            }
            (*puiFonts)++;
        }
    }

    return S_OK;

}

// Map Windows code page to script id 
// if multiple script id exist, we'll return the default one
STDMETHODIMP CMLFLink2::CodePageToScriptID(UINT uiCodePage, SCRIPT_ID *pSid)
{
    MIMECPINFO  cpInfo;
    HRESULT     hr = E_FAIL;

    if (!pSid)
        return E_INVALIDARG;

    if (NULL != g_pMimeDatabase)
    {
        if (SUCCEEDED(g_pMimeDatabase->GetCodePageInfo(uiCodePage, 0x409, &cpInfo)))
        {
            if (cpInfo.uiFamilyCodePage == CP_USER_DEFINED)
            {
                *pSid = sidUserDefined;
                hr = S_OK; 
            }
            else
                for (int i = 0; g_CharSetTransTable[i].uCodePage; i++)
                {
                    if (cpInfo.uiFamilyCodePage == g_CharSetTransTable[i].uCodePage)
                    {
                        *pSid = g_CharSetTransTable[i].sid[0];
                        hr = S_OK;
                        break;
                    }
                }            
        }
    }

    return hr;
}
        
CMLFLink2::CFontMappingCache2::CFontMappingCache2(void)
{
    GetSystemDirectory(szFontDataFilePath, MAX_PATH);
    MLPathCombine(szFontDataFilePath, szFontDataFilePath, FONT_DATA_FILE_NAME);
}

CMLFLink2::CFontMappingCache2::~CFontMappingCache2(void)
{
    EnterCriticalSection(&g_cs);

    if (g_pfont_table)
    {
        LocalFree(g_pfont_table); 
        g_pfont_table = NULL;
    }

    for (int i=0; i< ARRAYSIZE(g_urange_table); i++)
    {
        if (g_urange_table[i].nFonts)
        {
            LocalFree(g_urange_table[i].pFontIndex);
            g_urange_table[i].nFonts = 0;
        }
    }

    LeaveCriticalSection(&g_cs);
}

int CALLBACK CMLFLink2::CFontMappingCache2::MapFontExEnumFontProc(const LOGFONT* plfFont, const TEXTMETRIC*, DWORD FontType, LPARAM lParam)
{  
    if (FontType == TRUETYPE_FONTTYPE && plfFont->lfFaceName[0] != TEXT('@') )
    {
        CopyMemory(&g_pfont_table[*(int *)lParam].lf, plfFont, sizeof(LOGFONT));
        (*(int *)lParam)++;
        return 0;
    }
    return 1;
}

int CALLBACK CMLFLink2::CFontMappingCache2::SetFontScriptsEnumFontProc(const LOGFONT* plfFont, const TEXTMETRIC*, DWORD FontType, LPARAM lParam)
{      
    if (FontType == TRUETYPE_FONTTYPE)
    {
        if (g_pfont_table)
        {
            for (int i=1; i<= (int)g_pfont_table[0].dwCodePages[0]; i++)
                if (!MLStrCmpNI(plfFont->lfFaceName, g_pfont_table[i].szFaceName, LF_FACESIZE))
                {
                    SCRIPT_IDS scripts = 1;
                    scripts <<= lParam;
                    g_pfont_table[i].scripts |= scripts;
                    break;
                }

            if (i > (int)g_pfont_table[0].dwCodePages[0] && plfFont->lfFaceName[0] != TEXT('@'))        // GDI font not in current font table?
            {
                FONTINFO * pfont_table = NULL;

                pfont_table = (FONTINFO *) LocalReAlloc(g_pfont_table, 
                                        sizeof(FONTINFO) * (g_pfont_table[0].dwCodePages[0]+2),
                                        LMEM_MOVEABLE | LMEM_ZEROINIT);
                if (NULL != pfont_table)
                {
                    g_pfont_table = pfont_table;
                    g_pfont_table[0].dwCodePages[0]++;
                    MLStrCpyN(g_pfont_table[i].szFaceName, (char *)plfFont->lfFaceName, LF_FACESIZE);
                    CopyMemory(&g_pfont_table[i].lf, plfFont, sizeof(LOGFONT));

                    SCRIPT_IDS scripts = 1;
                    scripts <<= lParam;
                    g_pfont_table[i].scripts |= scripts;                    
                }
            }
        }
    }
    return 1;
}

int CALLBACK CMLFLink::VerifyFontSizeEnumFontProc(const LOGFONT* plfFont, const TEXTMETRIC* ptm, DWORD FontType, LPARAM lParam)
{
    LOGFONT* plfSrcFont = (LOGFONT*)lParam;

    if (FontType != TRUETYPE_FONTTYPE)
    {
        LONG lHeight = ptm->tmInternalLeading - ptm->tmHeight;
        // Match source font's lfHeight to physical bitmap font's lfHeight
        if (lHeight < 0 && plfSrcFont->lfHeight < 0 && lHeight < plfSrcFont->lfHeight)
        {
            plfSrcFont->lfHeight = lHeight ;
        }
    }

    return 0;
}

