"use strict";

(function(window, undefined){

function _FT_Common()
{


	this.UintToInt = function(v){
        return (v>2147483647)?v-4294967296:v;
    }
    this.UShort_To_Short = function(v){
        return (v>32767)?v-65536:v;
    }
    this.IntToUInt = function(v){
        return (v<0)?v+4294967296:v;
    }
    this.Short_To_UShort = function(v){
        return (v<0)?v+65536:v;
    }
    this.memset = function(d,v,s)
    {
        for (var i=0;i<s;i++)
            d[i]=v;
    }
    this.memcpy = function(d,s,l)
    {
        for (var i=0;i<l;i++)
            d[i]=s[i];
    }
    this.memset_p = function(d,v,s)
    {
        var _d = d.data;
        var _e = d.pos+s;
        for (var i=d.pos;i<_e;i++)
            _d[i]=v;
    }
    this.memcpy_p = function(d,s,l)
    {
        var _d1=d.data;
        var _p1=d.pos;
        var _d2=s.data;
        var _p2=s.pos;
        for (var i=0;i<l;i++)
            _d1[_p1++]=_d2[_p2++];
    }
    this.memcpy_p2 = function(d,s,p,l)
    {
        var _d1=d.data;
        var _p1=d.pos;
        var _p2=p;
        for (var i=0;i<l;i++)
            _d1[_p1++]=s[_p2++];
    }
    this.realloc = function(memory, pointer, cur_count, new_count)
    {
        var ret = { block: null, err : 0, size : new_count};
        if (cur_count < 0 || new_count < 0)
        {
            /* may help catch/prevent nasty security issues */
            ret.err = 6;
        }
        else if (new_count == 0)
        {
            ret.block = null;
        }
        else if (cur_count == 0)
        {
            ret.block = memory.Alloc(new_count);
        }
        else
        {
            var block2 = memory.Alloc(new_count);
            FT_Common.memcpy_p(block2, pointer, cur_count);
            ret.block = block2;
        }
        return ret;
    }

    this.realloc_long = function(memory, pointer, cur_count, new_count)
    {
        var ret = { block: null, err : 0, size : new_count};
        if (cur_count < 0 || new_count < 0)
        {
            /* may help catch/prevent nasty security issues */
            ret.err = 6;
        }
        else if (new_count == 0)
        {
            ret.block = null;
        }
        else if (cur_count == 0)
        {
            ret.block = CreateIntArray(new_count);
        }
        else
        {
            var block2 = CreateIntArray(new_count);
            for (var i = 0; i < cur_count; i++)
                block2[i] = pointer[i];

            ret.block = block2;
        }
        return ret;
    }
}
var FT_Common = new _FT_Common();
/******************************************************************************/
//                                  SERVICES
/******************************************************************************/

/******************************************************************************/
// bdf
/******************************************************************************/
var FT_SERVICE_ID_BDF = "bdf";
function FT_Service_BDFRec(get_charset_id_,get_property_)
{
    this.get_charset_id = get_charset_id_;
    this.get_property = get_property_;
}
/******************************************************************************/
// cid
/******************************************************************************/
var FT_SERVICE_ID_CID = "CID";
function FT_Service_CIDRec(get_ros_, get_is_cid_, get_cid_from_glyph_index_)
{
    this.get_ros = get_ros_;
    this.get_is_cid = get_is_cid_;
    this.get_cid_from_glyph_index = get_cid_from_glyph_index_;
}
/******************************************************************************/
// glyph-dict
/******************************************************************************/
var FT_SERVICE_ID_GLYPH_DICT = "glyph-dict";
function FT_Service_GlyphDictRec(get_name_, name_index_)
{
    this.get_name = get_name_;
    this.name_index = name_index_;
}
/******************************************************************************/
// gxval
/******************************************************************************/
var FT_SERVICE_ID_GX_VALIDATE = "truetypegx-validate";
var FT_SERVICE_ID_CLASSICKERN_VALIDATE = "classickern-validate";

function FT_Service_GXvalidateRec(validate_)
{
    this.validate = validate_;
}
function FT_Service_CKERNvalidateRec(validate_)
{
    this.validate = validate_;
}
/******************************************************************************/
// kerning
/******************************************************************************/
var FT_SERVICE_ID_KERNING = "kerning";
function FT_Service_KerningRec(get_track_)
{
    this.get_track = get_track_;
}
/******************************************************************************/
// multi-masters
/******************************************************************************/
var FT_SERVICE_ID_MULTI_MASTERS = "multi-masters";
function FT_Service_MultiMastersRec(get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_)
{
    this.get_mm = get_mm_;
    this.set_mm_design = set_mm_design_;
    this.set_mm_blend = set_mm_blend_;
    this.get_mm_var = get_mm_var_;
    this.set_var_design = set_var_design_;
}
/******************************************************************************/
// opentype-validate
/******************************************************************************/
var FT_SERVICE_ID_OPENTYPE_VALIDATE = "opentype-validate";
function FT_Service_OTvalidateRec(validate_)
{
    this.validate = validate_;
}
/******************************************************************************/
// pfr-metrics
/******************************************************************************/
var FT_SERVICE_ID_PFR_METRICS = "pfr-metrics";
function FT_Service_PfrMetricsRec(get_metrics_,get_kerning_,get_advance_)
{
    this.get_metrics = get_metrics_;
    this.get_kerning = get_kerning_;
    this.get_advance = get_advance_;
}
/******************************************************************************/
// postscript-font-name
/******************************************************************************/
var FT_SERVICE_ID_POSTSCRIPT_FONT_NAME = "postscript-font-name";
function FT_Service_PsFontNameRec(get_ps_font_name_)
{
    this.get_ps_font_name = get_ps_font_name_;
}
/******************************************************************************/
// postscript-cmaps
/******************************************************************************/
var FT_SERVICE_ID_POSTSCRIPT_CMAPS = "postscript-cmaps";

function PS_UniMap()
{
    this.unicode;
    this.glyph_index;
}
function PS_UnicodesRec()
{
    this.cmap;
    this.num_maps;
    this.maps;
}
function FT_Service_PsCMapsRec(unicode_value_, unicodes_init_,unicodes_char_index_, unicodes_char_next_, macintosh_name_,
                                    adobe_std_strings_, adobe_std_encoding_, adobe_expert_encoding_)
{
    this.unicode_value = unicode_value_;

    this.unicodes_init = unicodes_init_;
    this.unicodes_char_index = unicodes_char_index_;
    this.unicodes_char_next = unicodes_char_next_;

    this.macintosh_name = macintosh_name_;
    this.adobe_std_strings = adobe_std_strings_;
    this.adobe_std_encoding = adobe_std_strings_;
    this.adobe_expert_encoding = adobe_expert_encoding_;
}
/******************************************************************************/
// postscript-info
/******************************************************************************/
var FT_SERVICE_ID_POSTSCRIPT_INFO = "postscript-info";
function FT_Service_PsInfoRec(get_font_info_, ps_get_font_extra_, has_glyph_names_,get_font_private_, get_font_value_)
{
    this.ps_get_font_info = get_font_info_;
    this.ps_get_font_extra = ps_get_font_extra_;
    this.ps_has_glyph_names = has_glyph_names_;
    this.ps_get_font_private = get_font_private_;
    this.ps_get_font_value = get_font_value_;
}
/******************************************************************************/
// sfnt-table
/******************************************************************************/
var FT_SERVICE_ID_SFNT_TABLE = "sfnt-table";
function FT_Service_SFNT_TableRec(load_, get_, info_)
{
    this.load_table = load_;
    this.get_table = get_;
    this.table_info = info_;
}
/******************************************************************************/
// tt-cmaps
/******************************************************************************/
var FT_SERVICE_ID_TT_CMAP = "tt-cmaps";
function TT_CMapInfo()
{
    this.language;
    this.format;
}
function FT_Service_TTCMapsRec(get_cmap_info_)
{
    this.get_cmap_info = get_cmap_info_;
}
/******************************************************************************/
// truetype-engine
/******************************************************************************/
var FT_SERVICE_ID_TRUETYPE_ENGINE = "truetype-engine";
function FT_Service_TrueTypeEngineRec(engine_type_)
{
    this.engine_type = engine_type_;
}
/******************************************************************************/
// tt-glyf
/******************************************************************************/
var FT_SERVICE_ID_TT_GLYF = "tt-glyf";
function FT_Service_TTGlyfRec(get_location_)
{
    this.get_location = get_location_;
}
/******************************************************************************/
// winfonts
/******************************************************************************/
var FT_SERVICE_ID_WINFNT = "winfonts";
function FT_Service_WinFntRec(get_header_)
{
    this.get_header = get_header_;
}
/******************************************************************************/
// xf86
/******************************************************************************/
var FT_SERVICE_ID_XF86_NAME  = "xf86-driver-name";
var FT_XF86_FORMAT_TRUETYPE  = "TrueType";
var FT_XF86_FORMAT_TYPE_1    = "Type 1";
var FT_XF86_FORMAT_BDF       = "BDF";
var FT_XF86_FORMAT_PCF       = "PCF";
var FT_XF86_FORMAT_TYPE_42   = "Type 42";
var FT_XF86_FORMAT_CID       = "CID Type 1";
var FT_XF86_FORMAT_CFF       = "CFF";
var FT_XF86_FORMAT_PFR       = "PFR";
var FT_XF86_FORMAT_WINFNT    = "Windows FNT";

/******************************************************************************/
function FT_ServiceDescRec(id,data)
{
    this.serv_id = id;
    this.serv_data = data;
}
function ft_service_list_lookup(service_descriptors,service_id)
{
    var c = service_descriptors.length;
    for (var i=0;i<c;i++)
    {
        if (service_descriptors[i].serv_id == service_id)
            return service_descriptors[i].serv_data;
    }
    return null;
}

function FT_FACE_FIND_SERVICE(face, id)
{
    var module = face.driver;
    if (module.clazz.get_interface)
        return module.clazz.get_interface(module, id);
    return null;
}
function FT_FACE_FIND_GLOBAL_SERVICE(face, name)
{
    return ft_module_get_service(face.driver, name);
}
function ft_module_get_service(module, name)
{
    var result = null;

    if (module != null)
    {
        if (null != module.clazz.get_interface)
            result = module.clazz.get_interface(module, name);
        if (null == result)
        {
            var modules = module.library.modules;
            var count = modules.length;
            for (var i = 0;i<count;i++)
            {
                if (modules[i].clazz.get_interface)
                {
                    result = modules[i].clazz.get_interface(modules[i], name);
                    if (null != result)
                        break;
                }
            }
        }
    }

    return result;
}

function FT_ServiceCache()
{
    this.service_POSTSCRIPT_FONT_NAME = null;
    this.service_MULTI_MASTERS = null;
    this.service_GLYPH_DICT = null;
    this.service_PFR_METRICS = null;
    this.service_WINFNT = null;
}
/******************************************************************************/
// stream
/******************************************************************************/
function FT_Frame_Field(v,s,o)
{
    this.value = v;
    this.size = s;
    this.offset = o;
}
function FT_Stream(data, size)
{
    this.obj = null;
    this.data = data;
    this.size = size;
    this.pos = 0;
    this.cur = 0;

    this.Seek = function(_pos)
    {
        if (_pos > this.size)
            return 85;
        this.pos = _pos;
        return 0;
    }
    this.Skip = function(_skip)
    {
        if (_skip < 0)
            return 85;
        return this.Seek(this.pos + _skip);
    }
    this.Read = function(pointer, count)
    {
        return this.ReadAt(this.pos, pointer, count);
    }
    this.ReadArray = function(count)
    {
        var read_bytes = this.size - this.pos;
        if (read_bytes > count)
            read_bytes = count;
        if (0 == read_bytes)
            return null;
        var a = new Array(read_bytes);
        for (var i = 0;i<count;i++)
            a[i] = this.data[this.pos+i];
        return a;
    }
    this.ReadAt = function(_pos, pointer, count)
    {
        if (_pos > this.size)
            return 85;
        var read_bytes = this.size - _pos;
        if (read_bytes > count)
            read_bytes = count;

        FT_Common.memcpy_p2(pointer,this.data,_pos,count);

        this.pos = _pos + read_bytes;

        if (read_bytes < count)
            return 85;

        return 0;
    }
    this.TryRead = function(pointer, count)
    {
        var read_bytes = 0;
        if (this.pos < this.size)
            return read_bytes;
        read_bytes = this.size - this.pos;
        if (read_bytes > count)
            read_bytes = count;

        FT_Common.memcpy_p2(pointer,this.data,this.pos,count);

        this.pos += read_bytes;
        return read_bytes;
    }

    // 1 bytes
    this.GetUChar = function()
    {
        if (this.cur >= this.size)
            return 0;
        return this.data[this.cur++];
    }
    this.GetChar = function()
    {
        if (this.cur >= this.size)
            return 0;
        var m = this.data[this.cur++];
        if (m > 127)
            m -= 256;
        return m;
    }
    this.GetString1 = function(len)
    {
        if (this.cur + len > this.size)
            return "";
        var t = "";
        for (var i = 0; i < len; i++)
            t += String.fromCharCode(this.data[this.cur + i]);
        this.cur += len;
        return t;
    }
    this.ReadString1 = function(len)
    {
        if (this.pos + len > this.size)
            return "";
        var t = "";
        for (var i = 0; i < len; i++)
            t += String.fromCharCode(this.data[this.pos + i]);
        this.pos += len;
        return t;
    }

    this.ReadUChar = function()
    {
        if (this.pos >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return this.data[this.pos++];
    }
    this.ReadChar = function()
    {
        if (this.pos >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        var m = this.data[this.pos++];
        if (m > 127)
            m -= 256;
        return m;
    }

    // 2 byte
    this.GetUShort = function()
    {
        if (this.cur + 1 >= this.size)
            return 0;
        return (this.data[this.cur++] << 8 | this.data[this.cur++]);
    }
    this.GetShort = function()
    {
        return FT_Common.UShort_To_Short(this.GetUShort());
    }
    this.ReadUShort = function()
    {
        if (this.pos + 1 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] << 8 | this.data[this.pos++]);
    }
    this.ReadShort = function()
    {
        return FT_Common.UShort_To_Short(this.ReadUShort());
    }
    this.GetUShortLE = function()
    {
        if (this.cur + 1 >= this.size)
            return 0;
        return (this.data[this.cur++] | this.data[this.cur++] << 8);
    }
    this.GetShortLE = function()
    {
        return FT_Common.UShort_To_Short(this.GetUShortLE());
    }
    this.ReadUShortLE = function()
    {
        if (this.pos + 1 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] | this.data[this.pos++] << 8);
    }
    this.ReadShortLE = function()
    {
        return FT_Common.UShort_To_Short(this.ReadUShortLE());
    }

    // 4 byte
    this.GetULong = function()
    {
        if (this.cur + 3 >= this.size)
            return 0;
        //return (this.data[this.cur++] << 24 | this.data[this.cur++] << 16 | this.data[this.cur++] << 8 | this.data[this.cur++]);
        var s = (this.data[this.cur++] << 24 | this.data[this.cur++] << 16 | this.data[this.cur++] << 8 | this.data[this.cur++]);
        if (s < 0)
            s += 4294967296;
        return s;
    }
    this.GetLong = function()
    {
        // 32-битные числа - по умолчанию знаковые!!!
        //return FT_Common.UintToInt(this.GetULong());
        return (this.data[this.cur++] << 24 | this.data[this.cur++] << 16 | this.data[this.cur++] << 8 | this.data[this.cur++]);
    }
    this.ReadULong = function()
    {
        if (this.pos + 3 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        var s = (this.data[this.pos++] << 24 | this.data[this.pos++] << 16 | this.data[this.pos++] << 8 | this.data[this.pos++]);
        if (s < 0)
            s += 4294967296;
        return s;
    }
    this.ReadLong = function()
    {
        // 32-битные числа - по умолчанию знаковые!!!
        //return FT_Common.Uint_To_int(this.ReadULong());
        if (this.pos + 3 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] << 24 | this.data[this.pos++] << 16 | this.data[this.pos++] << 8 | this.data[this.pos++]);
    }

    this.GetULongLE = function()
    {
        if (this.cur + 3 >= this.size)
            return 0;
        return (this.data[this.cur++] | this.data[this.cur++] << 8 | this.data[this.cur++] << 16 | this.data[this.cur++] << 24);
    }
    this.GetLongLE = function()
    {
        return FT_Common.Uint_To_int(this.GetULongLE());
    }
    this.ReadULongLE = function()
    {
        if (this.pos + 3 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] | this.data[this.pos++] << 8 | this.data[this.pos++] << 16 | this.data[this.pos++] << 24);
    }
    this.ReadLongLE = function()
    {
        return FT_Common.Uint_To_int(this.ReadULongLE());
    }

    // 3 byte
    this.GetUOffset = function()
    {
        if (this.cur + 2 >= this.size)
            return 0;
        return (this.data[this.cur++] << 16 | this.data[this.cur++] << 8 | this.data[this.cur++]);
    }
    this.GetUOffsetLE = function()
    {
        if (this.cur + 2 >= this.size)
            return 0;
        return (this.data[this.cur++] | this.data[this.cur++] << 8 | this.data[this.cur++] << 16);
    }
    this.ReadUOffset = function()
    {
        if (this.pos + 2 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] << 16 | this.data[this.pos++] << 8 | this.data[this.pos++]);
    }
    this.ReadUOffsetLE = function()
    {
        if (this.pos + 2 >= this.size)
        {
            FT_Error = 85;
            return 0;
        }
        FT_Error = 0;
        return (this.data[this.pos++] | this.data[this.pos++] << 8 | this.data[this.pos++] << 16);
    }
    this.EnterFrame = function(count)
    {
        if (this.pos >= this.size || this.size - this.pos < count)
            return 85;

        this.cur = this.pos;
        this.pos += count;
        return 0;
    }
    this.ExtractFrame = function(count, pointer)
    {
        if (null == pointer)
            pointer = new CPointer();

        var err = this.EnterFrame(count);
        if (err != 0)
            return err;

        pointer.data = this.data;
        pointer.pos = this.cur;

        this.cur = 0;
        return err;
    }
    this.ReleaseFrame = function()
    {
    }
    this.ExitFrame = function()
    {
        this.cur = 0;
    }

    this.ReadFields = function(arrayFields, structure)
    {
        // arrayFields : array {value, size, offset}
        // structures : data pointer
        var error = 0;
        var cursor = this.cur;
        var frame_accessed = false;

        var data = null;
        var pos = 0;

        var ind = 0;
        do
        {
            var value;
            var sign_shift;

            switch (arrayFields[ind].value)
            {
                case 4:  /* access a new frame */
                    error = this.EnterFrame(arrayFields[ind].offset);
                    if (error != 0)
                    {
                        if (frame_accessed === true)
                            this.ExitFrame();
                        return error;
                    }

                    frame_accessed = true;
                    cursor = this.cur;
                    ind++;
                    continue;  /* loop! */

                case 24:  /* read a byte sequence */
                case 25:   /* skip some bytes      */
                {
                    var len = arrayFields[ind].size;
                    if ( cursor + len > this.size )
                    {
                        error = 85;
                        if (frame_accessed === true)
                            this.ExitFrame();
                        return error;
                    }

                    if ( arrayFields[ind] == 24 )
                    {
                        data = structure.data;
                        pos = structure.pos + arrayFields[ind].offset;

                        for (var i=0;i<len;i++)
                            data[i] = this.data[cursor+i];
                    }
                    cursor += len;
                    ind++;
                    continue;
                }

                case 8:
                case 9:  /* read a single byte */
                    value = this.data[cursor++];
                    sign_shift = 24;
                    break;

                case 13:
                case 12:  /* read a 2-byte big-endian short */
                    value = this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 16;
                    break;

                case 15:
                case 14:  /* read a 2-byte little-endian short */
                    value = this.data[cursor++] | this.data[cursor++] << 8;
                    sign_shift = 16;
                    break;

                case 17:
                case 16:  /* read a 4-byte big-endian long */
                    value = this.data[cursor++] << 24 | this.data[cursor++] << 16 | this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 0;
                    break;

                case 19:
                case 18:  /* read a 4-byte little-endian long */
                    value = this.data[cursor++] | this.data[cursor++] << 8 | this.data[cursor++] << 16 | this.data[cursor++] << 24;
                    sign_shift = 0;
                    break;

                case 21:
                case 20:  /* read a 3-byte big-endian long */
                    value = this.data[cursor++] << 16 | this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 8;
                    break;

                case 23:
                case 22:  /* read a 3-byte little-endian long */
                    value = this.data[cursor++] | this.data[cursor++] << 8 | this.data[cursor++] << 16;
                    sign_shift = 8;
                    break;

                default:
                    /* otherwise, exit the loop */
                    this.cur = cursor;
                    if (frame_accessed === true)
                        this.ExitFrame();
                    return error;
            }

            /* now, compute the signed value is necessary */
            if ( arrayFields[ind].value & 1 )
                value = (( value << sign_shift ) >>> sign_shift) & 0xFFFFFFFF;

            /* finally, store the value in the object */

            data = structure.data;
            pos = structure.pos + arrayFields[ind].offset;
            switch (arrayFields[ind])
            {
                case 1:
                    data[pos] = value & 0xFF;
                    break;

                case 2:
                    data[pos] = (value >>> 8)&0xFF;
                    data[pos+1] = value&0xFF;
                    break;

                case 4:
                    data[pos] = (value >>> 24)&0xFF;
                    data[pos+1] = (value >>> 16)&0xFF;
                    data[pos+2] = (value >>> 8)&0xFF;
                    data[pos+3] = value&0xFF;
                    break;

                default:
                    data[pos] = (value >>> 24)&0xFF;
                    data[pos+1] = (value >>> 16)&0xFF;
                    data[pos+2] = (value >>> 8)&0xFF;
                    data[pos+3] = value&0xFF;
            }

            /* go to next field */
            ind++;
        }
        while ( 1 );

        return error;
    }
    this.ReadFields2 = function(fields, structure)
    {
        // arrayFields : array {value, size, offset}
        // structures : data pointer
        var error = 0;
        var cursor = this.cur;
        var frame_accessed = false;

        var data = null;
        var pos = 0;

        var ind = 0;
        do
        {
            var value;
            var sign_shift;

            var fval = fields[ind];
            var fsize = fields[ind+1];
            var foffset = fields[ind+2];

            switch (fval)
            {
                case 4:  /* access a new frame */
                    error = this.EnterFrame(foffset);
                    if (error != 0)
                    {
                        if (frame_accessed === true)
                            this.ExitFrame();
                        return error;
                    }

                    frame_accessed = true;
                    cursor = this.cur;
                    ind+=3;
                    continue;  /* loop! */

                case 24:  /* read a byte sequence */
                case 25:   /* skip some bytes      */
                {
                    if ( cursor + fsize > this.size )
                    {
                        error = 85;
                        if (frame_accessed === true)
                            this.ExitFrame();
                        return error;
                    }

                    if ( fval == 24 )
                    {
                        data = structure.data;
                        pos = structure.pos + arrayFields[ind].offset;

                        for (var i=0;i<len;i++)
                            data[i] = this.data[cursor+i];
                    }
                    cursor += fsize;
                    ind++;
                    continue;
                }

                case 8:
                case 9:  /* read a single byte */
                    value = this.data[cursor++];
                    sign_shift = 24;
                    break;

                case 13:
                case 12:  /* read a 2-byte big-endian short */
                    value = this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 16;
                    break;

                case 15:
                case 14:  /* read a 2-byte little-endian short */
                    value = this.data[cursor++] | this.data[cursor++] << 8;
                    sign_shift = 16;
                    break;

                case 17:
                case 16:  /* read a 4-byte big-endian long */
                    value = this.data[cursor++] << 24 | this.data[cursor++] << 16 | this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 0;
                    break;

                case 19:
                case 18:  /* read a 4-byte little-endian long */
                    value = this.data[cursor++] | this.data[cursor++] << 8 | this.data[cursor++] << 16 | this.data[cursor++] << 24;
                    sign_shift = 0;
                    break;

                case 21:
                case 20:  /* read a 3-byte big-endian long */
                    value = this.data[cursor++] << 16 | this.data[cursor++] << 8 | this.data[cursor++];
                    sign_shift = 8;
                    break;

                case 23:
                case 22:  /* read a 3-byte little-endian long */
                    value = this.data[cursor++] | this.data[cursor++] << 8 | this.data[cursor++] << 16;
                    sign_shift = 8;
                    break;

                default:
                    /* otherwise, exit the loop */
                    this.cur = cursor;
                    if (frame_accessed === true)
                        this.ExitFrame();
                    return error;
            }

            /* now, compute the signed value is necessary */
            if (0 != (fval & 1))
                value = (( value << sign_shift ) >>> sign_shift) & 0xFFFFFFFF;

            /* finally, store the value in the object */

            data = structure.data;
            pos = structure.pos + arrayFields[ind].offset;
            switch (arrayFields[ind])
            {
                case 1:
                    data[pos] = value & 0xFF;
                    break;

                case 2:
                    data[pos] = (value >>> 8)&0xFF;
                    data[pos+1] = value&0xFF;
                    break;

                case 4:
                    data[pos] = (value >>> 24)&0xFF;
                    data[pos+1] = (value >>> 16)&0xFF;
                    data[pos+2] = (value >>> 8)&0xFF;
                    data[pos+3] = value&0xFF;
                    break;

                default:
                    data[pos] = (value >>> 24)&0xFF;
                    data[pos+1] = (value >>> 16)&0xFF;
                    data[pos+2] = (value >>> 8)&0xFF;
                    data[pos+3] = value&0xFF;
            }

            /* go to next field */
            ind+=3;
        }
        while ( 1 );

        return error;
    }
}
/******************************************************************************/
// memory
/******************************************************************************/
function CPointer()
{
    this.obj    = null;
    this.data   = null;
    this.pos    = 0;
}
function dublicate_pointer(p)
{
    if (null == p)
        return null;

    var d = new CPointer();
    d.data = p.data;
    d.pos = p.pos;
    return d;
}
function copy_pointer(p, size)
{
    var _p = g_memory.Alloc(size);
    for (var i = 0; i < size; i++)
        _p.data[i] = p.data[p.pos + i];
    return _p;
}

function FT_Memory()
{
    this.canvas = document.createElement('canvas');
    this.canvas.width = 1;
    this.canvas.height = 1;
    this.ctx    = this.canvas.getContext('2d');

    this.Alloc = function(size)
    {
        var p = new CPointer();
        p.obj = this.ctx.createImageData(1,parseInt((size + 3) / 4));
        p.data = p.obj.data;
        p.pos = 0;
        return p;
    }
    this.AllocHeap = function()
    {
        // TODO: нужно посмотреть, как эта память будет использоваться.
        // нужно ли здесь делать стек, либо все время от нуля делать??
    }
    this.CreateStream = function(size)
    {
        var _size = parseInt((size + 3) / 4);
        var obj = this.ctx.createImageData(1,_size);
        return new FT_Stream(obj.data,_size);
    }
}
var g_memory = new FT_Memory();

function FT_PEEK_CHAR(p)
{
    var r = p.data[p.pos];
    if (r > 127)
        return r-256;
    return r;
}
function FT_NEXT_CHAR(p)
{
    var r = p.data[p.pos++];
    if (r > 127)
        return r - 256;
    return r;
}
function FT_PEEK_BYTE(p)
{
    return p.data[p.pos];
}
function FT_NEXT_BYTE(p)
{
    var r = p.data[p.pos++];
    return r;
}
function FT_PEEK_SHORT(p)
{
    var r = (p.data[p.pos] << 8 | p.data[p.pos+1]);
    if (r > 32767)
        return r - 65536;
    return r;
}
function FT_PEEK_SHORT_LE(p)
{
    var r = (p.data[p.pos+1] << 8 | p.data[p.pos]);
    if (r > 32767)
        return r - 65536;
    return r;
}
function FT_NEXT_SHORT(p)
{
    var r = (p.data[p.pos++] << 8 | p.data[p.pos++]);
    if (r > 32767)
        return r - 65536;
    return r;
}
function FT_PEEK_USHORT(p)
{
    var r = (p.data[p.pos] << 8 | p.data[p.pos+1]);
    return r;
}
function FT_PEEK_USHORT_LE(p)
{
    var r = (p.data[p.pos + 1] << 8 | p.data[p.pos]);
    return r;
}
function FT_NEXT_USHORT(p)
{
    var r = (p.data[p.pos++] << 8 | p.data[p.pos++]);
    return r;
}
function FT_PEEK_UOFF3(p)
{
    var r = (p.data[p.pos] << 16 | p.data[p.pos+1] << 8 | p.data[p.pos+2]);
    return r;
}
function FT_NEXT_UOFF3(p)
{
    var r = (p.data[p.pos++] << 16 | p.data[p.pos++] << 8 | p.data[p.pos++]);
    return r;
}
function FT_PEEK_LONG(p)
{
    return (p.data[p.pos] << 24 | p.data[p.pos+1] << 16 | p.data[p.pos+2] << 8 | p.data[p.pos+3]);
}
function FT_NEXT_LONG(p)
{
    var r = (p.data[p.pos++] << 24 | p.data[p.pos++] << 16 | p.data[p.pos++] << 8 | p.data[p.pos++]);
    return r;
}
function FT_PEEK_ULONG(p)
{
    var r = (p.data[p.pos] << 24 | p.data[p.pos+1] << 16 | p.data[p.pos+2] << 8 | p.data[p.pos+3]);
    if (r<0)
        r+=4294967296;
    return r;
}
function FT_PEEK_ULONG_LE(p)
{
    var r = (p.data[p.pos+3] << 24 | p.data[p.pos+2] << 16 | p.data[p.pos+1] << 8 | p.data[p.pos]);
    if (r<0)
        r+=4294967296;
    return r;
}
function FT_NEXT_ULONG(p)
{
    var r = (p.data[p.pos++] << 24 | p.data[p.pos++] << 16 | p.data[p.pos++] << 8 | p.data[p.pos++]);
    if (r<0)
        r+=4294967296;
    return r;
}
function FT_PEEK_String1(p,len)
{
    var t = "";
    for (var i = 0; i < len; i++)
    {
        var r = p.data[p.pos + i];
        if (r == 0)
            return t;
        t += String.fromCharCode(r);
    }
    return t;
}
function FT_NEXT_String1(p,len)
{
    var t = "";
    for (var i = 0; i < len; i++)
        t += String.fromCharCode(p.data[p.pos + i]);
    p.pos+=len;
    return t;
}
function ft_memchr(p, v, size)
{
    for (var i=0;i<size;i++)
        if (p.data[p.pos+i] == v)
            return i;
    return -1;
}
function _strncmp(s1,s2,n)
{
    var m1 = s1.length;
    var m2 = s2.length;
    var l = n;
    if (m1 < l)
        l = m1;
    if (m2 < l)
        l = m2;

    for (var i=0;i<l;i++)
    {
        var c = s1.charCodeAt(i) - s2.charCodeAt(i);
        if (c != 0)
            return c;
    }
    return m1 - m2;
}

function _strcmp_data(s,p)
{
    var m = s.length;

    for (var i=0;i<m;i++)
    {
        var _c = p.data[p.pos + i];
        if (_c == 0)
            return 1;

        var c = s.charCodeAt(i) - _c;
        if (c != 0)
            return c;
    }

    if (p.data[p.pos + m] == 0)
        return 0;

    return -1;
}

function _strcmp(s1,s2)
{
    return (s1 == s2) ? 0 : 1;
}

function _strncmp_data(p,s,n)
{
    var m2 = s.length;
    var l = n;
    if (m2 < l)
        l = m2;

    var m1 = 0;
    for (var i=0;i<l;i++)
    {
        m1++;
        if (0 == p.data[p.pos + i])
        {
            m1 = i;
            break;
        }

        var c = p.data[p.pos + i] - s.charCodeAt(i);
        if (c != 0)
            return c;
    }
    return m1 - m2;
}
function _strncmp2(s1,s2,n,start1,start2)
{
    var m1 = s1.length - start1;
    var m2 = s2.length - start2;
    var l = n;
    if (m1 < l)
        l = m1;
    if (m2 < l)
        l = m2;

    for (var i=0;i<l;i++)
    {
        var c = s1.charCodeAt(i + start1) - s2.charCodeAt(i + start2);
        if (c != 0)
            return c;
    }
    return m1 - m2;
}
function _mem_strcpyn1(dst, src, size)
{
    var i = 0;
    var slen = src.length;
    while (size > 1 && i < slen)
    {
        dst.data[dst.pos + i] = src.charCodeAt(i);
        i++;
    }

    dst.data[dst.pos + i] = 0;  /* always zero-terminate */
    return i != slen;
}
/******************************************************************************/
// calc
/******************************************************************************/
function FT_LOAD_TARGET_MODE(x)
{
    return ((x >>> 16) & 15);
}
function FT_LOAD_TARGET(x)
{
    return ((x & 15) << 16);
}

function FT_PAD_FLOOR(x, n)
{
    return x & (~(n-1));
}
function FT_PAD_ROUND(x, n)
{
    return (x + (n >>> 1)) & (~(n-1));
}
function FT_PAD_CEIL(x, n)
{
    return (x + n - 1) & (~(n-1));
}

function FT_PIX_FLOOR(x)
{
    return x & ~63;
}
function FT_PIX_ROUND(x)
{
    return (x + 32) & ~63;
}
function FT_PIX_CEIL(x)
{
    return (x + 63) & ~63;
}

function FT_RoundFix(a)
{
    return (a >= 0) ? (a + 0x8000) & ~0xFFFF : -((-a + 0x8000) & ~0xFFFF);
}
function FT_CeilFix(a)
{
    return (a >= 0) ? (a + 0xFFFF) & ~0xFFFF : -((-a + 0xFFFF) & ~0xFFFF);
}
function FT_FloorFix(a)
{
    return (a >= 0) ?  a & ~0xFFFF : -((-a) & ~0xFFFF);
}

// int64 support
function _FT_MulDiv(a,b,c)
{
    var s = 1;
    var d = 0;

    if ( a < 0 ) { a = -a; s = -1; }
    if ( b < 0 ) { b = -b; s = -s; }
    if ( c < 0 ) { c = -c; s = -s; }

    d = (c > 0 ? parseInt((a * b + (c >>> 1)) / c) : 0x7FFFFFFF);
    return (s > 0) ? d : -d;
}
function _FT_MulFix(a,b)
{
    var s = 1;
    if (a < 0)
    {
        a = -a;
        s = -1;
    }
    if (b < 0)
    {
        b = -b;
        s = -s;
    }

    var c = ((a * b + 0x8000) >>> 16) & 0xFFFFFFFF;
    return ( s > 0 ) ? c : -c;
}
function _FT_DivFix(a,b)
{
    var q;
    var s = 1;
    if ( a < 0 ) { a = -a; s = -1; }
    if ( b < 0 ) { b = -b; s = -s; }

    if (b == 0)
        q = 0x7FFFFFFF;
    else
    {
        q = parseInt(((a * 65536) + (b >>> 1)) / b);
        if (q < 0)
            q += 4294967296;
    }

    return (s < 0 ? -q : q);
}

function Int64()
{
    this.lo = 0;
    this.hi = 0;
}
function ft_multo64(x, y, z)
{
    var lo1 = x & 0x0000FFFF;  var hi1 = x >>> 16;
    var lo2 = y & 0x0000FFFF;  var hi2 = y >>> 16;

    var lo = lo1 * lo2;
    var i1 = lo1 * hi2;
    var i2 = lo2 * hi1;
    var hi = hi1 * hi2;

    /* Check carry overflow of i1 + i2 */
    i1 += i2;
    if (i1 < i2)
        hi += (1 << 16);
    
    hi += (i1 >>> 16);
    i1  = (i1 << 16) & 0xFFFFFFFF;
    if (i1 < 0)
        i1 += 4294967296;

    /* Check carry overflow of i1 + lo */
    lo += i1;

    if (lo >= 4294967296)
        lo = FT_Common.IntToUInt(lo & 0xFFFFFFFF);

    if (lo < i1)
        hi++;

    z.lo = lo;
    z.hi = hi;
}
function ft_div64by32(hi, lo, y)
{
    var q = 0;
    var r = hi;

    if (r >= y)
        return 0x7FFFFFFF;

    var i = 32;
    do
    {
        r = (r << 1) & 0xFFFFFFFF;
        q = (q << 1) & 0xFFFFFFFF;

        r  |= (lo >>> 31);
        if (r < 0)
            r += 4294967296;

        if (r >= y)
        {
            r -= y;
            q |= 1;
        }

        if (q < 0)
            q += 4294967296;

        lo = (lo << 1) & 0xFFFFFFFF;
        if (lo < 0)
            lo += 4294967296;
    } while ( --i );

    return q;
}
function FT_Add64(x, y, z)
{
    var lo = x.lo + y.lo;
    var hi = x.hi + y.hi;
    if (lo < x.lo)
        hi++;

    z.lo = lo;
    z.hi = hi;
}
var temp1 = new Int64();
var temp2 = new Int64();
function FT_MulDiv(a, b, c)
{
    if (a > 2147483647 || b > 2147483647 || c > 2147483647)
        alert("error");

    if (a == 0 || b == c)
        return a;

    var s  = a; a = Math.abs(a);
    s ^= b; b = Math.abs(b);
    s ^= c; c = Math.abs(c);

    if (a <= 46340 && b <= 46340 && c <= 176095 && c > 0)
        a = ((a * b + (c >> 1)) / c) >> 0;
    else if (FT_Common.UintToInt(c) > 0)
    {
        ft_multo64(a >= 0 ? a : a + 4294967296, b >= 0 ? b : b + 4294967296, temp1);

        temp2.hi = 0;
        temp2.lo = (c >>> 1);
        FT_Add64(temp1, temp2, temp1);
        a = ft_div64by32(temp1.hi, temp1.lo, c);
    }
    else
        a = 0x7FFFFFFF;

    return (s < 0 ? -a : a);
}
function FT_MulFix(a, b)
{
    if (a > 2147483647 || b > 2147483647)
    {
		//alert("error");
		a &= 0xFFFFFFFF;
		b &= 0xFFFFFFFF;
	}

    if (a == 0 || b == 0x10000)
        return a;

    var s = a; a = Math.abs(a);
    s ^= b; b = Math.abs(b);

    var ua = a < 0 ? a + 4294967296 : a;
    var ub = b < 0 ? b + 4294967296 : b;

    if (ua <= 2048 && ub <= 1048576)
        ua = (ua * ub + 0x8000) >>> 16;
    else
    {
        var al = ua & 0xFFFF;

        ua = ((ua >>> 16) * ub) + (al * (ub >>> 16)) + ((al * (ub & 0xFFFF) + 0x8000) >>> 16);
    }
    var _l = FT_Common.UintToInt(ua);
    return (s < 0 ? -_l : _l);
}
function FT_DivFix(a, b)
{
    if (a > 2147483647 || b > 2147483647)
    {
        a = (a & 0xFFFFFFFF);
        b = (b & 0xFFFFFFFF);
    }
    
    var q = 0;

    var s = 1;
    if ( a < 0 ) { a = -a; s = -1; }
    if ( b < 0 ) { b = -b; s = -s; }

    if (b == 0)
    {
        q = 0x7FFFFFFF;
    }
    else if ((a >>> 16) == 0)
    {
        q = ((a * 65536 + (b >>> 1)) / b) >> 0;
        if (q < 0)
            q += 4294967296;
    }
    else
    {
        temp1.hi  = (a >>> 16);
        temp1.lo  = (a << 16) & 0xFFFFFFFF;
        if (temp1.lo < 0)
            temp1.lo += 4294967296;

        temp2.hi = 0;
        temp2.lo = (b >>> 1);

        if (temp2.lo < 0)
            temp2.lo += 4294967296;

        FT_Add64(temp1, temp2, temp1);
        q = ft_div64by32(temp1.hi, temp1.lo, b < 0 ? b + 4294967296 : b);
    }

    var _q = (q > 2147483647) ? q - 4294967296 : q;
    return (s < 0) ? -_q : _q;
}

function FT_Sqrt32(x)
{
    var val, root, newroot, mask;

    root = 0;
    mask = 0x40000000;
    val  = x;

    do
    {
        newroot = root + mask;
        if ( newroot <= val )
        {
            val -= newroot;
            root = newroot + mask;
        }

        root >>>= 1;
        mask >>>= 2;

    } while ( mask != 0 );

    return root;
}

function FT_Matrix_Multiply(a, b)
{
    var xx, xy, yx, yy;

    if ( !a || !b )
        return;

    xx = FT_MulFix(a.xx, b.xx) + FT_MulFix(a.xy, b.yx);
    xy = FT_MulFix(a.xx, b.xy) + FT_MulFix(a.xy, b.yy);
    yx = FT_MulFix(a.yx, b.xx) + FT_MulFix(a.yy, b.yx);
    yy = FT_MulFix(a.yx, b.xy) + FT_MulFix(a.yy, b.yy);

    b.xx = xx;  b.xy = xy;
    b.yx = yx;  b.yy = yy;
}

function FT_Matrix_Invert(matrix)
{
    var delta, xx, yy;

    if (!matrix)
        return 6;

    delta = FT_MulFix(matrix.xx, matrix.yy) - FT_MulFix(matrix.xy, matrix.yx);
    if ( !delta )
        return 6;

    matrix.xy = -FT_DivFix(matrix.xy, delta);
    matrix.yx = -FT_DivFix(matrix.yx, delta);

    xx = matrix.xx;
    yy = matrix.yy;

    matrix.xx = FT_DivFix(yy, delta);
    matrix.yy = FT_DivFix(xx, delta);

    return 0;
}

function FT_Matrix_Multiply_Scaled(a, b, scaling)
{
    var xx, xy, yx, yy;
    var val = 0x10000 * scaling;
    if ( !a || !b )
        return;

    xx = FT_MulDiv(a.xx, b.xx, val) + FT_MulDiv(a.xy, b.yx, val);
    xy = FT_MulDiv(a.xx, b.xy, val) + FT_MulDiv(a.xy, b.yy, val);
    yx = FT_MulDiv(a.yx, b.xx, val) + FT_MulDiv(a.yy, b.yx, val);
    yy = FT_MulDiv(a.yx, b.xy, val) + FT_MulDiv(a.yy, b.yy, val);

    b.xx = xx; b.xy = xy;
    b.yx = yx; b.yy = yy;
}

function FT_Vector_Transform_Scaled(vector, matrix, scaling)
{
    var xz, yz;
    var val = 0x10000 * scaling;
    if (!vector || !matrix)
        return;

    xz = FT_MulDiv(vector.x, matrix.xx, val) + FT_MulDiv(vector.y, matrix.xy, val);
    yz = FT_MulDiv(vector.x, matrix.yx, val) + FT_MulDiv(vector.y, matrix.yy, val);

    vector.x = xz;
    vector.y = yz;
}

function FT_SqrtFixed(x)
{
    var root, rem_hi, rem_lo, test_div;
    var count;

    root = 0;
    if (x > 0)
    {
        rem_hi = 0;
        rem_lo = x;
        count  = 24;
        do
        {
            rem_hi   = (rem_hi << 2) | (rem_lo >>> 30);
            rem_lo <<= 2;
            root   <<= 1;
            test_div = (root << 1) + 1;

            if (rem_hi >= test_div)
            {
                rem_hi -= test_div;
                root   += 1;
            }
        } while (--count);
    }

    return root;
}

function ft_corner_orientation(in_x, in_y, out_x, out_y)
{
    var result;
    if ( in_y == 0 )
    {
        if ( in_x >= 0 )
            result = out_y;
        else
            result = -out_y;
    }
    else if ( in_x == 0 )
    {
        if ( in_y >= 0 )
            result = -out_x;
        else
            result = out_x;
    }
    else if ( out_y == 0 )
    {
        if ( out_x >= 0 )
            result = in_y;
        else
            result = -in_y;
    }
    else if ( out_x == 0 )
    {
        if ( out_y >= 0 )
            result = -in_x;
        else
            result =  in_x;
    }
    else
    {
        var delta = in_x * out_y - in_y * out_x;

        if ( delta == 0 )
            result = 0;
        else
            result = 1 - 2 * ( delta < 0 );
    }

    return result;
}

function ft_corner_is_flat(in_x, in_y, out_x, out_y)
{
    var ax = in_x;
    var ay = in_y;

    var d_in, d_out, d_corner;

    if ( ax < 0 )
        ax = -ax;
    if ( ay < 0 )
        ay = -ay;
    d_in = ax + ay;

    ax = out_x;
    if ( ax < 0 )
        ax = -ax;
    ay = out_y;
    if ( ay < 0 )
        ay = -ay;
    d_out = ax + ay;

    ax = out_x + in_x;
    if ( ax < 0 )
        ax = -ax;
    ay = out_y + in_y;
    if ( ay < 0 )
        ay = -ay;
    d_corner = ax + ay;

    return (( d_in + d_out - d_corner ) < ( d_corner >>> 4 )) ? 1 : 0;
}
/******************************************************************************/
// glyph
/******************************************************************************/
// bitmap
function ft_bitmap_glyph_init(glyph, slot)
{
    var library = glyph.library;

    if (slot.format != 1651078259)
        return 18;

    glyph.left = slot.bitmap_left;
    glyph.top  = slot.bitmap_top;

    if ((slot.internal.flags & 1) != 0)
    {
        var d = glyph.bitmap;
        var s = slot.bitmap;

        d.rows = s.rows;
        d.width = s.width;
        d.pitch = s.pitch;
        d.buffer = dublicate_pointer(s.buffer);
        d.num_grays = s.num_grays;
        d.pixel_mode = s.pixel_mode;
        d.palette_mode = s.palette_mode;
        d.palette = s.palette;

        slot.internal.flags &= ~1;
    }
    else
    {
        FT_Bitmap_New(glyph.bitmap);
        return FT_Bitmap_Copy(library, slot.bitmap, glyph.bitmap);
    }
    return 0;
}
function ft_bitmap_glyph_copy(source, target)
{
    target.left = source.left;
    target.top  = source.top;
    return FT_Bitmap_Copy(source.library, source.bitmap, target.bitmap);
}
function ft_bitmap_glyph_done(bitmap_glyph)
{
    FT_Bitmap_Done(bitmap_glyph.library, bitmap_glyph.bitmap);
}
function ft_bitmap_glyph_bbox(glyph, cbox)
{
    cbox.xMin = glyph.left << 6;
    cbox.xMax = cbox.xMin + (glyph.bitmap.width << 6);
    cbox.yMax = glyph.top << 6;
    cbox.yMin = cbox.yMax - (glyph.bitmap.rows << 6);
}

// outline
function ft_outline_glyph_init(glyph, slot)
{
    var source = slot.outline;
    var target = glyph.outline;


    /* check format in glyph slot */
    if (slot.format != 1869968492)
        return 18;

    var error = FT_Outline_New(glyph.library, source.n_points, source.n_contours, glyph.outline);
    if (error != 0)
        return error;

    FT_Outline_Copy(source, target);
    return error;
}
function ft_outline_glyph_done(glyph)
{
    FT_Outline_Done(glyph.library, glyph.outline);
}
function ft_outline_glyph_copy(source, target)
{
    var library = source.library;
    var error = FT_Outline_New(library, source.outline.n_points, source.outline.n_contours, target.outline);
    if (error != 0)
        FT_Outline_Copy(source.outline, target.outline);

    return error;
}
function ft_outline_glyph_transform(glyph, matrix, delta)
{
    if (matrix != null)
        FT_Outline_Transform(glyph.outline, matrix);

    if (delta != null)
        FT_Outline_Translate(glyph.outline, delta.x, delta.y);
}
function ft_outline_glyph_bbox(glyph, bbox)
{
    FT_Outline_Get_CBox(glyph.outline, bbox);
}
function ft_outline_glyph_prepare(glyph, slot)
{
    slot.format         = 1869968492;

    var d = slot.outline;
    var s = glyph.outline;

    d.n_contours = s.n_contours;
    d.n_points = s.n_points;
    d.points = s.points;
    d.tags = s.tags;
    d.contours = s.contours;
    d.flags = s.flags;

    slot.outline.flags &= ~1;
    return 0;
}

function _ft_bitmap_glyph_class()
{
    this.glyph_size = 0; // glyph_type
    this.glyph_format = 1651078259;
    this.glyph_init = ft_bitmap_glyph_init;
    this.glyph_done = ft_bitmap_glyph_done;
    this.glyph_copy = ft_bitmap_glyph_copy;
    this.glyph_transform = null;
    this.glyph_bbox = ft_bitmap_glyph_bbox;
    this.glyph_prepare = null;
}
var ft_bitmap_glyph_class = new _ft_bitmap_glyph_class();
function _ft_outline_glyph_class()
{
    this.glyph_size = 1; // glyph_type
    this.glyph_format = 1869968492;
    this.glyph_init = ft_outline_glyph_init;
    this.glyph_done = ft_outline_glyph_done;
    this.glyph_copy = ft_outline_glyph_copy;
    this.glyph_transform = ft_outline_glyph_transform;
    this.glyph_bbox = ft_outline_glyph_bbox;
    this.glyph_prepare = ft_outline_glyph_prepare;
}
var ft_outline_glyph_class = new _ft_outline_glyph_class();

function FT_GlyphRec()
{
    this.library = null;
    this.clazz = null;
    this.format = 0;
    this.advance = new FT_Vector();
}
function FT_BitmapGlyphRec()
{
    this.library = null;
    this.clazz = null;
    this.format = 0;
    this.advance = new FT_Vector();

    this.left = 0;
    this.top = 0;
    this.bitmap = new FT_Bitmap();
}
function FT_OutlineGlyphRec()
{
    this.library = null;
    this.clazz = null;
    this.format = 0;
    this.advance = new FT_Vector();

    this.outline = new FT_Outline();
}
function ft_new_glyph(library, clazz)
{
    var glyph = null;
    if (clazz.glyph_size == 0)
        glyph = new FT_BitmapGlyphRec();
    else
        glyph = new FT_OutlineGlyphRec();

    glyph.library = library;
    glyph.clazz = clazz;
    glyph.format = clazz.glyph_format;
    return glyph;
}
/******************************************************************************/
// glyphloader
/******************************************************************************/
function FT_CreateVectorArray(array, start, count)
{
    for (var i = start + count - 1; i >= start; i--)
        array[i] = new FT_Vector();
}
function FT_CreateArray(array, start, count)
{
    for (var i = start + count - 1; i >= start; i--)
        array[i] = 0;
}
function FT_CreateArraySubGlyphs(array, start, count)
{
    for (var i = start + count - 1; i >= start; i--)
        array[i] = new FT_SubGlyph();
}
function FT_OutlineCur()
{
    this.n_contours = 0;
    this.n_points = 0;

    this.points = 0;
    this.tags = 0;
    this.contours = 0;

    this.flags = 0;
}
function FT_SubGlyph()
{
    this.index = 0;
    this.flags = 0;
    this.arg1 = 0;
    this.arg2 = 0;
    this.transform = new FT_Matrix();
}
function FT_GlyphLoad()
{
    this.outline = new FT_Outline();
    this.extra_points = null;
    this.extra_points2 = 0;
    this.num_subglyphs = 0;
    this.subglyphs = null;
}
function FT_GlyphLoadCur()
{
    this.outline = new FT_OutlineCur();
    this.extra_points = 0;
    this.extra_points2 = 0;
    this.num_subglyphs = 0;
    this.subglyphs = 0;
}
function FT_GlyphLoader()
{
    this.memory = null;
    this.max_points = 0;
    this.max_contours = 0;
    this.max_subglyphs = 0;
    this.use_extra = 0;

    this.base = new FT_GlyphLoad();
    this.current = new FT_GlyphLoadCur();

    this.other = null;
}

function FT_GlyphLoader_New(memory)
{
    var loader = new FT_GlyphLoader();
    loader.memory = memory;
    return loader;
}
function FT_GlyphLoader_Rewind(loader)
{
    var base    = loader.base;
    var current = loader.current;

    base.outline.n_points   = 0;
    base.outline.n_contours = 0;
    base.num_subglyphs      = 0;

    var _cur = current.outline;
    _cur.n_contours = 0;
    _cur.n_points = 0;
    _cur.points = 0;
    _cur.tags = 0;
    _cur.contours = 0;
    _cur.flags = 0;

    current.extra_points = 0;
    current.extra_points2 = 0;
    current.num_subglyphs = 0;
    current.subglyphs = 0;

    current.extra_points  = 0;
    current.extra_points2 = base.extra_points2;
}
function FT_GlyphLoader_Reset(loader)
{
    var base = loader.base;

    base.outline.points = null;
    base.outline.tags = null;
    base.outline.contours = null;
    base.extra_points = null;
    base.extra_points2 = 0;
    base.subglyphs = null;

    loader.max_points    = 0;
    loader.max_contours  = 0;
    loader.max_subglyphs = 0;

    FT_GlyphLoader_Rewind(loader);
}
function FT_GlyphLoader_Done(loader)
{
    if (loader)
        FT_GlyphLoader_Reset(loader);
}
function FT_GlyphLoader_Adjust_Points(loader)
{
    var base    = loader.base.outline;
    var current = loader.current.outline;

    current.points   = base.n_points;
    current.tags     = base.n_points;
    current.contours = base.n_contours;

    if (loader.use_extra == 1)
    {
        loader.current.extra_points  = base.n_points;
        loader.current.extra_points2 = loader.base.extra_points2 + base.n_points;
    }
}
function FT_GlyphLoader_CreateExtra(loader)
{
    var c = 2*loader.max_points;
    loader.base.extra_points = new Array(c);
    FT_CreateVectorArray(loader.base.extra_points, 0, c);
    loader.use_extra = 1;
    loader.base.extra_points2 = loader.max_points;
    FT_GlyphLoader_Adjust_Points(loader);
    return 0;
}
function FT_GlyphLoader_Adjust_Subglyphs(loader)
{
    loader.current.subglyphs = loader.base.num_subglyphs;
}
function FT_GlyphLoader_CheckPoints(loader, n_points, n_contours)
{
    var base = loader.base.outline;
    var current = loader.current.outline;
    var adjust = 0;

    var new_max = base.n_points + current.n_points + n_points;
    var old_max = loader.max_points;

    if (new_max > old_max)
    {
        new_max = (new_max + 7) & ~7;

        if (new_max > 32767)
            return FT_Error.FT_Err_Array_Too_Large;

        if (null == base.points)
            base.points = new Array(new_max);
        if (null == base.tags)
            base.tags = new Array(new_max);

        FT_CreateVectorArray(base.points, old_max, new_max - old_max);
        FT_CreateArray(base.tags, old_max, new_max - old_max);

        if (1 == loader.use_extra)
        {
            if (null == loader.base.extra_points)
                loader.base.extra_points = new Array(2 * (new_max - old_max));
            FT_CreateVectorArray(loader.base.extra_points, old_max * 2, 2 * (new_max - old_max));
            loader.base.extra_points2 = new_max;
        }

        adjust = 1;
        loader.max_points = new_max;
    }

    /* check contours */
    old_max = loader.max_contours;
    new_max = base.n_contours + current.n_contours + n_contours;
    if (new_max > old_max)
    {
        new_max = (new_max + 4) & ~4;

        if (new_max > 32767)
            return 10;

        if (base.contours == null)
            base.contours = new Array(new_max);
        FT_CreateArray(base.contours, old_max, new_max - old_max);

        adjust = 1;
        loader.max_contours = new_max;
    }

    if (adjust == 1)
        FT_GlyphLoader_Adjust_Points(loader);

    return 0;
}
function FT_GlyphLoader_CheckSubGlyphs(loader, n_subs)
{
    var base    = loader.base;
    var current = loader.current;

    var new_max = base.num_subglyphs + current.num_subglyphs + n_subs;
    var old_max = loader.max_subglyphs;
    if (new_max > old_max)
    {
        new_max = (new_max + 2) & ~2;

        if (null == base.subglyphs)
            base.subglyphs = new Array(new_max);
        FT_CreateArraySubGlyphs(base.subglyphs, old_max, new_max - old_max);
        loader.max_subglyphs = new_max;
        FT_GlyphLoader_Adjust_Subglyphs(loader);
    }
    return 0;
}
function FT_GlyphLoader_Prepare(loader)
{
    var current = loader.current;

    current.outline.n_points   = 0;
    current.outline.n_contours = 0;
    current.num_subglyphs      = 0;

    FT_GlyphLoader_Adjust_Points(loader);
    FT_GlyphLoader_Adjust_Subglyphs(loader);
}
function FT_GlyphLoader_Add(loader)
{
    if ( !loader )
        return;

    var base    = loader.base;
    var current = loader.current;

    var n_curr_contours = current.outline.n_contours;
    var n_base_points   = base.outline.n_points;

    base.outline.n_points = base.outline.n_points + current.outline.n_points;
    base.outline.n_contours = base.outline.n_contours + current.outline.n_contours;

    base.num_subglyphs += current.num_subglyphs;

    var mass = base.outline.contours;
    var start = current.outline.contours;
    for (var n = 0; n < n_curr_contours; n++)
        mass[start+n] = mass[start+n] + n_base_points;

    FT_GlyphLoader_Prepare(loader);
}
function FT_GlyphLoader_CopyPoints(target, source)
{
    var num_points   = source.base.outline.n_points;
    var num_contours = source.base.outline.n_contours;


    var error = FT_GlyphLoader_CheckPoints(target, num_points, num_contours);
    if (error != 0)
    {
        var _out = target.base.outline;
        var _in = source.base.outline;

        var i=0;

        var out_p = _out.points;
        var in_p = _in.points;
        var out_t = _out.tags;
        var in_t = _in.tags;

        for (i=0;i<num_points;i++)
        {
            out_p[i].x = in_p[i].x;
            out_p[i].y = in_p[i].y;

            out_t[i] = in_t[i];
        }

        var out_c = _out.contours;
        var in_c = _in.contours;
        for (i=0;i<num_contours;i++)
            out_c[i] = in_c[i];

        if (1 == target.use_extra && 1 == source.use_extra)
        {
            var out_e = target.base.extra_points;
            var in_e = source.base.extra_points;
            var c = 2 * num_points;
            for (i=0;i<c;i++)
            {
                out_e[i].x = in_e[i].x;
                out_e[i].y = in_e[i].y;
            }
        }

        _out.n_points   = num_points;
        _out.n_contours = num_contours;

        FT_GlyphLoader_Adjust_Points(target);
    }

    return error;
}
function FT_GLYPHLOADER_CHECK_POINTS(_loader,_points,_contours)
{
    if (_points == 0 || (_loader.base.outline.n_points + _loader.current.outline.n_points + _points) <= _loader.max_points)
    {
        if (_contours == 0 || (_loader.base.outline.n_contours + _loader.current.outline.n_contours + _contours) <= _loader.max_contours)
            return 0;
    }
    return FT_GlyphLoader_CheckPoints(_loader,_points,_contours);
}
/******************************************************************************/
// ftmm
/******************************************************************************/
function FT_MM_Axis()
{
    this.name = null;
    this.minimum = 0;
    this.maximum = 0;
}
function FT_Var_Axis()
{
    this.name = null;

    this.minimum = 0;
    this.def = 0;
    this.maximum = 0;

    this.tag = 0;
    this.strid = 0;
}
function FT_Var_Named_Style()
{
    this.coords = null;
    this.strid = 0;
}
function FT_MM_Var()
{
    this.num_axis = 0;
    this.num_designs = 0;
    this.num_namedstyles = 0;
    this.axis = null;
    this.namedstyle = null;

    this.dublicate = function()
    {
        var res = new FT_MM_Var();
        res.num_axis = this.num_axis;
        res.num_designs = this.num_designs;
        res.num_namedstyles = this.num_namedstyles;

        var c = res.num_axis;
        if (c != 0)
        {
            res.axis = new Array(c);
            for (var i=0;i<c;i++)
            {
                res.axis[i] = new FT_Var_Axis();
                var _m = res.axis[i];
                var _s = this.axis[i];

                _m.name = _s.name;
                _m.minimum = _s.minimum;
                _m.def = _s.def;
                _m.maximum = _s.maximum;
                _m.tag = _s.tag;
                _m.strid = _s.strid;
            }
        }
        c = res.num_namedstyles;
        if (c != 0)
        {
            res.namedstyle = new Array(c);
            for (var i=0;i<c;i++)
            {
                res.namedstyle[i] = new FT_Var_Named_Style();
                var _m = res.namedstyle[i];
                var _s = this.namedstyle[i];

                _m.strid = _s.strid;
                if (null != _s.coords)
                    _m.coords = _s.coords.splice(0,0);
            }
        }
    }
}
function FT_Multi_Master()
{
    this.num_axis = 0;
    this.num_designs = 0;
    this.axis = new Array(4);

    for (var i = 0; i < 4; i++)
        this.axis[i] = new FT_MM_Axis();
}
/******************************************************************************/
// ftdriver
/******************************************************************************/
function FT_Driver_Class()
{
    this.flags = 0;
    this.name = "";
    this.version = 0;
    this.requires = 0;

    this.module_interface = null;

    this.init = null;
    this.done = null;
    this.get_interface = null;

    this.face_object_size = 0;
    this.size_object_size = 0;
    this.slot_object_size = 0;

    this.init_face = null;
    this.done_face = null;

    this.init_size = null;
    this.done_size = null;

    this.init_slot = null;
    this.done_slot = null;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.set_char_sizes = null;
    this.set_pixel_sizes = null;
    //#endif

    this.load_glyph = null;

    this.get_kerning = null;
    this.attach_file = null;
    this.get_advances = null;

    this.request_size = null;
    this.select_size = null;
}
//#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
function ft_stub_set_char_sizes(size, width, height, horz_res, vert_res)
{
    var driver = size.face.driver;
    if (driver.clazz.request_size != null)
    {
        var req = new FT_Size_RequestRec();
        req.type   = 0;
        req.width  = width;
        req.height = height;

        if (horz_res == 0)
            horz_res = vert_res;

        if (vert_res == 0)
            vert_res = horz_res;

        if (horz_res == 0)
            horz_res = vert_res = 72;

        req.horiResolution = horz_res;
        req.vertResolution = vert_res;

        return driver.clazz.request_size(size, req);
    }
    return 0;
}
function  ft_stub_set_pixel_sizes(size, width, height)
{
    var driver = size.face.driver;
    if (driver.clazz.request_size)
    {
        var req = new FT_Size_RequestRec();
        req.type           = 0;
        req.width          = width  << 6;
        req.height         = height << 6;
        req.horiResolution = 0;
        req.vertResolution = 0;
        return driver.clazz.request_size(size, req);
    }
    return 0;
}
//#endif
/******************************************************************************/
// ftvalid
/******************************************************************************/
function FT_ValidatorRec()
{
    this.base = new CPointer();
    this.limit = 0;
    this.level = 0
    this.error = 0
}
/******************************************************************************/
// pshints
/******************************************************************************/
/******************************************************************************/
// tttypes
/******************************************************************************/
function TTC_HeaderRec()
{
    this.tag = 0;
    this.version = 0;
    this.count = 0;
    this.offsets = null;
}
function SFNT_HeaderRec()
{
    this.format_tag = 0;
    this.num_tables = 0;
    this.search_range = 0;
    this.entry_selector = 0;
    this.range_shift = 0;

    this.offset = 0;  /* not in file */
}
function TT_Table()
{
    this.Tag = 0;
    this.CheckSum = 0;
    this.Offset = 0;
    this.Length = 0;
}
function TT_LongMetricsRec()
{
    this.advance = 0;
    this.bearing = 0;
}
function TT_NameEntryRec()
{
    this.platformID = 0;
    this.encodingID = 0;
    this.languageID = 0;
    this.nameID = 0;
    this.stringLength = 0;
    this.stringOffset = 0;
    this.string = null;
}
function TT_NameTableRec()
{
    this.format = 0;
    this.numNameRecords = 0;
    this.storageOffset = 0;
    this.names = null;
    this.stream = null;
}
function TT_GaspRange()
{
    this.maxPPEM = 0;
    this.gaspFlag = 0;
}
function TT_Gasp()
{
    this.version = 0;
    this.numRanges = 0;
    this.gaspRanges = null;
}
//#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
function TT_HdmxEntryRec()
{
    this.ppem = 0;
    this.max_width = 0;
    this.widths = null;
}
function TT_HdmxRec()
{
    this.version = 0;
    this.num_records = 0;
    this.records = null;
}
function TT_Kern0_PairRec()
{
    this.left = 0;
    this.right = 0;
    this.value = 0;
}
//#endif
function TT_SBit_MetricsRec()
{
    this.height = 0;
    this.width = 0;

    this.horiBearingX = 0;
    this.horiBearingY = 0;
    this.horiAdvance = 0;

    this.vertBearingX = 0;
    this.vertBearingY = 0;
    this.vertAdvance = 0;
}
function TT_SBit_Small_Metrics()
{
    this.height = 0;
    this.width = 0;

    this.bearingX = 0;
    this.bearingY = 0;
    this.advance = 0;
}
function TT_SBit_LineMetricsRec()
{
    this.ascender = 0;
    this.descender = 0;
    this.max_width = 0;
    this.caret_slope_numerator = 0;
    this.caret_slope_denominator = 0;
    this.caret_offset = 0;
    this.min_origin_SB = 0;
    this.min_advance_SB = 0;
    this.max_before_BL = 0;
    this.min_after_BL = 0;

    this.pad1 = 0;
    this.pad2 = 0;
}
function TT_SBit_RangeRec()
{
    this.first_glyph = 0;
    this.last_glyph = 0;

    this.index_format = 0;
    this.image_format = 0;
    this.image_offset = 0;

    this.image_size = 0;
    this.metrics = new TT_SBit_MetricsRec();
    this.num_glyphs = 0;

    this.glyph_offsets = null;
    this.glyph_codes = null;

    this.table_offset = 0;
}
function TT_SBit_StrikeRec()
{
    this.num_ranges = 0;
    this.sbit_ranges = null;
    this.ranges_offset = 0;

    this.color_ref = 0;

    this.hori = new TT_SBit_LineMetricsRec();
    this.vert = new TT_SBit_LineMetricsRec();

    this.start_glyph = 0;
    this.end_glyph = 0;

    this.x_ppem = 0;
    this.y_ppem = 0;

    this.bit_depth = 0;
    this.flags = 0;
}
function TT_SBit_ComponentRec()
{
    this.glyph_code = 0;
    this.x_offset = 0;
    this.y_offset = 0;
}
function TT_SBit_ScaleRec()
{
    this.hori = new TT_SBit_LineMetricsRec();
    this.vert = new TT_SBit_LineMetricsRec();

    this.x_ppem = 0;
    this.y_ppem = 0;

    this.x_ppem_substitute = 0;
    this.y_ppem_substitute = 0;
}
function TT_Post_20Rec()
{
    this.num_glyphs = 0;
    this.num_names = 0;
    this.glyph_indices = null;
    this.glyph_names = null;
}
function TT_Post_25Rec()
{
    this.num_glyphs = 0;
    this.offsets = null;
}
function TT_Post_NamesRec()
{
    this.loaded = false;
    this.format = null;
}
//#ifdef TT_CONFIG_OPTION_BDF
function TT_BDFRec()
{
    this.table = null;
    this.table_end = 0;
    this.strings = null;
    this.strings_size = 0;
    this.num_strikes = 0;
    this.loaded = 0;
}
//#endif
function TT_GlyphZoneRec()
{
    this.memory = null;
    this.max_points = 0;
    this.max_contours = 0;
    this.n_points = 0;    /* number of points in zone    */
    this.n_contours = 0;  /* number of contours          */

    this.org = null;         /* original point coordinates  */
    this.cur = null;         /* current point coordinates   */
    this.orus = null;        /* original (unscaled) point coordinates */

    this.tags = null;        /* current touch flags         */
    this.contours = null;    /* contour end points          */

    this._offset_org = 0;
    this._offset_cur = 0;
    this._offset_orus = 0;
    this._offset_tags = 0;
    this._offset_contours = 0;

    this.first_point = 0; /* offset of first (#0) point  */
}
TT_GlyphZoneRec.prototype =
{
    Clear : function()
    {
        this.memory = null;
        this.max_points = 0;
        this.max_contours = 0;
        this.n_points = 0;
        this.n_contours = 0;

        this.org = null;
        this.cur = null;
        this.orus = null;

        this.tags = null;
        this.contours = null;

        this._offset_org = 0;
        this._offset_cur = 0;
        this._offset_orus = 0;
        this._offset_tags = 0;
        this._offset_contours = 0;

        this.first_point = 0;
    },

    Copy : function(src)
    {
        this.memory = src.memory;
        this.max_points = src.max_points;
        this.max_contours = src.max_contours;
        this.n_points = src.n_points;
        this.n_contours = src.n_contours;

        this.org = src.org;
        this.cur = src.cur;
        this.orus = src.orus;

        this.tags = src.tags;
        this.contours = src.contours;

        this._offset_org = src._offset_org;
        this._offset_cur = src._offset_cur;
        this._offset_orus = src._offset_orus;
        this._offset_tags = src._offset_tags;
        this._offset_contours = src._offset_contours;

        this.first_point = src.first_point;
    }
}

function TT_LoaderRec()
{
    this.face = null;
    this.size = null;
    this.glyph = null;
    this.gloader = null;

    this.load_flags = 0;
    this.glyph_index = 0;

    this.stream = null;
    this.byte_len = 0;

    this.n_contours = 0;
    this.bbox = new FT_BBox();
    this.left_bearing = 0;
    this.advance = 0;
    this.linear = 0;
    this.linear_def = 0;
    this.preserve_pps = 0;
    this.pp1 = new FT_Vector();
    this.pp2 = new FT_Vector();

    this.glyf_offset = 0;

    this.base = new TT_GlyphZoneRec();
    this.zone = new TT_GlyphZoneRec();

    this.exec = null;
    this.instructions = null;
    this.ins_pos = 0;

    this.other = null;

    this.top_bearing = 0;
    this.vadvance = 0;
    this.pp3 = new FT_Vector();
    this.pp4 = new FT_Vector();

    this.cursor = 0;
    this.limit = 0;

    this.Clear = function()
    {
        this.load_flags = 0;
        this.glyph_index = 0;

        this.stream = null;
        this.byte_len = 0;

        this.n_contours = 0;
        this.bbox.xMin = 0;
        this.bbox.yMin = 0;
        this.bbox.xMax = 0;
        this.bbox.yMax = 0;
        this.left_bearing = 0;
        this.advance = 0;
        this.linear = 0;
        this.linear_def = 0;
        this.preserve_pps = 0;
        this.pp1.x = 0;
        this.pp1.y = 0;
        this.pp2.x = 0;
        this.pp2.y = 0;

        this.glyf_offset = 0;

        this.exec = null;
        this.instructions = null;
        this.ins_pos = 0;

        this.other = null;

        this.top_bearing = 0;
        this.vadvance = 0;
        this.pp3.x = 0;
        this.pp3.y = 0;
        this.pp4.x = 0;
        this.pp4.y = 0;

        this.cursor = 0;
        this.limit = 0;

        this.gloader = null;
    }
}
/******************************************************************************/
// tttables
/******************************************************************************/
function TT_Header()
{
    this.Table_Version = 0;
    this.Font_Revision = 0;

    this.CheckSum_Adjust = 0;
    this.Magic_Number = 0;

    this.Flags = 0;
    this.Units_Per_EM = 0;

    this.Created1 = 0;
    this.Created2 = 0;
    this.Modified1 = 0;
    this.Modified2 = 0;

    this.xMin = 0;
    this.yMin = 0;
    this.xMax = 0;
    this.yMax = 0;

    this.Mac_Style = 0;
    this.Lowest_Rec_PPEM = 0;

    this.Font_Direction = 0;
    this.Index_To_Loc_Format = 0;
    this.Glyph_Data_Format = 0;
}
function TT_HoriHeader()
{
    this.Version = 0;
    this.Ascender = 0;
    this.Descender = 0;
    this.Line_Gap = 0;

    this.advance_Width_Max = 0;

    this.min_Left_Side_Bearing = 0;
    this.min_Right_Side_Bearing = 0;
    this.xMax_Extent = 0;
    this.caret_Slope_Rise = 0;
    this.caret_Slope_Run = 0;
    this.caret_Offset = 0;

    this.Reserved1 = 0;
    this.Reserved2 = 0;
    this.Reserved3 = 0;
    this.Reserved4 = 0;

    this.metric_Data_Format = 0;
    this.number_Of_HMetrics = 0;

    this.long_metrics = null;
    this.short_metrics = null;
}
function TT_VertHeader()
{
    this.Version = 0;
    this.Ascender = 0;
    this.Descender = 0;
    this.Line_Gap = 0;

    this.advance_Height_Max = 0;

    this.min_Top_Side_Bearing = 0;
    this.min_Bottom_Side_Bearing = 0;
    this.yMax_Extent = 0;
    this.caret_Slope_Rise = 0;
    this.caret_Slope_Run = 0;
    this.caret_Offset = 0;

    this.Reserved1 = 0;
    this.Reserved2 = 0;
    this.Reserved3 = 0;
    this.Reserved4 = 0;

    this.metric_Data_Format = 0;
    this.number_Of_VMetrics = 0;

    this.long_metrics = null;
    this.short_metrics = null;
}
function TT_OS2()
{
    this.version = 0;
    this.xAvgCharWidth = 0;
    this.usWeightClass = 0;
    this.usWidthClass = 0;
    this.fsType = 0;
    this.ySubscriptXSize = 0;
    this.ySubscriptYSize = 0;
    this.ySubscriptXOffset = 0;
    this.ySubscriptYOffset = 0;
    this.ySuperscriptXSize = 0;
    this.ySuperscriptYSize = 0;
    this.ySuperscriptXOffset = 0;
    this.ySuperscriptYOffset = 0;
    this.yStrikeoutSize = 0;
    this.yStrikeoutPosition = 0;
    this.sFamilyClass = 0;

    this.panose = new Array(10);

    this.ulUnicodeRange1 = 0;        /* Bits 0-31   */
    this.ulUnicodeRange2 = 0;        /* Bits 32-63  */
    this.ulUnicodeRange3 = 0;        /* Bits 64-95  */
    this.ulUnicodeRange4 = 0;        /* Bits 96-127 */

    this.achVendID = new Array(4);

    this.fsSelection = 0;
    this.usFirstCharIndex = 0;
    this.usLastCharIndex = 0;
    this.sTypoAscender = 0;
    this.sTypoDescender = 0;
    this.sTypoLineGap = 0;
    this.usWinAscent = 0;
    this.usWinDescent = 0;

    /* only version 1 tables: */
    this.ulCodePageRange1 = 0;       /* Bits 0-31   */
    this.ulCodePageRange2 = 0;       /* Bits 32-63  */

    /* only version 2 tables: */
    this.sxHeight = 0;
    this.sCapHeight = 0;
    this.usDefaultChar = 0;
    this.usBreakChar = 0;
    this.usMaxContext = 0;
}
function TT_Postscript()
{
    this.FormatType = 0;
    this.italicAngle = 0;
    this.underlinePosition = 0;
    this.underlineThickness = 0;
    this.isFixedPitch = 0;
    this.minMemType42 = 0;
    this.maxMemType42 = 0;
    this.minMemType1 = 0;
    this.maxMemType1 = 0;
}
function TT_PCLT()
{
    this.Version = 0;
    this.FontNumber = 0;
    this.Pitch = 0;
    this.xHeight = 0;
    this.Style = 0;
    this.TypeFamily = 0;
    this.CapHeight = 0;
    this.SymbolSet = 0;
    this.TypeFace = "";
    this.CharacterComplement = "";
    this.FileName = "";
    this.StrokeWeight = 0;
    this.WidthType = 0;
    this.SerifStyle = 0;
    this.Reserved = 0;
}
function TT_MaxProfile()
{
    this.version = 0;
    this.numGlyphs = 0;
    this.maxPoints = 0;
    this.maxContours = 0;
    this.maxCompositePoints = 0;
    this.maxCompositeContours = 0;
    this.maxZones = 0;
    this.maxTwilightPoints = 0;
    this.maxStorage = 0;
    this.maxFunctionDefs = 0;
    this.maxInstructionDefs = 0;
    this.maxStackElements = 0;
    this.maxSizeOfInstructions = 0;
    this.maxComponentElements = 0;
    this.maxComponentDepth = 0;
}
function FT_Get_Sfnt_Table(face, tag)
{
    var table = null;
    if (face && ((face.face_flags & 8) != 0))
    {
        var service = FT_FACE_FIND_SERVICE( face, "sfnt-table");
        if (service != null)
            table = service.get_table(face, tag);
    }
    return table;
}
function FT_Load_Sfnt_Table(face, tag, offset, buffer, length)
{
    if (face == null || ((face.face_flags & 8) == 0))
        return 35;

    var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_SFNT_TABLE);
    if (service == null)
        return 7;

    return service.load_table(face, tag, offset, buffer, length);
}
function FT_Sfnt_Table_Info(face, table_index, tag, length)
{
    if (face == null || ((face.face_flags & 8) == 0))
        return 35;

    var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_SFNT_TABLE);
    if (service == null)
        return 7;

    return service.table_info(face, table_index, tag);
}
function FT_Get_CMap_Language_ID(charmap)
{
    if (charmap == null || charmap.face == null)
        return 0;

    var service = FT_FACE_FIND_SERVICE(charmap.face, FT_SERVICE_ID_TT_CMAP);
    if (service == null)
        return 0;
    var cmap_info = new TT_CMapInfo();
    if (0 != service.get_cmap_info( charmap, cmap_info))
        return 0;
    return cmap_info.language;
}
/******************************************************************************/
// ftimage
/******************************************************************************/
function FT_Vector()
{
    this.x = 0;
    this.y = 0;
}
function dublicate_vector(v)
{
    var _v = new FT_Vector();
    _v.x = v.x;
    _v.y = v.y;
    return _v;
}
function copy_vector(dst, src)
{
    dst.x = src.x;
    dst.y = src.y;
}
function FT_BBox()
{
    this.xMin = 0;
    this.yMin = 0;
    this.xMax = 0;
    this.yMax = 0;
}
function dublicate_bbox(v)
{
    var _v = new FT_BBox();
    _v.xMin = v.xMin;
    _v.yMin = v.yMin;
    _v.xMax = v.xMax;
    _v.yMax = v.yMax;
    return _v;
}
function FT_Bitmap()
{
    this.rows = 0;
    this.width = 0;
    this.pitch = 0;
    this.buffer = null;
    this.num_grays = 0;
    this.pixel_mode = 0;
    this.palette_mode = 0;
    this.palette = null;
}
function DoNullBitmap(im)
{
    im.rows = 0;
    im.width = 0;
    im.pitch = 0;
    im.buffer = null;
    im.num_grays = 0;
    im.pixel_mode = 0;
    im.palette_mode = 0;
    im.palette = null;
}
function FT_Outline()
{
    this.n_contours = 0;
    this.n_points = 0;

    this.points = null;
    this.tags = null;
    this.contours = null;

    this.flags = 0;
}
function EquatingOutline(d, s)
{
    d.n_contours = s.n_contours;
    d.n_points = s.n_points;
    d.points = s.points;
    d.tags = s.tags;
    d.contours = s.contours;
    d.flags = s.flags;
}
function FT_Span()
{
    this.x = 0;
    this.len = 0;
    this.coverage = 0;
}
function FT_Raster_Params()
{
    this.target = null;
    this.source = null;
    this.flags = 0;
    this.gray_spans = null;
    this.black_spans = null;
    this.bit_test = null;
    this.bit_set = null;
    this.user = null;
    this.clip_box = new FT_BBox();
}
/******************************************************************************/
// ftbitmap
/******************************************************************************/
function FT_Bitmap_New(bitmap)
{
    DoNullBitmap(bitmap);
}
function FT_Bitmap_Copy(library, source, target)
{
    var pitch  = source.pitch;
    if (source == target)
        return 0;

    if (source.buffer == null)
    {
        target.rows = source.rows;
        target.width = source.width;
        target.pitch = source.pitch;
        target.buffer = source.buffer;
        target.num_grays = source.num_grays;
        target.pixel_mode = source.pixel_mode;
        target.palette_mode = source.palette_mode;
        target.palette = source.palette;

        return 0;
    }

    if (pitch < 0)
        pitch = -pitch;
    var size = (pitch * source.rows);

    if (target.buffer != null)
    {
        var target_pitch = target.pitch;

        if (target_pitch < 0 )
            target_pitch = -target_pitch;
        var target_size = (target_pitch * target.rows);

        if (target_size != size)
        {
            target.buffer = null;
            target.buffer = g_memory.Alloc(size);//new Array(size);
        }
    }
    else
        target.buffer = g_memory.Alloc(size);//new Array(size);

    var s = source.buffer;
    var d = target.buffer;
    for (var i=0;i<size;i++)
        d[i]=s[i];

    return 0;
}
function FT_Bitmap_Done(library, bitmap)
{
    DoNullBitmap(bitmap);
    return 0;
}
/******************************************************************************/
// ftsnames
/******************************************************************************/
function FT_SfntName()
{
    this.platform_id = 0;
    this.encoding_id = 0;
    this.language_id = 0;
    this.name_id = 0;

    this.string = "";
    this.string_len = 0;
}
function FT_Get_Sfnt_Name_Count(face)
{
    return (face != null && (face.face_flags & 8) != 0) ? face.num_names : 0;
}
function FT_Get_Sfnt_Name(face, idx, aname)
{
    var error = 6;
    if (face != null && (face.face_flags & 8) != 0)
    {
        if (idx < face.num_names)
        {
            var entry = face.name_table.names[idx];

            if (entry.stringLength > 0 && entry.string == null)
            {
                var stream = face.stream;
                error = stream.Seek(entry.stringOffset);
                entry.string = stream.ReadString1(entry.stringLength);
                error = FT_Error;

                if (0 != error)
                {
                    entry.string = null;
                    entry.stringLength = 0;
                }
            }

            aname.platform_id = entry.platformID;
            aname.encoding_id = entry.encodingID;
            aname.language_id = entry.languageID;
            aname.name_id     = entry.nameID;
            aname.string      = entry.string;
            aname.string_len  = entry.stringLength;
        }
    }
    return 0;
}
/******************************************************************************/
// ftincrem
/******************************************************************************/
function FT_Incremental_MetricsRec()
{
    this.bearing_x = 0;
    this.bearing_y = 0;
    this.advance = 0;
    this.advance_v = 0;
}
function FT_Incremental_FuncsRec()
{
    this.get_glyph_data = null;
    this.free_glyph_data = null;
    this.get_glyph_metrics = null;
}
function FT_Incremental_Interface()
{
    this.funcs = null;
    this.object = null;
}
/******************************************************************************/
// fttypes
/******************************************************************************/
function FT_Matrix()
{
    this.xx = 0;
    this.xy = 0;
    this.yx = 0;
    this.yy = 0;
}
function dublicate_matrix(m)
{
    var _m = new FT_Matrix();
    _m.xx = m.xx;
    _m.xy = m.xy;
    _m.yx = m.yx;
    _m.yy = m.yy;
    return _m;
}
function FT_Data()
{
    this.pointer = null;
    this.length = 0;
}
function FT_Generic()
{
    this.data = null;
    this.finalizer = null;
}
function FT_Glyph_Metrics()
{
    this.width = 0;
    this.height = 0;

    this.horiBearingX = 0;
    this.horiBearingY = 0;
    this.horiAdvance = 0;

    this.vertBearingX = 0;
    this.vertBearingY = 0;
    this.vertAdvance = 0;

}
function FT_Bitmap_Size()
{
    this.height = 0;
    this.width = 0;

    this.size = 0;

    this.x_ppem = 0;
    this.y_ppem = 0;
}
function FT_CharMapRec()
{
    this.face = null;
    this.encoding = 0;
    this.platform_id = 0;
    this.encoding_id = 0;
}
function FT_Face()
{
    this.num_faces = 0;
    this.face_index = 0;

    this.face_flags = 0;
    this.style_flags = 0;

    this.num_glyphs = 0;

    this.family_name = "";
    this.style_name = "";

    this.num_fixed_sizes = 0;
    this.available_sizes = [];

    this.num_charmaps = 0;
    this.charmaps = [];

    this.generic = new FT_Generic();

    /*# The following member variables (down to `underline_thickness') */
    /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
    /*# for bitmap fonts.                                              */
    this.bbox = new FT_BBox();

    this.units_per_EM = 0;
    this.ascender = 0;
    this.descender = 0;
    this.height = 0;

    this.max_advance_width = 0;
    this.max_advance_height = 0;

    this.underline_position = 0;
    this.underline_thickness = 0;

    this.glyph = null;
    this.size = null;
    this.charmap = null;

    /*@private begin */
    this.driver = null;
    this.memory = null;
    this.stream = null;

    this.sizes_list = [];

    this.autohint = [];
    this.extensions = null;

    this.internal = null;
    /*@private end */
}
function FT_Size_Metrics()
{
    this.x_ppem = 0;
    this.y_ppem = 0;

    this.x_scale = 0;
    this.y_scale = 0;

    this.ascender = 0;
    this.descender = 0;
    this.height = 0;
    this.max_advance = 0;
}
FT_Size_Metrics.prototype =
{
    Copy : function(src)
    {
        this.x_ppem = src.x_ppem;
        this.y_ppem = src.y_ppem;

        this.x_scale = src.x_scale;
        this.y_scale = src.y_scale;

        this.ascender = src.ascender;
        this.descender = src.descender;
        this.height = src.height;
        this.max_advance = src.max_advance;
    }
};

function FT_Size()
{
    this.face = null;
    this.generic = null;
    this.metrics = new FT_Size_Metrics();
    this.internal = null;
}
function FT_GlyphSlot()
{
    this.library = null;
    this.face = null;
    this.next = null;
    this.reserved = 0;       /* retained for binary compatibility */
    this.generic = null;

    this.metrics = new FT_Glyph_Metrics();
    this.linearHoriAdvance = 0;
    this.linearVertAdvance = 0;
    this.advance = new FT_Vector();

    this.format = 0;

    this.bitmap = new FT_Bitmap();
    this.bitmap_left = 0;
    this.bitmap_top = 0;

    this.outline = new FT_Outline();

    this.num_subglyphs = 0;
    this.subglyphs = [];

    this.control_data = null;
    this.control_len = 0;

    this.lsb_delta = 0;
    this.rsb_delta = 0;

    this.other = null;

    this.internal = null;

    this.base_root = null;
}
function FT_Open_Args()
{
    this.flags = null;
    this.memory_base = null;
    this.memory_size = null;
    this.pathname = "";
    this.stream = null;
    this.driver = null;
    this.num_params = 0;
    this.params = null;
}
function FT_Size_RequestRec()
{
    this.type = 0;
    this.width = 0;
    this.height = 0;
    this.horiResolution = 0;
    this.vertResolution = 0;
}
function FT_CMapRec()
{
    this.charmap = new FT_CharMapRec();
    this.clazz = null;

    this.type = 0;
}
function __FT_CMapRec(val)
{
    switch (val.type)
    {
        case 0:
            return val;
        case 1:
            return val.cmap;
        case 2:
        case 3:
        case 4:
        case 5:
            return val.cmap.cmap;
        default:
            break;
    }
    return val;
}
function __FT_TTCMapRec(val)
{
    switch (val.type)
    {
        case 0:
            return null;
        case 1:
            return val;
        case 2:
        case 3:
        case 4:
        case 5:
            return val.cmap;
        default:
            break;
    }
    return null;
}
function __FT_CharmapRec(val)
{
    switch (val.type)
    {
        case 0:
            return val.charmap;
        case 1:
            return val.cmap.charmap;
        case 2:
        case 3:
        case 4:
        case 5:
            return val.cmap.cmap.charmap;
        default:
            break;
    }
    return val.charmap;
}
function FT_CMap_ClassRec()
{
    this.size = 0;
    this.init = null;
    this.done = null;
    this.char_index = null;
    this.char_next = null;

    this.char_var_index = null;
    this.char_var_default = null;
    this.variant_list = null;
    this.charvariant_list = null;
    this.variantchar_list = null;
}
function create_cmap_class_rec(size_,init_,done_,char_index_,char_next_,var_index_,var_default_,
                               var_list_,char_var_list_, var_char_list_)
{
    var c = new FT_CMap_ClassRec();
    c.size = size_;
    c.init = init_;
    c.done = done_;
    c.char_index = char_index_;
    c.char_next = char_next_;

    c.char_var_index = var_index_;
    c.char_var_default = var_default_;
    c.variant_list = var_list_;
    c.charvariant_list = char_var_list_;
    c.variantchar_list = var_char_list_;
    return c;
}
function FT_Face_Internal()
{
    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.reserved1 = 0;
    this.reserved2 = 0;
    //#endif
    this.transform_matrix = new FT_Matrix();
    this.transform_delta = new FT_Vector();
    this.transform_flags = 0;

    this.services = new FT_ServiceCache();

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    this.incremental_interface = null;
    //#endif

    this.ignore_unpatented_hinter = false;
    this.refcount = 0;
}
function FT_Slot_Internal()
{
    this.loader = null;
    this.flags = 0;
    this.glyph_transformed = false;
    this.glyph_matrix = new FT_Matrix();
    this.glyph_delta = new FT_Vector();
    this.glyph_hints = null;
}
function FT_Module()
{
    this.clazz = null;      // FT_Module_Class
    this.library = null;    // FT_Library
    this.memory = null;     // FT_Memory
    this.generic = null;    // FT_Generic
}
function FT_Driver()
{
    this.root = new FT_Module();
    this.clazz = new FT_Driver_Class();
    this.faces_list = [];
    this.extensions = null;
    this.glyph_loader = null;
}
function FT_Module_Class()
{
    this.flags = 0;
    this.name = "";
    this.version = 0;
    this.requires = 0;

    this.module_interface = null;

    this.init = null;
    this.done = null;
    this.get_interface = null;
}
/******************************************************************************/
// outline
/******************************************************************************/
function FT_Outline_New(library, numPoints, numContours, anoutline)
{
    if (library == null)
        return 33;

    return FT_Outline_New_Internal(library.Memory, numPoints, numContours, anoutline);
}
function FT_Outline_New_Internal(memory, numPoints, numContours, anoutline)
{
    if (null == anoutline || null == memory)
        return 6;

    var _points = new Array(numPoints);
    for (var i = 0; i < numPoints; i++)
        _points[i] = new FT_Vector();

    var _tags = new Array(numPoints);
    for (var i = 0; i < numPoints; i++)
        _tags[i] = 0;
    var _contours = new Array(numContours);
    for (var i = 0; i < numContours; i++)
        _contours[i] = 0;

    anoutline.points = _points;
    anoutline.tags = _tags;
    anoutline.contours = _contours;

    anoutline.n_points    = numPoints & 0xFFFF;
    anoutline.n_contours  = numContours & 0xFFFF;
    anoutline.flags      |= 1;

    return 0;
}
function FT_Outline_Check(outline)
{
    if (outline != null)
    {
        var n_points   = outline.n_points;
        var n_contours = outline.n_contours;
        var end0, end;
        var n;

        if (n_points == 0 && n_contours == 0)
            return 0;

        if (n_points <= 0 || n_contours <= 0)
            return 6;

        end0 = end = -1;
        var _c = outline.contours;
        for (n = 0; n < n_contours; n++)
        {
            end = _c[n];
            if (end <= end0 || end >= n_points)
                return 6;
            end0 = end;
        }

        if ( end != n_points - 1 )
            return 6;

        return 0;
    }
    return 6;
}
function FT_Outline_Copy(source, target)
{
    if (null == source || null == target || source.n_points != target.n_points || source.n_contours != target.n_contours)
        return 6;

    if (source == target)
        return 0;

    var n_p = source.n_points;
    var s_p = source.points;
    var d_p = target.points;
    var s_t = source.tags;
    var t_t = target.tags;
    for (var i = 0; i < n_p; i++)
    {
        d_p[i].x = s_p[i].x;
        d_p[i].y = s_p[i].y;

        t_t[i] = s_t[i];
    }

    var n_c = source.n_contours;
    var s_c = source.contours;
    var t_c = target.contours;
    for (var i = 0; i < source.n_contours; i++)
    {
        t_c[i] = s_c[i];
    }

    var is_owner = target.flags & 1;
    target.flags = source.flags;

    target.flags &= ~1;
    target.flags |= is_owner;

    return 0;
}
function FT_Outline_Done_Internal(memory, outline)
{
    if (memory != null && outline != null)
    {
        if (outline.flags & 1)
        {
            outline.points = null;
            outline.tags = null;
            outline.contours = null;
        }

        outline.n_points = 0;
        outline.n_contours = 0;
        outline.flags = 0;

        return 0;
    }
    else
        return 6;
}
function FT_Outline_Done(library, outline)
{
    if (library == null)
        return 33;

    return FT_Outline_Done_Internal(library.memory, outline);
}
/******************************************************************************/
// obj
/******************************************************************************/
function find_unicode_charmap(face)
{
    if (null == face || null == face.charmaps)
        return 38;

    var count = Math.min(face.charmaps.length, face.num_charmaps);
    if (0 == count)
        return 38;

    var cur = count - 1;
    for ( ; cur >= 0; cur--)
    {
        var cmap = __FT_CharmapRec(face.charmaps[cur]);
        if (cmap.encoding == 1970170211)
        {
            if ((cmap.platform_id == 3 && cmap.encoding_id == 10) ||
                (cmap.platform_id == 0 && cmap.encoding_id == 4))
            {
                //#ifdef FT_MAX_CHARMAP_CACHEABLE
                if (cur > 15)
                {
                    continue;
                }
                //#endif
                face.charmap = face.charmaps[cur];
                return 0;
            }
        }
    }
    cur = count - 1;
    for ( ; cur >= 0; cur--)
    {
        var cmap = __FT_CharmapRec(face.charmaps[cur]);
        if (cmap.encoding == 1970170211)
        {
            //#ifdef FT_MAX_CHARMAP_CACHEABLE
            if (cur > 15)
            {
                continue;
            }
            //#endif
            face.charmap = face.charmaps[cur];
            return 0;
        }
    }
    return 38;
}
function ft_glyphslot_init(slot)
{
    var driver = slot.face.driver;
    slot.library = driver.library;
    slot.internal = new FT_Slot_Internal();

    var error = 0;
    if ((driver.flags & 512) == 0)
        slot.internal.loader = FT_GlyphLoader_New(driver.memory);

    if (driver.clazz.init_slot)
        error = driver.clazz.init_slot(slot);
    return error;
}
function ft_glyphslot_free_bitmap(slot)
{
    if (slot.internal != null && (slot.internal.flags & 1) != 0)
    {
        slot.bitmap.buffer = null;
        slot.internal.flags &= ~1;
    }
    else
    {
        slot.bitmap.buffer = null;
    }
}
function ft_glyphslot_set_bitmap(slot, buffer)
{
    ft_glyphslot_free_bitmap(slot);
    slot.bitmap.buffer = buffer;
}
function ft_glyphslot_alloc_bitmap(slot, size)
{
    if ((slot.internal.flags & 1) != 0)
        slot.bitmap.buffer = null;
    else
        slot.internal.flags |= 1;

    slot.bitmap.buffer = g_memory.Alloc(size);
    return 0;
}
function ft_glyphslot_clear(slot)
{
    ft_glyphslot_free_bitmap(slot);

    var metrics = slot.metrics;
    metrics.width = 0;
    metrics.height = 0;
    metrics.horiBearingX = 0;
    metrics.horiBearingY = 0;
    metrics.horiAdvance = 0;
    metrics.vertBearingX = 0;
    metrics.vertBearingY = 0;
    metrics.vertAdvance = 0;

    var outl = slot.outline;
    outl.n_contours = 0;
    outl.n_points = 0;
    outl.contours = null;
    outl.points = null;
    outl.tags = null;
    outl.flags = 0;

    slot.bitmap.width      = 0;
    slot.bitmap.rows       = 0;
    slot.bitmap.pitch      = 0;
    slot.bitmap.pixel_mode = 0;

    slot.bitmap_left   = 0;
    slot.bitmap_top    = 0;
    slot.num_subglyphs = 0;
    slot.subglyphs     = 0;
    slot.control_data  = 0;
    slot.control_len   = 0;
    slot.other         = 0;
    slot.format        = 0;

    slot.linearHoriAdvance = 0;
    slot.linearVertAdvance = 0;
    slot.lsb_delta         = 0;
    slot.rsb_delta         = 0;
}
function ft_glyphslot_done(slot)
{
    var clazz = slot.face.driver.clazz;
    if (clazz.done_slot != null)
        clazz.done_slot(slot);

    ft_glyphslot_free_bitmap(slot);
    slot.internal = null;
}
function ft_glyphslot_grid_fit_metrics(slot,  vertical)
{
    var metrics = slot.metrics;
    var right, bottom;

    if (1 ==  vertical)
    {
        metrics.horiBearingX = FT_PIX_FLOOR(metrics.horiBearingX);
        metrics.horiBearingY = FT_PIX_CEIL (metrics.horiBearingY);

        right  = FT_PIX_CEIL(metrics.vertBearingX + metrics.width);
        bottom = FT_PIX_CEIL(metrics.vertBearingY + metrics.height);

        metrics.vertBearingX = FT_PIX_FLOOR(metrics.vertBearingX);
        metrics.vertBearingY = FT_PIX_FLOOR(metrics.vertBearingY);

        metrics.width  = right - metrics.vertBearingX;
        metrics.height = bottom - metrics.vertBearingY;
    }
    else
    {
        metrics.vertBearingX = FT_PIX_FLOOR(metrics.vertBearingX);
        metrics.vertBearingY = FT_PIX_FLOOR(metrics.vertBearingY);

        right  = FT_PIX_CEIL (metrics.horiBearingX + metrics.width);
        bottom = FT_PIX_FLOOR(metrics.horiBearingY - metrics.height);

        metrics.horiBearingX = FT_PIX_FLOOR(metrics.horiBearingX);
        metrics.horiBearingY = FT_PIX_CEIL (metrics.horiBearingY);

        metrics.width  = right - metrics.horiBearingX;
        metrics.height = metrics.horiBearingY - bottom;
    }

    metrics.horiAdvance = FT_PIX_ROUND(metrics.horiAdvance);
    metrics.vertAdvance = FT_PIX_ROUND(metrics.vertAdvance);
}
function ft_synthesize_vertical_metrics(metrics, advance)
{
    var height = metrics.height;

    /* compensate for glyph with bbox above/below the baseline */
    if (metrics.horiBearingY < 0)
    {
        if (height < metrics.horiBearingY)
            height = metrics.horiBearingY;
    }
    else if (metrics.horiBearingY > 0)
        height -= metrics.horiBearingY;

    /* the factor 1.2 is a heuristical value */
    if (advance == 0)
        advance = parseInt(height * 12 / 10);

    metrics.vertBearingX = parseInt(metrics.horiBearingX - metrics.horiAdvance / 2);
    metrics.vertBearingY = parseInt((advance - height) / 2);
    metrics.vertAdvance  = advance;
}
var ft_standard_glyph_names = [".null","nonmarkingreturn","notequal","infinity","lessequal","greaterequal","partialdiff","summation","product","pi","integral",
"Omega","radical","approxequal","Delta","nonbreakingspace","lozenge","apple","franc","Gbreve","gbreve","Idotaccent","Scedilla",
"scedilla","Cacute","cacute","Ccaron","ccaron","dcroat",".notdef","space","exclam","quotedbl","numbersign","dollar","percent",
"ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three",
"four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G",
"H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum",
"underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency",
"quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered",
"paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave",
"acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE",
"ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot",
"mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior",
"registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde",
"Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex",
"Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute",
"acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex",
"idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis",
"ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall",
"Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle",
"threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
"threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior",
"lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior",
"parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall",
"Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall",
"Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall",
"centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall",
"figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths",
"seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior",
"ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior",
"eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall",
"Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall",
"Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall",
"Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall",
"Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black",
"Bold","Book","Light","Medium","Regular","Roman","Semibold"];

var ft_mac_names=[29,0,1,30,31,32,33,34,35,36,133,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,
66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,153,95,96,97,98,99,100,101,102,103,104,105,106,
107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,202,204,206,207,215,218,224,229,232,230,231,234,233,235,236,
239,237,238,240,243,241,242,244,245,248,246,247,249,251,254,252,253,141,190,126,127,131,145,144,178,194,199,182,154,160,2,167,170,3,
185,4,5,129,181,6,7,8,9,10,168,172,11,173,176,152,125,180,12,130,13,14,135,149,150,15,203,205,220,171,177,140,166,134,148,94,37,188,
16,256,227,128,132,136,137,138,139,142,143,146,147,151,201,208,200,209,210,211,212,213,214,216,217,17,219,222,223,225,174,155,156,
157,158,159,161,162,163,164,165,169,175,221,250,228,257,189,183,196,226,255,186,191,195,197,179,193,198,184,187,192,18,19,20,21,22,
23,24,25,26,27,28];

var ft_sid_names=[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,
69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,
109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,
175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,
274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,
307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,
340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,
373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,
406,407,408,409,410,411,412,413,414,415,416,417,418,419];

var t1_standard_encoding=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,
0,111,112,113,114,0,115,116,117,118,119,120,121,122,0,123,
0,124,125,126,127,128,129,130,131,0,132,133,0,134,135,136,
137,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,138,0,139,0,0,0,0,140,141,142,143,0,0,0,0,
0,144,0,0,0,145,0,0,146,147,148,149,0,0,0,0];

var t1_expert_encoding=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,229,230,0,231,232,233,234,235,236,237,238,13,14,15,99,
239,240,241,242,243,244,245,246,247,248,27,28,249,250,251,252,
0,253,254,255,256,257,0,0,0,258,0,0,259,260,261,262,
0,0,263,264,265,0,266,109,110,267,268,269,0,270,271,272,
273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,
289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,304,305,306,0,0,307,308,309,310,311,0,312,0,0,313,
0,0,314,315,0,0,316,317,318,0,0,0,158,155,163,319,
320,321,322,323,324,325,0,0,326,150,164,169,327,328,329,330,
331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,
347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,
363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378];

var ft_adobe_glyph_list=[0,52,0,106,2,167,3,63,4,220,6,125,9,143,10,23,
11,137,12,199,14,246,15,87,16,233,17,219,18,104,19,88,
22,110,23,32,23,71,24,77,27,156,29,73,31,247,32,107,
32,222,33,55,34,154,35,218,58,10,64,122,72,188,80,109,
88,104,93,61,98,168,106,91,114,111,115,237,122,180,127,255,
135,164,143,132,149,213,158,108,161,115,168,175,183,147,197,199,
202,25,204,166,208,209,209,81,215,26,65,143,0,65,0,140,
0,175,0,193,1,15,1,147,1,233,1,251,2,7,2,40,
2,57,2,82,2,91,2,128,2,136,2,154,69,131,0,198,
0,150,0,158,0,167,225,227,245,244,101,128,1,252,237,225,
227,242,239,110,128,1,226,243,237,225,236,108,128,247,230,225,
227,245,244,101,129,0,193,0,185,243,237,225,236,108,128,247,
225,226,242,229,246,101,134,1,2,0,213,0,221,0,232,0,
243,0,251,1,7,225,227,245,244,101,128,30,174,227,249,242,
233,236,236,233,99,128,4,208,228,239,244,226,229,236,239,119,
128,30,182,231,242,225,246,101,128,30,176,232,239,239,235,225,
226,239,246,101,128,30,178,244,233,236,228,101,128,30,180,99,
4,1,25,1,32,1,121,1,137,225,242,239,110,128,1,205,
233,242,99,2,1,40,1,45,236,101,128,36,182,245,237,230,
236,229,120,134,0,194,1,66,1,74,1,85,1,93,1,105,
1,113,225,227,245,244,101,128,30,164,228,239,244,226,229,236,
239,119,128,30,172,231,242,225,246,101,128,30,166,232,239,239,
235,225,226,239,246,101,128,30,168,243,237,225,236,108,128,247,
226,244,233,236,228,101,128,30,170,245,244,101,129,246,201,1,
129,243,237,225,236,108,128,247,180,249,242,233,236,236,233,99,
128,4,16,100,3,1,155,1,165,1,209,226,236,231,242,225,
246,101,128,2,0,233,229,242,229,243,233,115,131,0,196,1,
181,1,192,1,201,227,249,242,233,236,236,233,99,128,4,210,
237,225,227,242,239,110,128,1,222,243,237,225,236,108,128,247,
228,239,116,2,1,216,1,224,226,229,236,239,119,128,30,160,
237,225,227,242,239,110,128,1,224,231,242,225,246,101,129,0,
192,1,243,243,237,225,236,108,128,247,224,232,239,239,235,225,
226,239,246,101,128,30,162,105,2,2,13,2,25,229,227,249,
242,233,236,236,233,99,128,4,212,238,246,229,242,244,229,228,
226,242,229,246,101,128,2,2,236,240,232,97,129,3,145,2,
49,244,239,238,239,115,128,3,134,109,2,2,63,2,71,225,
227,242,239,110,128,1,0,239,238,239,243,240,225,227,101,128,
255,33,239,231,239,238,229,107,128,1,4,242,233,238,103,131,
0,197,2,104,2,112,2,120,225,227,245,244,101,128,1,250,
226,229,236,239,119,128,30,0,243,237,225,236,108,128,247,229,
243,237,225,236,108,128,247,97,244,233,236,228,101,129,0,195,
2,146,243,237,225,236,108,128,247,227,249,226,225,242,237,229,
238,233,225,110,128,5,49,66,137,0,66,2,189,2,198,2,
223,3,3,3,10,3,22,3,34,3,46,3,54,227,233,242,
227,236,101,128,36,183,228,239,116,2,2,206,2,215,225,227,
227,229,238,116,128,30,2,226,229,236,239,119,128,30,4,101,
3,2,231,2,242,2,254,227,249,242,233,236,236,233,99,128,
4,17,238,225,242,237,229,238,233,225,110,128,5,50,244,97,
128,3,146,232,239,239,107,128,1,129,236,233,238,229,226,229,
236,239,119,128,30,6,237,239,238,239,243,240,225,227,101,128,
255,34,242,229,246,229,243,237,225,236,108,128,246,244,243,237,
225,236,108,128,247,98,244,239,240,226,225,114,128,1,130,67,
137,0,67,3,85,3,127,3,193,3,210,3,224,4,171,4,
188,4,200,4,212,97,3,3,93,3,104,3,111,225,242,237,
229,238,233,225,110,128,5,62,227,245,244,101,128,1,6,242,
239,110,129,246,202,3,119,243,237,225,236,108,128,246,245,99,
3,3,135,3,142,3,171,225,242,239,110,128,1,12,229,228,
233,236,236,97,130,0,199,3,155,3,163,225,227,245,244,101,
128,30,8,243,237,225,236,108,128,247,231,233,242,99,2,3,
179,3,184,236,101,128,36,184,245,237,230,236,229,120,128,1,
8,228,239,116,129,1,10,3,201,225,227,227,229,238,116,128,
1,10,229,228,233,236,236,225,243,237,225,236,108,128,247,184,
104,4,3,234,3,246,4,161,4,165,225,225,242,237,229,238,
233,225,110,128,5,73,101,6,4,4,4,24,4,35,4,103,
4,115,4,136,225,226,235,232,225,243,233,225,238,227,249,242,
233,236,236,233,99,128,4,188,227,249,242,233,236,236,233,99,
128,4,39,100,2,4,41,4,85,229,243,227,229,238,228,229,
114,2,4,54,4,74,225,226,235,232,225,243,233,225,238,227,
249,242,233,236,236,233,99,128,4,190,227,249,242,233,236,236,
233,99,128,4,182,233,229,242,229,243,233,243,227,249,242,233,
236,236,233,99,128,4,244,232,225,242,237,229,238,233,225,110,
128,5,67,235,232,225,235,225,243,243,233,225,238,227,249,242,
233,236,236,233,99,128,4,203,246,229,242,244,233,227,225,236,
243,244,242,239,235,229,227,249,242,233,236,236,233,99,128,4,
184,105,128,3,167,239,239,107,128,1,135,233,242,227,245,237,
230,236,229,248,243,237,225,236,108,128,246,246,237,239,238,239,
243,240,225,227,101,128,255,35,239,225,242,237,229,238,233,225,
110,128,5,81,243,237,225,236,108,128,247,99,68,142,0,68,
4,252,5,10,5,36,5,96,5,121,5,166,5,173,5,231,
5,244,6,0,6,12,6,28,6,48,6,57,90,129,1,241,
5,2,227,225,242,239,110,128,1,196,97,2,5,16,5,27,
225,242,237,229,238,233,225,110,128,5,52,230,242,233,227,225,
110,128,1,137,99,4,5,46,5,53,5,62,5,89,225,242,
239,110,128,1,14,229,228,233,236,236,97,128,30,16,233,242,
99,2,5,70,5,75,236,101,128,36,185,245,237,230,236,229,
248,226,229,236,239,119,128,30,18,242,239,225,116,128,1,16,
228,239,116,2,5,104,5,113,225,227,227,229,238,116,128,30,
10,226,229,236,239,119,128,30,12,101,3,5,129,5,140,5,
150,227,249,242,233,236,236,233,99,128,4,20,233,227,239,240,
244,233,99,128,3,238,236,244,97,129,34,6,5,158,231,242,
229,229,107,128,3,148,232,239,239,107,128,1,138,105,2,5,
179,5,218,229,242,229,243,233,115,131,246,203,5,194,5,202,
5,210,193,227,245,244,101,128,246,204,199,242,225,246,101,128,
246,205,243,237,225,236,108,128,247,168,231,225,237,237,225,231,
242,229,229,107,128,3,220,234,229,227,249,242,233,236,236,233,
99,128,4,2,236,233,238,229,226,229,236,239,119,128,30,14,
237,239,238,239,243,240,225,227,101,128,255,36,239,244,225,227,
227,229,238,244,243,237,225,236,108,128,246,247,115,2,6,34,
6,41,236,225,243,104,128,1,16,237,225,236,108,128,247,100,
244,239,240,226,225,114,128,1,139,122,131,1,242,6,67,6,
75,6,112,227,225,242,239,110,128,1,197,101,2,6,81,6,
101,225,226,235,232,225,243,233,225,238,227,249,242,233,236,236,
233,99,128,4,224,227,249,242,233,236,236,233,99,128,4,5,
232,229,227,249,242,233,236,236,233,99,128,4,15,69,146,0,
69,6,165,6,183,6,191,7,89,7,153,7,165,7,183,7,
211,8,7,8,36,8,94,8,169,8,189,8,208,8,248,9,
44,9,109,9,115,225,227,245,244,101,129,0,201,6,175,243,
237,225,236,108,128,247,233,226,242,229,246,101,128,1,20,99,
5,6,203,6,210,6,224,6,236,7,79,225,242,239,110,128,
1,26,229,228,233,236,236,225,226,242,229,246,101,128,30,28,
232,225,242,237,229,238,233,225,110,128,5,53,233,242,99,2,
6,244,6,249,236,101,128,36,186,245,237,230,236,229,120,135,
0,202,7,16,7,24,7,32,7,43,7,51,7,63,7,71,
225,227,245,244,101,128,30,190,226,229,236,239,119,128,30,24,
228,239,244,226,229,236,239,119,128,30,198,231,242,225,246,101,
128,30,192,232,239,239,235,225,226,239,246,101,128,30,194,243,
237,225,236,108,128,247,234,244,233,236,228,101,128,30,196,249,
242,233,236,236,233,99,128,4,4,100,3,7,97,7,107,7,
127,226,236,231,242,225,246,101,128,2,4,233,229,242,229,243,
233,115,129,0,203,7,119,243,237,225,236,108,128,247,235,239,
116,130,1,22,7,136,7,145,225,227,227,229,238,116,128,1,
22,226,229,236,239,119,128,30,184,230,227,249,242,233,236,236,
233,99,128,4,36,231,242,225,246,101,129,0,200,7,175,243,
237,225,236,108,128,247,232,104,2,7,189,7,200,225,242,237,
229,238,233,225,110,128,5,55,239,239,235,225,226,239,246,101,
128,30,186,105,3,7,219,7,230,7,245,231,232,244,242,239,
237,225,110,128,33,103,238,246,229,242,244,229,228,226,242,229,
246,101,128,2,6,239,244,233,230,233,229,228,227,249,242,233,
236,236,233,99,128,4,100,108,2,8,13,8,24,227,249,242,
233,236,236,233,99,128,4,27,229,246,229,238,242,239,237,225,
110,128,33,106,109,3,8,44,8,72,8,83,225,227,242,239,
110,130,1,18,8,56,8,64,225,227,245,244,101,128,30,22,
231,242,225,246,101,128,30,20,227,249,242,233,236,236,233,99,
128,4,28,239,238,239,243,240,225,227,101,128,255,37,110,4,
8,104,8,115,8,135,8,154,227,249,242,233,236,236,233,99,
128,4,29,228,229,243,227,229,238,228,229,242,227,249,242,233,
236,236,233,99,128,4,162,103,129,1,74,8,141,232,229,227,
249,242,233,236,236,233,99,128,4,164,232,239,239,235,227,249,
242,233,236,236,233,99,128,4,199,111,2,8,175,8,183,231,
239,238,229,107,128,1,24,240,229,110,128,1,144,240,243,233,
236,239,110,129,3,149,8,200,244,239,238,239,115,128,3,136,
114,2,8,214,8,225,227,249,242,233,236,236,233,99,128,4,
32,229,246,229,242,243,229,100,129,1,142,8,237,227,249,242,
233,236,236,233,99,128,4,45,115,4,9,2,9,13,9,33,
9,37,227,249,242,233,236,236,233,99,128,4,33,228,229,243,
227,229,238,228,229,242,227,249,242,233,236,236,233,99,128,4,
170,104,128,1,169,237,225,236,108,128,247,101,116,3,9,52,
9,78,9,92,97,130,3,151,9,60,9,70,242,237,229,238,
233,225,110,128,5,56,244,239,238,239,115,128,3,137,104,129,
0,208,9,84,243,237,225,236,108,128,247,240,233,236,228,101,
129,30,188,9,101,226,229,236,239,119,128,30,26,245,242,111,
128,32,172,250,104,130,1,183,9,124,9,132,227,225,242,239,
110,128,1,238,242,229,246,229,242,243,229,100,128,1,184,70,
136,0,70,9,163,9,172,9,184,9,212,9,219,9,248,10,
4,10,15,227,233,242,227,236,101,128,36,187,228,239,244,225,
227,227,229,238,116,128,30,30,101,2,9,190,9,202,232,225,
242,237,229,238,233,225,110,128,5,86,233,227,239,240,244,233,
99,128,3,228,232,239,239,107,128,1,145,105,2,9,225,9,
238,244,225,227,249,242,233,236,236,233,99,128,4,114,246,229,
242,239,237,225,110,128,33,100,237,239,238,239,243,240,225,227,
101,128,255,38,239,245,242,242,239,237,225,110,128,33,99,243,
237,225,236,108,128,247,102,71,140,0,71,10,51,10,61,10,
107,10,115,10,176,10,193,10,205,11,39,11,52,11,65,11,
90,11,107,194,243,241,245,225,242,101,128,51,135,97,3,10,
69,10,76,10,94,227,245,244,101,128,1,244,237,237,97,129,
3,147,10,84,225,230,242,233,227,225,110,128,1,148,238,231,
233,225,227,239,240,244,233,99,128,3,234,226,242,229,246,101,
128,1,30,99,4,10,125,10,132,10,141,10,163,225,242,239,
110,128,1,230,229,228,233,236,236,97,128,1,34,233,242,99,
2,10,149,10,154,236,101,128,36,188,245,237,230,236,229,120,
128,1,28,239,237,237,225,225,227,227,229,238,116,128,1,34,
228,239,116,129,1,32,10,184,225,227,227,229,238,116,128,1,
32,229,227,249,242,233,236,236,233,99,128,4,19,104,3,10,
213,10,226,11,33,225,228,225,242,237,229,238,233,225,110,128,
5,66,101,3,10,234,10,255,11,16,237,233,228,228,236,229,
232,239,239,235,227,249,242,233,236,236,233,99,128,4,148,243,
244,242,239,235,229,227,249,242,233,236,236,233,99,128,4,146,
245,240,244,245,242,238,227,249,242,233,236,236,233,99,128,4,
144,239,239,107,128,1,147,233,237,225,242,237,229,238,233,225,
110,128,5,51,234,229,227,249,242,233,236,236,233,99,128,4,
3,109,2,11,71,11,79,225,227,242,239,110,128,30,32,239,
238,239,243,240,225,227,101,128,255,39,242,225,246,101,129,246,
206,11,99,243,237,225,236,108,128,247,96,115,2,11,113,11,
129,237,225,236,108,129,247,103,11,122,232,239,239,107,128,2,
155,244,242,239,235,101,128,1,228,72,140,0,72,11,165,11,
190,11,198,11,208,12,17,12,40,12,77,12,117,12,129,12,
157,12,165,12,189,177,184,53,3,11,175,11,180,11,185,179,
51,128,37,207,180,51,128,37,170,181,49,128,37,171,178,178,
176,183,51,128,37,161,208,243,241,245,225,242,101,128,51,203,
97,3,11,216,11,236,12,0,225,226,235,232,225,243,233,225,
238,227,249,242,233,236,236,233,99,128,4,168,228,229,243,227,
229,238,228,229,242,227,249,242,233,236,236,233,99,128,4,178,
242,228,243,233,231,238,227,249,242,233,236,236,233,99,128,4,
42,98,2,12,23,12,28,225,114,128,1,38,242,229,246,229,
226,229,236,239,119,128,30,42,99,2,12,46,12,55,229,228,
233,236,236,97,128,30,40,233,242,99,2,12,63,12,68,236,
101,128,36,189,245,237,230,236,229,120,128,1,36,100,2,12,
83,12,93,233,229,242,229,243,233,115,128,30,38,239,116,2,
12,100,12,109,225,227,227,229,238,116,128,30,34,226,229,236,
239,119,128,30,36,237,239,238,239,243,240,225,227,101,128,255,
40,111,2,12,135,12,146,225,242,237,229,238,233,225,110,128,
5,64,242,233,227,239,240,244,233,99,128,3,232,243,237,225,
236,108,128,247,104,245,238,231,225,242,245,237,236,225,245,116,
129,246,207,12,181,243,237,225,236,108,128,246,248,250,243,241,
245,225,242,101,128,51,144,73,146,0,73,12,239,12,251,12,
255,13,11,13,29,13,37,13,94,13,181,13,214,13,224,13,
242,13,254,14,48,14,86,14,99,14,166,14,187,14,205,193,
227,249,242,233,236,236,233,99,128,4,47,74,128,1,50,213,
227,249,242,233,236,236,233,99,128,4,46,225,227,245,244,101,
129,0,205,13,21,243,237,225,236,108,128,247,237,226,242,229,
246,101,128,1,44,99,3,13,45,13,52,13,84,225,242,239,
110,128,1,207,233,242,99,2,13,60,13,65,236,101,128,36,
190,245,237,230,236,229,120,129,0,206,13,76,243,237,225,236,
108,128,247,238,249,242,233,236,236,233,99,128,4,6,100,3,
13,102,13,112,13,155,226,236,231,242,225,246,101,128,2,8,
233,229,242,229,243,233,115,131,0,207,13,128,13,136,13,147,
225,227,245,244,101,128,30,46,227,249,242,233,236,236,233,99,
128,4,228,243,237,225,236,108,128,247,239,239,116,130,1,48,
13,164,13,173,225,227,227,229,238,116,128,1,48,226,229,236,
239,119,128,30,202,101,2,13,187,13,203,226,242,229,246,229,
227,249,242,233,236,236,233,99,128,4,214,227,249,242,233,236,
236,233,99,128,4,21,230,242,225,235,244,245,114,128,33,17,
231,242,225,246,101,129,0,204,13,234,243,237,225,236,108,128,
247,236,232,239,239,235,225,226,239,246,101,128,30,200,105,3,
14,6,14,17,14,32,227,249,242,233,236,236,233,99,128,4,
24,238,246,229,242,244,229,228,226,242,229,246,101,128,2,10,
243,232,239,242,244,227,249,242,233,236,236,233,99,128,4,25,
109,2,14,54,14,75,225,227,242,239,110,129,1,42,14,64,
227,249,242,233,236,236,233,99,128,4,226,239,238,239,243,240,
225,227,101,128,255,41,238,233,225,242,237,229,238,233,225,110,
128,5,59,111,3,14,107,14,118,14,126,227,249,242,233,236,
236,233,99,128,4,1,231,239,238,229,107,128,1,46,244,97,
131,3,153,14,137,14,147,14,158,225,230,242,233,227,225,110,
128,1,150,228,233,229,242,229,243,233,115,128,3,170,244,239,
238,239,115,128,3,138,115,2,14,172,14,179,237,225,236,108,
128,247,105,244,242,239,235,101,128,1,151,244,233,236,228,101,
129,1,40,14,197,226,229,236,239,119,128,30,44,250,232,233,
244,243,97,2,14,216,14,227,227,249,242,233,236,236,233,99,
128,4,116,228,226,236,231,242,225,246,229,227,249,242,233,236,
236,233,99,128,4,118,74,134,0,74,15,6,15,18,15,41,
15,53,15,67,15,79,225,225,242,237,229,238,233,225,110,128,
5,65,227,233,242,99,2,15,27,15,32,236,101,128,36,191,
245,237,230,236,229,120,128,1,52,229,227,249,242,233,236,236,
233,99,128,4,8,232,229,232,225,242,237,229,238,233,225,110,
128,5,75,237,239,238,239,243,240,225,227,101,128,255,42,243,
237,225,236,108,128,247,106,75,140,0,75,15,115,15,125,15,
135,16,18,16,65,16,76,16,106,16,143,16,156,16,168,16,
180,16,208,194,243,241,245,225,242,101,128,51,133,203,243,241,
245,225,242,101,128,51,205,97,7,15,151,15,169,15,191,15,
211,15,226,15,232,15,249,226,225,243,232,235,233,242,227,249,
242,233,236,236,233,99,128,4,160,99,2,15,175,15,181,245,
244,101,128,30,48,249,242,233,236,236,233,99,128,4,26,228,
229,243,227,229,238,228,229,242,227,249,242,233,236,236,233,99,
128,4,154,232,239,239,235,227,249,242,233,236,236,233,99,128,
4,195,240,240,97,128,3,154,243,244,242,239,235,229,227,249,
242,233,236,236,233,99,128,4,158,246,229,242,244,233,227,225,
236,243,244,242,239,235,229,227,249,242,233,236,236,233,99,128,
4,156,99,4,16,28,16,35,16,44,16,52,225,242,239,110,
128,1,232,229,228,233,236,236,97,128,1,54,233,242,227,236,
101,128,36,192,239,237,237,225,225,227,227,229,238,116,128,1,
54,228,239,244,226,229,236,239,119,128,30,50,101,2,16,82,
16,94,232,225,242,237,229,238,233,225,110,128,5,84,238,225,
242,237,229,238,233,225,110,128,5,63,104,3,16,114,16,126,
16,137,225,227,249,242,233,236,236,233,99,128,4,37,229,233,
227,239,240,244,233,99,128,3,230,239,239,107,128,1,152,234,
229,227,249,242,233,236,236,233,99,128,4,12,236,233,238,229,
226,229,236,239,119,128,30,52,237,239,238,239,243,240,225,227,
101,128,255,43,239,240,240,97,2,16,189,16,200,227,249,242,
233,236,236,233,99,128,4,128,231,242,229,229,107,128,3,222,
115,2,16,214,16,226,233,227,249,242,233,236,236,233,99,128,
4,110,237,225,236,108,128,247,107,76,138,0,76,17,1,17,
5,17,9,17,29,17,95,17,133,17,147,17,165,17,177,17,
189,74,128,1,199,76,128,246,191,97,2,17,15,17,22,227,
245,244,101,128,1,57,237,226,228,97,128,3,155,99,4,17,
39,17,46,17,55,17,82,225,242,239,110,128,1,61,229,228,
233,236,236,97,128,1,59,233,242,99,2,17,63,17,68,236,
101,128,36,193,245,237,230,236,229,248,226,229,236,239,119,128,
30,60,239,237,237,225,225,227,227,229,238,116,128,1,59,228,
239,116,130,1,63,17,105,17,114,225,227,227,229,238,116,128,
1,63,226,229,236,239,119,129,30,54,17,124,237,225,227,242,
239,110,128,30,56,233,247,238,225,242,237,229,238,233,225,110,
128,5,60,106,129,1,200,17,153,229,227,249,242,233,236,236,
233,99,128,4,9,236,233,238,229,226,229,236,239,119,128,30,
58,237,239,238,239,243,240,225,227,101,128,255,44,115,2,17,
195,17,212,236,225,243,104,129,1,65,17,204,243,237,225,236,
108,128,246,249,237,225,236,108,128,247,108,77,137,0,77,17,
241,17,251,18,24,18,33,18,58,18,71,18,83,18,91,18,
100,194,243,241,245,225,242,101,128,51,134,225,99,2,18,2,
18,18,242,239,110,129,246,208,18,10,243,237,225,236,108,128,
247,175,245,244,101,128,30,62,227,233,242,227,236,101,128,36,
194,228,239,116,2,18,41,18,50,225,227,227,229,238,116,128,
30,64,226,229,236,239,119,128,30,66,229,238,225,242,237,229,
238,233,225,110,128,5,68,237,239,238,239,243,240,225,227,101,
128,255,45,243,237,225,236,108,128,247,109,244,245,242,238,229,
100,128,1,156,117,128,3,156,78,141,0,78,18,134,18,138,
18,146,18,212,18,237,18,248,19,3,19,21,19,33,19,45,
19,58,19,66,19,84,74,128,1,202,225,227,245,244,101,128,
1,67,99,4,18,156,18,163,18,172,18,199,225,242,239,110,
128,1,71,229,228,233,236,236,97,128,1,69,233,242,99,2,
18,180,18,185,236,101,128,36,195,245,237,230,236,229,248,226,
229,236,239,119,128,30,74,239,237,237,225,225,227,227,229,238,
116,128,1,69,228,239,116,2,18,220,18,229,225,227,227,229,
238,116,128,30,68,226,229,236,239,119,128,30,70,232,239,239,
235,236,229,230,116,128,1,157,233,238,229,242,239,237,225,110,
128,33,104,106,129,1,203,19,9,229,227,249,242,233,236,236,
233,99,128,4,10,236,233,238,229,226,229,236,239,119,128,30,
72,237,239,238,239,243,240,225,227,101,128,255,46,239,247,225,
242,237,229,238,233,225,110,128,5,70,243,237,225,236,108,128,
247,110,244,233,236,228,101,129,0,209,19,76,243,237,225,236,
108,128,247,241,117,128,3,157,79,141,0,79,19,118,19,132,
19,150,19,203,20,78,20,152,20,187,21,48,21,69,21,213,
21,223,21,254,22,53,69,129,1,82,19,124,243,237,225,236,
108,128,246,250,225,227,245,244,101,129,0,211,19,142,243,237,
225,236,108,128,247,243,98,2,19,156,19,196,225,242,242,229,
100,2,19,166,19,177,227,249,242,233,236,236,233,99,128,4,
232,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
99,128,4,234,242,229,246,101,128,1,78,99,4,19,213,19,
220,19,235,20,68,225,242,239,110,128,1,209,229,238,244,229,
242,229,228,244,233,236,228,101,128,1,159,233,242,99,2,19,
243,19,248,236,101,128,36,196,245,237,230,236,229,120,134,0,
212,20,13,20,21,20,32,20,40,20,52,20,60,225,227,245,
244,101,128,30,208,228,239,244,226,229,236,239,119,128,30,216,
231,242,225,246,101,128,30,210,232,239,239,235,225,226,239,246,
101,128,30,212,243,237,225,236,108,128,247,244,244,233,236,228,
101,128,30,214,249,242,233,236,236,233,99,128,4,30,100,3,
20,86,20,109,20,142,226,108,2,20,93,20,101,225,227,245,
244,101,128,1,80,231,242,225,246,101,128,2,12,233,229,242,
229,243,233,115,130,0,214,20,123,20,134,227,249,242,233,236,
236,233,99,128,4,230,243,237,225,236,108,128,247,246,239,244,
226,229,236,239,119,128,30,204,103,2,20,158,20,170,239,238,
229,235,243,237,225,236,108,128,246,251,242,225,246,101,129,0,
210,20,179,243,237,225,236,108,128,247,242,104,4,20,197,20,
208,20,212,21,34,225,242,237,229,238,233,225,110,128,5,85,
109,128,33,38,111,2,20,218,20,228,239,235,225,226,239,246,
101,128,30,206,242,110,133,1,160,20,243,20,251,21,6,21,
14,21,26,225,227,245,244,101,128,30,218,228,239,244,226,229,
236,239,119,128,30,226,231,242,225,246,101,128,30,220,232,239,
239,235,225,226,239,246,101,128,30,222,244,233,236,228,101,128,
30,224,245,238,231,225,242,245,237,236,225,245,116,128,1,80,
105,129,1,162,21,54,238,246,229,242,244,229,228,226,242,229,
246,101,128,2,14,109,4,21,79,21,107,21,184,21,202,225,
227,242,239,110,130,1,76,21,91,21,99,225,227,245,244,101,
128,30,82,231,242,225,246,101,128,30,80,229,231,97,132,33,
38,21,121,21,132,21,140,21,156,227,249,242,233,236,236,233,
99,128,4,96,231,242,229,229,107,128,3,169,242,239,245,238,
228,227,249,242,233,236,236,233,99,128,4,122,116,2,21,162,
21,177,233,244,236,239,227,249,242,233,236,236,233,99,128,4,
124,239,238,239,115,128,3,143,233,227,242,239,110,129,3,159,
21,194,244,239,238,239,115,128,3,140,239,238,239,243,240,225,
227,101,128,255,47,238,229,242,239,237,225,110,128,33,96,111,
2,21,229,21,248,231,239,238,229,107,129,1,234,21,239,237,
225,227,242,239,110,128,1,236,240,229,110,128,1,134,115,3,
22,6,22,33,22,40,236,225,243,104,130,0,216,22,17,22,
25,225,227,245,244,101,128,1,254,243,237,225,236,108,128,247,
248,237,225,236,108,128,247,111,244,242,239,235,229,225,227,245,
244,101,128,1,254,116,2,22,59,22,70,227,249,242,233,236,
236,233,99,128,4,126,233,236,228,101,131,0,213,22,83,22,
91,22,102,225,227,245,244,101,128,30,76,228,233,229,242,229,
243,233,115,128,30,78,243,237,225,236,108,128,247,245,80,136,
0,80,22,130,22,138,22,147,22,159,22,211,22,227,22,246,
23,2,225,227,245,244,101,128,30,84,227,233,242,227,236,101,
128,36,197,228,239,244,225,227,227,229,238,116,128,30,86,101,
3,22,167,22,178,22,190,227,249,242,233,236,236,233,99,128,
4,31,232,225,242,237,229,238,233,225,110,128,5,74,237,233,
228,228,236,229,232,239,239,235,227,249,242,233,236,236,233,99,
128,4,166,104,2,22,217,22,221,105,128,3,166,239,239,107,
128,1,164,105,129,3,160,22,233,247,242,225,242,237,229,238,
233,225,110,128,5,83,237,239,238,239,243,240,225,227,101,128,
255,48,115,2,23,8,23,25,105,129,3,168,23,14,227,249,
242,233,236,236,233,99,128,4,112,237,225,236,108,128,247,112,
81,131,0,81,23,42,23,51,23,63,227,233,242,227,236,101,
128,36,198,237,239,238,239,243,240,225,227,101,128,255,49,243,
237,225,236,108,128,247,113,82,138,0,82,23,95,23,119,23,
166,23,217,23,230,23,240,23,245,24,19,24,31,24,43,97,
2,23,101,23,112,225,242,237,229,238,233,225,110,128,5,76,
227,245,244,101,128,1,84,99,4,23,129,23,136,23,145,23,
153,225,242,239,110,128,1,88,229,228,233,236,236,97,128,1,
86,233,242,227,236,101,128,36,199,239,237,237,225,225,227,227,
229,238,116,128,1,86,100,2,23,172,23,182,226,236,231,242,
225,246,101,128,2,16,239,116,2,23,189,23,198,225,227,227,
229,238,116,128,30,88,226,229,236,239,119,129,30,90,23,208,
237,225,227,242,239,110,128,30,92,229,232,225,242,237,229,238,
233,225,110,128,5,80,230,242,225,235,244,245,114,128,33,28,
232,111,128,3,161,233,110,2,23,252,24,5,231,243,237,225,
236,108,128,246,252,246,229,242,244,229,228,226,242,229,246,101,
128,2,18,236,233,238,229,226,229,236,239,119,128,30,94,237,
239,238,239,243,240,225,227,101,128,255,50,243,237,225,236,108,
129,247,114,24,53,233,238,246,229,242,244,229,100,129,2,129,
24,66,243,245,240,229,242,233,239,114,128,2,182,83,139,0,
83,24,103,26,17,26,55,26,182,26,221,26,250,27,84,27,
105,27,117,27,135,27,143,70,6,24,117,24,209,24,241,25,
77,25,119,25,221,48,9,24,137,24,145,24,153,24,161,24,
169,24,177,24,185,24,193,24,201,177,176,176,176,48,128,37,
12,178,176,176,176,48,128,37,20,179,176,176,176,48,128,37,
16,180,176,176,176,48,128,37,24,181,176,176,176,48,128,37,
60,182,176,176,176,48,128,37,44,183,176,176,176,48,128,37,
52,184,176,176,176,48,128,37,28,185,176,176,176,48,128,37,
36,49,3,24,217,24,225,24,233,176,176,176,176,48,128,37,
0,177,176,176,176,48,128,37,2,185,176,176,176,48,128,37,
97,50,9,25,5,25,13,25,21,25,29,25,37,25,45,25,
53,25,61,25,69,176,176,176,176,48,128,37,98,177,176,176,
176,48,128,37,86,178,176,176,176,48,128,37,85,179,176,176,
176,48,128,37,99,180,176,176,176,48,128,37,81,181,176,176,
176,48,128,37,87,182,176,176,176,48,128,37,93,183,176,176,
176,48,128,37,92,184,176,176,176,48,128,37,91,51,4,25,
87,25,95,25,103,25,111,182,176,176,176,48,128,37,94,183,
176,176,176,48,128,37,95,184,176,176,176,48,128,37,90,185,
176,176,176,48,128,37,84,52,10,25,141,25,149,25,157,25,
165,25,173,25,181,25,189,25,197,25,205,25,213,176,176,176,
176,48,128,37,105,177,176,176,176,48,128,37,102,178,176,176,
176,48,128,37,96,179,176,176,176,48,128,37,80,180,176,176,
176,48,128,37,108,181,176,176,176,48,128,37,103,182,176,176,
176,48,128,37,104,183,176,176,176,48,128,37,100,184,176,176,
176,48,128,37,101,185,176,176,176,48,128,37,89,53,5,25,
233,25,241,25,249,26,1,26,9,176,176,176,176,48,128,37,
88,177,176,176,176,48,128,37,82,178,176,176,176,48,128,37,
83,179,176,176,176,48,128,37,107,180,176,176,176,48,128,37,
106,97,2,26,23,26,44,227,245,244,101,129,1,90,26,32,
228,239,244,225,227,227,229,238,116,128,30,100,237,240,233,231,
242,229,229,107,128,3,224,99,5,26,67,26,98,26,107,26,
147,26,169,225,242,239,110,130,1,96,26,78,26,90,228,239,
244,225,227,227,229,238,116,128,30,102,243,237,225,236,108,128,
246,253,229,228,233,236,236,97,128,1,94,232,247,97,130,1,
143,26,117,26,128,227,249,242,233,236,236,233,99,128,4,216,
228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,99,
128,4,218,233,242,99,2,26,155,26,160,236,101,128,36,200,
245,237,230,236,229,120,128,1,92,239,237,237,225,225,227,227,
229,238,116,128,2,24,228,239,116,2,26,190,26,199,225,227,
227,229,238,116,128,30,96,226,229,236,239,119,129,30,98,26,
209,228,239,244,225,227,227,229,238,116,128,30,104,101,2,26,
227,26,239,232,225,242,237,229,238,233,225,110,128,5,77,246,
229,238,242,239,237,225,110,128,33,102,104,5,27,6,27,34,
27,48,27,59,27,72,97,2,27,12,27,23,225,242,237,229,
238,233,225,110,128,5,71,227,249,242,233,236,236,233,99,128,
4,40,227,232,225,227,249,242,233,236,236,233,99,128,4,41,
229,233,227,239,240,244,233,99,128,3,226,232,225,227,249,242,
233,236,236,233,99,128,4,186,233,237,225,227,239,240,244,233,
99,128,3,236,105,2,27,90,27,96,231,237,97,128,3,163,
248,242,239,237,225,110,128,33,101,237,239,238,239,243,240,225,
227,101,128,255,51,239,230,244,243,233,231,238,227,249,242,233,
236,236,233,99,128,4,44,243,237,225,236,108,128,247,115,244,
233,231,237,225,231,242,229,229,107,128,3,218,84,141,0,84,
27,186,27,191,27,197,28,7,28,32,28,96,28,147,28,177,
28,189,28,201,28,246,29,6,29,46,225,117,128,3,164,226,
225,114,128,1,102,99,4,27,207,27,214,27,223,27,250,225,
242,239,110,128,1,100,229,228,233,236,236,97,128,1,98,233,
242,99,2,27,231,27,236,236,101,128,36,201,245,237,230,236,
229,248,226,229,236,239,119,128,30,112,239,237,237,225,225,227,
227,229,238,116,128,1,98,228,239,116,2,28,15,28,24,225,
227,227,229,238,116,128,30,106,226,229,236,239,119,128,30,108,
101,4,28,42,28,53,28,73,28,82,227,249,242,233,236,236,
233,99,128,4,34,228,229,243,227,229,238,228,229,242,227,249,
242,233,236,236,233,99,128,4,172,238,242,239,237,225,110,128,
33,105,244,243,229,227,249,242,233,236,236,233,99,128,4,180,
104,3,28,104,28,110,28,136,229,244,97,128,3,152,111,2,
28,116,28,121,239,107,128,1,172,242,110,129,0,222,28,128,
243,237,225,236,108,128,247,254,242,229,229,242,239,237,225,110,
128,33,98,105,2,28,153,28,164,236,228,229,243,237,225,236,
108,128,246,254,247,238,225,242,237,229,238,233,225,110,128,5,
79,236,233,238,229,226,229,236,239,119,128,30,110,237,239,238,
239,243,240,225,227,101,128,255,52,111,2,28,207,28,218,225,
242,237,229,238,233,225,110,128,5,57,238,101,3,28,227,28,
234,28,240,230,233,246,101,128,1,188,243,233,120,128,1,132,
244,247,111,128,1,167,242,229,244,242,239,230,236,229,248,232,
239,239,107,128,1,174,115,3,29,14,29,26,29,39,229,227,
249,242,233,236,236,233,99,128,4,38,232,229,227,249,242,233,
236,236,233,99,128,4,11,237,225,236,108,128,247,116,119,2,
29,52,29,64,229,236,246,229,242,239,237,225,110,128,33,107,
239,242,239,237,225,110,128,33,97,85,142,0,85,29,105,29,
123,29,131,29,198,30,69,30,87,30,198,30,214,30,226,31,
21,31,30,31,142,31,149,31,219,225,227,245,244,101,129,0,
218,29,115,243,237,225,236,108,128,247,250,226,242,229,246,101,
128,1,108,99,3,29,139,29,146,29,188,225,242,239,110,128,
1,211,233,242,99,2,29,154,29,159,236,101,128,36,202,245,
237,230,236,229,120,130,0,219,29,172,29,180,226,229,236,239,
119,128,30,118,243,237,225,236,108,128,247,251,249,242,233,236,
236,233,99,128,4,35,100,3,29,206,29,229,30,59,226,108,
2,29,213,29,221,225,227,245,244,101,128,1,112,231,242,225,
246,101,128,2,20,233,229,242,229,243,233,115,134,0,220,29,
251,30,3,30,11,30,34,30,42,30,51,225,227,245,244,101,
128,1,215,226,229,236,239,119,128,30,114,99,2,30,17,30,
24,225,242,239,110,128,1,217,249,242,233,236,236,233,99,128,
4,240,231,242,225,246,101,128,1,219,237,225,227,242,239,110,
128,1,213,243,237,225,236,108,128,247,252,239,244,226,229,236,
239,119,128,30,228,231,242,225,246,101,129,0,217,30,79,243,
237,225,236,108,128,247,249,104,2,30,93,30,171,111,2,30,
99,30,109,239,235,225,226,239,246,101,128,30,230,242,110,133,
1,175,30,124,30,132,30,143,30,151,30,163,225,227,245,244,
101,128,30,232,228,239,244,226,229,236,239,119,128,30,240,231,
242,225,246,101,128,30,234,232,239,239,235,225,226,239,246,101,
128,30,236,244,233,236,228,101,128,30,238,245,238,231,225,242,
245,237,236,225,245,116,129,1,112,30,187,227,249,242,233,236,
236,233,99,128,4,242,233,238,246,229,242,244,229,228,226,242,
229,246,101,128,2,22,235,227,249,242,233,236,236,233,99,128,
4,120,109,2,30,232,31,10,225,227,242,239,110,130,1,106,
30,244,30,255,227,249,242,233,236,236,233,99,128,4,238,228,
233,229,242,229,243,233,115,128,30,122,239,238,239,243,240,225,
227,101,128,255,53,239,231,239,238,229,107,128,1,114,240,243,
233,236,239,110,133,3,165,31,49,31,53,31,90,31,121,31,
134,49,128,3,210,97,2,31,59,31,81,227,245,244,229,232,
239,239,235,243,249,237,226,239,236,231,242,229,229,107,128,3,
211,230,242,233,227,225,110,128,1,177,228,233,229,242,229,243,
233,115,129,3,171,31,103,232,239,239,235,243,249,237,226,239,
236,231,242,229,229,107,128,3,212,232,239,239,235,243,249,237,
226,239,108,128,3,210,244,239,238,239,115,128,3,142,242,233,
238,103,128,1,110,115,3,31,157,31,172,31,179,232,239,242,
244,227,249,242,233,236,236,233,99,128,4,14,237,225,236,108,
128,247,117,244,242,225,233,231,232,116,2,31,191,31,202,227,
249,242,233,236,236,233,99,128,4,174,243,244,242,239,235,229,
227,249,242,233,236,236,233,99,128,4,176,244,233,236,228,101,
130,1,104,31,231,31,239,225,227,245,244,101,128,30,120,226,
229,236,239,119,128,30,116,86,136,0,86,32,11,32,20,32,
31,32,60,32,67,32,79,32,91,32,99,227,233,242,227,236,
101,128,36,203,228,239,244,226,229,236,239,119,128,30,126,101,
2,32,37,32,48,227,249,242,233,236,236,233,99,128,4,18,
247,225,242,237,229,238,233,225,110,128,5,78,232,239,239,107,
128,1,178,237,239,238,239,243,240,225,227,101,128,255,54,239,
225,242,237,229,238,233,225,110,128,5,72,243,237,225,236,108,
128,247,118,244,233,236,228,101,128,30,124,87,134,0,87,32,
123,32,131,32,154,32,194,32,202,32,214,225,227,245,244,101,
128,30,130,227,233,242,99,2,32,140,32,145,236,101,128,36,
204,245,237,230,236,229,120,128,1,116,100,2,32,160,32,170,
233,229,242,229,243,233,115,128,30,132,239,116,2,32,177,32,
186,225,227,227,229,238,116,128,30,134,226,229,236,239,119,128,
30,136,231,242,225,246,101,128,30,128,237,239,238,239,243,240,
225,227,101,128,255,55,243,237,225,236,108,128,247,119,88,134,
0,88,32,238,32,247,33,18,33,31,33,35,33,47,227,233,
242,227,236,101,128,36,205,100,2,32,253,33,7,233,229,242,
229,243,233,115,128,30,140,239,244,225,227,227,229,238,116,128,
30,138,229,232,225,242,237,229,238,233,225,110,128,5,61,105,
128,3,158,237,239,238,239,243,240,225,227,101,128,255,56,243,
237,225,236,108,128,247,120,89,139,0,89,33,81,33,116,33,
139,33,189,33,228,33,236,33,253,34,40,34,52,34,60,34,
68,97,2,33,87,33,104,227,245,244,101,129,0,221,33,96,
243,237,225,236,108,128,247,253,244,227,249,242,233,236,236,233,
99,128,4,98,227,233,242,99,2,33,125,33,130,236,101,128,
36,206,245,237,230,236,229,120,128,1,118,100,2,33,145,33,
165,233,229,242,229,243,233,115,129,1,120,33,157,243,237,225,
236,108,128,247,255,239,116,2,33,172,33,181,225,227,227,229,
238,116,128,30,142,226,229,236,239,119,128,30,244,229,114,2,
33,196,33,208,233,227,249,242,233,236,236,233,99,128,4,43,
245,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
99,128,4,248,231,242,225,246,101,128,30,242,232,239,239,107,
129,1,179,33,245,225,226,239,246,101,128,30,246,105,3,34,
5,34,16,34,27,225,242,237,229,238,233,225,110,128,5,69,
227,249,242,233,236,236,233,99,128,4,7,247,238,225,242,237,
229,238,233,225,110,128,5,82,237,239,238,239,243,240,225,227,
101,128,255,57,243,237,225,236,108,128,247,121,244,233,236,228,
101,128,30,248,245,115,2,34,75,34,113,226,233,103,2,34,
83,34,94,227,249,242,233,236,236,233,99,128,4,106,233,239,
244,233,230,233,229,228,227,249,242,233,236,236,233,99,128,4,
108,236,233,244,244,236,101,2,34,124,34,135,227,249,242,233,
236,236,233,99,128,4,102,233,239,244,233,230,233,229,228,227,
249,242,233,236,236,233,99,128,4,104,90,136,0,90,34,174,
34,198,34,243,35,14,35,81,35,173,35,185,35,197,97,2,
34,180,34,191,225,242,237,229,238,233,225,110,128,5,54,227,
245,244,101,128,1,121,99,2,34,204,34,221,225,242,239,110,
129,1,125,34,213,243,237,225,236,108,128,246,255,233,242,99,
2,34,229,34,234,236,101,128,36,207,245,237,230,236,229,120,
128,30,144,228,239,116,130,1,123,34,253,35,6,225,227,227,
229,238,116,128,1,123,226,229,236,239,119,128,30,146,101,3,
35,22,35,33,35,76,227,249,242,233,236,236,233,99,128,4,
23,100,2,35,39,35,58,229,243,227,229,238,228,229,242,227,
249,242,233,236,236,233,99,128,4,152,233,229,242,229,243,233,
243,227,249,242,233,236,236,233,99,128,4,222,244,97,128,3,
150,232,101,4,35,92,35,103,35,119,35,130,225,242,237,229,
238,233,225,110,128,5,58,226,242,229,246,229,227,249,242,233,
236,236,233,99,128,4,193,227,249,242,233,236,236,233,99,128,
4,22,100,2,35,136,35,155,229,243,227,229,238,228,229,242,
227,249,242,233,236,236,233,99,128,4,150,233,229,242,229,243,
233,243,227,249,242,233,236,236,233,99,128,4,220,236,233,238,
229,226,229,236,239,119,128,30,148,237,239,238,239,243,240,225,
227,101,128,255,58,115,2,35,203,35,210,237,225,236,108,128,
247,122,244,242,239,235,101,128,1,181,97,158,0,97,36,26,
38,154,39,4,39,68,39,132,39,196,40,4,40,68,40,126,
40,190,41,70,41,217,42,137,42,237,43,17,49,192,49,229,
50,0,50,225,51,7,52,96,52,168,53,123,53,132,54,5,
56,13,57,3,57,50,57,201,57,215,49,138,39,1,36,50,
36,114,36,154,36,218,37,26,37,90,37,154,37,218,38,26,
38,90,48,138,39,33,36,74,36,78,36,82,36,86,36,90,
36,94,36,98,36,102,36,106,36,110,48,128,39,94,49,128,
39,97,50,128,39,98,51,128,39,99,52,128,39,100,53,128,
39,16,54,128,39,101,55,128,39,102,56,128,39,103,57,128,
38,96,49,134,38,27,36,130,36,134,36,138,36,142,36,146,
36,150,48,128,38,101,49,128,38,102,50,128,38,99,55,128,
39,9,56,128,39,8,57,128,39,7,50,138,38,30,36,178,
36,182,36,186,36,190,36,194,36,198,36,202,36,206,36,210,
36,214,48,128,36,96,49,128,36,97,50,128,36,98,51,128,
36,99,52,128,36,100,53,128,36,101,54,128,36,102,55,128,
36,103,56,128,36,104,57,128,36,105,51,138,39,12,36,242,
36,246,36,250,36,254,37,2,37,6,37,10,37,14,37,18,
37,22,48,128,39,118,49,128,39,119,50,128,39,120,51,128,
39,121,52,128,39,122,53,128,39,123,54,128,39,124,55,128,
39,125,56,128,39,126,57,128,39,127,52,138,39,13,37,50,
37,54,37,58,37,62,37,66,37,70,37,74,37,78,37,82,
37,86,48,128,39,128,49,128,39,129,50,128,39,130,51,128,
39,131,52,128,39,132,53,128,39,133,54,128,39,134,55,128,
39,135,56,128,39,136,57,128,39,137,53,138,39,14,37,114,
37,118,37,122,37,126,37,130,37,134,37,138,37,142,37,146,
37,150,48,128,39,138,49,128,39,139,50,128,39,140,51,128,
39,141,52,128,39,142,53,128,39,143,54,128,39,144,55,128,
39,145,56,128,39,146,57,128,39,147,54,138,39,15,37,178,
37,182,37,186,37,190,37,194,37,198,37,202,37,206,37,210,
37,214,48,128,39,148,49,128,33,146,50,128,39,163,51,128,
33,148,52,128,33,149,53,128,39,153,54,128,39,155,55,128,
39,156,56,128,39,157,57,128,39,158,55,138,39,17,37,242,
37,246,37,250,37,254,38,2,38,6,38,10,38,14,38,18,
38,22,48,128,39,159,49,128,39,160,50,128,39,161,51,128,
39,162,52,128,39,164,53,128,39,165,54,128,39,166,55,128,
39,167,56,128,39,168,57,128,39,169,56,138,39,18,38,50,
38,54,38,58,38,62,38,66,38,70,38,74,38,78,38,82,
38,86,48,128,39,171,49,128,39,173,50,128,39,175,51,128,
39,178,52,128,39,179,53,128,39,181,54,128,39,184,55,128,
39,186,56,128,39,187,57,128,39,188,57,138,39,19,38,114,
38,118,38,122,38,126,38,130,38,134,38,138,38,142,38,146,
38,150,48,128,39,189,49,128,39,190,50,128,39,154,51,128,
39,170,52,128,39,182,53,128,39,185,54,128,39,152,55,128,
39,180,56,128,39,183,57,128,39,172,50,138,39,2,38,178,
38,224,38,228,38,232,38,236,38,240,38,244,38,248,38,252,
39,0,48,135,39,20,38,196,38,200,38,204,38,208,38,212,
38,216,38,220,48,128,39,174,49,128,39,177,50,128,39,3,
51,128,39,80,52,128,39,82,53,128,39,110,54,128,39,112,
49,128,39,21,50,128,39,22,51,128,39,23,52,128,39,24,
53,128,39,25,54,128,39,26,55,128,39,27,56,128,39,28,
57,128,39,34,51,138,39,4,39,28,39,32,39,36,39,40,
39,44,39,48,39,52,39,56,39,60,39,64,48,128,39,35,
49,128,39,36,50,128,39,37,51,128,39,38,52,128,39,39,
53,128,38,5,54,128,39,41,55,128,39,42,56,128,39,43,
57,128,39,44,52,138,38,14,39,92,39,96,39,100,39,104,
39,108,39,112,39,116,39,120,39,124,39,128,48,128,39,45,
49,128,39,46,50,128,39,47,51,128,39,48,52,128,39,49,
53,128,39,50,54,128,39,51,55,128,39,52,56,128,39,53,
57,128,39,54,53,138,39,6,39,156,39,160,39,164,39,168,
39,172,39,176,39,180,39,184,39,188,39,192,48,128,39,55,
49,128,39,56,50,128,39,57,51,128,39,58,52,128,39,59,
53,128,39,60,54,128,39,61,55,128,39,62,56,128,39,63,
57,128,39,64,54,138,39,29,39,220,39,224,39,228,39,232,
39,236,39,240,39,244,39,248,39,252,40,0,48,128,39,65,
49,128,39,66,50,128,39,67,51,128,39,68,52,128,39,69,
53,128,39,70,54,128,39,71,55,128,39,72,56,128,39,73,
57,128,39,74,55,138,39,30,40,28,40,32,40,36,40,40,
40,44,40,48,40,52,40,56,40,60,40,64,48,128,39,75,
49,128,37,207,50,128,39,77,51,128,37,160,52,128,39,79,
53,128,39,81,54,128,37,178,55,128,37,188,56,128,37,198,
57,128,39,86,56,137,39,31,40,90,40,94,40,98,40,102,
40,106,40,110,40,114,40,118,40,122,49,128,37,215,50,128,
39,88,51,128,39,89,52,128,39,90,53,128,39,111,54,128,
39,113,55,128,39,114,56,128,39,115,57,128,39,104,57,138,
39,32,40,150,40,154,40,158,40,162,40,166,40,170,40,174,
40,178,40,182,40,186,48,128,39,105,49,128,39,108,50,128,
39,109,51,128,39,106,52,128,39,107,53,128,39,116,54,128,
39,117,55,128,39,91,56,128,39,92,57,128,39,93,97,7,
40,206,40,216,40,223,40,230,40,255,41,15,41,26,226,229,
238,231,225,236,105,128,9,134,227,245,244,101,128,0,225,228,
229,246,97,128,9,6,231,117,2,40,237,40,246,234,225,242,
225,244,105,128,10,134,242,237,245,235,232,105,128,10,6,237,
225,244,242,225,231,245,242,237,245,235,232,105,128,10,62,242,
245,243,241,245,225,242,101,128,51,3,246,239,247,229,236,243,
233,231,110,3,41,42,41,52,41,59,226,229,238,231,225,236,
105,128,9,190,228,229,246,97,128,9,62,231,245,234,225,242,
225,244,105,128,10,190,98,4,41,80,41,121,41,130,41,140,
226,242,229,246,233,225,244,233,239,110,2,41,95,41,110,237,
225,242,235,225,242,237,229,238,233,225,110,128,5,95,243,233,
231,238,228,229,246,97,128,9,112,229,238,231,225,236,105,128,
9,133,239,240,239,237,239,230,111,128,49,26,242,229,246,101,
134,1,3,41,159,41,167,41,178,41,189,41,197,41,209,225,
227,245,244,101,128,30,175,227,249,242,233,236,236,233,99,128,
4,209,228,239,244,226,229,236,239,119,128,30,183,231,242,225,
246,101,128,30,177,232,239,239,235,225,226,239,246,101,128,30,
179,244,233,236,228,101,128,30,181,99,4,41,227,41,234,42,
57,42,127,225,242,239,110,128,1,206,233,242,99,2,41,242,
41,247,236,101,128,36,208,245,237,230,236,229,120,133,0,226,
42,10,42,18,42,29,42,37,42,49,225,227,245,244,101,128,
30,165,228,239,244,226,229,236,239,119,128,30,173,231,242,225,
246,101,128,30,167,232,239,239,235,225,226,239,246,101,128,30,
169,244,233,236,228,101,128,30,171,245,244,101,133,0,180,42,
73,42,84,42,101,42,108,42,117,226,229,236,239,247,227,237,
98,128,3,23,99,2,42,90,42,95,237,98,128,3,1,239,
237,98,128,3,1,228,229,246,97,128,9,84,236,239,247,237,
239,100,128,2,207,244,239,238,229,227,237,98,128,3,65,249,
242,233,236,236,233,99,128,4,48,100,5,42,149,42,159,42,
173,42,179,42,213,226,236,231,242,225,246,101,128,2,1,228,
225,235,231,245,242,237,245,235,232,105,128,10,113,229,246,97,
128,9,5,233,229,242,229,243,233,115,130,0,228,42,193,42,
204,227,249,242,233,236,236,233,99,128,4,211,237,225,227,242,
239,110,128,1,223,239,116,2,42,220,42,228,226,229,236,239,
119,128,30,161,237,225,227,242,239,110,128,1,225,101,131,0,
230,42,247,42,255,43,8,225,227,245,244,101,128,1,253,235,
239,242,229,225,110,128,49,80,237,225,227,242,239,110,128,1,
227,230,233,105,6,43,33,43,53,45,246,45,252,46,11,49,
111,48,2,43,39,43,46,176,178,176,56,128,32,21,184,185,
180,49,128,32,164,177,48,3,43,62,45,86,45,221,48,9,
43,82,43,102,43,164,43,226,44,32,44,94,44,156,44,218,
45,24,49,3,43,90,43,94,43,98,55,128,4,16,56,128,
4,17,57,128,4,18,50,10,43,124,43,128,43,132,43,136,
43,140,43,144,43,148,43,152,43,156,43,160,48,128,4,19,
49,128,4,20,50,128,4,21,51,128,4,1,52,128,4,22,
53,128,4,23,54,128,4,24,55,128,4,25,56,128,4,26,
57,128,4,27,51,10,43,186,43,190,43,194,43,198,43,202,
43,206,43,210,43,214,43,218,43,222,48,128,4,28,49,128,
4,29,50,128,4,30,51,128,4,31,52,128,4,32,53,128,
4,33,54,128,4,34,55,128,4,35,56,128,4,36,57,128,
4,37,52,10,43,248,43,252,44,0,44,4,44,8,44,12,
44,16,44,20,44,24,44,28,48,128,4,38,49,128,4,39,
50,128,4,40,51,128,4,41,52,128,4,42,53,128,4,43,
54,128,4,44,55,128,4,45,56,128,4,46,57,128,4,47,
53,10,44,54,44,58,44,62,44,66,44,70,44,74,44,78,
44,82,44,86,44,90,48,128,4,144,49,128,4,2,50,128,
4,3,51,128,4,4,52,128,4,5,53,128,4,6,54,128,
4,7,55,128,4,8,56,128,4,9,57,128,4,10,54,10,
44,116,44,120,44,124,44,128,44,132,44,136,44,140,44,144,
44,148,44,152,48,128,4,11,49,128,4,12,50,128,4,14,
51,128,246,196,52,128,246,197,53,128,4,48,54,128,4,49,
55,128,4,50,56,128,4,51,57,128,4,52,55,10,44,178,
44,182,44,186,44,190,44,194,44,198,44,202,44,206,44,210,
44,214,48,128,4,53,49,128,4,81,50,128,4,54,51,128,
4,55,52,128,4,56,53,128,4,57,54,128,4,58,55,128,
4,59,56,128,4,60,57,128,4,61,56,10,44,240,44,244,
44,248,44,252,45,0,45,4,45,8,45,12,45,16,45,20,
48,128,4,62,49,128,4,63,50,128,4,64,51,128,4,65,
52,128,4,66,53,128,4,67,54,128,4,68,55,128,4,69,
56,128,4,70,57,128,4,71,57,10,45,46,45,50,45,54,
45,58,45,62,45,66,45,70,45,74,45,78,45,82,48,128,
4,72,49,128,4,73,50,128,4,74,51,128,4,75,52,128,
4,76,53,128,4,77,54,128,4,78,55,128,4,79,56,128,
4,145,57,128,4,82,49,4,45,96,45,158,45,163,45,189,
48,10,45,118,45,122,45,126,45,130,45,134,45,138,45,142,
45,146,45,150,45,154,48,128,4,83,49,128,4,84,50,128,
4,85,51,128,4,86,52,128,4,87,53,128,4,88,54,128,
4,89,55,128,4,90,56,128,4,91,57,128,4,92,177,48,
128,4,94,52,4,45,173,45,177,45,181,45,185,53,128,4,
15,54,128,4,98,55,128,4,114,56,128,4,116,57,5,45,
201,45,205,45,209,45,213,45,217,50,128,246,198,51,128,4,
95,52,128,4,99,53,128,4,115,54,128,4,117,56,2,45,
227,45,241,51,2,45,233,45,237,49,128,246,199,50,128,246,
200,180,54,128,4,217,178,185,57,128,32,14,179,48,2,46,
3,46,7,48,128,32,15,49,128,32,13,181,55,7,46,28,
46,98,47,163,47,240,48,197,49,34,49,105,51,2,46,34,
46,48,56,2,46,40,46,44,49,128,6,106,56,128,6,12,
57,8,46,66,46,70,46,74,46,78,46,82,46,86,46,90,
46,94,50,128,6,96,51,128,6,97,52,128,6,98,53,128,
6,99,54,128,6,100,55,128,6,101,56,128,6,102,57,128,
6,103,52,7,46,114,46,146,46,208,47,14,47,46,47,102,
47,158,48,5,46,126,46,130,46,134,46,138,46,142,48,128,
6,104,49,128,6,105,51,128,6,27,55,128,6,31,57,128,
6,33,49,10,46,168,46,172,46,176,46,180,46,184,46,188,
46,192,46,196,46,200,46,204,48,128,6,34,49,128,6,35,
50,128,6,36,51,128,6,37,52,128,6,38,53,128,6,39,
54,128,6,40,55,128,6,41,56,128,6,42,57,128,6,43,
50,10,46,230,46,234,46,238,46,242,46,246,46,250,46,254,
47,2,47,6,47,10,48,128,6,44,49,128,6,45,50,128,
6,46,51,128,6,47,52,128,6,48,53,128,6,49,54,128,
6,50,55,128,6,51,56,128,6,52,57,128,6,53,51,5,
47,26,47,30,47,34,47,38,47,42,48,128,6,54,49,128,
6,55,50,128,6,56,51,128,6,57,52,128,6,58,52,9,
47,66,47,70,47,74,47,78,47,82,47,86,47,90,47,94,
47,98,48,128,6,64,49,128,6,65,50,128,6,66,51,128,
6,67,52,128,6,68,53,128,6,69,54,128,6,70,56,128,
6,72,57,128,6,73,53,9,47,122,47,126,47,130,47,134,
47,138,47,142,47,146,47,150,47,154,48,128,6,74,49,128,
6,75,50,128,6,76,51,128,6,77,52,128,6,78,53,128,
6,79,54,128,6,80,55,128,6,81,56,128,6,82,183,48,
128,6,71,53,3,47,171,47,203,47,235,48,5,47,183,47,
187,47,191,47,195,47,199,53,128,6,164,54,128,6,126,55,
128,6,134,56,128,6,152,57,128,6,175,49,5,47,215,47,
219,47,223,47,227,47,231,49,128,6,121,50,128,6,136,51,
128,6,145,52,128,6,186,57,128,6,210,179,52,128,6,213,
54,7,48,0,48,5,48,10,48,15,48,53,48,115,48,177,
179,54,128,32,170,180,53,128,5,190,181,56,128,5,195,54,
6,48,29,48,33,48,37,48,41,48,45,48,49,52,128,5,
208,53,128,5,209,54,128,5,210,55,128,5,211,56,128,5,
212,57,128,5,213,55,10,48,75,48,79,48,83,48,87,48,
91,48,95,48,99,48,103,48,107,48,111,48,128,5,214,49,
128,5,215,50,128,5,216,51,128,5,217,52,128,5,218,53,
128,5,219,54,128,5,220,55,128,5,221,56,128,5,222,57,
128,5,223,56,10,48,137,48,141,48,145,48,149,48,153,48,
157,48,161,48,165,48,169,48,173,48,128,5,224,49,128,5,
225,50,128,5,226,51,128,5,227,52,128,5,228,53,128,5,
229,54,128,5,230,55,128,5,231,56,128,5,232,57,128,5,
233,57,3,48,185,48,189,48,193,48,128,5,234,52,128,251,
42,53,128,251,43,55,4,48,207,48,221,48,241,48,246,48,
2,48,213,48,217,48,128,251,75,53,128,251,31,49,3,48,
229,48,233,48,237,54,128,5,240,55,128,5,241,56,128,5,
242,178,51,128,251,53,57,7,49,6,49,10,49,14,49,18,
49,22,49,26,49,30,51,128,5,180,52,128,5,181,53,128,
5,182,54,128,5,187,55,128,5,184,56,128,5,183,57,128,
5,176,56,3,49,42,49,86,49,91,48,7,49,58,49,62,
49,66,49,70,49,74,49,78,49,82,48,128,5,178,49,128,
5,177,50,128,5,179,51,128,5,194,52,128,5,193,54,128,
5,185,55,128,5,188,179,57,128,5,189,52,2,49,97,49,
101,49,128,5,191,50,128,5,192,185,178,57,128,2,188,54,
3,49,119,49,178,49,185,49,4,49,129,49,145,49,151,49,
172,50,2,49,135,49,140,180,56,128,33,5,184,57,128,33,
19,179,181,50,128,33,22,181,55,3,49,160,49,164,49,168,
51,128,32,44,52,128,32,45,53,128,32,46,182,182,52,128,
32,12,179,177,182,55,128,6,109,180,185,179,55,128,2,189,
103,2,49,198,49,205,242,225,246,101,128,0,224,117,2,49,
211,49,220,234,225,242,225,244,105,128,10,133,242,237,245,235,
232,105,128,10,5,104,2,49,235,49,245,233,242,225,231,225,
238,97,128,48,66,239,239,235,225,226,239,246,101,128,30,163,
105,7,50,16,50,41,50,48,50,60,50,85,50,101,50,181,
98,2,50,22,50,31,229,238,231,225,236,105,128,9,144,239,
240,239,237,239,230,111,128,49,30,228,229,246,97,128,9,16,
229,227,249,242,233,236,236,233,99,128,4,213,231,117,2,50,
67,50,76,234,225,242,225,244,105,128,10,144,242,237,245,235,
232,105,128,10,16,237,225,244,242,225,231,245,242,237,245,235,
232,105,128,10,72,110,5,50,113,50,122,50,136,50,152,50,
167,225,242,225,226,233,99,128,6,57,230,233,238,225,236,225,
242,225,226,233,99,128,254,202,233,238,233,244,233,225,236,225,
242,225,226,233,99,128,254,203,237,229,228,233,225,236,225,242,
225,226,233,99,128,254,204,246,229,242,244,229,228,226,242,229,
246,101,128,2,3,246,239,247,229,236,243,233,231,110,3,50,
197,50,207,50,214,226,229,238,231,225,236,105,128,9,200,228,
229,246,97,128,9,72,231,245,234,225,242,225,244,105,128,10,
200,107,2,50,231,50,255,225,244,225,235,225,238,97,129,48,
162,50,243,232,225,236,230,247,233,228,244,104,128,255,113,239,
242,229,225,110,128,49,79,108,3,51,15,52,71,52,80,101,
2,51,21,52,66,102,136,5,208,51,41,51,50,51,65,51,
79,51,168,51,182,52,37,52,51,225,242,225,226,233,99,128,
6,39,228,225,231,229,243,232,232,229,226,242,229,119,128,251,
48,230,233,238,225,236,225,242,225,226,233,99,128,254,142,104,
2,51,85,51,160,225,237,250,97,2,51,94,51,127,225,226,
239,246,101,2,51,104,51,113,225,242,225,226,233,99,128,6,
35,230,233,238,225,236,225,242,225,226,233,99,128,254,132,226,
229,236,239,119,2,51,137,51,146,225,242,225,226,233,99,128,
6,37,230,233,238,225,236,225,242,225,226,233,99,128,254,136,
229,226,242,229,119,128,5,208,236,225,237,229,228,232,229,226,
242,229,119,128,251,79,237,97,2,51,189,51,225,228,228,225,
225,226,239,246,101,2,51,202,51,211,225,242,225,226,233,99,
128,6,34,230,233,238,225,236,225,242,225,226,233,99,128,254,
130,235,243,245,242,97,4,51,239,51,248,52,6,52,22,225,
242,225,226,233,99,128,6,73,230,233,238,225,236,225,242,225,
226,233,99,128,254,240,233,238,233,244,233,225,236,225,242,225,
226,233,99,128,254,243,237,229,228,233,225,236,225,242,225,226,
233,99,128,254,244,240,225,244,225,232,232,229,226,242,229,119,
128,251,46,241,225,237,225,244,243,232,229,226,242,229,119,128,
251,47,240,104,128,33,53,236,229,241,245,225,108,128,34,76,
240,232,97,129,3,177,52,88,244,239,238,239,115,128,3,172,
109,4,52,106,52,114,52,125,52,159,225,227,242,239,110,128,
1,1,239,238,239,243,240,225,227,101,128,255,65,240,229,242,
243,225,238,100,130,0,38,52,139,52,151,237,239,238,239,243,
240,225,227,101,128,255,6,243,237,225,236,108,128,247,38,243,
241,245,225,242,101,128,51,194,110,4,52,178,52,189,53,55,
53,65,226,239,240,239,237,239,230,111,128,49,34,103,4,52,
199,52,210,52,224,53,47,226,239,240,239,237,239,230,111,128,
49,36,235,232,225,238,235,232,245,244,232,225,105,128,14,90,
236,101,131,34,32,52,235,53,32,53,39,226,242,225,227,235,
229,116,2,52,247,53,11,236,229,230,116,129,48,8,53,0,
246,229,242,244,233,227,225,108,128,254,63,242,233,231,232,116,
129,48,9,53,21,246,229,242,244,233,227,225,108,128,254,64,
236,229,230,116,128,35,41,242,233,231,232,116,128,35,42,243,
244,242,239,109,128,33,43,239,244,229,236,229,233,97,128,3,
135,117,2,53,71,53,83,228,225,244,244,225,228,229,246,97,
128,9,82,243,246,225,242,97,3,53,95,53,105,53,112,226,
229,238,231,225,236,105,128,9,130,228,229,246,97,128,9,2,
231,245,234,225,242,225,244,105,128,10,130,239,231,239,238,229,
107,128,1,5,112,3,53,140,53,164,53,194,97,2,53,146,
53,158,225,244,239,243,241,245,225,242,101,128,51,0,242,229,
110,128,36,156,239,243,244,242,239,240,232,101,2,53,177,53,
188,225,242,237,229,238,233,225,110,128,5,90,237,239,100,128,
2,188,112,2,53,200,53,205,236,101,128,248,255,242,111,2,
53,212,53,220,225,227,232,229,115,128,34,80,120,2,53,226,
53,246,229,241,245,225,108,129,34,72,53,236,239,242,233,237,
225,231,101,128,34,82,233,237,225,244,229,236,249,229,241,245,
225,108,128,34,69,114,4,54,15,54,42,54,46,54,91,225,
229,97,2,54,23,54,33,229,235,239,242,229,225,110,128,49,
142,235,239,242,229,225,110,128,49,141,99,128,35,18,105,2,
54,52,54,66,231,232,244,232,225,236,230,242,233,238,103,128,
30,154,238,103,130,0,229,54,75,54,83,225,227,245,244,101,
128,1,251,226,229,236,239,119,128,30,1,242,239,119,8,54,
111,54,118,54,247,55,57,55,107,55,162,55,185,56,4,226,
239,244,104,128,33,148,100,3,54,126,54,165,54,212,225,243,
104,4,54,138,54,145,54,152,54,160,228,239,247,110,128,33,
227,236,229,230,116,128,33,224,242,233,231,232,116,128,33,226,
245,112,128,33,225,226,108,5,54,178,54,185,54,192,54,199,
54,207,226,239,244,104,128,33,212,228,239,247,110,128,33,211,
236,229,230,116,128,33,208,242,233,231,232,116,128,33,210,245,
112,128,33,209,239,247,110,131,33,147,54,224,54,231,54,239,
236,229,230,116,128,33,153,242,233,231,232,116,128,33,152,247,
232,233,244,101,128,33,233,104,2,54,253,55,48,229,225,100,
4,55,9,55,19,55,29,55,40,228,239,247,238,237,239,100,
128,2,197,236,229,230,244,237,239,100,128,2,194,242,233,231,
232,244,237,239,100,128,2,195,245,240,237,239,100,128,2,196,
239,242,233,250,229,120,128,248,231,236,229,230,116,131,33,144,
55,70,55,87,55,99,228,226,108,129,33,208,55,78,243,244,
242,239,235,101,128,33,205,239,246,229,242,242,233,231,232,116,
128,33,198,247,232,233,244,101,128,33,230,242,233,231,232,116,
132,33,146,55,123,55,135,55,143,55,154,228,226,236,243,244,
242,239,235,101,128,33,207,232,229,225,246,121,128,39,158,239,
246,229,242,236,229,230,116,128,33,196,247,232,233,244,101,128,
33,232,244,225,98,2,55,170,55,177,236,229,230,116,128,33,
228,242,233,231,232,116,128,33,229,245,112,132,33,145,55,198,
55,226,55,244,55,252,100,2,55,204,55,216,110,129,33,149,
55,210,226,243,101,128,33,168,239,247,238,226,225,243,101,128,
33,168,236,229,230,116,129,33,150,55,235,239,230,228,239,247,
110,128,33,197,242,233,231,232,116,128,33,151,247,232,233,244,
101,128,33,231,246,229,242,244,229,120,128,248,230,115,5,56,
25,56,101,56,146,56,229,56,239,99,2,56,31,56,83,233,
105,2,56,38,56,61,227,233,242,227,245,109,129,0,94,56,
49,237,239,238,239,243,240,225,227,101,128,255,62,244,233,236,
228,101,129,0,126,56,71,237,239,238,239,243,240,225,227,101,
128,255,94,242,233,240,116,129,2,81,56,92,244,245,242,238,
229,100,128,2,82,237,225,236,108,2,56,110,56,121,232,233,
242,225,231,225,238,97,128,48,65,235,225,244,225,235,225,238,
97,129,48,161,56,134,232,225,236,230,247,233,228,244,104,128,
255,103,244,229,242,233,115,2,56,156,56,225,107,131,0,42,
56,166,56,194,56,217,97,2,56,172,56,186,236,244,239,238,
229,225,242,225,226,233,99,128,6,109,242,225,226,233,99,128,
6,109,109,2,56,200,56,206,225,244,104,128,34,23,239,238,
239,243,240,225,227,101,128,255,10,243,237,225,236,108,128,254,
97,109,128,32,66,245,240,229,242,233,239,114,128,246,233,249,
237,240,244,239,244,233,227,225,236,236,249,229,241,245,225,108,
128,34,67,116,132,0,64,57,15,57,22,57,34,57,42,233,
236,228,101,128,0,227,237,239,238,239,243,240,225,227,101,128,
255,32,243,237,225,236,108,128,254,107,245,242,238,229,100,128,
2,80,117,6,57,64,57,89,57,96,57,121,57,141,57,157,
98,2,57,70,57,79,229,238,231,225,236,105,128,9,148,239,
240,239,237,239,230,111,128,49,32,228,229,246,97,128,9,20,
231,117,2,57,103,57,112,234,225,242,225,244,105,128,10,148,
242,237,245,235,232,105,128,10,20,236,229,238,231,244,232,237,
225,242,235,226,229,238,231,225,236,105,128,9,215,237,225,244,
242,225,231,245,242,237,245,235,232,105,128,10,76,246,239,247,
229,236,243,233,231,110,3,57,173,57,183,57,190,226,229,238,
231,225,236,105,128,9,204,228,229,246,97,128,9,76,231,245,
234,225,242,225,244,105,128,10,204,246,225,231,242,225,232,225,
228,229,246,97,128,9,61,121,2,57,221,57,233,226,225,242,
237,229,238,233,225,110,128,5,97,233,110,130,5,226,57,242,
58,1,225,236,244,239,238,229,232,229,226,242,229,119,128,251,
32,232,229,226,242,229,119,128,5,226,98,144,0,98,58,46,
58,181,58,192,58,201,58,226,60,11,60,73,60,146,62,72,
62,84,62,127,62,135,62,145,64,15,64,39,64,48,97,7,
58,62,58,72,58,96,58,103,58,128,58,152,58,163,226,229,
238,231,225,236,105,128,9,172,227,235,243,236,225,243,104,129,
0,92,58,84,237,239,238,239,243,240,225,227,101,128,255,60,
228,229,246,97,128,9,44,231,117,2,58,110,58,119,234,225,
242,225,244,105,128,10,172,242,237,245,235,232,105,128,10,44,
104,2,58,134,58,144,233,242,225,231,225,238,97,128,48,112,
244,244,232,225,105,128,14,63,235,225,244,225,235,225,238,97,
128,48,208,114,129,0,124,58,169,237,239,238,239,243,240,225,
227,101,128,255,92,226,239,240,239,237,239,230,111,128,49,5,
227,233,242,227,236,101,128,36,209,228,239,116,2,58,209,58,
218,225,227,227,229,238,116,128,30,3,226,229,236,239,119,128,
30,5,101,6,58,240,59,5,59,28,59,170,59,181,59,193,
225,237,229,228,243,233,248,244,229,229,238,244,232,238,239,244,
229,115,128,38,108,99,2,59,11,59,18,225,245,243,101,128,
34,53,249,242,233,236,236,233,99,128,4,49,104,5,59,40,
59,49,59,63,59,93,59,152,225,242,225,226,233,99,128,6,
40,230,233,238,225,236,225,242,225,226,233,99,128,254,144,105,
2,59,69,59,84,238,233,244,233,225,236,225,242,225,226,233,
99,128,254,145,242,225,231,225,238,97,128,48,121,237,101,2,
59,100,59,113,228,233,225,236,225,242,225,226,233,99,128,254,
146,229,237,105,2,59,121,59,136,238,233,244,233,225,236,225,
242,225,226,233,99,128,252,159,243,239,236,225,244,229,228,225,
242,225,226,233,99,128,252,8,238,239,239,238,230,233,238,225,
236,225,242,225,226,233,99,128,252,109,235,225,244,225,235,225,
238,97,128,48,217,238,225,242,237,229,238,233,225,110,128,5,
98,116,132,5,209,59,205,59,225,59,245,59,254,97,129,3,
178,59,211,243,249,237,226,239,236,231,242,229,229,107,128,3,
208,228,225,231,229,243,104,129,251,49,59,236,232,229,226,242,
229,119,128,251,49,232,229,226,242,229,119,128,5,209,242,225,
230,229,232,229,226,242,229,119,128,251,76,104,2,60,17,60,
67,97,3,60,25,60,35,60,42,226,229,238,231,225,236,105,
128,9,173,228,229,246,97,128,9,45,231,117,2,60,49,60,
58,234,225,242,225,244,105,128,10,173,242,237,245,235,232,105,
128,10,45,239,239,107,128,2,83,105,5,60,85,60,96,60,
107,60,121,60,135,232,233,242,225,231,225,238,97,128,48,115,
235,225,244,225,235,225,238,97,128,48,211,236,225,226,233,225,
236,227,236,233,227,107,128,2,152,238,228,233,231,245,242,237,
245,235,232,105,128,10,2,242,245,243,241,245,225,242,101,128,
51,49,108,3,60,154,62,55,62,66,97,2,60,160,62,50,
227,107,6,60,175,60,184,60,221,61,114,61,169,61,221,227,
233,242,227,236,101,128,37,207,100,2,60,190,60,199,233,225,
237,239,238,100,128,37,198,239,247,238,240,239,233,238,244,233,
238,231,244,242,233,225,238,231,236,101,128,37,188,108,2,60,
227,61,74,101,2,60,233,61,13,230,244,240,239,233,238,244,
233,238,103,2,60,248,61,2,240,239,233,238,244,229,114,128,
37,196,244,242,233,225,238,231,236,101,128,37,192,238,244,233,
227,245,236,225,242,226,242,225,227,235,229,116,2,61,33,61,
53,236,229,230,116,129,48,16,61,42,246,229,242,244,233,227,
225,108,128,254,59,242,233,231,232,116,129,48,17,61,63,246,
229,242,244,233,227,225,108,128,254,60,239,247,229,114,2,61,
83,61,98,236,229,230,244,244,242,233,225,238,231,236,101,128,
37,227,242,233,231,232,244,244,242,233,225,238,231,236,101,128,
37,226,114,2,61,120,61,131,229,227,244,225,238,231,236,101,
128,37,172,233,231,232,244,240,239,233,238,244,233,238,103,2,
61,148,61,158,240,239,233,238,244,229,114,128,37,186,244,242,
233,225,238,231,236,101,128,37,182,115,3,61,177,61,207,61,
215,109,2,61,183,61,195,225,236,236,243,241,245,225,242,101,
128,37,170,233,236,233,238,231,230,225,227,101,128,38,59,241,
245,225,242,101,128,37,160,244,225,114,128,38,5,245,240,112,
2,61,229,62,11,229,114,2,61,236,61,251,236,229,230,244,
244,242,233,225,238,231,236,101,128,37,228,242,233,231,232,244,
244,242,233,225,238,231,236,101,128,37,229,239,233,238,244,233,
238,103,2,62,23,62,39,243,237,225,236,236,244,242,233,225,
238,231,236,101,128,37,180,244,242,233,225,238,231,236,101,128,
37,178,238,107,128,36,35,233,238,229,226,229,236,239,119,128,
30,7,239,227,107,128,37,136,237,239,238,239,243,240,225,227,
101,128,255,66,111,3,62,92,62,105,62,116,226,225,233,237,
225,233,244,232,225,105,128,14,26,232,233,242,225,231,225,238,
97,128,48,124,235,225,244,225,235,225,238,97,128,48,220,240,
225,242,229,110,128,36,157,241,243,241,245,225,242,101,128,51,
195,114,4,62,155,63,149,63,222,64,5,225,99,2,62,162,
63,56,101,3,62,170,62,175,62,243,229,120,128,248,244,236,
229,230,116,133,0,123,62,192,62,197,62,219,62,227,62,232,
226,116,128,248,243,109,2,62,203,62,208,233,100,128,248,242,
239,238,239,243,240,225,227,101,128,255,91,243,237,225,236,108,
128,254,91,244,112,128,248,241,246,229,242,244,233,227,225,108,
128,254,55,242,233,231,232,116,133,0,125,63,5,63,10,63,
32,63,40,63,45,226,116,128,248,254,109,2,63,16,63,21,
233,100,128,248,253,239,238,239,243,240,225,227,101,128,255,93,
243,237,225,236,108,128,254,92,244,112,128,248,252,246,229,242,
244,233,227,225,108,128,254,56,235,229,116,2,63,64,63,106,
236,229,230,116,132,0,91,63,79,63,84,63,89,63,101,226,
116,128,248,240,229,120,128,248,239,237,239,238,239,243,240,225,
227,101,128,255,59,244,112,128,248,238,242,233,231,232,116,132,
0,93,63,122,63,127,63,132,63,144,226,116,128,248,251,229,
120,128,248,250,237,239,238,239,243,240,225,227,101,128,255,61,
244,112,128,248,249,229,246,101,131,2,216,63,161,63,172,63,
178,226,229,236,239,247,227,237,98,128,3,46,227,237,98,128,
3,6,233,238,246,229,242,244,229,100,3,63,193,63,204,63,
210,226,229,236,239,247,227,237,98,128,3,47,227,237,98,128,
3,17,228,239,245,226,236,229,227,237,98,128,3,97,233,228,
231,101,2,63,231,63,242,226,229,236,239,247,227,237,98,128,
3,42,233,238,246,229,242,244,229,228,226,229,236,239,247,227,
237,98,128,3,58,239,235,229,238,226,225,114,128,0,166,115,
2,64,21,64,29,244,242,239,235,101,128,1,128,245,240,229,
242,233,239,114,128,246,234,244,239,240,226,225,114,128,1,131,
117,3,64,56,64,67,64,78,232,233,242,225,231,225,238,97,
128,48,118,235,225,244,225,235,225,238,97,128,48,214,236,108,
2,64,85,64,115,229,116,130,32,34,64,94,64,104,233,238,
246,229,242,243,101,128,37,216,239,240,229,242,225,244,239,114,
128,34,25,243,229,249,101,128,37,206,99,143,0,99,64,156,
65,105,65,116,65,180,65,211,66,48,67,215,68,199,69,43,
69,92,72,84,72,92,72,102,72,114,72,147,97,9,64,176,
64,187,64,197,64,204,64,211,64,236,64,246,65,42,65,51,
225,242,237,229,238,233,225,110,128,5,110,226,229,238,231,225,
236,105,128,9,154,227,245,244,101,128,1,7,228,229,246,97,
128,9,26,231,117,2,64,218,64,227,234,225,242,225,244,105,
128,10,154,242,237,245,235,232,105,128,10,26,236,243,241,245,
225,242,101,128,51,136,238,228,242,225,226,233,238,228,117,4,
65,8,65,18,65,24,65,31,226,229,238,231,225,236,105,128,
9,129,227,237,98,128,3,16,228,229,246,97,128,9,1,231,
245,234,225,242,225,244,105,128,10,129,240,243,236,239,227,107,
128,33,234,114,3,65,59,65,65,65,91,229,239,102,128,33,
5,239,110,130,2,199,65,74,65,85,226,229,236,239,247,227,
237,98,128,3,44,227,237,98,128,3,12,242,233,225,231,229,
242,229,244,245,242,110,128,33,181,226,239,240,239,237,239,230,
111,128,49,24,99,4,65,126,65,133,65,152,65,174,225,242,
239,110,128,1,13,229,228,233,236,236,97,129,0,231,65,144,
225,227,245,244,101,128,30,9,233,242,99,2,65,160,65,165,
236,101,128,36,210,245,237,230,236,229,120,128,1,9,245,242,
108,128,2,85,100,2,65,186,65,202,239,116,129,1,11,65,
193,225,227,227,229,238,116,128,1,11,243,241,245,225,242,101,
128,51,197,101,2,65,217,65,233,228,233,236,236,97,129,0,
184,65,227,227,237,98,128,3,39,238,116,132,0,162,65,246,
66,14,66,26,66,37,105,2,65,252,66,4,231,242,225,228,
101,128,33,3,238,230,229,242,233,239,114,128,246,223,237,239,
238,239,243,240,225,227,101,128,255,224,239,236,228,243,244,249,
236,101,128,247,162,243,245,240,229,242,233,239,114,128,246,224,
104,5,66,60,66,123,66,134,67,62,67,154,97,4,66,70,
66,81,66,91,66,98,225,242,237,229,238,233,225,110,128,5,
121,226,229,238,231,225,236,105,128,9,155,228,229,246,97,128,
9,27,231,117,2,66,105,66,114,234,225,242,225,244,105,128,
10,155,242,237,245,235,232,105,128,10,27,226,239,240,239,237,
239,230,111,128,49,20,101,6,66,148,66,168,66,192,67,4,
67,16,67,37,225,226,235,232,225,243,233,225,238,227,249,242,
233,236,236,233,99,128,4,189,99,2,66,174,66,182,235,237,
225,242,107,128,39,19,249,242,233,236,236,233,99,128,4,71,
100,2,66,198,66,242,229,243,227,229,238,228,229,114,2,66,
211,66,231,225,226,235,232,225,243,233,225,238,227,249,242,233,
236,236,233,99,128,4,191,227,249,242,233,236,236,233,99,128,
4,183,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
99,128,4,245,232,225,242,237,229,238,233,225,110,128,5,115,
235,232,225,235,225,243,243,233,225,238,227,249,242,233,236,236,
233,99,128,4,204,246,229,242,244,233,227,225,236,243,244,242,
239,235,229,227,249,242,233,236,236,233,99,128,4,185,105,129,
3,199,67,68,229,245,227,104,4,67,81,67,116,67,131,67,
140,97,2,67,87,67,102,227,233,242,227,236,229,235,239,242,
229,225,110,128,50,119,240,225,242,229,238,235,239,242,229,225,
110,128,50,23,227,233,242,227,236,229,235,239,242,229,225,110,
128,50,105,235,239,242,229,225,110,128,49,74,240,225,242,229,
238,235,239,242,229,225,110,128,50,9,111,2,67,160,67,210,
227,104,3,67,169,67,191,67,201,225,110,2,67,176,67,184,
231,244,232,225,105,128,14,10,244,232,225,105,128,14,8,233,
238,231,244,232,225,105,128,14,9,239,229,244,232,225,105,128,
14,12,239,107,128,1,136,105,2,67,221,68,67,229,245,99,
5,67,235,68,14,68,29,68,38,68,52,97,2,67,241,68,
0,227,233,242,227,236,229,235,239,242,229,225,110,128,50,118,
240,225,242,229,238,235,239,242,229,225,110,128,50,22,227,233,
242,227,236,229,235,239,242,229,225,110,128,50,104,235,239,242,
229,225,110,128,49,72,240,225,242,229,238,235,239,242,229,225,
110,128,50,8,245,240,225,242,229,238,235,239,242,229,225,110,
128,50,28,242,99,2,68,74,68,169,236,101,132,37,203,68,
87,68,98,68,103,68,127,237,245,236,244,233,240,236,121,128,
34,151,239,116,128,34,153,112,2,68,109,68,115,236,245,115,
128,34,149,239,243,244,225,236,237,225,242,107,128,48,54,247,
233,244,104,2,68,136,68,152,236,229,230,244,232,225,236,230,
226,236,225,227,107,128,37,208,242,233,231,232,244,232,225,236,
230,226,236,225,227,107,128,37,209,245,237,230,236,229,120,130,
2,198,68,182,68,193,226,229,236,239,247,227,237,98,128,3,
45,227,237,98,128,3,2,108,3,68,207,68,213,69,11,229,
225,114,128,35,39,233,227,107,4,68,225,68,236,68,245,68,
255,225,236,246,229,239,236,225,114,128,1,194,228,229,238,244,
225,108,128,1,192,236,225,244,229,242,225,108,128,1,193,242,
229,244,242,239,230,236,229,120,128,1,195,245,98,129,38,99,
69,18,243,245,233,116,2,69,27,69,35,226,236,225,227,107,
128,38,99,247,232,233,244,101,128,38,103,109,3,69,51,69,
65,69,76,227,245,226,229,228,243,241,245,225,242,101,128,51,
164,239,238,239,243,240,225,227,101,128,255,67,243,241,245,225,
242,229,228,243,241,245,225,242,101,128,51,160,111,8,69,110,
69,121,69,208,70,150,71,179,71,210,72,61,72,70,225,242,
237,229,238,233,225,110,128,5,129,236,239,110,131,0,58,69,
133,69,158,69,177,237,239,110,2,69,141,69,149,229,244,225,
242,121,128,32,161,239,243,240,225,227,101,128,255,26,115,2,
69,164,69,170,233,231,110,128,32,161,237,225,236,108,128,254,
85,244,242,233,225,238,231,245,236,225,114,2,69,192,69,202,
232,225,236,230,237,239,100,128,2,209,237,239,100,128,2,208,
109,2,69,214,70,143,237,97,134,0,44,69,231,70,39,70,
50,70,62,70,92,70,115,97,3,69,239,70,9,70,17,226,
239,246,101,2,69,248,69,254,227,237,98,128,3,19,242,233,
231,232,244,227,237,98,128,3,21,227,227,229,238,116,128,246,
195,114,2,70,23,70,30,225,226,233,99,128,6,12,237,229,
238,233,225,110,128,5,93,233,238,230,229,242,233,239,114,128,
246,225,237,239,238,239,243,240,225,227,101,128,255,12,242,229,
246,229,242,243,229,100,2,70,75,70,86,225,226,239,246,229,
227,237,98,128,3,20,237,239,100,128,2,189,115,2,70,98,
70,105,237,225,236,108,128,254,80,245,240,229,242,233,239,114,
128,246,226,244,245,242,238,229,100,2,70,126,70,137,225,226,
239,246,229,227,237,98,128,3,18,237,239,100,128,2,187,240,
225,243,115,128,38,60,110,2,70,156,70,165,231,242,245,229,
238,116,128,34,69,116,2,70,171,70,185,239,245,242,233,238,
244,229,231,242,225,108,128,34,46,242,239,108,142,35,3,70,
219,70,225,70,240,70,255,71,43,71,88,71,102,71,107,71,
112,71,117,71,123,71,128,71,169,71,174,193,195,75,128,0,
6,66,2,70,231,70,236,197,76,128,0,7,83,128,0,8,
67,2,70,246,70,251,193,78,128,0,24,82,128,0,13,68,
3,71,7,71,33,71,38,67,4,71,17,71,21,71,25,71,
29,49,128,0,17,50,128,0,18,51,128,0,19,52,128,0,
20,197,76,128,0,127,204,69,128,0,16,69,5,71,55,71,
59,71,64,71,69,71,74,77,128,0,25,206,81,128,0,5,
207,84,128,0,4,211,67,128,0,27,84,2,71,80,71,84,
66,128,0,23,88,128,0,3,70,2,71,94,71,98,70,128,
0,12,83,128,0,28,199,83,128,0,29,200,84,128,0,9,
204,70,128,0,10,206,193,75,128,0,21,210,83,128,0,30,
83,5,71,140,71,144,71,154,71,159,71,164,73,128,0,15,
79,129,0,14,71,150,84,128,0,2,212,88,128,0,1,213,
66,128,0,26,217,78,128,0,22,213,83,128,0,31,214,84,
128,0,11,240,249,242,233,231,232,116,129,0,169,71,191,115,
2,71,197,71,203,225,238,115,128,248,233,229,242,233,102,128,
246,217,114,2,71,216,72,44,238,229,242,226,242,225,227,235,
229,116,2,71,231,72,9,236,229,230,116,130,48,12,71,242,
71,254,232,225,236,230,247,233,228,244,104,128,255,98,246,229,
242,244,233,227,225,108,128,254,65,242,233,231,232,116,130,48,
13,72,21,72,33,232,225,236,230,247,233,228,244,104,128,255,
99,246,229,242,244,233,227,225,108,128,254,66,240,239,242,225,
244,233,239,238,243,241,245,225,242,101,128,51,127,243,241,245,
225,242,101,128,51,199,246,229,242,235,231,243,241,245,225,242,
101,128,51,198,240,225,242,229,110,128,36,158,242,245,250,229,
233,242,111,128,32,162,243,244,242,229,244,227,232,229,100,128,
2,151,245,114,2,72,121,72,139,236,121,2,72,128,72,134,
225,238,100,128,34,207,239,114,128,34,206,242,229,238,227,121,
128,0,164,249,114,4,72,158,72,166,72,173,72,181,194,242,
229,246,101,128,246,209,198,236,229,120,128,246,210,226,242,229,
246,101,128,246,212,230,236,229,120,128,246,213,100,146,0,100,
72,228,74,110,75,134,75,194,76,114,77,68,77,130,78,59,
78,72,78,81,78,107,78,132,78,141,79,208,79,216,79,227,
79,247,80,19,97,11,72,252,73,7,73,17,73,89,73,152,
73,163,73,174,73,243,74,49,74,55,74,85,225,242,237,229,
238,233,225,110,128,5,100,226,229,238,231,225,236,105,128,9,
166,100,5,73,29,73,38,73,44,73,58,73,74,225,242,225,
226,233,99,128,6,54,229,246,97,128,9,38,230,233,238,225,
236,225,242,225,226,233,99,128,254,190,233,238,233,244,233,225,
236,225,242,225,226,233,99,128,254,191,237,229,228,233,225,236,
225,242,225,226,233,99,128,254,192,103,3,73,97,73,114,73,
128,229,243,104,129,5,188,73,105,232,229,226,242,229,119,128,
5,188,231,229,114,129,32,32,73,122,228,226,108,128,32,33,
117,2,73,134,73,143,234,225,242,225,244,105,128,10,166,242,
237,245,235,232,105,128,10,38,232,233,242,225,231,225,238,97,
128,48,96,235,225,244,225,235,225,238,97,128,48,192,108,3,
73,182,73,191,73,229,225,242,225,226,233,99,128,6,47,229,
116,130,5,211,73,200,73,220,228,225,231,229,243,104,129,251,
51,73,211,232,229,226,242,229,119,128,251,51,232,229,226,242,
229,119,128,5,211,230,233,238,225,236,225,242,225,226,233,99,
128,254,170,237,237,97,3,73,253,74,6,74,18,225,242,225,
226,233,99,128,6,79,236,239,247,225,242,225,226,233,99,128,
6,79,244,225,238,97,2,74,27,74,41,236,244,239,238,229,
225,242,225,226,233,99,128,6,76,242,225,226,233,99,128,6,
76,238,228,97,128,9,100,242,231,97,2,74,63,74,72,232,
229,226,242,229,119,128,5,167,236,229,230,244,232,229,226,242,
229,119,128,5,167,243,233,225,240,238,229,245,237,225,244,225,
227,249,242,233,236,236,233,227,227,237,98,128,4,133,98,3,
74,118,75,115,75,125,108,9,74,138,74,146,75,3,75,11,
75,27,75,38,75,56,75,70,75,81,199,242,225,246,101,128,
246,211,97,2,74,152,74,209,238,231,236,229,226,242,225,227,
235,229,116,2,74,168,74,188,236,229,230,116,129,48,10,74,
177,246,229,242,244,233,227,225,108,128,254,61,242,233,231,232,
116,129,48,11,74,198,246,229,242,244,233,227,225,108,128,254,
62,114,2,74,215,74,236,227,232,233,238,246,229,242,244,229,
228,226,229,236,239,247,227,237,98,128,3,43,242,239,119,2,
74,244,74,251,236,229,230,116,128,33,212,242,233,231,232,116,
128,33,210,228,225,238,228,97,128,9,101,231,242,225,246,101,
129,246,214,75,21,227,237,98,128,3,15,233,238,244,229,231,
242,225,108,128,34,44,236,239,247,236,233,238,101,129,32,23,
75,50,227,237,98,128,3,51,239,246,229,242,236,233,238,229,
227,237,98,128,3,63,240,242,233,237,229,237,239,100,128,2,
186,246,229,242,244,233,227,225,108,2,75,94,75,100,226,225,
114,128,32,22,236,233,238,229,225,226,239,246,229,227,237,98,
128,3,14,239,240,239,237,239,230,111,128,49,9,243,241,245,
225,242,101,128,51,200,99,4,75,144,75,151,75,160,75,187,
225,242,239,110,128,1,15,229,228,233,236,236,97,128,30,17,
233,242,99,2,75,168,75,173,236,101,128,36,211,245,237,230,
236,229,248,226,229,236,239,119,128,30,19,242,239,225,116,128,
1,17,100,4,75,204,76,29,76,39,76,90,97,4,75,214,
75,224,75,231,76,0,226,229,238,231,225,236,105,128,9,161,
228,229,246,97,128,9,33,231,117,2,75,238,75,247,234,225,
242,225,244,105,128,10,161,242,237,245,235,232,105,128,10,33,
108,2,76,6,76,15,225,242,225,226,233,99,128,6,136,230,
233,238,225,236,225,242,225,226,233,99,128,251,137,228,232,225,
228,229,246,97,128,9,92,232,97,3,76,48,76,58,76,65,
226,229,238,231,225,236,105,128,9,162,228,229,246,97,128,9,
34,231,117,2,76,72,76,81,234,225,242,225,244,105,128,10,
162,242,237,245,235,232,105,128,10,34,239,116,2,76,97,76,
106,225,227,227,229,238,116,128,30,11,226,229,236,239,119,128,
30,13,101,8,76,132,76,185,76,192,76,217,76,227,76,238,
77,27,77,63,99,2,76,138,76,175,233,237,225,236,243,229,
240,225,242,225,244,239,114,2,76,156,76,165,225,242,225,226,
233,99,128,6,107,240,229,242,243,233,225,110,128,6,107,249,
242,233,236,236,233,99,128,4,52,231,242,229,101,128,0,176,
232,105,2,76,199,76,208,232,229,226,242,229,119,128,5,173,
242,225,231,225,238,97,128,48,103,233,227,239,240,244,233,99,
128,3,239,235,225,244,225,235,225,238,97,128,48,199,108,2,
76,244,77,11,229,244,101,2,76,252,77,3,236,229,230,116,
128,35,43,242,233,231,232,116,128,35,38,244,97,129,3,180,
77,18,244,245,242,238,229,100,128,1,141,238,239,237,233,238,
225,244,239,242,237,233,238,245,243,239,238,229,238,245,237,229,
242,225,244,239,242,226,229,238,231,225,236,105,128,9,248,250,
104,128,2,164,104,2,77,74,77,124,97,3,77,82,77,92,
77,99,226,229,238,231,225,236,105,128,9,167,228,229,246,97,
128,9,39,231,117,2,77,106,77,115,234,225,242,225,244,105,
128,10,167,242,237,245,235,232,105,128,10,39,239,239,107,128,
2,87,105,6,77,144,77,193,77,253,78,8,78,19,78,29,
97,2,77,150,77,172,236,249,244,233,235,225,244,239,238,239,
115,129,3,133,77,166,227,237,98,128,3,68,237,239,238,100,
129,38,102,77,181,243,245,233,244,247,232,233,244,101,128,38,
98,229,242,229,243,233,115,133,0,168,77,212,77,220,77,231,
77,237,77,245,225,227,245,244,101,128,246,215,226,229,236,239,
247,227,237,98,128,3,36,227,237,98,128,3,8,231,242,225,
246,101,128,246,216,244,239,238,239,115,128,3,133,232,233,242,
225,231,225,238,97,128,48,98,235,225,244,225,235,225,238,97,
128,48,194,244,244,239,237,225,242,107,128,48,3,246,105,2,
78,36,78,47,228,101,129,0,247,78,43,115,128,34,35,243,
233,239,238,243,236,225,243,104,128,34,21,234,229,227,249,242,
233,236,236,233,99,128,4,82,235,243,232,225,228,101,128,37,
147,108,2,78,87,78,98,233,238,229,226,229,236,239,119,128,
30,15,243,241,245,225,242,101,128,51,151,109,2,78,113,78,
121,225,227,242,239,110,128,1,17,239,238,239,243,240,225,227,
101,128,255,68,238,226,236,239,227,107,128,37,132,111,10,78,
163,78,175,78,185,78,196,78,207,79,23,79,28,79,39,79,
154,79,180,227,232,225,228,225,244,232,225,105,128,14,14,228,
229,235,244,232,225,105,128,14,20,232,233,242,225,231,225,238,
97,128,48,105,235,225,244,225,235,225,238,97,128,48,201,236,
236,225,114,132,0,36,78,222,78,233,78,245,79,0,233,238,
230,229,242,233,239,114,128,246,227,237,239,238,239,243,240,225,
227,101,128,255,4,239,236,228,243,244,249,236,101,128,247,36,
115,2,79,6,79,13,237,225,236,108,128,254,105,245,240,229,
242,233,239,114,128,246,228,238,103,128,32,171,242,245,243,241,
245,225,242,101,128,51,38,116,6,79,53,79,70,79,92,79,
103,79,135,79,142,225,227,227,229,238,116,129,2,217,79,64,
227,237,98,128,3,7,226,229,236,239,247,99,2,79,81,79,
86,237,98,128,3,35,239,237,98,128,3,35,235,225,244,225,
235,225,238,97,128,48,251,236,229,243,115,2,79,112,79,116,
105,128,1,49,106,129,246,190,79,122,243,244,242,239,235,229,
232,239,239,107,128,2,132,237,225,244,104,128,34,197,244,229,
228,227,233,242,227,236,101,128,37,204,245,226,236,229,249,239,
228,240,225,244,225,104,129,251,31,79,171,232,229,226,242,229,
119,128,251,31,247,238,244,225,227,107,2,79,191,79,202,226,
229,236,239,247,227,237,98,128,3,30,237,239,100,128,2,213,
240,225,242,229,110,128,36,159,243,245,240,229,242,233,239,114,
128,246,235,116,2,79,233,79,239,225,233,108,128,2,86,239,
240,226,225,114,128,1,140,117,2,79,253,80,8,232,233,242,
225,231,225,238,97,128,48,101,235,225,244,225,235,225,238,97,
128,48,197,122,132,1,243,80,31,80,40,80,59,80,96,225,
236,244,239,238,101,128,2,163,99,2,80,46,80,53,225,242,
239,110,128,1,198,245,242,108,128,2,165,101,2,80,65,80,
85,225,226,235,232,225,243,233,225,238,227,249,242,233,236,236,
233,99,128,4,225,227,249,242,233,236,236,233,99,128,4,85,
232,229,227,249,242,233,236,236,233,99,128,4,95,101,151,0,
101,80,159,80,178,80,212,81,186,81,248,82,25,82,37,82,
60,82,113,83,225,84,27,84,129,84,245,85,124,85,199,85,
230,86,36,86,89,87,24,87,157,87,177,87,221,88,56,97,
2,80,165,80,172,227,245,244,101,128,0,233,242,244,104,128,
38,65,98,3,80,186,80,195,80,205,229,238,231,225,236,105,
128,9,143,239,240,239,237,239,230,111,128,49,28,242,229,246,
101,128,1,21,99,5,80,224,81,41,81,55,81,87,81,176,
97,2,80,230,81,35,238,228,242,97,3,80,241,80,248,81,
3,228,229,246,97,128,9,13,231,245,234,225,242,225,244,105,
128,10,141,246,239,247,229,236,243,233,231,110,2,81,17,81,
24,228,229,246,97,128,9,69,231,245,234,225,242,225,244,105,
128,10,197,242,239,110,128,1,27,229,228,233,236,236,225,226,
242,229,246,101,128,30,29,104,2,81,61,81,72,225,242,237,
229,238,233,225,110,128,5,101,249,233,247,238,225,242,237,229,
238,233,225,110,128,5,135,233,242,99,2,81,95,81,100,236,
101,128,36,212,245,237,230,236,229,120,134,0,234,81,121,81,
129,81,137,81,148,81,156,81,168,225,227,245,244,101,128,30,
191,226,229,236,239,119,128,30,25,228,239,244,226,229,236,239,
119,128,30,199,231,242,225,246,101,128,30,193,232,239,239,235,
225,226,239,246,101,128,30,195,244,233,236,228,101,128,30,197,
249,242,233,236,236,233,99,128,4,84,100,4,81,196,81,206,
81,212,81,222,226,236,231,242,225,246,101,128,2,5,229,246,
97,128,9,15,233,229,242,229,243,233,115,128,0,235,239,116,
130,1,23,81,231,81,240,225,227,227,229,238,116,128,1,23,
226,229,236,239,119,128,30,185,101,2,81,254,82,9,231,245,
242,237,245,235,232,105,128,10,15,237,225,244,242,225,231,245,
242,237,245,235,232,105,128,10,71,230,227,249,242,233,236,236,
233,99,128,4,68,103,2,82,43,82,50,242,225,246,101,128,
0,232,245,234,225,242,225,244,105,128,10,143,104,4,82,70,
82,81,82,92,82,102,225,242,237,229,238,233,225,110,128,5,
103,226,239,240,239,237,239,230,111,128,49,29,233,242,225,231,
225,238,97,128,48,72,239,239,235,225,226,239,246,101,128,30,
187,105,4,82,123,82,134,83,192,83,207,226,239,240,239,237,
239,230,111,128,49,31,231,232,116,142,0,56,82,168,82,177,
82,187,82,217,82,224,83,6,83,31,83,76,83,110,83,122,
83,133,83,166,83,174,83,185,225,242,225,226,233,99,128,6,
104,226,229,238,231,225,236,105,128,9,238,227,233,242,227,236,
101,129,36,103,82,198,233,238,246,229,242,243,229,243,225,238,
243,243,229,242,233,102,128,39,145,228,229,246,97,128,9,110,
229,229,110,2,82,232,82,241,227,233,242,227,236,101,128,36,
113,112,2,82,247,82,254,225,242,229,110,128,36,133,229,242,
233,239,100,128,36,153,231,117,2,83,13,83,22,234,225,242,
225,244,105,128,10,238,242,237,245,235,232,105,128,10,110,104,
2,83,37,83,63,97,2,83,43,83,54,227,235,225,242,225,
226,233,99,128,6,104,238,231,250,232,239,117,128,48,40,238,
239,244,229,226,229,225,237,229,100,128,38,107,105,2,83,82,
83,100,228,229,239,231,242,225,240,232,233,227,240,225,242,229,
110,128,50,39,238,230,229,242,233,239,114,128,32,136,237,239,
238,239,243,240,225,227,101,128,255,24,239,236,228,243,244,249,
236,101,128,247,56,112,2,83,139,83,146,225,242,229,110,128,
36,123,229,114,2,83,153,83,159,233,239,100,128,36,143,243,
233,225,110,128,6,248,242,239,237,225,110,128,33,119,243,245,
240,229,242,233,239,114,128,32,120,244,232,225,105,128,14,88,
238,246,229,242,244,229,228,226,242,229,246,101,128,2,7,239,
244,233,230,233,229,228,227,249,242,233,236,236,233,99,128,4,
101,107,2,83,231,83,255,225,244,225,235,225,238,97,129,48,
168,83,243,232,225,236,230,247,233,228,244,104,128,255,116,111,
2,84,5,84,20,238,235,225,242,231,245,242,237,245,235,232,
105,128,10,116,242,229,225,110,128,49,84,108,3,84,35,84,
46,84,107,227,249,242,233,236,236,233,99,128,4,59,101,2,
84,52,84,59,237,229,238,116,128,34,8,246,229,110,3,84,
69,84,78,84,99,227,233,242,227,236,101,128,36,106,112,2,
84,84,84,91,225,242,229,110,128,36,126,229,242,233,239,100,
128,36,146,242,239,237,225,110,128,33,122,236,233,240,243,233,
115,129,32,38,84,118,246,229,242,244,233,227,225,108,128,34,
238,109,5,84,141,84,169,84,180,84,200,84,211,225,227,242,
239,110,130,1,19,84,153,84,161,225,227,245,244,101,128,30,
23,231,242,225,246,101,128,30,21,227,249,242,233,236,236,233,
99,128,4,60,228,225,243,104,129,32,20,84,189,246,229,242,
244,233,227,225,108,128,254,49,239,238,239,243,240,225,227,101,
128,255,69,112,2,84,217,84,237,232,225,243,233,243,237,225,
242,235,225,242,237,229,238,233,225,110,128,5,91,244,249,243,
229,116,128,34,5,110,6,85,3,85,14,85,25,85,69,85,
101,85,116,226,239,240,239,237,239,230,111,128,49,35,227,249,
242,233,236,236,233,99,128,4,61,100,2,85,31,85,50,225,
243,104,129,32,19,85,39,246,229,242,244,233,227,225,108,128,
254,50,229,243,227,229,238,228,229,242,227,249,242,233,236,236,
233,99,128,4,163,103,130,1,75,85,77,85,88,226,239,240,
239,237,239,230,111,128,49,37,232,229,227,249,242,233,236,236,
233,99,128,4,165,232,239,239,235,227,249,242,233,236,236,233,
99,128,4,200,243,240,225,227,101,128,32,2,111,3,85,132,
85,140,85,149,231,239,238,229,107,128,1,25,235,239,242,229,
225,110,128,49,83,240,229,110,130,2,91,85,159,85,168,227,
236,239,243,229,100,128,2,154,242,229,246,229,242,243,229,100,
130,2,92,85,183,85,192,227,236,239,243,229,100,128,2,94,
232,239,239,107,128,2,93,112,2,85,205,85,212,225,242,229,
110,128,36,160,243,233,236,239,110,129,3,181,85,222,244,239,
238,239,115,128,3,173,241,117,2,85,237,86,25,225,108,130,
0,61,85,246,86,2,237,239,238,239,243,240,225,227,101,128,
255,29,115,2,86,8,86,15,237,225,236,108,128,254,102,245,
240,229,242,233,239,114,128,32,124,233,246,225,236,229,238,227,
101,128,34,97,114,3,86,44,86,55,86,66,226,239,240,239,
237,239,230,111,128,49,38,227,249,242,233,236,236,233,99,128,
4,64,229,246,229,242,243,229,100,129,2,88,86,78,227,249,
242,233,236,236,233,99,128,4,77,115,6,86,103,86,114,86,
134,86,215,87,4,87,14,227,249,242,233,236,236,233,99,128,
4,65,228,229,243,227,229,238,228,229,242,227,249,242,233,236,
236,233,99,128,4,171,104,132,2,131,86,146,86,153,86,184,
86,199,227,245,242,108,128,2,134,239,242,116,2,86,161,86,
168,228,229,246,97,128,9,14,246,239,247,229,236,243,233,231,
238,228,229,246,97,128,9,70,242,229,246,229,242,243,229,228,
236,239,239,112,128,1,170,243,241,245,225,244,242,229,246,229,
242,243,229,100,128,2,133,237,225,236,108,2,86,224,86,235,
232,233,242,225,231,225,238,97,128,48,71,235,225,244,225,235,
225,238,97,129,48,167,86,248,232,225,236,230,247,233,228,244,
104,128,255,106,244,233,237,225,244,229,100,128,33,46,245,240,
229,242,233,239,114,128,246,236,116,5,87,36,87,62,87,66,
87,83,87,149,97,130,3,183,87,44,87,54,242,237,229,238,
233,225,110,128,5,104,244,239,238,239,115,128,3,174,104,128,
0,240,233,236,228,101,129,30,189,87,75,226,229,236,239,119,
128,30,27,238,225,232,244,97,3,87,95,87,127,87,136,230,
239,245,235,104,2,87,105,87,114,232,229,226,242,229,119,128,
5,145,236,229,230,244,232,229,226,242,229,119,128,5,145,232,
229,226,242,229,119,128,5,145,236,229,230,244,232,229,226,242,
229,119,128,5,145,245,242,238,229,100,128,1,221,117,2,87,
163,87,172,235,239,242,229,225,110,128,49,97,242,111,128,32,
172,246,239,247,229,236,243,233,231,110,3,87,193,87,203,87,
210,226,229,238,231,225,236,105,128,9,199,228,229,246,97,128,
9,71,231,245,234,225,242,225,244,105,128,10,199,120,2,87,
227,88,44,227,236,225,109,132,0,33,87,242,87,253,88,24,
88,36,225,242,237,229,238,233,225,110,128,5,92,100,2,88,
3,88,8,226,108,128,32,60,239,247,110,129,0,161,88,16,
243,237,225,236,108,128,247,161,237,239,238,239,243,240,225,227,
101,128,255,1,243,237,225,236,108,128,247,33,233,243,244,229,
238,244,233,225,108,128,34,3,250,104,131,2,146,88,67,88,
86,88,97,99,2,88,73,88,80,225,242,239,110,128,1,239,
245,242,108,128,2,147,242,229,246,229,242,243,229,100,128,1,
185,244,225,233,108,128,1,186,102,140,0,102,88,132,88,214,
88,225,88,234,88,246,89,93,89,109,91,117,91,130,91,156,
93,33,93,41,97,4,88,142,88,149,88,160,88,171,228,229,
246,97,128,9,94,231,245,242,237,245,235,232,105,128,10,94,
232,242,229,238,232,229,233,116,128,33,9,244,232,97,3,88,
181,88,190,88,202,225,242,225,226,233,99,128,6,78,236,239,
247,225,242,225,226,233,99,128,6,78,244,225,238,225,242,225,
226,233,99,128,6,75,226,239,240,239,237,239,230,111,128,49,
8,227,233,242,227,236,101,128,36,213,228,239,244,225,227,227,
229,238,116,128,30,31,101,3,88,254,89,76,89,86,104,4,
89,8,89,31,89,45,89,61,225,114,2,89,15,89,22,225,
226,233,99,128,6,65,237,229,238,233,225,110,128,5,134,230,
233,238,225,236,225,242,225,226,233,99,128,254,210,233,238,233,
244,233,225,236,225,242,225,226,233,99,128,254,211,237,229,228,
233,225,236,225,242,225,226,233,99,128,254,212,233,227,239,240,
244,233,99,128,3,229,237,225,236,101,128,38,64,102,130,251,
0,89,101,89,105,105,128,251,3,108,128,251,4,105,136,251,
1,89,129,89,169,89,180,89,202,90,68,90,85,90,93,90,
106,230,244,229,229,110,2,89,139,89,148,227,233,242,227,236,
101,128,36,110,112,2,89,154,89,161,225,242,229,110,128,36,
130,229,242,233,239,100,128,36,150,231,245,242,229,228,225,243,
104,128,32,18,236,236,229,100,2,89,189,89,195,226,239,120,
128,37,160,242,229,227,116,128,37,172,238,225,108,5,89,216,
89,255,90,16,90,33,90,49,235,225,102,130,5,218,89,226,
89,246,228,225,231,229,243,104,129,251,58,89,237,232,229,226,
242,229,119,128,251,58,232,229,226,242,229,119,128,5,218,237,
229,109,129,5,221,90,7,232,229,226,242,229,119,128,5,221,
238,245,110,129,5,223,90,24,232,229,226,242,229,119,128,5,
223,240,101,129,5,227,90,40,232,229,226,242,229,119,128,5,
227,244,243,225,228,105,129,5,229,90,59,232,229,226,242,229,
119,128,5,229,242,243,244,244,239,238,229,227,232,233,238,229,
243,101,128,2,201,243,232,229,249,101,128,37,201,244,225,227,
249,242,233,236,236,233,99,128,4,115,246,101,142,0,53,90,
139,90,148,90,158,90,188,90,195,90,205,90,230,91,1,91,
35,91,47,91,58,91,91,91,99,91,110,225,242,225,226,233,
99,128,6,101,226,229,238,231,225,236,105,128,9,235,227,233,
242,227,236,101,129,36,100,90,169,233,238,246,229,242,243,229,
243,225,238,243,243,229,242,233,102,128,39,142,228,229,246,97,
128,9,107,229,233,231,232,244,232,115,128,33,93,231,117,2,
90,212,90,221,234,225,242,225,244,105,128,10,235,242,237,245,
235,232,105,128,10,107,232,97,2,90,237,90,248,227,235,225,
242,225,226,233,99,128,6,101,238,231,250,232,239,117,128,48,
37,105,2,91,7,91,25,228,229,239,231,242,225,240,232,233,
227,240,225,242,229,110,128,50,36,238,230,229,242,233,239,114,
128,32,133,237,239,238,239,243,240,225,227,101,128,255,21,239,
236,228,243,244,249,236,101,128,247,53,112,2,91,64,91,71,
225,242,229,110,128,36,120,229,114,2,91,78,91,84,233,239,
100,128,36,140,243,233,225,110,128,6,245,242,239,237,225,110,
128,33,116,243,245,240,229,242,233,239,114,128,32,117,244,232,
225,105,128,14,85,108,129,251,2,91,123,239,242,233,110,128,
1,146,109,2,91,136,91,147,239,238,239,243,240,225,227,101,
128,255,70,243,241,245,225,242,101,128,51,153,111,4,91,166,
91,188,91,200,91,207,230,97,2,91,173,91,181,238,244,232,
225,105,128,14,31,244,232,225,105,128,14,29,238,231,237,225,
238,244,232,225,105,128,14,79,242,225,236,108,128,34,0,245,
114,142,0,52,91,240,91,249,92,3,92,33,92,40,92,65,
92,92,92,126,92,138,92,157,92,168,92,201,92,209,92,220,
225,242,225,226,233,99,128,6,100,226,229,238,231,225,236,105,
128,9,234,227,233,242,227,236,101,129,36,99,92,14,233,238,
246,229,242,243,229,243,225,238,243,243,229,242,233,102,128,39,
141,228,229,246,97,128,9,106,231,117,2,92,47,92,56,234,
225,242,225,244,105,128,10,234,242,237,245,235,232,105,128,10,
106,232,97,2,92,72,92,83,227,235,225,242,225,226,233,99,
128,6,100,238,231,250,232,239,117,128,48,36,105,2,92,98,
92,116,228,229,239,231,242,225,240,232,233,227,240,225,242,229,
110,128,50,35,238,230,229,242,233,239,114,128,32,132,237,239,
238,239,243,240,225,227,101,128,255,20,238,245,237,229,242,225,
244,239,242,226,229,238,231,225,236,105,128,9,247,239,236,228,
243,244,249,236,101,128,247,52,112,2,92,174,92,181,225,242,
229,110,128,36,119,229,114,2,92,188,92,194,233,239,100,128,
36,139,243,233,225,110,128,6,244,242,239,237,225,110,128,33,
115,243,245,240,229,242,233,239,114,128,32,116,116,2,92,226,
93,8,229,229,110,2,92,234,92,243,227,233,242,227,236,101,
128,36,109,112,2,92,249,93,0,225,242,229,110,128,36,129,
229,242,233,239,100,128,36,149,104,2,93,14,93,19,225,105,
128,14,84,244,239,238,229,227,232,233,238,229,243,101,128,2,
203,240,225,242,229,110,128,36,161,242,97,2,93,48,93,56,
227,244,233,239,110,128,32,68,238,99,128,32,163,103,144,0,
103,93,97,94,43,94,66,94,127,94,144,95,65,96,58,96,
143,96,156,97,14,97,39,97,67,97,89,98,34,98,56,98,
158,97,9,93,117,93,127,93,134,93,141,93,205,93,230,93,
241,93,252,94,30,226,229,238,231,225,236,105,128,9,151,227,
245,244,101,128,1,245,228,229,246,97,128,9,23,102,4,93,
151,93,160,93,174,93,190,225,242,225,226,233,99,128,6,175,
230,233,238,225,236,225,242,225,226,233,99,128,251,147,233,238,
233,244,233,225,236,225,242,225,226,233,99,128,251,148,237,229,
228,233,225,236,225,242,225,226,233,99,128,251,149,231,117,2,
93,212,93,221,234,225,242,225,244,105,128,10,151,242,237,245,
235,232,105,128,10,23,232,233,242,225,231,225,238,97,128,48,
76,235,225,244,225,235,225,238,97,128,48,172,237,237,97,130,
3,179,94,6,94,19,236,225,244,233,238,243,237,225,236,108,
128,2,99,243,245,240,229,242,233,239,114,128,2,224,238,231,
233,225,227,239,240,244,233,99,128,3,235,98,2,94,49,94,
59,239,240,239,237,239,230,111,128,49,13,242,229,246,101,128,
1,31,99,4,94,76,94,83,94,92,94,114,225,242,239,110,
128,1,231,229,228,233,236,236,97,128,1,35,233,242,99,2,
94,100,94,105,236,101,128,36,214,245,237,230,236,229,120,128,
1,29,239,237,237,225,225,227,227,229,238,116,128,1,35,228,
239,116,129,1,33,94,135,225,227,227,229,238,116,128,1,33,
101,6,94,158,94,169,94,180,94,191,94,210,95,56,227,249,
242,233,236,236,233,99,128,4,51,232,233,242,225,231,225,238,
97,128,48,82,235,225,244,225,235,225,238,97,128,48,178,239,
237,229,244,242,233,227,225,236,236,249,229,241,245,225,108,128,
34,81,114,3,94,218,95,11,95,21,229,243,104,3,94,228,
94,243,94,252,225,227,227,229,238,244,232,229,226,242,229,119,
128,5,156,232,229,226,242,229,119,128,5,243,237,245,241,228,
225,237,232,229,226,242,229,119,128,5,157,237,225,238,228,226,
236,115,128,0,223,243,232,225,249,233,109,2,95,32,95,47,
225,227,227,229,238,244,232,229,226,242,229,119,128,5,158,232,
229,226,242,229,119,128,5,244,244,225,237,225,242,107,128,48,
19,104,5,95,77,95,210,96,17,96,42,96,48,97,4,95,
87,95,97,95,120,95,145,226,229,238,231,225,236,105,128,9,
152,100,2,95,103,95,114,225,242,237,229,238,233,225,110,128,
5,114,229,246,97,128,9,24,231,117,2,95,127,95,136,234,
225,242,225,244,105,128,10,152,242,237,245,235,232,105,128,10,
24,233,110,4,95,156,95,165,95,179,95,195,225,242,225,226,
233,99,128,6,58,230,233,238,225,236,225,242,225,226,233,99,
128,254,206,233,238,233,244,233,225,236,225,242,225,226,233,99,
128,254,207,237,229,228,233,225,236,225,242,225,226,233,99,128,
254,208,101,3,95,218,95,239,96,0,237,233,228,228,236,229,
232,239,239,235,227,249,242,233,236,236,233,99,128,4,149,243,
244,242,239,235,229,227,249,242,233,236,236,233,99,128,4,147,
245,240,244,245,242,238,227,249,242,233,236,236,233,99,128,4,
145,232,97,2,96,24,96,31,228,229,246,97,128,9,90,231,
245,242,237,245,235,232,105,128,10,90,239,239,107,128,2,96,
250,243,241,245,225,242,101,128,51,147,105,3,96,66,96,77,
96,88,232,233,242,225,231,225,238,97,128,48,78,235,225,244,
225,235,225,238,97,128,48,174,109,2,96,94,96,105,225,242,
237,229,238,233,225,110,128,5,99,229,108,130,5,210,96,114,
96,134,228,225,231,229,243,104,129,251,50,96,125,232,229,226,
242,229,119,128,251,50,232,229,226,242,229,119,128,5,210,234,
229,227,249,242,233,236,236,233,99,128,4,83,236,239,244,244,
225,108,2,96,167,96,184,233,238,246,229,242,244,229,228,243,
244,242,239,235,101,128,1,190,243,244,239,112,132,2,148,96,
199,96,210,96,216,96,248,233,238,246,229,242,244,229,100,128,
2,150,237,239,100,128,2,192,242,229,246,229,242,243,229,100,
130,2,149,96,231,96,237,237,239,100,128,2,193,243,245,240,
229,242,233,239,114,128,2,228,243,244,242,239,235,101,129,2,
161,97,3,242,229,246,229,242,243,229,100,128,2,162,109,2,
97,20,97,28,225,227,242,239,110,128,30,33,239,238,239,243,
240,225,227,101,128,255,71,111,2,97,45,97,56,232,233,242,
225,231,225,238,97,128,48,84,235,225,244,225,235,225,238,97,
128,48,180,240,97,2,97,74,97,80,242,229,110,128,36,162,
243,241,245,225,242,101,128,51,172,114,2,97,95,97,192,97,
2,97,101,97,109,228,233,229,238,116,128,34,7,246,101,134,
0,96,97,126,97,137,97,154,97,161,97,170,97,182,226,229,
236,239,247,227,237,98,128,3,22,99,2,97,143,97,148,237,
98,128,3,0,239,237,98,128,3,0,228,229,246,97,128,9,
83,236,239,247,237,239,100,128,2,206,237,239,238,239,243,240,
225,227,101,128,255,64,244,239,238,229,227,237,98,128,3,64,
229,225,244,229,114,132,0,62,97,208,97,227,97,239,98,26,
229,241,245,225,108,129,34,101,97,218,239,242,236,229,243,115,
128,34,219,237,239,238,239,243,240,225,227,101,128,255,30,111,
2,97,245,98,15,114,2,97,251,98,8,229,241,245,233,246,
225,236,229,238,116,128,34,115,236,229,243,115,128,34,119,246,
229,242,229,241,245,225,108,128,34,103,243,237,225,236,108,128,
254,101,115,2,98,40,98,48,227,242,233,240,116,128,2,97,
244,242,239,235,101,128,1,229,117,4,98,66,98,77,98,134,
98,145,232,233,242,225,231,225,238,97,128,48,80,233,108,2,
98,84,98,109,236,229,237,239,116,2,98,94,98,101,236,229,
230,116,128,0,171,242,233,231,232,116,128,0,187,243,233,238,
231,108,2,98,119,98,126,236,229,230,116,128,32,57,242,233,
231,232,116,128,32,58,235,225,244,225,235,225,238,97,128,48,
176,242,225,237,245,243,241,245,225,242,101,128,51,24,249,243,
241,245,225,242,101,128,51,201,104,144,0,104,98,204,101,90,
101,125,101,162,101,202,103,90,103,110,104,75,104,87,104,99,
105,167,105,175,105,186,105,195,106,19,106,23,97,13,98,232,
99,15,99,25,99,55,99,80,99,158,99,170,99,195,99,210,
99,239,99,252,100,54,100,63,97,2,98,238,99,1,226,235,
232,225,243,233,225,238,227,249,242,233,236,236,233,99,128,4,
169,236,244,239,238,229,225,242,225,226,233,99,128,6,193,226,
229,238,231,225,236,105,128,9,185,228,101,2,99,32,99,50,
243,227,229,238,228,229,242,227,249,242,233,236,236,233,99,128,
4,179,246,97,128,9,57,231,117,2,99,62,99,71,234,225,
242,225,244,105,128,10,185,242,237,245,235,232,105,128,10,57,
104,4,99,90,99,99,99,113,99,143,225,242,225,226,233,99,
128,6,45,230,233,238,225,236,225,242,225,226,233,99,128,254,
162,105,2,99,119,99,134,238,233,244,233,225,236,225,242,225,
226,233,99,128,254,163,242,225,231,225,238,97,128,48,111,237,
229,228,233,225,236,225,242,225,226,233,99,128,254,164,233,244,
245,243,241,245,225,242,101,128,51,42,235,225,244,225,235,225,
238,97,129,48,207,99,183,232,225,236,230,247,233,228,244,104,
128,255,138,236,225,238,244,231,245,242,237,245,235,232,105,128,
10,77,237,250,97,2,99,218,99,227,225,242,225,226,233,99,
128,6,33,236,239,247,225,242,225,226,233,99,128,6,33,238,
231,245,236,230,233,236,236,229,114,128,49,100,114,2,100,2,
100,18,228,243,233,231,238,227,249,242,233,236,236,233,99,128,
4,74,240,239,239,110,2,100,27,100,40,236,229,230,244,226,
225,242,226,245,112,128,33,188,242,233,231,232,244,226,225,242,
226,245,112,128,33,192,243,241,245,225,242,101,128,51,202,244,
225,102,3,100,73,100,165,101,0,240,225,244,225,104,134,5,
178,100,93,100,98,100,112,100,121,100,136,100,152,177,54,128,
5,178,50,2,100,104,100,108,51,128,5,178,102,128,5,178,
232,229,226,242,229,119,128,5,178,238,225,242,242,239,247,232,
229,226,242,229,119,128,5,178,241,245,225,242,244,229,242,232,
229,226,242,229,119,128,5,178,247,233,228,229,232,229,226,242,
229,119,128,5,178,241,225,237,225,244,115,135,5,179,100,188,
100,193,100,198,100,203,100,212,100,227,100,243,177,98,128,5,
179,178,56,128,5,179,179,52,128,5,179,232,229,226,242,229,
119,128,5,179,238,225,242,242,239,247,232,229,226,242,229,119,
128,5,179,241,245,225,242,244,229,242,232,229,226,242,229,119,
128,5,179,247,233,228,229,232,229,226,242,229,119,128,5,179,
243,229,231,239,108,135,5,177,101,22,101,27,101,32,101,37,
101,46,101,61,101,77,177,55,128,5,177,178,52,128,5,177,
179,48,128,5,177,232,229,226,242,229,119,128,5,177,238,225,
242,242,239,247,232,229,226,242,229,119,128,5,177,241,245,225,
242,244,229,242,232,229,226,242,229,119,128,5,177,247,233,228,
229,232,229,226,242,229,119,128,5,177,98,3,101,98,101,103,
101,113,225,114,128,1,39,239,240,239,237,239,230,111,128,49,
15,242,229,246,229,226,229,236,239,119,128,30,43,99,2,101,
131,101,140,229,228,233,236,236,97,128,30,41,233,242,99,2,
101,148,101,153,236,101,128,36,215,245,237,230,236,229,120,128,
1,37,100,2,101,168,101,178,233,229,242,229,243,233,115,128,
30,39,239,116,2,101,185,101,194,225,227,227,229,238,116,128,
30,35,226,229,236,239,119,128,30,37,101,136,5,212,101,222,
101,255,102,19,102,248,103,8,103,53,103,62,103,75,225,242,
116,129,38,101,101,230,243,245,233,116,2,101,239,101,247,226,
236,225,227,107,128,38,101,247,232,233,244,101,128,38,97,228,
225,231,229,243,104,129,251,52,102,10,232,229,226,242,229,119,
128,251,52,104,6,102,33,102,61,102,69,102,119,102,165,102,
214,97,2,102,39,102,53,236,244,239,238,229,225,242,225,226,
233,99,128,6,193,242,225,226,233,99,128,6,71,229,226,242,
229,119,128,5,212,230,233,238,225,236,97,2,102,80,102,111,
236,116,2,102,87,102,99,239,238,229,225,242,225,226,233,99,
128,251,167,244,247,239,225,242,225,226,233,99,128,254,234,242,
225,226,233,99,128,254,234,232,225,237,250,225,225,226,239,246,
101,2,102,134,102,148,230,233,238,225,236,225,242,225,226,233,
99,128,251,165,233,243,239,236,225,244,229,228,225,242,225,226,
233,99,128,251,164,105,2,102,171,102,205,238,233,244,233,225,
236,97,2,102,183,102,197,236,244,239,238,229,225,242,225,226,
233,99,128,251,168,242,225,226,233,99,128,254,235,242,225,231,
225,238,97,128,48,120,237,229,228,233,225,236,97,2,102,226,
102,240,236,244,239,238,229,225,242,225,226,233,99,128,251,169,
242,225,226,233,99,128,254,236,233,243,229,233,229,242,225,243,
241,245,225,242,101,128,51,123,107,2,103,14,103,38,225,244,
225,235,225,238,97,129,48,216,103,26,232,225,236,230,247,233,
228,244,104,128,255,141,245,244,225,225,242,245,243,241,245,225,
242,101,128,51,54,238,231,232,239,239,107,128,2,103,242,245,
244,245,243,241,245,225,242,101,128,51,57,116,129,5,215,103,
81,232,229,226,242,229,119,128,5,215,232,239,239,107,129,2,
102,103,99,243,245,240,229,242,233,239,114,128,2,177,105,4,
103,120,103,205,103,216,103,241,229,245,104,4,103,132,103,167,
103,182,103,191,97,2,103,138,103,153,227,233,242,227,236,229,
235,239,242,229,225,110,128,50,123,240,225,242,229,238,235,239,
242,229,225,110,128,50,27,227,233,242,227,236,229,235,239,242,
229,225,110,128,50,109,235,239,242,229,225,110,128,49,78,240,
225,242,229,238,235,239,242,229,225,110,128,50,13,232,233,242,
225,231,225,238,97,128,48,114,235,225,244,225,235,225,238,97,
129,48,210,103,229,232,225,236,230,247,233,228,244,104,128,255,
139,242,233,113,134,5,180,104,3,104,8,104,22,104,31,104,
46,104,62,177,52,128,5,180,50,2,104,14,104,18,49,128,
5,180,100,128,5,180,232,229,226,242,229,119,128,5,180,238,
225,242,242,239,247,232,229,226,242,229,119,128,5,180,241,245,
225,242,244,229,242,232,229,226,242,229,119,128,5,180,247,233,
228,229,232,229,226,242,229,119,128,5,180,236,233,238,229,226,
229,236,239,119,128,30,150,237,239,238,239,243,240,225,227,101,
128,255,72,111,9,104,119,104,130,104,154,104,179,105,11,105,
24,105,110,105,150,105,161,225,242,237,229,238,233,225,110,128,
5,112,232,105,2,104,137,104,145,240,244,232,225,105,128,14,
43,242,225,231,225,238,97,128,48,123,235,225,244,225,235,225,
238,97,129,48,219,104,167,232,225,236,230,247,233,228,244,104,
128,255,142,236,225,109,135,5,185,104,199,104,204,104,209,104,
214,104,223,104,238,104,254,177,57,128,5,185,178,54,128,5,
185,179,50,128,5,185,232,229,226,242,229,119,128,5,185,238,
225,242,242,239,247,232,229,226,242,229,119,128,5,185,241,245,
225,242,244,229,242,232,229,226,242,229,119,128,5,185,247,233,
228,229,232,229,226,242,229,119,128,5,185,238,239,235,232,245,
235,244,232,225,105,128,14,46,111,2,105,30,105,100,107,4,
105,40,105,52,105,58,105,80,225,226,239,246,229,227,239,237,
98,128,3,9,227,237,98,128,3,9,240,225,236,225,244,225,
236,233,250,229,228,226,229,236,239,247,227,237,98,128,3,33,
242,229,244,242,239,230,236,229,248,226,229,236,239,247,227,237,
98,128,3,34,238,243,241,245,225,242,101,128,51,66,114,2,
105,116,105,143,105,2,105,122,105,131,227,239,240,244,233,99,
128,3,233,250,239,238,244,225,236,226,225,114,128,32,21,238,
227,237,98,128,3,27,244,243,240,242,233,238,231,115,128,38,
104,245,243,101,128,35,2,240,225,242,229,110,128,36,163,243,
245,240,229,242,233,239,114,128,2,176,244,245,242,238,229,100,
128,2,101,117,4,105,205,105,216,105,229,105,254,232,233,242,
225,231,225,238,97,128,48,117,233,233,244,239,243,241,245,225,
242,101,128,51,51,235,225,244,225,235,225,238,97,129,48,213,
105,242,232,225,236,230,247,233,228,244,104,128,255,140,238,231,
225,242,245,237,236,225,245,116,129,2,221,106,13,227,237,98,
128,3,11,118,128,1,149,249,240,232,229,110,132,0,45,106,
39,106,50,106,62,106,85,233,238,230,229,242,233,239,114,128,
246,229,237,239,238,239,243,240,225,227,101,128,255,13,115,2,
106,68,106,75,237,225,236,108,128,254,99,245,240,229,242,233,
239,114,128,246,230,244,247,111,128,32,16,105,149,0,105,106,
137,106,160,106,194,106,241,110,123,110,243,111,24,111,51,111,
213,111,217,111,255,112,21,112,105,113,14,113,89,113,97,113,
110,113,197,113,254,114,26,114,70,225,99,2,106,144,106,150,
245,244,101,128,0,237,249,242,233,236,236,233,99,128,4,79,
98,3,106,168,106,177,106,187,229,238,231,225,236,105,128,9,
135,239,240,239,237,239,230,111,128,49,39,242,229,246,101,128,
1,45,99,3,106,202,106,209,106,231,225,242,239,110,128,1,
208,233,242,99,2,106,217,106,222,236,101,128,36,216,245,237,
230,236,229,120,128,0,238,249,242,233,236,236,233,99,128,4,
86,100,4,106,251,107,5,110,80,110,113,226,236,231,242,225,
246,101,128,2,9,101,2,107,11,110,75,239,231,242,225,240,
104,7,107,32,107,46,107,59,109,244,110,19,110,32,110,44,
229,225,242,244,232,227,233,242,227,236,101,128,50,143,230,233,
242,229,227,233,242,227,236,101,128,50,139,233,99,14,107,90,
107,106,107,205,108,3,108,69,108,98,108,114,108,171,108,220,
108,232,109,3,109,70,109,208,109,237,225,236,236,233,225,238,
227,229,240,225,242,229,110,128,50,63,99,4,107,116,107,127,
107,141,107,148,225,236,236,240,225,242,229,110,128,50,58,229,
238,244,242,229,227,233,242,227,236,101,128,50,165,236,239,243,
101,128,48,6,111,3,107,156,107,171,107,191,237,237,97,129,
48,1,107,164,236,229,230,116,128,255,100,238,231,242,225,244,
245,236,225,244,233,239,238,240,225,242,229,110,128,50,55,242,
242,229,227,244,227,233,242,227,236,101,128,50,163,101,3,107,
213,107,225,107,242,225,242,244,232,240,225,242,229,110,128,50,
47,238,244,229,242,240,242,233,243,229,240,225,242,229,110,128,
50,61,248,227,229,236,236,229,238,244,227,233,242,227,236,101,
128,50,157,102,2,108,9,108,24,229,243,244,233,246,225,236,
240,225,242,229,110,128,50,64,105,2,108,30,108,59,238,225,
238,227,233,225,108,2,108,42,108,51,227,233,242,227,236,101,
128,50,150,240,225,242,229,110,128,50,54,242,229,240,225,242,
229,110,128,50,43,104,2,108,75,108,86,225,246,229,240,225,
242,229,110,128,50,50,233,231,232,227,233,242,227,236,101,128,
50,164,233,244,229,242,225,244,233,239,238,237,225,242,107,128,
48,5,108,3,108,122,108,148,108,160,225,226,239,114,2,108,
131,108,140,227,233,242,227,236,101,128,50,152,240,225,242,229,
110,128,50,56,229,230,244,227,233,242,227,236,101,128,50,167,
239,247,227,233,242,227,236,101,128,50,166,109,2,108,177,108,
209,101,2,108,183,108,198,228,233,227,233,238,229,227,233,242,
227,236,101,128,50,169,244,225,236,240,225,242,229,110,128,50,
46,239,239,238,240,225,242,229,110,128,50,42,238,225,237,229,
240,225,242,229,110,128,50,52,112,2,108,238,108,246,229,242,
233,239,100,128,48,2,242,233,238,244,227,233,242,227,236,101,
128,50,158,114,2,109,9,109,57,101,3,109,17,109,28,109,
43,225,227,232,240,225,242,229,110,128,50,67,240,242,229,243,
229,238,244,240,225,242,229,110,128,50,57,243,239,245,242,227,
229,240,225,242,229,110,128,50,62,233,231,232,244,227,233,242,
227,236,101,128,50,168,115,5,109,82,109,111,109,125,109,150,
109,178,101,2,109,88,109,101,227,242,229,244,227,233,242,227,
236,101,128,50,153,236,230,240,225,242,229,110,128,50,66,239,
227,233,229,244,249,240,225,242,229,110,128,50,51,112,2,109,
131,109,137,225,227,101,128,48,0,229,227,233,225,236,240,225,
242,229,110,128,50,53,116,2,109,156,109,167,239,227,235,240,
225,242,229,110,128,50,49,245,228,249,240,225,242,229,110,128,
50,59,117,2,109,184,109,193,238,240,225,242,229,110,128,50,
48,240,229,242,246,233,243,229,240,225,242,229,110,128,50,60,
119,2,109,214,109,226,225,244,229,242,240,225,242,229,110,128,
50,44,239,239,228,240,225,242,229,110,128,50,45,250,229,242,
111,128,48,7,109,2,109,250,110,7,229,244,225,236,227,233,
242,227,236,101,128,50,142,239,239,238,227,233,242,227,236,101,
128,50,138,238,225,237,229,227,233,242,227,236,101,128,50,148,
243,245,238,227,233,242,227,236,101,128,50,144,119,2,110,50,
110,63,225,244,229,242,227,233,242,227,236,101,128,50,140,239,
239,228,227,233,242,227,236,101,128,50,141,246,97,128,9,7,
233,229,242,229,243,233,115,130,0,239,110,94,110,102,225,227,
245,244,101,128,30,47,227,249,242,233,236,236,233,99,128,4,
229,239,244,226,229,236,239,119,128,30,203,101,3,110,131,110,
147,110,158,226,242,229,246,229,227,249,242,233,236,236,233,99,
128,4,215,227,249,242,233,236,236,233,99,128,4,53,245,238,
103,4,110,170,110,205,110,220,110,229,97,2,110,176,110,191,
227,233,242,227,236,229,235,239,242,229,225,110,128,50,117,240,
225,242,229,238,235,239,242,229,225,110,128,50,21,227,233,242,
227,236,229,235,239,242,229,225,110,128,50,103,235,239,242,229,
225,110,128,49,71,240,225,242,229,238,235,239,242,229,225,110,
128,50,7,103,2,110,249,111,0,242,225,246,101,128,0,236,
117,2,111,6,111,15,234,225,242,225,244,105,128,10,135,242,
237,245,235,232,105,128,10,7,104,2,111,30,111,40,233,242,
225,231,225,238,97,128,48,68,239,239,235,225,226,239,246,101,
128,30,201,105,8,111,69,111,79,111,90,111,97,111,122,111,
138,111,153,111,169,226,229,238,231,225,236,105,128,9,136,227,
249,242,233,236,236,233,99,128,4,56,228,229,246,97,128,9,
8,231,117,2,111,104,111,113,234,225,242,225,244,105,128,10,
136,242,237,245,235,232,105,128,10,8,237,225,244,242,225,231,
245,242,237,245,235,232,105,128,10,64,238,246,229,242,244,229,
228,226,242,229,246,101,128,2,11,243,232,239,242,244,227,249,
242,233,236,236,233,99,128,4,57,246,239,247,229,236,243,233,
231,110,3,111,185,111,195,111,202,226,229,238,231,225,236,105,
128,9,192,228,229,246,97,128,9,64,231,245,234,225,242,225,
244,105,128,10,192,106,128,1,51,107,2,111,223,111,247,225,
244,225,235,225,238,97,129,48,164,111,235,232,225,236,230,247,
233,228,244,104,128,255,114,239,242,229,225,110,128,49,99,108,
2,112,5,112,10,228,101,128,2,220,245,249,232,229,226,242,
229,119,128,5,172,109,2,112,27,112,94,97,3,112,35,112,
55,112,80,227,242,239,110,129,1,43,112,44,227,249,242,233,
236,236,233,99,128,4,227,231,229,239,242,225,240,240,242,239,
248,233,237,225,244,229,236,249,229,241,245,225,108,128,34,83,
244,242,225,231,245,242,237,245,235,232,105,128,10,63,239,238,
239,243,240,225,227,101,128,255,73,110,5,112,117,112,127,112,
136,112,148,112,232,227,242,229,237,229,238,116,128,34,6,230,
233,238,233,244,121,128,34,30,233,225,242,237,229,238,233,225,
110,128,5,107,116,2,112,154,112,222,101,2,112,160,112,211,
231,242,225,108,131,34,43,112,173,112,191,112,196,98,2,112,
179,112,187,239,244,244,239,109,128,35,33,116,128,35,33,229,
120,128,248,245,116,2,112,202,112,207,239,112,128,35,32,112,
128,35,32,242,243,229,227,244,233,239,110,128,34,41,233,243,
241,245,225,242,101,128,51,5,118,3,112,240,112,249,113,2,
226,245,236,236,229,116,128,37,216,227,233,242,227,236,101,128,
37,217,243,237,233,236,229,230,225,227,101,128,38,59,111,3,
113,22,113,33,113,41,227,249,242,233,236,236,233,99,128,4,
81,231,239,238,229,107,128,1,47,244,97,131,3,185,113,52,
113,73,113,81,228,233,229,242,229,243,233,115,129,3,202,113,
65,244,239,238,239,115,128,3,144,236,225,244,233,110,128,2,
105,244,239,238,239,115,128,3,175,240,225,242,229,110,128,36,
164,242,233,231,245,242,237,245,235,232,105,128,10,114,115,4,
113,120,113,165,113,179,113,187,237,225,236,108,2,113,129,113,
140,232,233,242,225,231,225,238,97,128,48,67,235,225,244,225,
235,225,238,97,129,48,163,113,153,232,225,236,230,247,233,228,
244,104,128,255,104,243,232,225,242,226,229,238,231,225,236,105,
128,9,250,244,242,239,235,101,128,2,104,245,240,229,242,233,
239,114,128,246,237,116,2,113,203,113,237,229,242,225,244,233,
239,110,2,113,215,113,226,232,233,242,225,231,225,238,97,128,
48,157,235,225,244,225,235,225,238,97,128,48,253,233,236,228,
101,129,1,41,113,246,226,229,236,239,119,128,30,45,117,2,
114,4,114,15,226,239,240,239,237,239,230,111,128,49,41,227,
249,242,233,236,236,233,99,128,4,78,246,239,247,229,236,243,
233,231,110,3,114,42,114,52,114,59,226,229,238,231,225,236,
105,128,9,191,228,229,246,97,128,9,63,231,245,234,225,242,
225,244,105,128,10,191,250,232,233,244,243,97,2,114,81,114,
92,227,249,242,233,236,236,233,99,128,4,117,228,226,236,231,
242,225,246,229,227,249,242,233,236,236,233,99,128,4,119,106,
138,0,106,114,135,114,198,114,209,115,3,115,19,115,132,115,
201,115,206,115,218,115,226,97,4,114,145,114,156,114,166,114,
173,225,242,237,229,238,233,225,110,128,5,113,226,229,238,231,
225,236,105,128,9,156,228,229,246,97,128,9,28,231,117,2,
114,180,114,189,234,225,242,225,244,105,128,10,156,242,237,245,
235,232,105,128,10,28,226,239,240,239,237,239,230,111,128,49,
16,99,3,114,217,114,224,114,246,225,242,239,110,128,1,240,
233,242,99,2,114,232,114,237,236,101,128,36,217,245,237,230,
236,229,120,128,1,53,242,239,243,243,229,228,244,225,233,108,
128,2,157,228,239,244,236,229,243,243,243,244,242,239,235,101,
128,2,95,101,3,115,27,115,38,115,103,227,249,242,233,236,
236,233,99,128,4,88,229,109,4,115,49,115,58,115,72,115,
88,225,242,225,226,233,99,128,6,44,230,233,238,225,236,225,
242,225,226,233,99,128,254,158,233,238,233,244,233,225,236,225,
242,225,226,233,99,128,254,159,237,229,228,233,225,236,225,242,
225,226,233,99,128,254,160,104,2,115,109,115,118,225,242,225,
226,233,99,128,6,152,230,233,238,225,236,225,242,225,226,233,
99,128,251,139,104,2,115,138,115,188,97,3,115,146,115,156,
115,163,226,229,238,231,225,236,105,128,9,157,228,229,246,97,
128,9,29,231,117,2,115,170,115,179,234,225,242,225,244,105,
128,10,157,242,237,245,235,232,105,128,10,29,229,232,225,242,
237,229,238,233,225,110,128,5,123,233,115,128,48,4,237,239,
238,239,243,240,225,227,101,128,255,74,240,225,242,229,110,128,
36,165,243,245,240,229,242,233,239,114,128,2,178,107,146,0,
107,116,21,118,110,118,121,118,183,118,194,119,28,119,42,120,
150,121,90,121,103,121,129,121,178,122,60,122,82,122,95,122,
118,122,160,122,170,97,12,116,47,116,79,116,101,116,131,116,
245,117,14,117,44,117,69,117,175,117,189,118,56,118,85,98,
2,116,53,116,70,225,243,232,235,233,242,227,249,242,233,236,
236,233,99,128,4,161,229,238,231,225,236,105,128,9,149,99,
2,116,85,116,91,245,244,101,128,30,49,249,242,233,236,236,
233,99,128,4,58,228,101,2,116,108,116,126,243,227,229,238,
228,229,242,227,249,242,233,236,236,233,99,128,4,155,246,97,
128,9,21,102,135,5,219,116,149,116,158,116,178,116,192,116,
201,116,217,116,232,225,242,225,226,233,99,128,6,67,228,225,
231,229,243,104,129,251,59,116,169,232,229,226,242,229,119,128,
251,59,230,233,238,225,236,225,242,225,226,233,99,128,254,218,
232,229,226,242,229,119,128,5,219,233,238,233,244,233,225,236,
225,242,225,226,233,99,128,254,219,237,229,228,233,225,236,225,
242,225,226,233,99,128,254,220,242,225,230,229,232,229,226,242,
229,119,128,251,77,231,117,2,116,252,117,5,234,225,242,225,
244,105,128,10,149,242,237,245,235,232,105,128,10,21,104,2,
117,20,117,30,233,242,225,231,225,238,97,128,48,75,239,239,
235,227,249,242,233,236,236,233,99,128,4,196,235,225,244,225,
235,225,238,97,129,48,171,117,57,232,225,236,230,247,233,228,
244,104,128,255,118,112,2,117,75,117,96,240,97,129,3,186,
117,82,243,249,237,226,239,236,231,242,229,229,107,128,3,240,
249,229,239,245,110,3,117,108,117,122,117,156,237,233,229,245,
237,235,239,242,229,225,110,128,49,113,112,2,117,128,117,143,
232,233,229,245,240,232,235,239,242,229,225,110,128,49,132,233,
229,245,240,235,239,242,229,225,110,128,49,120,243,243,225,238,
231,240,233,229,245,240,235,239,242,229,225,110,128,49,121,242,
239,242,233,233,243,241,245,225,242,101,128,51,13,115,5,117,
201,117,245,118,4,118,12,118,40,232,233,228,225,225,245,244,
111,2,117,214,117,223,225,242,225,226,233,99,128,6,64,238,
239,243,233,228,229,226,229,225,242,233,238,231,225,242,225,226,
233,99,128,6,64,237,225,236,236,235,225,244,225,235,225,238,
97,128,48,245,241,245,225,242,101,128,51,132,242,97,2,118,
19,118,28,225,242,225,226,233,99,128,6,80,244,225,238,225,
242,225,226,233,99,128,6,77,244,242,239,235,229,227,249,242,
233,236,236,233,99,128,4,159,244,225,232,233,242,225,240,242,
239,236,239,238,231,237,225,242,235,232,225,236,230,247,233,228,
244,104,128,255,112,246,229,242,244,233,227,225,236,243,244,242,
239,235,229,227,249,242,233,236,236,233,99,128,4,157,226,239,
240,239,237,239,230,111,128,49,14,99,4,118,131,118,153,118,
162,118,170,97,2,118,137,118,147,236,243,241,245,225,242,101,
128,51,137,242,239,110,128,1,233,229,228,233,236,236,97,128,
1,55,233,242,227,236,101,128,36,218,239,237,237,225,225,227,
227,229,238,116,128,1,55,228,239,244,226,229,236,239,119,128,
30,51,101,4,118,204,118,231,119,0,119,12,104,2,118,210,
118,221,225,242,237,229,238,233,225,110,128,5,132,233,242,225,
231,225,238,97,128,48,81,235,225,244,225,235,225,238,97,129,
48,177,118,244,232,225,236,230,247,233,228,244,104,128,255,121,
238,225,242,237,229,238,233,225,110,128,5,111,243,237,225,236,
236,235,225,244,225,235,225,238,97,128,48,246,231,242,229,229,
238,236,225,238,228,233,99,128,1,56,104,6,119,56,119,185,
119,196,119,221,120,52,120,140,97,5,119,68,119,78,119,89,
119,96,119,121,226,229,238,231,225,236,105,128,9,150,227,249,
242,233,236,236,233,99,128,4,69,228,229,246,97,128,9,22,
231,117,2,119,103,119,112,234,225,242,225,244,105,128,10,150,
242,237,245,235,232,105,128,10,22,104,4,119,131,119,140,119,
154,119,170,225,242,225,226,233,99,128,6,46,230,233,238,225,
236,225,242,225,226,233,99,128,254,166,233,238,233,244,233,225,
236,225,242,225,226,233,99,128,254,167,237,229,228,233,225,236,
225,242,225,226,233,99,128,254,168,229,233,227,239,240,244,233,
99,128,3,231,232,97,2,119,203,119,210,228,229,246,97,128,
9,89,231,245,242,237,245,235,232,105,128,10,89,233,229,245,
235,104,4,119,235,120,14,120,29,120,38,97,2,119,241,120,
0,227,233,242,227,236,229,235,239,242,229,225,110,128,50,120,
240,225,242,229,238,235,239,242,229,225,110,128,50,24,227,233,
242,227,236,229,235,239,242,229,225,110,128,50,106,235,239,242,
229,225,110,128,49,75,240,225,242,229,238,235,239,242,229,225,
110,128,50,10,111,4,120,62,120,111,120,121,120,126,235,104,
4,120,73,120,82,120,91,120,101,225,233,244,232,225,105,128,
14,2,239,238,244,232,225,105,128,14,5,245,225,244,244,232,
225,105,128,14,3,247,225,233,244,232,225,105,128,14,4,237,
245,244,244,232,225,105,128,14,91,239,107,128,1,153,242,225,
235,232,225,238,231,244,232,225,105,128,14,6,250,243,241,245,
225,242,101,128,51,145,105,4,120,160,120,171,120,196,120,245,
232,233,242,225,231,225,238,97,128,48,77,235,225,244,225,235,
225,238,97,129,48,173,120,184,232,225,236,230,247,233,228,244,
104,128,255,119,242,111,3,120,205,120,220,120,236,231,245,242,
225,237,245,243,241,245,225,242,101,128,51,21,237,229,229,244,
239,242,245,243,241,245,225,242,101,128,51,22,243,241,245,225,
242,101,128,51,20,249,229,239,107,5,121,4,121,39,121,54,
121,63,121,77,97,2,121,10,121,25,227,233,242,227,236,229,
235,239,242,229,225,110,128,50,110,240,225,242,229,238,235,239,
242,229,225,110,128,50,14,227,233,242,227,236,229,235,239,242,
229,225,110,128,50,96,235,239,242,229,225,110,128,49,49,240,
225,242,229,238,235,239,242,229,225,110,128,50,0,243,233,239,
243,235,239,242,229,225,110,128,49,51,234,229,227,249,242,233,
236,236,233,99,128,4,92,108,2,121,109,121,120,233,238,229,
226,229,236,239,119,128,30,53,243,241,245,225,242,101,128,51,
152,109,3,121,137,121,151,121,162,227,245,226,229,228,243,241,
245,225,242,101,128,51,166,239,238,239,243,240,225,227,101,128,
255,75,243,241,245,225,242,229,228,243,241,245,225,242,101,128,
51,162,111,5,121,190,121,216,121,254,122,10,122,24,104,2,
121,196,121,206,233,242,225,231,225,238,97,128,48,83,237,243,
241,245,225,242,101,128,51,192,235,97,2,121,223,121,231,233,
244,232,225,105,128,14,1,244,225,235,225,238,97,129,48,179,
121,242,232,225,236,230,247,233,228,244,104,128,255,122,239,240,
239,243,241,245,225,242,101,128,51,30,240,240,225,227,249,242,
233,236,236,233,99,128,4,129,114,2,122,30,122,50,229,225,
238,243,244,225,238,228,225,242,228,243,249,237,226,239,108,128,
50,127,239,238,233,243,227,237,98,128,3,67,240,97,2,122,
67,122,73,242,229,110,128,36,166,243,241,245,225,242,101,128,
51,170,243,233,227,249,242,233,236,236,233,99,128,4,111,116,
2,122,101,122,110,243,241,245,225,242,101,128,51,207,245,242,
238,229,100,128,2,158,117,2,122,124,122,135,232,233,242,225,
231,225,238,97,128,48,79,235,225,244,225,235,225,238,97,129,
48,175,122,148,232,225,236,230,247,233,228,244,104,128,255,120,
246,243,241,245,225,242,101,128,51,184,247,243,241,245,225,242,
101,128,51,190,108,146,0,108,122,220,124,247,125,20,125,86,
125,124,126,20,126,29,126,45,126,69,126,87,126,205,126,246,
127,125,127,133,127,166,127,175,127,183,127,245,97,7,122,236,
122,246,122,253,123,4,123,29,123,45,124,235,226,229,238,231,
225,236,105,128,9,178,227,245,244,101,128,1,58,228,229,246,
97,128,9,50,231,117,2,123,11,123,20,234,225,242,225,244,
105,128,10,178,242,237,245,235,232,105,128,10,50,235,235,232,
225,238,231,249,225,239,244,232,225,105,128,14,69,109,10,123,
67,124,6,124,23,124,61,124,75,124,94,124,110,124,130,124,
150,124,173,97,2,123,73,123,254,236,229,102,4,123,85,123,
99,123,191,123,208,230,233,238,225,236,225,242,225,226,233,99,
128,254,252,232,225,237,250,97,2,123,109,123,150,225,226,239,
246,101,2,123,119,123,133,230,233,238,225,236,225,242,225,226,
233,99,128,254,248,233,243,239,236,225,244,229,228,225,242,225,
226,233,99,128,254,247,226,229,236,239,119,2,123,160,123,174,
230,233,238,225,236,225,242,225,226,233,99,128,254,250,233,243,
239,236,225,244,229,228,225,242,225,226,233,99,128,254,249,233,
243,239,236,225,244,229,228,225,242,225,226,233,99,128,254,251,
237,225,228,228,225,225,226,239,246,101,2,123,223,123,237,230,
233,238,225,236,225,242,225,226,233,99,128,254,246,233,243,239,
236,225,244,229,228,225,242,225,226,233,99,128,254,245,242,225,
226,233,99,128,6,68,226,228,97,129,3,187,124,14,243,244,
242,239,235,101,128,1,155,229,100,130,5,220,124,32,124,52,
228,225,231,229,243,104,129,251,60,124,43,232,229,226,242,229,
119,128,251,60,232,229,226,242,229,119,128,5,220,230,233,238,
225,236,225,242,225,226,233,99,128,254,222,232,225,232,233,238,
233,244,233,225,236,225,242,225,226,233,99,128,252,202,233,238,
233,244,233,225,236,225,242,225,226,233,99,128,254,223,234,229,
229,237,233,238,233,244,233,225,236,225,242,225,226,233,99,128,
252,201,235,232,225,232,233,238,233,244,233,225,236,225,242,225,
226,233,99,128,252,203,236,225,237,232,229,232,233,243,239,236,
225,244,229,228,225,242,225,226,233,99,128,253,242,237,101,2,
124,180,124,193,228,233,225,236,225,242,225,226,233,99,128,254,
224,229,109,2,124,200,124,219,232,225,232,233,238,233,244,233,
225,236,225,242,225,226,233,99,128,253,136,233,238,233,244,233,
225,236,225,242,225,226,233,99,128,252,204,242,231,229,227,233,
242,227,236,101,128,37,239,98,3,124,255,125,4,125,10,225,
114,128,1,154,229,236,116,128,2,108,239,240,239,237,239,230,
111,128,49,12,99,4,125,30,125,37,125,46,125,73,225,242,
239,110,128,1,62,229,228,233,236,236,97,128,1,60,233,242,
99,2,125,54,125,59,236,101,128,36,219,245,237,230,236,229,
248,226,229,236,239,119,128,30,61,239,237,237,225,225,227,227,
229,238,116,128,1,60,228,239,116,130,1,64,125,96,125,105,
225,227,227,229,238,116,128,1,64,226,229,236,239,119,129,30,
55,125,115,237,225,227,242,239,110,128,30,57,101,3,125,132,
125,170,126,15,230,116,2,125,139,125,155,225,238,231,236,229,
225,226,239,246,229,227,237,98,128,3,26,244,225,227,235,226,
229,236,239,247,227,237,98,128,3,24,243,115,132,0,60,125,
183,125,205,125,217,126,7,229,241,245,225,108,129,34,100,125,
193,239,242,231,242,229,225,244,229,114,128,34,218,237,239,238,
239,243,240,225,227,101,128,255,28,111,2,125,223,125,252,114,
2,125,229,125,242,229,241,245,233,246,225,236,229,238,116,128,
34,114,231,242,229,225,244,229,114,128,34,118,246,229,242,229,
241,245,225,108,128,34,102,243,237,225,236,108,128,254,100,250,
104,128,2,110,230,226,236,239,227,107,128,37,140,232,239,239,
235,242,229,244,242,239,230,236,229,120,128,2,109,105,2,126,
51,126,56,242,97,128,32,164,247,238,225,242,237,229,238,233,
225,110,128,5,108,106,129,1,201,126,75,229,227,249,242,233,
236,236,233,99,128,4,89,108,132,246,192,126,99,126,123,126,
134,126,143,97,2,126,105,126,112,228,229,246,97,128,9,51,
231,245,234,225,242,225,244,105,128,10,179,233,238,229,226,229,
236,239,119,128,30,59,236,225,228,229,246,97,128,9,52,246,
239,227,225,236,233,99,3,126,157,126,167,126,174,226,229,238,
231,225,236,105,128,9,225,228,229,246,97,128,9,97,246,239,
247,229,236,243,233,231,110,2,126,188,126,198,226,229,238,231,
225,236,105,128,9,227,228,229,246,97,128,9,99,109,3,126,
213,126,226,126,237,233,228,228,236,229,244,233,236,228,101,128,
2,107,239,238,239,243,240,225,227,101,128,255,76,243,241,245,
225,242,101,128,51,208,111,6,127,4,127,16,127,58,127,69,
127,75,127,117,227,232,245,236,225,244,232,225,105,128,14,44,
231,233,227,225,108,3,127,28,127,34,127,53,225,238,100,128,
34,39,238,239,116,129,0,172,127,42,242,229,246,229,242,243,
229,100,128,35,16,239,114,128,34,40,236,233,238,231,244,232,
225,105,128,14,37,238,231,115,128,1,127,247,236,233,238,101,
2,127,85,127,108,99,2,127,91,127,103,229,238,244,229,242,
236,233,238,101,128,254,78,237,98,128,3,50,228,225,243,232,
229,100,128,254,77,250,229,238,231,101,128,37,202,240,225,242,
229,110,128,36,167,115,3,127,141,127,148,127,156,236,225,243,
104,128,1,66,241,245,225,242,101,128,33,19,245,240,229,242,
233,239,114,128,246,238,244,243,232,225,228,101,128,37,145,245,
244,232,225,105,128,14,38,246,239,227,225,236,233,99,3,127,
197,127,207,127,214,226,229,238,231,225,236,105,128,9,140,228,
229,246,97,128,9,12,246,239,247,229,236,243,233,231,110,2,
127,228,127,238,226,229,238,231,225,236,105,128,9,226,228,229,
246,97,128,9,98,248,243,241,245,225,242,101,128,51,211,109,
144,0,109,128,35,130,144,130,169,130,196,130,221,132,18,132,
40,133,95,133,125,133,174,134,25,134,47,134,72,134,81,135,
108,135,136,97,12,128,61,128,71,128,135,128,142,128,167,128,
215,130,51,130,76,130,81,130,95,130,107,130,112,226,229,238,
231,225,236,105,128,9,174,99,2,128,77,128,129,242,239,110,
132,0,175,128,91,128,102,128,108,128,117,226,229,236,239,247,
227,237,98,128,3,49,227,237,98,128,3,4,236,239,247,237,
239,100,128,2,205,237,239,238,239,243,240,225,227,101,128,255,
227,245,244,101,128,30,63,228,229,246,97,128,9,46,231,117,
2,128,149,128,158,234,225,242,225,244,105,128,10,174,242,237,
245,235,232,105,128,10,46,104,2,128,173,128,205,225,240,225,
235,104,2,128,183,128,192,232,229,226,242,229,119,128,5,164,
236,229,230,244,232,229,226,242,229,119,128,5,164,233,242,225,
231,225,238,97,128,48,126,105,5,128,227,129,40,129,103,129,
133,130,39,227,232,225,244,244,225,247,97,3,128,242,129,17,
129,24,236,239,119,2,128,250,129,5,236,229,230,244,244,232,
225,105,128,248,149,242,233,231,232,244,244,232,225,105,128,248,
148,244,232,225,105,128,14,75,245,240,240,229,242,236,229,230,
244,244,232,225,105,128,248,147,229,107,3,129,49,129,80,129,
87,236,239,119,2,129,57,129,68,236,229,230,244,244,232,225,
105,128,248,140,242,233,231,232,244,244,232,225,105,128,248,139,
244,232,225,105,128,14,72,245,240,240,229,242,236,229,230,244,
244,232,225,105,128,248,138,232,225,238,225,235,225,116,2,129,
115,129,126,236,229,230,244,244,232,225,105,128,248,132,244,232,
225,105,128,14,49,116,3,129,141,129,169,129,232,225,233,235,
232,117,2,129,151,129,162,236,229,230,244,244,232,225,105,128,
248,137,244,232,225,105,128,14,71,232,111,3,129,178,129,209,
129,216,236,239,119,2,129,186,129,197,236,229,230,244,244,232,
225,105,128,248,143,242,233,231,232,244,244,232,225,105,128,248,
142,244,232,225,105,128,14,73,245,240,240,229,242,236,229,230,
244,244,232,225,105,128,248,141,242,105,3,129,241,130,16,130,
23,236,239,119,2,129,249,130,4,236,229,230,244,244,232,225,
105,128,248,146,242,233,231,232,244,244,232,225,105,128,248,145,
244,232,225,105,128,14,74,245,240,240,229,242,236,229,230,244,
244,232,225,105,128,248,144,249,225,237,239,235,244,232,225,105,
128,14,70,235,225,244,225,235,225,238,97,129,48,222,130,64,
232,225,236,230,247,233,228,244,104,128,255,143,236,101,128,38,
66,238,243,249,239,238,243,241,245,225,242,101,128,51,71,241,
225,230,232,229,226,242,229,119,128,5,190,242,115,128,38,66,
115,2,130,118,130,136,239,242,225,227,233,242,227,236,229,232,
229,226,242,229,119,128,5,175,241,245,225,242,101,128,51,131,
98,2,130,150,130,160,239,240,239,237,239,230,111,128,49,7,
243,241,245,225,242,101,128,51,212,99,2,130,175,130,183,233,
242,227,236,101,128,36,220,245,226,229,228,243,241,245,225,242,
101,128,51,165,228,239,116,2,130,204,130,213,225,227,227,229,
238,116,128,30,65,226,229,236,239,119,128,30,67,101,7,130,
237,131,108,131,119,131,134,131,159,131,196,131,208,101,2,130,
243,131,95,109,4,130,253,131,6,131,20,131,36,225,242,225,
226,233,99,128,6,69,230,233,238,225,236,225,242,225,226,233,
99,128,254,226,233,238,233,244,233,225,236,225,242,225,226,233,
99,128,254,227,237,101,2,131,43,131,56,228,233,225,236,225,
242,225,226,233,99,128,254,228,229,237,105,2,131,64,131,79,
238,233,244,233,225,236,225,242,225,226,233,99,128,252,209,243,
239,236,225,244,229,228,225,242,225,226,233,99,128,252,72,244,
239,242,245,243,241,245,225,242,101,128,51,77,232,233,242,225,
231,225,238,97,128,48,129,233,250,233,229,242,225,243,241,245,
225,242,101,128,51,126,235,225,244,225,235,225,238,97,129,48,
225,131,147,232,225,236,230,247,233,228,244,104,128,255,146,109,
130,5,222,131,167,131,187,228,225,231,229,243,104,129,251,62,
131,178,232,229,226,242,229,119,128,251,62,232,229,226,242,229,
119,128,5,222,238,225,242,237,229,238,233,225,110,128,5,116,
242,235,232,97,3,131,219,131,228,132,5,232,229,226,242,229,
119,128,5,165,235,229,230,245,236,97,2,131,239,131,248,232,
229,226,242,229,119,128,5,166,236,229,230,244,232,229,226,242,
229,119,128,5,166,236,229,230,244,232,229,226,242,229,119,128,
5,165,104,2,132,24,132,30,239,239,107,128,2,113,250,243,
241,245,225,242,101,128,51,146,105,6,132,54,132,91,132,228,
132,239,133,8,133,65,228,100,2,132,61,132,86,236,229,228,
239,244,235,225,244,225,235,225,238,225,232,225,236,230,247,233,
228,244,104,128,255,101,239,116,128,0,183,229,245,109,5,132,
105,132,140,132,155,132,164,132,215,97,2,132,111,132,126,227,
233,242,227,236,229,235,239,242,229,225,110,128,50,114,240,225,
242,229,238,235,239,242,229,225,110,128,50,18,227,233,242,227,
236,229,235,239,242,229,225,110,128,50,100,235,239,242,229,225,
110,128,49,65,112,2,132,170,132,202,97,2,132,176,132,190,
238,243,233,239,243,235,239,242,229,225,110,128,49,112,242,229,
238,235,239,242,229,225,110,128,50,4,233,229,245,240,235,239,
242,229,225,110,128,49,110,243,233,239,243,235,239,242,229,225,
110,128,49,111,232,233,242,225,231,225,238,97,128,48,127,235,
225,244,225,235,225,238,97,129,48,223,132,252,232,225,236,230,
247,233,228,244,104,128,255,144,238,117,2,133,15,133,60,115,
132,34,18,133,27,133,38,133,47,133,53,226,229,236,239,247,
227,237,98,128,3,32,227,233,242,227,236,101,128,34,150,237,
239,100,128,2,215,240,236,245,115,128,34,19,244,101,128,32,
50,242,105,2,133,72,133,86,226,225,225,242,245,243,241,245,
225,242,101,128,51,74,243,241,245,225,242,101,128,51,73,108,
2,133,101,133,116,239,238,231,236,229,231,244,245,242,238,229,
100,128,2,112,243,241,245,225,242,101,128,51,150,109,3,133,
133,133,147,133,158,227,245,226,229,228,243,241,245,225,242,101,
128,51,163,239,238,239,243,240,225,227,101,128,255,77,243,241,
245,225,242,229,228,243,241,245,225,242,101,128,51,159,111,5,
133,186,133,212,133,237,133,247,134,0,104,2,133,192,133,202,
233,242,225,231,225,238,97,128,48,130,237,243,241,245,225,242,
101,128,51,193,235,225,244,225,235,225,238,97,129,48,226,133,
225,232,225,236,230,247,233,228,244,104,128,255,147,236,243,241,
245,225,242,101,128,51,214,237,225,244,232,225,105,128,14,33,
246,229,242,243,243,241,245,225,242,101,129,51,167,134,15,228,
243,241,245,225,242,101,128,51,168,240,97,2,134,32,134,38,
242,229,110,128,36,168,243,241,245,225,242,101,128,51,171,115,
2,134,53,134,62,243,241,245,225,242,101,128,51,179,245,240,
229,242,233,239,114,128,246,239,244,245,242,238,229,100,128,2,
111,117,141,0,181,134,111,134,115,134,125,134,149,134,159,134,
181,134,192,134,217,134,240,134,250,135,24,135,88,135,98,49,
128,0,181,225,243,241,245,225,242,101,128,51,130,227,104,2,
134,132,134,142,231,242,229,225,244,229,114,128,34,107,236,229,
243,115,128,34,106,230,243,241,245,225,242,101,128,51,140,103,
2,134,165,134,172,242,229,229,107,128,3,188,243,241,245,225,
242,101,128,51,141,232,233,242,225,231,225,238,97,128,48,128,
235,225,244,225,235,225,238,97,129,48,224,134,205,232,225,236,
230,247,233,228,244,104,128,255,145,108,2,134,223,134,232,243,
241,245,225,242,101,128,51,149,244,233,240,236,121,128,0,215,
237,243,241,245,225,242,101,128,51,155,238,225,104,2,135,2,
135,11,232,229,226,242,229,119,128,5,163,236,229,230,244,232,
229,226,242,229,119,128,5,163,115,2,135,30,135,79,233,99,
3,135,39,135,56,135,67,225,236,238,239,244,101,129,38,106,
135,50,228,226,108,128,38,107,230,236,225,244,243,233,231,110,
128,38,109,243,232,225,242,240,243,233,231,110,128,38,111,243,
241,245,225,242,101,128,51,178,246,243,241,245,225,242,101,128,
51,182,247,243,241,245,225,242,101,128,51,188,118,2,135,114,
135,127,237,229,231,225,243,241,245,225,242,101,128,51,185,243,
241,245,225,242,101,128,51,183,119,2,135,142,135,155,237,229,
231,225,243,241,245,225,242,101,128,51,191,243,241,245,225,242,
101,128,51,189,110,150,0,110,135,212,136,90,136,114,136,180,
136,205,137,7,137,17,137,84,137,127,139,161,139,179,139,204,
139,235,140,5,140,70,142,52,142,60,142,85,142,93,143,61,
143,71,143,81,97,8,135,230,135,250,136,1,136,8,136,33,
136,44,136,69,136,81,98,2,135,236,135,245,229,238,231,225,
236,105,128,9,168,236,97,128,34,7,227,245,244,101,128,1,
68,228,229,246,97,128,9,40,231,117,2,136,15,136,24,234,
225,242,225,244,105,128,10,168,242,237,245,235,232,105,128,10,
40,232,233,242,225,231,225,238,97,128,48,106,235,225,244,225,
235,225,238,97,129,48,202,136,57,232,225,236,230,247,233,228,
244,104,128,255,133,240,239,243,244,242,239,240,232,101,128,1,
73,243,241,245,225,242,101,128,51,129,98,2,136,96,136,106,
239,240,239,237,239,230,111,128,49,11,243,240,225,227,101,128,
0,160,99,4,136,124,136,131,136,140,136,167,225,242,239,110,
128,1,72,229,228,233,236,236,97,128,1,70,233,242,99,2,
136,148,136,153,236,101,128,36,221,245,237,230,236,229,248,226,
229,236,239,119,128,30,75,239,237,237,225,225,227,227,229,238,
116,128,1,70,228,239,116,2,136,188,136,197,225,227,227,229,
238,116,128,30,69,226,229,236,239,119,128,30,71,101,3,136,
213,136,224,136,249,232,233,242,225,231,225,238,97,128,48,109,
235,225,244,225,235,225,238,97,129,48,205,136,237,232,225,236,
230,247,233,228,244,104,128,255,136,247,243,232,229,241,229,236,
243,233,231,110,128,32,170,230,243,241,245,225,242,101,128,51,
139,103,2,137,23,137,73,97,3,137,31,137,41,137,48,226,
229,238,231,225,236,105,128,9,153,228,229,246,97,128,9,25,
231,117,2,137,55,137,64,234,225,242,225,244,105,128,10,153,
242,237,245,235,232,105,128,10,25,239,238,231,245,244,232,225,
105,128,14,7,104,2,137,90,137,100,233,242,225,231,225,238,
97,128,48,147,239,239,107,2,137,108,137,115,236,229,230,116,
128,2,114,242,229,244,242,239,230,236,229,120,128,2,115,105,
4,137,137,138,50,138,61,138,119,229,245,110,7,137,155,137,
190,137,222,137,236,137,245,138,22,138,35,97,2,137,161,137,
176,227,233,242,227,236,229,235,239,242,229,225,110,128,50,111,
240,225,242,229,238,235,239,242,229,225,110,128,50,15,227,105,
2,137,197,137,209,229,245,227,235,239,242,229,225,110,128,49,
53,242,227,236,229,235,239,242,229,225,110,128,50,97,232,233,
229,245,232,235,239,242,229,225,110,128,49,54,235,239,242,229,
225,110,128,49,52,240,97,2,137,252,138,10,238,243,233,239,
243,235,239,242,229,225,110,128,49,104,242,229,238,235,239,242,
229,225,110,128,50,1,243,233,239,243,235,239,242,229,225,110,
128,49,103,244,233,235,229,245,244,235,239,242,229,225,110,128,
49,102,232,233,242,225,231,225,238,97,128,48,107,107,2,138,
67,138,91,225,244,225,235,225,238,97,129,48,203,138,79,232,
225,236,230,247,233,228,244,104,128,255,134,232,225,232,233,116,
2,138,101,138,112,236,229,230,244,244,232,225,105,128,248,153,
244,232,225,105,128,14,77,238,101,141,0,57,138,150,138,159,
138,169,138,199,138,206,138,231,139,2,139,36,139,48,139,59,
139,92,139,100,139,111,225,242,225,226,233,99,128,6,105,226,
229,238,231,225,236,105,128,9,239,227,233,242,227,236,101,129,
36,104,138,180,233,238,246,229,242,243,229,243,225,238,243,243,
229,242,233,102,128,39,146,228,229,246,97,128,9,111,231,117,
2,138,213,138,222,234,225,242,225,244,105,128,10,239,242,237,
245,235,232,105,128,10,111,232,97,2,138,238,138,249,227,235,
225,242,225,226,233,99,128,6,105,238,231,250,232,239,117,128,
48,41,105,2,139,8,139,26,228,229,239,231,242,225,240,232,
233,227,240,225,242,229,110,128,50,40,238,230,229,242,233,239,
114,128,32,137,237,239,238,239,243,240,225,227,101,128,255,25,
239,236,228,243,244,249,236,101,128,247,57,112,2,139,65,139,
72,225,242,229,110,128,36,124,229,114,2,139,79,139,85,233,
239,100,128,36,144,243,233,225,110,128,6,249,242,239,237,225,
110,128,33,120,243,245,240,229,242,233,239,114,128,32,121,116,
2,139,117,139,155,229,229,110,2,139,125,139,134,227,233,242,
227,236,101,128,36,114,112,2,139,140,139,147,225,242,229,110,
128,36,134,229,242,233,239,100,128,36,154,232,225,105,128,14,
89,106,129,1,204,139,167,229,227,249,242,233,236,236,233,99,
128,4,90,235,225,244,225,235,225,238,97,129,48,243,139,192,
232,225,236,230,247,233,228,244,104,128,255,157,108,2,139,210,
139,224,229,231,242,233,231,232,244,236,239,238,103,128,1,158,
233,238,229,226,229,236,239,119,128,30,73,109,2,139,241,139,
252,239,238,239,243,240,225,227,101,128,255,78,243,241,245,225,
242,101,128,51,154,110,2,140,11,140,61,97,3,140,19,140,
29,140,36,226,229,238,231,225,236,105,128,9,163,228,229,246,
97,128,9,35,231,117,2,140,43,140,52,234,225,242,225,244,
105,128,10,163,242,237,245,235,232,105,128,10,35,238,225,228,
229,246,97,128,9,41,111,6,140,84,140,95,140,120,140,161,
141,113,142,40,232,233,242,225,231,225,238,97,128,48,110,235,
225,244,225,235,225,238,97,129,48,206,140,108,232,225,236,230,
247,233,228,244,104,128,255,137,110,3,140,128,140,144,140,153,
226,242,229,225,235,233,238,231,243,240,225,227,101,128,0,160,
229,238,244,232,225,105,128,14,19,245,244,232,225,105,128,14,
25,239,110,7,140,178,140,187,140,201,140,235,140,251,141,36,
141,95,225,242,225,226,233,99,128,6,70,230,233,238,225,236,
225,242,225,226,233,99,128,254,230,231,232,245,238,238,97,2,
140,212,140,221,225,242,225,226,233,99,128,6,186,230,233,238,
225,236,225,242,225,226,233,99,128,251,159,233,238,233,244,233,
225,236,225,242,225,226,233,99,128,254,231,234,229,229,237,105,
2,141,5,141,20,238,233,244,233,225,236,225,242,225,226,233,
99,128,252,210,243,239,236,225,244,229,228,225,242,225,226,233,
99,128,252,75,237,101,2,141,43,141,56,228,233,225,236,225,
242,225,226,233,99,128,254,232,229,237,105,2,141,64,141,79,
238,233,244,233,225,236,225,242,225,226,233,99,128,252,213,243,
239,236,225,244,229,228,225,242,225,226,233,99,128,252,78,238,
239,239,238,230,233,238,225,236,225,242,225,226,233,99,128,252,
141,116,7,141,129,141,140,141,169,141,204,141,216,141,236,142,
6,227,239,238,244,225,233,238,115,128,34,12,101,2,141,146,
141,162,236,229,237,229,238,116,129,34,9,141,157,239,102,128,
34,9,241,245,225,108,128,34,96,231,242,229,225,244,229,114,
129,34,111,141,181,238,239,114,2,141,189,141,197,229,241,245,
225,108,128,34,113,236,229,243,115,128,34,121,233,228,229,238,
244,233,227,225,108,128,34,98,236,229,243,115,129,34,110,141,
225,238,239,242,229,241,245,225,108,128,34,112,112,2,141,242,
141,252,225,242,225,236,236,229,108,128,34,38,242,229,227,229,
228,229,115,128,34,128,243,117,3,142,15,142,22,142,31,226,
243,229,116,128,34,132,227,227,229,229,228,115,128,34,129,240,
229,242,243,229,116,128,34,133,247,225,242,237,229,238,233,225,
110,128,5,118,240,225,242,229,110,128,36,169,115,2,142,66,
142,75,243,241,245,225,242,101,128,51,177,245,240,229,242,233,
239,114,128,32,127,244,233,236,228,101,128,0,241,117,132,3,
189,142,105,142,116,142,197,143,24,232,233,242,225,231,225,238,
97,128,48,108,107,2,142,122,142,146,225,244,225,235,225,238,
97,129,48,204,142,134,232,225,236,230,247,233,228,244,104,128,
255,135,244,97,3,142,155,142,165,142,172,226,229,238,231,225,
236,105,128,9,188,228,229,246,97,128,9,60,231,117,2,142,
179,142,188,234,225,242,225,244,105,128,10,188,242,237,245,235,
232,105,128,10,60,109,2,142,203,142,237,226,229,242,243,233,
231,110,130,0,35,142,217,142,229,237,239,238,239,243,240,225,
227,101,128,255,3,243,237,225,236,108,128,254,95,229,114,2,
142,244,143,20,225,236,243,233,231,110,2,142,255,143,7,231,
242,229,229,107,128,3,116,236,239,247,229,242,231,242,229,229,
107,128,3,117,111,128,33,22,110,130,5,224,143,32,143,52,
228,225,231,229,243,104,129,251,64,143,43,232,229,226,242,229,
119,128,251,64,232,229,226,242,229,119,128,5,224,246,243,241,
245,225,242,101,128,51,181,247,243,241,245,225,242,101,128,51,
187,249,97,3,143,90,143,100,143,107,226,229,238,231,225,236,
105,128,9,158,228,229,246,97,128,9,30,231,117,2,143,114,
143,123,234,225,242,225,244,105,128,10,158,242,237,245,235,232,
105,128,10,30,111,147,0,111,143,174,143,196,144,18,144,188,
145,4,145,19,145,59,145,182,145,203,145,241,145,252,146,174,
148,8,148,72,148,105,148,151,149,24,149,71,149,83,97,2,
143,180,143,187,227,245,244,101,128,0,243,238,231,244,232,225,
105,128,14,45,98,4,143,206,143,248,144,1,144,11,225,242,
242,229,100,130,2,117,143,218,143,229,227,249,242,233,236,236,
233,99,128,4,233,228,233,229,242,229,243,233,243,227,249,242,
233,236,236,233,99,128,4,235,229,238,231,225,236,105,128,9,
147,239,240,239,237,239,230,111,128,49,27,242,229,246,101,128,
1,79,99,3,144,26,144,99,144,178,97,2,144,32,144,93,
238,228,242,97,3,144,43,144,50,144,61,228,229,246,97,128,
9,17,231,245,234,225,242,225,244,105,128,10,145,246,239,247,
229,236,243,233,231,110,2,144,75,144,82,228,229,246,97,128,
9,73,231,245,234,225,242,225,244,105,128,10,201,242,239,110,
128,1,210,233,242,99,2,144,107,144,112,236,101,128,36,222,
245,237,230,236,229,120,133,0,244,144,131,144,139,144,150,144,
158,144,170,225,227,245,244,101,128,30,209,228,239,244,226,229,
236,239,119,128,30,217,231,242,225,246,101,128,30,211,232,239,
239,235,225,226,239,246,101,128,30,213,244,233,236,228,101,128,
30,215,249,242,233,236,236,233,99,128,4,62,100,4,144,198,
144,221,144,227,144,250,226,108,2,144,205,144,213,225,227,245,
244,101,128,1,81,231,242,225,246,101,128,2,13,229,246,97,
128,9,19,233,229,242,229,243,233,115,129,0,246,144,239,227,
249,242,233,236,236,233,99,128,4,231,239,244,226,229,236,239,
119,128,30,205,101,129,1,83,145,10,235,239,242,229,225,110,
128,49,90,103,3,145,27,145,42,145,49,239,238,229,107,129,
2,219,145,36,227,237,98,128,3,40,242,225,246,101,128,0,
242,245,234,225,242,225,244,105,128,10,147,104,4,145,69,145,
80,145,90,145,168,225,242,237,229,238,233,225,110,128,5,133,
233,242,225,231,225,238,97,128,48,74,111,2,145,96,145,106,
239,235,225,226,239,246,101,128,30,207,242,110,133,1,161,145,
121,145,129,145,140,145,148,145,160,225,227,245,244,101,128,30,
219,228,239,244,226,229,236,239,119,128,30,227,231,242,225,246,
101,128,30,221,232,239,239,235,225,226,239,246,101,128,30,223,
244,233,236,228,101,128,30,225,245,238,231,225,242,245,237,236,
225,245,116,128,1,81,105,129,1,163,145,188,238,246,229,242,
244,229,228,226,242,229,246,101,128,2,15,107,2,145,209,145,
233,225,244,225,235,225,238,97,129,48,170,145,221,232,225,236,
230,247,233,228,244,104,128,255,117,239,242,229,225,110,128,49,
87,236,229,232,229,226,242,229,119,128,5,171,109,6,146,10,
146,38,146,45,146,134,146,145,146,163,225,227,242,239,110,130,
1,77,146,22,146,30,225,227,245,244,101,128,30,83,231,242,
225,246,101,128,30,81,228,229,246,97,128,9,80,229,231,97,
133,3,201,146,61,146,65,146,76,146,90,146,106,49,128,3,
214,227,249,242,233,236,236,233,99,128,4,97,236,225,244,233,
238,227,236,239,243,229,100,128,2,119,242,239,245,238,228,227,
249,242,233,236,236,233,99,128,4,123,116,2,146,112,146,127,
233,244,236,239,227,249,242,233,236,236,233,99,128,4,125,239,
238,239,115,128,3,206,231,245,234,225,242,225,244,105,128,10,
208,233,227,242,239,110,129,3,191,146,155,244,239,238,239,115,
128,3,204,239,238,239,243,240,225,227,101,128,255,79,238,101,
145,0,49,146,213,146,222,146,232,147,6,147,31,147,40,147,
49,147,74,147,108,147,142,147,154,147,173,147,184,147,217,147,
227,147,235,147,246,225,242,225,226,233,99,128,6,97,226,229,
238,231,225,236,105,128,9,231,227,233,242,227,236,101,129,36,
96,146,243,233,238,246,229,242,243,229,243,225,238,243,243,229,
242,233,102,128,39,138,100,2,147,12,147,18,229,246,97,128,
9,103,239,244,229,238,236,229,225,228,229,114,128,32,36,229,
233,231,232,244,104,128,33,91,230,233,244,244,229,100,128,246,
220,231,117,2,147,56,147,65,234,225,242,225,244,105,128,10,
231,242,237,245,235,232,105,128,10,103,232,97,3,147,83,147,
94,147,99,227,235,225,242,225,226,233,99,128,6,97,236,102,
128,0,189,238,231,250,232,239,117,128,48,33,105,2,147,114,
147,132,228,229,239,231,242,225,240,232,233,227,240,225,242,229,
110,128,50,32,238,230,229,242,233,239,114,128,32,129,237,239,
238,239,243,240,225,227,101,128,255,17,238,245,237,229,242,225,
244,239,242,226,229,238,231,225,236,105,128,9,244,239,236,228,
243,244,249,236,101,128,247,49,112,2,147,190,147,197,225,242,
229,110,128,36,116,229,114,2,147,204,147,210,233,239,100,128,
36,136,243,233,225,110,128,6,241,241,245,225,242,244,229,114,
128,0,188,242,239,237,225,110,128,33,112,243,245,240,229,242,
233,239,114,128,0,185,244,104,2,147,253,148,2,225,105,128,
14,81,233,242,100,128,33,83,111,3,148,16,148,50,148,66,
103,2,148,22,148,40,239,238,229,107,129,1,235,148,31,237,
225,227,242,239,110,128,1,237,245,242,237,245,235,232,105,128,
10,19,237,225,244,242,225,231,245,242,237,245,235,232,105,128,
10,75,240,229,110,128,2,84,112,3,148,80,148,87,148,98,
225,242,229,110,128,36,170,229,238,226,245,236,236,229,116,128,
37,230,244,233,239,110,128,35,37,114,2,148,111,148,140,100,
2,148,117,148,128,230,229,237,233,238,233,238,101,128,0,170,
237,225,243,227,245,236,233,238,101,128,0,186,244,232,239,231,
239,238,225,108,128,34,31,115,5,148,163,148,195,148,212,149,
1,149,14,232,239,242,116,2,148,172,148,179,228,229,246,97,
128,9,18,246,239,247,229,236,243,233,231,238,228,229,246,97,
128,9,74,236,225,243,104,129,0,248,148,204,225,227,245,244,
101,128,1,255,237,225,236,108,2,148,221,148,232,232,233,242,
225,231,225,238,97,128,48,73,235,225,244,225,235,225,238,97,
129,48,169,148,245,232,225,236,230,247,233,228,244,104,128,255,
107,244,242,239,235,229,225,227,245,244,101,128,1,255,245,240,
229,242,233,239,114,128,246,240,116,2,149,30,149,41,227,249,
242,233,236,236,233,99,128,4,127,233,236,228,101,130,0,245,
149,52,149,60,225,227,245,244,101,128,30,77,228,233,229,242,
229,243,233,115,128,30,79,245,226,239,240,239,237,239,230,111,
128,49,33,118,2,149,89,149,170,229,114,2,149,96,149,162,
236,233,238,101,131,32,62,149,109,149,132,149,155,99,2,149,
115,149,127,229,238,244,229,242,236,233,238,101,128,254,74,237,
98,128,3,5,100,2,149,138,149,146,225,243,232,229,100,128,
254,73,226,236,247,225,246,121,128,254,76,247,225,246,121,128,
254,75,243,227,239,242,101,128,0,175,239,247,229,236,243,233,
231,110,3,149,185,149,195,149,202,226,229,238,231,225,236,105,
128,9,203,228,229,246,97,128,9,75,231,245,234,225,242,225,
244,105,128,10,203,112,145,0,112,149,251,152,123,152,134,152,
143,152,155,154,80,154,90,155,82,156,101,156,191,156,217,157,
92,157,100,158,2,158,60,158,88,158,98,97,14,150,25,150,
57,150,67,150,74,150,81,150,129,150,140,150,154,150,165,150,
212,150,226,151,238,152,21,152,111,97,2,150,31,150,43,237,
240,243,243,241,245,225,242,101,128,51,128,243,229,238,244,239,
243,241,245,225,242,101,128,51,43,226,229,238,231,225,236,105,
128,9,170,227,245,244,101,128,30,85,228,229,246,97,128,9,
42,103,2,150,87,150,105,101,2,150,93,150,100,228,239,247,
110,128,33,223,245,112,128,33,222,117,2,150,111,150,120,234,
225,242,225,244,105,128,10,170,242,237,245,235,232,105,128,10,
42,232,233,242,225,231,225,238,97,128,48,113,233,249,225,238,
238,239,233,244,232,225,105,128,14,47,235,225,244,225,235,225,
238,97,128,48,209,108,2,150,171,150,196,225,244,225,236,233,
250,225,244,233,239,238,227,249,242,233,236,236,233,227,227,237,
98,128,4,132,239,227,232,235,225,227,249,242,233,236,236,233,
99,128,4,192,238,243,233,239,243,235,239,242,229,225,110,128,
49,127,114,3,150,234,150,255,151,227,97,2,150,240,150,248,
231,242,225,240,104,128,0,182,236,236,229,108,128,34,37,229,
110,2,151,6,151,116,236,229,230,116,136,0,40,151,29,151,
44,151,49,151,54,151,65,151,77,151,100,151,105,225,236,244,
239,238,229,225,242,225,226,233,99,128,253,62,226,116,128,248,
237,229,120,128,248,236,233,238,230,229,242,233,239,114,128,32,
141,237,239,238,239,243,240,225,227,101,128,255,8,115,2,151,
83,151,90,237,225,236,108,128,254,89,245,240,229,242,233,239,
114,128,32,125,244,112,128,248,235,246,229,242,244,233,227,225,
108,128,254,53,242,233,231,232,116,136,0,41,151,140,151,155,
151,160,151,165,151,176,151,188,151,211,151,216,225,236,244,239,
238,229,225,242,225,226,233,99,128,253,63,226,116,128,248,248,
229,120,128,248,247,233,238,230,229,242,233,239,114,128,32,142,
237,239,238,239,243,240,225,227,101,128,255,9,115,2,151,194,
151,201,237,225,236,108,128,254,90,245,240,229,242,233,239,114,
128,32,126,244,112,128,248,246,246,229,242,244,233,227,225,108,
128,254,54,244,233,225,236,228,233,230,102,128,34,2,115,3,
151,246,152,1,152,13,229,241,232,229,226,242,229,119,128,5,
192,232,244,225,232,229,226,242,229,119,128,5,153,241,245,225,
242,101,128,51,169,244,225,104,134,5,183,152,39,152,53,152,
58,152,67,152,82,152,98,49,2,152,45,152,49,49,128,5,
183,100,128,5,183,178,97,128,5,183,232,229,226,242,229,119,
128,5,183,238,225,242,242,239,247,232,229,226,242,229,119,128,
5,183,241,245,225,242,244,229,242,232,229,226,242,229,119,128,
5,183,247,233,228,229,232,229,226,242,229,119,128,5,183,250,
229,242,232,229,226,242,229,119,128,5,161,226,239,240,239,237,
239,230,111,128,49,6,227,233,242,227,236,101,128,36,223,228,
239,244,225,227,227,229,238,116,128,30,87,101,137,5,228,152,
177,152,188,152,208,152,220,152,240,153,86,153,97,153,118,154,
73,227,249,242,233,236,236,233,99,128,4,63,228,225,231,229,
243,104,129,251,68,152,199,232,229,226,242,229,119,128,251,68,
229,250,233,243,241,245,225,242,101,128,51,59,230,233,238,225,
236,228,225,231,229,243,232,232,229,226,242,229,119,128,251,67,
104,5,152,252,153,19,153,27,153,41,153,71,225,114,2,153,
3,153,10,225,226,233,99,128,6,126,237,229,238,233,225,110,
128,5,122,229,226,242,229,119,128,5,228,230,233,238,225,236,
225,242,225,226,233,99,128,251,87,105,2,153,47,153,62,238,
233,244,233,225,236,225,242,225,226,233,99,128,251,88,242,225,
231,225,238,97,128,48,122,237,229,228,233,225,236,225,242,225,
226,233,99,128,251,89,235,225,244,225,235,225,238,97,128,48,
218,237,233,228,228,236,229,232,239,239,235,227,249,242,233,236,
236,233,99,128,4,167,114,5,153,130,153,142,153,184,154,49,
154,62,225,230,229,232,229,226,242,229,119,128,251,78,227,229,
238,116,131,0,37,153,155,153,164,153,176,225,242,225,226,233,
99,128,6,106,237,239,238,239,243,240,225,227,101,128,255,5,
243,237,225,236,108,128,254,106,105,2,153,190,154,31,239,100,
134,0,46,153,207,153,218,153,229,153,241,153,252,154,8,225,
242,237,229,238,233,225,110,128,5,137,227,229,238,244,229,242,
229,100,128,0,183,232,225,236,230,247,233,228,244,104,128,255,
97,233,238,230,229,242,233,239,114,128,246,231,237,239,238,239,
243,240,225,227,101,128,255,14,115,2,154,14,154,21,237,225,
236,108,128,254,82,245,240,229,242,233,239,114,128,246,232,243,
240,239,237,229,238,233,231,242,229,229,235,227,237,98,128,3,
66,240,229,238,228,233,227,245,236,225,114,128,34,165,244,232,
239,245,243,225,238,100,128,32,48,243,229,244,97,128,32,167,
230,243,241,245,225,242,101,128,51,138,104,3,154,98,154,148,
155,29,97,3,154,106,154,116,154,123,226,229,238,231,225,236,
105,128,9,171,228,229,246,97,128,9,43,231,117,2,154,130,
154,139,234,225,242,225,244,105,128,10,171,242,237,245,235,232,
105,128,10,43,105,133,3,198,154,162,154,166,154,252,155,4,
155,15,49,128,3,213,229,245,240,104,4,154,179,154,214,154,
229,154,238,97,2,154,185,154,200,227,233,242,227,236,229,235,
239,242,229,225,110,128,50,122,240,225,242,229,238,235,239,242,
229,225,110,128,50,26,227,233,242,227,236,229,235,239,242,229,
225,110,128,50,108,235,239,242,229,225,110,128,49,77,240,225,
242,229,238,235,239,242,229,225,110,128,50,12,236,225,244,233,
110,128,2,120,238,244,232,245,244,232,225,105,128,14,58,243,
249,237,226,239,236,231,242,229,229,107,128,3,213,111,3,155,
37,155,42,155,68,239,107,128,1,165,240,104,2,155,49,155,
58,225,238,244,232,225,105,128,14,30,245,238,231,244,232,225,
105,128,14,28,243,225,237,240,232,225,239,244,232,225,105,128,
14,32,105,133,3,192,155,96,156,52,156,63,156,74,156,88,
229,245,112,6,155,112,155,147,155,179,155,207,155,221,156,17,
97,2,155,118,155,133,227,233,242,227,236,229,235,239,242,229,
225,110,128,50,115,240,225,242,229,238,235,239,242,229,225,110,
128,50,19,227,105,2,155,154,155,166,229,245,227,235,239,242,
229,225,110,128,49,118,242,227,236,229,235,239,242,229,225,110,
128,50,101,107,2,155,185,155,199,233,249,229,239,235,235,239,
242,229,225,110,128,49,114,239,242,229,225,110,128,49,66,240,
225,242,229,238,235,239,242,229,225,110,128,50,5,243,233,239,
115,2,155,230,156,2,107,2,155,236,155,250,233,249,229,239,
235,235,239,242,229,225,110,128,49,116,239,242,229,225,110,128,
49,68,244,233,235,229,245,244,235,239,242,229,225,110,128,49,
117,116,2,156,23,156,38,232,233,229,245,244,232,235,239,242,
229,225,110,128,49,119,233,235,229,245,244,235,239,242,229,225,
110,128,49,115,232,233,242,225,231,225,238,97,128,48,116,235,
225,244,225,235,225,238,97,128,48,212,243,249,237,226,239,236,
231,242,229,229,107,128,3,214,247,242,225,242,237,229,238,233,
225,110,128,5,131,236,245,115,132,0,43,156,115,156,126,156,
135,156,168,226,229,236,239,247,227,237,98,128,3,31,227,233,
242,227,236,101,128,34,149,109,2,156,141,156,148,233,238,245,
115,128,0,177,111,2,156,154,156,158,100,128,2,214,238,239,
243,240,225,227,101,128,255,11,115,2,156,174,156,181,237,225,
236,108,128,254,98,245,240,229,242,233,239,114,128,32,122,109,
2,156,197,156,208,239,238,239,243,240,225,227,101,128,255,80,
243,241,245,225,242,101,128,51,216,111,5,156,229,156,240,157,
51,157,62,157,72,232,233,242,225,231,225,238,97,128,48,125,
233,238,244,233,238,231,233,238,228,229,120,4,157,4,157,16,
157,28,157,41,228,239,247,238,247,232,233,244,101,128,38,31,
236,229,230,244,247,232,233,244,101,128,38,28,242,233,231,232,
244,247,232,233,244,101,128,38,30,245,240,247,232,233,244,101,
128,38,29,235,225,244,225,235,225,238,97,128,48,221,240,236,
225,244,232,225,105,128,14,27,243,244,225,236,237,225,242,107,
129,48,18,157,85,230,225,227,101,128,48,32,240,225,242,229,
110,128,36,171,114,3,157,108,157,134,157,159,101,2,157,114,
157,122,227,229,228,229,115,128,34,122,243,227,242,233,240,244,
233,239,110,128,33,30,233,237,101,2,157,142,157,148,237,239,
100,128,2,185,242,229,246,229,242,243,229,100,128,32,53,111,
4,157,169,157,176,157,186,157,199,228,245,227,116,128,34,15,
234,229,227,244,233,246,101,128,35,5,236,239,238,231,229,228,
235,225,238,97,128,48,252,112,2,157,205,157,242,101,2,157,
211,157,218,236,236,239,114,128,35,24,242,243,117,2,157,226,
157,233,226,243,229,116,128,34,130,240,229,242,243,229,116,128,
34,131,239,242,244,233,239,110,129,34,55,157,253,225,108,128,
34,29,115,2,158,8,158,51,105,130,3,200,158,16,158,27,
227,249,242,233,236,236,233,99,128,4,113,236,233,240,238,229,
245,237,225,244,225,227,249,242,233,236,236,233,227,227,237,98,
128,4,134,243,241,245,225,242,101,128,51,176,117,2,158,66,
158,77,232,233,242,225,231,225,238,97,128,48,119,235,225,244,
225,235,225,238,97,128,48,215,246,243,241,245,225,242,101,128,
51,180,247,243,241,245,225,242,101,128,51,186,113,136,0,113,
158,128,159,177,159,188,159,197,159,204,159,216,159,254,160,6,
97,4,158,138,158,161,158,225,159,160,100,2,158,144,158,150,
229,246,97,128,9,88,237,225,232,229,226,242,229,119,128,5,
168,102,4,158,171,158,180,158,194,158,210,225,242,225,226,233,
99,128,6,66,230,233,238,225,236,225,242,225,226,233,99,128,
254,214,233,238,233,244,233,225,236,225,242,225,226,233,99,128,
254,215,237,229,228,233,225,236,225,242,225,226,233,99,128,254,
216,237,225,244,115,136,5,184,158,248,159,12,159,26,159,31,
159,36,159,45,159,60,159,147,49,3,159,0,159,4,159,8,
48,128,5,184,97,128,5,184,99,128,5,184,50,2,159,18,
159,22,55,128,5,184,57,128,5,184,179,51,128,5,184,228,
101,128,5,184,232,229,226,242,229,119,128,5,184,238,225,242,
242,239,247,232,229,226,242,229,119,128,5,184,113,2,159,66,
159,132,225,244,225,110,4,159,79,159,88,159,103,159,119,232,
229,226,242,229,119,128,5,184,238,225,242,242,239,247,232,229,
226,242,229,119,128,5,184,241,245,225,242,244,229,242,232,229,
226,242,229,119,128,5,184,247,233,228,229,232,229,226,242,229,
119,128,5,184,245,225,242,244,229,242,232,229,226,242,229,119,
128,5,184,247,233,228,229,232,229,226,242,229,119,128,5,184,
242,238,229,249,240,225,242,225,232,229,226,242,229,119,128,5,
159,226,239,240,239,237,239,230,111,128,49,17,227,233,242,227,
236,101,128,36,224,232,239,239,107,128,2,160,237,239,238,239,
243,240,225,227,101,128,255,81,239,102,130,5,231,159,225,159,
245,228,225,231,229,243,104,129,251,71,159,236,232,229,226,242,
229,119,128,251,71,232,229,226,242,229,119,128,5,231,240,225,
242,229,110,128,36,172,117,4,160,16,160,28,160,117,160,204,
225,242,244,229,242,238,239,244,101,128,38,105,226,245,244,115,
135,5,187,160,49,160,54,160,59,160,64,160,73,160,88,160,
104,177,56,128,5,187,178,53,128,5,187,179,49,128,5,187,
232,229,226,242,229,119,128,5,187,238,225,242,242,239,247,232,
229,226,242,229,119,128,5,187,241,245,225,242,244,229,242,232,
229,226,242,229,119,128,5,187,247,233,228,229,232,229,226,242,
229,119,128,5,187,229,243,244,233,239,110,133,0,63,160,136,
160,159,160,176,160,184,160,196,225,114,2,160,143,160,150,225,
226,233,99,128,6,31,237,229,238,233,225,110,128,5,94,228,
239,247,110,129,0,191,160,168,243,237,225,236,108,128,247,191,
231,242,229,229,107,128,3,126,237,239,238,239,243,240,225,227,
101,128,255,31,243,237,225,236,108,128,247,63,239,244,101,4,
160,216,161,31,161,51,161,80,228,226,108,133,0,34,160,232,
160,239,160,246,161,2,161,23,226,225,243,101,128,32,30,236,
229,230,116,128,32,28,237,239,238,239,243,240,225,227,101,128,
255,2,240,242,233,237,101,129,48,30,161,12,242,229,246,229,
242,243,229,100,128,48,29,242,233,231,232,116,128,32,29,236,
229,230,116,129,32,24,161,40,242,229,246,229,242,243,229,100,
128,32,27,114,2,161,57,161,67,229,246,229,242,243,229,100,
128,32,27,233,231,232,116,129,32,25,161,76,110,128,1,73,
243,233,238,231,108,2,161,90,161,97,226,225,243,101,128,32,
26,101,129,0,39,161,103,237,239,238,239,243,240,225,227,101,
128,255,7,114,145,0,114,161,153,162,157,162,168,162,215,163,
10,164,27,164,51,164,146,166,180,166,217,166,229,167,27,167,
35,167,197,167,208,167,243,168,87,97,11,161,177,161,188,161,
198,161,205,162,14,162,30,162,55,162,66,162,91,162,114,162,
151,225,242,237,229,238,233,225,110,128,5,124,226,229,238,231,
225,236,105,128,9,176,227,245,244,101,128,1,85,100,4,161,
215,161,221,161,235,162,5,229,246,97,128,9,48,233,227,225,
108,129,34,26,161,230,229,120,128,248,229,239,246,229,242,243,
243,241,245,225,242,101,129,51,174,161,251,228,243,241,245,225,
242,101,128,51,175,243,241,245,225,242,101,128,51,173,230,101,
129,5,191,162,21,232,229,226,242,229,119,128,5,191,231,117,
2,162,37,162,46,234,225,242,225,244,105,128,10,176,242,237,
245,235,232,105,128,10,48,232,233,242,225,231,225,238,97,128,
48,137,235,225,244,225,235,225,238,97,129,48,233,162,79,232,
225,236,230,247,233,228,244,104,128,255,151,236,239,247,229,242,
228,233,225,231,239,238,225,236,226,229,238,231,225,236,105,128,
9,241,109,2,162,120,162,143,233,228,228,236,229,228,233,225,
231,239,238,225,236,226,229,238,231,225,236,105,128,9,240,243,
232,239,242,110,128,2,100,244,233,111,128,34,54,226,239,240,
239,237,239,230,111,128,49,22,99,4,162,178,162,185,162,194,
162,202,225,242,239,110,128,1,89,229,228,233,236,236,97,128,
1,87,233,242,227,236,101,128,36,225,239,237,237,225,225,227,
227,229,238,116,128,1,87,100,2,162,221,162,231,226,236,231,
242,225,246,101,128,2,17,239,116,2,162,238,162,247,225,227,
227,229,238,116,128,30,89,226,229,236,239,119,129,30,91,163,
1,237,225,227,242,239,110,128,30,93,101,6,163,24,163,69,
163,104,163,159,163,184,163,217,102,2,163,30,163,43,229,242,
229,238,227,229,237,225,242,107,128,32,59,236,229,248,243,117,
2,163,53,163,60,226,243,229,116,128,34,134,240,229,242,243,
229,116,128,34,135,231,233,243,244,229,114,2,163,80,163,85,
229,100,128,0,174,115,2,163,91,163,97,225,238,115,128,248,
232,229,242,233,102,128,246,218,104,3,163,112,163,135,163,149,
225,114,2,163,119,163,126,225,226,233,99,128,6,49,237,229,
238,233,225,110,128,5,128,230,233,238,225,236,225,242,225,226,
233,99,128,254,174,233,242,225,231,225,238,97,128,48,140,235,
225,244,225,235,225,238,97,129,48,236,163,172,232,225,236,230,
247,233,228,244,104,128,255,154,243,104,130,5,232,163,193,163,
208,228,225,231,229,243,232,232,229,226,242,229,119,128,251,72,
232,229,226,242,229,119,128,5,232,118,3,163,225,163,238,164,
14,229,242,243,229,228,244,233,236,228,101,128,34,61,233,97,
2,163,245,163,254,232,229,226,242,229,119,128,5,151,237,245,
231,242,225,243,232,232,229,226,242,229,119,128,5,151,236,239,
231,233,227,225,236,238,239,116,128,35,16,230,233,243,232,232,
239,239,107,129,2,126,164,40,242,229,246,229,242,243,229,100,
128,2,127,104,2,164,57,164,80,97,2,164,63,164,73,226,
229,238,231,225,236,105,128,9,221,228,229,246,97,128,9,93,
111,131,3,193,164,90,164,119,164,133,239,107,129,2,125,164,
97,244,245,242,238,229,100,129,2,123,164,108,243,245,240,229,
242,233,239,114,128,2,181,243,249,237,226,239,236,231,242,229,
229,107,128,3,241,244,233,227,232,239,239,235,237,239,100,128,
2,222,105,6,164,160,165,204,165,250,166,5,166,30,166,166,
229,245,108,9,164,182,164,217,164,232,164,246,165,36,165,50,
165,136,165,149,165,184,97,2,164,188,164,203,227,233,242,227,
236,229,235,239,242,229,225,110,128,50,113,240,225,242,229,238,
235,239,242,229,225,110,128,50,17,227,233,242,227,236,229,235,
239,242,229,225,110,128,50,99,232,233,229,245,232,235,239,242,
229,225,110,128,49,64,107,2,164,252,165,28,233,249,229,239,
107,2,165,6,165,15,235,239,242,229,225,110,128,49,58,243,
233,239,243,235,239,242,229,225,110,128,49,105,239,242,229,225,
110,128,49,57,237,233,229,245,237,235,239,242,229,225,110,128,
49,59,112,3,165,58,165,90,165,105,97,2,165,64,165,78,
238,243,233,239,243,235,239,242,229,225,110,128,49,108,242,229,
238,235,239,242,229,225,110,128,50,3,232,233,229,245,240,232,
235,239,242,229,225,110,128,49,63,233,229,245,112,2,165,114,
165,123,235,239,242,229,225,110,128,49,60,243,233,239,243,235,
239,242,229,225,110,128,49,107,243,233,239,243,235,239,242,229,
225,110,128,49,61,116,2,165,155,165,170,232,233,229,245,244,
232,235,239,242,229,225,110,128,49,62,233,235,229,245,244,235,
239,242,229,225,110,128,49,106,249,229,239,242,233,238,232,233,
229,245,232,235,239,242,229,225,110,128,49,109,231,232,116,2,
165,212,165,220,225,238,231,236,101,128,34,31,116,2,165,226,
165,240,225,227,235,226,229,236,239,247,227,237,98,128,3,25,
242,233,225,238,231,236,101,128,34,191,232,233,242,225,231,225,
238,97,128,48,138,235,225,244,225,235,225,238,97,129,48,234,
166,18,232,225,236,230,247,233,228,244,104,128,255,152,110,2,
166,36,166,152,103,131,2,218,166,46,166,57,166,63,226,229,
236,239,247,227,237,98,128,3,37,227,237,98,128,3,10,232,
225,236,102,2,166,72,166,118,236,229,230,116,131,2,191,166,
85,166,96,166,107,225,242,237,229,238,233,225,110,128,5,89,
226,229,236,239,247,227,237,98,128,3,28,227,229,238,244,229,
242,229,100,128,2,211,242,233,231,232,116,130,2,190,166,130,
166,141,226,229,236,239,247,227,237,98,128,3,57,227,229,238,
244,229,242,229,100,128,2,210,246,229,242,244,229,228,226,242,
229,246,101,128,2,19,244,244,239,242,245,243,241,245,225,242,
101,128,51,81,108,2,166,186,166,197,233,238,229,226,229,236,
239,119,128,30,95,239,238,231,236,229,103,129,2,124,166,208,
244,245,242,238,229,100,128,2,122,237,239,238,239,243,240,225,
227,101,128,255,82,111,3,166,237,166,248,167,17,232,233,242,
225,231,225,238,97,128,48,141,235,225,244,225,235,225,238,97,
129,48,237,167,5,232,225,236,230,247,233,228,244,104,128,255,
155,242,245,225,244,232,225,105,128,14,35,240,225,242,229,110,
128,36,173,114,3,167,43,167,79,167,109,97,3,167,51,167,
61,167,68,226,229,238,231,225,236,105,128,9,220,228,229,246,
97,128,9,49,231,245,242,237,245,235,232,105,128,10,92,229,
104,2,167,86,167,95,225,242,225,226,233,99,128,6,145,230,
233,238,225,236,225,242,225,226,233,99,128,251,141,246,239,227,
225,236,233,99,4,167,125,167,135,167,142,167,153,226,229,238,
231,225,236,105,128,9,224,228,229,246,97,128,9,96,231,245,
234,225,242,225,244,105,128,10,224,246,239,247,229,236,243,233,
231,110,3,167,169,167,179,167,186,226,229,238,231,225,236,105,
128,9,196,228,229,246,97,128,9,68,231,245,234,225,242,225,
244,105,128,10,196,243,245,240,229,242,233,239,114,128,246,241,
116,2,167,214,167,222,226,236,239,227,107,128,37,144,245,242,
238,229,100,129,2,121,167,232,243,245,240,229,242,233,239,114,
128,2,180,117,4,167,253,168,8,168,33,168,80,232,233,242,
225,231,225,238,97,128,48,139,235,225,244,225,235,225,238,97,
129,48,235,168,21,232,225,236,230,247,233,228,244,104,128,255,
153,112,2,168,39,168,74,229,101,2,168,46,168,60,237,225,
242,235,226,229,238,231,225,236,105,128,9,242,243,233,231,238,
226,229,238,231,225,236,105,128,9,243,233,225,104,128,246,221,
244,232,225,105,128,14,36,246,239,227,225,236,233,99,4,168,
103,168,113,168,120,168,131,226,229,238,231,225,236,105,128,9,
139,228,229,246,97,128,9,11,231,245,234,225,242,225,244,105,
128,10,139,246,239,247,229,236,243,233,231,110,3,168,147,168,
157,168,164,226,229,238,231,225,236,105,128,9,195,228,229,246,
97,128,9,67,231,245,234,225,242,225,244,105,128,10,195,115,
147,0,115,168,217,170,187,170,198,171,68,171,107,174,49,174,
60,176,203,179,85,179,131,179,158,180,93,180,160,181,193,181,
203,182,133,182,206,183,120,183,130,97,9,168,237,168,247,169,
12,169,84,169,109,169,120,169,145,169,177,169,217,226,229,238,
231,225,236,105,128,9,184,227,245,244,101,129,1,91,169,0,
228,239,244,225,227,227,229,238,116,128,30,101,100,5,169,24,
169,33,169,39,169,53,169,69,225,242,225,226,233,99,128,6,
53,229,246,97,128,9,56,230,233,238,225,236,225,242,225,226,
233,99,128,254,186,233,238,233,244,233,225,236,225,242,225,226,
233,99,128,254,187,237,229,228,233,225,236,225,242,225,226,233,
99,128,254,188,231,117,2,169,91,169,100,234,225,242,225,244,
105,128,10,184,242,237,245,235,232,105,128,10,56,232,233,242,
225,231,225,238,97,128,48,85,235,225,244,225,235,225,238,97,
129,48,181,169,133,232,225,236,230,247,233,228,244,104,128,255,
123,236,236,225,236,236,225,232,239,245,225,236,225,249,232,229,
247,225,243,225,236,236,225,237,225,242,225,226,233,99,128,253,
250,237,229,235,104,130,5,225,169,188,169,208,228,225,231,229,
243,104,129,251,65,169,199,232,229,226,242,229,119,128,251,65,
232,229,226,242,229,119,128,5,225,242,97,5,169,230,170,48,
170,56,170,106,170,114,97,5,169,242,169,250,170,2,170,33,
170,41,225,244,232,225,105,128,14,50,229,244,232,225,105,128,
14,65,233,237,225,233,109,2,170,12,170,23,225,236,225,233,
244,232,225,105,128,14,68,245,225,238,244,232,225,105,128,14,
67,237,244,232,225,105,128,14,51,244,232,225,105,128,14,48,
229,244,232,225,105,128,14,64,105,3,170,64,170,88,170,99,
105,2,170,70,170,81,236,229,230,244,244,232,225,105,128,248,
134,244,232,225,105,128,14,53,236,229,230,244,244,232,225,105,
128,248,133,244,232,225,105,128,14,52,239,244,232,225,105,128,
14,66,117,3,170,122,170,172,170,179,101,3,170,130,170,154,
170,165,101,2,170,136,170,147,236,229,230,244,244,232,225,105,
128,248,136,244,232,225,105,128,14,55,236,229,230,244,244,232,
225,105,128,248,135,244,232,225,105,128,14,54,244,232,225,105,
128,14,56,245,244,232,225,105,128,14,57,226,239,240,239,237,
239,230,111,128,49,25,99,5,170,210,170,231,170,240,171,33,
171,55,225,242,239,110,129,1,97,170,219,228,239,244,225,227,
227,229,238,116,128,30,103,229,228,233,236,236,97,128,1,95,
232,247,97,131,2,89,170,252,171,7,171,26,227,249,242,233,
236,236,233,99,128,4,217,228,233,229,242,229,243,233,243,227,
249,242,233,236,236,233,99,128,4,219,232,239,239,107,128,2,
90,233,242,99,2,171,41,171,46,236,101,128,36,226,245,237,
230,236,229,120,128,1,93,239,237,237,225,225,227,227,229,238,
116,128,2,25,228,239,116,2,171,76,171,85,225,227,227,229,
238,116,128,30,97,226,229,236,239,119,129,30,99,171,95,228,
239,244,225,227,227,229,238,116,128,30,105,101,9,171,127,171,
143,171,178,171,243,172,90,172,117,172,142,172,223,172,250,225,
231,245,236,236,226,229,236,239,247,227,237,98,128,3,60,99,
2,171,149,171,171,239,238,100,129,32,51,171,157,244,239,238,
229,227,232,233,238,229,243,101,128,2,202,244,233,239,110,128,
0,167,229,110,4,171,189,171,198,171,212,171,228,225,242,225,
226,233,99,128,6,51,230,233,238,225,236,225,242,225,226,233,
99,128,254,178,233,238,233,244,233,225,236,225,242,225,226,233,
99,128,254,179,237,229,228,233,225,236,225,242,225,226,233,99,
128,254,180,231,239,108,135,5,182,172,7,172,21,172,26,172,
35,172,50,172,66,172,77,49,2,172,13,172,17,51,128,5,
182,102,128,5,182,178,99,128,5,182,232,229,226,242,229,119,
128,5,182,238,225,242,242,239,247,232,229,226,242,229,119,128,
5,182,241,245,225,242,244,229,242,232,229,226,242,229,119,128,
5,182,244,225,232,229,226,242,229,119,128,5,146,247,233,228,
229,232,229,226,242,229,119,128,5,182,104,2,172,96,172,107,
225,242,237,229,238,233,225,110,128,5,125,233,242,225,231,225,
238,97,128,48,91,235,225,244,225,235,225,238,97,129,48,187,
172,130,232,225,236,230,247,233,228,244,104,128,255,126,237,105,
2,172,149,172,192,227,239,236,239,110,131,0,59,172,163,172,
172,172,184,225,242,225,226,233,99,128,6,27,237,239,238,239,
243,240,225,227,101,128,255,27,243,237,225,236,108,128,254,84,
246,239,233,227,229,228,237,225,242,235,235,225,238,97,129,48,
156,172,211,232,225,236,230,247,233,228,244,104,128,255,159,238,
116,2,172,230,172,240,233,243,241,245,225,242,101,128,51,34,
239,243,241,245,225,242,101,128,51,35,246,229,110,142,0,55,
173,28,173,37,173,47,173,77,173,84,173,94,173,119,173,146,
173,180,173,192,173,203,173,236,173,244,173,255,225,242,225,226,
233,99,128,6,103,226,229,238,231,225,236,105,128,9,237,227,
233,242,227,236,101,129,36,102,173,58,233,238,246,229,242,243,
229,243,225,238,243,243,229,242,233,102,128,39,144,228,229,246,
97,128,9,109,229,233,231,232,244,232,115,128,33,94,231,117,
2,173,101,173,110,234,225,242,225,244,105,128,10,237,242,237,
245,235,232,105,128,10,109,232,97,2,173,126,173,137,227,235,
225,242,225,226,233,99,128,6,103,238,231,250,232,239,117,128,
48,39,105,2,173,152,173,170,228,229,239,231,242,225,240,232,
233,227,240,225,242,229,110,128,50,38,238,230,229,242,233,239,
114,128,32,135,237,239,238,239,243,240,225,227,101,128,255,23,
239,236,228,243,244,249,236,101,128,247,55,112,2,173,209,173,
216,225,242,229,110,128,36,122,229,114,2,173,223,173,229,233,
239,100,128,36,142,243,233,225,110,128,6,247,242,239,237,225,
110,128,33,118,243,245,240,229,242,233,239,114,128,32,119,116,
2,174,5,174,43,229,229,110,2,174,13,174,22,227,233,242,
227,236,101,128,36,112,112,2,174,28,174,35,225,242,229,110,
128,36,132,229,242,233,239,100,128,36,152,232,225,105,128,14,
87,230,244,232,249,240,232,229,110,128,0,173,104,7,174,76,
175,50,175,61,175,75,176,20,176,33,176,197,97,6,174,90,
174,101,174,111,174,122,175,9,175,34,225,242,237,229,238,233,
225,110,128,5,119,226,229,238,231,225,236,105,128,9,182,227,
249,242,233,236,236,233,99,128,4,72,100,2,174,128,174,224,
228,97,4,174,139,174,148,174,179,174,193,225,242,225,226,233,
99,128,6,81,228,225,237,237,97,2,174,158,174,167,225,242,
225,226,233,99,128,252,97,244,225,238,225,242,225,226,233,99,
128,252,94,230,225,244,232,225,225,242,225,226,233,99,128,252,
96,235,225,243,242,97,2,174,203,174,212,225,242,225,226,233,
99,128,252,98,244,225,238,225,242,225,226,233,99,128,252,95,
101,132,37,146,174,236,174,243,174,251,175,4,228,225,242,107,
128,37,147,236,233,231,232,116,128,37,145,237,229,228,233,245,
109,128,37,146,246,97,128,9,54,231,117,2,175,16,175,25,
234,225,242,225,244,105,128,10,182,242,237,245,235,232,105,128,
10,54,236,243,232,229,236,229,244,232,229,226,242,229,119,128,
5,147,226,239,240,239,237,239,230,111,128,49,21,227,232,225,
227,249,242,233,236,236,233,99,128,4,73,101,4,175,85,175,
150,175,160,175,177,229,110,4,175,96,175,105,175,119,175,135,
225,242,225,226,233,99,128,6,52,230,233,238,225,236,225,242,
225,226,233,99,128,254,182,233,238,233,244,233,225,236,225,242,
225,226,233,99,128,254,183,237,229,228,233,225,236,225,242,225,
226,233,99,128,254,184,233,227,239,240,244,233,99,128,3,227,
241,229,108,129,32,170,175,168,232,229,226,242,229,119,128,32,
170,246,97,134,5,176,175,194,175,209,175,223,175,232,175,247,
176,7,49,2,175,200,175,205,177,53,128,5,176,53,128,5,
176,50,2,175,215,175,219,50,128,5,176,101,128,5,176,232,
229,226,242,229,119,128,5,176,238,225,242,242,239,247,232,229,
226,242,229,119,128,5,176,241,245,225,242,244,229,242,232,229,
226,242,229,119,128,5,176,247,233,228,229,232,229,226,242,229,
119,128,5,176,232,225,227,249,242,233,236,236,233,99,128,4,
187,105,2,176,39,176,50,237,225,227,239,240,244,233,99,128,
3,237,110,131,5,233,176,60,176,143,176,152,100,2,176,66,
176,132,225,231,229,243,104,130,251,73,176,78,176,87,232,229,
226,242,229,119,128,251,73,115,2,176,93,176,113,232,233,238,
228,239,116,129,251,44,176,104,232,229,226,242,229,119,128,251,
44,233,238,228,239,116,129,251,45,176,123,232,229,226,242,229,
119,128,251,45,239,244,232,229,226,242,229,119,128,5,193,232,
229,226,242,229,119,128,5,233,115,2,176,158,176,178,232,233,
238,228,239,116,129,251,42,176,169,232,229,226,242,229,119,128,
251,42,233,238,228,239,116,129,251,43,176,188,232,229,226,242,
229,119,128,251,43,239,239,107,128,2,130,105,8,176,221,177,
9,177,20,177,45,177,75,177,83,177,96,178,11,231,237,97,
131,3,195,176,233,176,237,176,245,49,128,3,194,230,233,238,
225,108,128,3,194,236,245,238,225,244,229,243,249,237,226,239,
236,231,242,229,229,107,128,3,242,232,233,242,225,231,225,238,
97,128,48,87,235,225,244,225,235,225,238,97,129,48,183,177,
33,232,225,236,230,247,233,228,244,104,128,255,124,236,245,113,
2,177,53,177,62,232,229,226,242,229,119,128,5,189,236,229,
230,244,232,229,226,242,229,119,128,5,189,237,233,236,225,114,
128,34,60,238,228,239,244,232,229,226,242,229,119,128,5,194,
239,115,6,177,111,177,146,177,178,177,206,177,220,177,252,97,
2,177,117,177,132,227,233,242,227,236,229,235,239,242,229,225,
110,128,50,116,240,225,242,229,238,235,239,242,229,225,110,128,
50,20,227,105,2,177,153,177,165,229,245,227,235,239,242,229,
225,110,128,49,126,242,227,236,229,235,239,242,229,225,110,128,
50,102,107,2,177,184,177,198,233,249,229,239,235,235,239,242,
229,225,110,128,49,122,239,242,229,225,110,128,49,69,238,233,
229,245,238,235,239,242,229,225,110,128,49,123,112,2,177,226,
177,239,225,242,229,238,235,239,242,229,225,110,128,50,6,233,
229,245,240,235,239,242,229,225,110,128,49,125,244,233,235,229,
245,244,235,239,242,229,225,110,128,49,124,120,141,0,54,178,
41,178,50,178,60,178,90,178,97,178,122,178,149,178,183,178,
195,178,206,178,239,178,247,179,2,225,242,225,226,233,99,128,
6,102,226,229,238,231,225,236,105,128,9,236,227,233,242,227,
236,101,129,36,101,178,71,233,238,246,229,242,243,229,243,225,
238,243,243,229,242,233,102,128,39,143,228,229,246,97,128,9,
108,231,117,2,178,104,178,113,234,225,242,225,244,105,128,10,
236,242,237,245,235,232,105,128,10,108,232,97,2,178,129,178,
140,227,235,225,242,225,226,233,99,128,6,102,238,231,250,232,
239,117,128,48,38,105,2,178,155,178,173,228,229,239,231,242,
225,240,232,233,227,240,225,242,229,110,128,50,37,238,230,229,
242,233,239,114,128,32,134,237,239,238,239,243,240,225,227,101,
128,255,22,239,236,228,243,244,249,236,101,128,247,54,112,2,
178,212,178,219,225,242,229,110,128,36,121,229,114,2,178,226,
178,232,233,239,100,128,36,141,243,233,225,110,128,6,246,242,
239,237,225,110,128,33,117,243,245,240,229,242,233,239,114,128,
32,118,116,2,179,8,179,79,229,229,110,2,179,16,179,58,
99,2,179,22,179,30,233,242,227,236,101,128,36,111,245,242,
242,229,238,227,249,228,229,238,239,237,233,238,225,244,239,242,
226,229,238,231,225,236,105,128,9,249,112,2,179,64,179,71,
225,242,229,110,128,36,131,229,242,233,239,100,128,36,151,232,
225,105,128,14,86,108,2,179,91,179,111,225,243,104,129,0,
47,179,99,237,239,238,239,243,240,225,227,101,128,255,15,239,
238,103,129,1,127,179,119,228,239,244,225,227,227,229,238,116,
128,30,155,109,2,179,137,179,147,233,236,229,230,225,227,101,
128,38,58,239,238,239,243,240,225,227,101,128,255,83,111,6,
179,172,179,222,179,233,180,2,180,47,180,58,102,2,179,178,
179,192,240,225,243,245,241,232,229,226,242,229,119,128,5,195,
116,2,179,198,179,207,232,249,240,232,229,110,128,0,173,243,
233,231,238,227,249,242,233,236,236,233,99,128,4,76,232,233,
242,225,231,225,238,97,128,48,93,235,225,244,225,235,225,238,
97,129,48,189,179,246,232,225,236,230,247,233,228,244,104,128,
255,127,236,233,228,245,115,2,180,12,180,29,236,239,238,231,
239,246,229,242,236,225,249,227,237,98,128,3,56,243,232,239,
242,244,239,246,229,242,236,225,249,227,237,98,128,3,55,242,
245,243,233,244,232,225,105,128,14,41,115,3,180,66,180,76,
180,84,225,236,225,244,232,225,105,128,14,40,239,244,232,225,
105,128,14,11,245,225,244,232,225,105,128,14,42,240,97,3,
180,102,180,122,180,154,227,101,129,0,32,180,109,232,225,227,
235,225,242,225,226,233,99,128,0,32,228,101,129,38,96,180,
129,243,245,233,116,2,180,138,180,146,226,236,225,227,107,128,
38,96,247,232,233,244,101,128,38,100,242,229,110,128,36,174,
241,245,225,242,101,11,180,188,180,199,180,213,180,238,180,255,
181,25,181,40,181,73,181,100,181,156,181,171,226,229,236,239,
247,227,237,98,128,3,59,99,2,180,205,180,209,99,128,51,
196,109,128,51,157,228,233,225,231,239,238,225,236,227,242,239,
243,243,232,225,244,227,232,230,233,236,108,128,37,169,232,239,
242,233,250,239,238,244,225,236,230,233,236,108,128,37,164,107,
2,181,5,181,9,103,128,51,143,109,129,51,158,181,15,227,
225,240,233,244,225,108,128,51,206,108,2,181,31,181,35,110,
128,51,209,239,103,128,51,210,109,4,181,50,181,54,181,59,
181,63,103,128,51,142,233,108,128,51,213,109,128,51,156,243,
241,245,225,242,229,100,128,51,161,239,242,244,232,239,231,239,
238,225,236,227,242,239,243,243,232,225,244,227,232,230,233,236,
108,128,37,166,245,240,240,229,114,2,181,110,181,133,236,229,
230,244,244,239,236,239,247,229,242,242,233,231,232,244,230,233,
236,108,128,37,167,242,233,231,232,244,244,239,236,239,247,229,
242,236,229,230,244,230,233,236,108,128,37,168,246,229,242,244,
233,227,225,236,230,233,236,108,128,37,165,247,232,233,244,229,
247,233,244,232,243,237,225,236,236,226,236,225,227,107,128,37,
163,242,243,241,245,225,242,101,128,51,219,115,2,181,209,182,
123,97,4,181,219,181,229,181,236,181,247,226,229,238,231,225,
236,105,128,9,183,228,229,246,97,128,9,55,231,245,234,225,
242,225,244,105,128,10,183,238,103,8,182,10,182,24,182,38,
182,52,182,67,182,81,182,95,182,108,227,233,229,245,227,235,
239,242,229,225,110,128,49,73,232,233,229,245,232,235,239,242,
229,225,110,128,49,133,233,229,245,238,231,235,239,242,229,225,
110,128,49,128,235,233,249,229,239,235,235,239,242,229,225,110,
128,49,50,238,233,229,245,238,235,239,242,229,225,110,128,49,
101,240,233,229,245,240,235,239,242,229,225,110,128,49,67,243,
233,239,243,235,239,242,229,225,110,128,49,70,244,233,235,229,
245,244,235,239,242,229,225,110,128,49,56,245,240,229,242,233,
239,114,128,246,242,116,2,182,139,182,162,229,242,236,233,238,
103,129,0,163,182,150,237,239,238,239,243,240,225,227,101,128,
255,225,242,239,235,101,2,182,171,182,188,236,239,238,231,239,
246,229,242,236,225,249,227,237,98,128,3,54,243,232,239,242,
244,239,246,229,242,236,225,249,227,237,98,128,3,53,117,7,
182,222,182,254,183,20,183,31,183,72,183,82,183,86,226,243,
229,116,130,34,130,182,233,182,244,238,239,244,229,241,245,225,
108,128,34,138,239,242,229,241,245,225,108,128,34,134,99,2,
183,4,183,12,227,229,229,228,115,128,34,123,232,244,232,225,
116,128,34,11,232,233,242,225,231,225,238,97,128,48,89,107,
2,183,37,183,61,225,244,225,235,225,238,97,129,48,185,183,
49,232,225,236,230,247,233,228,244,104,128,255,125,245,238,225,
242,225,226,233,99,128,6,82,237,237,225,244,233,239,110,128,
34,17,110,128,38,60,240,229,242,243,229,116,130,34,131,183,
99,183,110,238,239,244,229,241,245,225,108,128,34,139,239,242,
229,241,245,225,108,128,34,135,246,243,241,245,225,242,101,128,
51,220,249,239,245,247,225,229,242,225,243,241,245,225,242,101,
128,51,124,116,144,0,116,183,183,184,192,184,213,185,100,185,
140,187,188,191,70,192,145,192,157,192,169,193,202,193,227,194,
57,194,237,195,165,195,255,97,10,183,205,183,215,183,236,183,
243,184,12,184,90,184,107,184,132,184,146,184,150,226,229,238,
231,225,236,105,128,9,164,227,107,2,183,222,183,229,228,239,
247,110,128,34,164,236,229,230,116,128,34,163,228,229,246,97,
128,9,36,231,117,2,183,250,184,3,234,225,242,225,244,105,
128,10,164,242,237,245,235,232,105,128,10,36,104,4,184,22,
184,31,184,45,184,75,225,242,225,226,233,99,128,6,55,230,
233,238,225,236,225,242,225,226,233,99,128,254,194,105,2,184,
51,184,66,238,233,244,233,225,236,225,242,225,226,233,99,128,
254,195,242,225,231,225,238,97,128,48,95,237,229,228,233,225,
236,225,242,225,226,233,99,128,254,196,233,243,249,239,245,229,
242,225,243,241,245,225,242,101,128,51,125,235,225,244,225,235,
225,238,97,129,48,191,184,120,232,225,236,230,247,233,228,244,
104,128,255,128,244,247,229,229,236,225,242,225,226,233,99,128,
6,64,117,128,3,196,118,130,5,234,184,158,184,183,228,225,
231,229,115,129,251,74,184,168,104,129,251,74,184,174,232,229,
226,242,229,119,128,251,74,232,229,226,242,229,119,128,5,234,
98,2,184,198,184,203,225,114,128,1,103,239,240,239,237,239,
230,111,128,49,10,99,6,184,227,184,234,184,241,184,250,185,
60,185,87,225,242,239,110,128,1,101,227,245,242,108,128,2,
168,229,228,233,236,236,97,128,1,99,232,229,104,4,185,6,
185,15,185,29,185,45,225,242,225,226,233,99,128,6,134,230,
233,238,225,236,225,242,225,226,233,99,128,251,123,233,238,233,
244,233,225,236,225,242,225,226,233,99,128,251,124,237,229,228,
233,225,236,225,242,225,226,233,99,128,251,125,233,242,99,2,
185,68,185,73,236,101,128,36,227,245,237,230,236,229,248,226,
229,236,239,119,128,30,113,239,237,237,225,225,227,227,229,238,
116,128,1,99,100,2,185,106,185,116,233,229,242,229,243,233,
115,128,30,151,239,116,2,185,123,185,132,225,227,227,229,238,
116,128,30,107,226,229,236,239,119,128,30,109,101,9,185,160,
185,171,185,191,186,201,186,226,187,34,187,101,187,106,187,158,
227,249,242,233,236,236,233,99,128,4,66,228,229,243,227,229,
238,228,229,242,227,249,242,233,236,236,233,99,128,4,173,104,
7,185,207,185,216,185,230,186,14,186,44,186,85,186,183,225,
242,225,226,233,99,128,6,42,230,233,238,225,236,225,242,225,
226,233,99,128,254,150,232,225,232,105,2,185,239,185,254,238,
233,244,233,225,236,225,242,225,226,233,99,128,252,162,243,239,
236,225,244,229,228,225,242,225,226,233,99,128,252,12,105,2,
186,20,186,35,238,233,244,233,225,236,225,242,225,226,233,99,
128,254,151,242,225,231,225,238,97,128,48,102,234,229,229,237,
105,2,186,54,186,69,238,233,244,233,225,236,225,242,225,226,
233,99,128,252,161,243,239,236,225,244,229,228,225,242,225,226,
233,99,128,252,11,109,2,186,91,186,125,225,242,226,245,244,
97,2,186,102,186,111,225,242,225,226,233,99,128,6,41,230,
233,238,225,236,225,242,225,226,233,99,128,254,148,101,2,186,
131,186,144,228,233,225,236,225,242,225,226,233,99,128,254,152,
229,237,105,2,186,152,186,167,238,233,244,233,225,236,225,242,
225,226,233,99,128,252,164,243,239,236,225,244,229,228,225,242,
225,226,233,99,128,252,14,238,239,239,238,230,233,238,225,236,
225,242,225,226,233,99,128,252,115,235,225,244,225,235,225,238,
97,129,48,198,186,214,232,225,236,230,247,233,228,244,104,128,
255,131,108,2,186,232,186,251,229,240,232,239,238,101,129,33,
33,186,243,226,236,225,227,107,128,38,14,233,243,232,97,2,
187,4,187,19,231,229,228,239,236,225,232,229,226,242,229,119,
128,5,160,241,229,244,225,238,225,232,229,226,242,229,119,128,
5,169,110,4,187,44,187,53,187,72,187,93,227,233,242,227,
236,101,128,36,105,233,228,229,239,231,242,225,240,232,233,227,
240,225,242,229,110,128,50,41,112,2,187,78,187,85,225,242,
229,110,128,36,125,229,242,233,239,100,128,36,145,242,239,237,
225,110,128,33,121,243,104,128,2,167,116,131,5,216,187,116,
187,136,187,145,228,225,231,229,243,104,129,251,56,187,127,232,
229,226,242,229,119,128,251,56,232,229,226,242,229,119,128,5,
216,243,229,227,249,242,233,236,236,233,99,128,4,181,246,233,
114,2,187,166,187,175,232,229,226,242,229,119,128,5,155,236,
229,230,244,232,229,226,242,229,119,128,5,155,104,6,187,202,
188,98,188,220,189,96,190,3,191,60,97,5,187,214,187,224,
187,231,188,0,188,29,226,229,238,231,225,236,105,128,9,165,
228,229,246,97,128,9,37,231,117,2,187,238,187,247,234,225,
242,225,244,105,128,10,165,242,237,245,235,232,105,128,10,37,
108,2,188,6,188,15,225,242,225,226,233,99,128,6,48,230,
233,238,225,236,225,242,225,226,233,99,128,254,172,238,244,232,
225,235,232,225,116,3,188,44,188,75,188,82,236,239,119,2,
188,52,188,63,236,229,230,244,244,232,225,105,128,248,152,242,
233,231,232,244,244,232,225,105,128,248,151,244,232,225,105,128,
14,76,245,240,240,229,242,236,229,230,244,244,232,225,105,128,
248,150,101,3,188,106,188,170,188,193,104,4,188,116,188,125,
188,139,188,155,225,242,225,226,233,99,128,6,43,230,233,238,
225,236,225,242,225,226,233,99,128,254,154,233,238,233,244,233,
225,236,225,242,225,226,233,99,128,254,155,237,229,228,233,225,
236,225,242,225,226,233,99,128,254,156,242,101,2,188,177,188,
186,229,248,233,243,244,115,128,34,3,230,239,242,101,128,34,
52,244,97,130,3,184,188,202,188,206,49,128,3,209,243,249,
237,226,239,236,231,242,229,229,107,128,3,209,105,2,188,226,
189,56,229,245,244,104,4,188,239,189,18,189,33,189,42,97,
2,188,245,189,4,227,233,242,227,236,229,235,239,242,229,225,
110,128,50,121,240,225,242,229,238,235,239,242,229,225,110,128,
50,25,227,233,242,227,236,229,235,239,242,229,225,110,128,50,
107,235,239,242,229,225,110,128,49,76,240,225,242,229,238,235,
239,242,229,225,110,128,50,11,242,244,229,229,110,2,189,66,
189,75,227,233,242,227,236,101,128,36,108,112,2,189,81,189,
88,225,242,229,110,128,36,128,229,242,233,239,100,128,36,148,
111,6,189,110,189,127,189,132,189,146,189,151,189,204,238,225,
238,231,237,239,238,244,232,239,244,232,225,105,128,14,17,239,
107,128,1,173,240,232,245,244,232,225,239,244,232,225,105,128,
14,18,242,110,128,0,254,244,104,3,189,160,189,184,189,194,
97,2,189,166,189,176,232,225,238,244,232,225,105,128,14,23,
238,244,232,225,105,128,14,16,239,238,231,244,232,225,105,128,
14,24,245,238,231,244,232,225,105,128,14,22,245,243,225,238,
100,2,189,214,189,225,227,249,242,233,236,236,233,99,128,4,
130,243,243,229,240,225,242,225,244,239,114,2,189,240,189,249,
225,242,225,226,233,99,128,6,108,240,229,242,243,233,225,110,
128,6,108,242,229,101,144,0,51,190,41,190,50,190,60,190,
90,190,97,190,107,190,132,190,159,190,193,190,205,190,224,190,
235,191,12,191,34,191,42,191,53,225,242,225,226,233,99,128,
6,99,226,229,238,231,225,236,105,128,9,233,227,233,242,227,
236,101,129,36,98,190,71,233,238,246,229,242,243,229,243,225,
238,243,243,229,242,233,102,128,39,140,228,229,246,97,128,9,
105,229,233,231,232,244,232,115,128,33,92,231,117,2,190,114,
190,123,234,225,242,225,244,105,128,10,233,242,237,245,235,232,
105,128,10,105,232,97,2,190,139,190,150,227,235,225,242,225,
226,233,99,128,6,99,238,231,250,232,239,117,128,48,35,105,
2,190,165,190,183,228,229,239,231,242,225,240,232,233,227,240,
225,242,229,110,128,50,34,238,230,229,242,233,239,114,128,32,
131,237,239,238,239,243,240,225,227,101,128,255,19,238,245,237,
229,242,225,244,239,242,226,229,238,231,225,236,105,128,9,246,
239,236,228,243,244,249,236,101,128,247,51,112,2,190,241,190,
248,225,242,229,110,128,36,118,229,114,2,190,255,191,5,233,
239,100,128,36,138,243,233,225,110,128,6,243,241,245,225,242,
244,229,242,115,129,0,190,191,25,229,237,228,225,243,104,128,
246,222,242,239,237,225,110,128,33,114,243,245,240,229,242,233,
239,114,128,0,179,244,232,225,105,128,14,83,250,243,241,245,
225,242,101,128,51,148,105,7,191,86,191,97,191,212,192,54,
192,66,192,115,192,132,232,233,242,225,231,225,238,97,128,48,
97,107,2,191,103,191,127,225,244,225,235,225,238,97,129,48,
193,191,115,232,225,236,230,247,233,228,244,104,128,255,129,229,
245,116,4,191,139,191,174,191,189,191,198,97,2,191,145,191,
160,227,233,242,227,236,229,235,239,242,229,225,110,128,50,112,
240,225,242,229,238,235,239,242,229,225,110,128,50,16,227,233,
242,227,236,229,235,239,242,229,225,110,128,50,98,235,239,242,
229,225,110,128,49,55,240,225,242,229,238,235,239,242,229,225,
110,128,50,2,236,228,101,133,2,220,191,228,191,239,192,0,
192,12,192,40,226,229,236,239,247,227,237,98,128,3,48,99,
2,191,245,191,250,237,98,128,3,3,239,237,98,128,3,3,
228,239,245,226,236,229,227,237,98,128,3,96,111,2,192,18,
192,28,240,229,242,225,244,239,114,128,34,60,246,229,242,236,
225,249,227,237,98,128,3,52,246,229,242,244,233,227,225,236,
227,237,98,128,3,62,237,229,243,227,233,242,227,236,101,128,
34,151,112,2,192,72,192,102,229,232,97,2,192,80,192,89,
232,229,226,242,229,119,128,5,150,236,229,230,244,232,229,226,
242,229,119,128,5,150,240,233,231,245,242,237,245,235,232,105,
128,10,112,244,236,239,227,249,242,233,236,236,233,227,227,237,
98,128,4,131,247,238,225,242,237,229,238,233,225,110,128,5,
127,236,233,238,229,226,229,236,239,119,128,30,111,237,239,238,
239,243,240,225,227,101,128,255,84,111,7,192,185,192,196,192,
207,192,232,193,96,193,108,193,192,225,242,237,229,238,233,225,
110,128,5,105,232,233,242,225,231,225,238,97,128,48,104,235,
225,244,225,235,225,238,97,129,48,200,192,220,232,225,236,230,
247,233,228,244,104,128,255,132,110,3,192,240,193,82,193,87,
101,4,192,250,193,63,193,70,193,76,226,225,114,4,193,6,
193,35,193,45,193,54,229,248,244,242,97,2,193,16,193,26,
232,233,231,232,237,239,100,128,2,229,236,239,247,237,239,100,
128,2,233,232,233,231,232,237,239,100,128,2,230,236,239,247,
237,239,100,128,2,232,237,233,228,237,239,100,128,2,231,230,
233,246,101,128,1,189,243,233,120,128,1,133,244,247,111,128,
1,168,239,115,128,3,132,243,241,245,225,242,101,128,51,39,
240,225,244,225,235,244,232,225,105,128,14,15,242,244,239,233,
243,229,243,232,229,236,236,226,242,225,227,235,229,116,2,193,
131,193,161,236,229,230,116,130,48,20,193,142,193,150,243,237,
225,236,108,128,254,93,246,229,242,244,233,227,225,108,128,254,
57,242,233,231,232,116,130,48,21,193,173,193,181,243,237,225,
236,108,128,254,94,246,229,242,244,233,227,225,108,128,254,58,
244,225,239,244,232,225,105,128,14,21,240,97,2,193,209,193,
221,236,225,244,225,236,232,239,239,107,128,1,171,242,229,110,
128,36,175,114,3,193,235,194,10,194,25,225,228,229,237,225,
242,107,129,33,34,193,247,115,2,193,253,194,3,225,238,115,
128,248,234,229,242,233,102,128,246,219,229,244,242,239,230,236,
229,248,232,239,239,107,128,2,136,233,225,103,4,194,37,194,
42,194,47,194,52,228,110,128,37,188,236,102,128,37,196,242,
116,128,37,186,245,112,128,37,178,115,132,2,166,194,69,194,
108,194,214,194,227,225,228,105,130,5,230,194,79,194,99,228,
225,231,229,243,104,129,251,70,194,90,232,229,226,242,229,119,
128,251,70,232,229,226,242,229,119,128,5,230,101,2,194,114,
194,125,227,249,242,233,236,236,233,99,128,4,70,242,101,134,
5,181,194,142,194,156,194,161,194,170,194,185,194,201,49,2,
194,148,194,152,50,128,5,181,101,128,5,181,178,98,128,5,
181,232,229,226,242,229,119,128,5,181,238,225,242,242,239,247,
232,229,226,242,229,119,128,5,181,241,245,225,242,244,229,242,
232,229,226,242,229,119,128,5,181,247,233,228,229,232,229,226,
242,229,119,128,5,181,232,229,227,249,242,233,236,236,233,99,
128,4,91,245,240,229,242,233,239,114,128,246,243,116,4,194,
247,195,41,195,106,195,157,97,3,194,255,195,9,195,16,226,
229,238,231,225,236,105,128,9,159,228,229,246,97,128,9,31,
231,117,2,195,23,195,32,234,225,242,225,244,105,128,10,159,
242,237,245,235,232,105,128,10,31,229,104,4,195,52,195,61,
195,75,195,91,225,242,225,226,233,99,128,6,121,230,233,238,
225,236,225,242,225,226,233,99,128,251,103,233,238,233,244,233,
225,236,225,242,225,226,233,99,128,251,104,237,229,228,233,225,
236,225,242,225,226,233,99,128,251,105,232,97,3,195,115,195,
125,195,132,226,229,238,231,225,236,105,128,9,160,228,229,246,
97,128,9,32,231,117,2,195,139,195,148,234,225,242,225,244,
105,128,10,160,242,237,245,235,232,105,128,10,32,245,242,238,
229,100,128,2,135,117,3,195,173,195,184,195,209,232,233,242,
225,231,225,238,97,128,48,100,235,225,244,225,235,225,238,97,
129,48,196,195,197,232,225,236,230,247,233,228,244,104,128,255,
130,243,237,225,236,108,2,195,219,195,230,232,233,242,225,231,
225,238,97,128,48,99,235,225,244,225,235,225,238,97,129,48,
195,195,243,232,225,236,230,247,233,228,244,104,128,255,111,119,
2,196,5,196,110,101,2,196,11,196,59,236,246,101,3,196,
21,196,30,196,51,227,233,242,227,236,101,128,36,107,112,2,
196,36,196,43,225,242,229,110,128,36,127,229,242,233,239,100,
128,36,147,242,239,237,225,110,128,33,123,238,244,121,3,196,
69,196,78,196,89,227,233,242,227,236,101,128,36,115,232,225,
238,231,250,232,239,117,128,83,68,112,2,196,95,196,102,225,
242,229,110,128,36,135,229,242,233,239,100,128,36,155,111,142,
0,50,196,142,196,151,196,161,196,191,196,243,197,12,197,39,
197,73,197,85,197,104,197,115,197,148,197,156,197,180,225,242,
225,226,233,99,128,6,98,226,229,238,231,225,236,105,128,9,
232,227,233,242,227,236,101,129,36,97,196,172,233,238,246,229,
242,243,229,243,225,238,243,243,229,242,233,102,128,39,139,100,
2,196,197,196,203,229,246,97,128,9,104,239,116,2,196,210,
196,221,229,238,236,229,225,228,229,114,128,32,37,236,229,225,
228,229,114,129,32,37,196,232,246,229,242,244,233,227,225,108,
128,254,48,231,117,2,196,250,197,3,234,225,242,225,244,105,
128,10,232,242,237,245,235,232,105,128,10,104,232,97,2,197,
19,197,30,227,235,225,242,225,226,233,99,128,6,98,238,231,
250,232,239,117,128,48,34,105,2,197,45,197,63,228,229,239,
231,242,225,240,232,233,227,240,225,242,229,110,128,50,33,238,
230,229,242,233,239,114,128,32,130,237,239,238,239,243,240,225,
227,101,128,255,18,238,245,237,229,242,225,244,239,242,226,229,
238,231,225,236,105,128,9,245,239,236,228,243,244,249,236,101,
128,247,50,112,2,197,121,197,128,225,242,229,110,128,36,117,
229,114,2,197,135,197,141,233,239,100,128,36,137,243,233,225,
110,128,6,242,242,239,237,225,110,128,33,113,115,2,197,162,
197,170,244,242,239,235,101,128,1,187,245,240,229,242,233,239,
114,128,0,178,244,104,2,197,187,197,192,225,105,128,14,82,
233,242,228,115,128,33,84,117,145,0,117,197,237,197,245,198,
30,198,87,198,225,199,6,199,129,199,145,199,196,200,10,200,
91,200,100,200,219,200,243,201,95,201,123,201,237,225,227,245,
244,101,128,0,250,98,4,197,255,198,4,198,13,198,23,225,
114,128,2,137,229,238,231,225,236,105,128,9,137,239,240,239,
237,239,230,111,128,49,40,242,229,246,101,128,1,109,99,3,
198,38,198,45,198,77,225,242,239,110,128,1,212,233,242,99,
2,198,53,198,58,236,101,128,36,228,245,237,230,236,229,120,
129,0,251,198,69,226,229,236,239,119,128,30,119,249,242,233,
236,236,233,99,128,4,67,100,5,198,99,198,110,198,133,198,
139,198,215,225,244,244,225,228,229,246,97,128,9,81,226,108,
2,198,117,198,125,225,227,245,244,101,128,1,113,231,242,225,
246,101,128,2,21,229,246,97,128,9,9,233,229,242,229,243,
233,115,133,0,252,198,159,198,167,198,175,198,198,198,206,225,
227,245,244,101,128,1,216,226,229,236,239,119,128,30,115,99,
2,198,181,198,188,225,242,239,110,128,1,218,249,242,233,236,
236,233,99,128,4,241,231,242,225,246,101,128,1,220,237,225,
227,242,239,110,128,1,214,239,244,226,229,236,239,119,128,30,
229,103,2,198,231,198,238,242,225,246,101,128,0,249,117,2,
198,244,198,253,234,225,242,225,244,105,128,10,137,242,237,245,
235,232,105,128,10,9,104,3,199,14,199,24,199,102,233,242,
225,231,225,238,97,128,48,70,111,2,199,30,199,40,239,235,
225,226,239,246,101,128,30,231,242,110,133,1,176,199,55,199,
63,199,74,199,82,199,94,225,227,245,244,101,128,30,233,228,
239,244,226,229,236,239,119,128,30,241,231,242,225,246,101,128,
30,235,232,239,239,235,225,226,239,246,101,128,30,237,244,233,
236,228,101,128,30,239,245,238,231,225,242,245,237,236,225,245,
116,129,1,113,199,118,227,249,242,233,236,236,233,99,128,4,
243,233,238,246,229,242,244,229,228,226,242,229,246,101,128,2,
23,107,3,199,153,199,177,199,188,225,244,225,235,225,238,97,
129,48,166,199,165,232,225,236,230,247,233,228,244,104,128,255,
115,227,249,242,233,236,236,233,99,128,4,121,239,242,229,225,
110,128,49,92,109,2,199,202,199,255,97,2,199,208,199,241,
227,242,239,110,130,1,107,199,219,199,230,227,249,242,233,236,
236,233,99,128,4,239,228,233,229,242,229,243,233,115,128,30,
123,244,242,225,231,245,242,237,245,235,232,105,128,10,65,239,
238,239,243,240,225,227,101,128,255,85,110,2,200,16,200,71,
228,229,242,243,227,239,242,101,132,0,95,200,35,200,41,200,
53,200,64,228,226,108,128,32,23,237,239,238,239,243,240,225,
227,101,128,255,63,246,229,242,244,233,227,225,108,128,254,51,
247,225,246,121,128,254,79,105,2,200,77,200,82,239,110,128,
34,42,246,229,242,243,225,108,128,34,0,239,231,239,238,229,
107,128,1,115,112,5,200,112,200,119,200,127,200,142,200,193,
225,242,229,110,128,36,176,226,236,239,227,107,128,37,128,240,
229,242,228,239,244,232,229,226,242,229,119,128,5,196,243,233,
236,239,110,131,3,197,200,156,200,177,200,185,228,233,229,242,
229,243,233,115,129,3,203,200,169,244,239,238,239,115,128,3,
176,236,225,244,233,110,128,2,138,244,239,238,239,115,128,3,
205,244,225,227,107,2,200,202,200,213,226,229,236,239,247,227,
237,98,128,3,29,237,239,100,128,2,212,114,2,200,225,200,
237,225,231,245,242,237,245,235,232,105,128,10,115,233,238,103,
128,1,111,115,3,200,251,201,10,201,55,232,239,242,244,227,
249,242,233,236,236,233,99,128,4,94,237,225,236,108,2,201,
19,201,30,232,233,242,225,231,225,238,97,128,48,69,235,225,
244,225,235,225,238,97,129,48,165,201,43,232,225,236,230,247,
233,228,244,104,128,255,105,244,242,225,233,231,232,116,2,201,
67,201,78,227,249,242,233,236,236,233,99,128,4,175,243,244,
242,239,235,229,227,249,242,233,236,236,233,99,128,4,177,244,
233,236,228,101,130,1,105,201,107,201,115,225,227,245,244,101,
128,30,121,226,229,236,239,119,128,30,117,117,5,201,135,201,
145,201,152,201,177,201,193,226,229,238,231,225,236,105,128,9,
138,228,229,246,97,128,9,10,231,117,2,201,159,201,168,234,
225,242,225,244,105,128,10,138,242,237,245,235,232,105,128,10,
10,237,225,244,242,225,231,245,242,237,245,235,232,105,128,10,
66,246,239,247,229,236,243,233,231,110,3,201,209,201,219,201,
226,226,229,238,231,225,236,105,128,9,194,228,229,246,97,128,
9,66,231,245,234,225,242,225,244,105,128,10,194,246,239,247,
229,236,243,233,231,110,3,201,253,202,7,202,14,226,229,238,
231,225,236,105,128,9,193,228,229,246,97,128,9,65,231,245,
234,225,242,225,244,105,128,10,193,118,139,0,118,202,51,202,
199,202,208,202,219,203,148,203,155,203,253,204,9,204,109,204,
117,204,138,97,4,202,61,202,68,202,93,202,104,228,229,246,
97,128,9,53,231,117,2,202,75,202,84,234,225,242,225,244,
105,128,10,181,242,237,245,235,232,105,128,10,53,235,225,244,
225,235,225,238,97,128,48,247,118,132,5,213,202,116,202,143,
202,175,202,187,228,225,231,229,243,104,130,251,53,202,129,202,
134,182,53,128,251,53,232,229,226,242,229,119,128,251,53,104,
2,202,149,202,157,229,226,242,229,119,128,5,213,239,236,225,
109,129,251,75,202,166,232,229,226,242,229,119,128,251,75,246,
225,246,232,229,226,242,229,119,128,5,240,249,239,228,232,229,
226,242,229,119,128,5,241,227,233,242,227,236,101,128,36,229,
228,239,244,226,229,236,239,119,128,30,127,101,6,202,233,202,
244,203,52,203,63,203,69,203,136,227,249,242,233,236,236,233,
99,128,4,50,104,4,202,254,203,7,203,21,203,37,225,242,
225,226,233,99,128,6,164,230,233,238,225,236,225,242,225,226,
233,99,128,251,107,233,238,233,244,233,225,236,225,242,225,226,
233,99,128,251,108,237,229,228,233,225,236,225,242,225,226,233,
99,128,251,109,235,225,244,225,235,225,238,97,128,48,249,238,
245,115,128,38,64,242,244,233,227,225,108,2,203,80,203,86,
226,225,114,128,0,124,236,233,238,101,4,203,99,203,110,203,
121,203,130,225,226,239,246,229,227,237,98,128,3,13,226,229,
236,239,247,227,237,98,128,3,41,236,239,247,237,239,100,128,
2,204,237,239,100,128,2,200,247,225,242,237,229,238,233,225,
110,128,5,126,232,239,239,107,128,2,139,105,3,203,163,203,
174,203,213,235,225,244,225,235,225,238,97,128,48,248,242,225,
237,97,3,203,185,203,195,203,202,226,229,238,231,225,236,105,
128,9,205,228,229,246,97,128,9,77,231,245,234,225,242,225,
244,105,128,10,205,243,225,242,231,97,3,203,225,203,235,203,
242,226,229,238,231,225,236,105,128,9,131,228,229,246,97,128,
9,3,231,245,234,225,242,225,244,105,128,10,131,237,239,238,
239,243,240,225,227,101,128,255,86,111,3,204,17,204,28,204,
98,225,242,237,229,238,233,225,110,128,5,120,233,227,229,100,
2,204,37,204,73,233,244,229,242,225,244,233,239,110,2,204,
51,204,62,232,233,242,225,231,225,238,97,128,48,158,235,225,
244,225,235,225,238,97,128,48,254,237,225,242,235,235,225,238,
97,129,48,155,204,86,232,225,236,230,247,233,228,244,104,128,
255,158,235,225,244,225,235,225,238,97,128,48,250,240,225,242,
229,110,128,36,177,116,2,204,123,204,130,233,236,228,101,128,
30,125,245,242,238,229,100,128,2,140,117,2,204,144,204,155,
232,233,242,225,231,225,238,97,128,48,148,235,225,244,225,235,
225,238,97,128,48,244,119,143,0,119,204,200,205,177,205,187,
205,210,205,250,206,61,206,69,208,40,208,81,208,93,208,168,
208,176,208,183,208,194,208,203,97,8,204,218,204,225,204,235,
204,246,205,28,205,60,205,72,205,108,227,245,244,101,128,30,
131,229,235,239,242,229,225,110,128,49,89,232,233,242,225,231,
225,238,97,128,48,143,107,2,204,252,205,20,225,244,225,235,
225,238,97,129,48,239,205,8,232,225,236,230,247,233,228,244,
104,128,255,156,239,242,229,225,110,128,49,88,243,237,225,236,
108,2,205,38,205,49,232,233,242,225,231,225,238,97,128,48,
142,235,225,244,225,235,225,238,97,128,48,238,244,244,239,243,
241,245,225,242,101,128,51,87,118,2,205,78,205,86,229,228,
225,243,104,128,48,28,249,245,238,228,229,242,243,227,239,242,
229,246,229,242,244,233,227,225,108,128,254,52,119,3,205,116,
205,125,205,139,225,242,225,226,233,99,128,6,72,230,233,238,
225,236,225,242,225,226,233,99,128,254,238,232,225,237,250,225,
225,226,239,246,101,2,205,154,205,163,225,242,225,226,233,99,
128,6,36,230,233,238,225,236,225,242,225,226,233,99,128,254,
134,226,243,241,245,225,242,101,128,51,221,227,233,242,99,2,
205,196,205,201,236,101,128,36,230,245,237,230,236,229,120,128,
1,117,100,2,205,216,205,226,233,229,242,229,243,233,115,128,
30,133,239,116,2,205,233,205,242,225,227,227,229,238,116,128,
30,135,226,229,236,239,119,128,30,137,101,4,206,4,206,15,
206,27,206,51,232,233,242,225,231,225,238,97,128,48,145,233,
229,242,243,244,242,225,243,115,128,33,24,107,2,206,33,206,
43,225,244,225,235,225,238,97,128,48,241,239,242,229,225,110,
128,49,94,239,235,239,242,229,225,110,128,49,93,231,242,225,
246,101,128,30,129,232,233,244,101,8,206,90,206,99,206,183,
207,17,207,101,207,146,207,198,207,254,226,245,236,236,229,116,
128,37,230,99,2,206,105,206,125,233,242,227,236,101,129,37,
203,206,115,233,238,246,229,242,243,101,128,37,217,239,242,238,
229,242,226,242,225,227,235,229,116,2,206,142,206,162,236,229,
230,116,129,48,14,206,151,246,229,242,244,233,227,225,108,128,
254,67,242,233,231,232,116,129,48,15,206,172,246,229,242,244,
233,227,225,108,128,254,68,100,2,206,189,206,230,233,225,237,
239,238,100,129,37,199,206,200,227,239,238,244,225,233,238,233,
238,231,226,236,225,227,235,243,237,225,236,236,228,233,225,237,
239,238,100,128,37,200,239,247,238,240,239,233,238,244,233,238,
103,2,206,246,207,6,243,237,225,236,236,244,242,233,225,238,
231,236,101,128,37,191,244,242,233,225,238,231,236,101,128,37,
189,236,101,2,207,24,207,66,230,244,240,239,233,238,244,233,
238,103,2,207,39,207,55,243,237,225,236,236,244,242,233,225,
238,231,236,101,128,37,195,244,242,233,225,238,231,236,101,128,
37,193,238,244,233,227,245,236,225,242,226,242,225,227,235,229,
116,2,207,86,207,93,236,229,230,116,128,48,22,242,233,231,
232,116,128,48,23,242,233,231,232,244,240,239,233,238,244,233,
238,103,2,207,119,207,135,243,237,225,236,236,244,242,233,225,
238,231,236,101,128,37,185,244,242,233,225,238,231,236,101,128,
37,183,115,3,207,154,207,184,207,192,109,2,207,160,207,172,
225,236,236,243,241,245,225,242,101,128,37,171,233,236,233,238,
231,230,225,227,101,128,38,58,241,245,225,242,101,128,37,161,
244,225,114,128,38,6,116,2,207,204,207,215,229,236,229,240,
232,239,238,101,128,38,15,239,242,244,239,233,243,229,243,232,
229,236,236,226,242,225,227,235,229,116,2,207,239,207,246,236,
229,230,116,128,48,24,242,233,231,232,116,128,48,25,245,240,
240,239,233,238,244,233,238,103,2,208,13,208,29,243,237,225,
236,236,244,242,233,225,238,231,236,101,128,37,181,244,242,233,
225,238,231,236,101,128,37,179,105,2,208,46,208,57,232,233,
242,225,231,225,238,97,128,48,144,107,2,208,63,208,73,225,
244,225,235,225,238,97,128,48,240,239,242,229,225,110,128,49,
95,237,239,238,239,243,240,225,227,101,128,255,87,111,4,208,
103,208,114,208,139,208,157,232,233,242,225,231,225,238,97,128,
48,146,235,225,244,225,235,225,238,97,129,48,242,208,127,232,
225,236,230,247,233,228,244,104,128,255,102,110,129,32,169,208,
145,237,239,238,239,243,240,225,227,101,128,255,230,247,225,229,
238,244,232,225,105,128,14,39,240,225,242,229,110,128,36,178,
242,233,238,103,128,30,152,243,245,240,229,242,233,239,114,128,
2,183,244,245,242,238,229,100,128,2,141,249,238,110,128,1,
191,120,137,0,120,208,231,208,242,208,253,209,6,209,33,209,
46,209,50,209,62,209,70,225,226,239,246,229,227,237,98,128,
3,61,226,239,240,239,237,239,230,111,128,49,18,227,233,242,
227,236,101,128,36,231,100,2,209,12,209,22,233,229,242,229,
243,233,115,128,30,141,239,244,225,227,227,229,238,116,128,30,
139,229,232,225,242,237,229,238,233,225,110,128,5,109,105,128,
3,190,237,239,238,239,243,240,225,227,101,128,255,88,240,225,
242,229,110,128,36,179,243,245,240,229,242,233,239,114,128,2,
227,121,143,0,121,209,115,210,74,210,97,210,137,212,103,212,
111,212,128,212,192,212,204,213,201,213,241,213,253,214,8,214,
29,215,2,97,11,209,139,209,151,209,161,209,168,209,175,209,
185,209,210,209,221,210,3,210,16,210,62,225,228,239,243,241,
245,225,242,101,128,51,78,226,229,238,231,225,236,105,128,9,
175,227,245,244,101,128,0,253,228,229,246,97,128,9,47,229,
235,239,242,229,225,110,128,49,82,231,117,2,209,192,209,201,
234,225,242,225,244,105,128,10,175,242,237,245,235,232,105,128,
10,47,232,233,242,225,231,225,238,97,128,48,132,107,2,209,
227,209,251,225,244,225,235,225,238,97,129,48,228,209,239,232,
225,236,230,247,233,228,244,104,128,255,148,239,242,229,225,110,
128,49,81,237,225,235,235,225,238,244,232,225,105,128,14,78,
243,237,225,236,108,2,210,26,210,37,232,233,242,225,231,225,
238,97,128,48,131,235,225,244,225,235,225,238,97,129,48,227,
210,50,232,225,236,230,247,233,228,244,104,128,255,108,244,227,
249,242,233,236,236,233,99,128,4,99,227,233,242,99,2,210,
83,210,88,236,101,128,36,232,245,237,230,236,229,120,128,1,
119,100,2,210,103,210,113,233,229,242,229,243,233,115,128,0,
255,239,116,2,210,120,210,129,225,227,227,229,238,116,128,30,
143,226,229,236,239,119,128,30,245,101,7,210,153,211,161,211,
170,211,188,211,220,212,40,212,91,104,8,210,171,210,180,210,
214,210,228,211,45,211,61,211,120,211,138,225,242,225,226,233,
99,128,6,74,226,225,242,242,229,101,2,210,191,210,200,225,
242,225,226,233,99,128,6,210,230,233,238,225,236,225,242,225,
226,233,99,128,251,175,230,233,238,225,236,225,242,225,226,233,
99,128,254,242,232,225,237,250,225,225,226,239,246,101,4,210,
247,211,0,211,14,211,30,225,242,225,226,233,99,128,6,38,
230,233,238,225,236,225,242,225,226,233,99,128,254,138,233,238,
233,244,233,225,236,225,242,225,226,233,99,128,254,139,237,229,
228,233,225,236,225,242,225,226,233,99,128,254,140,233,238,233,
244,233,225,236,225,242,225,226,233,99,128,254,243,237,101,2,
211,68,211,81,228,233,225,236,225,242,225,226,233,99,128,254,
244,229,237,105,2,211,89,211,104,238,233,244,233,225,236,225,
242,225,226,233,99,128,252,221,243,239,236,225,244,229,228,225,
242,225,226,233,99,128,252,88,238,239,239,238,230,233,238,225,
236,225,242,225,226,233,99,128,252,148,244,232,242,229,229,228,
239,244,243,226,229,236,239,247,225,242,225,226,233,99,128,6,
209,235,239,242,229,225,110,128,49,86,110,129,0,165,211,176,
237,239,238,239,243,240,225,227,101,128,255,229,111,2,211,194,
211,203,235,239,242,229,225,110,128,49,85,242,233,238,232,233,
229,245,232,235,239,242,229,225,110,128,49,134,114,3,211,228,
212,8,212,20,225,232,226,229,238,249,239,237,111,2,211,242,
211,251,232,229,226,242,229,119,128,5,170,236,229,230,244,232,
229,226,242,229,119,128,5,170,233,227,249,242,233,236,236,233,
99,128,4,75,245,228,233,229,242,229,243,233,243,227,249,242,
233,236,236,233,99,128,4,249,243,233,229,245,238,103,3,212,
53,212,62,212,78,235,239,242,229,225,110,128,49,129,240,225,
238,243,233,239,243,235,239,242,229,225,110,128,49,131,243,233,
239,243,235,239,242,229,225,110,128,49,130,244,233,246,232,229,
226,242,229,119,128,5,154,231,242,225,246,101,128,30,243,232,
239,239,107,129,1,180,212,120,225,226,239,246,101,128,30,247,
105,5,212,140,212,151,212,162,212,171,212,179,225,242,237,229,
238,233,225,110,128,5,117,227,249,242,233,236,236,233,99,128,
4,87,235,239,242,229,225,110,128,49,98,238,249,225,238,103,
128,38,47,247,238,225,242,237,229,238,233,225,110,128,5,130,
237,239,238,239,243,240,225,227,101,128,255,89,111,7,212,220,
213,34,213,45,213,55,213,93,213,139,213,148,100,131,5,217,
212,230,212,250,213,3,228,225,231,229,243,104,129,251,57,212,
241,232,229,226,242,229,119,128,251,57,232,229,226,242,229,119,
128,5,217,249,239,100,2,213,11,213,20,232,229,226,242,229,
119,128,5,242,240,225,244,225,232,232,229,226,242,229,119,128,
251,31,232,233,242,225,231,225,238,97,128,48,136,233,235,239,
242,229,225,110,128,49,137,107,2,213,61,213,85,225,244,225,
235,225,238,97,129,48,232,213,73,232,225,236,230,247,233,228,
244,104,128,255,150,239,242,229,225,110,128,49,91,243,237,225,
236,108,2,213,103,213,114,232,233,242,225,231,225,238,97,128,
48,135,235,225,244,225,235,225,238,97,129,48,231,213,127,232,
225,236,230,247,233,228,244,104,128,255,110,244,231,242,229,229,
107,128,3,243,121,2,213,154,213,191,97,2,213,160,213,170,
229,235,239,242,229,225,110,128,49,136,107,2,213,176,213,184,
239,242,229,225,110,128,49,135,244,232,225,105,128,14,34,233,
238,231,244,232,225,105,128,14,13,112,2,213,207,213,214,225,
242,229,110,128,36,180,239,231,229,231,242,225,237,237,229,238,
105,129,3,122,213,230,231,242,229,229,235,227,237,98,128,3,
69,114,129,1,166,213,247,233,238,103,128,30,153,243,245,240,
229,242,233,239,114,128,2,184,116,2,214,14,214,21,233,236,
228,101,128,30,249,245,242,238,229,100,128,2,142,117,5,214,
41,214,52,214,62,214,100,214,232,232,233,242,225,231,225,238,
97,128,48,134,233,235,239,242,229,225,110,128,49,140,107,2,
214,68,214,92,225,244,225,235,225,238,97,129,48,230,214,80,
232,225,236,230,247,233,228,244,104,128,255,149,239,242,229,225,
110,128,49,96,115,3,214,108,214,146,214,187,226,233,103,2,
214,116,214,127,227,249,242,233,236,236,233,99,128,4,107,233,
239,244,233,230,233,229,228,227,249,242,233,236,236,233,99,128,
4,109,236,233,244,244,236,101,2,214,157,214,168,227,249,242,
233,236,236,233,99,128,4,103,233,239,244,233,230,233,229,228,
227,249,242,233,236,236,233,99,128,4,105,237,225,236,108,2,
214,196,214,207,232,233,242,225,231,225,238,97,128,48,133,235,
225,244,225,235,225,238,97,129,48,229,214,220,232,225,236,230,
247,233,228,244,104,128,255,109,249,101,2,214,239,214,248,235,
239,242,229,225,110,128,49,139,239,235,239,242,229,225,110,128,
49,138,249,97,2,215,9,215,19,226,229,238,231,225,236,105,
128,9,223,228,229,246,97,128,9,95,122,142,0,122,215,58,
216,66,216,77,216,120,216,147,217,182,218,34,218,76,218,88,
218,100,218,128,218,136,218,152,218,161,97,10,215,80,215,91,
215,98,215,105,215,116,215,194,215,224,215,235,216,15,216,27,
225,242,237,229,238,233,225,110,128,5,102,227,245,244,101,128,
1,122,228,229,246,97,128,9,91,231,245,242,237,245,235,232,
105,128,10,91,104,4,215,126,215,135,215,149,215,179,225,242,
225,226,233,99,128,6,56,230,233,238,225,236,225,242,225,226,
233,99,128,254,198,105,2,215,155,215,170,238,233,244,233,225,
236,225,242,225,226,233,99,128,254,199,242,225,231,225,238,97,
128,48,86,237,229,228,233,225,236,225,242,225,226,233,99,128,
254,200,233,110,2,215,201,215,210,225,242,225,226,233,99,128,
6,50,230,233,238,225,236,225,242,225,226,233,99,128,254,176,
235,225,244,225,235,225,238,97,128,48,182,241,229,102,2,215,
243,216,1,231,225,228,239,236,232,229,226,242,229,119,128,5,
149,241,225,244,225,238,232,229,226,242,229,119,128,5,148,242,
241,225,232,229,226,242,229,119,128,5,152,249,233,110,130,5,
214,216,37,216,57,228,225,231,229,243,104,129,251,54,216,48,
232,229,226,242,229,119,128,251,54,232,229,226,242,229,119,128,
5,214,226,239,240,239,237,239,230,111,128,49,23,99,3,216,
85,216,92,216,114,225,242,239,110,128,1,126,233,242,99,2,
216,100,216,105,236,101,128,36,233,245,237,230,236,229,120,128,
30,145,245,242,108,128,2,145,228,239,116,130,1,124,216,130,
216,139,225,227,227,229,238,116,128,1,124,226,229,236,239,119,
128,30,147,101,6,216,161,216,172,216,215,216,226,216,237,217,
177,227,249,242,233,236,236,233,99,128,4,55,100,2,216,178,
216,197,229,243,227,229,238,228,229,242,227,249,242,233,236,236,
233,99,128,4,153,233,229,242,229,243,233,243,227,249,242,233,
236,236,233,99,128,4,223,232,233,242,225,231,225,238,97,128,
48,92,235,225,244,225,235,225,238,97,128,48,188,242,111,140,
0,48,217,10,217,19,217,29,217,36,217,61,217,74,217,85,
217,97,217,108,217,118,217,129,217,136,225,242,225,226,233,99,
128,6,96,226,229,238,231,225,236,105,128,9,230,228,229,246,
97,128,9,102,231,117,2,217,43,217,52,234,225,242,225,244,
105,128,10,230,242,237,245,235,232,105,128,10,102,232,225,227,
235,225,242,225,226,233,99,128,6,96,233,238,230,229,242,233,
239,114,128,32,128,237,239,238,239,243,240,225,227,101,128,255,
16,239,236,228,243,244,249,236,101,128,247,48,240,229,242,243,
233,225,110,128,6,240,243,245,240,229,242,233,239,114,128,32,
112,244,232,225,105,128,14,80,247,233,228,244,104,3,217,148,
217,157,217,169,234,239,233,238,229,114,128,254,255,238,239,238,
234,239,233,238,229,114,128,32,12,243,240,225,227,101,128,32,
11,244,97,128,3,182,104,2,217,188,217,199,226,239,240,239,
237,239,230,111,128,49,19,101,4,217,209,217,220,217,236,217,
247,225,242,237,229,238,233,225,110,128,5,106,226,242,229,246,
229,227,249,242,233,236,236,233,99,128,4,194,227,249,242,233,
236,236,233,99,128,4,54,100,2,217,253,218,16,229,243,227,
229,238,228,229,242,227,249,242,233,236,236,233,99,128,4,151,
233,229,242,229,243,233,243,227,249,242,233,236,236,233,99,128,
4,221,105,3,218,42,218,53,218,64,232,233,242,225,231,225,
238,97,128,48,88,235,225,244,225,235,225,238,97,128,48,184,
238,239,242,232,229,226,242,229,119,128,5,174,236,233,238,229,
226,229,236,239,119,128,30,149,237,239,238,239,243,240,225,227,
101,128,255,90,111,2,218,106,218,117,232,233,242,225,231,225,
238,97,128,48,94,235,225,244,225,235,225,238,97,128,48,190,
240,225,242,229,110,128,36,181,242,229,244,242,239,230,236,229,
248,232,239,239,107,128,2,144,243,244,242,239,235,101,128,1,
182,117,2,218,167,218,178,232,233,242,225,231,225,238,97,128,
48,90,235,225,244,225,235,225,238,97,128,48,186];

function ft_get_adobe_glyph_index(_name,limit)
{
    var name = 0;
    var c = 0;
    var count, min, max;
    var p = 0;

    if (_name == null || _name == "" || 0 >= limit)
        return 0;

    c = _name.charCodeAt(name);
    name++;
    count = ft_adobe_glyph_list[1];
    p += 2;

    min = 0;
    max = count;

    var go_to_found = 0;
    var go_to_next = 0;
    while (min < max)
    {
        var mid = (min + max) >>> 1;
        var q = p + mid * 2;
        q = ((ft_adobe_glyph_list[q] << 8) | ft_adobe_glyph_list[q+1]);
        var c2 = ft_adobe_glyph_list[q] & 127;
        if (c2 == c)
        {
            p = q;
            go_to_found = 1;
            break;
        }
        if ( c2 < c )
            min = mid + 1;
        else
            max = mid;
    }
    if (0 == go_to_found)
        return 0;

    for (;;)
    {
        if (name >= limit)
        {
            if ((ft_adobe_glyph_list[p] & 128) == 0 && (ft_adobe_glyph_list[p+1] & 128) != 0)
                return ((ft_adobe_glyph_list[p+2] << 8) | ft_adobe_glyph_list[p+3]);
            return 0;
        }
        c = _name.charCodeAt(name++);
        if (ft_adobe_glyph_list[p] & 128)
        {
            p++;
            if (c != (ft_adobe_glyph_list[p] & 127))
                return 0;
            continue;
        }

        p++;
        count = ft_adobe_glyph_list[p] & 127;
        if (ft_adobe_glyph_list[p] & 128)
            p += 2;

        p++;
        for (; count > 0; count--, p += 2)
        {
            var offset = (ft_adobe_glyph_list[p] << 8) | ft_adobe_glyph_list[p+1];
            var q = offset;
            if (c == (ft_adobe_glyph_list[q] & 127))
            {
                p = q;
                go_to_next = 1;
                break;
            }
        }
        if (0 == go_to_next)
            return 0;
    }
    return 0;
}

function PS_UniMap()
{
    this.unicode = 0;
    this.glyph_index = 0;
}
function PS_UnicodesRec()
{
    this.cmap = null;
    this.num_maps = 0;
    this.maps = null;
}

function ps_unicode_value(glyph_name)
{
    var len = glyph_name.length;
    if (glyph_name.charCodeAt(0) == 117 && glyph_name.charCodeAt(1) == 110 && glyph_name.charCodeAt(2) == 105)
    {
        var count;
        var value = 0;
        var p = 3;

        for ( count = 4; count > 0; count--, p++ )
        {
            var c = 0;
            if (p < len)
                c = glyph_name.charCodeAt(p);
            if (c<0)
                c+=256;
            var d = c - 48;
            if (d < 0)
                d += 256;
            if (d >= 10)
            {
                d = c - 65;
                if (d < 0)
                    d += 256;
                if (d >= 6)
                    d = 16;
                else
                    d += 10;
            }

            if (d >= 16)
                break;

            value = (value << 4) + d;
        }

        if (count == 0)
        {
            if (p >= len)
                return value;
            if (glyph_name.charCodeAt(p) == 46)
                return (value | 0x7FFFFFFF);
        }
    }

    if (glyph_name.charCodeAt(0) == 117)
    {
        var count;
        var value = 0;
        var p     = 1;

        for (count = 6; count > 0; count--, p++)
        {
            var c = 0;
            if (p < len)
                c = glyph_name.charCodeAt(p);
            if (c<0)
                c+=256;
            var d = c - 48;
            if (d < 0)
                d += 256;
            if (d >= 10)
            {
                d = c - 65;
                if (d < 0)
                    d += 256;
                if (d >= 6)
                    d = 16;
                else
                    d += 10;
            }

            if (d >= 16)
                break;

            value = (value << 4) + d;
        }

        if (count <= 2)
        {
            if (p >= len)
                return value;
            if (glyph_name.charCodeAt(p) == 46)
                return (value | 0x7FFFFFFF);
      }
    }

    var p = 0;
    var dot = 0;

    for (; p<len; p++)
    {
        if (glyph_name.charCodeAt(p) == 46 && p > 0)
        {
            dot = p;
            break;
        }
    }

    if (dot == 0)
        return ft_get_adobe_glyph_index(glyph_name, p);
    else
        return ft_get_adobe_glyph_index(glyph_name, dot | 0x7FFFFFFF);
}

function compare_uni_maps(map1, map2)
{
    var unicode1 = map1.unicode & 0x7FFFFFFF;
    var unicode2 = map2.unicode & 0x7FFFFFFF;
    if (unicode1 == unicode2)
    {
        if (map1.unicode > map2.unicode)
            return 1;
        else if (map1.unicode < map2.unicode)
            return -1;
        else
            return 0;
    }
    else
    {
        if (unicode1 > unicode2)
            return 1;
        else if (unicode1 < unicode2)
            return -1;
        else
            return 0;
    }
}

function ft_qsort(mass, count, compare)
{
    var t;
    for (var i=0;i<count;i++)
    {
        for (var j=i+1;j<count;j++)
        {
            if (compare(mass[i],mass[j]) == 1)
            {
                t=mass[i];
                mass[i]=mass[j];
                mass[j]=t;
            }
        }
    }
}

var ft_extra_glyph_unicodes=[0x0394,0x03A9,0x2215,0x00AD,0x02C9,0x03BC,0x2219,0x00A0,0x021A,0x021B];
var ft_extra_glyph_names=["Delta","Omega","fraction","hyphen","macron","mu","periodcentered","space","Tcommaaccent","tcommaaccent"];

function ps_check_extra_glyph_name(gname,glyph,extra_glyphs,states)
{
    for (var n = 0; n < 10; n++)
    {
        if (ft_extra_glyph_names[n] == gname)
        {
            if ( states[n] == 0 )
            {
                states[n]     = 1;
                extra_glyphs[n] = glyph;
            }

            return;
        }
    }
}
function ps_check_extra_glyph_unicode(uni_char,states)
{
    for (var n = 0; n < 10; n++)
    {
        if (uni_char == ft_extra_glyph_unicodes[n])
        {
            states[n] = 2;
            return;
        }
    }
}

function ps_unicodes_init(memory,table,num_glyphs,get_glyph_name,free_glyph_name,glyph_data)
{
    var error = 0;

    var extra_glyph_list_states = [0,0,0,0,0,0,0,0,0,0];
    var extra_glyphs = [0,0,0,0,0,0,0,0,0,0];

    table.num_maps = 0;
    table.maps = new Array(num_glyphs + 10);
    for (var i = 0; i < (num_glyphs + 10); i++)
        table.maps[i] = new PS_UniMap();

    var n;
    var map = 0;
    var maps = table.maps;
    var uni_char;

    for ( n = 0; n < num_glyphs; n++)
    {
        var gname = get_glyph_name(glyph_data, n);
        if (gname != null)
        {
            ps_check_extra_glyph_name(gname, n, extra_glyphs, extra_glyph_list_states);

            if (n == 86)
            {
                var __nnn = 0;
                __nnn += 1;
                __nnn += 1;
                __nnn += 1;
            }

            uni_char = ps_unicode_value(gname);

            if ((uni_char & 0x7FFFFFFF) != 0)
            {
                ps_check_extra_glyph_unicode(uni_char, extra_glyph_list_states);
                maps[map].unicode = uni_char;
                maps[map].glyph_index = n;
                map++;
            }

            if (free_glyph_name)
                free_glyph_name(glyph_data, gname);
        }
    }

    for (n = 0; n < 10; n++)
    {
        if (extra_glyph_list_states[n] == 1)
        {
            maps[map].unicode = ft_extra_glyph_unicodes[n];
            maps[map].glyph_index = extra_glyphs[n];
            map++;
        }
    }

    var count = map;
    if ( count == 0 )
    {
        table.maps = null;
        if (!error)
            error = 163;
    }
    else
    {
        if (count < num_glyphs / 2)
        {
            table.maps[count-1] = new PS_UniMap();
            error = 0;
        }

        var del_count = num_glyphs + 10 - count;
        if (del_count > 10)
            maps.splice(count, del_count);
        maps.sort(compare_uni_maps);
        //ft_qsort(table.maps, count, compare_uni_maps);
    }

    table.num_maps = count;

    return error;
}

function ps_unicodes_char_index(table, unicode)
{
    var mid, result = -1;

    var maps = table.maps;
    var min = 0;
    var max = table.num_maps - 1;

    while (min <= max)
    {
        mid = min + ((max - min) >>> 1);

        if (maps[mid].unicode == unicode)
        {
            result = mid;
            break;
        }

        var base_glyph = maps[mid].unicode & 0x7FFFFFFF;
        if (base_glyph == unicode)
            result = mid;

        if (min == max)
            break;

        if (base_glyph < unicode)
            min = mid + 1;
        else
            max = mid - 1;
    }

    if (result != -1)
        return maps[result].glyph_index;
    return 0;
}

function ps_unicodes_char_next(table, unicode)
{
    var result = 0;
    var char_code = unicode + 1;

    var min = 0;
    var max = table.num_maps;
    var mid;
    var map;
    var base_glyph;

    while ( min < max )
    {
        mid = min + ((max - min) >>> 1);
        map = table.maps[mid];

        if (map.unicode == char_code)
        {
            result = map.glyph_index;
            return {unicode:char_code, result:result};
        }

        base_glyph = (map.unicode & 0x7FFFFFFF);

        if (base_glyph == char_code)
            result = map.glyph_index;

        if (base_glyph < char_code)
            min = mid + 1;
        else
            max = mid;
    }

    if (result != 0)
        return {unicode:char_code, result:result};

    char_code = 0;

    if (min < table.num_maps)
    {
        map       = table.maps[min];
        result    = map.glyph_index;
        char_code = (map.unicode & 0x7FFFFFFF);
    }

    return {unicode:char_code, result:result};
}

function ps_get_macintosh_name(name_index)
{
    if (name_index >= 258)
        name_index = 0;

    return ft_standard_glyph_names[ft_mac_names[name_index]];
}

function ps_get_standard_strings(sid)
{
    if (sid >= 391)
        return 0;

    return ft_standard_glyph_names[ft_sid_names[sid]];
}

function _pscmaps_interface()
{
    this.unicode_value = ps_unicode_value;
    this.unicodes_init = ps_unicodes_init;
    this.unicodes_char_index = ps_unicodes_char_index;
    this.unicodes_char_next = ps_unicodes_char_next;

    this.macintosh_name = ps_get_macintosh_name;
    this.adobe_std_strings = ps_get_standard_strings;
    this.adobe_std_encoding = t1_standard_encoding;
    this.adobe_expert_encoding = t1_expert_encoding;
}

var pscmaps_interface = new _pscmaps_interface();
function create_psnames_module(library)
{
    var psnames_mod = new FT_Module();
    psnames_mod.clazz = new FT_Module_Class();

    var clazz = psnames_mod.clazz;
    clazz.flags = 0;
    clazz.name = "psnames";
    clazz.version = 0x10000;
    clazz.requires = 0x20000;

    clazz.module_interface = pscmaps_interface;
    clazz.init = null;
    clazz.done = null;

    clazz.get_interface = function(module, name)
    {
        if (name == "postscript-cmaps")
            return pscmaps_interface;
        return null;
    }

    psnames_mod.library = library;
    psnames_mod.memory = library.Memory;
    psnames_mod.generic = null;

    return psnames_mod;
}
/******************************************************************************/
// t1tables
/******************************************************************************/
function _isdigit(x)
{
    if ((x >= 48) && (x <= 57))
        return 1;
    return 0;
}
function _isxdigit(x)
{
    if (((x >= 48) && (x <= 57)) ||
        ((x >= 97) && (x <= 102)) ||
        ((x >= 65) && (x <= 70)))
        return 1;
    return 0;
}

function PS_FontInfoRec()
{
    this.version        = "";
    this.notice         = "";
    this.full_name      = "";
    this.family_name    = "";
    this.weight         = "";
    this.italic_angle   = 0;
    this.is_fixed_pitch = 0;
    this.underline_position     = 0;
    this.underline_thickness    = 0;

    this.CreateDublicate = function()
    {
        var _ret = new PS_FontInfoRec();

        _ret.version = this.version;
        _ret.notice = this.notice;
        _ret.full_name = this.full_name;
        _ret.family_name = this.family_name;
        _ret.weight = this.weight;
        _ret.italic_angle = this.italic_angle;
        _ret.is_fixed_pitch = this.is_fixed_pitch;
        _ret.underline_position = this.underline_position;
        _ret.underline_thickness = this.underline_thickness;

        return _ret;
    }
}
function PS_PrivateRec()
{
    this.unique_id = 0;
    this.lenIV = 0;

    this.num_blue_values = 0;
    this.num_other_blues = 0;
    this.num_family_blues = 0;
    this.num_family_other_blues = 0;

    this.blue_values = [0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    this.other_blues = [0,0,0,0,0,0,0,0,0,0];

    this.family_blues = [0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    this.family_other_blues = [0,0,0,0,0,0,0,0,0,0];

    this.blue_scale = 0;
    this.blue_shift = 0;
    this.blue_fuzz = 0;

    this.standard_width = [0];
    this.standard_height = [0];

    this.num_snap_widths = 0;
    this.num_snap_heights = 0;
    this.force_bold = 0;
    this.round_stem_up = 0;

    this.snap_widths = [0,0,0,0,0,0,0,0,0,0,0,0,0];
    this.snap_heights = [0,0,0,0,0,0,0,0,0,0,0,0,0];

    this.expansion_factor = 0;

    this.language_group = 0;
    this.password = 0;

    this.min_feature = [0,0];

    this.clear = function()
    {
        this.unique_id = 0;
        this.lenIV = 0;

        this.num_blue_values = 0;
        this.num_other_blues = 0;
        this.num_family_blues = 0;
        this.num_family_other_blues = 0;

        for (var i = 0; i < 14; i++)
            this.blue_values[i] = 0;

        for (var i = 0; i < 10; i++)
            this.other_blues[i] = 0;

        for (var i = 0; i < 14; i++)
            this.family_blues[i] = 0;

        for (var i = 0; i < 10; i++)
            this.family_other_blues[i] = 0;

        this.blue_scale = 0;
        this.blue_shift = 0;
        this.blue_fuzz = 0;

        this.standard_width[0] = 0;
        this.standard_height[0] = 0;

        this.num_snap_widths = 0;
        this.num_snap_heights = 0;
        this.force_bold = 0;
        this.round_stem_up = 0;

        for (var i = 0; i < 13; i++)
            this.snap_widths[i] = 0;
        for (var i = 0; i < 13; i++)
            this.snap_heights[i] = 0;

        this.expansion_factor = 0;

        this.language_group = 0;
        this.password = 0;

        this.min_feature[0] = 0;
        this.min_feature[1] = 0;
    }
}

function PS_DesignMapRec()
{
    this.num_points = 0;
    this.design_points = null;
    this.blend_points = null;
}

function PS_BlendRec()
{
    this.num_designs = 0;
    this.num_axis = 0;

    this.axis_names = CreateNullArray(4);
    this.design_pos = CreateNullArray(16);
    this.design_map = new Array(4);
    for (var i = 0; i < 4; i++)
        this.design_map[i] = new PS_DesignMapRec();

    this.weight_vector = null;
    this.default_weight_vector = 0;

    this.font_infos = CreateNullArray(16 + 1);
    this.privates = CreateNullArray(16 + 1);

    this.blend_bitflags = 0;

    this.bboxes = CreateNullArray(16 + 1);

    this.default_design_vector = CreateUIntArray(16);
    this.num_default_design_vector = 0;
}

function CID_FaceDictRec()
{
    this.private_dict = new PS_PrivateRec();

    this.len_buildchar = 0;
    this.forcebold_threshold = 0;
    this.stroke_width = 0;
    this.expansion_factor = 0;

    this.paint_type = 0;
    this.font_type = 0;
    this.font_matrix = new FT_Matrix();
    this.font_offset = new FT_Vector();

    this.num_subrs = 0;
    this.subrmap_offset = 0;
    this.sd_bytes = 0;
}

function CID_FaceInfoRec()
{
    this.cid_font_name = "";
    this.cid_version = 0;
    this.cid_font_type = 0;

    this.registry = "";
    this.ordering = "";
    this.supplement = 0;

    this.font_info = new PS_FontInfoRec();
    this.font_bbox = new FT_BBox();
    this.uid_base = 0;

    this.num_xuid = 0;
    this.xuid = CreateUIntArray(16);

    this.cidmap_offset = 0;
    this.fd_bytes = 0;
    this.gd_bytes = 0;
    this.cid_count = 0;

    this.num_dicts = 0;
    this.font_dicts = null;

    this.data_offset = 0;
}

function FT_Has_PS_Glyph_Names(face)
{
    var result = 0;
    if (face != null)
    {
        var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_INFO);

        if (service && service.ps_has_glyph_names)
            result = service.ps_has_glyph_names(face);
    }
    return result;
}
function FT_Get_PS_Font_Info(face, afont_info)
{
    var error = 6;
    if (face != null)
    {
        var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_INFO);

        if (service && service.ps_get_font_info)
            error = service.ps_get_font_info(face, afont_info);
    }
    return error;
}
function FT_Get_PS_Font_Private(face, afont_private)
{
    var error = 6;
    if (face != null)
    {
        var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_INFO);

        if (service && service.ps_get_font_private)
            error = service.ps_get_font_private(face, afont_private);
    }
    return error;
}
function FT_Get_PS_Font_Value(face, key, idx, value, value_len)
{
    var result  = 0;
    if (face != null)
    {
        var service = FT_FACE_FIND_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_INFO);

        if (service && service.ps_get_font_value)
            result = service.ps_get_font_value(face, key, idx, value, value_len);
    }
    return result;
}
/******************************************************************************/
// t1types
/******************************************************************************/
function T1_EncodingRec()
{
    this.num_chars  = 0;
    this.code_first = 0;
    this.code_last  = 0;

    this.char_index = null;
    this.char_name  = null;
}
function PS_FontExtraRec()
{
    this.fs_type = 0;

    this.CreateDublicate = function()
    {
        var _ret = new PS_FontExtraRec();
        _ret.fs_type = this.fs_type;
        return _ret;
    }
}
function T1_FontRec()
{
    this.font_info      = new PS_FontInfoRec();
    this.font_extra     = new PS_FontExtraRec();
    this.private_dict   = new PS_PrivateRec();
    this.font_name      = "";

    this.encoding_type  = 0;
    this.encoding       = new T1_EncodingRec();

    this.subrs_block        = null;
    this.charstrings_block  = null;
    this.glyph_names_block  = null;

    this.num_subrs = 0;
    this.subrs = null;
    this.subrs_len = null;

    this.num_glyphs = 0;
    this.glyph_names = null;
    this.charstrings = null;
    this.charstrings_len = null;

    this.paint_type = 0;
    this.font_type = 0;
    this.font_matrix = new FT_Matrix();
    this.font_offset = new FT_Vector();
    this.font_bbox = new FT_BBox();
    this.font_id = 0;

    this.stroke_width = 0;
}
function CID_SubrsRec()
{
    this.num_subrs = 0;
    this.code = null;
}

function AFM_TrackKernRec()
{
    this.degree     = 0;
    this.min_ptsize = 0;
    this.min_kern   = 0;
    this.max_ptsize = 0;
    this.max_kern   = 0;
}
function AFM_KernPairRec()
{
    this.index1 = 0;
    this.index2 = 0;
    this.x = 0;
    this.y = 0;
}
function AFM_FontInfoRec()
{
    this.IsCIDFont = 0;
    this.FontBBox = new FT_BBox();
    this.Ascender = 0;
    this.Descender = 0;
    this.TrackKerns = null;
    this.NumTrackKern = 0;
    this.KernPairs = null;
    this.NumKernPair = 0;
}



function T1_Face()
{
    this.num_faces = 0;
    this.face_index = 0;

    this.face_flags = 0;
    this.style_flags = 0;

    this.num_glyphs = 0;

    this.family_name = "";
    this.style_name = "";

    this.num_fixed_sizes = 0;
    this.available_sizes = [];

    this.num_charmaps = 0;
    this.charmaps = [];

    this.generic = new FT_Generic();

    /*# The following member variables (down to `underline_thickness') */
    /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
    /*# for bitmap fonts.                                              */
    this.bbox = new FT_BBox();

    this.units_per_EM = 0;
    this.ascender = 0;
    this.descender = 0;
    this.height = 0;

    this.max_advance_width = 0;
    this.max_advance_height = 0;

    this.underline_position = 0;
    this.underline_thickness = 0;

    this.glyph = null;
    this.size = null;
    this.charmap = null;

    /*@private begin */
    this.driver = null;
    this.memory = null;
    this.stream = null;

    this.sizes_list = [];

    this.autohint = [];
    this.extensions = null;

    this.internal = null;
    /*@private end */

    this.type1 = new T1_FontRec();
    this.psnames = null;
    this.psaux = null;
    this.afm_data = null;

    this.charmaprecs = new Array(2);
    this.charmaprecs[0] = new FT_CharMapRec();
    this.charmaprecs[1] = new FT_CharMapRec();

    this.charmaps = new Array(2);
    this.charmaps[0] = null;
    this.charmaps[1] = null;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.unicode_map = null;
    //#endif

    /* support for Multiple Masters fonts */
    this.blend = null;

    /* undocumented, optional: indices of subroutines that express      */
    /* the NormalizeDesignVector and the ConvertDesignVector procedure, */
    /* respectively, as Type 2 charstrings; -1 if keywords not present  */
    this.ndv_idx = 0;
    this.cdv_idx = 0;

    /* undocumented, optional: has the same meaning as len_buildchar */
    /* for Type 2 fonts; manipulated by othersubrs 19, 24, and 25    */
    this.len_buildchar = 0;
    this.buildchar = null;

    /* since version 2.1 - interface to PostScript hinter */
    this.pshinter = null;
}

function CID_Face()
{
    this.num_faces = 0;
    this.face_index = 0;

    this.face_flags = 0;
    this.style_flags = 0;

    this.num_glyphs = 0;

    this.family_name = "";
    this.style_name = "";

    this.num_fixed_sizes = 0;
    this.available_sizes = [];

    this.num_charmaps = 0;
    this.charmaps = [];

    this.generic = new FT_Generic();

    /*# The following member variables (down to `underline_thickness') */
    /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
    /*# for bitmap fonts.                                              */
    this.bbox = new FT_BBox();

    this.units_per_EM = 0;
    this.ascender = 0;
    this.descender = 0;
    this.height = 0;

    this.max_advance_width = 0;
    this.max_advance_height = 0;

    this.underline_position = 0;
    this.underline_thickness = 0;

    this.glyph = null;
    this.size = null;
    this.charmap = null;

    /*@private begin */
    this.driver = null;
    this.memory = null;
    this.stream = null;

    this.sizes_list = [];

    this.autohint = [];
    this.extensions = null;

    this.internal = null;
    /*@private end */

    this.psnames = null;
    this.psaux = null;
    this.cid = new CID_FaceInfoRec();
    this.font_extra = new PS_FontExtraRec();

    this.subrs = null;

    /* since version 2.1 - interface to PostScript hinter */
    this.pshinter = null;

    this.binary_data = new CPointer(); /* used if hex data has been converted */
    this.cid_stream = null;
}

/******************************************************************************/
function IS_PS_NEWLINE(ch)
{
    if (ch == 13 || ch == 10)
        return true;
    return false;
}
function IS_PS_SPACE(ch)
{
    switch (ch)
    {
        case 32:
        case 13:
        case 10:
        case 9:
        case 12:
        case 0:
            return true;
    }
    return false;
}
function IS_PS_SPECIAL(ch)
{
    switch (ch)
    {
        case 47:
        case 40:
        case 91:
        case 123:
        case 41:
        case 93:
        case 125:
        case 60:
        case 62:
        case 37:
            return true;
    }
    return false;
}
function IS_PS_DELIM(ch)
{
    switch (ch)
    {
        case 32:
        case 13:
        case 10:
        case 9:
        case 12:
        case 0:
        case 47:
        case 40:
        case 91:
        case 123:
        case 41:
        case 93:
        case 125:
        case 60:
        case 62:
        case 37:
            return true;
    }
    return false;
}
function IS_PS_DIGIT(ch)
{
    return (ch >= 48 && ch <= 57) ? true : false;
}
function IS_PS_XDIGIT(ch)
{
    return (IS_PS_DIGIT(ch) || (ch >= 65 && ch <= 70)
        || (ch >= 97 && ch <= 102)) ? true : false;
}
function IS_PS_BASE85(ch)
{
    return (ch >= 33 && ch <= 117) ? true : false;
}

function IS_PS_TOKEN(cur, limit, token, len)
{
    if (cur.data[cur.pos] == token.charCodeAt(0) && (((cur.pos + len) == limit) || ((cur.pos + len) < limit && IS_PS_DELIM(cur.data[cur.pos + len - 1]))) && _strncmp_data(cur, token, len - 1) == 0)
        return 1;
    return 0;
}

/******************************************************************************/
// psconv
/******************************************************************************/
var ft_char_table =
[
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1
];

function PS_Conv_Strtol(cursor, limit, base)
{
    var _data = cursor.data;
    var pos = cursor.pos;

    var num = 0;
    var sign = 0;

    if (pos == limit || base < 2 || base > 36)
        return 0;

    if (_data[pos] == 45 || _data[pos] == 43)
    {
        sign = (_data[pos] == 45) ? 1 : 0;

        pos++;
        if (pos == limit)
            return 0;
    }

    for (; pos < limit; pos++)
    {
        if (IS_PS_SPACE(_data[pos]) || _data[pos] >= 0x80)
            break;

        var c = ft_char_table[_data[pos] & 0x7f];

        if (c < 0 || c >= base)
            break;

        num = num * base + c;
    }

    if (sign == 1)
        num = -num;

    cursor.pos = pos;
    return num;
}
function PS_Conv_ToInt(cursor, limit)
{
    var num = PS_Conv_Strtol(cursor, limit, 10);
    var data = cursor.data;
    var pos = cursor.pos;

    if (pos < limit && data[pos] == 35)
    {
        cursor.pos = pos + 1;
        return PS_Conv_Strtol(cursor, limit, num);
    }
    return num;
}
function PS_Conv_ToFixed(cursor, limit, power_ten)
{
    var p = dublicate_pointer(cursor);
    var integral;
    var decimal = 0, divider = 1;
    var sign = 0;

    if (p.pos == limit)
        return 0;

    var _data = p.data;
    if (_data[p.pos] == 45 || _data[p.pos] == 43)
    {
        sign = (_data[p.pos] == 45) ? 1 : 0;

        p.pos++;
        if (p.pos == limit)
            return 0;
    }

    if (_data[p.pos] != 46)
        integral = PS_Conv_ToInt(p, limit) << 16;
    else
        integral = 0;

    /* read the decimal part */
    if (p.pos < limit && _data[p.pos] == 46)
    {
        p.pos++;
        for (; p.pos < limit; p.pos++)
        {
            if (IS_PS_SPACE(_data[p.pos]) || _data[p.pos] >= 0x80)
                break;

            var c = ft_char_table[_data[p.pos] & 0x7f];

            if (c < 0 || c >= 10)
                break;

            if (!integral && power_ten > 0)
            {
                power_ten--;
                decimal = decimal * 10 + c;
            }
            else
            {
                if (divider < 10000000)
                {
                    decimal = decimal * 10 + c;
                    divider *= 10;
                }
            }
        }
    }

    /* read exponent, if any */
    if (p.pos + 1 < limit && (_data[p.pos] == 101 || _data[p.pos] == 69))
    {
        p.pos++;
        power_ten += PS_Conv_ToInt(p, limit);
    }

    while (power_ten > 0)
    {
        integral *= 10;
        decimal  *= 10;
        power_ten--;
    }

    while (power_ten < 0)
    {
        integral /= 10;
        divider  *= 10;
        power_ten++;
    }

    if (decimal)
        integral += FT_DivFix(decimal, divider);

    if (sign)
        integral = -integral;

    cursor.pos = p.pos;

    return integral;
}

function PS_Conv_ASCIIHexDecode(cursor, limit, buffer, n)
{
    var r = 0;
    var w = buffer.pos;
    var pad = 0x01;
    
    n *= 2;

    var p  = dublicate_pointer(cursor);
    if (n > (limit - p.pos))
        n = (limit - p.pos);

    /* we try to process two nibbles at a time to be as fast as possible */
    var _data = p.data;
    for ( ; r < n; r++)
    {
        var c = _data[p.pos + r];

        if (IS_PS_SPACE(c))
            continue;

        if (c >= 0x80)
            break;

        c = ft_char_table[c & 0x7F];
        var _c = (c >= 0) ? c : c + 256;
        if (c >= 16)
            break;

        pad = (pad << 4) | c;
        if (pad & 0x100)
        {
            buffer.data[w++] = pad & 0xFF;
            pad = 0x01;
        }
    }

    if (pad != 0x01)
        buffer[w++] = ((pad << 4) & 0xFF);

    cursor.pos = p.pos + r;
    return w;
}

function PS_Conv_EexecDecode(cursor, limit, buffer, n, seed)
{
    var r;
    var s = seed;

    var _sd = cursor.data;
    var _sp = cursor.pos;

    if (n > (limit - _sp))
        n = (limit - _sp);

    var _dd = buffer.data;
    var _dp = buffer.pos;

    var v = 0;
    var b = 0;

    for (r = 0; r < n; r++, _sp++, _dp++)
    {
        v = _sd[_sp];
        b = (v ^ (s >> 8));

        s = ((v + s)*52845 + 22719) & 0xFFFF;
        _dd[_dp] = b & 0xFF;
    }

    cursor.pos = _sp;
    return { r : r, seed : s };
}
/******************************************************************************/
// afmparse
/******************************************************************************/
function AFM_ValueRec()
{
    this.type = 0;
    this.u;
}
function AFM_StreamRec()
{
    this.base = null;
    this.cursor = 0;
    this.limit = 0;

    this.status = 0;
}

function AFM_IS_NEWLINE(ch)
{
    if (ch == 13 || ch == 10)
        return true;
    return false;
}
function AFM_IS_EOF(ch)
{
    if (ch == -1  || ch == 0x1A)
        return true;
    return false;
}
function AFM_IS_SPACE(ch)
{
    if (ch == 32  || ch == 9)
        return true;
    return false;
}
function AFM_IS_SEP(ch)
{
    return (ch == 59) ? true : false;
}
function AFM_GETC(stream)
{
    return (stream.cursor < stream.limit) ? stream.base[stream.cursor++] : -1;
}

function afm_stream_skip_spaces(stream)
{
    var ch = 0;

    if (stream.status >= 1)
        return 59;

    while (true)
    {
        ch = AFM_GETC();
        if (!AFM_IS_SPACE(ch))
            break;
    }

    if (AFM_IS_NEWLINE(ch))
        stream.status = 2;
    else if (AFM_IS_SEP(ch))
        stream.status = 1;
    else if (AFM_IS_EOF(ch))
        stream.status = 3;

    return ch;
}
function afm_stream_read_one(stream)
{
    afm_stream_skip_spaces( stream );
    if (stream.status >= 1)
        return null;

    var str = new CPointer();
    str.data = stream.base;
    str.pos = stream.cursor - 1;
    var ch;

    while ( 1 )
    {
        ch = AFM_GETC();
        if (AFM_IS_SPACE(ch))
            break;
        else if (AFM_IS_NEWLINE(ch))
        {
            stream.status = 2;
            break;
        }
        else if (AFM_IS_SEP(ch))
        {
            stream.status = 1;
            break;
        }
        else if (AFM_IS_EOF(ch))
        {
            stream.status = 3;
            break;
        }
    }

    return str;
}
function afm_stream_read_string(stream)
{
    afm_stream_skip_spaces(stream);
    if (stream.status >= 2)
        return null;

    var str = new CPointer();
    str.data = stream.base;
    str.pos = stream.cursor - 1;
    var ch;

    /* scan to eol */
    while ( 1 )
    {
        ch = AFM_GETC();
        if (AFM_IS_NEWLINE(ch))
        {
            stream.status = 2;
            break;
        }
        else if (AFM_IS_EOF(ch))
        {
            stream.status = 3;
            break;
        }
    }

    return str;
}
var afm_key_table =
[
    "Ascender",
    "AxisLabel",
    "AxisType",
    "B",
    "BlendAxisTypes",
    "BlendDesignMap",
    "BlendDesignPositions",
    "C",
    "CC",
    "CH",
    "CapHeight",
    "CharWidth",
    "CharacterSet",
    "Characters",
    "Descender",
    "EncodingScheme",
    "EndAxis",
    "EndCharMetrics",
    "EndComposites",
    "EndDirection",
    "EndFontMetrics",
    "EndKernData",
    "EndKernPairs",
    "EndTrackKern",
    "EscChar",
    "FamilyName",
    "FontBBox",
    "FontName",
    "FullName",
    "IsBaseFont",
    "IsCIDFont",
    "IsFixedPitch",
    "IsFixedV",
    "ItalicAngle",
    "KP",
    "KPH",
    "KPX",
    "KPY",
    "L",
    "MappingScheme",
    "MetricsSets",
    "N",
    "Notice",
    "PCC",
    "StartAxis",
    "StartCharMetrics",
    "StartComposites",
    "StartDirection",
    "StartFontMetrics",
    "StartKernData",
    "StartKernPairs",
    "StartKernPairs0",
    "StartKernPairs1",
    "StartTrackKern",
    "StdHW",
    "StdVW",
    "TrackKern",
    "UnderlinePosition",
    "UnderlineThickness",
    "VV",
    "VVector",
    "Version",
    "W",
    "W0",
    "W0X",
    "W0Y",
    "W1",
    "W1X",
    "W1Y",
    "WX",
    "WY",
    "Weight",
    "WeightVector",
    "XHeight"
];

function afm_parser_read_vals(parser, vals, n)
{
    var stream = parser.stream;
    var str = null;

    if (n > 5)
        return 0;

    var i = 0;
    for (; i < n; i++)
    {
        var val = vals[i];

        if (val.type == 0)
            str = afm_stream_read_string(stream);
        else
            str = afm_stream_read_one(stream);

        if (str == null)
            break;

        var len = stream.cursor - str.pos - 1;

        switch (val.type)
        {
            case 0:
            case 1:
                val.u = "";
                for (var j = 0; j < len; j++)
                    val.u += String.fromCharCode(str.data[str.pos++]);
                break;

            case 2:
                val.u = PS_Conv_ToFixed(str, str.pos + len, 0);
                break;

            case 3:
                val.u = PS_Conv_ToInt(str, str.pos + len);
                break;

            case 4:
                val.u = (len == 4 && (0 == _strncmp(str, "true", 4)));
                break;

            case 5:
                if (parser.get_index != null)
                    val.u = parser.get_index(str, len, parser.user_data);
                else
                    val.u = 0;
                break;
        }
    }

    return i;
}

function afm_parser_next_key(parser, line)
{
    var stream = parser.stream;
    var key = null;

    if (line)
    {
        while ( 1 )
        {
            /* skip current line */
            if (stream.status < 2)
                afm_stream_read_string(stream);

            stream.status = 0;
            key = afm_stream_read_one(stream);

            /* skip empty line */
            if (key == null  && (stream.status < 3) && (stream.status >= 2))
                continue;

            break;
        }
    }
    else
    {
        while ( 1 )
        {
            /* skip current column */
            while (stream.status < 1)
                afm_stream_read_one(stream);

            stream.status = 0;
            key = afm_stream_read_one(stream);

            /* skip empty column */
            if (key == null  && (stream.status < 3) && (stream.status >= 1))
                continue;

            break;
        }
    }

    var ret_len;
    ret_len = (key != null) ? (stream.cursor - key.pos - 1) : 0;

    return { key : key, len: ret_len };
}

function afm_tokenize(key, len)
{
    var n = 0;
    for (; n < 74; n++)
    {
        if (afm_key_table[n].charCodeAt(0) == key.charCodeAt(0))
        {
            for (; n < 74; n++)
            {
                if (afm_key_table[n].charCodeAt(0) != key.charCodeAt(0))
                    return 75;

                if (_strncmp(afm_key_table[n], key, len) == 0)
                    return n;
            }
        }
    }
    return 75;
}

function afm_parser_init(parser, memory, base, limit)
{
    var stream = new AFM_StreamRec();

    stream.base = dublicate_pointer(base);
    stream.cursor = stream.base.pos;
    stream.limit  = limit;

    /* don't skip the first line during the first call */
    stream.status = 2;

    parser.memory    = memory;
    parser.stream    = stream;
    parser.FontInfo  = null;
    parser.get_index = null;

    return 0;
}

function afm_parser_done(parser)
{
    parser.stream = null;
}

function afm_parser_read_int(parser)
{
    var val = new AFM_ValueRec();
    val.type = 3;

    if (afm_parser_read_vals(parser, val, 1) == 1)
    {
        return { pint: val.u, err: 0 };
    }
    return { pint: 0, err: 160 };
}

function afm_parse_track_kern(parser)
{
    var fi = parser.FontInfo;
    var n = -1;

    var ret = afm_parser_read_int(parser);
    if (0 != ret.err)
        return 160;

    fi.NumTrackKern = ret.pint;
    if (fi.NumTrackKern != 0)
    {
        fi.TrackKerns = new Array(fi.NumTrackKern);
    }

    while (true)
    {
        var _key_len = afm_parser_next_key(parser, 1);
        if (_key_len.key == null)
            break;

        var shared_vals = new Array(5);
        shared_vals[0] = new AFM_ValueRec();
        shared_vals[1] = new AFM_ValueRec();
        shared_vals[2] = new AFM_ValueRec();
        shared_vals[3] = new AFM_ValueRec();
        shared_vals[4] = new AFM_ValueRec();

        switch (afm_tokenize(_key_len.key, _key_len.len))
        {
            case 56:
                n++;

                if (n >= fi.NumTrackKern)
                    return 160;

                var tk = fi.TrackKerns[n];

                shared_vals[0].type = 3;
                shared_vals[1].type = 2;
                shared_vals[2].type = 2;
                shared_vals[3].type = 2;
                shared_vals[4].type = 2;
                if (afm_parser_read_vals( parser, shared_vals, 5) != 5)
                    return 160;

                tk.degree     = shared_vals[0].u;
                tk.min_ptsize = shared_vals[1].u;
                tk.min_kern   = shared_vals[2].u;
                tk.max_ptsize = shared_vals[3].u;
                tk.max_kern   = shared_vals[4].u;

                /* is this correct? */
                if (tk.degree < 0 && tk.min_kern > 0)
                    tk.min_kern = -tk.min_kern;
                break;

            case 23:
            case 21:
            case 20:
                fi.NumTrackKern = n + 1;
                return 0;

            case 75:
                break;

            default:
                return 160;
        }
    }

    return 160;
}

function afm_compare_kern_pairs(kp1, kp2)
{
    var index1 = kp1.index1 << 16 | kp1.index2;
    var index2 = kp2.index1 << 16 | kp2.index2;

    if ( index1 > index2 )
        return 1;
    else if ( index1 < index2 )
        return -1;
    else
        return 0;
}

function afm_parse_kern_pairs(parser)
{
    var fi = parser.FontInfo;
    var kp = null;
    var n = -1;

    var _key_len = afm_parser_read_int(parser);
    if (null != _key_len.key)
        return 160;

    fi.NumKernPair = _key_len.len;
    if (fi.NumKernPair != 0)
    {
        fi.KernPairs = new Array(fi.NumKernPair);
        for (var i = 0; i < fi.NumKernPair; i++)
            fi.NumKernPair[i] = new AFM_KernPairRec();
    }

    while (true)
    {
        _key_len = afm_parser_next_key(parser, 1);
        if (null == _key_len.key)
            break;

        var token = afm_tokenize(_key_len.key, _key_len.len);
        switch (token)
        {
            case 34:
            case 36:
            case 37:
                var shared_vals = new Array(4);
                shared_vals[0] = new AFM_ValueRec();
                shared_vals[1] = new AFM_ValueRec();
                shared_vals[2] = new AFM_ValueRec();
                shared_vals[3] = new AFM_ValueRec();
                n++;

                if (n >= fi.NumKernPair)
                    return 160;

                kp = fi.KernPairs[n];

                shared_vals[0].type = 5;
                shared_vals[1].type = 5;
                shared_vals[2].type = 3;
                shared_vals[3].type = 3;
                var r = afm_parser_read_vals(parser, shared_vals, 4);
                if (r < 3)
                    return 160;

                kp.index1 = shared_vals[0].u;
                kp.index2 = shared_vals[1].u;
                if (token == 37)
                {
                    kp.x = 0;
                    kp.y = shared_vals[2].u;
                }
                else
                {
                    kp.x = shared_vals[2].u.i;
                    kp.y = (token == 34 && r == 4) ? shared_vals[3].u : 0;
                }
                break;

            case 22:
            case 21:
            case 20:
                fi.NumKernPair = n + 1;
                ft_qsort(fi.KernPairs, fi.NumKernPair, afm_compare_kern_pairs);
                return 0;

            case 75:
                break;

            default:
                return 160;
        }
    }

    return 160;
}

function afm_parse_kern_data(parser)
{
    while (true)
    {
        var _key_len = afm_parser_next_key(parser, 1);
        if (null != _key_len)
            break;

        switch (afm_tokenize(_key_len.key, _key_len.len))
        {
            case 53:
                var error = afm_parse_track_kern(parser);
                if (error != 0)
                    return error;
                break;

            case 50:
            case 51:
                var error = afm_parse_kern_pairs(parser);
                if (error != 0)
                    return error;
                break;

            case 21:
            case 20:
                return 0;

            case 75:
                break;

            default:
                return 160;
        }
    }
    
    return 160;
}

function afm_parser_skip_section(parser, n, end_section)
{
    while ( n-- > 0 )
    {
        var _key_len = afm_parser_next_key(parser, 1);
        if (_key_len.key != null)
            return 160;
    }

    while (true)
    {
        var _key_len = afm_parser_next_key(parser, 1);
        if (_key_len.key != null)
            break;

        var token = afm_tokenize(_key_len.key, _key_len.len);

        if (token == end_section || token == 20)
            return 0;
    }

    return 160;
}

function afm_parser_parse(parser)
{
    var fi = parser.FontInfo;
    var error = 160;
    var metrics_sets = 0;

    if (fi == null)
        return 6;

    var _key_len = afm_parser_next_key(parser, 1);
    if (null == _key_len.key || _key_len.len != 16 || _strncmp(_key_len.key, "StartFontMetrics", 16) != 0)
        return 2;

    while (true)
    {
        _key_len = afm_parser_next_key(parser, 1);
        if (null == _key_len)
            break;

        var shared_vals = new Array(4);
        shared_vals[0] = new AFM_ValueRec();
        shared_vals[1] = new AFM_ValueRec();
        shared_vals[2] = new AFM_ValueRec();
        shared_vals[3] = new AFM_ValueRec();

        var token = afm_tokenize(_key_len.key, _key_len.len);
        switch (token)
        {
            case 40:
                var ret = afm_parser_read_int(parser);
                if (ret.err != 0)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }
                metrics_sets = ret.pint;
                if (metrics_sets != 0 && metrics_sets != 2)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return 7;
                }
                break;

            case 30:
                shared_vals[0].type = 4;
                if (afm_parser_read_vals(parser, shared_vals, 1) != 1)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }

                fi.IsCIDFont = shared_vals[0].u;
                break;

            case 26:
                shared_vals[0].type = 2;
                shared_vals[1].type = 2;
                shared_vals[2].type = 2;
                shared_vals[3].type = 2;
                if (afm_parser_read_vals( parser, shared_vals, 4 ) != 4)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }

                fi.FontBBox.xMin = shared_vals[0].u;
                fi.FontBBox.yMin = shared_vals[1].u;
                fi.FontBBox.xMax = shared_vals[2].u;
                fi.FontBBox.yMax = shared_vals[3].u;
                break;

            case 0:
                shared_vals[0].type = 2;
                if (afm_parser_read_vals(parser, shared_vals, 1) != 1)
                {
                    // fail
                    return error;
                }

                fi.Ascender = shared_vals[0].u;
                break;

            case 14:
                shared_vals[0].type = 2;
                if (afm_parser_read_vals( parser, shared_vals, 1 ) != 1)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }

                fi.Descender = shared_vals[0].u;
                break;

            case 45:
                var ret = afm_parser_read_int(parser);
                if (ret.err != 0)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }
                var n = ret.pint;
                error = afm_parser_skip_section(parser, n, 17);
                if (error != 0)
                    return error;
                break;

            case 49:
                error = afm_parse_kern_data(parser);
                if (error != 0)
                {
                    fi.TrackKerns = null;
                    fi.NumTrackKern = 0;

                    fi.KernPairs = null;
                    fi.NumKernPair = 0;

                    fi.IsCIDFont = 0;

                    return error;
                }
            /* fall through since we only support kern data */
            case 20:
                return 0;

            default:
                break;
        }
    }

    fi.TrackKerns = null;
    fi.NumTrackKern = 0;

    fi.KernPairs = null;
    fi.NumKernPair = 0;

    fi.IsCIDFont = 0;

    return error;
}

/******************************************************************************/
// t1cmap
/******************************************************************************/
function T1_CMapStdRec()
{
    this.cmap = new FT_CMapRec();

    this.code_to_sid = null;
    this.sid_to_string = null;

    this.num_glyphs = 0;
    this.glyph_names = null;

    this.type = 1;
}
function T1_CMapCustomRec()
{
    this.cmap = new FT_CMapRec();
    this.first = 0;
    this.count = 0;
    this.indices = null;

    this.type = 1;
}

// standart
function t1_cmap_std_init(cmap, is_expert)
{
    var face = __FT_CMapRec(cmap).charmap.face;
    var psnames = face.psnames;

    cmap.num_glyphs    = face.type1.num_glyphs;
    cmap.glyph_names   = face.type1.glyph_names;
    cmap.sid_to_string = psnames.adobe_std_strings;
    cmap.code_to_sid   = (is_expert == 1) ? psnames.adobe_expert_encoding : psnames.adobe_std_encoding;
}

function t1_cmap_std_done(cmap)
{
    cmap.num_glyphs    = 0;
    cmap.glyph_names   = null;
    cmap.sid_to_string = null;
    cmap.code_to_sid   = null;
}

function t1_cmap_std_char_index(cmap, char_code)
{
    var result = 0;

    if (char_code < 256)
    {
        /* convert character code to Adobe SID string */
        var code       = cmap.code_to_sid[char_code];
        var glyph_name = cmap.sid_to_string(code);

        /* look for the corresponding glyph name */
        for (var n = 0; n < cmap.num_glyphs; n++ )
        {
            var gname = cmap.glyph_names[n];

            if (gname != null && gname.charCodeAt(0) == glyph_name.charCodeAt(0) && gname == glyph_name)
            {
                result = n;
                break;
            }
        }
    }

    return result;
}

function t1_cmap_std_char_next(cmap, char_code)
{
    var result = 0;
    var pchar_code = char_code + 1;

    while (pchar_code < 256)
    {
        result = t1_cmap_std_char_index( cmap, pchar_code );
        if ( result != 0 )
            return {gindex:result,char_code:pchar_code};

        pchar_code++;
    }
    pchar_code = 0;
    return {gindex:result,char_code:pchar_code};
}

function t1_cmap_standard_init(cmap)
{
    t1_cmap_std_init(cmap, 0);
    return 0;
}

var t1_cmap_standard_class_rec = create_cmap_class_rec(0,t1_cmap_standard_init,t1_cmap_std_done,t1_cmap_std_char_index,t1_cmap_std_char_next,null,null,null,null,null);

// expert
function t1_cmap_expert_init(cmap)
{
    t1_cmap_std_init(cmap, 1);
    return 0;
}

var t1_cmap_expert_class_rec = create_cmap_class_rec(0,t1_cmap_expert_init,t1_cmap_std_done,t1_cmap_std_char_index,t1_cmap_std_char_next,null,null,null,null,null);

// custom
function t1_cmap_custom_init(cmap)
{
    var face = __FT_CMapRec(cmap).charmap.face;
    var encoding = face.type1.encoding;

    cmap.first   = encoding.code_first;
    cmap.count   = (encoding.code_last - cmap.first);

    if (0 > cmap.count)
        cmap.count = 0;

    cmap.indices = encoding.char_index;

    return 0;
}

function t1_cmap_custom_done(cmap)
{
    cmap.indices = null;
    cmap.first   = 0;
    cmap.count   = 0;
}

function t1_cmap_custom_char_index(cmap, char_code)
{
    if (char_code >= cmap.first && char_code < (cmap.first + cmap.count))
        return cmap.indices[char_code];
    return 0;
}

function t1_cmap_custom_char_next(cmap, _char_code)
{
    var result = 0;
    var char_code = _char_code;
    ++char_code;

    if (char_code < cmap.first)
        char_code = cmap.first;

    var last = cmap.first + cmap.count;
    for (; char_code < last; char_code++)
    {
        result = cmap.indices[char_code];
        if (result != 0)
            return {gindex:result,char_code:char_code};
    }

    return {gindex:result,char_code:0};
}

var t1_cmap_custom_class_rec = create_cmap_class_rec(0,t1_cmap_custom_init,t1_cmap_custom_done,t1_cmap_custom_char_index,t1_cmap_custom_char_next,null,null,null,null,null);

// unicode
function t1_get_glyph_name(face, idx)
{
    return face.type1.glyph_names[idx];
}

function t1_cmap_unicode_init(unicodes)
{
    var face = __FT_CMapRec(unicodes).charmap.face;
    var memory  = face.memory;
    var psnames = face.psnames;

    return psnames.unicodes_init(memory, unicodes, face.type1.num_glyphs, t1_get_glyph_name, null, face);
}

function t1_cmap_unicode_done(unicodes)
{
    unicodes.maps = null;
    unicodes.num_maps = 0;
}

function t1_cmap_unicode_char_index(unicodes, char_code)
{
    var face = __FT_CMapRec(unicodes).charmap.face;
    var psnames = face.psnames;

    return psnames.unicodes_char_index(unicodes, char_code);
}

function t1_cmap_unicode_char_next(unicodes, pchar_code)
{
    var face = __FT_CMapRec(unicodes).charmap.face;
    var psnames = face.psnames;

    return psnames.unicodes_char_next(unicodes, pchar_code);
}

var t1_cmap_unicode_class_rec = create_cmap_class_rec(0,t1_cmap_unicode_init,t1_cmap_unicode_done,t1_cmap_unicode_char_index,t1_cmap_unicode_char_next,null,null,null,null,null);

/******************************************************************************/
// psobj
/******************************************************************************/

function ps_table_new(table, count, memory)
{
    table.memory = memory;
    table.elements = new Array(count);

    for (var i = 0; i < count; i++)
        table.elements[i] = new CPointer();

    table.lengths = CreateIntArray(count);

    table.max_elems = count;
    table.init      = 0xDEADBEEF;
    table.num_elems = 0;
    table.block     = null;
    table.capacity  = 0;
    table.cursor    = 0;

    table.funcs = ps_table_funcs;
    return 0;
}

function shift_elements(table, old_base)
{
    var delta  = table.block.pos - old_base.pos;
    var els = table.elements;

    var limit = table.max_elems;
    for (var i = 0; i < limit; i++)
    {
        if (null != els[i])
        {
            els[i].data[els[i].pos] += delta;
        }
    }
}

function reallocate_t1_table(table, new_size)
{
    var memory = table.memory;
    var old_base = table.block;

    table.block = memory.Alloc(new_size);
    var dst = table.block.data;
    if (null != old_base)
    {
        var src = old_base.data;
        for (var i = 0; i < table.capacity; i++)
        {
            dst[i] = src[i];
        }
    }

    var _els = table.elements;
    var _elc = table.max_elems;
    for (var j = 0; j < _elc; j++)
        _els[j].data = dst;

    old_base = null;
    table.capacity = new_size;
    return 0;
}

function ps_table_add(table, idx, object, length)
{
    if (idx < 0 || idx >= table.max_elems)
        return 6;

    if (length < 0)
        return 6;

    /*
    table.elements[idx] = copy_pointer(object, length);
    table.lengths[idx] = length;
    table.cursor = idx + 1;
    */

    if (table.cursor + length > table.capacity)
    {
        var new_size = table.capacity;

        while (new_size < (table.cursor + length))
        {
            /* increase size by 25% and round up to the nearest multiple
             of 1024 */
            new_size += (new_size >> 2) + 1;
            new_size = parseInt((new_size + 1023) / 1024) * 1024;
        }

        reallocate_t1_table(table, new_size);
    }

    /* add the object to the base block and adjust offset */
    table.elements[idx].pos = table.cursor;
    table.lengths[idx] = length;

    var dd = table.block.data;
    var dp = table.cursor;
    var sd = object.data;
    var sp = object.pos;

    for (var i = 0; i < length; i++)
        dd[dp++] = sd[sp++];

    table.cursor += length;

    return 0;
}

function ps_table_done(table)
{
    if (null == table.block)
        return 0;

    reallocate_t1_table(table, table.cursor);
    return 0;
}

function ps_table_release(table)
{
    if (table.init == 0xDEADBEEF)
    {
        table.block = null;
        table.elements = null;
        table.lengths = null;
        table.init = 0;
    }
}

function skip_comment(cur, limit)
{
    while (cur.pos < limit)
    {
        if (IS_PS_NEWLINE(cur.data[cur.pos]))
            break;
        cur.pos++;
    }
}

function skip_spaces(cur, limit)
{
    while (cur.pos < limit)
    {
        if (!IS_PS_SPACE(cur.data[cur.pos]))
        {
            if (cur.data[cur.pos] == 37)
            {
                /* According to the PLRM, a comment is equal to a space. */
                skip_comment(cur, limit);
            }
            else
                break;
        }
        cur.pos++;
    }
}

function skip_literal_string(cur, limit)
{
    var embed = 0;
    var error = 3;

    while (cur.pos < limit)
    {
        var c = cur.data[cur.pos];
        cur.pos++;

        if (c == 92)
        {
            /* Red Book 3rd ed., section `Literal Text Strings', p. 29:     */
            /* A backslash can introduce three different types              */
            /* of escape sequences:                                         */
            /*   - a special escaped char like \r, \n, etc.                 */
            /*   - a one-, two-, or three-digit octal number                */
            /*   - none of the above in which case the backslash is ignored */

            if (cur.pos == limit)
                break;/* error (or to be ignored?) */

            switch (cur.data[cur.pos])
            {
            /* skip `special' escape */
            case 110:
            case 114:
            case 116:
            case 98:
            case 102:
            case 92:
            case 40:
            case 41:
                cur.pos++;
                break;

            default:
                /* skip octal escape or ignore backslash */
                for (var i = 0; i < 3 && cur.pos < limit; i++)
                {
                    if (48 > cur.data[cur.pos] || 55 < cur.data[cur.pos])
                        break;

                    cur.pos++;
                }
            }
        }
        else if (c == 40)
            embed++;
        else if (c == 41)
        {
            embed--;
            if (embed == 0)
            {
                error = 0;
                break;
            }
        }
    }
    return error;
}

function skip_string(cur, limit)
{
    while (cur.pos < limit)
    {
        /* All whitespace characters are ignored. */
        skip_spaces(cur, limit);
        if (cur.pos >= limit)
            break;

        if (!IS_PS_XDIGIT(cur.data[cur.pos]))
            break;
    }

    if (cur.pos < limit && cur.data[cur.pos] != 62)
    {
        return 3;
    }
    else
        cur.pos++;

    return 0;
}

function skip_procedure(cur, limit)
{
    var embed = 0;

    var error = 0;
    for (; cur.pos < limit && error == 0; cur.pos++)
    {
        switch (cur.data[cur.pos])
        {
        case 123:
            ++embed;
            break;

        case 125:
            --embed;
            if (embed == 0)
            {
                cur.pos++;
                return 0;
            }
            break;

        case 40:
            error = skip_literal_string(cur, limit);
            break;

        case 60:
            error = skip_string(cur, limit);
            break;

        case 37:
            skip_comment(cur, limit);
            break;
        }
    }

    if (embed != 0)
        error = 3;

    return error;
}

function ps_parser_skip_PS_token(parser)
{
    /* Note: PostScript allows any non-delimiting, non-whitespace        */
    /*       character in a name (PS Ref Manual, 3rd ed, p31).           */
    /*       PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */

    var cur = dublicate_pointer(parser.cursor);
    var limit = parser.limit;
    var error = 0;

    var cursor_pos = cur.pos;
    skip_spaces(cur, limit);             /* this also skips comments */
    if (cur.pos >= limit)
    {
        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    /* self-delimiting, single-character tokens */
    if (cur.data[cur.pos] == 91 || cur.data[cur.pos] == 93)
    {
        cur.pos++;
        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    /* skip balanced expressions (procedures and strings) */

    if (cur.data[cur.pos] == 123)                              /* {...} */
    {
        error = skip_procedure(cur, limit);
        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    if (cur.data[cur.pos] == 40)                              /* (...) */
    {
        error = skip_literal_string(cur, limit);
        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    if (cur.data[cur.pos] == 60)                              /* <...> */
    {
        if (cur.pos + 1 < limit && cur.data[cur.pos + 1] == 60)   /* << */
        {
            cur.pos += 2;
        }
        else
            error = skip_string(cur, limit);

        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    if (cur.data[cur.pos] == 62)
    {
        cur.pos++;
        if (cur.pos >= limit || cur.data[cur.pos] != 62)             /* >> */
        {
            error = 3;

            parser.error  = error;
            parser.cursor.pos = cur.pos;
            return;
        }
        cur.pos++;
        if (cur.pos == cursor_pos)
        {
            error = 3;
        }

        parser.error  = error;
        parser.cursor.pos = cur.pos;
        return;
    }

    if (cur.data[cur.pos] == 47)
        cur.pos++;

    /* anything else */
    while (cur.pos < limit)
    {
        /* *cur might be invalid (e.g., ')' or '}'), but this   */
        /* is handled by the test `cur == parser->cursor' below */
        if (IS_PS_DELIM(cur.data[cur.pos]))
            break;

        cur.pos++;
    }

    //exit
    if (cur.pos == cursor_pos)
    {
        error = 3;
    }

    parser.error  = error;
    parser.cursor.pos = cur.pos;
}

function ps_parser_skip_spaces(parser)
{
    skip_spaces(parser.cursor, parser.limit);
}

function ps_parser_to_token(parser, token)
{
    var limit = parser.limit;
    var embed = 0;

    token.type  = 0;
    token.start = null;
    token.limit = 0;

    /* first of all, skip leading whitespace */
    ps_parser_skip_spaces(parser);
    var cur = dublicate_pointer(parser.cursor);

    if (cur.pos >= limit)
        return;

    switch (cur.data[cur.pos])
    {
        /************* check for literal string *****************/
    case 40:
        token.type  = 2;
        token.start = dublicate_pointer(cur);

        if (skip_literal_string(cur, limit) == 0)
            token.limit = cur.pos;
        break;

        /************* check for programs/array *****************/
    case 123:
        token.type  = 3;
        token.start = dublicate_pointer(cur);

        if (skip_procedure(cur, limit) == 0)
            token.limit = cur.pos;
        break;

        /************* check for table/array ********************/
        /* XXX: in theory we should also look for "<<"          */
        /*      since this is semantically equivalent to "[";   */
        /*      in practice it doesn't matter (?)               */
    case 91:
        token.type  = 3;
        embed        = 1;
        token.start  = dublicate_pointer(cur);
        cur.pos++;

        /* we need this to catch `[ ]' */
        parser.cursor.pos = cur.pos;
        ps_parser_skip_spaces(parser);
        cur.pos = parser.cursor.pos;

        while (cur.pos < limit && parser.error == 0)
        {
            /* XXX: this is wrong because it does not      */
            /*      skip comments, procedures, and strings */
            if (cur.data[cur.pos] == 91)
                embed++;
            else if (cur.data[cur.pos] == 93)
            {
                embed--;
                if ( embed <= 0 )
                {
                    cur.pos++;
                    token.limit = cur.pos;
                    break;
                }
            }

            parser.cursor.pos = cur.pos;
            ps_parser_skip_PS_token(parser);
            /* we need this to catch `[XXX ]' */
            ps_parser_skip_spaces(parser);
            cur.pos = parser.cursor.pos;
        }
        break;

        /* ************ otherwise, it is any token **************/
    default:
        token.start = dublicate_pointer(cur);
        token.type  = ((cur.data[cur.pos] == 47) ? 4 : 1);
        ps_parser_skip_PS_token(parser);
        cur.pos = parser.cursor.pos;
        if (!parser.error)
            token.limit = cur.pos;
    }

    if (token.limit == 0)
    {
        token.start = null;
        token.type  = 0;
    }

    parser.cursor.pos = cur.pos;
}

function ps_parser_to_token_array(parser, tokens, max_tokens)
{
    var master = new T1_TokenRec();
    var pnum_tokens = -1;
    /* this also handles leading whitespace */
    ps_parser_to_token(parser, master);

    if (master.type == 3)
    {
        var old_cursor = parser.cursor.pos;
        var old_limit  = parser.limit;
        var cur        = 0;

        /* don't include outermost delimiters */
        parser.cursor = dublicate_pointer(master.start);
        parser.cursor.pos += 1;
        parser.limit  = master.limit - 1;

        while (parser.cursor.pos < parser.limit)
        {
            var token = new T1_TokenRec();
            ps_parser_to_token(parser, token);
            if (token.type == 0)
                break;

            if (tokens != null && cur < max_tokens)
            {
                var _cur = tokens[cur];
                _cur.start = dublicate_pointer(token.start);
                _cur.limit = token.limit;
                _cur.type = token.type;
            }

            cur++;
        }

        pnum_tokens = cur;

        parser.cursor.pos = old_cursor;
        parser.limit  = old_limit;
    }

    return pnum_tokens;
}

function ps_tocoordarray(cur, limit, max_coords, coords)
{
    var count = 0;
    if (cur.pos >= limit)
        return count;

    /* check for the beginning of an array; otherwise, only one number */
    /* will be read                                                    */
    var c = cur.data[cur.pos];
    var ender = 0;

    if (c == 91)
        ender = 93;
    else if (c == 123)
        ender = 125;

    if (ender != 0)
        cur.pos++;

    /* now, read the coordinates */
    while ( cur < limit )
    {
        /* skip whitespace in front of data */
        skip_spaces(cur, limit);
        if (cur.pos >= limit)
            return count;

        if (cur.data[cur.pos] == ender)
        {
            cur.pos++;
            break;
        }

        var old_cur = cur.pos;

        if ( coords != NULL && count >= max_coords )
            break;

        /* call PS_Conv_ToFixed() even if coords == NULL */
        /* to properly parse number at `cur'             */
        var dummy = PS_Conv_ToFixed(cur, limit, 0) >> 16;
        if (null != coords)
            coords[count] = dummy;

        if (old_cur == cur.pos)
        {
            count = -1;
            return count;
        }
        else
            count++;

        if (ender == 0)
            break;
    }

    return count;
}

function ps_tofixedarray(cur, limit, max_values, values, power_ten)
{
    var count = 0;
    if (cur.pos >= limit)
        return count;

    /* Check for the beginning of an array.  Otherwise, only one number */
    /* will be read.                                                    */
    var c = cur.data[cur.pos];
    var ender = 0;

    if (c == 91)
        ender = 93;
    else if (c == 123)
        ender = 125;

    if (ender != 0)
        cur.pos++;

    /* now, read the values */
    while (cur.pos < limit)
    {
        /* skip whitespace in front of data */
        skip_spaces(cur, limit);
        if (cur.pos >= limit)
            return count;

        if (cur.data[cur.pos] == ender)
        {
            cur.pos++;
            break;
        }

        var old_cur = cur.pos;
        if (values != null && count >= max_values)
            break;

        /* call PS_Conv_ToFixed() even if coords == NULL */
        /* to properly parse number at `cur'             */
        var dummy = PS_Conv_ToFixed(cur, limit, power_ten);
        if (null != values)
            values[count] = dummy;

        if (old_cur == cur.pos)
        {
            count = -1;
            return count;
        }
        else
            count++;

        if (ender == 0)
            break;
    }

    return count;
}

function ps_tobool(cur, limit)
{
    var result = 0;
    var data = cur.data;
    var pos = cur.pos;

    /* return 1 if we find `true', 0 otherwise */
    if (cur.pos + 3 < limit &&
        data[pos] == 116 &&
        data[pos + 1] == 114 &&
        data[pos + 2] == 117 &&
        data[pos + 3] == 101)
    {
        result = 1;
        cur.pos += 5;
    }
    else if (cur.pos + 4 < limit &&
        data[pos] == 102 &&
        data[pos + 1] == 97 &&
        data[pos + 2] == 108 &&
        data[pos + 3] == 115 &&
        data[pos + 4] == 101)
    {
        result = 0;
        cur.pos   += 6;
    }

    return result;
}

function ps_parser_load_field(parser, field, objects, max_objects, pflags)
{
    var token = new T1_TokenRec();
    /* this also skips leading whitespace */
    ps_parser_to_token(parser, token);
    if (token.type == 0)
        return 3;

    var count = 1;
    var idx   = 0;
    var cur   = dublicate_pointer(token.start);
    var limit = token.limit;

    /* we must detect arrays in /FontBBox */
    if (field.type == 7)
    {
        var token2 = new T1_TokenRec();
        var old_cur   = dublicate_pointer(parser.cursor);
        var old_limit = parser.limit;

        /* don't include delimiters */
        parser.cursor = dublicate_pointer(token.start);
        parser.cursor.pos += 1;
        parser.limit  = token.limit - 1;

        ps_parser_to_token(parser, token2);
        parser.cursor = dublicate_pointer(old_cur);
        parser.limit  = old_limit;

        if (token2.type == 3)
        {
            /* if this is an array and we have no blend, an error occurs */
            if (max_objects == 0)
                return 3;

            count = max_objects;
            idx   = 1;

            /* don't include delimiters */
            cur.pos++;
            limit--;
        }
    }
    else if (token.type == 3)
    {
        /* if this is an array and we have no blend, an error occurs */
        if (max_objects == 0)
            return 3;

        count = max_objects;
        idx   = 1;

        /* don't include delimiters */
        cur.pos++;
        limit--;
    }

    for (; count > 0; count--, idx++)
    {
        var __obj = objects[idx];
        var val;
        skip_spaces(cur, limit);

        switch (field.type)
        {
            case 1:
                val = ps_tobool(cur, limit);
                fire_t1_field(__obj, val, field);
                break;

            case 3:
                val = PS_Conv_ToFixed(cur, limit, 0);
                fire_t1_field(__obj, val, field);
                break;

            case 4:
                val = PS_Conv_ToFixed(cur, limit, 3);
                fire_t1_field(__obj, val, field);
                break;

            case 2:
                val = PS_Conv_ToInt(cur, limit);
                fire_t1_field(__obj, val, field);
                break;

            case 5:
            case 6:
                var len = (limit - cur.pos);

                if (cur.pos >= limit)
                    break;

                /* we allow both a string or a name   */
                /* for cases like /FontName (foo) def */
                if (token.type == 4)
                {
                    /* don't include leading `/' */
                    len--;
                    cur.pos++;
                }
                else if (token.type == 2)
                {
                    /* don't include delimiting parentheses    */
                    /* XXX we don't handle <<...>> here        */
                    /* XXX should we convert octal escapes?    */
                    /*     if so, what encoding should we use? */
                    cur.pos++;
                    len -= 2;
                }
                else
                {
                    return 3;
                }

                /* for this to work (FT_String**)q must have been */
                /* initialized to NULL                            */
                var _s = "";
                for (var i = 0; i < len; i++)
                    _s += String.fromCharCode(cur.data[cur.pos + i]);

                fire_t1_field(__obj, _s, field);
                break;

            case 7:
                var temp = new Array(4);
                temp[0] = 0;
                temp[1] = 0;
                temp[2] = 0;
                temp[3] = 0;

                var result = ps_tofixedarray(cur, limit, 4, temp, 0);

                if ( result < 0 )
                {
                    return 3;
                }

                __obj.xMin = FT_RoundFix(temp[0]);
                __obj.yMin = FT_RoundFix(temp[1]);
                __obj.xMax = FT_RoundFix(temp[2]);
                __obj.yMax = FT_RoundFix(temp[3]);

                break;

            default:
                /* an error occurred */
                return 3;
        }
    }

    return 0;
}

function ps_parser_load_field_table(parser, field, objects, max_objects, pflags)
{
    var elements = new Array(32);
    for (var i = 0; i < 32; i++)
        elements[i] = new T1_TokenRec();

    var fieldrec = create_dublicate_t1_field(field);

    fieldrec.type = 2;
    if (field.type == 9 || field.type == 7)
        fieldrec.type = 3;

    var num_elements = ps_parser_to_token_array(parser, elements, 32);
    if (num_elements < 0)
        return 162;

    if (num_elements > field.array_max)
        num_elements = field.array_max;

    var old_cursor = dublicate_pointer(parser.cursor);
    var old_limit  = parser.limit;

    /* we store the elements count if necessary;           */
    /* we further assume that `count_offset' can't be zero */
    if (field.type != 7 && field.set_field_count != undefined)
    {
        fire_t1_field_count(objects[0], num_elements, field);
    }

    /* we now load each element, adjusting the field.offset on each one */
    var token = 0;
    for ( ; num_elements > 0; num_elements--, token++)
    {
        fieldrec.offset = token;
        parser.cursor = dublicate_pointer(elements[token].start);
        parser.limit  = elements[token].limit;
        ps_parser_load_field(parser, fieldrec, objects, max_objects, 0);
    }

    parser.cursor = dublicate_pointer(old_cursor);
    parser.limit  = old_limit;

    return 0;
}

function ps_parser_to_int(parser)
{
    ps_parser_skip_spaces(parser);
    return PS_Conv_ToInt(parser.cursor, parser.limit);
}

function ps_parser_to_bytes(parser, bytes, max_bytes, delimiters)
{
    ps_parser_skip_spaces(parser);
    var cur = dublicate_pointer(parser.cursor);
    
    if (cur.pos >= parser.limit)
        return 0;

    if (delimiters)
    {
        if (cur.data[cur.pos] != 60)
        {
            return {err: 3, num_bytes: 0};
        }

        cur.pos++;
    }

    var num_bytes = PS_Conv_ASCIIHexDecode(cur, parser.limit, bytes, max_bytes);

    if (delimiters)
    {
        if (cur.pos < parser.limit && cur.data[cur.pos] != 62)
        {
            return {err: 3, num_bytes: num_bytes};
        }

        cur.pos++;
    }

    parser.cursor.pos = cur.pos;
    return {err: 0, num_bytes: num_bytes};
}

function ps_parser_to_fixed(parser, power_ten)
{
    ps_parser_skip_spaces(parser);
    return PS_Conv_ToFixed(parser.cursor, parser.limit, power_ten);
}

function ps_parser_to_coord_array(parser, max_coords, coords)
{
    ps_parser_skip_spaces(parser);
    return PS_Conv_ToFixed(parser.cursor, parser.limit, max_coords, coords);
}

function ps_parser_to_fixed_array(parser, max_values, values, power_ten)
{
    ps_parser_skip_spaces(parser);
    return ps_tofixedarray(parser.cursor, parser.limit, max_values, values, power_ten);
}

function ps_parser_init(parser, base, limit, memory)
{
    parser.error  = 0;
    parser.base   = dublicate_pointer(base);
    parser.limit  = limit;
    parser.cursor = dublicate_pointer(base);
    parser.memory = memory;
    parser.funcs  = ps_parser_funcs;
}

function ps_parser_done(parser)
{
}

function t1_builder_init(builder, face, size, _glyph, hinting)
{
    var glyph = (null == _glyph) ? null : _glyph.root;

    builder.parse_state = 0;
    builder.load_points = 1;

    builder.face = face;
    builder.glyph = glyph;
    builder.memory = face.memory;

    if (glyph != null)
    {
        var loader = glyph.internal.loader;

        builder.loader  = loader;
        builder.base    = loader.base.outline;
        builder.current = loader.current.outline;
        FT_GlyphLoader_Rewind(loader);

        builder.hints_globals = size.internal;
        builder.hints_funcs   = null;

        if (hinting)
            builder.hints_funcs = glyph.internal.glyph_hints;
    }

    builder.pos_x = 0;
    builder.pos_y = 0;

    builder.left_bearing.x = 0;
    builder.left_bearing.y = 0;
    builder.advance.x      = 0;
    builder.advance.y      = 0;

    builder.funcs = t1_builder_funcs;
}

function t1_builder_done(builder)
{
    var glyph = builder.glyph;

    if (glyph != null)
        EquatingOutline(glyph.outline, builder.base);
}

function t1_builder_check_points(builder, count)
{
    return FT_GLYPHLOADER_CHECK_POINTS(builder.loader, count, 0);
}

function t1_builder_add_point(builder, x, y, flag)
{
    var outline = builder.current;
    var base = builder.base;

    if (builder.load_points)
    {
        var point = base.points[outline.points + outline.n_points];
        point.x = (FT_RoundFix(x) >> 16);
        point.y = (FT_RoundFix(y) >> 16);

        base.tags[outline.tags + outline.n_points] = flag ? 1 : 2;
    }
    outline.n_points++;
}

function t1_builder_add_point1(builder, x, y)
{
    var error = t1_builder_check_points(builder, 1);
    if ( !error )
        t1_builder_add_point(builder, x, y, 1);

    return error;
}

function t1_builder_add_contour(builder)
{
    var outline = builder.current;

    /* this might happen in invalid fonts */
    if (outline == null)
    {
        return 3;
    }

    if (builder.load_points == 0)
    {
        outline.n_contours++;
        return 0;
    }

    var error = FT_GLYPHLOADER_CHECK_POINTS(builder.loader, 0, 1);
    if (error == 0)
    {
        if (outline.n_contours > 0)
            builder.base.contours[outline.contours + outline.n_contours - 1] = (outline.n_points - 1);

        outline.n_contours++;
    }

    return error;
}

function t1_builder_start_point(builder, x, y)
{
    var error = 3;

    /* test whether we are building a new contour */
    if (builder.parse_state == 3)
        error = 0;
    else
    {
        builder.parse_state = 3;
        error = t1_builder_add_contour(builder);
        if ( !error )
            error = t1_builder_add_point1(builder, x, y);
    }

    return error;
}

function t1_builder_close_contour(builder)
{
    var outline = builder.current;

    if (outline == null)
        return;

    var base = builder.base;
    var first = (outline.n_contours <= 1) ? 0 : base.contours[outline.contours + outline.n_contours - 2] + 1;

    /* We must not include the last point in the path if it */
    /* is located on the first point.                       */
    if (outline.n_points > 1)
    {
        var p1 = base.points[outline.points + first];
        var p2 = base.points[outline.points + outline.n_points - 1];

        var control = base.tags[outline.tags + outline.n_points - 1];

        /* `delete' last point only if it coincides with the first */
        /* point and it is not a control point (which can happen). */
        if (p1.x == p2.x && p1.y == p2.y)
            if (control == 1)
                outline.n_points--;
    }

    if (outline.n_contours > 0)
    {
        /* Don't add contours only consisting of one point, i.e.,  */
        /* check whether the first and the last point is the same. */
        if (first == outline.n_points - 1)
        {
            outline.n_contours--;
            outline.n_points--;
        }
        else
            base.contours[outline.contours + outline.n_contours - 1] = (outline.n_points - 1);
    }
}

function t1_decrypt(buffer, length, seed)
{
    var mem = dublicate_pointer(buffer);
    PS_Conv_EexecDecode(mem, mem.pos + length, mem, length, seed);
}

/******************************************************************************/
// t1decode
/******************************************************************************/

var t1_args_count = [
    0, /* none */
    0, /* endchar */
    2, /* hsbw */
    5, /* seac */
    4, /* sbw */
    0, /* closepath */
    1, /* hlineto */
    1, /* hmoveto */
    4, /* hvcurveto */
    2, /* rlineto */
    2, /* rmoveto */
    6, /* rrcurveto */
    4, /* vhcurveto */
    1, /* vlineto */
    1, /* vmoveto */
    0, /* dotsection */
    2, /* hstem */
    6, /* hstem3 */
    2, /* vstem */
    6, /* vstem3 */
    2, /* div */
    -1, /* callothersubr */
    1, /* callsubr */
    0, /* pop */
    0, /* return */
    2, /* setcurrentpoint */
    2  /* opcode 15 (undocumented and obsolete) */
];

function t1_lookup_glyph_by_stdcharcode(decoder, charcode)
{
    /* check range of standard char code */
    if (charcode < 0 || charcode > 255)
        return -1;

    var glyph_name = decoder.psnames.adobe_std_strings(decoder.psnames.adobe_std_encoding[charcode]);

    for (var n = 0; n < decoder.num_glyphs; n++)
    {
        var name = decoder.glyph_names[n];

        if (name != null && name == glyph_name)
            return n;
    }

    return -1;
}

function t1operator_seac(decoder, asb, adx, ady, bchar, achar)
{
    var bchar_index, achar_index;

    if (decoder.seac)
        return 160;

    var error = 0;
    var face = decoder.builder.face;

    /* seac weirdness */
    adx += decoder.builder.left_bearing.x;

    /* `glyph_names' is set to 0 for CID fonts which do not */
    /* include an encoding.  How can we deal with these?    */
    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (decoder.glyph_names == null && null == face.internal.incremental_interface)
        return 160;

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (face.internal.incremental_interface != null)
    {
        /* the caller must handle the font encoding also */
        bchar_index = bchar;
        achar_index = achar;
    }
    else// #endif
    {
        bchar_index = t1_lookup_glyph_by_stdcharcode(decoder, bchar);
        achar_index = t1_lookup_glyph_by_stdcharcode(decoder, achar);
    }

    if (bchar_index < 0 || achar_index < 0)
        return 160;

    /* if we are trying to load a composite glyph, do not load the */
    /* accent character and return the array of subglyphs.         */
    if (decoder.builder.no_recurse)
    {
        var glyph = decoder.builder.glyph;
        var loader = glyph.internal.loader;

        /* reallocate subglyph array if necessary */
        error = FT_GlyphLoader_CheckSubGlyphs(loader, 2);
        if (error != 0)
            return error;

        var subg = loader.current.subglyphs[0];

        /* subglyph 0 = base character */
        subg.index = bchar_index;
        subg.flags = 2 | 512;
        subg.arg1  = 0;
        subg.arg2  = 0;

        subg = loader.current.subglyphs[1];

        /* subglyph 1 = accent character */
        subg.index = achar_index;
        subg.flags = 2;
        subg.arg1  = FT_RoundFix(adx - asb) >> 16;
        subg.arg2  = FT_RoundFix(ady) >> 16;

        /* set up remaining glyph fields */
        glyph.num_subglyphs = 2;
        glyph.subglyphs     = loader.base.subglyphs;
        glyph.format        = 1668246896;

        loader.current.num_subglyphs = 2;
        return error;
    }

    /* First load `bchar' in builder */
    /* now load the unscaled outline */

    FT_GlyphLoader_Prepare(decoder.builder.loader);  /* prepare loader */

    /* the seac operator must not be nested */
    decoder.seac = 1;
    error = t1_decoder_parse_glyph(decoder, bchar_index);
    decoder.seac = 0;
    if (error != 0)
        return error;

    /* save the left bearing and width of the base character */
    /* as they will be erased by the next load.              */

    var left_bearing = dublicate_vector(decoder.builder.left_bearing);
    var advance      = dublicate_vector(decoder.builder.advance);

    decoder.builder.left_bearing.x = 0;
    decoder.builder.left_bearing.y = 0;

    decoder.builder.pos_x = adx - asb;
    decoder.builder.pos_y = ady;

    /* Now load `achar' on top of */
    /* the base outline           */

    /* the seac operator must not be nested */
    decoder.seac = 1;
    error = t1_decoder_parse_glyph(decoder, achar_index);
    decoder.seac = 0;
    if (error != 0)
        return error;

    /* restore the left side bearing and   */
    /* advance width of the base character */

    decoder.builder.left_bearing = left_bearing;
    decoder.builder.advance      = advance;

    decoder.builder.pos_x = 0;
    decoder.builder.pos_y = 0;

    return error;
}

function t1_decoder_parse_charstrings(decoder, charstring_base, charstring_len)
{
    var builder = decoder.builder;
    var x, y, orig_x, orig_y;
    var known_othersubr_result_cnt   = 0;
    var unknown_othersubr_result_cnt = 0;
    var large_int = 0;

    /* compute random seed from stack address of parameter */
    var seed = parseInt(Math.random() * 0xFFFF);
    if (seed == 0)
        seed = 0x7384;

    /* First of all, initialize the decoder */
    decoder.top  = 0;
    decoder.zone = 0;
    var zone  = 0;
    var zones = decoder.zones;

    builder.parse_state = 0;

    var hinter = builder.hints_funcs;

    /* a font that reads BuildCharArray without setting */
    /* its values first is buggy, but ...               */
    if (decoder.buildchar != null && decoder.len_buildchar > 0)
    {
        for (var i = 0; i < decoder.len_buildchar; i++)
        {
            decoder.buildchar[i] = 0;
        }
    }

    zones[zone].base = dublicate_pointer(charstring_base);
    zones[zone].limit  = charstring_base.pos + charstring_len;
    var limit = zones[zone].limit;

    zones[zone].cursor = 0;
    var ip = dublicate_pointer(zones[zone].base);

    var error = 0;

    x = orig_x = builder.pos_x;
    y = orig_y = builder.pos_y;

    /* begin hints recording session, if any */
    if (hinter != null)
        hinter.open(hinter.hints);

    large_int = 0;

    /* now, execute loop */
    while (ip.pos < limit)
    {
        var tops = decoder.stack;
        var top = decoder.top;
        var op = 0;
        var value = 0;

        /*********************************************************************/
        /*                                                                   */
        /* Decode operator or operand                                        */
        /*                                                                   */
        /*                                                                   */
        /* first of all, decompress operator or value */
        var _oper = ip.data[ip.pos];
        ip.pos++;
        switch (_oper)
        {
        case 1:
            op = 16;
            break;

        case 3:
            op = 18;
            break;
        case 4:
            op = 14;
            break;
        case 5:
            op = 9;
            break;
        case 6:
            op = 6;
            break;
        case 7:
            op = 13;
            break;
        case 8:
            op = 11;
            break;
        case 9:
            op = 5;
            break;
        case 10:
            op = 22;
            break;
        case 11:
            op = 24;
            break;

        case 13:
            op = 2;
            break;
        case 14:
            op = 1;
            break;

        case 15:          /* undocumented, obsolete operator */
            op = 26;
            break;

        case 21:
            op = 10;
            break;
        case 22:
            op = 7;
            break;

        case 30:
            op = 12;
            break;
        case 31:
            op = 8;
            break;

        case 12:
            if (ip.pos > limit)
                return 160;

            var _oper2 = ip.data[ip.pos];
            ip.pos++;
            switch (_oper2)
            {
            case 0:
                op = 15;
                break;
            case 1:
                op = 19;
                break;
            case 2:
                op = 17;
                break;
            case 6:
                op = 3;
                break;
            case 7:
                op = 4;
                break;
            case 12:
                op = 20;
                break;
            case 16:
                op = 21;
                break;
            case 17:
                op = 23;
                break;
            case 33:
                op = 25;
                break;

            default:
                return 160;
            }
            break;

        case 255:    /* four bytes integer */
            if (ip.pos + 4 > limit)
            {
                return 160;
            }

            value = ((ip.data[ip.pos] << 24) | (ip.data[ip.pos + 1] << 16) | (ip.data[ip.pos + 2] << 8) |  (ip.data[ip.pos + 3]));
            ip.pos += 4;

            /* According to the specification, values > 32000 or < -32000 must */
            /* be followed by a `div' operator to make the result be in the    */
            /* range [-32000;32000].  We expect that the second argument of    */
            /* `div' is not a large number.  Additionally, we don't handle     */
            /* stuff like `<large1> <large2> <num> div <num> div' or           */
            /* <large1> <large2> <num> div div'.  This is probably not allowed */
            /* anyway.                                                         */
            if (value > 32000 || value < -32000)
            {
                if (large_int)
                {
                }
                else
                    large_int = 1;
            }
            else
            {
                if (!large_int)
                    value <<= 16;
            }

            break;

        default:
            if (ip.data[ip.pos - 1] >= 32)
            {
                if (ip.data[ip.pos - 1] < 247)
                    value = ip.data[ip.pos - 1] - 139;
                else
                {
                    ip.pos++;
                    if (ip.pos > limit)
                    {
                        return 160;
                    }

                    if (ip.data[ip.pos - 2] < 251)
                        value =  ((ip.data[ip.pos - 2] - 247) << 8) + ip.data[ip.pos - 1] + 108;
                    else
                        value = -(((ip.data[ip.pos - 2] - 251) << 8) + ip.data[ip.pos - 1] + 108);
                }

                if (!large_int)
                    value <<= 16;
            }
            else
            {
                return 160;
            }
        }

        if (unknown_othersubr_result_cnt > 0)
        {
            switch (op)
            {
                case 22:
                case 24:
                case 0:
                case 23:
                    break;

                default:
                    /* all operands have been transferred by previous pops */
                    unknown_othersubr_result_cnt = 0;
                    break;
            }
        }

        if ( large_int && !(op == 0 || op == 20))
        {
            large_int = 0;
        }

        /*********************************************************************/
        /*                                                                   */
        /*  Push value on stack, or process operator                         */
        /*                                                                   */
        /*                                                                   */
        if (op == 0)
        {
            if (top >= 256)
            {
                return 160;
            }

            tops[top] = value;
            top++;
            decoder.top = top;
        }
        else if ( op == 21 )  /* callothersubr */
        {
            if (top < 2)
                return 130;

            top -= 2;

            var subr_no = (tops[top + 1] >> 16) & 0xFFFF;
            var arg_cnt = (tops[top] >> 16) & 0xFFFF;

            /***********************************************************/
            /*                                                         */
            /* remove all operands to callothersubr from the stack     */
            /*                                                         */
            /* for handled othersubrs, where we know the number of     */
            /* arguments, we increase the stack by the value of        */
            /* known_othersubr_result_cnt                              */
            /*                                                         */
            /* for unhandled othersubrs the following pops adjust the  */
            /* stack pointer as necessary                              */

            if (arg_cnt > top)
                return 130;

            top -= arg_cnt;

            known_othersubr_result_cnt   = 0;
            unknown_othersubr_result_cnt = 0;

            /* XXX TODO: The checks to `arg_count == <whatever>'       */
            /* might not be correct; an othersubr expects a certain    */
            /* number of operands on the PostScript stack (as opposed  */
            /* to the T1 stack) but it doesn't have to put them there  */
            /* by itself; previous othersubrs might have left the      */
            /* operands there if they were not followed by an          */
            /* appropriate number of pops                              */
            /*                                                         */
            /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */
            /* accept a font that contains charstrings like            */
            /*                                                         */
            /*     100 200 2 20 callothersubr                          */
            /*     300 1 20 callothersubr pop                          */
            /*                                                         */
            /* Perhaps this is the reason why BuildCharArray exists.   */

            switch (subr_no)
            {
                case 0:                     /* end flex feature */
                    if (arg_cnt != 3)
                        return 160;

                    if (decoder.flex_state == 0 || decoder.num_flex_vectors != 7)
                    {
                        return 160;
                    }

                    /* the two `results' are popped by the following setcurrentpoint */
                    tops[top] = x;
                    tops[top + 1] = y;
                    known_othersubr_result_cnt = 2;
                    break;

                case 1:                     /* start flex feature */
                    if (arg_cnt != 0)
                        return 160;

                    decoder.flex_state        = 1;
                    decoder.num_flex_vectors  = 0;

                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;

                    error = t1_builder_check_points(builder, 6);
                    if (error != 0)
                        return error;

                    break;

                case 2:                     /* add flex vectors */
                    if (arg_cnt != 0)
                        return 160;

                    if (decoder.flex_state == 0)
                    {
                        return 160;
                    }

                    /* note that we should not add a point for index 0; */
                    /* this will move our current position to the flex  */
                    /* point without adding any point to the outline    */
                    var idx = decoder.num_flex_vectors;
                    decoder.num_flex_vectors++;
                    if (idx > 0 && idx < 7)
                        t1_builder_add_point(builder, x, y, (idx == 3 || idx == 6) ? 1 : 0);

                    break;

                case 3:                     /* change hints */
                    if (arg_cnt != 1)
                        return 160;

                    known_othersubr_result_cnt = 1;

                    if (hinter)
                        hinter.reset(hinter.hints, builder.current.n_points);
                    break;

                case 12:
                case 13:
                    /* counter control hints, clear stack */
                    top = 0;
                    break;

                case 14:
                case 15:
                case 16:
                case 17:
                case 18:                    /* multiple masters */
                {
                    var blend = decoder.blend;

                    if ( !blend )
                    {
                        return 160;
                    }

                    var num_points = subr_no - 13;
                    if (subr_no == 18)
                        num_points++;
                    if (arg_cnt != (num_points * blend.num_designs))
                    {
                        return 160;
                    }

                    /* We want to compute                                    */
                    /*                                                       */
                    /*   a0*w0 + a1*w1 + ... + ak*wk                         */
                    /*                                                       */
                    /* but we only have a0, a1-a0, a2-a0, ..., ak-a0.        */
                    /*                                                       */
                    /* However, given that w0 + w1 + ... + wk == 1, we can   */
                    /* rewrite it easily as                                  */
                    /*                                                       */
                    /*   a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk     */
                    /*                                                       */
                    /* where k == num_designs-1.                             */
                    /*                                                       */
                    /* I guess that's why it's written in this `compact'     */
                    /* form.                                                 */
                    /*                                                       */
                    var delta  = top + num_points;
                    var values = top;
                    for (var nn = 0; nn < num_points; nn++)
                    {
                        var tmp = tops[values];

                        for (var mm = 1; mm < blend.num_designs; mm++)
                            tmp += FT_MulFix(tops[delta++], blend.weight_vector[mm]);

                        tops[values++] = tmp;
                    }

                    known_othersubr_result_cnt = num_points;
                    break;
                }

                case 19:
                    /* <idx> 1 19 callothersubr                             */
                    /* => replace elements starting from index cvi( <idx> ) */
                    /*    of BuildCharArray with WeightVector               */
                    var blend = decoder.blend;

                    if (arg_cnt != 1 || blend == null)
                        return 160;

                    var idx = (tops[top] >> 16) & 0xFFFF;

                    if (idx < 0 || (idx + blend.num_designs) > decoder.len_buildchar)
                        return 160;

                    for (var ii = 0; ii < blend.num_designs; ii++)
                        decoder.buildchar[idx + ii] = blend.weight_vector[ii];

                    break;

                case 20:
                    /* <arg1> <arg2> 2 20 callothersubr pop   */
                    /* ==> push <arg1> + <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        return 160;

                    tops[top] += tops[top + 1]; /* XXX (over|under)flow */

                    known_othersubr_result_cnt = 1;
                    break;

                case 21:
                    /* <arg1> <arg2> 2 21 callothersubr pop   */
                    /* ==> push <arg1> - <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        return 160;

                    tops[top] -= tops[top + 1]; /* XXX (over|under)flow */

                    known_othersubr_result_cnt = 1;
                    break;

                case 22:
                    /* <arg1> <arg2> 2 22 callothersubr pop   */
                    /* ==> push <arg1> * <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        return 160;

                    tops[top] = FT_MulFix(tops[top], tops[top + 1]);

                    known_othersubr_result_cnt = 1;
                    break;

                case 23:
                    /* <arg1> <arg2> 2 23 callothersubr pop   */
                    /* ==> push <arg1> / <arg2> onto T1 stack */
                    if (arg_cnt != 2 || tops[top + 1] == 0)
                        return 160;

                    tops[top] = FT_DivFix(tops[top], tops[top + 1]);

                    known_othersubr_result_cnt = 1;
                    break;

                case 24:
                    /* <val> <idx> 2 24 callothersubr               */
                    /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
                    var blend = decoder.blend;

                    if (arg_cnt != 2 || blend == null)
                        return 160;

                    var idx = (tops[top + 1] >> 16) & 0xFFFF;

                    if (idx < 0 || idx >= decoder.len_buildchar)
                        return 160;

                    decoder.buildchar[idx] = tops[top];

                    break;

                case 25:
                    /* <idx> 1 25 callothersubr pop        */
                    /* ==> push BuildCharArray[cvi( idx )] */
                    /*     onto T1 stack                   */
                    var blend = decoder.blend;

                    if (arg_cnt != 1 || blend == null)
                        return 160;

                    idx = (tops[top] >> 16) & 0xFFFF;

                    if (idx < 0 || idx >= decoder.len_buildchar)
                        return 160;

                    tops[top] = decoder.buildchar[idx];

                    known_othersubr_result_cnt = 1;
                    break;

                case 27:
                    /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
                    /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
                    /*     otherwise push <res2>                          */
                    if (arg_cnt != 4)
                        return 160;

                    if (tops[top + 2] > tops[top + 3])
                        tops[top] = tops[top + 1];

                    known_othersubr_result_cnt = 1;
                    break;

                case 28:
                    /* 0 28 callothersubr pop                               */
                    /* => push random value from interval [0, 1) onto stack */
                    if (arg_cnt != 0)
                        return 160;

                    var Rand = seed;
                    if (Rand >= 0x8000)
                        Rand++;

                    tops[top] = Rand;

                    seed = FT_MulFix(seed, 0x10000 - seed);
                    if (seed == 0)
                        seed += 0x2873;

                    known_othersubr_result_cnt = 1;
                    break;

                default:
                    if (arg_cnt >= 0 && subr_no >= 0)
                    {
                        unknown_othersubr_result_cnt = arg_cnt;
                        break;
                    }
                    /* fall through */

                    return 160;
            }

            top += known_othersubr_result_cnt;
            decoder.top = top;
        }
        else  /* general operator */
        {
            var num_args = t1_args_count[op];

            if (top < num_args)
                return 130;

            /* XXX Operators usually take their operands from the        */
            /*     bottom of the stack, i.e., the operands are           */
            /*     decoder->stack[0], ..., decoder->stack[num_args - 1]; */
            /*     only div, callsubr, and callothersubr are different.  */
            /*     In practice it doesn't matter (?).                    */

            top -= num_args;

            switch (op)
            {
                case 1:

                    t1_builder_close_contour(builder);

                    /* close hints recording session */
                    if (hinter != null)
                    {
                        if (hinter.close(hinter.hints, builder.current.n_points))
                            return 160;

                        /* apply hints to the loaded glyph outline now */
                        hinter.apply(hinter.hints, builder.current, builder.hints_globals, decoder.hint_mode);
                    }

                    /* add current outline to the glyph slot */
                    FT_GlyphLoader_Add(builder.loader);

                    /* the compiler should optimize away this empty loop but ... */
                    /* return now! */
                    return 0;

                case 2:
                    builder.parse_state = 1;

                    builder.left_bearing.x += tops[top];
                    builder.advance.x       = tops[top + 1];
                    builder.advance.y       = 0;

                    orig_x = x = builder.pos_x + tops[top];
                    orig_y = y = builder.pos_y;

                    /* the `metrics_only' indicates that we only want to compute */
                    /* the glyph's metrics (lsb + advance width), not load the   */
                    /* rest of it; so exit immediately                           */
                    if (builder.metrics_only)
                        return 0;

                    break;

                case 3:
                    return t1operator_seac(decoder, tops[top], tops[top + 1], tops[top + 2],
                        (tops[top + 3] >> 16) & 0xFFFF, (tops[top + 4] >> 16) & 0xFFFF);

                case 4:
                    builder.parse_state = 1;

                    builder.left_bearing.x += tops[top];
                    builder.left_bearing.y += tops[top + 1];
                    builder.advance.x       = tops[top + 2];
                    builder.advance.y       = tops[top + 3];

                    x = builder.pos_x + tops[top];
                    y = builder.pos_y + tops[top + 1];

                    /* the `metrics_only' indicates that we only want to compute */
                    /* the glyph's metrics (lsb + advance width), not load the   */
                    /* rest of it; so exit immediately                           */
                    if (builder.metrics_only)
                        return 0;

                    break;

                case 5:
                    /* if there is no path, `closepath' is a no-op */
                    if (builder.parse_state == 3 || builder.parse_state == 2)
                        t1_builder_close_contour(builder);

                    builder.parse_state = 1;
                    break;

                case 6:

                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;

                    x += tops[top];
                    error = t1_builder_add_point1(builder, x, y);
                    if (error != 0)
                        return error;

                    break;

                case 7:
                    x += tops[top];
                    if (decoder.flex_state == 0)
                    {
                        if (builder.parse_state == 0)
                            return 160;
                        builder.parse_state = 2;
                    }
                    break;

                case 8:

                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;
                    error = t1_builder_check_points(builder, 3);
                    if (error != 0)
                        return error;

                    x += tops[top];
                    t1_builder_add_point(builder, x, y, 0);
                    x += tops[top + 1];
                    y += tops[top + 2];
                    t1_builder_add_point(builder, x, y, 0);
                    y += tops[top + 3];
                    t1_builder_add_point(builder, x, y, 1);
                    break;

                case 9:
                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;

                    x += tops[top];
                    y += tops[top + 1];

                    error = t1_builder_add_point1(builder, x, y);
                    if (error != 0)
                        return error;

                    break;

                case 10:
                    x += tops[top];
                    y += tops[top + 1];
                    if (decoder.flex_state == 0)
                    {
                        if (builder.parse_state == 0)
                            return 160;
                        builder.parse_state = 2;
                    }
                    break;

                case 11:

                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;
                    error = t1_builder_check_points(builder, 3);
                    if (error != 0)
                        return error;

                    x += tops[top];
                    y += tops[top + 1];
                    t1_builder_add_point(builder, x, y, 0);

                    x += tops[top + 2];
                    y += tops[top + 3];
                    t1_builder_add_point(builder, x, y, 0);

                    x += tops[top + 4];
                    y += tops[top + 5];
                    t1_builder_add_point(builder, x, y, 1);
                    break;

                case 12:
                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;
                    error = t1_builder_check_points(builder, 3);
                    if (error != 0)
                        return error;

                    y += tops[top];
                    t1_builder_add_point(builder, x, y, 0);
                    x += tops[top + 1];
                    y += tops[top + 2];
                    t1_builder_add_point(builder, x, y, 0);
                    x += tops[top + 3];
                    t1_builder_add_point(builder, x, y, 1);
                    break;

                case 13:
                    error = t1_builder_start_point(builder, x, y);
                    if (error != 0)
                        return error;

                    y += tops[top];
                    error = t1_builder_add_point1(builder, x, y);
                    if (error != 0)
                        return error;
                    break;

                case 14:
                    y += tops[top];
                    if (decoder.flex_state == 0)
                    {
                        if (builder.parse_state == 0)
                            return 160;
                        builder.parse_state = 2;
                    }
                    break;

                case 20:
                    /* if `large_int' is set, we divide unscaled numbers; */
                    /* otherwise, we divide numbers in 16.16 format --    */
                    /* in both cases, it is the same operation            */
                    tops[top] = FT_DivFix(tops[top], tops[top + 1]);
                    ++top;

                    large_int = 0;
                    break;

                case 22:
                {
                    var idx = (tops[top] >> 16) & 0xFFFF;
                    if (idx < 0 || idx >= decoder.num_subrs)
                        return 160;

                    if (zone >= 16)
                        return 160;

                    zones[zone].cursor = dublicate_pointer(ip);  /* save current instruction pointer */
                    zone++;

                    /* The Type 1 driver stores subroutines without the seed bytes. */
                    /* The CID driver stores subroutines with seed bytes.  This     */
                    /* case is taken care of when decoder->subrs_len == 0.          */
                    zones[zone].base = dublicate_pointer(decoder.subrs[idx]);

                    if (decoder.subrs_len != 0)
                        zones[zone].limit = zones[zone].base.pos + decoder.subrs_len[idx];
                    else
                    {
                        /* We are using subroutines from a CID font.  We must adjust */
                        /* for the seed bytes.                                       */
                        zones[zone].base.pos  += (decoder.lenIV >= 0 ? decoder.lenIV : 0);
                        zones[zone].limit = decoder.subrs[idx + 1].pos;
                    }

                    zones[zone].cursor = dublicate_pointer(zones[zone].base);

                    if (zones[zone].base == null)
                        return 160;

                    decoder.zone = zone;
                    ip           = dublicate_pointer(zones[zone].base);
                    limit        = zones[zone].limit;
                    break;
                }

                case 23:

                    if (known_othersubr_result_cnt > 0)
                    {
                        known_othersubr_result_cnt--;
                        /* ignore, we pushed the operands ourselves */
                        break;
                    }

                    if (unknown_othersubr_result_cnt == 0)
                        return 160;

                    unknown_othersubr_result_cnt--;
                    top++;   /* `push' the operand to callothersubr onto the stack */
                    break;

                case 24:

                    if (zone <= 0)
                        return 160;

                    zone--;
                    ip           = dublicate_pointer(zones[zone].cursor);
                    limit        = zones[zone].limit;
                    decoder.zone = zone;
                    break;

                case 15:
                    break;

                case 16:
                    /* record horizontal hint */
                    if (hinter != null)
                    {
                        /* tops[0] += builder->left_bearing.y; */
                        hinter.stem(hinter.hints, 1, tops, top);
                    }
                    break;

                case 17:
                    /* record horizontal counter-controlled hints */
                    if (hinter != null)
                        hinter.stem3(hinter.hints, 1, tops, top);
                    break;

                case 18:
                    /* record vertical hint */
                    if (hinter != null)
                    {
                        tops[top] += orig_x;
                        hinter.stem(hinter.hints, 0, tops, top);
                    }
                    break;

                case 19:
                    /* record vertical counter-controlled hints */
                    if (hinter != null)
                    {
                        tops[top] += orig_x;
                        tops[top + 2] += orig_x;
                        tops[top + 4] += orig_x;
                        hinter.stem3(hinter.hints, 0, tops, top);
                    }
                    break;

                case 25:
                    /* From the T1 specification, section 6.4:                */
                    /*                                                        */
                    /*   The setcurrentpoint command is used only in          */
                    /*   conjunction with results from OtherSubrs procedures. */

                    /* known_othersubr_result_cnt != 0 is already handled     */
                    /* above.                                                 */

                    /* Note, however, that both Ghostscript and Adobe         */
                    /* Distiller handle this situation by silently ignoring   */
                    /* the inappropriate `setcurrentpoint' instruction.  So   */
                    /* we do the same.                                        */
                    x = tops[top];
                    y = tops[top + 1];
                    decoder.flex_state = 0;
                    break;

                case 26:
                    /* nothing to do except to pop the two arguments */
                    break;

                default:
                    return 160;
            }

            /* XXX Operators usually clear the operand stack;  */
            /*     only div, callsubr, callothersubr, pop, and */
            /*     return are different.                       */
            /*     In practice it doesn't matter (?).          */

            decoder.top = top;

        } /* general operator processing */

    } /* while ip < limit */

    return error;
}

function t1_decoder_parse_glyph(decoder, glyph)
{
    return decoder.parse_callback(decoder, glyph);
}

function t1_decoder_init(decoder, face, size, slot, glyph_names, blend, hinting, hint_mode, parse_callback)
{
    decoder.clear();

    var psnames = FT_FACE_FIND_GLOBAL_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_CMAPS);
    if (psnames == null)
        return 7;

    decoder.psnames = psnames;

    t1_builder_init(decoder.builder, face, size, slot, hinting);

    /* decoder->buildchar and decoder->len_buildchar have to be  */
    /* initialized by the caller since we cannot know the length */
    /* of the BuildCharArray                                     */
    decoder.num_glyphs     = face.num_glyphs;
    decoder.glyph_names    = glyph_names;
    decoder.hint_mode      = hint_mode;
    decoder.blend          = blend;
    decoder.parse_callback = parse_callback;

    decoder.funcs          = t1_decoder_funcs;

    return 0;
}

function t1_decoder_done(decoder)
{
    t1_builder_done(decoder.builder);
}


/******************************************************************************/
// psaux
/******************************************************************************/
function PS_Table_FuncsRec(_init, _done, _add, _release)
{
    this.init       = _init;
    this.done       = _done;
    this.add        = _add;
    this.release    = _release;
}

function PS_TableRec()
{
    this.block = null;
    this.cursor = 0;
    this.capacity = 0;
    this.init = 0;

    this.max_elems = 0;
    this.num_elems = 0;
    this.elements = null;
    this.lengths = null;

    this.memory = null;
    this.funcs = new PS_Table_FuncsRec(null, null, null, null);

    this.clear = function()
    {
        this.block = null;
        this.cursor = 0;
        this.capacity = 0;
        this.init = 0;

        this.max_elems = 0;
        this.num_elems = 0;
        this.elements = null;
        this.lengths = null;

        this.memory = null;
        this.funcs = new PS_Table_FuncsRec(null, null, null, null);
    }
}

function T1_TokenRec()
{
    this.start = new CPointer();
    this.limit = 0;
    this.type = 0;
}

function T1_FieldRec()
{
    this.ident = "";
    this.location = 0;
    this.type = 0;
    this.reader = null;
    this.offset = 0; // у нас это просто индекс члена класса. А не сдвиг в памяти
    this.size = 0;
    this.array_max = 0;

    this.count_offset = 0;
    this.dict = 0;

    this.set_field = null;
    this.set_field_count = null;
}

function create_dublicate_t1_field(_field)
{
    var ret = new T1_FieldRec();

    ret.ident = _field.ident;
    ret.location = _field.location;
    ret.type = _field.type;
    ret.reader = _field.reader;
    ret.offset = _field.offset; // у нас это просто индекс в МАССИВЕ (т.е. для не массива это поле не нужно)
    ret.size = _field.size;     // не пользуем (на будущее оставим)
    ret.array_max = _field.array_max;

    ret.count_offset = _field.count_offset;
    ret.dict = _field.dict;

    ret.set_field = _field.set_field;
    ret.set_field_count = _field.set_field_count;

    return ret;
}

function fire_t1_field(obj, val, f)
{
    f.set_field(obj, val, f);
}
function fire_t1_field_count(obj, val, f)
{
    f.set_field_count(obj, val, f);
}

function create_t1_field(_ident, _location, _type, _reader, _offset, _size, _array_max, _count_offset, _dict, func_set_field, func_set_field_count)
{
    var ret = new T1_FieldRec();
    ret.ident = _ident;
    ret.location = _location;
    ret.type = _type;
    ret.reader = _reader;
    ret.offset = _offset;
    ret.size = _size;
    ret.array_max = _array_max;
    ret.count_offset = _count_offset;
    ret.dict = _dict;

    ret.set_field = func_set_field;
    ret.set_field_count = func_set_field_count;

    return ret;
}
function create_t1_field2(_ident, _location, _type, func_set_field, func_set_field_count)
{
    var ret = new T1_FieldRec();
    ret.ident = _ident;
    ret.location = _location;
    ret.type = _type;

    ret.set_field = func_set_field;
    ret.set_field_count = func_set_field_count;

    return ret;
}
function create_t1_field4(_ident, _location, _type, _dict, func_set_field, func_set_field_count)
{
    var ret = new T1_FieldRec();
    ret.ident = _ident;
    ret.location = _location;
    ret.type = _type;
    ret.dict = _dict;

    ret.set_field = func_set_field;
    ret.set_field_count = func_set_field_count;

    return ret;
}
function create_t1_field3(_ident, _location, _type, _max, func_set_field, func_set_field_count)
{
    var ret = new T1_FieldRec();
    ret.ident = _ident;
    ret.location = _location;
    ret.type = _type;
    ret.array_max = _max;

    ret.set_field = func_set_field;
    ret.set_field_count = func_set_field_count;

    return ret;
}
function create_t1_field5(_ident, _location, _type, _max, _dict, func_set_field, func_set_field_count)
{
    var ret = new T1_FieldRec();
    ret.ident = _ident;
    ret.location = _location;
    ret.type = _type;
    ret.array_max = _max;
    ret.dict = _dict;

    ret.set_field = func_set_field;
    ret.set_field_count = func_set_field_count;

    return ret;
}

function PS_Parser_FuncsRec(_init,_done,_skip_spaces,_skip_PS_token,_to_int,_to_fixed,_to_bytes,_to_coord_array,_to_fixed_array,
                            _to_token,_to_token_array,_load_field,_load_field_table)
{
    this.init = _init;
    this.done = _done;

    this.skip_spaces    = _skip_spaces;
    this.skip_PS_token  = _skip_PS_token;

    this.to_int         = _to_int;
    this.to_fixed       = _to_fixed;
    this.to_bytes       = _to_bytes;
    this.to_coord_array = _to_coord_array;
    this.to_fixed_array = _to_fixed_array;
    this.to_token       = _to_token;
    this.to_token_array = _to_token_array;
    this.load_field     = _load_field;
    this.load_field_table = _load_field_table;
}

function T1_Builder_FuncsRec(_init,_done,_check_points,_add_point,_add_point1,_add_contour,_start_point,_close_contour)
{
    this.init = _init;
    this.done = _done;

    this.check_points   = _check_points;
    this.add_point      = _add_point;
    this.add_point1     = _add_point1;
    this.add_contour    = _add_contour;
    this.start_point    = _start_point;
    this.close_contour  = _close_contour;
}

function PS_ParserRec()
{
    this.cursor = 0;
    this.base = null;
    this.limit = 0;
    this.error = 0;
    this.memory = null;

    this.funcs = null;

    this.clear = function()
    {
        this.cursor = 0;
        this.base = null;
        this.limit = 0;
        this.error = 0;
        this.memory = null;

        this.funcs = null;
    }
}

function T1_BuilderRec()
{
    this.memory = null;
    this.face = null;
    this.glyph = null;
    this.loader = null;
    this.base = null;
    this.current = null;

    this.pos_x = 0;
    this.pos_y = 0;

    this.left_bearing = new FT_Vector();
    this.advance = new FT_Vector();

    this.bbox = new FT_BBox();
    this.parse_state = 0;
    this.load_points = 0;
    this.no_recurse = 0;

    this.metrics_only = 0;

    this.hints_funcs = null;
    this.hints_globals = null;

    this.funcs = null;

    this.clear = function()
    {
        this.memory = null;
        this.face = null;
        this.glyph = null;
        this.loader = null;
        this.base = null;
        this.current = null;

        this.pos_x = 0;
        this.pos_y = 0;

        this.left_bearing.x = 0;
        this.left_bearing.y = 0;
        this.advance.x = 0;
        this.advance.y = 0;

        this.bbox.xMin = 0;
        this.bbox.yMin = 0;
        this.bbox.xMax = 0;
        this.bbox.yMax = 0;

        this.parse_state = 0;
        this.load_points = 0;
        this.no_recurse = 0;

        this.metrics_only = 0;

        this.hints_funcs = null;
        this.hints_globals = null;

        this.funcs = null;
    }
}

function T1_Decoder_ZoneRec()
{
    this.cursor = 0;
    this.base = null;
    this.limit = 0;
}

function T1_Decoder_FuncsRec(_init, _done, _parse_charstrings)
{
    this.init = _init;
    this.done = _done;
    this.parse_charstrings = _parse_charstrings;
}

function T1_DecoderRec()
{
    this.builder = new T1_BuilderRec();

    this.stack = CreateIntArray(256);
    this.top = 0;

    this.zones = new Array(16 + 1);
    for (var i = 0; i <= 16; i++)
        this.zones[i] = new T1_Decoder_ZoneRec();

    this.zone = 0;

    this.psnames = null;
    this.num_glyphs = 0;
    this.glyph_names = null;

    this.lenIV = 0;
    this.num_subrs = 0;
    this.subrs = null;
    this.subrs_len = null;

    this.font_matrix = new FT_Matrix();
    this.font_offset = new FT_Vector();

    this.flex_state = 0;
    this.num_flex_vectors = 0;
    this.flex_vectors = new Array(7);
    for (var i = 0; i < 7; i++)
        this.flex_vectors[i] = new FT_Vector();

    this.blend = null;

    this.hint_mode = 0;

    this.parse_callback = null;
    this.funcs = null;

    this.buildchar = null;
    this.len_buildchar = 0;

    this.seac = 0;

    this.clear = function()
    {
        this.builder.clear();

        for (var i = 0; i < 256; i++)
            this.stack[i] = 0;

        this.top = 0;

        for (var i = 0; i <= 16; i++)
        {
            var _z = this.zones[i];
            _z.base = null;
            _z.cursor = 0;
            _z.limit = 0;
        }
        this.zone = 0;

        this.psnames = null;
        this.num_glyphs = 0;
        this.glyph_names = null;

        this.lenIV = 0;
        this.num_subrs = 0;
        this.subrs = null;
        this.subrs_len = null;

        this.font_matrix.xx = 0;
        this.font_matrix.xy = 0;
        this.font_matrix.yx = 0;
        this.font_matrix.yy = 0;

        this.font_offset.x = 0;
        this.font_offset.y = 0;

        this.flex_state = 0;
        this.num_flex_vectors = 0;
        for (var i = 0; i < 7; i++)
        {
            this.flex_vectors[i].x = 0;
            this.flex_vectors[i].y = 0;
        }

        this.blend = null;

        this.hint_mode = 0;

        this.parse_callback = null;
        this.funcs = null;

        this.buildchar = null;
        this.len_buildchar = 0;

        this.seac = 0;
    }
}

var ps_table_funcs = {
    init : ps_table_new,
    done : ps_table_done,
    add : ps_table_add,
    release : ps_table_release
};

var ps_parser_funcs = {
    init : ps_parser_init,
    done : ps_parser_done,
    skip_spaces : ps_parser_skip_spaces,
    skip_PS_token : ps_parser_skip_PS_token,
    to_int : ps_parser_to_int,
    to_fixed : ps_parser_to_fixed,
    to_bytes : ps_parser_to_bytes,
    to_coord_array : ps_parser_to_coord_array,
    to_fixed_array : ps_parser_to_fixed_array,
    to_token : ps_parser_to_token,
    to_token_array : ps_parser_to_token_array,
    load_field : ps_parser_load_field,
    load_field_table : ps_parser_load_field_table
};

var t1_builder_funcs = {
    init : t1_builder_init,
    done : t1_builder_done,
    check_points : t1_builder_check_points,
    add_point : t1_builder_add_point,
    add_point1 : t1_builder_add_point1,
    add_contour : t1_builder_add_contour,
    start_point : t1_builder_start_point,
    close_contour : t1_builder_close_contour
};

var t1_decoder_funcs = {
    init : t1_decoder_init,
    done : t1_decoder_done,
    parse_charstrings : t1_decoder_parse_charstrings
};

var afm_parser_funcs = {
    init : afm_parser_init,
    done : afm_parser_done,
    parse : afm_parser_parse
};

var t1_cmap_classes = {
    standard : t1_cmap_standard_class_rec,
    expert : t1_cmap_expert_class_rec,
    custom : t1_cmap_custom_class_rec,
    unicode : t1_cmap_unicode_class_rec
};

function PSAux_ServiceRec()
{
    this.ps_table_funcs = null;
    this.ps_parser_funcs = null;
    this.t1_builder_funcs = null;
    this.t1_decoder_funcs = null;

    this.t1_decrypt = null;

    this.t1_cmap_classes = null;

    this.afm_parser_funcs = null;
}

function create_psaux_interface(_table_funcs,_parser_funcs,_builder_funcs,_decoder_funcs,_decrypt,_cmap_classes,_afm_parser_funcs)
{
    var ret = new PSAux_ServiceRec();

    ret.ps_table_funcs = _table_funcs;
    ret.ps_parser_funcs = _parser_funcs;
    ret.t1_builder_funcs = _builder_funcs;
    ret.t1_decoder_funcs = _decoder_funcs;
    ret.t1_decrypt = _decrypt;
    ret.t1_cmap_classes = _cmap_classes;
    ret.afm_parser_funcs = _afm_parser_funcs;

    return ret;
}
var psaux_interface = create_psaux_interface(ps_table_funcs,ps_parser_funcs,t1_builder_funcs,t1_decoder_funcs, t1_decrypt, t1_cmap_classes, afm_parser_funcs);

function create_psaux_module(library)
{
    var psaux_mod = new FT_Module();
    psaux_mod.clazz = new FT_Module_Class();

    var clazz = psaux_mod.clazz;
    clazz.flags = 0;
    clazz.name = "psaux";
    clazz.version = 0x20000;
    clazz.requires = 0x20000;

    clazz.module_interface = psaux_interface;
    clazz.init = null;
    clazz.done = null;

    clazz.get_interface = null;

    psaux_mod.library = library;
    psaux_mod.memory = library.Memory;
    psaux_mod.generic = null;

    return psaux_mod;
}

/******************************************************************************/
// bdf
/******************************************************************************/
function BDF_PropertyRec()
{
    this.type;
    this.u;
}

function tt_face_free_bdf_props(face)
{
    var bdf = face.bdf;
    if (bdf.loaded == 1)
    {
        bdf.table = null;
        bdf.table_end = 0;
        bdf.strings = null;
        bdf.strings_size = 0;
    }
}

function tt_face_load_bdf_props(face, stream)
{
    var bdf = face.bdf;
    var error = 0;

    bdf.table = null;
    bdf.table_end = 0;
    bdf.strings = null;
    bdf.strings_size = 0;
    bdf.num_strikes = 0;
    bdf.loaded = 0;

    var length = tt_face_goto_table(face, 1111770656, stream);
    error = FT_Error;
    if (error != 0 || length < 8)
        return 8;

    bdf.table = new CPointer();
    error = stream.ExtractFrame(length, bdf.table);

    if (error != 0)
        return 8;

    bdf.table_end = length;

    var p = dublicate_pointer(bdf.table);
    var version = FT_NEXT_USHORT(p);
    var num_strikes = FT_NEXT_USHORT(p);
    var strings = FT_NEXT_ULONG (p);

    if (version != 0x0001 || strings < 8  || (strings - 8) / 4 < num_strikes || strings + 1 > length)
    {
        bdf.table = null;
        return 8;
    }

    bdf.num_strikes = num_strikes;
    bdf.strings = new CPointer();
    bdf.strings.data = bdf.table.data;
    bdf.strings.pos = bdf.table.pos + strings;
    bdf.strings_size = length - strings;

    var count = bdf.num_strikes;
    p.pos = bdf.table.pos + 8;
    var strike = p.pos + count * 4;

    for ( ; count > 0; count--)
    {
        p.pos+=2;
        var num_items = FT_PEEK_USHORT(p);

        strike += 10 * num_items;
        p.pos+=2;
    }

    if (strike > bdf.strings)
    {
        bdf.table = null;
        bdf.table_end = 0;
        bdf.strings = null;
        bdf.strings_size = 0;
        bdf.num_strikes = 0;
        bdf.loaded = 0;
        return 8;
    }

    bdf.loaded = 1;
    return error;
}

function tt_face_find_bdf_prop(face,property_name,aprop)
{
    var bdf = face.bdf;
    var size = face.size;
    var error = 0;

    aprop.type = 0;

    if (bdf.loaded == 0)
    {
        error = tt_face_load_bdf_props(face, face.stream);
        if (error != 0)
            return error;
    }

    var count = bdf.num_strikes;
    var p = dublicate_pointer(bdf.table);
    p.pos += 8;

    var strike = dublicate_pointer(p);
    strike.pos += 4 * count;

    error = 6;

    var property_len = property_name.length;
    if (size == null || property_len == 0)
        return error;

    var is_strike = 0;
    for ( ; count > 0; count-- )
    {
        var _ppem  = FT_NEXT_USHORT(p);
        var _count = FT_NEXT_USHORT(p);

        if (_ppem == size.metrics.y_ppem)
        {
            count = _count;
            is_strike = 1;
            break;
        }

        strike.pos += 10 * _count;
    }
    if (0 == is_strike)
        return error;

    p.pos = strike.pos;
    var point = dublicate_pointer(bdf.strings);
    for (; count > 0; count--)
    {
        p.pos+=4;
        var type = FT_PEEK_USHORT(p);
        p.pos-=4;

        if ((type & 0x10) != 0)
        {
            var name_offset = FT_PEEK_ULONG(p);
            p.pos+=6;
            var value = FT_PEEK_ULONG(p);
            p.pos-=6;

            point.pos = bdf.strings.pos + name_offset;
            if (name_offset < bdf.strings_size && property_len < bdf.strings_size - name_offset &&
                        property_name == FT_PEEK_String1(point,bdf.strings_size-name_offset))
            {
                switch ( type & 0x0F )
                {
                    case 0x00:
                    case 0x01:
                        point.pos = bdf.strings.pos + value;
                        if (value < bdf.strings_size && -1 != ft_memchr(point, 0, bdf.strings_size))
                        {
                            aprop.type = 1;
                            aprop.u = FT_PEEK_String1(point,bdf.strings_size);
                            return 0;
                        }
                        break;

                    case 0x02:
                        aprop.type = 2;
                        aprop.u = ((value > 2147483647) ? value-4294967296 : value);
                        return 0;
                    case 0x03:
                        aprop.type = 3;
                        aprop.u = ((value >= 0) ? value : value + 2147483647);
                        return 0;
                    default:
                        break;
                }
            }
        }
        p.pos += 10;
    }

    return error;
}

function sfnt_get_charset_id(face,acharset_encoding,acharset_registry)
{
    var encoding = new BDF_PropertyRec();
    var registry = new BDF_PropertyRec();

    FT_Error = tt_face_find_bdf_prop(face, "CHARSET_REGISTRY", registry);
    if (FT_Error == 0)
    {
        FT_Error = tt_face_find_bdf_prop(face, "CHARSET_ENCODING", encoding);
        if (FT_Error == 0)
        {
            if (registry.type == BDF_PROPERTY_TYPE_ATOM && encoding.type == BDF_PROPERTY_TYPE_ATOM)
            {
                return {enc:encoding.u,reg:registry.u};
            }
            else
                FT_Error = 6;
        }
    }

    return {enc:"",reg:""};
}
/******************************************************************************/
// ttkern
/******************************************************************************/
function tt_face_load_kern(face, stream)
{
    var error = 0;
    var table_size = face.goto_table(face, 1801810542, stream);
    error = FT_Error;

    if (error != 0)
        return error;

    if (table_size < 4)
        return 142;

    face.kern_table = new CPointer();
    error = stream.ExtractFrame(table_size, face.kern_table);
    if (error != 0)
        return error;

    face.kern_table_size = table_size;
    stream.cur = face.kern_table.pos;
    var p_limit = stream.cur + table_size;

    stream.cur += 2; // skip version
    var num_tables = stream.GetUShort();

    if (num_tables > 32)
        num_tables = 32;

    var avail = 0;
    var ordered = 0;

    var nn = 0;
    for (; nn < num_tables; nn++)
    {
        var mask = 1 << nn;

        var p_next = stream.cur;

        if (stream.cur + 6 < p_limit)
            break;

        stream.cur += 2; // skip version
        var length = stream.GetUShort();
        var coverage = stream.GetUShort();

        if (length <= 6)
            break;

        p_next += length;

        if (p_next > p_limit)
            p_next = p_limit;

        if ((coverage & ~8) != 0x0001 || (stream.cur + 8) > p_limit)
        {
            stream.cur = p_next;
            continue;
        }

        var num_pairs = stream.GetUShort();
        stream.cur += 6;

        if ((p_next - stream.cur) < (6 * num_pairs))
            num_pairs = parseInt((p_next - stream.cur) / 6);

        avail |= mask;

        if (num_pairs > 0)
        {
            var count = 0;
            var old_pair = stream.GetULong();

            stream.cur += 2;

            for (count = num_pairs - 1; count > 0; count--)
            {
                var cur_pair = stream.GetULong();
                if (cur_pair <= old_pair)
                    break;

                stream.cur += 2;
                old_pair = cur_pair;
            }

            if (count == 0)
                ordered |= mask;
        }

        stream.cur = p_next;
    }

    face.num_kern_tables = nn;
    face.kern_avail_bits = avail;
    face.kern_order_bits = ordered;

    return error;
}
function tt_face_done_kern(face)
{
    face.kern_table = null;
    face.kern_table_size = 0;
    face.num_kern_tables = 0;
    face.kern_avail_bits = 0;
    face.kern_order_bits = 0;
}
function tt_face_get_kerning(face,left_glyph,right_glyph)
{
    var result = 0;
    var count, mask = 1;
    var p = dublicate_pointer(face.kern_table);
    if (null == p)
        return result;

    var p_limit = p.pos + face.kern_table_size;

    p.pos += 4;
    mask = 0x0001;

    for (count = face.num_kern_tables; count > 0 && p.pos + 6 <= p_limit; count--, mask <<= 1)
    {
        if (mask < 0)
            mask+=4294967296;
        var version  = FT_NEXT_USHORT( p );
        var length   = FT_NEXT_USHORT( p );
        var coverage = FT_NEXT_USHORT( p );
        var value = 0;

        var base = p.pos;
        var next = base + length;

        if (next > p_limit)
            next = p_limit;

        if ((face.kern_avail_bits & mask) == 0)
        {
            p.pos = next;
            continue;
        }

        if (p.pos + 8 > next)
        {
            p.pos = next;
            continue;
        }

        var num_pairs = FT_NEXT_USHORT( p );
        p.pos += 6;

        if (( next - p.pos) < 6 * num_pairs)
            num_pairs = parseInt((next - p.pos)/6);

        switch (coverage >>> 8)
        {
            case 0:
            {
                var key0 = (left_glyph<<16 | right_glyph)&0xFFFFFFFF;
                if (key0 < 0)
                    key0 += 4294967296;

                if ((face.kern_order_bits & mask) != 0)
                {
                    var min = 0;
                    var max = num_pairs;

                    var q = dublicate_pointer(p);
                    var is_found = 0;
                    while ( min < max )
                    {
                        var mid = (min + max) >>> 1;
                        q.pos = p.pos + 6 * mid;
                        var key = FT_NEXT_ULONG(q);

                        if (key == key0)
                        {
                            value = FT_PEEK_SHORT( q );
                            if ((coverage & 8) != 0)
                                result = value;
                            else
                                result += value;
                            p.pos = next;
                            is_found = 1;
                            break;
                        }
                        if (key < key0)
                            min = mid + 1;
                        else
                            max = mid;
                    }
                    if (1 == is_found)
                        continue;
                }
                else
                {
                    var is_found = 0;
                    for (var count2 = num_pairs; count2 > 0; count2--)
                    {
                        var key = FT_NEXT_ULONG(p);

                        if (key == key0)
                        {
                            value = FT_PEEK_SHORT(p);
                            if ((coverage & 8) != 0)
                                result = value;
                            else
                                result += value;
                            p.pos = next;
                            is_found = 1;
                            break;
                        }
                        p.pos += 2;
                    }
                    if (is_found == 1)
                        continue;
                }
            }
            break;
        default:
            break;
        }
        p.pos = next;
    }

    return result;
}
/******************************************************************************/
// ttload
/******************************************************************************/
function tt_face_lookup_table(face, tag)
{
    var count = face.num_tables;
    for (var i=0; i<count; i++)
    {
        var entry = face.dir_tables[i];
        if (entry.Tag == tag && entry.Length != 0)
            return entry;
    }
    return null;
}
function tt_face_goto_table(face, tag, stream)
{
    var length = 0;
    var table = tt_face_lookup_table(face, tag);
    if (table != null)
    {
        length = table.Length;
        FT_Error = stream.Seek(table.Offset);
        return table.Length;
    }
    FT_Error = 142;
    return 0;
}
function check_table_dir(sfnt, stream)
{
    var error = 0;
    var nn = 0;
    var valid_entries = 0;
    var has_head = 0;
    var has_sing = 0;
    var has_meta = 0;
    var offset = sfnt.offset + 12;

    error = stream.Seek(offset);
    if (0 != error)
        return error;

    var num_t = sfnt.num_tables;
    for (nn = 0; nn < num_t; nn++)
    {
        error = stream.EnterFrame(16);
        if (error != 0)
        {
            nn--;
            sfnt.num_tables = nn;
            break;
        }

        var Tag = stream.GetULong();
        var CheckSum = stream.GetULong();
        var Offset = stream.GetULong();
        var Length = stream.GetULong();

        stream.ExitFrame();

        if (Offset + Length > stream.size)
            continue;
        else
            valid_entries++;

        if (Tag == 1751474532 || Tag == 1651008868)
        {
            has_head = 1;
            if (Length < 0x36)
                return 142;

            error = stream.Seek(Offset + 12);
            if (error != 0)
                return error;

            var magic = stream.ReadULong();
            if (magic != 0x5F0F3CF5)
                return 142;

            error = stream.Seek(offset + (nn+1)*16);
            if (error != 0)
                return error;
        }
        else if (Tag == 1397313095)
            has_sing = 1;
        else if (Tag == 1296389185)
            has_meta = 1;
    }

    sfnt.num_tables = valid_entries;

    if (sfnt.num_tables == 0)
        return 2;

    if ((has_head == 1) || ((has_sing == 1) && (has_meta == 1)))
        return 0;
    else
        return 142;

    return error;
}
function tt_face_load_font_dir(face, stream)
{
    var sfnt = new SFNT_HeaderRec();
    var error = 0;

    sfnt.offset = stream.pos;
    sfnt.format_tag = stream.ReadULong();

    error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(8);
    if (error != 0)
        return error;

    sfnt.num_tables = stream.GetUShort();
    sfnt.search_range = stream.GetUShort();
    sfnt.entry_selector = stream.GetUShort();
    sfnt.range_shift = stream.GetUShort();

    stream.ExitFrame();

    error = check_table_dir(sfnt, stream);
    if (0 != error)
        return error;

    face.num_tables = sfnt.num_tables;
    face.format_tag = sfnt.format_tag;

    face.dir_tables = new Array(face.num_tables);

    error = stream.Seek(sfnt.offset + 12);
    if (0 == error)
    {
        error = stream.EnterFrame(face.num_tables * 16);
    }
    if (0 != error)
        return error;

    var cur = 0;
    for (var nn = 0; nn < sfnt.num_tables; nn++ )
    {
        face.dir_tables[cur] = new TT_Table();
        var entry = face.dir_tables[cur];
        entry.Tag      = stream.GetULong();
        entry.CheckSum = stream.GetULong();
        entry.Offset   = stream.GetLong();
        entry.Length   = stream.GetLong();

        /* ignore invalid tables */
        if (entry.Offset + entry.Length > stream.size)
            continue;
        cur++;
    }

    stream.ExitFrame();
    return error;
}
function tt_face_load_any(face,tag,offset,buffer,length)
{
    var error = 0;
    var table;
    var size;

    if (tag != 0)
    {
        table = tt_face_lookup_table(face, tag);
        if (null == table)
            return 142;

        offset += table.Offset;
        size = table.Length;
    }
    else
    {
        size = face.stream.size;
    }

    if (length == 0)
    {
        FT_Error = 0;
        return size;
    }

    if (null != length)
        size = length;

    error = face.stream.ReadAt(offset,buffer,size);
    FT_Error = error;
    return size;
}
function tt_face_load_generic_header(face, stream, tag)
{
    var error = 0;
    face.goto_table(face, tag, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(54);
    if (error != 0)
        return error;

    var header = face.header;

    header.Table_Version = stream.GetULong();
    header.Font_Revision = stream.GetULong();
    header.CheckSum_Adjust = stream.GetLong();
    header.Magic_Number = stream.GetLong();
    header.Flags = stream.GetUShort();
    header.Units_Per_EM = stream.GetUShort();
    header.Created1 = stream.GetLong();
    header.Created2 = stream.GetLong();
    header.Modified1 = stream.GetLong();
    header.Modified2 = stream.GetLong();
    header.xMin = stream.GetShort();
    header.yMin = stream.GetShort();
    header.xMax = stream.GetShort();
    header.yMax = stream.GetShort();
    header.Mac_Style = stream.GetUShort();
    header.Lowest_Rec_PPEM = stream.GetUShort();
    header.Font_Direction = stream.GetShort();
    header.Index_To_Loc_Format = stream.GetShort();
    header.Glyph_Data_Format = stream.GetShort();

    stream.ExitFrame();

    return error;
}
function tt_face_load_head(face, stream)
{
    return tt_face_load_generic_header(face, stream, 1751474532);
}
function tt_face_load_bhed(face, stream)
{
    return tt_face_load_generic_header(face, stream, 1651008868);
}
function tt_face_load_maxp(face, stream)
{
    var error = 0;
    face.goto_table(face, 1835104368, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(6);
    if (error != 0)
        return error;

    var maxProfile = face.max_profile;
    maxProfile.version = stream.GetLong();
    maxProfile.numGlyphs = stream.GetUShort();
    stream.ExitFrame();

    maxProfile.maxPoints             = 0;
    maxProfile.maxContours           = 0;
    maxProfile.maxCompositePoints    = 0;
    maxProfile.maxCompositeContours  = 0;
    maxProfile.maxZones              = 0;
    maxProfile.maxTwilightPoints     = 0;
    maxProfile.maxStorage            = 0;
    maxProfile.maxFunctionDefs       = 0;
    maxProfile.maxInstructionDefs    = 0;
    maxProfile.maxStackElements      = 0;
    maxProfile.maxSizeOfInstructions = 0;
    maxProfile.maxComponentElements  = 0;
    maxProfile.maxComponentDepth     = 0;

    if (maxProfile.version >= 0x10000)
    {
        error = stream.EnterFrame(26);
        if (error != 0)
            return error;

        maxProfile.maxPoints             = stream.GetUShort();
        maxProfile.maxContours           = stream.GetUShort();
        maxProfile.maxCompositePoints    = stream.GetUShort();
        maxProfile.maxCompositeContours  = stream.GetUShort();
        maxProfile.maxZones              = stream.GetUShort();
        maxProfile.maxTwilightPoints     = stream.GetUShort();
        maxProfile.maxStorage            = stream.GetUShort();
        maxProfile.maxFunctionDefs       = stream.GetUShort();
        maxProfile.maxInstructionDefs    = stream.GetUShort();
        maxProfile.maxStackElements      = stream.GetUShort();
        maxProfile.maxSizeOfInstructions = stream.GetUShort();
        maxProfile.maxComponentElements  = stream.GetUShort();
        maxProfile.maxComponentDepth     = stream.GetUShort();

        if (maxProfile.maxFunctionDefs < 64)
            maxProfile.maxFunctionDefs = 64;

        /* we add 4 phantom points later */
        if (maxProfile.maxTwilightPoints > (0xFFFF - 4))
            maxProfile.maxTwilightPoints = 0xFFFF - 4;

        /* we arbitrarily limit recursion to avoid stack exhaustion */
        if (maxProfile.maxComponentDepth > 100)
            maxProfile.maxComponentDepth = 100;

        stream.ExitFrame();
    }
    return error;
}
function tt_face_load_name(face, stream)
{
    var error = 0;
    var table_pos = 0;
    var table_len = 0;
    var storage_start = 0;
    var storage_limit = 0;
    var count = 0;

    table_len = face.goto_table(face, 1851878757, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    var table = face.name_table;
    table.stream = stream;

    table_pos = stream.pos;

    error = stream.EnterFrame(6);
    if (error != 0)
        return error;

    table.format = stream.GetUShort();
    table.numNameRecords = stream.GetUShort();
    table.storageOffset = stream.GetUShort();

    stream.ExitFrame();

    storage_start = table_pos + 6 + 12*table.numNameRecords;
    storage_limit = table_pos + table_len;

    if (storage_start > storage_limit)
        return 145;

    /* Allocate the array of name records. */
    count = table.numNameRecords;
    table.numNameRecords = 0;

    table.names = new Array(count);

    error = stream.EnterFrame(count * 12);
    for (; count > 0; count--)
    {
        var entry = new TT_NameEntryRec();
        entry.platformID = stream.GetUShort();
        entry.encodingID = stream.GetUShort();
        entry.languageID = stream.GetUShort();
        entry.nameID = stream.GetUShort();
        entry.stringLength = stream.GetUShort();
        entry.stringOffset = stream.GetUShort();

        if (entry.stringLength == 0)
            continue;

        entry.stringOffset += (table_pos + table.storageOffset);
        if (entry.stringOffset < storage_start || (entry.stringOffset + entry.stringLength) > storage_limit)
            continue;

        table.names[table.numNameRecords] = entry;
        table.numNameRecords++;
    }

    stream.ExitFrame();

    /* everything went well, update face->num_names */
    face.num_names = table.numNameRecords;
    return error;
}
function tt_face_free_name(face)
{
    face.name_table.names = null;
    var table = face.num_table;
    table.names = null;
    table.numNameRecords = 0;
    table.format         = 0;
    table.storageOffset  = 0;
}
function tt_face_load_cmap(face, stream)
{
    face.cmap_size = face.goto_table(face, 1668112752, stream);
    if (FT_Error != 0)
        return FT_Error;

    face.cmap_table = new CPointer();
    var error = stream.ExtractFrame(face.cmap_size, face.cmap_table);
    if (error != 0)
    {
        face.cmap_size = 0;
        face.cmap_table = null;
    }
    return error;
}
function tt_face_load_os2(face, stream)
{
    var error = 0;

    var os2 = face.os2;

    face.goto_table(face, 1330851634, stream);
    if (error != 0)
        return error;

    error = stream.EnterFrame(78);
    if (error != 0)
        return error;

    os2.version = stream.GetUShort();
    os2.xAvgCharWidth = stream.GetShort();
    os2.usWeightClass = stream.GetUShort();
    os2.usWidthClass = stream.GetUShort();
    os2.fsType = stream.GetShort();
    os2.ySubscriptXSize = stream.GetShort();
    os2.ySubscriptYSize = stream.GetShort();
    os2.ySubscriptXOffset = stream.GetShort();
    os2.ySubscriptYOffset = stream.GetShort();
    os2.ySuperscriptXSize = stream.GetShort();
    os2.ySuperscriptYSize = stream.GetShort();
    os2.ySuperscriptXOffset = stream.GetShort();
    os2.ySuperscriptYOffset = stream.GetShort();
    os2.yStrikeoutSize = stream.GetShort();
    os2.yStrikeoutPosition = stream.GetShort();
    os2.sFamilyClass = stream.GetShort();

    os2.panose = new Array(10);
    os2.panose[0] = stream.GetUChar();
    os2.panose[1] = stream.GetUChar();
    os2.panose[2] = stream.GetUChar();
    os2.panose[3] = stream.GetUChar();
    os2.panose[4] = stream.GetUChar();
    os2.panose[5] = stream.GetUChar();
    os2.panose[6] = stream.GetUChar();
    os2.panose[7] = stream.GetUChar();
    os2.panose[8] = stream.GetUChar();
    os2.panose[9] = stream.GetUChar();

    os2.ulUnicodeRange1 = stream.GetULong();
    os2.ulUnicodeRange2 = stream.GetULong();
    os2.ulUnicodeRange3 = stream.GetULong();
    os2.ulUnicodeRange4 = stream.GetULong();

    os2.achVendID = new Array(4);
    os2.achVendID[0] = stream.GetUChar();
    os2.achVendID[1] = stream.GetUChar();
    os2.achVendID[2] = stream.GetUChar();
    os2.achVendID[3] = stream.GetUChar();

    os2.fsSelection = stream.GetUShort();
    os2.usFirstCharIndex = stream.GetUShort();
    os2.usLastCharIndex = stream.GetUShort();
    os2.sTypoAscender = stream.GetShort();
    os2.sTypoDescender = stream.GetShort();
    os2.sTypoLineGap = stream.GetShort();
    os2.usWinAscent = stream.GetUShort();
    os2.usWinDescent = stream.GetUShort();

    os2.ulCodePageRange1 = 0;
    os2.ulCodePageRange2 = 0;
    os2.sxHeight         = 0;
    os2.sCapHeight       = 0;
    os2.usDefaultChar    = 0;
    os2.usBreakChar      = 0;
    os2.usMaxContext     = 0;

    stream.ExitFrame();

    if (os2.version >= 0x0001)
    {
        error = stream.EnterFrame(8);
        if (error != 0)
            return error;

        os2.ulCodePageRange1 = stream.GetULong();
        os2.ulCodePageRange2 = stream.GetULong();

        if (os2.version >= 0x0002)
        {
            error = stream.EnterFrame(10);
            if (error != 0)
                return error;

            os2.sxHeight         = stream.GetShort();
            os2.sCapHeight       = stream.GetShort();
            os2.usDefaultChar    = stream.GetUShort();
            os2.usBreakChar      = stream.GetUShort();
            os2.usMaxContext     = stream.GetUShort();
        }
    }

    return error;
}
function tt_face_load_post(face, stream)
{
    var error = 0;

    face.goto_table(face, 1886352244, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(32);
    if (error != 0)
        return error;

    var post = face.postscript;
    post.FormatType = stream.GetLong();
    post.italicAngle = stream.GetLong();
    post.underlinePosition = stream.GetShort();
    post.underlineThickness = stream.GetShort();
    post.isFixedPitch = stream.GetULong();
    post.minMemType42 = stream.GetULong();
    post.maxMemType42 = stream.GetULong();
    post.minMemType1 = stream.GetULong();
    post.maxMemType1 = stream.GetULong();

    stream.ExitFrame();

    return error;
}
function tt_face_load_pclt(face, stream)
{
    var error = 0;
    var pclt = face.pclt;

    /* optional table */
    face.goto_table(face, 1346587732, stream);
	error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(54);
    if (error != 0)
        return error;

    pclt.Version = stream.GetULong();
    pclt.FontNumber = stream.GetULong();
    pclt.Pitch = stream.GetUShort();
    pclt.xHeight = stream.GetUShort();
    pclt.Style = stream.GetUShort();
    pclt.TypeFamily = stream.GetUShort();
    pclt.CapHeight = stream.GetUShort();
    pclt.SymbolSet = stream.GetUShort();
    pclt.TypeFace = stream.GetString1(16);
    pclt.CharacterComplement = stream.GetString1(8);
    pclt.FileName = stream.GetString1(6);
    pclt.StrokeWeight = stream.GetChar();
    pclt.WidthType = stream.GetChar();
    pclt.SerifStyle = stream.GetUChar();
    pclt.Reserved = stream.GetUChar();

    stream.ExitFrame();
    return error;
}
function tt_face_load_gasp(face, stream)
{
    var error = 0;
    face.goto_table(face, 1734439792, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(4);
    if (error != 0)
        return error;

    face.gasp.version   = stream.GetUShort();
    face.gasp.numRanges = stream.GetUShort();

    stream.ExitFrame();

    if (face.gasp.version >= 2)
    {
        face.gasp.numRanges = 0;
        return 8;
    }

    var num_ranges = face.gasp.numRanges;
    face.gasp.gaspRanges = new Array(num_ranges);
    var ranges = face.gasp.gaspRanges;

    error = stream.EnterFrame(num_ranges * 4);
    if (error != 0)
        return error;

    for (var i = 0; i < num_ranges; i++)
    {
        ranges[i] = new TT_GaspRange();
        ranges[i].maxPPEM = stream.GetUShort();
        ranges[i].gaspFlag = stream.GetUShort();
    }

    stream.ExitFrame();
    return error;
}
/******************************************************************************/
// ttmtx
/******************************************************************************/
function tt_face_load_hmtx(face, stream, isvertical)
{
    var error = 0;

    var table_len = 0;
    var num_shorts = 0;
    var num_longs = 0;
    var num_shorts_checked = 0;

    var lm = null;

    if (isvertical == 1)
    {
        table_len = face.goto_table(face, 1986884728, stream);
        error = FT_Error;
        if (error != 0)
            return error;

        num_longs = face.vertical.number_Of_VMetrics;
        if (num_longs > (table_len / 4))
            num_longs = parseInt(table_len / 4);

        face.vertical.number_Of_VMetrics = 0;

        lm = face.vertical;
    }
    else
    {
        table_len = face.goto_table(face, 1752003704, stream);
        error = FT_Error;
        if (error != 0)
            return error;

        num_longs = face.horizontal.number_Of_HMetrics;
        if (num_longs > (table_len / 4))
            num_longs = parseInt(table_len / 4);

        face.horizontal.number_Of_HMetrics = 0;

        lm = face.horizontal;
    }

    /* never trust derived values */
    num_shorts         = face.max_profile.numGlyphs - num_longs;
    num_shorts_checked = parseInt((table_len - num_longs * 4) / 2);

    if (num_longs > 0)
        lm.long_metrics = new Array(num_longs);
    if (num_shorts > 0)
        lm.short_metrics = new Array(num_shorts);

    var longs  = lm.long_metrics;
    var shorts = lm.short_metrics;

    if (num_shorts < 0)
        num_shorts = 0;

    error = stream.EnterFrame(table_len);

    for (var i = 0; i < num_longs; i++)
    {
        longs[i] = new TT_LongMetricsRec();
        longs[i].advance = stream.GetUShort();
        longs[i].bearing = stream.GetShort();
    }

    var count_s = Math.min(num_shorts, num_shorts_checked);
    for (var i = 0; i < count_s; i++)
    {
        shorts[i] = stream.GetShort();
    }

    if (num_shorts > num_shorts_checked && num_shorts_checked > 0)
    {
        var ind = shorts.length;
        var val = shorts[num_shorts_checked - 1];
        for (var i = ind; i < num_shorts; i++)
            shorts[i] = val;
    }

    stream.ExitFrame();

    if (isvertical)
        face.vertical.number_Of_VMetrics = num_longs;
    else
        face.horizontal.number_Of_HMetrics = num_longs;

    return error;
}
function tt_face_load_hhea(face, stream, isvertical)
{
    var error = 0;
    var header = null;
    if (1 == isvertical)
    {
        face.goto_table(face, 1986553185, stream);
        error = FT_Error;
        if (error != 0)
            return error;

        header = face.vertical;
    }
    else
    {
        face.goto_table(face, 1751672161, stream);
        error = FT_Error;
        if (error != 0)
            return error;

        header = face.horizontal;
    }

    error = stream.EnterFrame(36);
    if (error != 0)
        return error;

    if (0 == isvertical)
    {
        header.Version = stream.GetULong();
        header.Ascender = stream.GetShort();
        header.Descender = stream.GetShort();
        header.Line_Gap = stream.GetShort();

        header.advance_Width_Max = stream.GetUShort();

        header.min_Left_Side_Bearing = stream.GetShort();
        header.min_Right_Side_Bearing = stream.GetShort();
        header.xMax_Extent = stream.GetShort();
        header.caret_Slope_Rise = stream.GetShort();
        header.caret_Slope_Run = stream.GetShort();
        header.caret_Offset = stream.GetShort();

        header.Reserved1 = stream.GetShort();
        header.Reserved2 = stream.GetShort();
        header.Reserved3 = stream.GetShort();
        header.Reserved4 = stream.GetShort();

        header.metric_Data_Format = stream.GetShort();
        header.number_Of_HMetrics = stream.GetUShort();
    }
    else
    {
        header.Version = stream.GetULong();
        header.Ascender = stream.GetShort();
        header.Descender = stream.GetShort();
        header.Line_Gap = stream.GetShort();

        header.advance_Height_Max = stream.GetUShort();

        header.min_Top_Side_Bearing = stream.GetShort();
        header.min_Bottom_Side_Bearing = stream.GetShort();
        header.yMax_Extent = stream.GetShort();
        header.caret_Slope_Rise = stream.GetShort();
        header.caret_Slope_Run = stream.GetShort();
        header.caret_Offset = stream.GetShort();

        header.Reserved1 = stream.GetShort();
        header.Reserved2 = stream.GetShort();
        header.Reserved3 = stream.GetShort();
        header.Reserved4 = stream.GetShort();

        header.metric_Data_Format = stream.GetShort();
        header.number_Of_VMetrics = stream.GetUShort();
    }

    stream.ExitFrame();

    header.long_metrics  = null;
    header.short_metrics = null;

    return error;
}
function tt_face_get_metrics(face, vertical, gindex)
{
    var header = (vertical == 1) ? face.vertical : face.horizontal;

    var longs_m = null;
    var k = header.number_Of_HMetrics;

    var v1 = 0;
    var v2 = 0;
    if (k == 0 || null == header.long_metrics || gindex >= face.max_profile.numGlyphs)
        return { bearing:0,advance:0 };

    if (gindex < k)
    {
        longs_m = header.long_metrics[gindex];
        v1 = longs_m.bearing;
        v2 = longs_m.advance;
    }
    else
    {
        v1 = header.short_metrics[gindex - k];
        v2 = header.long_metrics[k - 1].advance;
    }
    return { bearing:v1,advance:v2 };
}
/******************************************************************************/
// ttpost
/******************************************************************************/
function load_format_20(face, stream, post_limit)
{
    var error = 0;

    var num_glyphs = stream.ReadUShort();
    error = FT_Error;
    if (error != 0)
        return error;

    if (num_glyphs > face.max_profile.numGlyphs)
        return 3;

    error = stream.EnterFrame(num_glyphs*2);
    if (error != 0)
        return error;

    var glyph_indices = new Array(num_glyphs);
    for (var n=0;n<num_glyphs;n++)
        glyph_indices[n] = stream.GetUShort();
    stream.ExitFrame();

    var num_names = 0;
    for ( n = 0; n < num_glyphs; n++ )
    {
        var idx = glyph_indices[n];
        if (idx >= 258)
        {
            idx -= 257;
            if (idx > num_names)
                num_names = idx;
        }
    }

    var name_strings = new Array(num_names);
    var n=0;
    for (n = 0; n < num_names; n++)
    {
        var len = 0;
        if (stream.pos >= post_limit)
            break;
        else
        {
            len = stream.ReadUChar();
            error = FT_Error;
            if (error != 0)
                return error;
        }

        if (len > post_limit || stream.pos > post_limit - len)
        {
            len = Math.max(0, post_limit - stream.pos);
        }

        name_strings[n] = stream.ReadString1(len);
    }

    if (n < num_names)
    {
        for (; n < num_names; n++)
            name_strings[n] = "";
    }

    var table = face.postscript_names.names.format_20;
    table.num_glyphs    = num_glyphs;
    table.num_names     = num_names;
    table.glyph_indices = glyph_indices;
    table.glyph_names   = name_strings;

    return 0;
}
function load_format_25(face, stream, post_limit)
{
    var num_glyphs = stream.ReadUShort();
    var error = FT_Error;
    if (error != 0)
        return error;

    if (num_glyphs > face.max_profile.numGlyphs || num_glyphs > 258)
        return 3;

    var offset_table = g_memory.Alloc(num_glyphs);
    error = stream.Read(offset_table,num_glyphs);

    if (error != 0)
        return error;

    for (var n = 0; n < num_glyphs; n++ )
    {
        var idx = n + offset_table[n];
        if ( idx < 0 || idx > num_glyphs )
            return 3;
    }

    var table = face.postscript_names.names.format_25;
    table.num_glyphs = num_glyphs;
    table.offsets = offset_table;
    return 0;
}
function load_post_names(face)
{
    var stream = face.stream;
    var post_len = face.goto_table(face, 1886352244, stream);
    var error = FT_Error;
    if (error != 0)
        return error;

    var post_limit = stream.pos + post_len;
    var format = face.postscript.FormatType;

    error = stream.Skip(32);
    if (error != 0)
        return error;

    if (format == 0x00020000)
        error = load_format_20(face, stream, post_limit);
    else if (format == 0x00028000)
        error = load_format_25(face, stream, post_limit);
    else
        error = 3;

    face.postscript_names.loaded = 1;
    return error;
}
function tt_face_free_ps_names(face)
{
    var names = face.postscript_names;

    if (names.loaded == 1)
    {
        var format = face.postscript.FormatType;
        if (format == 0x00020000)
        {
            var table = names.names.format_20;

            table.glyph_indices = null;
            table.num_glyphs = 0;

            table.glyph_names = null;
            table.num_names = 0;
        }
        else if (format == 0x00028000)
        {
            var table = names.names.format_25;
            table.offsets = null;
            table.num_glyphs = 0;
        }
    }
    names.loaded = 0;
}
function tt_face_get_ps_name(face, idx)
{
    if (null == face)
    {
        FT_Error = 35;
        return "";
    }
    if (idx >= face.max_profile.numGlyphs)
    {
        FT_Error = 16;
        return "";
    }
    var psnames = face.psnames;
    if (null == psnames)
    {
        FT_Error = 7;
        return "";
    }

    FT_Error = 0;
    var names = face.postscript_names;
    var format = face.postscript.FormatType;

    var res = psnames.macintosh_name(0);
    if (format == 0x00010000)
    {
        if (idx < 258)
            res = psnames.macintosh_name(idx);
    }
    else if (format == 0x00020000)
    {
        var table = names.names.format_20;
        if (0 == names.loaded)
        {
            FT_Error = load_post_names(face);
            if (FT_Error != 0)
                return res;
        }

        if (idx < table.num_glyphs)
        {
            var name_index = table.glyph_indices[idx];

            if (name_index < 258)
                res = psnames.macintosh_name(name_index);
            else
                res = table.glyph_names[name_index - 258];
        }
    }
    else if (format == 0x00028000)
    {
        var table = names.names.format_25;
        if (0 == names.loaded)
        {
            FT_Error = load_post_names( face );
            if (FT_Error != 0)
                return res;
        }

        if (idx < table.num_glyphs)
        {
            idx += table.offsets[idx];
            res = psnames.macintosh_name(idx);
        }
    }
    return res;
}
/******************************************************************************/
// ttsbit
/******************************************************************************/
function blit_sbit(target, _source, line_bits, byte_padded, x_offset, y_offset, source_height)
{
    var line_incr = target.pitch;
    var line_buff = target.buffer;

    var source = dublicate_pointer(_source);

    if (line_incr < 0)
        line_buff -= line_incr * (target.rows - 1);

    line_buff.pos += (x_offset >> 3) + y_offset * line_incr;

    var acc    = 0;
    var loaded = 0;

    for (var height = source_height; height > 0; height--)
    {
        var cur = dublicate_pointer(line_buff);
        var count = line_bits;
        var shift = (x_offset & 7);
        var space = (8 - shift);

        if ( count >= 8 )
        {
            count -= 8;
            do
            {
                if (loaded < 8)
                {
                    acc |= (source.data[source.pos++] << ( 8 - loaded));
                    loaded += 8;
                }

                val = (0xFF & (acc >>> 8));
                if (shift != 0)
                {
                    cur.data[cur.pos] |= (0xFF & (val >>> shift));
                    cur.data[cur.pos+1] |= (0xFF & (val << space));
                }
                else
                    cur.data[cur.pos] |= val;

                cur.pos++;
                acc <<= 8;
                loaded -= 8;
                count  -= 8;
            } while (count >= 0);
            count += 8;
        }

        if (count > 0)
        {
            if (loaded < count)
            {
                acc |= ((source.data[source.pos++]) << (8 - loaded));
                loaded += 8;
            }

            var val = ((0xFF&(acc >>> 8)) & ~(0xFF >>> count));
            cur.data[cur.pos] |= (0xFF & (val >>> shift));

            if (count > space)
                cur.data[cur.pos+1] |= (0xFF & (val << space));

            acc <<= count;
            loaded -= count;
        }

        if ( byte_padded )
        {
            acc    = 0;
            loaded = 0;
        }
        line_buff.pos += line_incr;
    }
}
function Load_SBit_Const_Metrics(range, stream)
{
    range.image_size = stream.ReadULong();
    if (FT_Error != 0)
        return FT_Error;

    var error = stream.EnterFrame(8);
    if (0 == error)
    {
        var metrics = range.metrics;

        metrics.height = stream.GetUChar();
        metrics.width = stream.GetUChar();

        metrics.horiBearingX = stream.GetChar();
        metrics.horiBearingY = stream.GetChar();
        metrics.horiAdvance = stream.GetUChar();

        metrics.vertBearingX = stream.GetChar();
        metrics.vertBearingY = stream.GetChar();
        metrics.vertAdvance = stream.GetUChar();
    }
    stream.ExitFrame();
    return error;
}
function Load_SBit_Range_Codes(range, stream, load_offsets)
{
    var count = stream.ReadULong();
    if (FT_Error != 0)
        return FT_Error;

    range.num_glyphs = count;

    var size = 2 * count;
    if (load_offsets != 0)
    {
        range.glyph_offsets = new Array(count);
        size *= 2;
    }

    var error = stream.EnterFrame(size);
    if (0 != error)
        return error;

    range.glyph_codes = new Array(count);

    for (var n = 0; n < count; n++)
    {
        range.glyph_codes[n] = stream.GetUShort();

        if (load_offsets != 0)
            range.glyph_offsets[n] = range.image_offset + stream.GetUShort();
    }

    stream.ExitFrame();
    return error;
}
function Load_SBit_Range(range, stream)
{
    var error = 0;

    switch(range.index_format)
    {
    case 1:
    case 3:
        if (range.last_glyph < range.first_glyph)
            return 3;

        var num_glyphs = range.last_glyph - range.first_glyph + 1;
        range.num_glyphs = num_glyphs;
        num_glyphs++;

        var large = (range.index_format == 1) ? 1 : 0;
        var size_elem = large ? 4 : 2;

        error = stream.EnterFrame(num_glyphs*size_elem);
        if (error != 0)
            return error;
        range.glyph_offsets = new Array(num_glyphs);

        for (var n = 0; n < num_glyphs; n++)
            range.glyph_offsets[n] = (range.image_offset + ((large == 1) ? stream.GetULong() : stream.GetUShort()));
        stream.ExitFrame();
        break;

    case 2:
        error = Load_SBit_Const_Metrics(range, stream);
        break;
    case 4:
        error = Load_SBit_Range_Codes(range, stream, 1);
        break;
    case 5:
        error = Load_SBit_Const_Metrics(range, stream);
        if (error != 0)
            error = Load_SBit_Range_Codes(range, stream, 0);
        break;
    default:
        error = 3;
    }
    return error;
}

function tt_face_load_eblc(face, stream)
{
    var error = 0;

    face.num_sbit_strikes = 0;
    face.goto_table(face, 1161972803, stream);
    if (FT_Error != 0)
        face.goto_table(face, 1651273571, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    var table_base = stream.pos;
    error = stream.EnterFrame(8);
    if (error != 0)
        return error;

    var version = stream.GetLong();
    var num_strikes = stream.GetULong();

    stream.ExitFrame();

    if (version != 0x00020000 || num_strikes >= 0x10000)
        return 3;

    face.sbit_strikes = new Array(num_strikes);
    face.num_sbit_strikes = num_strikes;
    
    for (var i = 0; i < num_strikes; i++)
        face.sbit_strikes[i] = new TT_SBit_StrikeRec();

    var strike_ind = 0;
    var count  = num_strikes;

    error = stream.EnterFrame(48*num_strikes);
    if (error != 0)
        return error;

    while ( count > 0 )
    {
        var strike = face.sbit_strikes[strike_ind];

        strike.ranges_offset = stream.GetULong();
        stream.cur += 4;
        strike.num_ranges = stream.GetULong();
        strike.color_ref = stream.GetULong();

        var h = strike.hori;
        var v = strike.vert;

        h.ascender = stream.GetChar();
        h.descender = stream.GetChar();
        h.max_width = stream.GetUChar();
        h.caret_slope_numerator = stream.GetChar();
        h.caret_slope_denominator = stream.GetChar();
        h.caret_offset = stream.GetChar();
        h.min_origin_SB = stream.GetChar();
        h.min_advance_SB = stream.GetChar();
        h.max_before_BL = stream.GetChar();
        h.min_after_BL = stream.GetChar();
        h.pads1 = stream.GetChar();
        h.pads2 = stream.GetChar();

        v.ascender = stream.GetChar();
        v.descender = stream.GetChar();
        v.max_width = stream.GetUChar();
        v.caret_slope_numerator = stream.GetChar();
        v.caret_slope_denominator = stream.GetChar();
        v.caret_offset = stream.GetChar();
        v.min_origin_SB = stream.GetChar();
        v.min_advance_SB = stream.GetChar();
        v.max_before_BL = stream.GetChar();
        v.min_after_BL = stream.GetChar();
        v.pads1 = stream.GetChar();
        v.pads2 = stream.GetChar();

        strike.start_glyph = stream.GetUShort();
        strike.end_glyph = stream.GetUShort();
        strike.x_ppem = stream.GetUChar();
        strike.y_ppem = stream.GetUChar();
        strike.bit_depth = stream.GetUChar();
        strike.flags = stream.GetChar();

        count--;
        strike_ind++;
    }
    
    stream.ExitFrame();
    strike_ind = 0;
    count  = num_strikes;

    while ( count > 0 )
    {
        var strike = face.sbit_strikes[strike_ind];
        var range;
        var count2 = strike.num_ranges;

        error = stream.Seek(table_base + strike.ranges_offset);
        if (0 != error)
            return error;

        error = stream.EnterFrame(strike.num_ranges * 8);
        if (0 != error)
            return error;

        strike.sbit_ranges = new Array(strike.num_ranges);
        var __count = strike.num_ranges;
        for (var i = 0; i < __count; i++)
            strike.sbit_ranges[i] = new TT_SBit_RangeRec();

        var range_ind = 0;
        while ( count2 > 0 )
        {
            range = strike.sbit_ranges[range_ind];
            range.first_glyph  = stream.GetUShort();
            range.last_glyph   = stream.GetUShort();
            range.table_offset = table_base + strike.ranges_offset + stream.GetULong();
            count2--;
            range_ind++;
        }

        stream.ExitFrame();

        count2 = strike.num_ranges;
        range_ind = 0;
        while (count2 > 0)
        {
            range = strike.sbit_ranges[range_ind];
            error = stream.Seek(range.table_offset);
            if (error != 0)
                return error;
            error = stream.EnterFrame(8);
            if (error != 0)
                return error;

            range.index_format = stream.GetUShort();
            range.image_format = stream.GetUShort();
            range.image_offset = stream.GetULong();

            stream.ExitFrame();

            error = Load_SBit_Range(range, stream);
            if (error != 0)
                return error;

            count2--;
            range_ind++;
        }

        count--;
        strike_ind++;
    }

    return error;
}
function tt_face_free_eblc(face)
{
    face.sbit_strikes = null;
    face.num_sbit_strikes = 0;
}
function tt_face_set_sbit_strike(face, req)
{
    return FT_Match_Size(face, req, 0);
}
function tt_face_load_strike_metrics(face, strike_index, metrics)
{
    if (strike_index >= face.num_sbit_strikes)
      return 6;

    var strike = face.sbit_strikes[strike_index];

    metrics.x_ppem = strike.x_ppem;
    metrics.y_ppem = strike.y_ppem;

    metrics.ascender  = strike.hori.ascender << 6;
    metrics.descender = strike.hori.descender << 6;

    metrics.max_advance = (strike.hori.min_origin_SB + strike.hori.max_width + strike.hori.min_advance_SB) << 6;

    metrics.height = metrics.ascender - metrics.descender;

    return 0;
}
function find_sbit_range(glyph_index, strike)
{
    FT_Error = 0;
    var range = null;
    var glyph_offset = 0;

    if (glyph_index < strike.start_glyph || glyph_index > strike.end_glyph)
    {
        FT_Error = 6;
    }

    var range_ind = 0;
    var range_limit = strike.num_ranges;

    for ( ; range_ind < range_limit; range_ind++)
    {
        range = strike.sbit_ranges[range_ind];
        if (glyph_index >= range.first_glyph && glyph_index <= range.last_glyph)
        {
            var delta = (glyph_index - range.first_glyph);

            switch (range.index_format)
            {
            case 1:
            case 3:
                glyph_offset = range.glyph_offsets[delta];
                break;
            case 2:
                glyph_offset = range.image_offset + range.image_size * delta;
                break;
            case 4:
            case 5:
                for (var n = 0; n < range.num_glyphs; n++)
                {
                    if (range.glyph_codes[n] == glyph_index)
                    {
                        if (range.index_format == 4)
                            glyph_offset = range.glyph_offsets[n];
                        else
                            glyph_offset = range.image_offset + n * range.image_size;
                        return {range:range,glyph_offset:glyph_offset};
                    }
                }
                break;
            default:
                break;
            }

            return {range:range,glyph_offset:glyph_offset};
        }
    }

    FT_Error = 6;
    return {range:range,glyph_offset:glyph_offset};
}
function tt_find_sbit_image(face, glyph_index, strike_index)
{
    FT_Error = 0;
    if (null == face.sbit_strikes || (face.num_sbit_strikes <= strike_index))
    {
        FT_Error = 6;
        return {strike:null,range:null,glyph_offset:0};
    }

    var strike = face.sbit_strikes[strike_index];
    var ret = find_sbit_range(glyph_index, strike);
    if (FT_Error != 0)
    {
        FT_Error = 6;
        return {strike:null,range:null,glyph_offset:0};
    }

    return {strike:strike,range:ret.range,glyph_offset:ret.glyph_offset};
}
function tt_load_sbit_metrics(stream, range, metrics)
{
    var error = 0;
    switch (range.image_format)
    {
    case 1:
    case 2:
    case 8:
        error = stream.EnterFrame(5);
        if (error != 0)
            return error;

        metrics.height       = stream.GetUChar();
        metrics.width        = stream.GetUChar();
        metrics.horiBearingX = stream.GetChar();
        metrics.horiBearingY = stream.GetChar();
        metrics.horiAdvance  = stream.GetUChar();

        metrics.vertBearingX = 0;
        metrics.vertBearingY = 0;
        metrics.vertAdvance  = 0;

        stream.ExitFrame();
        break;

    case 6:
    case 7:
    case 9:
        error = stream.EnterFrame(8);
        if (error != 0)
            return error;

        metrics.height       = stream.GetUChar();
        metrics.width        = stream.GetUChar();
        metrics.horiBearingX = stream.GetChar();
        metrics.horiBearingY = stream.GetChar();
        metrics.horiAdvance  = stream.GetUChar();

        metrics.vertBearingX = stream.GetChar();
        metrics.vertBearingY = stream.GetChar();
        metrics.vertAdvance  = stream.GetUChar();

        stream.ExitFrame();
        break;
    case 5:
    default:
        if (range.index_format == 2 || range.index_format == 5)
        {
            var s = range.metrics;
            metrics.height       = s.height;
            metrics.width        = s.width;
            metrics.horiBearingX = s.horiBearingX;
            metrics.horiBearingY = s.horiBearingY;
            metrics.horiAdvance  = s.horiAdvance;

            metrics.vertBearingX = s.vertBearingX;
            metrics.vertBearingY = s.vertBearingY;
            metrics.vertAdvance  = s.vertAdvance;
        }
        else
            return 3;
    }

    return error;
}
function crop_bitmap(map, metrics)
{
    var rows, count;
    var line_len;
    var line = new CPointer();
    line.data = map.buffer.data;
    var d = line.data;

    // 1
    line.pos = map.buffer.pos;
    rows = map.rows;
    line_len = map.pitch;

    var is_go_to = 0;
    for (count = 0; count < rows; count++)
    {
        for (var cur=0; cur < line_len; cur++)
        {
            if (d[line.pos+cur] != 0)
            {
                is_go_to = 1;
                break;
            }
        }
        if (1 == is_go_to)
            break;
        line.pos += line_len;
    }

    if (count >= rows)
    {
        map.width      = 0;
        map.rows       = 0;
        map.pitch      = 0;
        map.pixel_mode = 1;
    }

    if ( count > 0 )
    {
        line.pos = map.buffer.pos;
        var pos1 = line.pos;
        var pos2 = line.pos + count * line_len;

        var c = (rows - count)*line_len;

        for (var i=0;i<c;i++)
            d[pos1+i] = d[pos2+i];

        metrics.height       = (metrics.height - count)&0xFF;
        metrics.horiBearingY = (metrics.horiBearingY - count);
        metrics.vertBearingY = (metrics.vertBearingY - count);

        map.rows -= count;
        rows -= count;
    }

    //2
    is_go_to = 0;
    line.pos = (rows - 1) * line_len;

    for (count = 0; count < rows; count++)
    {
        for (var cur=0; cur < line_len; cur++)
        {
            if (d[line.pos+cur] != 0)
            {
                is_go_to = 1;
                break;
            }
        }
        if (is_go_to == 1)
            break;

        line.pos -= line_len;
    }

    if (count > 0)
    {
        metrics.height = (metrics.height - count);
        rows -= count;
        map.rows -= count;
    }

    // 3
    is_go_to = 0;
    do
    {
        line.pos = map.buffer.pos;
        var limit = line.pos + rows * line_len;

        for (; line.pos < limit; line.pos += line_len)
        {
            if ((d[line.pos] & 0x80) != 0)
            {
                is_go_to = 1;
                break;
            }
        }
        if (1 == is_go_to)
            break;

        line.pos = map.buffer.pos;
        limit = line.pos + rows * line_len;

        for (; line.pos < limit; line.pos += line_len)
        {
            var width = map.width;
            var cur = line.pos;
            var old = 0xFF & (d[cur] << 1);
            for (var n = 8; n < width; n += 8)
            {
                var val = d[cur+1];
                d[cur] = 0xFF & (old | (val >>> 7));
                old = 0xFF & (val << 1);
                cur++;
            }
            d[cur] = old;
        }

        map.width--;
        metrics.horiBearingX++;
        metrics.vertBearingX++;
        metrics.width--;

    } while (map.width > 0);

    // 4
    do
    {
        var right = map.width - 1;
        line.pos = (right >>> 3);
        var limit = line.pos + rows * line_len;
        var mask = 0xFF & (0x80 >>> (right & 7));

        for ( ; line.pos < limit; line.pos += line_len)
            if ((d[line.pos] & mask)!=0)
                return;

        map.width--;
        metrics.width--;

    } while ( map.width > 0 );
}
function Load_SBit_Single(map, x_offset, y_offset, pix_bits, image_format, metrics, stream)
{
    var error = 0;
    if (x_offset < 0 || x_offset + metrics.width > map.width || y_offset < 0 || y_offset + metrics.height > map.rows)
        return 6;

    var glyph_width = metrics.width;
    var glyph_height = metrics.height;
    var glyph_size;
    var line_bits = pix_bits * glyph_width;
    var pad_bytes = 0;

    switch ( image_format )
    {
        case 1:  /* byte-padded formats */
        case 6:
            var line_length;
            switch ( pix_bits )
            {
                case 1:
                    line_length = (glyph_width + 7) >>> 3;
                    break;
                case 2:
                    line_length = (glyph_width + 3) >>> 2;
                    break;
                case 4:
                    line_length = (glyph_width + 1) >>> 1;
                    break;
                default:
                    line_length = glyph_width;
            }

            glyph_size = glyph_height * line_length;
            pad_bytes  = 1;
            break;
      case 2:
      case 5:
      case 7:
            line_bits  = glyph_width * pix_bits;
            glyph_size = (glyph_height * line_bits + 7) >>> 3;
            break;
      default:
            return 3;
    }

    error = stream.EnterFrame(glyph_size);
    if (0 != error)
        return error;

    var s = new CPointer();
    s.data = stream.data;
    s.pos = stream.cur;
    blit_sbit(map, s, line_bits, pad_bytes, x_offset * pix_bits, y_offset, metrics.height);

    stream.ExitFrame();
    return error;
}
function Load_SBit_Image(strike, range, ebdt_pos, glyph_offset, slot, x_offset, y_offset, stream, metrics, depth)
{
    var map = slot.bitmap;
    var error = stream.Seek(ebdt_pos + glyph_offset);
    if (0 != error)
        return error;

    error = tt_load_sbit_metrics(stream, range, metrics);
    if (error != 0)
        return 0;

    if (depth == 0)
    {
        map.width = metrics.width;
        map.rows  = metrics.height;

        switch (strike.bit_depth)
        {
        case 1:
            map.pixel_mode = 1;
            map.pitch = (map.width + 7) >>> 3;
            break;
        case 2:
            map.pixel_mode = 3;
            map.pitch = (map.width + 3) >>> 2;
            break;
        case 4:
            map.pixel_mode = 4;
            map.pitch = (map.width + 1) >>> 1;
            break;
        case 8:
            map.pixel_mode = 2;
            map.pitch = map.width;
            break;
        default:
            return 3;
        }

        var size = map.rows * map.pitch;
        if (size == 0)
            return error;

        error = ft_glyphslot_alloc_bitmap(slot, size);
        if (error != 0)
            return error;
    }

    switch (range.image_format)
    {
        case 1:
        case 2:
        case 5:
        case 6:
        case 7:
            return Load_SBit_Single(map, x_offset, y_offset, strike.bit_depth, range.image_format, metrics, stream);

        case 8:
            error = stream.Skip(1);
            if (error != 0)
                return 83;
        case 9:
            break;

        default:
            return 3;
    }

    var num_components = stream.ReadUShort();
    FT_Error = error;
    if (error != 0)
        return error;

    error = stream.EnterFrame(4*num_components);
    if (error != 0)
        return error;

    var components = new Array(num_components);
    var count = num_components;
    var comp = 0;

    for (; count > 0; count--, comp++)
    {
        var _comp = components[comp];
        _comp.glyph_code = stream.GetUShort();
        _comp.x_offset   = stream.GetChar();
        _comp.y_offset   = stream.GetChar();
    }

    stream.ExitFrame();
    count = num_components;
    comp = 0;
    for ( ; count > 0; count--, comp++ )
    {
        var elem_metrics = new TT_SBit_MetricsRec();

        var _comp = components[comp];
        var elem = find_sbit_range(_comp.glyph_code, strike);
        error = FT_Error;
        if (error != 0)
        {
            components = null;
            return error;
        }

        error = Load_SBit_Image(strike, elem.range, ebdt_pos, elem.glyph_offset, slot, x_offset + _comp.x_offset,
                                 y_offset + _comp.y_offset, stream, elem_metrics, depth + 1);
        if (error != 0)
        {
            components = null;
            return error;
        }
    }

    return error;
}
function tt_face_load_sbit_image(face, strike_index, glyph_index, load_flags, stream, map, metrics)
{
    var elem = tt_find_sbit_image(face, glyph_index, strike_index);
    var error = FT_Error;
    if (error != 0)
        return error;

    face.goto_table(face, 1161970772, stream);
    error = FT_Error;
    if (error != 0)
    {
        face.goto_table(face, 1650745716, stream);
        error = FT_Error;
    }
    if (error != 0)
        return error;

    var ebdt_pos = stream.pos;
    var strike = elem.strike;

    error = Load_SBit_Image(strike, elem.range, ebdt_pos, elem.glyph_offset,
                             face.glyph, 0, 0, stream, metrics, 0);
    if (error != 0)
        return error;

    if (strike.flags & 1)
    {
        var advance = strike.hori.ascender - strike.hori.descender;

        metrics.vertBearingX = parseInt((-metrics.width / 2));
        metrics.vertBearingY = parseInt((advance - metrics.height)/2);
        metrics.vertAdvance  = parseInt(advance*12/10);
    }

    if (load_flags & 64)
        crop_bitmap(map, metrics);
    return error;
}
/******************************************************************************/
// ttcmap
/******************************************************************************/
function TT_CMapRec()
{
    this.cmap = new FT_CMapRec();
    this.data = null;
    this.flags = 0;

    this.type = 1;
}
function TT_CMap_ClassRec()
{
    this.clazz = new FT_CMap_ClassRec();
    this.format = 0;
    this.validate = null;
    this.get_cmap_info = null;
}
function TT_Validator()
{
    this.validator = new FT_ValidatorRec();
    this.num_glyphs = 0;
}
function tt_cmap_init(cmap, table)
{
    cmap.data = dublicate_pointer(table);
    return 0;
}
// cmap0 ----------------------------------------------------------------------------------
function tt_cmap0_char_index(cmap, char_code)
{
    var table = cmap.data;
    return char_code < 256 ? table.data[table.pos + 6 + char_code] : 0;
}
function tt_cmap0_char_next(cmap, char_code)
{
    var table = cmap.data;
    var pos_base = table.pos + 6;
    var charcode = char_code;
    var result = 0;
    var gindex = 0;

    var d = table.data;
    while (++charcode < 256)
    {
        gindex = d[pos_base + charcode];
        if (gindex != 0)
        {
            result = charcode;
            break;
        }
    }

    return {gindex:gindex,char_code:result};
}
function tt_cmap0_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap_init,null,tt_cmap0_char_index,tt_cmap0_char_next,null,null,null,null,null);
    this.format = 0;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        p.pos += 2;
        var length = FT_NEXT_USHORT(p);

        if (length > valid.limit || length < 262)
            return 8;

        if (valid.level >= 1)
        {
            var idx;
            var d = p.data;
            var c = table.pos + 6;
            for (var n = 0; n < 256; n++)
            {
                idx = d[c++];
                if (idx >= valid.num_glyphs)
                    return 16;
            }
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.data;
        data.pos += 4;
        cmap_info.format   = 0;
        cmap_info.language = FT_PEEK_USHORT(data);
        data.pos -= 4;
        return 0;
    }
}
// cmap2 ----------------------------------------------------------------------------------
function tt_cmap2_get_subheader(table, char_code)
{
    if (char_code < 0x10000)
    {
        var char_lo = (char_code & 0xFF);
        var char_hi = (char_code >>> 8);
        var p = dublicate_pointer(table);
        p.pos += 6;

        var subs = table.pos + 518;
        var sub = subs;

        if (char_hi == 0)
        {
            sub = subs;
            p.pos += char_lo * 2;
            if (FT_PEEK_USHORT(p) != 0)
                return null;
        }
        else
        {
            p.pos += char_hi * 2;
            sub = subs + (FT_PEEK_USHORT(p) & ~7);
            if (sub == subs)
                return null;
        }
        var result = new CPointer();
        result.data = table.data;
        result.pos = sub;
        return result;
    }
    return null;
}
function tt_cmap2_char_index(cmap, char_code)
{
    var table = dublicate_pointer(cmap.data);
    var result = 0;
    var subheader = tt_cmap2_get_subheader(table, char_code);
    if (subheader != null)
    {
        var p = subheader;
        var idx = (char_code & 0xFF);

        var start  = FT_NEXT_USHORT(p);
        var count  = FT_NEXT_USHORT(p);
        var delta  = FT_NEXT_SHORT (p);
        var offset = FT_PEEK_USHORT(p);

        idx -= start;
        if ( idx < count && offset != 0 )
        {
            p.pos += offset + 2 * idx;
            idx = FT_PEEK_USHORT(p);
            if (idx != 0)
                result = (idx + delta) & 0xFFFF;
        }
    }
    return result;
}
function tt_cmap2_char_next(cmap, charcode_)
{
    var table = dublicate_pointer(cmap.data);
    var gindex = 0;
    var result = 0;
    var charcode = charcode_ + 1;
    var subheader = null;

    while (charcode < 0x10000)
    {
        subheader = tt_cmap2_get_subheader(table, charcode);
        if (subheader != null)
        {
            var p = subheader;
            var start   = FT_NEXT_USHORT(p);
            var count   = FT_NEXT_USHORT(p);
            var delta   = FT_NEXT_SHORT(p);
            var offset  = FT_PEEK_USHORT(p);
            var char_lo = (charcode & 0xFF);
            var pos, idx;

            if (offset == 0)
            {
                charcode = charcode & ~255 + 256;
                continue;
            }

            if (char_lo < start)
            {
                char_lo = start;
                pos = 0;
            }
            else
                pos = (char_lo - start);

            p.pos += offset + pos * 2;
            charcode = charcode & ~255 + char_lo;

            for ( ; pos < count; pos++, charcode++ )
            {
                idx = FT_NEXT_USHORT(p);

                if ( idx != 0 )
                {
                    gindex = (idx + delta) & 0xFFFF;
                    if ( gindex != 0 )
                    {
                        result = charcode;
                        return {gindex:gindex,char_code:result};
                    }
                }
            }
        }
        charcode = charcode & ~255 + 256;
    }
    return {gindex:gindex,char_code:result};
}
function tt_cmap2_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap_init,null,tt_cmap2_char_index,tt_cmap2_char_next,null,null,null,null,null);
    this.format = 2;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        p.pos += 2;
        var length = FT_PEEK_USHORT(p);
        var n = 0;

        if (length > valid.limit || length < 6 + 512)
            return 8;

        p.pos = table.pos + 6;
        var max_subs = 0;
        for ( n = 0; n < 256; n++ )
        {
            var idx = FT_NEXT_USHORT(p);
            if (valid.level >= 2 && (idx & 7) != 0)
                return 8;

            idx >>>= 3;

            if (idx > max_subs)
                max_subs = idx;
        }

        var glyph_ids = p.pos + (max_subs + 1) * 8;
        if (glyph_ids > valid.limit)
            return 8;

        for (n = 0; n <= max_subs; n++)
        {
            var first_code = FT_NEXT_USHORT(p);
            var code_count = FT_NEXT_USHORT(p);
            var delta = FT_NEXT_SHORT(p);
            var offset = FT_NEXT_USHORT(p);

            if (code_count == 0)
                continue;

            if (valid.level >= 2)
            {
                if (first_code >= 256 || first_code + code_count > 256)
                    return 8;
            }

            if (offset != 0)
            {
                var ids = p.pos - 2 + offset;
                if (ids < glyph_ids || ids + code_count*2 > (table.pos+length))
                    return 9;

                if (valid.level >= 1)
                {
                    var limit = p.pos + code_count * 2;
                    var idx;
                    for (; p.pos < limit;)
                    {
                        idx = FT_NEXT_USHORT(p);
                        if ( idx != 0 )
                        {
                            idx = (idx + delta) & 0xFFFF;
                            if (idx >= valid.num_glyphs)
                                return 16;
                        }
                    }
                }
            }
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        cmap.data.pos += 4;
        cmap_info.format   = 2;
        cmap_info.language = FT_PEEK_USHORT(cmap.data);
        cmap.data.pos -= 4;
        return 0;
    }
}
// cmap4 ----------------------------------------------------------------------------------
function TT_CMap4Rec()
{
    this.cmap = new TT_CMapRec();
    this.cur_charcode;
    this.cur_gindex;

    this.num_ranges;
    this.cur_range;
    this.cur_start;
    this.cur_end;
    this.cur_delta;
    this.cur_values;

    this.type = 2;
}
function tt_cmap4_init(cmap,table)
{
    var p = dublicate_pointer(table);
    p.pos += 6;
    cmap.cmap.data = dublicate_pointer(table);
    cmap.num_ranges = FT_PEEK_USHORT(p) >>> 1;
    cmap.cur_charcode = 0xFFFFFFFF;
    cmap.cur_gindex = 0;
    return 0;
}
function tt_cmap4_set_range(cmap, range_index)
{
    var table = cmap.cmap.data.pos;
    var p = dublicate_pointer(cmap.cmap.data);
    var num_ranges = cmap.num_ranges;

    while (range_index < num_ranges)
    {
        p.pos = table + 14 + range_index * 2;
        cmap.cur_end = FT_PEEK_USHORT(p);

        p.pos += 2 + num_ranges * 2;
        cmap.cur_start = FT_PEEK_USHORT(p);

        p.pos += num_ranges * 2;
        cmap.cur_delta = FT_PEEK_SHORT(p);

        p.pos += num_ranges * 2;
        var offset = FT_PEEK_USHORT(p);

        if (range_index >= num_ranges - 1 && cmap.cur_start == 0xFFFF && cmap.cur_end == 0xFFFF)
        {
            var face = cmap.cmap.cmap.charmap.face;
            var limit = face.cmap_table.pos + face.cmap_size;
            if (offset && p.pos + offset + 2 > limit)
            {
                cmap.cur_delta = 1;
                offset = 0;
            }
        }
        if (offset != 0xFFFF)
        {
            cmap.cur_values = null;
            if (offset != 0)
            {
                cmap.cur_values = dublicate_pointer(p);
                cmap.cur_values.pos += offset;
            }
            cmap.cur_range = range_index;
            return 0;
        }
        range_index++;
    }
    return -1;
}
function tt_cmap4_next(cmap)
{
    if (cmap.cur_charcode >= 0xFFFF)
    {
        cmap.cur_charcode = 0xFFFFFFFF;
        cmap.cur_gindex = 0;
        return;
    }

    var charcode = cmap.cur_charcode + 1;

    if (charcode < cmap.cur_start)
        charcode = cmap.cur_start;

    while(true)
    {
        var p = new CPointer();
        var values = cmap.cur_values;
        var end = cmap.cur_end;
        var delta = cmap.cur_delta;

        if ( charcode <= end )
        {
            if (values != null)
            {
                p.data = values.data;
                p.pos = values.pos;
                p.pos += 2*(charcode - cmap.cur_start);
                do
                {
                    var gindex = FT_NEXT_USHORT(p);
                    if (gindex != 0)
                    {
                        gindex = ((gindex + delta) & 0xFFFF);
                        if (gindex != 0)
                        {
                            cmap.cur_charcode = charcode;
                            cmap.cur_gindex   = gindex;
                            return;
                        }
                    }
                } while ( ++charcode <= end );
            }
            else
            {
                do
                {
                    var gindex = ((charcode + delta)&0xFFFF);
                    if (gindex != 0)
                    {
                        cmap.cur_charcode = charcode;
                        cmap.cur_gindex = gindex;
                        return;
                    }
                } while ( ++charcode <= end );
            }
        }

        if (tt_cmap4_set_range(cmap, cmap.cur_range + 1) < 0)
            break;

        if (charcode < cmap.cur_start)
            charcode = cmap.cur_start;
    }
    cmap.cur_charcode = 0xFFFFFFFF;
    cmap.cur_gindex = 0;
}
function tt_cmap4_char_map_linear(cmap, _charcode, next)
{
    var num_segs2, start, end, offset;
    var delta;
    var i, num_segs;
    var charcode = _charcode;
    var gindex = 0;
    var base = cmap.cmap.data.pos;
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 6;

    num_segs2 = FT_PEEK_USHORT(p) & ~1;
    num_segs = num_segs2 >>> 1;

    if (num_segs == 0)
        return {gindex:0,char_code:_charcode};

    if (next != 0)
        charcode++;

    for (; charcode <= 0xFFFF; charcode++)
    {
        p.pos = base + 14;
        var q = dublicate_pointer(p);
        q.pos = base + 16 + num_segs2;

        for ( i = 0; i < num_segs; i++ )
        {
            end   = FT_NEXT_USHORT(p);
            start = FT_NEXT_USHORT(q);

            if ( charcode >= start && charcode <= end )
            {
                p.pos = q.pos - 2 + num_segs2;
                delta = FT_PEEK_SHORT(p);
                p.pos += num_segs2;
                offset = FT_PEEK_USHORT(p);

                if (i >= num_segs - 1 && start == 0xFFFF && end == 0xFFFF)
                {
                    var face = cmap.cmap.cmap.charmap.face;
                    var limit = face.cmap_table.pos + face.cmap_size;
                    if (offset != 0 && p.pos + offset + 2 > limit)
                    {
                        delta  = 1;
                        offset = 0;
                    }
                }

                if (offset == 0xFFFF)
                    continue;

                if (offset != 0)
                {
                    p.pos += offset + (charcode - start) * 2;
                    gindex = FT_PEEK_USHORT(p);
                    if (gindex != 0)
                        gindex = (gindex + delta) & 0xFFFF;
                }
                else
                    gindex = (charcode + delta) & 0xFFFF;

                break;
            }
        }

        if (next == 0 || gindex != 0)
            break;
    }

    if (next != 0 && gindex != 0)
        return {gindex:gindex,char_code:charcode};

    return {gindex:gindex,char_code:_char_code};
}
function tt_cmap4_char_map_binary(cmap, _charcode, next)
{
    var num_segs2, start, end, offset;
    var delta;
    var max, min, mid, num_segs;
    var __charcode = _charcode;
    var charcode = _charcode;
    var gindex = 0;
    var p = dublicate_pointer(cmap.cmap.data);
    var base = p.pos;

    p.pos += 6;
    num_segs2 = FT_PEEK_USHORT(p) & ~1;

    if (num_segs2 == 0)
        return {gindex:gindex,char_code:__charcode};

    num_segs = num_segs2 >>> 1;

    mid = num_segs;
    end = 0xFFFF;

    if (next != 0)
        charcode++;

    min = 0;
    max = num_segs;

    while (min < max)
    {
        mid = (min + max) >>> 1;
        p.pos = base + 14 + mid * 2;
        end = FT_PEEK_USHORT(p);
        p.pos += 2 + num_segs2;
        start = FT_PEEK_USHORT(p);

        if (charcode < start)
            max = mid;
        else if (charcode > end)
            min = mid + 1;
        else
        {
            p.pos += num_segs2;
            delta = FT_PEEK_SHORT(p);
            p.pos += num_segs2;
            offset = FT_PEEK_USHORT(p);

            if (mid >= num_segs - 1 && start == 0xFFFF && end == 0xFFFF)
            {
                var face = cmap.cmap.cmap.charmap.face;
                var limit = face.cmap_table.pos + face.cmap_size;
                if (offset && p.pos + offset + 2 > limit)
                {
                    delta  = 1;
                    offset = 0;
                }
            }

            if ((cmap.flags & 2) != 0)
            {
                var i;
                max = mid;
                if (offset == 0xFFFF)
                    mid = max + 1;

                for (i = max ; i > 0; i--)
                {
                    var old_p = p.pos;
                    p.pos = base + 14 + (i - 1) * 2;
                    var prev_end = FT_PEEK_USHORT(p);

                    if ( charcode > prev_end )
                    {
                        p.pos = old_p.pos;
                        break;
                    }

                    end = prev_end;
                    p.pos += 2 + num_segs2;
                    start = FT_PEEK_USHORT(p);
                    p.pos += num_segs2;
                    delta = FT_PEEK_SHORT(p);
                    p.pos += num_segs2;
                    offset = FT_PEEK_USHORT(p);

                    if (offset != 0xFFFF)
                        mid = i - 1;
                }

                if (mid == max + 1)
                {
                    if (i != max)
                    {
                        p.pos = base + 14 + max * 2;
                        end = FT_PEEK_USHORT(p);
                        p.pos += 2 + num_segs2;
                        start = FT_PEEK_USHORT(p);
                        p.pos += num_segs2;
                        delta = FT_PEEK_SHORT(p);
                        p.pos += num_segs2;
                        offset = FT_PEEK_USHORT(p);
                    }
                    mid = max;
                    for (i = max + 1; i < num_segs; i++)
                    {
                        p.pos = base + 14 + i * 2;
                        var next_end = FT_PEEK_USHORT(p);
                        p.pos += 2 + num_segs2;
                        var next_start = FT_PEEK_USHORT(p);

                        if (charcode < next_start)
                            break;

                        end = next_end;
                        start = next_start;
                        p.pos += num_segs2;
                        delta = FT_PEEK_SHORT(p);
                        p.pos += num_segs2;
                        offset = FT_PEEK_USHORT(p);

                        if (offset != 0xFFFF)
                            mid = i;
                    }
                    i--;

                    if (mid == max)
                    {
                        mid = i;
                        break;
                    }
                }

                if (mid != i)
                {
                    p.pos = base + 14 + mid * 2;
                    end = FT_PEEK_USHORT(p);
                    p.pos += 2 + num_segs2;
                    start = FT_PEEK_USHORT(p);
                    p.pos += num_segs2;
                    delta = FT_PEEK_SHORT(p);
                    p.pos += num_segs2;
                    offset = FT_PEEK_USHORT(p);
                }
            }
            else
            {
                if (offset == 0xFFFF)
                    break;
            }

            if (offset != 0)
            {
                p.pos += offset + (charcode - start) * 2;
                gindex = FT_PEEK_USHORT(p);
                if (gindex != 0)
                    gindex = (gindex + delta) & 0xFFFF;
            }
            else
                gindex = (charcode + delta) & 0xFFFF;

            break;
        }
    }

    if (next != 0)
    {
        if ( charcode > end )
        {
            mid++;
            if ( mid == num_segs )
                return {gindex:0,char_code:__charcode};
        }

        if (tt_cmap4_set_range(cmap, mid) != 0)
        {
            if (gindex != 0)
                __charcode = charcode;
        }
        else
        {
            cmap.cur_charcode = charcode;
            if (gindex != 0)
                cmap.cur_gindex = gindex;
            else
            {
                cmap.cur_charcode = charcode;
                tt_cmap4_next(cmap);
                gindex = cmap.cur_gindex;
            }

            if (gindex != 0)
                __charcode = cmap.cur_charcode;
        }
    }

    return {gindex:gindex,char_code:__charcode};
}
function tt_cmap4_char_index(cmap,char_code)
{
    if (char_code >= 0x10000)
        return 0;

    if ((cmap.cmap.flags & 1) != 0)
        return tt_cmap4_char_map_linear(cmap, char_code, 0).gindex;
    else
        return tt_cmap4_char_map_binary(cmap, char_code, 0).gindex;
}
function tt_cmap4_char_next(cmap,char_code)
{
    var gindex = 0;
    var _char_code = char_code;
    if (_char_code >= 0xFFFF)
        return {gindex:gindex,char_code:_char_code};

    if ((cmap.flags & 1) != 0)
    {
        var r = tt_cmap4_char_map_linear(cmap, char_code, 1);
        gindex = r.gindex;
        _char_code = r.char_code;
    }
    else
    {
        if (_char_code == cmap.cur_charcode)
        {
            tt_cmap4_next(cmap);
            gindex = cmap.cur_gindex;
            if (gindex)
                _char_code = cmap.cur_charcode;
        }
        else
        {
            var r = tt_cmap4_char_map_binary(cmap, char_code, 1);
            gindex = r.gindex;
            _char_code = r.char_code;
        }
    }
    return {gindex:gindex,char_code:_char_code};
}
function tt_cmap4_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap4_init,null,tt_cmap4_char_index,tt_cmap4_char_next,null,null,null,null,null);
    this.format = 4;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        p.pos += 2;
        var length = FT_NEXT_USHORT(p);
        var error = 0;

        if (length < 16)
            return 8;

        if (table.pos + length > valid.limit)
        {
            if (valid.level >= 1)
                return 8;

            length = (valid.limit - table.pos);
        }

        p.pos = table.pos + 6;
        var num_segs = FT_NEXT_USHORT(p);

        if (valid.level >= 2)
        {
            if ((num_segs & 1) != 0)
                return 8;
        }

        num_segs >>>= 1;

        if (length < 16 + num_segs * 2 * 4)
            return 8;

        if (valid.level >= 2)
        {
            var search_range   = FT_NEXT_USHORT(p);
            var entry_selector = FT_NEXT_USHORT(p);
            var range_shift    = FT_NEXT_USHORT(p);

            if (((search_range | range_shift) & 1) != 0)
                return 8;

            search_range >>>= 1;
            range_shift >>>= 1;

            if (search_range > num_segs || search_range * 2 < num_segs || search_range + range_shift != num_segs                 ||
                                search_range != (1 << entry_selector))
                return 8;
        }

        var ends      = table.pos + 14;
        var starts    = table.pos + 16 + num_segs * 2;
        var deltas    = starts  + num_segs * 2;
        var offsets   = deltas  + num_segs * 2;
        var glyph_ids = offsets + num_segs * 2;

        if (valid.level >= 2)
        {
            p.pos = ends + (num_segs - 1) * 2;
            if (FT_PEEK_USHORT(p) != 0xFFFF)
                return 8;
        }

        var start, end, offset, n;
        var last_start = 0, last_end = 0;
        var delta;
        var p_start = new CPointer(); p_start.data = p.data; p_start.pos = starts;
        var p_end = new CPointer(); p_end.data = p.data; p_end.pos = ends;
        var p_delta = new CPointer(); p_delta.data = p.data; p_delta.pos = deltas;
        var p_offset = new CPointer(); p_offset.data = p.data; p_offset.pos = offsets;

        for ( n = 0; n < num_segs; n++ )
        {
            p.pos = p_offset.pos;
            start  = FT_NEXT_USHORT(p_start);
            end    = FT_NEXT_USHORT(p_end);
            delta  = FT_NEXT_SHORT(p_delta);
            offset = FT_NEXT_USHORT(p_offset);

            if (start > end)
                return 8;;

            if (start <= last_end && n > 0)
            {
                if (valid.level >= 1)
                    return 8;
                else
                {
                    if (last_start > start || last_end > end)
                        error |= 1;
                    else
                        error |= 2;
                }
            }

            if (offset && offset != 0xFFFF)
            {
                p.pos += offset;
                if (valid.level >= 1)
                {
                    if (p.pos < glyph_ids || p.pos + (end - start + 1) * 2 > table.pos + length)
                        return 8;
                }
                else if (n != num_segs - 1 || !(start == 0xFFFF && end == 0xFFFF))
                {
                    if (p.pos < glyph_ids || p.pos + (end - start + 1) * 2 > valid.limit)
                        return 8;
                }

                if (valid.level >= 1)
                {
                    var idx;
                    for (var i = start; i < end; i++ )
                    {
                        idx = FT_NEXT_USHORT(p);
                        if (idx != 0)
                        {
                            idx = (idx + delta) & 0xFFFF;
                            if (idx >= valid.num_glyphs)
                                return 16;
                        }
                    }
                }
            }
            else if (offset == 0xFFFF)
            {
              if (valid.level >= 2 || n != num_segs - 1 || !( start == 0xFFFF && end == 0xFFFF))
                return 8;
            }

            last_start = start;
            last_end   = end;
        }

        return error;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.cmap.data;
        data.pos += 4;
        cmap_info.format = 4;
        cmap_info.language = FT_PEEK_USHORT(data);
        data.pos -= 4;
        return 0;
    }
}
// cmap6 ----------------------------------------------------------------------------------
function tt_cmap6_char_index(cmap, char_code)
{
    var p = dublicate_pointer(cmap.data);
    p.pos += 6;
    var result = 0;
    var start = FT_NEXT_USHORT(p);
    var count = FT_NEXT_USHORT(p);
    var idx =  char_code - start;

    if ( idx < count )
    {
        p.pos += 2 * idx;
        result = FT_PEEK_USHORT(p);
    }
    return result;
}
function tt_cmap6_char_next(cmap, _char_code)
{
    var p = dublicate_pointer(cmap.data);
    p.pos += 6;
    var result    = 0;
    var char_code = _char_code + 1;
    var gindex = 0;

    var start = FT_NEXT_USHORT(p);
    var count = FT_NEXT_USHORT(p);

    if (char_code >= 0x10000)
        return {gindex:gindex,char_code:_char_code};

    if (char_code < start)
        char_code = start;

    var idx = char_code - start;
    p.pos += 2 * idx;
    for (; idx < count; idx++)
    {
        gindex = FT_NEXT_USHORT(p);
        if (gindex != 0)
        {
            result = char_code;
            break;
        }
        char_code++;
    }
    return {gindex:gindex,char_code:result};
}
function tt_cmap6_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap_init,null,tt_cmap6_char_index,tt_cmap6_char_next,null,null,null,null,null);
    this.format = 6;
    this.validate = function(table, valid)
    {
        var base = table.pos;
        var p = new CPointer();
        p.data = table.data;

        if (base + 10 > valid.limit)
            return 8;

        p.pos = base + 2;
        var length = FT_NEXT_USHORT(p);
        p.pos = base + 8;
        var count = FT_NEXT_USHORT(p);

        if (base + length > valid.limit || length < 10 + count * 2)
            return 8;

        if (valid.level >= 1)
        {
            var gindex;
            for (; count > 0; count--)
            {
                gindex = FT_NEXT_USHORT(p);
                if (gindex >= valid.num_glyphs)
                    return 16;
            }
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.data;
        data.pos += 4;
        cmap_info.format = 6;
        cmap_info.language = FT_PEEK_USHORT(data);
        data.pos -= 4;
        return 0;
    }
}
// cmap8 ----------------------------------------------------------------------------------
function tt_cmap8_char_index(cmap, char_code)
{
    var result = 0;
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 8204;
    var num_groups = FT_NEXT_ULONG(p);
    var start, end, start_id;
    for (; num_groups > 0; num_groups--)
    {
        start    = FT_NEXT_ULONG(p);
        end      = FT_NEXT_ULONG(p);
        start_id = FT_NEXT_ULONG(p);

        if (char_code < start)
            break;

        if (char_code <= end)
        {
            result = (start_id + char_code - start);
            break;
        }
    }
    return result;
}
function tt_cmap8_char_next(cmap, _char_code)
{
    var result = 0;
    var char_code = _char_code + 1;
    var gindex = 0;
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 8204;
    var num_groups = FT_NEXT_ULONG(p);
    var start, end, start_id;

    for ( ; num_groups > 0; num_groups-- )
    {
        start    = FT_NEXT_ULONG(p);
        end      = FT_NEXT_ULONG(p);
        start_id = FT_NEXT_ULONG(p);

        if (char_code < start)
            char_code = start;

        if (char_code <= end)
        {
            gindex = (char_code - start + start_id);
            if ( gindex != 0 )
            {
                result = char_code;
                break;
            }
        }
    }
    return {gindex:gindex,char_code:result};
}
function tt_cmap8_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap_init,null,tt_cmap8_char_index,tt_cmap8_char_next,null,null,null,null,null);
    this.format = 8;
    this.validate = function(table, valid)
    {
        var base = table.pos;
        var p = dublicate_pointer(table);
        p.pos += 4;

        if (base + 16 + 8192 > valid.limit)
            return 8;

        var length = FT_NEXT_ULONG(p);
        if (length > (valid.limit - base) || length < 8192 + 16)
            return 8;

        var is32 = base + 12;
        p.pos = is32 + 8192;
        var num_groups = FT_NEXT_ULONG(p);

        if (p.pos + num_groups * 12 > valid.limit)
            return 8;

        var n, start, end, start_id, count, last = 0;
        for ( n = 0; n < num_groups; n++ )
        {
            var hi, lo;
            start    = FT_NEXT_ULONG(p);
            end      = FT_NEXT_ULONG(p );
            start_id = FT_NEXT_ULONG(p);

            if (start > end)
                return 8;

            if (n > 0 && start <= last)
                return 8;

            if (valid.level >= 1)
            {
                if (start_id + end - start >= valid.num_glyphs)
                    return 16;

                count = (end - start + 1);
                if ((start & ~0xFFFF) != 0)
                {
                    for (; count > 0; count--, start++)
                    {
                        hi = (start >>> 16);
                        lo = (start & 0xFFFF);

                        if ((p.data[is32 + hi >>> 3] & (0x80 >>> (hi & 7))) == 0)
                            return 8;

                        if ((p.data[is32 + lo >>> 3] & (0x80 >>> (lo & 7))) == 0)
                            return 8;
                    }
                }
                else
                {
                    if ((end & ~0xFFFF) != 0)
                        return 8;

                    for ( ; count > 0; count--, start++ )
                    {
                        lo = (start & 0xFFFF);
                        if ((p.data[is32 + lo >>> 3] & (0x80 >>> (lo & 7))) != 0)
                            return 8;
                    }
                }
            }
            last = end;
        }

        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.cmap.data;
        data.pos += 8;
        cmap_info.format = 8;
        cmap_info.language = FT_PEEK_ULONG(data);
        data.pos -= 8;
        return 0;
    }
}
// cmap10 ---------------------------------------------------------------------------------
function tt_cmap10_char_index(cmap, char_code)
{
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 12;
    var result = 0;
    var start = FT_NEXT_ULONG(p);
    var count = FT_NEXT_ULONG(p);
    var idx = (char_code - start);

    if (idx < count)
    {
        p.pos += 2 * idx;
        result = FT_PEEK_USHORT(p);
    }
    return result;
}
function tt_cmap10_char_next(cmap, _char_code)
{
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 12;
    var char_code = _char_code + 1;
    var gindex = 0;
    var start = FT_NEXT_ULONG(p);
    var count = FT_NEXT_ULONG(p);

    if (char_code < start)
        char_code = start;

    var idx = char_code - start;
    p.pos += 2 * idx;

    for (; idx < count; idx++)
    {
        gindex = FT_NEXT_USHORT(p);
        if (gindex != 0)
            break;
        char_code++;
    }
    return {gindex:gindex,char_code:char_code};
}
function tt_cmap10_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap_init,null,tt_cmap10_char_index,tt_cmap10_char_next,null,null,null,null,null);
    this.format = 10;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        var base = table.pos;
        p.pos += 4;

        if (base + 20 > valid.limit)
            return 8;

        var length = FT_NEXT_ULONG(p);
        p.pos = base + 16;
        var count = FT_NEXT_ULONG(p);

        if (length > (valid.limit - base) || length < 20 + count * 2)
            return 8;

        if (valid.level >= 1)
        {
            var gindex;
            for (; count > 0; count--)
            {
                gindex = FT_NEXT_USHORT(p);
                if (gindex >= valid.num_glyphs)
                    return 16;
            }
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.cmap.data;
        data.pos += 8;
        cmap_info.format = 10;
        cmap_info.language = FT_PEEK_ULONG(data);
        data.pos -= 8;
        return 0;
    }
}
// cmap12 ---------------------------------------------------------------------------------
function TT_CMap12Rec()
{
    this.cmap = new TT_CMapRec();
    this.valid;
    this.cur_charcode;
    this.cur_gindex;
    this.cur_group;
    this.num_groups;

    this.type = 3;
}
function tt_cmap12_init(cmap, table)
{
    cmap.cmap.data = dublicate_pointer(table);
    table.pos += 12;
    cmap.num_groups = FT_PEEK_ULONG(table);
    table.pos -= 12;
    cmap.valid = 0;
    return 0;
}
function tt_cmap12_next(cmap)
{
    if (cmap.cur_charcode >= 0xFFFFFFFF)
    {
        cmap.valid = 0;
        return;
    }

    var p = dublicate_pointer(cmap.cmap.data);
    var base = p.pos;
    var start, end, start_id;
    var gindex;

    var char_code = cmap.cur_charcode + 1;
    for (var n = cmap.cur_group; n < cmap.num_groups; n++)
    {
        p.pos = base + 16 + 12 * n;
        start    = FT_NEXT_ULONG(p);
        end      = FT_NEXT_ULONG(p);
        start_id = FT_PEEK_ULONG(p);

        if (char_code < start)
            char_code = start;

        for ( ; char_code <= end; char_code++ )
        {
            gindex = (start_id + char_code - start);
            if (gindex != 0)
            {
                cmap.cur_charcode = char_code;
                cmap.cur_gindex   = gindex;
                cmap.cur_group    = n;
                return;
            }
        }
    }
    cmap.valid = 0;
}
function tt_cmap12_char_map_binary(cmap, _char_code, next)
{
    var gindex = 0;
    var p = dublicate_pointer(cmap.cmap.data);
    var base = p.pos;
    p.pos += 12;
    var num_groups = FT_PEEK_ULONG(p);
    var char_code = _char_code;
    var __char_code = _char_code;
    var start, end, start_id;
    var max, min, mid;

    if (num_groups == 0)
        return {gindex:gindex,char_code:__char_code};

    mid = num_groups;
    end = 0xFFFFFFFF;

    if (next != 0)
        char_code++;

    min = 0;
    max = num_groups;

    while (min < max)
    {
        mid = (min + max) >>> 1;
        p.pos = base + 16 + 12 * mid;

        start = FT_NEXT_ULONG(p);
        end = FT_NEXT_ULONG(p);

        if (char_code < start)
            max = mid;
        else if (char_code > end)
            min = mid + 1;
        else
        {
            start_id = FT_PEEK_ULONG(p);
            gindex = (start_id + char_code - start);
            break;
        }
    }

    if (next != 0)
    {
        if (char_code > end)
        {
            mid++;
            if (mid == num_groups)
                return 0;
        }

        cmap.valid = 1;
        cmap.cur_charcode = char_code;
        cmap12.cur_group = mid;

        if (gindex == 0)
        {
            tt_cmap12_next(cmap);

            if (cmap.valid == 1)
                gindex = cmap.cur_gindex;
        }
        else
            cmap.cur_gindex = gindex;

        if (gindex != 0)
            __char_code = cmap.cur_charcode;
    }

    return {gindex:gindex,char_code:__char_code};
}
function tt_cmap12_char_index(cmap, char_code)
{
    return tt_cmap12_char_map_binary(cmap, char_code, 0).gindex;
}
function tt_cmap12_char_next(cmap, _char_code)
{
    var gindex = 0;
    var __char_code = _char_code;

    if (cmap.cur_charcode >= 0xFFFFFFFF)
        return {gindex:gindex,char_code:__char_code};

    if (cmap12.valid == 1 && cmap.cur_charcode == _char_code)
    {
        tt_cmap12_next(cmap);
        if (1 == cmap.valid)
        {
            gindex = cmap.cur_gindex;
            if (gindex != 0)
                __char_code = cmap.cur_charcode;
        }
        else
            gindex = 0;
    }
    else
        return tt_cmap12_char_map_binary(cmap, _char_code, 1);

    return {gindex:gindex,char_code:__char_code};
}
function tt_cmap12_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap12_init,null,tt_cmap12_char_index,tt_cmap12_char_next,null,null,null,null,null);
    this.format = 12;
    this.validate = function(table, valid)
    {
        var base = table.pos;
        var p = dublicate_pointer(table);
        if (p.pos + 16 > valid.limit)
            return 8;
        p.pos = base + 4;
        var length = FT_NEXT_ULONG(p);
        p.pos = base + 12;
        var num_groups = FT_NEXT_ULONG(p);
        if (length > (valid.limit - base) || length < 16 + 12 * num_groups)
            return 8;
        var n, start, end, start_id, last = 0;
        for (n = 0; n < num_groups; n++)
        {
            start    = FT_NEXT_ULONG(p);
            end      = FT_NEXT_ULONG(p);
            start_id = FT_NEXT_ULONG(p);

            if (start > end)
                return 8;
            if (n > 0 && start <= last)
                return 8;
            if (valid.level >= 1)
            {
                if (start_id + end - start >= valid.num_glyphs)
                    return 16;
            }
            last = end;
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.cmap.data;
        data.pos += 8;
        cmap_info.format = 12;
        cmap_info.language = FT_PEEK_ULONG(data);
        data.pos -= 8;
        return 0;
    }
}
// cmap13 ---------------------------------------------------------------------------------
function TT_CMap13Rec()
{
    this.cmap = new TT_CMapRec();
    this.valid;
    this.cur_charcode;
    this.cur_gindex;
    this.cur_group;
    this.num_groups;

    this.type = 4;
}
function tt_cmap13_init(cmap, table)
{
    cmap.cmap.data = dublicate_pointer(table);
    table.pos += 12;
    cmap.num_groups = FT_PEEK_ULONG(table);
    table.pos -= 12;
    cmap.valid = 0;
    return 0;
}
function tt_cmap13_next(cmap)
{
    if (cmap.cur_charcode >= 0xFFFFFFFF)
    {
        cmap.valid = 0;
        return;
    }

    var p = dublicate_pointer(cmap.cmap.data);
    var base = p.pos;
    var start, end, glyph_id, char_code;
    var gindex;

    char_code = cmap.cur_charcode + 1;
    for (var n = cmap.cur_group; n < cmap.num_groups; n++ )
    {
        p.pos = base + 16 + 12 * n;
        start = FT_NEXT_ULONG(p);
        end = FT_NEXT_ULONG(p);
        glyph_id = FT_PEEK_ULONG(p);

        if (char_code < start)
            char_code = start;

        if ( char_code <= end )
        {
            gindex = glyph_id;
            if (gindex != 0)
            {
                cmap.cur_charcode = char_code;
                cmap.cur_gindex = gindex;
                cmap.cur_group = n;
                return;
            }
        }
    }
    cmap.valid = 0;
}
function tt_cmap13_char_map_binary(cmap, _char_code, next)
{
    var gindex = 0;
    var p = dublicate_pointer(cmap.cmap.data);
    var base = p.pos;
    p.pos += 12;
    var num_groups = FT_PEEK_ULONG(p);
    var char_code = _char_code;
    var __char_code = _char_code;
    var start, end;
    var max, min, mid;

    if (num_groups == 0)
        return {gindex:gindex,char_code:__char_code};

    mid = num_groups;
    end = 0xFFFFFFFF;

    if (next != 0)
        char_code++;

        min = 0;
        max = num_groups;

    while (min < max)
    {
        mid = (min + max) >>> 1;
        p.pos = base + 16 + 12 * mid;

        start = FT_NEXT_ULONG(p);
        end   = FT_NEXT_ULONG(p);

        if (char_code < start)
            max = mid;
        else if (char_code > end)
            min = mid + 1;
        else
        {
            gindex = FT_PEEK_ULONG(p);
            break;
        }
    }

    if (next != 0)
    {
        if (char_code > end)
        {
            mid++;
            if (mid == num_groups)
                return 0;
        }

        cmap.valid        = 1;
        cmap.cur_charcode = char_code;
        cmap.cur_group    = mid;

        if (gindex == 0)
        {
            tt_cmap13_next( cmap13 );
            if (cmap.valid == 1)
                gindex = cmap.cur_gindex;
        }
        else
            cmap.cur_gindex = gindex;

        if (gindex != 0)
            __char_code = cmap.cur_charcode;
    }

    return {gindex:gindex,char_code:__char_code};
}
function tt_cmap13_char_index(cmap, char_code)
{
    return tt_cmap13_char_map_binary(cmap, char_code, 0).gindex;
}
function tt_cmap13_char_next(cmap, _char_code)
{
    if (cmap.cur_charcode >= 0xFFFFFFFF)
        return {gindex:0,char_code:_char_code};

    var gindex;
    var __char_code = _char_code;
    if (cmap.valid == 1 && cmap.cur_charcode == _char_code)
    {
        tt_cmap13_next(cmap);
        if (cmap.valid == 1)
        {
            gindex = cmap.cur_gindex;
            if (gindex != 0)
                __char_code = cmap.cur_charcode;
        }
        else
            gindex = 0;
    }
    else
        return tt_cmap13_char_map_binary( cmap, pchar_code, 1 );

    return {gindex:gindex,char_code:__char_code};
}
function tt_cmap13_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap13_init,null,tt_cmap13_char_index,tt_cmap13_char_next,null,null,null,null,null);
    this.format = 13;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        var base = p.pos;

        if (bae + 16 > valid.limit)
            return 8;

        p.pos = base + 4;
        var length = FT_NEXT_ULONG(p);
        p.pos = base + 12;
        var num_groups = FT_NEXT_ULONG(p);

        if (length > (valid.limit - base) || length < 16 + 12 * num_groups)
            return 8;

        var start, end, glyph_id, last = 0;
        for (var n = 0; n < num_groups; n++)
        {
            start    = FT_NEXT_ULONG(p);
            end      = FT_NEXT_ULONG(p);
            glyph_id = FT_NEXT_ULONG(p);

            if (start > end)
                return 8;

            if (n > 0 && start <= last)
                return 8;

            if (valid.level >= 1)
            {
                if (glyph_id >= valid.num_glyphs)
                    return 16;
            }

            last = end;
        }
        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        var data = cmap.data;
        data.pos += 8;
        cmap_info.format = 13;
        cmap_info.language = FT_PEEK_ULONG(data);
        data.pos -= 8;
        return 0;
    }
}
// cmap14 ---------------------------------------------------------------------------------
function TT_CMap14Rec()
{
    this.cmap = new TT_CMapRec();
    this.num_selectors;
    this.max_results;
    this.results = null;

    this.type = 5;
}
function tt_cmap14_done(cmap)
{
    cmap.results = null;
}
function tt_cmap14_ensure(cmap, num_results, memory)
{
    var old_max = cmap.max_results;
    if (num_results > cmap.max_results)
    {
        var c = num_results - old_max;
        for (;c>0;c--)
            cmap.results[old_max+c-1] = 0;
        cmap.max_results = num_results;
    }
    return 0;
}
function tt_cmap14_init(cmap, table)
{
    cmap.cmap.data = dublicate_pointer(table);
    table.pos += 6;
    cmap.num_selectors = FT_PEEK_ULONG(table);
    table.pos -= 6;
    cmap.max_results   = 0;
    cmap.results = null;
    return 0;
}
function tt_cmap14_char_index(cmap,char_code)
{
    return 0;
}
function tt_cmap14_char_next(cmap, _char_code)
{
    return {gindex:0,char_code:0};
}
function tt_cmap14_char_map_def_binary(base, char_code)
{
    var p = dublicate_pointer(base);
    var numRanges = FT_PEEK_ULONG(p);
    var min = 0;
    var max = numRanges;

    while (min < max)
    {
        var mid = (min + max) >>> 1;
        p.pos = base.pos + 4 + 4 * mid;
        var start = FT_NEXT_UOFF3(p);
        var cnt = FT_NEXT_BYTE(p);

        if (char_code < start)
            max = mid;
        else if (char_code > start+cnt)
            min = mid + 1;
        else
            return 1;
    }
    return 0;
}
function tt_cmap14_char_map_nondef_binary(base, char_code)
{
    var p = dublicate_pointer(base);
    var numMappings = FT_PEEK_ULONG(p);
    var min = 0;
    var max = numMappings;

    while (min < max)
    {
        var mid = (min + max) >>> 1;
        p.pos = base.pos + 4 + 5 * mid;
        var uni = FT_NEXT_UOFF3(p);

        if (char_code < uni)
            max = mid;
        else if (char_code > uni)
            min = mid + 1;
        else
            return FT_PEEK_USHORT(p);
    }
    return 0;
}
function tt_cmap14_find_variant(base, variantCode)
{
    var p = dublicate_pointer(base);
    var numVar = FT_PEEK_ULONG(p);
    var min = 0;
    var max = numVar;

    while (min < max)
    {
        var mid = (min + max) >>> 1;
        p.pos = base.pos + 4 + 11 * mid;
        var varSel = FT_NEXT_UOFF3(p);

        if (variantCode < varSel)
            max = mid;
        else if (variantCode > varSel)
            min = mid + 1;
        else
            return p;
    }
    return null;
}
function tt_cmap14_char_var_index(cmap, ucmap, charcode, variantSelector)
{
    var base = dublicate_pointer(cmap.cmap.data);
    base.pos += 6;
    var p = tt_cmap14_find_variant(base, variantSelector);
    if (null == p)
        return 0;

    var defOff = FT_NEXT_ULONG(p);
    var nondefOff = FT_PEEK_ULONG(p);

    base.pos = cmap.cmap.data.pos + defOff;
    if (defOff != 0 && 0 != tt_cmap14_char_map_def_binary(base, charcode))
    {
        return ucmap.cmap.clazz.char_index(ucmap.cmap, charcode);
    }

    base.pos = cmap.cmap.data.pos + nondefOff;
    if (nondefOff != 0)
        return tt_cmap14_char_map_nondef_binary(base, charcode);

    return 0;
}
function tt_cmap14_char_var_isdefault(cmap, charcode, variantSelector)
{
    var base = dublicate_pointer(cmap.cmap.data);
    var base_pos = base.pos;
    base += 6;
    var p = tt_cmap14_find_variant(base, variantSelector);
    if (null == p)
        return -1;

    var defOff    = FT_NEXT_ULONG(p);
    var nondefOff = FT_NEXT_ULONG(p);

    base.pos = base_pos + defOff;
    if (defOff != 0 && 0 != tt_cmap14_char_map_def_binary(base, charcode))
        return 1;

    base.pos = base_pos + nondefOff;
    if (nondefOff != 0 && tt_cmap14_char_map_nondef_binary(base, charcode) != 0)
        return 0;

    return -1;
}
function tt_cmap14_variants(cmap, memory)
{
    var count = cmap.num_selectors;
    var p = dublicate_pointer(cmap.cmap.data);
    p.pos += 10;

    if (0 != tt_cmap14_ensure(cmap, (count + 1), memory))
        return null;

    var result = cmap.results;
    var i = 0;
    for (; i < count; i++)
    {
        result[i] = FT_NEXT_UOFF3(p);
        p.pos += 8;
    }
    result[i] = 0;

    p.data = result;
    p.pos = 0;
    return p;
}
function tt_cmap14_char_variants(cmap, memory, charCode)
{
    var count = cmap.num_selectors;
    var p = dublicate_pointer(cmap.cmap.data);
    var base_pos = p.pos;
    p.pos += 10;
    
    if (0 != tt_cmap14_ensure(cmap, (count + 1), memory))
        return null;

    var p1 = dublicate_pointer(p);
    var p2 = dublicate_pointer(p);

    var results = cmap.results;
    var q = 0;
    for (; count > 0; --count)
    {
        var varSel    = FT_NEXT_UOFF3(p);
        var defOff    = FT_NEXT_ULONG(p);
        var nondefOff = FT_NEXT_ULONG(p);

        p1.pos = base_pos + defOff;
        p2.pos = base_pos + nondefOff;
        if ((defOff != 0 && 0 != tt_cmap14_char_map_def_binary(p1, charCode)) ||
           (nondefOff != 0 && 0 != tt_cmap14_char_map_nondef_binary(p2, charCode)))
        {
            results[q] = varSel;
            q++;
        }
    }
    results[q] = 0;
    p.data = results;
    p.pos = 0;
    return p;
}
function tt_cmap14_def_char_count(_p)
{
    var p = dublicate_pointer(_p);
    var numRanges = FT_NEXT_ULONG(p);
    var tot = 0;
    p.pos += 3;
    for (; numRanges > 0; numRanges--)
    {
        tot += 1 + p.data[p.pos];
        p.pos += 4;
    }
    return tot;
}
function tt_cmap14_get_def_chars(cmap, _p, memory)
{
    var p = dublicate_pointer(_p);
    var cnt = tt_cmap14_def_char_count(p);
    var numRanges = FT_NEXT_ULONG(p);

    if (0 != tt_cmap14_ensure(cmap, (cnt + 1), memory))
        return null;

    var results = cmap.results;
    var q = 0;
    for (; numRanges > 0; --numRanges)
    {
        var uni = FT_NEXT_UOFF3(p);
        cnt = FT_NEXT_BYTE(p) + 1;
        do
        {
            results[q] = uni;
            uni  += 1;
            q    += 1;
        } while (--cnt != 0);
    }
    results[q] = 0;
    p.data = results;
    p.pos = 0;
    return p;
}
function tt_cmap14_get_nondef_chars(cmap, _p, memory)
{
    var p = dublicate_pointer(_p);
    var numMappings = FT_NEXT_ULONG(p);

    if (0 != tt_cmap14_ensure(cmap, (numMappings + 1), memory))
        return null;

    var ret = cmap.results;
    var i = 0;
    for (; i < numMappings; i++)
    {
        ret[i] = FT_NEXT_UOFF3(p);
        p.pos += 2;
    }
    ret[i] = 0;
    p.data = ret;
    p.pos = 0;
    return p;
}
function tt_cmap14_variant_chars(cmap, memory, variantSelector)
{
    var base = dublicate_pointer(cmap.cmap.data);
    var base_pos = base.pos;
    base.pos += 6;
    var p = tt_cmap14_find_variant(base, variantSelector);
    if (null == p)
        return null;

    var defOff = FT_NEXT_ULONG(p);
    var nondefOff = FT_NEXT_ULONG(p);

    if (defOff == 0 && nondefOff == 0)
        return null;

    base.pos = base_pos + nondefOff;
    if (defOff == 0)
        return tt_cmap14_get_nondef_chars(cmap, base, memory);
    base.pos = base_pos + defOff;
    if (nondefOff == 0)
        return tt_cmap14_get_def_chars(cmap, base, memory);

    var numRanges;
    var numMappings;
    var duni;
    var dcnt;
    var nuni;
    var dp;
    var di, ni, k;

    var _cmap_data = cmap.cmap.data;
    p = dublicate_pointer(_cmap_data);
    p.pos += nondefOff;
    dp = dublicate_pointer(_cmap_data);
    dp.pos += defOff;

    numMappings = FT_NEXT_ULONG(p);
    dcnt        = tt_cmap14_def_char_count(dp);
    numRanges   = FT_NEXT_ULONG(dp);

    if (numMappings == 0)
    {
        var __pp = dublicate_pointer(_cmap_data);
        __pp += defOff;
        return tt_cmap14_get_def_chars(cmap, _p, memory);
    }
    if (dcnt == 0)
    {
        var __pp = dublicate_pointer(_cmap_data);
        __pp += nondefOff;
        return tt_cmap14_get_nondef_chars(cmap, __p, memory);
    }

    if (0 != tt_cmap14_ensure(cmap, (dcnt + numMappings + 1), memory))
        return null;

    var ret = cmap.results;
    duni = FT_NEXT_UOFF3(dp);
    dcnt = FT_NEXT_BYTE(dp);
    di = 1;
    nuni = FT_NEXT_UOFF3(p);
    p.pos += 2;
    ni = 1;
    i = 0;

    for ( ;; )
    {
        if (nuni > duni + dcnt)
        {
            for (k = 0; k <= dcnt; k++)
                ret[i++] = duni + k;

            ++di;

            if (di > numRanges)
                break;

            duni = FT_NEXT_UOFF3(dp);
            dcnt = FT_NEXT_BYTE(dp);
        }
        else
        {
            if (nuni < duni)
                ret[i++] = nuni;
            ++ni;
            if (ni > numMappings)
                break;

            nuni = FT_NEXT_UOFF3(p);
            p.pos += 2;
        }
    }

    if (ni <= numMappings)
    {
        ret[i++] = nuni;
        while (ni < numMappings)
        {
            ret[i++] = FT_NEXT_UOFF3(p);
            p.pos += 2;
            ++ni;
        }
    }
    else if (di <= numRanges)
    {
        for (k = 0; k <= dcnt; k++)
            ret[i++] = duni + k;

        while (di < numRanges)
        {
            duni = FT_NEXT_UOFF3(dp);
            dcnt = FT_NEXT_BYTE(dp);

            for (k = 0; k <= dcnt; k++)
                ret[i++] = duni + k;
            ++di;
        }
    }
    ret[i] = 0;
    p.data = ret;
    p.pos = 0;
    return p;
}
function tt_cmap14_class_rec()
{
    this.clazz = create_cmap_class_rec(0,tt_cmap14_init,tt_cmap14_done,tt_cmap14_char_index,tt_cmap14_char_next,
        tt_cmap14_char_var_index,tt_cmap14_char_var_isdefault,tt_cmap14_variants,
        tt_cmap14_char_variants,tt_cmap14_variant_chars);
    this.format = 14;
    this.validate = function(table, valid)
    {
        var p = dublicate_pointer(table);
        var defp = dublicate_pointer(table);
        var base = p.pos;
        p.pos += 2;
        var length        = FT_NEXT_ULONG(p);
        var num_selectors = FT_NEXT_ULONG(p);

        if (length > (valid.limit - base) || length < 10 + 11 * num_selectors)
            return 8;

        var n, lastVarSel = 1;
        for ( n = 0; n < num_selectors; n++ )
        {
            var varSel = FT_NEXT_UOFF3(p);
            var defOff = FT_NEXT_ULONG(p);
            var nondefOff = FT_NEXT_ULONG(p);

            if (defOff >= length || nondefOff >= length)
                return 8;

            if (varSel < lastVarSel)
                return 8;

            lastVarSel = varSel + 1;
            if (defOff != 0)
            {
                defp.pos = base + defOff;
                var numRanges = FT_NEXT_ULONG(defp);
                var lastBase  = 0;

                if (defp.pos + numRanges * 4 > valid.limit)
                    return 8;

                for (var i = 0; i < numRanges; ++i)
                {
                    var _base = FT_NEXT_UOFF3(defp);
                    var cnt  = FT_NEXT_BYTE(defp);

                    if (_base + cnt >= 0x110000)
                        return 8;

                    if (_base < lastBase)
                        return 8;

                    lastBase = _base + cnt + 1;
                }
            }

            if (nondefOff != 0)
            {
                defp.pos = base + nondefOff;
                var numMappings = FT_NEXT_ULONG(defp);
                var lastUni = 0;

                if (numMappings * 4 > (valid.limit - defp.pos))
                    return 8;

                for (var i = 0; i < numMappings; ++i)
                {
                    var uni = FT_NEXT_UOFF3(defp);
                    var gid = FT_NEXT_USHORT(defp);

                    if ( uni >= 0x110000)
                        return 8;
                    if (uni < lastUni)
                        return 8;
                    lastUni = uni + 1;
                    if (valid.level >= 1 && gid >= valid.num_glyphs)
                        return 16;
                }
            }
        }

        return 0;
    }
    this.get_cmap_info = function(cmap, cmap_info)
    {
        cmap_info.format = 14;
        cmap_info.language = 0xFFFFFFFF;
        return 0;
    }
}
// finally
function tt_face_build_cmaps(face)
{
    var table = dublicate_pointer(face.cmap_table);

    if (null == table)
        return 8;

    var limit = table.pos + face.cmap_size;
    var p = dublicate_pointer(table);

    if (p.data == null || p.pos + 4 > limit)
        return 8;

    if (FT_NEXT_USHORT(p) != 0)
        return 8;

    var num_cmaps = FT_NEXT_USHORT(p);
    //#ifdef FT_MAX_CHARMAP_CACHEABLE
    if (num_cmaps > 15)
    {
    }
    //#endif

    var pclazz = FT_TT_CMAP_CLASSES_GET;
    var pclazz_len = pclazz.length;
    var clazz;
    for (; num_cmaps > 0 && p.pos + 8 <= limit; num_cmaps--)
    {
        var charmap = new FT_CharMapRec();
        charmap.platform_id = FT_NEXT_USHORT(p);
        charmap.encoding_id = FT_NEXT_USHORT(p);
        charmap.face        = face;
        charmap.encoding    = 0;
        var offset          = FT_NEXT_ULONG(p);

        if (offset && offset <= face.cmap_size - 2)
        {
            var cmap = dublicate_pointer(table);
            cmap.pos += offset;
            var format = FT_PEEK_USHORT(cmap);
            for (var i = 0; i < pclazz_len; i++)
            {
                clazz = pclazz[i];
                if (clazz.format == format)
                {
                    var valid = new TT_Validator();
                    valid.base = cmap;
                    valid.limit = limit;
                    valid.level = 0;
                    valid.error = 0;
                    valid.num_glyphs = face.max_profile.numGlyphs;

                    var error = clazz.validate(cmap, valid);
                    if (error == 0)
                    {
                        var ttcmap = FT_CMap_New(clazz, cmap, charmap);
                        if (null != ttcmap)
                        {
                            ttcmap.flags = 0;
                        }
                    }
                    break;
                }
            }
        }
    }
    return 0;
}
function tt_get_cmap_info(cmap, cmap_info)
{
    var clazz = __FT_CMapRec(cmap).clazz;
    return clazz.get_cmap_info(cmap, cmap_info);
}
/******************************************************************************/
// driver
/******************************************************************************/
function tt_face_load_sfnt_header_stub(face,stream,face_index,header)
{
    return 7;
}
function tt_face_load_directory_stub(face,stream,header)
{
    return 7;
}
function tt_face_load_hdmx_stub(face,stream)
{
    return 7;
}
function tt_face_free_hdmx_stub(face)
{
}
function tt_face_set_sbit_strike_stub(face,x_ppem,y_ppem)
{
    var req = new FT_Size_RequestRec();
    req.type = 0;
    req.width = x_ppem;
    req.height = y_ppem;
    req.horiResolution = 0;
    req.vertResolution = 0;

    var strikeindex = 0x7FFFFFFF;
    return tt_face_set_sbit_strike(face, req, strikeindex);
}

function tt_face_load_sbit_stub(face,stream)
{
    return 7;
}
function tt_face_free_sbit_stub(face)
{
}

function tt_face_load_charmap_stub(face,cmap,input)
{
    return 7;
}
function tt_face_free_charmap_stub(face,cmap)
{
    return 0;
}

function get_sfnt_table(face, tag)
{
    var table = null;
    switch ( tag )
    {
    case 0:
        table = face.header;
        break;
    case 3:
        table = face.horizontal;
        break;
    case 4:
        table = (face.vertical_info == 1) ? face.vertical : null;
        break;
    case 2:
        table = (face.os2.version == 0xFFFF) ? null : face.os2;
        break;
    case 5:
        table = face.postscript;
        break;
    case 1:
        table = face.max_profile;
        break;
    case 6:
        table = (face.pclt.Version != 0) ? face.pclt : null;
        break;
    default:
        break;
    }
    return table;
}
function sfnt_table_info(face, idx, tag)
{
    if (tag != null)
        return {tag:0,offset:0,length:face.num_tables};

    if (idx >= face.num_tables)
        return null;
    var t = face.dir_tables[idx];
    return {tag:t.Tag,offset:t.Offset,length:t.Length};
}
function ft_mem_strcpyn(dst, src, size)
{
    var len = src.length;
    var ret = 1;
    if (len > size)
    {
        len = size;
        ret = 0;
    }
    var i=0;
    while (len > 1)
    {
        dst[dst.pos + i] = src.charCodeAt(i);
        i++;
        size--;
    }
    dst[dst.pos + i] = 0;
    return ret;
}
function sfnt_get_glyph_name(face, glyph_index, buffer, buffer_max)
{
    var gname = tt_face_get_ps_name(face, glyph_index);
    var error = FT_Error;
    if (error == 0)
        ft_mem_strcpyn(buffer, gname, buffer_max);
    return error;
}
function sfnt_get_name_index(face, glyph_name)
{
    var max_gid = 0xFFFFFFFF;
    if (face.num_glyphs < 0)
        return 0;
    else if (face.num_glyphs < 0xFFFFFFFF)
        max_gid = face.num_glyphs;
    for (var i = 0; i < max_gid; i++)
    {
        var gname = tt_face_get_ps_name(face, i);
        var error = FT_Error;
        if (error != 0)
            continue;

        if (glyph_name == gname)
            return i;
    }
    return 0;
}
function sfnt_get_ps_name(face)
{
    var n, found_win, found_apple;
    var result = "";

    if (face.postscript_name != "")
        return face.postscript_name;

    found_win   = -1;
    found_apple = -1;

    var num_faces = face.num_faces;
    for (n = 0; n < num_faces; n++)
    {
        var name = face.name_table.names[n];
        if (name.nameID == 6 && name.stringLength > 0)
        {
            if (name.platformID == 3 && name.encodingID == 1 && name.languageID == 0x409)
                found_win = n;

            if (name.platformID == 1 && name.encodingID == 0 && name.languageID == 0)
                found_apple = n;
        }
    }

    if (found_win != -1)
    {
        var stream = face.name_table.stream;
        var name = face.name_table.names[found_win];
        var len = parseInt(name.stringLength / 2);

        var error = stream.Seek(name.stringOffset);
        if (error == 0)
            error = stream.EnterFrame(name.stringLength);
        if (error != 0)
        {
            name.stringOffset = 0;
            name.stringLength = 0;
            face.postscript_name = "";
            return "";
        }
        var d = stream.data;
        var cur = stream.cur;
        for (; len > 0; len--, cur += 2)
        {
            if (d[cur] == 0 && d[cur+1] >= 32 && d[cur+1] < 128)
                result += String.fromCharCode(d[cur+1]);
        }
        stream.ExitFrame();
        face.postscript_name = result;
        return result;
    }

    if (found_apple != -1)
    {
        var stream = face.name_table.stream;
        var name = face.name_table.names[found_apple];
        var len = name.stringLength;

        var error = stream.Seek(name.stringOffset);
        if (error == 0)
        {
            result = stream.ReadString1(name.stringLength);
            error = FT_Error;
        }
        if (error != 0)
        {
            name.stringOffset = 0;
            name.stringLength = 0;
            face.postscript_name = "";
            return "";
        }

        face.postscript_name = result;
        return result;
    }

    face.postscript_name = result;
    return result;
}

var sfnt_service_glyph_dict = new FT_Service_GlyphDictRec(sfnt_get_glyph_name,sfnt_get_name_index);
var sfnt_service_ps_name = new FT_Service_PsFontNameRec(sfnt_get_ps_name);
var tt_service_get_cmap_info = new FT_Service_TTCMapsRec(tt_get_cmap_info);

var tt_cmap_classes = new Array(9);
tt_cmap_classes[0] = new tt_cmap0_class_rec();
tt_cmap_classes[1] = new tt_cmap2_class_rec();
tt_cmap_classes[2] = new tt_cmap4_class_rec();
tt_cmap_classes[3] = new tt_cmap6_class_rec();
tt_cmap_classes[4] = new tt_cmap8_class_rec();
tt_cmap_classes[5] = new tt_cmap10_class_rec();
tt_cmap_classes[6] = new tt_cmap12_class_rec();
tt_cmap_classes[7] = new tt_cmap13_class_rec();
tt_cmap_classes[8] = new tt_cmap14_class_rec();

var sfnt_service_sfnt_table = new FT_Service_SFNT_TableRec(tt_face_load_any,get_sfnt_table,sfnt_table_info);
var sfnt_service_bdf = new FT_Service_BDFRec(sfnt_get_charset_id,tt_face_find_bdf_prop);

var sfnt_services = new Array(5);
sfnt_services[0] = new FT_ServiceDescRec(FT_SERVICE_ID_SFNT_TABLE,sfnt_service_sfnt_table);
sfnt_services[1] = new FT_ServiceDescRec(FT_SERVICE_ID_POSTSCRIPT_FONT_NAME,sfnt_service_ps_name);
sfnt_services[2] = new FT_ServiceDescRec(FT_SERVICE_ID_GLYPH_DICT,sfnt_service_glyph_dict);
sfnt_services[3] = new FT_ServiceDescRec(FT_SERVICE_ID_BDF,sfnt_service_bdf);
sfnt_services[4] = new FT_ServiceDescRec(FT_SERVICE_ID_TT_CMAP,tt_service_get_cmap_info);

function tt_face_get_name(face, nameid)
{
    FT_Error = 0;
    var found_apple         = -1;
    var found_apple_roman   = -1;
    var found_apple_english = -1;
    var found_win           = -1;
    var found_unicode       = -1;

    var is_english = false;
    var convert = null;
    var rec = null;
    var num_names = face.num_names;
    var n = 0;
    for (; n < face.num_names; n++)
    {
        rec = face.name_table.names[n];
        if (rec.nameID == nameid && rec.stringLength > 0)
        {
            switch (rec.platformID)
            {
            case 0:
            case 2:
                found_unicode = n;
                break;

            case 1:
                if ( rec.languageID == 0)
                    found_apple_english = n;
                else if (rec.encodingID == 0)
                    found_apple_roman = n;
                break;

            case 3:
                if (found_win == -1 || (rec.languageID & 0x3FF) == 0x009)
                {
                    switch ( rec.encodingID )
                    {
                    case 0:
                    case 1:
                    case 10:
                        is_english = ((rec.languageID & 0x3FF) == 0x009) ? true : false;
                        found_win  = n;
                        break;

                    default:
                        break;
                    }
                }
                break;
            default:
                break;
            }
        }
    }

    found_apple = found_apple_roman;
    if (found_apple_english >= 0)
        found_apple = found_apple_english;

    if (found_win >= 0 && !(found_apple >= 0 && false === is_english))
    {
        rec = face.name_table.names[found_win];
        switch (rec.encodingID)
        {
        case 1:
        case 0:
            convert = tt_name_entry_ascii_from_utf16;
            break;

        case 10:
            convert = tt_name_entry_ascii_from_utf16;
            break;

        default:
            break;
        }
    }
    else if (found_apple >= 0)
    {
        rec     = face.name_table.names[found_apple];
        convert = tt_name_entry_ascii_from_other;
    }
    else if (found_unicode >= 0)
    {
        rec     = face.name_table.names[found_unicode];
        convert = tt_name_entry_ascii_from_utf16;
    }

    if (rec && convert)
    {
        if (rec.string == null)
        {
            var stream = face.name_table.stream;
            FT_Error = stream.Seek(rec.stringOffset);
            if (FT_Error == 0)
            {
                rec.string = stream.ReadArray(rec.stringLength);
            }
            if (FT_Error != 0 || null == rec.string)
                return "";
        }
        return convert(rec);
    }
    return "";
}
/* convert an Apple Roman or symbol name entry to ASCII */
function tt_name_entry_ascii_from_utf16(entry)
{
    var string = "";
    var p = entry.string;
    var len = parseInt(entry.stringLength / 2);
    if (len > p.length)
        len = p.length;

    var code = 0;
    for (var n = 0; n < len; n++)
    {
        code = p[2*n] << 8 | p[2*n+1];
        if (code < 32 || code > 127)
            code = 63;

        string += String.fromCharCode(code);
    }
    return string;
}

/* convert an Apple Roman or symbol name entry to ASCII */
function tt_name_entry_ascii_from_other(entry)
{
    var string = "";
    var p = entry.string;
    var len = entry.stringLength;
    if (len > p.length)
        len = p.length;

    var code = 0;
    for (var n = 0; n < len; n++)
    {
        code = p[n];
        if ( code < 32 || code > 127 )
            code = 63;

        string += String.fromCharCode(code);
    }
    return string;
}

function sfnt_init_face(stream, face, face_index)
{
    var error = 0;
    var library = face.driver.library;

    var sfnt = face.sfnt;
    if (null == sfnt)
    {
        sfnt = library.FT_Get_Module_Interface("sfnt");
        if (null == sfnt)
            return 3;

        face.sfnt = sfnt;
        face.goto_table = sfnt.goto_table;
    }

    face.psnames = FT_FACE_FIND_GLOBAL_SERVICE(face, "postscript-cmaps");

    error = sfnt_open_font(stream, face);
    if (error != 0)
        return error;

    if (face_index < 0)
        face_index = 0;

    if (face_index >= face.ttc_header.count)
        return 6;

    error = stream.Seek(face.ttc_header.offsets[face_index])
    if (error != 0)
        return error;

    /* check that we have a valid TrueType file */
    error = sfnt.load_font_dir(face, stream);
    if (error != 0)
        return error;

    face.num_faces = face.ttc_header.count;
    face.face_index = face_index;

    return error;
}
function sfnt_load_face(stream, face, face_index)
{
    var error = 0;
    var psnames_error = 0;

    var has_outline = false;
    var is_apple_sbit = false;
    var ignore_preferred_family = false;
    var ignore_preferred_subfamily = false;

    var sfnt = face.sfnt;

    if (face.internal.incremental_interface != null)
        has_outline = true;
    else if (null != tt_face_lookup_table(face, 1735162214))
        has_outline = true;
    else if (null != tt_face_lookup_table(face, 1128678944))
        has_outline = true;

    if (false === has_outline && sfnt.load_bhed != null)
    {
        error = sfnt.load_bhed(face, stream);
        is_apple_sbit = (error == 0) ? false : true;
    }

    if (false === is_apple_sbit)
    {
        error = sfnt.load_head(face, stream);
        if (error != 0)
            return error;
    }

    if (0 == face.header.Units_Per_EM)
        return 8;

    error = sfnt.load_maxp(face, stream);
    error = sfnt.load_cmap(face, stream);

    error = sfnt.load_name(face, stream);
    error = sfnt.load_post(face, stream);

    psnames_error = error;

    if (false === is_apple_sbit)
    {
        error = sfnt.load_hhea(face, stream, 0);
        if (error == 0)
        {
            error = sfnt.load_hmtx(face, stream, 0);
            if (error == 142)
            {
                error = 147;

                //#ifdef FT_CONFIG_OPTION_INCREMENTAL
                if (face.internal.incremental_interface &&
                    face.internal.incremental_interface.funcs.get_glyph_metrics)
                {
                    face.horizontal.number_Of_HMetrics = 0;
                    error = 0;
                }
                //#endif
            }
        }
        else if (error == 142)
        {
            if (face.format_tag == 1953658213)
            {
                has_outline = false;
                error       = 0;
            }
            else
            {
                error = 143;

                //#ifdef FT_CONFIG_OPTION_INCREMENTAL
                if (face.internal.incremental_interface &&
                    face.internal.incremental_interface.funcs.get_glyph_metrics)
                {
                    face.horizontal.number_Of_HMetrics = 0;
                    error = 0;
                }
                //#endif
            }
        }

        if (error != 0)
            return error;

        error = sfnt.load_hhea(face, stream, 1);
        if (error == 0)
        {
            error = sfnt.load_hmtx(face, stream, 1);
            if (error == 0)
                face.vertical_info = 1;
        }

        if (error != 0 && error != 142)
            return error;

        error = sfnt.load_os2(face, stream);
        if (error != 0)
        {
            /* we treat the table as missing if there are any errors */
            face.os2.version = 0xFFFF;
        }
    }

    if (sfnt.load_eblc)
    {
        error = sfnt.load_eblc(face, stream);
        if (error != 0)
        {
            if (error == 142)
                error = 0;
            else
                return error;
        }
    }

    error = sfnt.load_pclt(face, stream);
    if (error != 0)
    {
        if (error != 142)
            return error;

        face.pclt.Version = 0;
    }

    error = sfnt.load_gasp(face, stream);
    error = sfnt.load_kern(face, stream);

    face.num_glyphs = face.max_profile.numGlyphs;

    face.family_name = "";
    face.style_name = "";

    if (face.os2.version != 0xFFFF && (face.os2.fsSelection & 256) != 0)
    {
        /*
        if (false ==  ignore_preferred_family)
        {
            face.family_name = tt_face_get_name(face, 16);
            error = FT_Error;
            if (error != 0)
                return error;
        }
        */
        if ("" == face.family_name)
        {
            face.family_name = tt_face_get_name(face, 1);
            error = FT_Error;
            if (error != 0)
                return error;
        }

        /*
        if (false == ignore_preferred_subfamily)
        {
            face.style_name = tt_face_get_name(face, 17);
            error = FT_Error;
            if (error != 0)
                return error;
        }
        */
        if ("" == face.style_name)
        {
            face.style_name = tt_face_get_name(face, 2);
            error = FT_Error;
            if (error != 0)
                return error;
        }
    }
    else
    {
        face.family_name = tt_face_get_name(face, 21);
        error = FT_Error;
        if (error != 0)
            return error;

        /*
        if (face.family_name == "" && false == ignore_preferred_family)
        {
            face.family_name = tt_face_get_name(face, 16);
            error = FT_Error;
            if (error != 0)
                return error;
        }
        */
        if (face.family_name == "")
        {
            face.family_name = tt_face_get_name(face, 1);
            error = FT_Error;
            if (error != 0)
                return error;
        }

        face.style_name = tt_face_get_name(face, 22);
        error = FT_Error;
        if (error != 0)
            return error;

        /*
        if (face.style_name == "" && false == ignore_preferred_subfamily)
        {
            face.style_name = tt_face_get_name(face, 17);
            error = FT_Error;
            if (error != 0)
                return error;
        }
        */
        if (face.style_name == "")
        {
            face.style_name = tt_face_get_name(face, 2);
            error = FT_Error;
            if (error != 0)
                return error;
        }
    }

    // face_flags
    var flags = face.face_flags;
    if (has_outline === true)
        flags |= 1;

    flags |= (8 | 16);

    //#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
    if (psnames_error == 0 && face.postscript.FormatType != 0x00030000)
        flags |= 512;
    //#endif

    /* fixed width font? */
    if (face.postscript.isFixedPitch)
        flags |= 4;

    /* vertical information? */
    if (face.vertical_info)
        flags |= 32;

    /* kerning available ? */
    if (face.kern_avail_bits != 0)
        flags |= 64;

    //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    /* Don't bother to load the tables unless somebody asks for them. */
    /* No need to do work which will (probably) not be used.          */
    if (null != tt_face_lookup_table(face, 1735162214) &&
        null != tt_face_lookup_table(face, 1719034226) &&
        null != tt_face_lookup_table(face, 1735811442))
        flags |= 256;
    //#endif

    face.face_flags = flags;

    // Compute style flags.
    flags = 0;
    if (has_outline === true && face.os2.version != 0xFFFF)
    {
        if ((face.os2.fsSelection & 512) != 0)
            flags |= 1;
        else if ((face.os2.fsSelection & 1) != 0)
            flags |= 1;

        if ((face.os2.fsSelection & 32) != 0)
            flags |= 2;
    }
    else
    {
        if ((face.header.Mac_Style & 1) != 0)
            flags |= 2;

        if ((face.header.Mac_Style & 2) != 0)
            flags |= 1;
    }

    face.style_flags = flags;

    // TODO: ---
    tt_face_build_cmaps(face);

    for (var m = 0; m < face.num_charmaps; m++)
    {
        var charmap = __FT_CharmapRec(face.charmaps[m]);
        charmap.encoding = 0;
        charmap.encoding = sfnt_find_encoding(charmap.platform_id, charmap.encoding_id);
    }

    //#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    var count = face.num_sbit_strikes;

    if (count > 0)
    {
        var em_size = face.header.Units_Per_EM;
        var avgwidth = face.os2.xAvgCharWidth;
        var metrics = new FT_Size_Metrics();

        if (em_size == 0 || face.os2.version == 0xFFFF)
        {
            avgwidth = 0;
            em_size = 1;
        }

        face.available_sizes = new Array(count);

        for (var i = 0; i < count; i++)
        {
            face.available_sizes[i] = new FT_Bitmap_Size();
            var bsize = face.available_sizes[i];

            error = sfnt.load_strike_metrics(face, i, metrics);
            if (error != 0)
                return error;

            bsize.height = (metrics.height >>> 6) & 0xFFFF;
            bsize.width = parseInt((avgwidth * metrics.x_ppem + parseInt(em_size / 2)) / em_size) & 0xFFFF;

            bsize.x_ppem = metrics.x_ppem << 6;
            bsize.y_ppem = metrics.y_ppem << 6;

            /* assume 72dpi */
            bsize.size   = metrics.y_ppem << 6;
        }

        face.face_flags |= 2;
        face.num_fixed_sizes = count;
    }

    //#endif TT_CONFIG_OPTION_EMBEDDED_BITMAPS

    /* a font with no bitmaps and no outlines is scalable; */
    /* it has only empty glyphs then                       */
    if (((face.face_flags & 2) == 0) &&
        ((face.face_flags & 1) == 0))
        face.face_flags |= 1;

    // Set up metrics
    if ((face.face_flags & 1) != 0)
    {
        face.bbox.xMin    = face.header.xMin;
        face.bbox.yMin    = face.header.yMin;
        face.bbox.xMax    = face.header.xMax;
        face.bbox.yMax    = face.header.yMax;
        face.units_per_EM = face.header.Units_Per_EM;

        face.ascender  = face.horizontal.Ascender;
        face.descender = face.horizontal.Descender;

        face.height = (face.ascender - face.descender + face.horizontal.Line_Gap);

        if (face.ascender == 0 && face.descender == 0)
        {
            if (face.os2.version != 0xFFFF)
            {
                if (face.os2.sTypoAscender || face.os2.sTypoDescender)
                {
                    face.ascender  = face.os2.sTypoAscender;
                    face.descender = face.os2.sTypoDescender;

                    face.height = (face.ascender - face.descender + face.os2.sTypoLineGap);
                }
                else
                {
                    face.ascender  = face.os2.usWinAscent;
                    face.descender = -face.os2.usWinDescent;

                    face.height = (face.ascender - face.descender);
                }
            }
        }

        face.max_advance_width  = face.horizontal.advance_Width_Max;
        face.max_advance_height = ((face.vertical_info != 0) ? face.vertical.advance_Height_Max : face.height);

        face.underline_position  = face.postscript.underlinePosition - parseInt(face.postscript.underlineThickness / 2);
        face.underline_thickness = face.postscript.underlineThickness;
    }
    return error;
}
function sfnt_done_face()
{
}

function sfnt_find_encoding(platform_id, encoding_id)
{
    switch (platform_id)
    {
        case 2:
        {
            return 1970170211;
        }
        case 0:
        {
            return 1970170211;
        }
        case 1:
        {
            if (0 == encoding_id)
                return 1634889070;
            break;
        }
        case 3:
        {
            if (0 == encoding_id)
                return 1937337698;
            if (10 == encoding_id)
                return 1970170211;
            if (1 == encoding_id)
                return 1970170211;
            if (2 == encoding_id)
                return 1936353651;
            if (3 == encoding_id)
                return 1734484000;
            if (4 == encoding_id)
                return 1651074869;
            if (5 == encoding_id)
                return 2002873971;
            if (6 == encoding_id)
                return 1785686113;
            break;
        }
        default:
            break;
    }
    return 0;
}

function sfnt_open_font(stream, face)
{
    var error = 0;

    face.ttc_header.tag     = 0;
    face.ttc_header.version = 0;
    face.ttc_header.count   = 0;

    var offset = stream.pos;
    var tag = stream.ReadULong();
    error = FT_Error;
    FT_Error = 0;

    if (error != 0)
      return error;

    if (tag != 0x00010000 && tag != 1953784678 && tag != 1330926671 &&
         tag != 1953658213 && tag != 1954115633 && tag != 0x00020000)
        return 2;

    face.ttc_header.tag = 1953784678;

    if (tag == 1953784678)
    {
        error = stream.EnterFrame(8);
        if (error != 0)
            return error;

        face.ttc_header.version = stream.GetLong();
        face.ttc_header.count = stream.GetLong();

        stream.ExitFrame();

        if (face.ttc_header.count == 0)
            return 8;

        var ul = FT_Common.IntToUInt(face.ttc_header.count);
        if (ul > (stream.size / (28 + 4)))
            return 10;

        face.ttc_header.offsets = new Array(ul);
        error = stream.EnterFrame(4*ul);

        if (error != 0)
            return error;

        for (var n = 0; n < ul; n++)
            face.ttc_header.offsets[n] = stream.GetULong();

        stream.ExitFrame();
    }
    else
    {
        face.ttc_header.version = 1 << 16;
        face.ttc_header.count = 1;

        face.ttc_header.offsets = new Array(1);
        face.ttc_header.offsets[0] = offset;
    }

    return error;
}

function _sfnt_interface()
{
    this.goto_table = tt_face_goto_table;

    this.init_face = sfnt_init_face;
    this.load_face = sfnt_load_face;
    this.done_face = sfnt_done_face;
    this.get_interface = sfnt_get_interface;
    
    this.load_any = tt_face_load_any;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.load_sfnt_header = tt_face_load_sfnt_header_stub;
    this.load_directory = tt_face_load_directory_stub;
    //#endif

    this.load_head = tt_face_load_head;
    this.load_hhea = tt_face_load_hhea;
    this.load_cmap = tt_face_load_cmap;
    this.load_maxp = tt_face_load_maxp;
    this.load_os2 = tt_face_load_os2;
    this.load_post = tt_face_load_post;

    this.load_name = tt_face_load_name;
    this.free_name = tt_face_free_name;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.load_hdmx_stub = tt_face_load_hdmx_stub;
    this.free_hdmx_stub = tt_face_free_hdmx_stub;
    //#endif

    this.load_kern = tt_face_load_kern;

    this.load_gasp = tt_face_load_gasp;
    this.load_pclt = tt_face_load_pclt;

    this.load_bhed = tt_face_load_bhed;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.set_sbit_strike_stub = tt_face_set_sbit_strike_stub;
    this.load_sbits_stub = tt_face_load_sbit_stub;

    this.find_sbit_image = tt_find_sbit_image;
    this.load_sbit_metrics = tt_load_sbit_metrics;
    //#endif

    this.load_sbit_image = tt_face_load_sbit_image;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.free_sbits_stub = tt_face_free_sbit_stub;
    //#endif

    this.get_psname = tt_face_get_ps_name;
    this.free_psnames = tt_face_free_ps_names;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.load_charmap_stub = tt_face_load_charmap_stub;
    this.free_charmap_stub = tt_face_free_charmap_stub;
    //#endif

    this.get_kerning = tt_face_get_kerning;

    this.load_font_dir = tt_face_load_font_dir;
    this.load_hmtx = tt_face_load_hmtx;

    this.load_eblc = tt_face_load_eblc;
    this.free_eblc = tt_face_free_eblc;

    this.set_sbit_strike = tt_face_set_sbit_strike;
    this.load_strike_metrics = tt_face_load_strike_metrics;

    this.get_metrics = tt_face_get_metrics;
}
var sfnt_interface = new _sfnt_interface();

/******************************************************************************/
// pic
/******************************************************************************/
var FT_SFNT_SERVICES_GET            = sfnt_services;
var FT_SFNT_SERVICE_GLYPH_DICT_GET  = sfnt_service_glyph_dict;
var FT_SFNT_SERVICE_PS_NAME_GET     = sfnt_service_ps_name;
var FT_TT_SERVICE_GET_CMAP_INFO_GET = tt_service_get_cmap_info;
var FT_TT_CMAP_CLASSES_GET          = tt_cmap_classes;
var FT_SFNT_SERVICE_SFNT_TABLE_GET  = sfnt_service_sfnt_table;
var FT_SFNT_SERVICE_BDF_GET         = sfnt_service_bdf;
var FT_SFNT_INTERFACE_GET           = sfnt_interface;

function sfnt_get_interface(module,name)
{
    return ft_service_list_lookup(FT_SFNT_SERVICES_GET, name);
}

function create_sfnt_module(library)
{
    var sfnt_mod = new FT_Module();
    sfnt_mod.clazz = new FT_Module_Class();

    var clazz = sfnt_mod.clazz;
    clazz.flags = 0;
    clazz.name = "sfnt";
    clazz.version = 0x10000;
    clazz.requires = 0x20000;

    clazz.module_interface = FT_SFNT_INTERFACE_GET;
    clazz.init = null;
    clazz.done = null;

    clazz.get_interface = sfnt_get_interface;

    sfnt_mod.library = library;
    sfnt_mod.memory = library.Memory;
    sfnt_mod.generic = null;

    return sfnt_mod;
}


function CRasterMemory()
{
    this.width = 0;
    this.height = 0;
    this.pitch = 0;

    this.m_oBuffer = null;
    this.CheckSize = function(w, h)
    {
        if (this.width < (w + 1) || this.height < (h + 1))
        {
            this.width = Math.max(this.width, w + 1);
            this.pitch = 4 * this.width;
            this.height = Math.max(this.height, h + 1);

            this.m_oBuffer = null;
            this.m_oBuffer = g_memory.ctx.createImageData(this.width, this.height);
        }
    }
}
var raster_memory = new CRasterMemory();

// outline ---
function _FT_Outline_Funcs_Gray()
{
    this.move_to = function(to, worker)
    {
        /* record current cell, if any */
        var err = gray_record_cell(worker);
        if (err == -100)
            return -100;

        /* start to a new position */
        var x = to.x << 2;
        var y = to.y << 2;

        err = gray_start_cell(worker, x >> 8, y >> 8);

        worker.x = x;
        worker.y = y;
        return err;
    }

    this.line_to = function(to, worker)
    {
        return gray_render_line(worker, to.x << 2, to.y << 2);
    }

    this.conic_to = function(control, to, worker)
    {
        return gray_render_conic(worker, control, to);
    }

    this.cubic_to = function(control1, control2, to, worker)
    {
        return gray_render_cubic(worker, control1, control2, to);
    }

    this.shift = 0;
    this.delta = 0;
}
var ft_outline_funcs_gray = new _FT_Outline_Funcs_Gray();

function FT_Outline_Decompose(outline, func_interface, user)
{
    if (!outline || !func_interface)
        return 6;

    var v_last      = new FT_Vector();
    var v_control   = new FT_Vector();
    var v_start     = new FT_Vector();

    var point;
    var limit;

    var tags = 0;
    var first = 0;
    var tag = 0;

    var shift = func_interface.shift;
    var delta = func_interface.delta;

    var error = 0;

    var count = outline.n_contours;
    var _c = outline.contours;
    var _p = outline.points;
    var _t = outline.tags;
    for (var n = 0; n < count; n++)
    {
        var last = _c[n];
        if (last < 0)
            return 20;
        limit = last;

        v_start.x = (_p[first].x << shift) - delta;
        v_start.y = (_p[first].y << shift) - delta;

        v_last.x = (_p[last].x << shift) - delta;
        v_last.y = (_p[last].y << shift) - delta;

        v_control.x = v_start.x;
        v_control.y = v_start.y;

        point = first;
        tags  = first;
        tag   = _t[tags] & 3;

        /* A contour cannot start with a cubic control point! */
        if (tag == 2)
            return 20;

        /* check first point to determine origin */
        if (tag == 0)
        {
            /* first point is conic control.  Yes, this happens. */
            if ((_t[last] & 3) == 1)
            {
                /* start at last point if it is on the curve */
                v_start.x = v_last.x;
                v_start.y = v_last.y;
                limit--;
            }
            else
            {
                /* if both first and last points are conic,         */
                /* start at their middle and record its position    */
                /* for closure                                      */
                v_start.x = parseInt((v_start.x + v_last.x) / 2);
                v_start.y = parseInt((v_start.y + v_last.y) / 2);

                v_last.x = v_start.x;
                v_last.y = v_start.y;
            }
            point--;
            tags--;
        }

        error = func_interface.move_to(v_start, user);
        if (error != 0)
            return error;

        var isClose = 0;
        while (point < limit)
        {
            point++;
            tags++;

            tag = _t[tags] & 3;
            switch ( tag )
            {
                case 1:  /* emit a single line_to */
                {
                    var vec = new FT_Vector();

                    vec.x = (_p[point].x << shift) - delta;
                    vec.y = (_p[point].y << shift) - delta;

                    error = func_interface.line_to(vec, user);
                    if (0 != error)
                        return error;
                    continue;
                }

                case 0:  /* consume conic arcs */
                    v_control.x = (_p[point].x << shift) - delta;
                    v_control.y = (_p[point].y << shift) - delta;

                    var isCont = 0;
                    while (true)
                    {
                        if (point < limit)
                        {
                            var vec = new FT_Vector();
                            var v_middle = new FT_Vector();

                            point++;
                            tags++;
                            tag = _t[tags] & 3;

                            vec.x = (_p[point].x << shift) - delta;
                            vec.y = (_p[point].y << shift) - delta;

                            if (tag == 1)
                            {
                                error = func_interface.conic_to(v_control, vec, user);
                                if (0 != error)
                                    return error;
                                isCont = 1;
                                break;
                            }

                            if (tag != 0)
                                return 20;

                            v_middle.x = parseInt((v_control.x + vec.x) / 2);
                            v_middle.y = parseInt((v_control.y + vec.y) / 2);

                            error = func_interface.conic_to(v_control, v_middle, user);
                            if (0 != error)
                                return error;

                            v_control.x = vec.x;
                            v_control.y = vec.y;

                            continue;
                        }
                        break;
                    }
                    if (1 == isCont/* && (point < limit)*/)
                        continue;

                    error = func_interface.conic_to(v_control, v_start, user);
                    isClose = 1;
                    break;

                default:  /* FT_CURVE_TAG_CUBIC */
                {
                    var vec1 = new FT_Vector();
                    var vec2 = new FT_Vector();

                    if ((point + 1 > limit) || (_t[tags+1]&3) != 2)
                        return 20;

                    point += 2;
                    tags  += 2;

                    vec1.x = (_p[point-2].x << shift) - delta;
                    vec1.y = (_p[point-2].y << shift) - delta;

                    vec2.x = (_p[point-1].x << shift) - delta;
                    vec2.y = (_p[point-1].y << shift) - delta;

                    if (point <= limit)
                    {
                        var vec = new FT_Vector();
                        vec.x = (_p[point].x << shift) - delta;
                        vec.y = (_p[point].y << shift) - delta;

                        error = func_interface.cubic_to(vec1, vec2, vec, user);
                        if (0 != error)
                            return error;
                        continue;
                    }

                    error = func_interface.cubic_to(vec1, vec2, v_start, user);
                    isClose = 1;
                    break;
                }
            }

            if (isClose == 1)
                break;
        }

        if (error != 0)
            return error;

        /* close the contour with a line segment */
        if (0 == isClose)
            error = func_interface.line_to(v_start, user);

        if (error != 0)
            return error;

        isClose = 0;
        first = last + 1;
    }
    return 0;
}

function FT_Outline_Get_Orientation(outline)
{
    if (outline == null || outline.n_points <= 0)
        return 0;

    /* We use the nonzero winding rule to find the orientation.       */
    /* Since glyph outlines behave much more `regular' than arbitrary */
    /* cubic or quadratic curves, this test deals with the polygon    */
    /* only which is spanned up by the control points.                */
    var points = outline.points;

	var v_cur;
    var first = 0;
    var area = 0;
    for (var c = 0; c < outline.n_contours; c++)
    {
        var last = outline.contours[c];
        var v_prev = points[last];

        for (var n = first; n <= last; n++)
        {
            v_cur = points[n];
            area += (v_cur.y - v_prev.y) * (v_cur.x + v_prev.x);
            v_prev = v_cur;
        }

        first = last + 1;
    }

    if (area > 0)
        return 1;
    else if ( area < 0 )
        return 0;
    return 2;
}

function FT_Outline_Get_Orientation_Cur(outline, base)
{
    if (outline == null || outline.n_points <= 0)
        return 0;

    /* We use the nonzero winding rule to find the orientation.       */
    /* Since glyph outlines behave much more `regular' than arbitrary */
    /* cubic or quadratic curves, this test deals with the polygon    */
    /* only which is spanned up by the control points.                */
    var points = base.points;
    var contours = base.contours;
    var _p_off = outline.points;
    var _c_off = outline.contours;

    var v_prev = new FT_Vector();
    var v_cur = new FT_Vector();

    var first = 0;
    var area = 0;
    for (var c = 0; c < outline.n_contours; c++)
    {
        var last = contours[_c_off + c];
        v_prev.x = points[_p_off + last].x;
        v_prev.y = points[_p_off + last].y;

        for (var n = first; n <= last; n++)
        {
            v_cur.x = points[_p_off + n].x;
            v_cur.y = points[_p_off + n].y;
            area += (v_cur.y - v_prev.y) * (v_cur.x + v_prev.x);
            v_prev.x = v_cur.x;
            v_prev.y = v_cur.y;
        }

        first = last + 1;
    }

    if (area > 0)
        return 1;
    else if ( area < 0 )
        return 0;
    return 2;
}

function ft_trig_prenorm(vec)
{
    var x = vec.x;
    var y = vec.y;

    var z = ((x >= 0) ? x : - x) | ((y >= 0) ? y : -y);
    var shift = 0;

    /* determine msb bit index in `shift' */
    if (z >= (1 << 16))
    {
        z >>= 16;
        shift += 16;
    }
    if (z >= (1 << 8))
    {
        z >>= 8;
        shift += 8;
    }
    if (z >= (1 << 4))
    {
        z >>= 4;
        shift += 4;
    }
    if (z >= (1 << 2))
    {
        z >>= 2;
        shift += 2;
    }
    if (z >= (1 << 1))
    {
        z >>= 1;
        shift += 1;
    }

    if (shift <= 29)
    {
        shift  = 29 - shift;
        vec.x = x << shift;
        vec.y = y << shift;
    }
    else
    {
        shift -= 29;
        vec.x = x >> shift;
        vec.y = y >> shift;
        shift = -shift;
    }

    return shift;
}

var ft_trig_arctan_table = [1740967, 919879, 466945, 234379, 117304, 58666, 29335, 14668, 7334,
    3667, 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1, 0];

function ft_trig_pseudo_polarize(vec)
{
    var x = vec.x;
    var y = vec.y;

    var theta = 0;
    var xtemp = 0;

    /* Get the vector into [-PI/4,PI/4] sector */
    if ( y > x )
    {
        if ( y > -x )
        {
            theta =  5898240;
            xtemp =  y;
            y     = -x;
            x     =  xtemp;
        }
        else
        {
            theta =  y > 0 ? 11796480 : -11796480;
            x     = -x;
            y     = -y;
        }
    }
    else
    {
        if ( y < -x )
        {
            theta = -5898240;
            xtemp = -y;
            y     =  x;
            x     =  xtemp;
        }
        else
        {
            theta = 0;
        }
    }

    var arctanptr = 0;
    var i, b;

    /* Pseudorotations, with right shifts */
    for ( i = 1, b = 1; i < 23; b <<= 1, i++ )
    {
        if ( y > 0 )
        {
            xtemp  = x + ( ( y + b ) >> i );
            y      = y - ( ( x + b ) >> i );
            x      = xtemp;
            theta += ft_trig_arctan_table[arctanptr++];
        }
        else
        {
            xtemp  = x - ( ( y + b ) >> i );
            y      = y + ( ( x + b ) >> i );
            x      = xtemp;
            theta -= ft_trig_arctan_table[arctanptr++];
        }
    }

    /* round theta */
    if ( theta >= 0 )
        theta = FT_PAD_ROUND( theta, 32 );
    else
        theta = -FT_PAD_ROUND( -theta, 32 );

    vec.x = x;
    vec.y = theta;
}

function ft_trig_downscale(val)
{
    var s   = val;
    val = (val >= 0) ? val : -val;

    var v1 = val >>> 16;
    var v2 = FT_Common.Short_To_UShort(val & 0xFFFF);

    var k1 =  0xDBD9;   /* constant */
    var k2 = 0x5B16;   /* constant */

    var hi   = k1 * v1;
    var lo1  = k1 * v2 + k2 * v1;       /* can't overflow */

    var lo2  = ( k2 * v2 ) >>> 16;

    var lo3  = ( lo1 >= lo2 ) ? lo1 : lo2;
    lo1 += lo2;

    hi  += (lo1 >>> 16);
    if (lo1 < lo3)
        hi += 0x10000;

    var val  = FT_Common.UintToInt(hi);

    return (s >= 0) ? val : -val;
}

function FT_Vector_Length(vec)
{
    var v = new FT_Vector();
    v.x = vec.x;
    v.y = vec.y;

    /* handle trivial cases */
    if (v.x == 0)
    {
        return (v.y >= 0) ? v.y : -v.y;
    }
    else if (v.y == 0)
    {
        return (v.x >= 0) ? v.x : -v.x;
    }

    /* general case */
    var shift = ft_trig_prenorm(v);
    ft_trig_pseudo_polarize(v);

    v.x = ft_trig_downscale(v.x);

    if (shift > 0)
        return (v.x + (1 << (shift - 1))) >> shift;
    return v.x << -shift;
}

function FT_Outline_EmboldenXY(outline, xstrength, ystrength)
{
    if (outline == null)
        return 6;

    xstrength /= 2;
    ystrength /= 2;

    if (xstrength == 0 && ystrength == 0)
        return 0;

    var orientation = FT_Outline_Get_Orientation(outline);
    if (orientation == 2)
    {
        if (outline.n_contours != 0)
            return 6;
        else
            return 0;
    }

    var points = outline.points;
    var contours = outline.contours;

    var v_first = new FT_Vector();
    var v_prev  = new FT_Vector();
    var v_cur   = new FT_Vector();
    var v_next  = new FT_Vector();

    var first = 0;
    var last = 0;
    for (var c = 0; c < outline.n_contours; c++)
    {
        var _in = new FT_Vector();
        var out = new FT_Vector();
        var shift = new FT_Vector();

        last = contours[c];

        v_first.x = points[first].x;
        v_first.y = points[first].y;

        v_prev.x = points[last].x;
        v_prev.y = points[last].y;

        v_cur.x = v_first.x;
        v_cur.y = v_first.y;

        /* compute the incoming vector and its length */
        _in.x = v_cur.x - v_prev.x;
        _in.y = v_cur.y - v_prev.y;
        var l_in = FT_Vector_Length(_in);

        for (var n = first; n <= last; n++)
        {
            if (n < last)
                v_next = points[n + 1];
            else
                v_next = v_first;

            /* compute the outgoing vector and its length */
            out.x = v_next.x - v_cur.x;
            out.y = v_next.y - v_cur.y;
            var l_out = FT_Vector_Length(out);

            var d = l_in * l_out + _in.x * out.x + _in.y * out.y;

            /* shift only if turn is less then ~160 degrees */
            if (16 * d > l_in * l_out)
            {
                /* shift components are aligned along bisector        */
                /* and directed according to the outline orientation. */
                shift.x = l_out * _in.y + l_in * out.y;
                shift.y = l_out * _in.x + l_in * out.x;

                if (orientation == 0)
                    shift.x = -shift.x;
                else
                    shift.y = -shift.y;

                /* threshold strength to better handle collapsing segments */
                var l = Math.min(l_in, l_out);
                var q = out.x * _in.y - out.y * _in.x;
                if (orientation == 0)
                    q = -q;

                if (FT_MulDiv(xstrength, q, l) < d)
                    shift.x = FT_MulDiv(shift.x, xstrength, d);
                else
                    shift.x = FT_MulDiv(shift.x, l, q);

                if (FT_MulDiv(ystrength, q, l) < d)
                    shift.y = FT_MulDiv(shift.y, ystrength, d);
                else
                    shift.y = FT_MulDiv(shift.y, l, q);
            }
            else
                shift.x = shift.y = 0;

            points[n].x = v_cur.x + xstrength + shift.x;
            points[n].y = v_cur.y + ystrength + shift.y;

            _in    = out;
            l_in  = l_out;
            v_cur.x = v_next.x;
            v_cur.y = v_next.y;
        }

        first = last + 1;
    }

    return 0;
}

function FT_Outline_EmboldenXY_cur(outline, base, xstrength, ystrength)
{
    if (outline == null)
        return 6;

    xstrength /= 2;
    ystrength /= 2;

    if (xstrength == 0 && ystrength == 0)
        return 0;

    var orientation = FT_Outline_Get_Orientation_Cur(outline, base);
    if (orientation == 2)
    {
        if (outline.n_contours != 0)
            return 6;
        else
            return 0;
    }

    var points = base.points;
    var contours = base.contours;

    var _p_off = outline.points;
    var _c_off = outline.contours;

    var v_first = new FT_Vector();
    var v_prev  = new FT_Vector();
    var v_cur   = new FT_Vector();
    var v_next  = new FT_Vector();

    var first = 0;
    var last = 0;
    for (var c = 0; c < outline.n_contours; c++)
    {
        var _in = new FT_Vector();
        var out = new FT_Vector();
        var shift = new FT_Vector();

        last = contours[_c_off + c];

        v_first.x = points[_p_off + first].x;
        v_first.y = points[_p_off + first].y;

        v_prev.x = points[_p_off + last].x;
        v_prev.y = points[_p_off + last].y;

        v_cur.x = v_first.x;
        v_cur.y = v_first.y;

        /* compute the incoming vector and its length */
        _in.x = v_cur.x - v_prev.x;
        _in.y = v_cur.y - v_prev.y;
        var l_in = FT_Vector_Length(_in);

        for (var n = first; n <= last; n++)
        {
            if (n < last)
            {
                v_next.x = points[_p_off + n + 1].x;
                v_next.y = points[_p_off + n + 1].y;
            }
            else
            {
                v_next.x = v_first.x;
                v_next.y = v_first.y;
            }

            /* compute the outgoing vector and its length */
            out.x = v_next.x - v_cur.x;
            out.y = v_next.y - v_cur.y;
            var l_out = FT_Vector_Length(out);

            var d = l_in * l_out + _in.x * out.x + _in.y * out.y;

            /* shift only if turn is less then ~160 degrees */
            if (16 * d > l_in * l_out)
            {
                /* shift components are aligned along bisector        */
                /* and directed according to the outline orientation. */
                shift.x = l_out * _in.y + l_in * out.y;
                shift.y = l_out * _in.x + l_in * out.x;

                if (orientation == 0)
                    shift.x = -shift.x;
                else
                    shift.y = -shift.y;

                /* threshold strength to better handle collapsing segments */
                var l = Math.min(l_in, l_out);
                var q = out.x * _in.y - out.y * _in.x;
                if (orientation == 0)
                    q = -q;

                if (FT_MulDiv(xstrength, q, l) < d)
                    shift.x = FT_MulDiv(shift.x, xstrength, d);
                else
                    shift.x = FT_MulDiv(shift.x, l, q);

                if (FT_MulDiv(ystrength, q, l) < d)
                    shift.y = FT_MulDiv(shift.y, ystrength, d);
                else
                    shift.y = FT_MulDiv(shift.y, l, q);
            }
            else
                shift.x = shift.y = 0;

            points[_p_off + n].x = v_cur.x + xstrength + shift.x;
            points[_p_off + n].y = v_cur.y + ystrength + shift.y;

            _in    = out;
            l_in  = l_out;
            v_cur.x = v_next.x;
            v_cur.y = v_next.y;
        }

        first = last + 1;
    }

    return 0;
}


function FT_Outline_Embolden(outline, strength)
{
    return FT_Outline_EmboldenXY(outline, strength, strength);
}
//------------

function FT_Outline_Transform(outline, matrix)
{
    if ( !outline || !matrix )
        return;

    var vec = outline.points;
    var len = outline.n_points;

    for (var i=0; i < len; i++)
        FT_Vector_Transform(vec[i], matrix);
}

function FT_Vector_Transform(vector, matrix)
{
    if ( !vector || !matrix )
        return;

    var xz = FT_MulFix(vector.x, matrix.xx) + FT_MulFix(vector.y, matrix.xy);
    var yz = FT_MulFix(vector.x, matrix.yx) + FT_MulFix(vector.y, matrix.yy);

    vector.x = xz;
    vector.y = yz;
}

function FT_Outline_Translate(outline, xOffset, yOffset)
{
    if ( !outline )
        return;

    var vec = outline.points;
    var c = outline.n_points;

    for (var n=0;n<c;n++)
    {
        vec[n].x += xOffset;
        vec[n].y += yOffset;
    }
}

function FT_Outline_Get_CBox(outline, acbox)
{
    var xMin, yMin, xMax, yMax;

    if (outline != null && acbox != null)
    {
        if (outline.n_points == 0)
        {
            xMin = 0;
            yMin = 0;
            xMax = 0;
            yMax = 0;
        }
        else
        {
            var p = outline.points;
            var vec = p[0];
            var count = outline.n_points;

            xMin = xMax = vec.x;
            yMin = yMax = vec.y;

            for (var i = 1; i < count; i++)
            {
                var x = p[i].x;
                if ( x < xMin ) xMin = x;
                if ( x > xMax ) xMax = x;

                var y = p[i].y;
                if ( y < yMin ) yMin = y;
                if ( y > yMax ) yMax = y;
            }
        }
        acbox.xMin = xMin;
        acbox.xMax = xMax;
        acbox.yMin = yMin;
        acbox.yMax = yMax;
    }
}

function FT_Glyph_Class()
{
    this.glyph_size = 0;
    this.glyph_format = 0;
    this.glyph_init = null;
    this.glyph_done = null;
    this.glyph_copy = null;
    this.glyph_transform = null;
    this.glyph_bbox = null;
    this.glyph_prepare = null;
}

// для быстроты работы не пользуется эта структура.
// все ради всех браузеров, кроме ie9. в них типизированные массивы
// быстрее обычных (а тут скорость доступа - самое главное)
function TCell()
{
    this.x      = 0;
    this.cover  = 0;
    this.area   = 0;
    this.next   = 0;
}

function TRaster()
{
    this.worker = new TWorker();

    this.memory = null;

    this.cell_x     = CreateIntArray(1024);
    this.cell_cover = CreateIntArray(1024);
    this.cell_area  = CreateIntArray(1024);
    this.cell_next  = CreateIntArray(1024);

    this.buffer_size    = 0;
    this.band_size      = 0;
}

// - smooth -----------------------------------------------------------
// ------------ rasterizer --------------------------------------------
// пул памяти для растеризатора делаем Array int'ов. Почему не массив структур?
// 1) не выделять память под саму структуру (хотя кто знает как работает js)
// 2) самое главное - это возможность замены на типизированный массив!!!
// сам растеризатор ничего не знает о происхождении массива. За создание
// его отвечает функция CreateIntArray
function CreateIntArray(size)
{
    var arr = null;
    if (typeof(Int32Array) != 'undefined' && !window.opera)
        arr = new Int32Array(size);
    else
        arr = new Array(size);
    for (var i=0;i<size;i++)
        arr[i] = 0;
    return arr;
}
function CreateUIntArray(size)
{
    var arr = null;
    if (typeof(Uint32Array) != 'undefined' && !window.opera)
        arr = new Uint32Array(size);
    else
        arr = new Array(size);
    for (var i=0;i<size;i++)
        arr[i] = 0;
    return arr;
}
function CreateCharArray(size)
{
    var arr = null;
    if (typeof(Int8Array) != 'undefined' && !window.opera)
        arr = new Int8Array(size);
    else
        arr = new Array(size);
    for (var i=0;i<size;i++)
        arr[i] = 0;
    return arr;
}
function CreateNullArray(size)
{
    var arr = new Array(size);
    for (var i=0;i<size;i++)
        arr[i] = null;
    return arr;
}
function TWorker()
{
    this.ex = 0;
    this.ey = 0;

    this.min_ex = 0;
    this.max_ex = 0;
    this.min_ey = 0;
    this.max_ey = 0;

    this.count_ex = 0;
    this.count_ey = 0;

    this.area = 0;
    this.cover = 0;
    this.invalid = 0;

    this.max_cells = 0;
    this.num_cells = 0;

    this.cx = 0;
    this.cy = 0;
    this.x = 0;
    this.y = 0;

    this.last_ey = 0;

    this.bez_stack_x = CreateIntArray(97);
    this.bez_stack_y = CreateIntArray(97);
    this.lev_stack = CreateIntArray(32);

    this.outline = null;
    this.target = null;
    this.clip_box = new FT_BBox();

    this.gray_spans = new Array(32);
    for (var i=0;i<32;i++)
        this.gray_spans[i] = new FT_Span();

    this.num_gray_spans = 0;

    this.render_span = null;
    
    this.render_span_data = null;
    this.span_y = 0;

    this.band_size = 0;
    this.band_shoot = 0;

    this.cell_x     = null;
    this.cell_cover = null;
    this.cell_area  = null;
    this.cell_next  = null;
    this.buffer_size = 1024;

    this.ycells = CreateIntArray(this.buffer_size);
    this.ycount = 0;

    this.raster_memory = raster_memory;
}

function gray_init_cells(worker, raster)
{
    worker.cell_x     = raster.cell_x;
    worker.cell_cover = raster.cell_cover;
    worker.cell_area  = raster.cell_area;
    worker.cell_next  = raster.cell_next;
    worker.buffer_size = 1024;

    worker.max_cells   = 0;
    worker.num_cells   = 0;
    worker.area        = 0;
    worker.cover       = 0;
    worker.invalid     = 1;
}

function gray_compute_cbox(worker)
{
    var outline = worker.outline;
    var p = outline.points;
    var c = outline.n_points;

    if ( c <= 0 )
    {
        worker.min_ex = worker.max_ex = 0;
        worker.min_ey = worker.max_ey = 0;
        return;
    }

    worker.min_ex = worker.max_ex = p[0].x;
    worker.min_ey = worker.max_ey = p[0].y;

    for (var i = 1; i < c; i++)
    {
        var x = p[i].x;
        var y = p[i].y;

        if ( x < worker.min_ex ) worker.min_ex = x;
        if ( x > worker.max_ex ) worker.max_ex = x;
        if ( y < worker.min_ey ) worker.min_ey = y;
        if ( y > worker.max_ey ) worker.max_ey = y;
    }

    /* truncate the bounding box to integer pixels */
    worker.min_ex = worker.min_ex >> 6;
    worker.min_ey = worker.min_ey >> 6;
    worker.max_ex = ( worker.max_ex + 63 ) >> 6;
    worker.max_ey = ( worker.max_ey + 63 ) >> 6;
}

function TBand()
{
    this.min = 0;
    this.max = 0;
}

function gray_find_cell(ras)
{
    var x = ras.ex;

    if ( x > ras.count_ex )
        x = ras.count_ex;

    var pcell = ras.ey;
    var cell = ras.ycells[pcell];

    var bis_y = 1;
    for (;;)
    {
        if (cell == -1 || ras.cell_x[cell] > x)
            break;

        if (ras.cell_x[cell] == x)
            return cell;

        bis_y = 0;
        pcell = cell;
        cell = ras.cell_next[cell];
    }

    if (ras.num_cells >= ras.max_cells)
        return -1;

    var oldcell = cell;

    cell = ras.num_cells++;
    ras.cell_x[cell] = x;
    ras.cell_area[cell] = 0;
    ras.cell_cover[cell] = 0;
    ras.cell_next[cell] = oldcell;

    if (1 == bis_y)
        ras.ycells[pcell] = cell;
    else
        ras.cell_next[pcell] = cell;
    return cell;
}

function gray_record_cell(ras)
{
    if (0 == ras.invalid && ((ras.area | ras.cover) != 0))
    {
        var cell = gray_find_cell(ras);
        if (-1 == cell)
            return -100;

        ras.cell_area[cell] += ras.area;
        ras.cell_cover[cell] += ras.cover;
    }
    return 0;
}

function gray_set_cell(ras, ex, ey)
{
    ey -= ras.min_ey;

    if ( ex > ras.max_ex )
        ex = ras.max_ex;

    ex -= ras.min_ex;
    if ( ex < 0 )
        ex = -1;

    /* are we moving to a different cell ? */
    if ( ex != ras.ex || ey != ras.ey )
    {
        /* record the current one if it is valid */
        if (0 == ras.invalid)
        {
            var err = gray_record_cell(ras);
            if (err == -100)
                return -100;
        }

        ras.area  = 0;
        ras.cover = 0;
    }

    ras.ex      = ex;
    ras.ey      = ey;

    var _ey = ey >= 0 ? ey : ey + 4294967296;
    var _cy = ras.count_ey >= 0 ? ras.count_ey : ras.count_ey + 4294967296;

    ras.invalid = (_ey >= _cy || ex >= ras.count_ex) ? 1 : 0;
    return 0;
}

function gray_start_cell(ras, ex, ey)
{
    if (ex > ras.max_ex)
        ex = ras.max_ex;

    if (ex < ras.min_ex)
        ex = (ras.min_ex - 1);

    ras.area    = 0;
    ras.cover   = 0;
    ras.ex      = ex - ras.min_ex;
    ras.ey      = ey - ras.min_ey;
    ras.last_ey = ey << 8;
    ras.invalid = 0;

    return gray_set_cell(ras, ex, ey);
}

function gray_render_line(ras, to_x, to_y)
{
    var ey1, ey2, fy1, fy2, mod;
    var dx, dy, x, x2;
    var p, first;
    var delta, rem, lift, incr;

    ey1 = (ras.last_ey >> 8);
    ey2 = (to_y >> 8);     /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
    fy1 = (ras.y - ras.last_ey);
    fy2 = (to_y - (ey2 << 8));

    dx = to_x - ras.x;
    dy = to_y - ras.y;

    var min = ey1;
    var max = ey2;

    if (ey1 > ey2)
    {
        min = ey2;
        max = ey1;
    }
    if (min >= ras.max_ey || max < ras.min_ey)
    {
        ras.x       = to_x;
        ras.y       = to_y;
        ras.last_ey = ey2 << 8;
        return 0;
    }

    var err = 0;
    /* everything is on a single scanline */
    if (ey1 == ey2)
    {
        err = gray_render_scanline(ras, ey1, ras.x, fy1, to_x, fy2);
        if (err == -100)
            return -100;
        ras.x       = to_x;
        ras.y       = to_y;
        ras.last_ey = ey2 << 8;
        return 0;
    }

    /* vertical line - avoid calling gray_render_scanline */
    incr = 1;

    if (dx == 0)
    {
        var ex = ras.x >> 8;
        var two_fx = ((ras.x - (ex << 8)) << 1);
        var area;

        first = 256;
        if (dy < 0)
        {
            first = 0;
            incr  = -1;
        }

        delta      = (first - fy1);
        ras.area  += (two_fx * delta);
        ras.cover += delta;
        ey1       += incr;

        err = gray_set_cell(ras, ex, ey1);
        if (err == -100)
            return -100;

        delta = (first + first - 256);
        area  = two_fx * delta;
        while (ey1 != ey2)
        {
            ras.area  += area;
            ras.cover += delta;
            ey1       += incr;

            err = gray_set_cell(ras, ex, ey1);
            if (err == -100)
                return -100;
        }

        delta      = (fy2 - 256 + first);
        ras.area  += (two_fx * delta);
        ras.cover += delta;

        ras.x       = to_x;
        ras.y       = to_y;
        ras.last_ey = ey2 << 8;
        return 0;
    }

    /* ok, we have to render several scanlines */
    p     = (256 - fy1) * dx;
    first = 256;
    incr  = 1;

    if (dy < 0)
    {
        p     = fy1 * dx;
        first = 0;
        incr  = -1;
        dy    = -dy;
    }

    delta = parseInt(p / dy);
    mod   = (p % dy);
    if (mod < 0)
    {
        delta--;
        mod += dy;
    }

    x = ras.x + delta;
    err = gray_render_scanline(ras, ey1, ras.x, fy1, x, first);
    if (err == -100)
        return -100;

    ey1 += incr;
    err = gray_set_cell(ras, x >> 8, ey1);
    if (err == -100)
        return -100;

    if (ey1 != ey2)
    {
        p     = 256 * dx;
        lift  = parseInt(p / dy);
        rem   = (p % dy);
        if (rem < 0)
        {
            lift--;
            rem += dy;
        }
        mod -= dy;

        while (ey1 != ey2)
        {
            delta = lift;
            mod  += rem;
            if (mod >= 0)
            {
                mod -= dy;
                delta++;
            }

            x2 = x + delta;
            err = gray_render_scanline(ras, ey1, x, 256 - first, x2, first);
            if (err == -100)
                return -100;
            x = x2;

            ey1 += incr;
            err = gray_set_cell(ras, x >> 8, ey1);
            if (err == -100)
                return -100;
        }
    }

    err = gray_render_scanline(ras, ey1, x, 256 - first, to_x, fy2);
    if (err == -100)
        return -100;

    ras.x       = to_x;
    ras.y       = to_y;
    ras.last_ey = (ey2 << 8);
    return 0;
}

function gray_split_conic(bx,by,base)
{
    var a,b;
    bx[base+4] = bx[base+2];
    b = bx[base+1];
    a=bx[base+3] = parseInt((bx[base+2]+b)/2);
    b=bx[base+1] = parseInt((bx[base]+b)/2);
    bx[base+2] = parseInt((a+b)/2);

    by[base+4] = by[base+2];
    b = by[base+1];
    a=by[base+3] = parseInt((by[base+2]+b)/2);
    b=by[base+1] = parseInt((by[base]+b)/2);
    by[base+2] = parseInt((a+b)/2);
}

function gray_render_conic(ras, control, to)
{
    var dx, dy;
    var min, max, y;
    var top, level;
    var arc = 0;

    var arcx = ras.bez_stack_x;
    var arcy = ras.bez_stack_y;

    arcx[arc] = (to.x << 2);
    arcy[arc] = (to.y << 2);
    arcx[arc+1] = (control.x << 2);
    arcy[arc+1] = (control.y << 2);
    arcx[arc+2] = ras.x;
    arcy[arc+2] = ras.y;
    top = 0;

    dx = Math.abs(arcx[arc+2] + arcx[arc] - 2*arcx[arc+1]);
    dy = Math.abs(arcy[arc+2] + arcy[arc] - 2*arcy[arc+1]);
    if (dx < dy)
        dx = dy;

    if (dx < 64)
    {
        return gray_render_line(ras, arcx[arc], arcy[arc]);
    }

    /* short-cut the arc that crosses the current band */
    min = max = arcy[arc];

    y = arcy[arc+1];
    if (y < min) min = y;
    if (y > max) max = y;

    y = arcy[arc+2];
    if (y < min) min = y;
    if (y > max) max = y;

    if (((min >> 8) >= ras.max_ey) || ((max >> 8) < ras.min_ey))
    {
        return gray_render_line(ras, arcx[arc], arcy[arc]);
    }

    level = 0;
    do
    {
        dx >>= 2;
        level++;
    } while (dx > 64);

    var levels = ras.lev_stack;
    levels[0] = level;

    var err = 0;
    do
    {
        level = levels[top];
        if (level > 0)
        {
            gray_split_conic(arcx, arcy, arc);
            arc += 2;
            top++;
            levels[top] = levels[top - 1] = level - 1;
            continue;
        }

        err = gray_render_line(ras, arcx[arc], arcy[arc]);
        if (err == -100)
            return -100;
        top--;
        arc -= 2;

    }
    while (top >= 0);
    return 0;
}

function gray_split_cubic(bx,by,base)
{
    var a,b,c,d;
    bx[base+6] = bx[base+3];
    c = bx[base+1];
    d = bx[base+2];
    bx[base+1] = a = parseInt((bx[base]+c)/2);
    bx[base+5] = b = parseInt((bx[base+3]+d)/2);
    c = parseInt((c+d)/2);
    bx[base+2] = a = parseInt((a+c)/2);
    bx[base+4] = b = parseInt((b+c)/2);
    bx[base+3] = parseInt((a+b)/2);

    by[base+6] = by[base+3];
    c = by[base+1];
    d = by[base+2];
    by[base+1] = a = parseInt((by[base]+c)/2);
    by[base+5] = b = parseInt((by[base+3]+d)/2);
    c = parseInt((c+d)/2);
    by[base+2] = a = parseInt((a+c)/2);
    by[base+4] = b = parseInt((b+c)/2);
    by[base+3] = parseInt((a+b)/2);
}

function gray_render_cubic(ras, control1, control2, to)
{
    var min, max, y;

    var arcx = ras.bez_stack_x;
    var arcy = ras.bez_stack_y;
    arcx[0] = to.x << 2;
    arcy[0] = to.y << 2;
    arcx[1] = control2.x << 2;
    arcy[1] = control2.y << 2;
    arcx[2] = control1.x << 2;
    arcy[2] = control1.y << 2;
    arcx[3] = ras.x;
    arcy[3] = ras.y;

    var arc = 0;

    /* Short-cut the arc that crosses the current band. */
    min = max = arcy[0];

    y = arcy[arc+1];
    if (y<min)
        min = y;
    if (y>max)
        max = y;

    y = arcy[arc+2];
    if (y<min)
        min = y;
    if (y>max)
        max = y;

    y = arcy[arc+3];
    if (y<min)
        min = y;
    if (y>max)
        max = y;

    var err = 0;
    if ((min >> 8) >= ras.max_ey || (max >> 8) < ras.min_ey)
    {
        return gray_render_line(ras, arcx[arc], arcy[arc]);
    }

    for (;;)
    {
        var dx, dy, dx_, dy_;
        var dx1, dy1, dx2, dy2;
        var L, s, s_limit;

        dx = arcx[arc+3] - arcx[arc];
        dy = arcy[arc+3] - arcy[arc];

        dx_ = Math.abs(dx);
        dy_ = Math.abs(dy);

        L = ((dx_ > dy_) ? (236 * dx_ + 97 * dy_) : (97 * dx_ + 236 * dy_)) >>> 8;

        if ( L > 32767 )
        {
            gray_split_cubic(arcx,arcy,arc);
            arc += 3;
            continue;
        }

        s_limit = L * 42;

        dx1 = arcx[arc+1] - arcx[arc];
        dy1 = arcy[arc+1] - arcy[arc];
        s = Math.abs(dy*dx1 - dx*dy1);

        if (s>s_limit)
        {
            gray_split_cubic(arcx,arcy,arc);
            arc += 3;
            continue;
        }

        dx2 = arcx[arc+2] - arcx[arc];
        dy2 = arcy[arc+2] - arcy[arc];
        s = Math.abs(dy*dx2 - dx*dy2);

        if (s>s_limit)
        {
            gray_split_cubic(arcx,arcy,arc);
            arc += 3;
            continue;
        }

        if (((dy*dy1 + dx*dx1) < 0) || ((dy*dy2 + dx*dx2) < 0) ||
            ((dy*(arcy[arc+3]-arcy[arc+1]) + dx*(arcx[arc+3]-arcx[arc+1])) < 0) ||
            ((dy*(arcy[arc+3]-arcy[arc+2]) + dx*(arcx[arc+3]-arcx[arc+2])) < 0))
        {
            gray_split_cubic(arcx,arcy,arc);
            arc += 3;
            continue;
        }

        /* No reason to split. */
        err = gray_render_line(ras, arcx[arc], arcy[arc]);
        if (err == -100)
            return -100;

        if (arc == 0)
            return 0;

        arc -= 3;
    }
    return 0;
}

function gray_render_scanline(ras, ey, x1, y1, x2, y2)
{
    var ex1, ex2, fx1, fx2, delta, mod, lift, rem;
    var p, first, dx;
    var incr;

    dx = x2 - x1;

    ex1 = (x1 >> 8);
    ex2 = (x2 >> 8);
    fx1 = x1 - (ex1 << 8);
    fx2 = x2 - (ex2 << 8);

    /* trivial case.  Happens often */
    if (y1 == y2)
    {
        return gray_set_cell(ras, ex2, ey);
    }

    /* everything is located in a single cell.  That is easy! */
    if (ex1 == ex2)
    {
        delta = y2 - y1;
        ras.area  += ((fx1+fx2)*delta);
        ras.cover += delta;
        return 0;
    }

    /* ok, we'll have to render a run of adjacent cells on the same scanline */
    p = (256 - fx1)*(y2-y1);
    first = 256;
    incr  = 1;

    if (dx < 0)
    {
        p     = fx1*(y2-y1);
        first = 0;
        incr  = -1;
        dx    = -dx;
    }

    delta = parseInt(p/dx);
    mod   = p%dx;
    if (mod < 0)
    {
        delta--;
        mod += dx;
    }

    ras.area  += ((fx1+first)*delta);
    ras.cover += delta;

    ex1 += incr;
    var err = gray_set_cell(ras, ex1, ey);
    if (err == -100)
        return err;
    y1  += delta;

    if (ex1 != ex2)
    {
        p    = 256 * (y2 - y1 + delta);
        lift = parseInt(p/dx);
        rem  = (p%dx);
        if (rem < 0)
        {
            lift--;
            rem += dx;
        }

        mod -= dx;

        while (ex1 != ex2)
        {
            delta = lift;
            mod  += rem;
            if (mod >= 0)
            {
                mod -= dx;
                delta++;
            }

            ras.area  += (256*delta);
            ras.cover += delta;
            y1        += delta;
            ex1       += incr;
            err = gray_set_cell(ras, ex1, ey);
            if (err == -100)
                return err;
        }
    }

    delta      = y2 - y1;
    ras.area  += ((fx2 + 256 - first)*delta);
    ras.cover += delta;
    return 0;
}

function _gray_render_span(y, count, spans, span_start, worker)
{
    var map = worker.target;
    var pixels = map.buffer.data;
    var p = -y * map.pitch;
    if (map.pitch >= 0)
        p += ((map.rows-1)*map.pitch);

    var s = span_start;
    for (;count > 0;count--,s++)
    {
        var coverage = spans[s].coverage;

        if (coverage != 0)
        {
            var len = spans[s].len;
            var q = spans[s].x + p;
            for (;len>0;len--)
                pixels[q++] = coverage;
        }
    }
}
function gray_render_span(y, count, spans, span_start, worker)
{
    var map = worker.raster_memory;
    var pixels = map.m_oBuffer.data;
    var p = -y * map.pitch;
    if (worker.target.pitch >= 0)
        p += ((worker.target.rows-1)*map.pitch);

    p+=3;

    var s = span_start;
    for (;count > 0;count--,s++)
    {
        var coverage = spans[s].coverage;

        if (coverage != 0)
        {
            var len = spans[s].len;
            var q = (spans[s].x * 4) + p;
            for (;len>0;len--,q+=4)
                pixels[q] = coverage;
        }
    }
}

function gray_hline(ras, x, y, area, acount)
{
    var spans = ras.gray_spans;
    var span_i = 0;
    var span;

    var count;

    var coverage = (area >> 9);
    /* use range 0..256 */
    if (coverage < 0)
        coverage = -coverage;

    if ((ras.outline.flags & 2) != 0)
    {
        coverage &= 511;
        if (coverage > 256)
            coverage = 512 - coverage;
        else if (coverage == 256)
            coverage = 255;
    }
    else
    {
        if ( coverage >= 256 )
            coverage = 255;
    }

    y += ras.min_ey;
    x += ras.min_ex;

    if (x >= 32767)
        x = 32767;

    /* FT_Span.y is an integer, so limit our coordinates appropriately */
    if (y >= 2147483647)
        y = 2147483647;

    if (coverage != 0)
    {
        /* see whether we can add this span to the current list */
        count = ras.num_gray_spans;
        span_i  = count - 1;
        span = spans[span_i];
        if ((count > 0) && (ras.span_y == y) && ((span.x + span.len) == x) && (span.coverage == coverage))
        {
            span.len = (span.len + acount) & 0xFFFF;
            return;
        }

        if (ras.span_y != y || count >= 32)
        {
            if (ras.render_span && count > 0)
                ras.render_span(ras.span_y,count,ras.gray_spans,0,ras.render_span_data);

            ras.num_gray_spans = 0;
            ras.span_y = y;

            count = 0;
            span_i = 0;
            span = spans[0];
        }
        else
        {
            span_i++;
            span = spans[span_i];
        }

        /* add a gray span to the current list */
        span.x        = x;
        span.len      = acount & 0xFFFF;
        span.coverage = coverage & 0xFF;

        ras.num_gray_spans++;
    }
}

function gray_sweep(ras, target)
{
    if (ras.num_cells == 0)
        return;

    ras.num_gray_spans = 0;

    var _next = ras.cell_next;
    var _cover = ras.cell_cover;
    var _area = ras.cell_area;
    var _x = ras.cell_x;

    for (var yindex = 0; yindex < ras.ycount; yindex++)
    {
        var cell = ras.ycells[yindex];
        var cover = 0;
        var x = 0;

        for (; cell != -1; cell = _next[cell])
        {
            if (_x[cell] > x && cover != 0)
                gray_hline(ras, x, yindex, cover * 512, _x[cell] - x);

            cover += _cover[cell];
            var area = cover * 512 - _area[cell];

            if (area != 0 && _x[cell] >= 0)
                gray_hline(ras, _x[cell], yindex, area, 1);

            x = _x[cell] + 1;
        }

        if ( cover != 0 )
            gray_hline(ras, x, yindex, cover * 512, ras.count_ex - x);
    }

    if (ras.render_span && ras.num_gray_spans > 0)
        ras.render_span(ras.span_y, ras.num_gray_spans, ras.gray_spans, 0, ras.render_span_data);
}

function __grays_raster_render(raster, params)
{
    var outline = params.source;
    var target_map = params.target;

    if (raster == null)
        return 6;

    if (outline == null)
        return 20;

    /* return immediately if the outline is empty */
    if (outline.n_points == 0 || outline.n_contours <= 0)
        return 0;

    if (outline.contours == null || outline.points == null)
        return 20;

    if (outline.n_points != outline.contours[outline.n_contours - 1] + 1)
        return 20;

    var worker = raster.worker;

    /* if direct mode is not set, we must have a target bitmap */
    if (0 == (params.flags & 2))
    {
        if (target_map == null)
            return 6;

        /* nothing to do */
        if (0 == target_map.width || 0 == target_map.rows)
            return 0;

        if (null == target_map.buffer && null == worker.raster_memory)
            return 6;
    }

    /* this version does not support monochrome rendering */
    if (0 == (params.flags & 1))
        return 6;

    /* compute clipping box */
    if (0 == (params.flags & 2))
    {
        /* compute clip box from target pixmap */
        worker.clip_box.xMin = 0;
        worker.clip_box.yMin = 0;
        worker.clip_box.xMax = target_map.width;
        worker.clip_box.yMax = target_map.rows;
    }
    else if (params.flags & 4)
    {
        worker.clip_box.xMin = params.clip_box.xMin;
        worker.clip_box.yMin = params.clip_box.yMin;
        worker.clip_box.xMax = params.clip_box.xMax;
        worker.clip_box.yMax = params.clip_box.yMax;
    }
    else
    {
        worker.clip_box.xMin = -32768;
        worker.clip_box.yMin = -32768;
        worker.clip_box.xMax =  32767;
        worker.clip_box.yMax =  32767;
    }

    gray_init_cells(worker, raster);

    worker.outline        = outline;
    worker.num_cells      = 0;
    worker.invalid        = 1;
    worker.band_size      = raster.band_size;
    worker.num_gray_spans = 0;

    if (0 != (params.flags & 2))
    {
        worker.render_span      = params.gray_spans;
        worker.render_span_data = params.user;
    }
    else
    {
        worker.target           = target_map;
        worker.render_span      = gray_render_span;
        worker.render_span_data = worker;
    }

    if (worker.raster_memory != null)
        worker.raster_memory.CheckSize(target_map.width, target_map.rows);

    return __gray_convert_glyph(worker);
}
function __gray_convert_glyph(ras)
{
    var bands = m_bands;
    var band = 0;
    var n = 0;
    var num_bands = 0;
    var min = 0;
    var max = 0;
    var max_y = 0;
    var clip = null;

    gray_compute_cbox(ras);

    /* clip to target bitmap, exit if nothing to do */
    clip = ras.clip_box;

    if (ras.max_ex <= clip.xMin || ras.min_ex >= clip.xMax ||
        ras.max_ey <= clip.yMin || ras.min_ey >= clip.yMax )
        return 0;

    if (ras.min_ex < clip.xMin) ras.min_ex = clip.xMin;
    if (ras.min_ey < clip.yMin) ras.min_ey = clip.yMin;

    if (ras.max_ex > clip.xMax ) ras.max_ex = clip.xMax;
    if (ras.max_ey > clip.yMax ) ras.max_ey = clip.yMax;

    ras.count_ex = ras.max_ex - ras.min_ex;
    ras.count_ey = ras.max_ey - ras.min_ey;

    /* set up vertical bands */
    num_bands = parseInt((ras.max_ey - ras.min_ey) / ras.band_size);
    if (num_bands == 0)
        num_bands = 1;
    if (num_bands >= 39)
        num_bands = 39;

    ras.band_shoot = 0;

    min   = ras.min_ey;
    max_y = ras.max_ey;

    for (n = 0; n < num_bands; n++, min = max)
    {
        max = min + ras.band_size;
        if ((n == num_bands - 1) || (max > max_y))
            max = max_y;

        bands[0].min = min;
        bands[0].max = max;
        band = 0;

        while (band >= 0)
        {
            var isReduceBands = 0;

            var bottom, top, middle;
            var error = 0;
            var _band = bands[band];

            var cells_max;

            ras.ycount = _band.max - _band.min;
            if (ras.ycount >= ras.buffer_size)
                isReduceBands = 1;

            if (0 == isReduceBands)
            {
                ras.max_cells = ras.buffer_size;
                if (ras.max_cells < 2)
                {
                    // такого быть не может из-за лишнего килобайтика)
                    isReduceBands = 1;
                }

                if (0 == isReduceBands)
                {
                    for (var yindex = 0; yindex < ras.ycount; yindex++)
                        ras.ycells[yindex] = -1;

                    ras.num_cells = 0;
                    ras.invalid   = 1;
                    ras.min_ey    = _band.min;
                    ras.max_ey    = _band.max;
                    ras.count_ey  = _band.max - _band.min;

                    error = __gray_convert_glyph_inner(ras);

                    if (0 == error)
                    {
                        gray_sweep(ras, ras.target);
                        band--;
                        continue;
                    }
                    else if (error != 98)
                        return 1;
                }
            }

            /* render pool overflow; we will reduce the render band by half */
            bottom = _band.min;
            top    = _band.max;
            middle = bottom + ((top - bottom) >> 1);

            /* This is too complex for a single scanline; there must */
            /* be some problems.                                     */
            if (middle == bottom)
                return 1;

            if (bottom-top >= ras.band_size)
                ras.band_shoot++;


            bands[band+1].min = bottom;
            bands[band+1].max = middle;
            bands[band].min = middle;
            bands[band].max = top;

            band++;
        }
    }

    if (ras.band_shoot > 8 && ras.band_size > 16)
        ras.band_size = parseInt(ras.band_size / 2);

    return 0;
}

function __gray_convert_glyph_inner(ras)
{
    var error1 = FT_Outline_Decompose(ras.outline, ft_outline_funcs_gray, ras);

    if (-100 == error1)
        return 98;

    var error2 = gray_record_cell(ras);
    if (-100 == error2)
        return 98;

    return error1;
}

var m_bands = new Array(40);
for (var i = 0; i < 40; i++)
    m_bands[i] = new TBand();

function FT_Grays_Raster()
{
    // --------
    this.glyph_format = 1869968492;

    this.raster_new = function(memory, raster)
    {
        raster.memory = memory;
        return 0;
    }
    this.raster_reset = function(raster, pool_base, pool_size)
    {
        raster.buffer_size    = 1024;
        raster.band_size      = 117; // сделал как в си-шной либе, чтобы было удобно дебажиться.
        // а вообще - нужно так: (мы памяти выделяем больше на 1kb (под указатели ycells))
        //raster.band_size = 128;
    }
    this.raster_set_mode = null;
    this.raster_render = __grays_raster_render;
    this.raster_done = function()
    {
    }
}
// --------------------------------------------------------------------

function FT_Smooth_Renderer_Class()
{
    this.flags = 2;
    this.name = "smooth";
    this.version = 0x10000;
    this.requires = 0x20000;

    this.module_interface = null;

    this.init = ft_smooth_init;
    this.done = null;
    this.get_interface = null;

    this.glyph_format = 1869968492;

    this.render_glyph = ft_smooth_render;
    this.transform_glyph = ft_smooth_transform;
    this.get_glyph_cbox = ft_smooth_get_cbox;
    this.set_mode = ft_smooth_set_mode;

    this.raster_class = new FT_Grays_Raster();
}
function FT_Smooth_Renderer()
{
    this.clazz = new FT_Smooth_Renderer_Class();
    this.library = null;
    this.memory = null;
    this.generic = null;

    this.glyph_format = 0;
    this.glyph_class = new FT_Glyph_Class();
    this.raster = null;

    this.raster_render = null;
    this.render = null;
}

function create_renderer_smooth_module(library)
{
    var ren1_mod = new FT_Smooth_Renderer();
    ren1_mod.clazz = new FT_Smooth_Renderer_Class();
    ren1_mod.clazz.render_glyph = ft_smooth_render;

    ren1_mod.library = library;
    ren1_mod.memory = library.Memory;
    ren1_mod.generic = null;

    return ren1_mod;
}
function create_renderer_smooth_lcd_module(library)
{
    var ren1_mod = new FT_Smooth_Renderer();
    ren1_mod.clazz = new FT_Smooth_Renderer_Class();
    ren1_mod.clazz.render_glyph = ft_smooth_render_lcd;

    ren1_mod.library = library;
    ren1_mod.memory = library.Memory;
    ren1_mod.generic = null;

    return ren1_mod;
}
function create_renderer_smooth_lcd_v_module(library)
{
    var ren1_mod = new FT_Smooth_Renderer();
    ren1_mod.clazz = new FT_Smooth_Renderer_Class();
    ren1_mod.clazz.render_glyph = ft_smooth_render_lcd_v;

    ren1_mod.library = library;
    ren1_mod.memory = library.Memory;
    ren1_mod.generic = null;

    return ren1_mod;
}

function ft_smooth_init(render)
{
    render.clazz.raster_class.raster_reset(render.raster, render.library.raster_pool, render.library.raster_pool_size);
    return 0;
}

function ft_smooth_set_mode(render, mode_tag, data)
{
    return render.clazz.raster_class.raster_set_mode(render.raster, mode_tag, data);
}

function ft_smooth_transform(render, slot, matrix, delta)
{
    if (slot.format != render.glyph_format)
        return 6;

    if (matrix)
        FT_Outline_Transform(slot.outline, matrix);

    if (delta)
        FT_Outline_Translate(slot.outline, delta.x, delta.y);

    return 0;
}

function ft_smooth_get_cbox(render, slot, cbox)
{
    cbox.xMin = 0;
    cbox.yMin = 0;
    cbox.xMax = 0;
    cbox.yMax = 0;

    if (slot.format == render.glyph_format)
        FT_Outline_Get_CBox(slot.outline, cbox);
}

function ft_smooth_render(render, slot, mode, origin)
{
    if (mode == 1)
        mode = 0;

    return ft_smooth_render_generic(render, slot, mode, origin, 0);
}

function ft_smooth_render_lcd(render, slot, mode, origin)
{
    var error = ft_smooth_render_generic(render, slot, mode, origin, 3);
    if (error == 0)
        slot.bitmap.pixel_mode = 5;

    return error;
}

function ft_smooth_render_lcd_v(render, slot, mode, origin)
{
    var error = ft_smooth_render_generic(render, slot, mode, origin, 4);
    if (error == 0)
        slot.bitmap.pixel_mode = 6;

    return error;
}

function ft_smooth_render_generic(render, slot, mode, origin, required_mode)
{
    var error = 0;
    var outline = null;
    var cbox = new FT_BBox();
    var width, height, pitch;

    //#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    var height_org, width_org;
    //#endif

    var hmul = (mode == 3) ? 1 : 0;
    var vmul = (mode == 4) ? 1 : 0;
    var x_shift, y_shift, x_left, y_top;

    var params = new FT_Raster_Params();

    /* check glyph image format */
    if (slot.format != render.glyph_format)
        return 6;

    /* check mode */
    if (mode != required_mode)
        return 19;

    outline = slot.outline;

    /* translate the outline to the new origin if needed */
    if (origin != null)
        FT_Outline_Translate(outline, origin.x, origin.y);

    /* compute the control box, and grid fit it */
    FT_Outline_Get_CBox(outline, cbox);

    cbox.xMin = (cbox.xMin & ~63);
    cbox.yMin = (cbox.yMin & ~63);
    cbox.xMax = ((cbox.xMax+63) & ~63);
    cbox.yMax = ((cbox.yMax+63) & ~63);

    if (cbox.xMin < 0 && cbox.xMax > 2147483647 + cbox.xMin)
    {
        if (outline && origin)
            FT_Outline_Translate(outline, -origin.x, -origin.y);
        return 98;
    }
    else
        width  = ((cbox.xMax - cbox.xMin) >>> 6);

    if ( cbox.yMin < 0 && cbox.yMax > 2147483647 + cbox.yMin )
    {
        if ( outline && origin )
            FT_Outline_Translate( outline, -origin.x, -origin.y );
        return 98;
    }
    else
        height = ((cbox.yMax - cbox.yMin) >>> 6);

    var bitmap = slot.bitmap;
    var memory = render.memory;

    //#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    width_org  = width;
    height_org = height;
    //#endif

    /* release old bitmap buffer */
    if (slot.internal.flags & 1)
    {
        delete bitmap.buffer;
        slot.internal.flags &= ~1;
    }

    /* allocate new one */
    pitch = width;
    if (hmul == 1)
    {
        width = width * 3;
        pitch = (width + 3) & ~3;
    }

    if (vmul == 1)
        height *= 3;

    x_shift = cbox.xMin;
    y_shift = cbox.yMin;
    x_left  = cbox.xMin >> 6;
    y_top   = cbox.yMax >> 6;

    //#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    /*
    if (slot.library.lcd_filter_func)
    {
        var extra = slot.library.lcd_extra;
        if (hmul == 1)
        {
            x_shift -= 64 * (extra >>> 1);
            width   += 3 * extra;
            pitch    = (width + 3) & ~3;
            x_left  -= (extra >>> 1);
        }
        if (vmul == 1)
        {
            y_shift -= 64 * (extra >>> 1);
            height  += 3 * extra;
            y_top   += (extra >>> 1);
        }
    }
    */
    //#endif

    if (width > 0x7FFF || height > 0x7FFF)
    {
        if (outline && origin)
            FT_Outline_Translate(outline, -origin.x, -origin.y);
        return 98;
    }

    bitmap.pixel_mode = 2;
    bitmap.num_grays  = 256;
    bitmap.width      = width;
    bitmap.rows       = height;
    bitmap.pitch      = pitch;

    if (bitmap.width > 1000 || bitmap.height > 1000)
        return 130;

    /* translate outline to render it into the bitmap */
    FT_Outline_Translate(outline, -x_shift, -y_shift);
    var memory = slot.library.Memory;

    //bitmap.buffer = memory.Alloc(pitch * height);

    slot.internal.flags |= 1;

    /* set up parameters */
    params.target = bitmap;
    params.source = outline;
    params.flags  = 1;

    //#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    /*
    var points = outline.points;
    var count = outline.n_points;

    if (hmul == 1)
        for (var i = 0; i < count; i++)
            points[i].x = points[i].x * 3;

    if (vmul == 1)
        for (var i = 0; i < count; i++)
            points[i].y = points[i].y * 3;
    error = render.raster_render(render.raster, params);
    if (hmul == 1)
        for (var i = 0; i < count; i++)
            points[i].x = parseInt(points[i].x / 3);

    if (vmul == 1)
        for (var i = 0; i < count; i++)
            points[i].y = parseInt(points[i].y / 3);

    if (slot.library.lcd_filter_func)
        slot.library.lcd_filter_func(bitmap, mode, slot.library);
    */

    //#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
    error = render.raster_render(render.raster, params);
    if (hmul == 1)
    {
        var pixels = bitmap.buffer.data;
        var line = 0;
        for (var hh = height_org; hh > 0; hh--, line += pitch)
        {
            var end = line + width;
            for (var xx = width_org; xx > 0; xx--)
            {
                var pixel = pixels[line+xx-1];

                pixels[end-3] = pixel;
                pixels[end-2] = pixel;
                pixels[end-1] = pixel;
                end -= 3;
            }
        }
    }
    if (vmul == 1)
    {
        var pixels = bitmap.buffer.data;
        var read = (height - height_org) * pitch;
        var write = 0;
        var ii = 0;
        for (var hh = height_org; hh > 0; hh--)
        {
            for (ii=0;ii<pitch;ii++)
                pixels[write+ii] = pixels[read+ii];

            write += pitch;

            for (ii=0;ii<pitch;ii++)
                pixels[write+ii] = pixels[read+ii];

            write += pitch;

            for (ii=0;ii<pitch;ii++)
                pixels[write+ii] = pixels[read+ii];

            write += pitch;
            read  += pitch;
        }
    }
    //#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */

    FT_Outline_Translate(outline, x_shift, y_shift);

    if (x_left > 0x7FFFFFFF || y_top > 0x7FFFFFFF)
        return 23;

    if (error == 0)
    {
        slot.format      = 1651078259;
        slot.bitmap_left = x_left;
        slot.bitmap_top  = y_top;
    }

    if ( outline && origin )
        FT_Outline_Translate(outline, -origin.x, -origin.y);

    return error;
}
var TT_Round_Off             = 5;
var TT_Round_To_Half_Grid    = 0;
var TT_Round_To_Grid         = 1;
var TT_Round_To_Double_Grid  = 2;
var TT_Round_Up_To_Grid      = 4;
var TT_Round_Down_To_Grid    = 3;
var TT_Round_Super           = 6;
var TT_Round_Super_45        = 7;

function LibraryHintingParams()
{
    this.TT_USE_BYTECODE_INTERPRETER            = false;
    this.TT_CONFIG_OPTION_SUBPIXEL_HINTING      = false;
    this.TT_CONFIG_OPTION_UNPATENTED_HINTING    = false;

    this.TT_HINTER_FLAG = (this.TT_USE_BYTECODE_INTERPRETER) ? 1024 : 0;
    this.TT_CONFIG_OPTION_INTERPRETER_SWITCH    = false;
}

function TT_CallRec()
{
    this.Caller_Range   = 0;
    this.Caller_IP      = 0;
    this.Cur_Count      = 0;

    this.Def            = null;
}

function SPH_TweakRule()
{
    this.family = "";
    this.ppem = 0;
    this.style = "";
    this.glyph = 0;
}

function SPH_ScaleRule()
{
    this.family = "";
    this.ppem = 0;
    this.style = "";
    this.glyph = 0;
    this.scale = 0;
}

var MAX_NAME_SIZE       = 32;
var MAX_CLASS_MEMBERS   = 100;

function Font_Class()
{
    this.name = "";
    this.member = null;
}
function create_font_class(name, arr)
{
    var fc = new Font_Class();
    fc.name = name;
    fc.member = arr;
    return fc;
}

function FT_UnitVector()
{
    this.x = 0;
    this.y = 0;
}

function TT_GraphicsState()
{
    this.rp0 = 0;
    this.rp1 = 0;
    this.rp2 = 0;

    this.dualVector = new FT_UnitVector();
    this.projVector = new FT_UnitVector();
    this.freeVector = new FT_UnitVector();

    // #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    this.both_x_axis = false;
    // #endif

    this.loop = 0;
    this.minimum_distance = 0;
    this.round_state = 0;

    this.auto_flip = false;
    this.control_value_cutin = 0;
    this.single_width_cutin = 0;
    this.single_width_value = 0;
    this.delta_base = 0;
    this.delta_shift = 0;

    this.instruct_control = 0;
    /* According to Greg Hitchcock from Microsoft, the `scan_control'     */
    /* variable as documented in the TrueType specification is a 32-bit   */
    /* integer; the high-word part holds the SCANTYPE value, the low-word */
    /* part the SCANCTRL value.  We separate it into two fields.          */
    this.scan_control = false;
    this.scan_type = 0;

    this.gep0 = 0;
    this.gep1 = 0;
    this.gep2 = 0;
}
TT_GraphicsState.prototype =
{
    default_tt : function()
    {
        this.rp0 = 0;
        this.rp1 = 0;
        this.rp2 = 0;

        this.dualVector.x = 0x4000; this.dualVector.y = 0;
        this.projVector.x = 0x4000; this.projVector.y = 0;
        this.freeVector.x = 0x4000; this.freeVector.y = 0;

        this.both_x_axis = true;

        this.loop = 1;
        this.minimum_distance = 64;
        this.round_state = 1;

        this.auto_flip = true;
        this.control_value_cutin = 68;
        this.single_width_cutin = 0;
        this.single_width_value = 0;
        this.delta_base = 9;
        this.delta_shift = 3;

        this.instruct_control = 0;
        /* According to Greg Hitchcock from Microsoft, the `scan_control'     */
        /* variable as documented in the TrueType specification is a 32-bit   */
        /* integer; the high-word part holds the SCANTYPE value, the low-word */
        /* part the SCANCTRL value.  We separate it into two fields.          */
        this.scan_control = false;
        this.scan_type = 0;

        this.gep0 = 1;
        this.gep1 = 1;
        this.gep2 = 1;
    },

    Copy : function(dst)
    {
        dst.rp0 = this.rp0;
        dst.rp1 = this.rp1;
        dst.rp2 = this.rp2;

        dst.dualVector.x = this.dualVector.x; dst.dualVector.y = this.dualVector.y;
        dst.projVector.x = this.projVector.x; dst.projVector.y = this.projVector.y;
        dst.freeVector.x = this.freeVector.x; dst.freeVector.y = this.freeVector.y;

        dst.both_x_axis = this.both_x_axis;

        dst.loop = this.loop;
        dst.minimum_distance = this.minimum_distance;
        dst.round_state = this.round_state;

        dst.auto_flip = this.auto_flip;
        dst.control_value_cutin = this.control_value_cutin;
        dst.single_width_cutin = this.single_width_cutin;
        dst.single_width_value = this.single_width_value;
        dst.delta_base = this.delta_base;
        dst.delta_shift = this.delta_shift;

        dst.instruct_control = this.instruct_control;
        /* According to Greg Hitchcock from Microsoft, the `scan_control'     */
        /* variable as documented in the TrueType specification is a 32-bit   */
        /* integer; the high-word part holds the SCANTYPE value, the low-word */
        /* part the SCANCTRL value.  We separate it into two fields.          */
        dst.scan_control = this.scan_control;
        dst.scan_type = this.scan_type;

        dst.gep0 = this.gep0;
        dst.gep1 = this.gep1;
        dst.gep2 = this.gep2;
    }
}

function TT_DefRecord()
{
    this.range = 0;             /* in which code range is it located?     */
    this.start = 0;             /* where does it start?                   */
    this.end   = 0;             /* where does it end?                     */
    this.opc   = 0;             /* function #, or instruction code        */
    this.active = false;        /* is it active?                          */
    this.inline_delta = false;  /* is function that defines inline delta? */
    this.sph_fdef_flags = 0;
}

function TT_CodeRange()
{
    this.base = null;
    this.size = 0;
}
function coderange_copy(d, s)
{
    d.base = dublicate_pointer(s.base);
    d.size = s.size;
}

function TT_ExecContextRec()
{
    this.face   = null;
    this.size   = null;
    this.memory = null;

    /* instructions state */
    this.error  = 0;      /* last execution error */
    this.top    = 0;        /* top of exec. stack   */

    this.stackSize  = 0;  /* size of exec. stack  */
    this.stack      = null;      /* current exec. stack  */

    this.args       = 0;
    this.new_top    = 0;    /* new top after exec.  */

    this.zp0 = new TT_GlyphZoneRec();
    this.zp1 = new TT_GlyphZoneRec();
    this.zp2 = new TT_GlyphZoneRec();
    this.pts = new TT_GlyphZoneRec();
    this.twilight = new TT_GlyphZoneRec();

    this.metrics = new FT_Size_Metrics();
    this.tt_metrics = new TT_Size_Metrics(); /* size metrics */

    this.GS = new TT_GraphicsState();         /* current graphics state */

    this.curRange = 0;  /* current code range number   */
    this.code = null;   /* current code range          */
    this.IP = 0;        /* current instruction pointer */
    this.codeSize = 0;  /* size of current range       */

    this.opcode = 0;    /* current opcode              */
    this.length = 0;    /* length of current opcode    */

    this.step_ins = false;  /* true if the interpreter must */
                            /* increment IP after ins. exec */
    this.cvtSize = 0;
    this.cvt = null;

    this.glyphSize = 0; /* glyph instructions buffer size */
    this.glyphIns = null;  /* glyph instructions buffer */

    this.numFDefs = 0;  /* number of function defs         */
    this.maxFDefs = 0;  /* maximum number of function defs */
    this.FDefs = null;     /* table of FDefs entries          */

    this.numIDefs = 0;  /* number of instruction defs */
    this.maxIDefs = 0;  /* maximum number of ins defs */
    this.IDefs = null;     /* table of IDefs entries     */

    this.maxFunc = 0;   /* maximum function index     */
    this.maxIns = 0;    /* maximum instruction index  */

    this.callTop = 0;    /* top of call stack during execution */
    this.callSize = 0;   /* size of call stack */
    this.callStack = null;  /* call stack */

    this.maxPoints = 0;    /* capacity of this context's `pts' */
    this.maxContours = 0;  /* record, expressed in points and  */
                                     /* contours.                        */

    /* table of valid code ranges */
    /* useful for the debugger   */
    this.codeRangeTable = new Array(3);
    for (var i = 0; i < 3; i++)
        this.codeRangeTable[i] = new TT_CodeRange();
    
    this.storeSize = 0;  /* size of current storage */
    this.storage = null;    /* storage area            */

    this.period = 0;     /* values used for the */
    this.phase = 0;      /* `SuperRounding'     */
    this.threshold = 0;

    //#if 0
    /* this seems to be unused */
    //FT_Int             cur_ppem;   /* ppem along the current proj vector */
    //#endif

    this.instruction_trap = false; /* If `True', the interpreter will */
                                         /* exit after each instruction     */

    this.default_GS = new TT_GraphicsState();       /* graphics state resulting from   */
                                                    /* the prep program                */
    this.is_composite = false;     /* true if the glyph is composite  */
    this.pedantic_hinting = false; /* true if pedantic interpretation */

    /* latest interpreter additions */
    this.F_dot_P = 0;           /* dot product of freedom and projection */
                                /* vectors                               */
    this.func_round = null;     /* current rounding function             */

    this.func_project = null;   /* current projection function */
    this.func_dualproj = null;  /* current dual proj. function */
    this.func_freeProj = null;  /* current freedom proj. func  */

    this.func_move = null;      /* current point move function */
    this.func_move_orig = null; /* move original position function */

    this.func_read_cvt = null;  /* read a cvt entry              */
    this.func_write_cvt = null; /* write a cvt entry (in pixels) */
    this.func_move_cvt = null;  /* incr a cvt entry (in pixels)  */

    this.grayscale = false;      /* are we hinting for grayscale? */

    // #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    this.func_round_sphn = null;          /* subpixel rounding function */

    this.grayscale_hinting = false;       /* Using grayscale hinting?      */
    this.subpixel_hinting = false;        /* Using subpixel hinting?       */
    this.native_hinting = false;          /* Using native hinting?         */
    this.ignore_x_mode  = false;          /* Standard rendering mode for   */
                                          /* subpixel hinting.  On if gray */
                                          /* or subpixel hinting is on )   */
    this.compatibility_mode = false;      /* Additional exceptions to      */
                                          /* native TT rules for legacy    */
                                          /* fonts.  Implies               */
                                          /* ignore_x_mode.                */

    /* The following 4 aren't fully implemented but here for MS rasterizer */
    /* compatibility.                                                      */
    this.compatible_widths = false;     /* compatible widths?        */
    this.symmetrical_smoothing = false; /* symmetrical_smoothing?    */
    this.bgr = false;                   /* bgr instead of rgb?       */
    this.subpixel_positioned = false;   /* subpixel positioned       */
                                              /* (DirectWrite ClearType)?  */

    this.rasterizer_version = 0;    /* MS rasterizer version */

    this.iup_called = false;            /* IUP called for glyph?  */

    this.sph_tweak_flags = 0;       /* flags to control */
                                    /* hint tweaks      */

    this.sph_in_func_flags = 0;     /* flags to indicate if in   */
                                    /* special functions         */

    // #endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
}

function Init_Context(exec, memory)
{
    exec.memory   = memory;
    exec.callSize = 32;

    exec.callStack = new Array(exec.callSize);
    for (var i = 0; i < exec.callSize; i++)
        exec.callStack[i] = new TT_CallRec();

    /* all values in the context are set to 0 already, but this is */
    /* here as a remainder                                         */
    exec.maxPoints   = 0;
    exec.maxContours = 0;

    exec.stackSize = 0;
    exec.glyphSize = 0;

    exec.stack     = null;
    exec.glyphIns  = null;

    exec.face = null;
    exec.size = null;

    return 0;
}

function TT_New_Context(driver)
{
    if (driver.context == null)
    {
        driver.context = new TT_ExecContextRec();

        /* initialize it; in case of error this deallocates `exec' too */
        var error = Init_Context(driver.context, driver.memory);
        if (error != 0)
            driver.context = null;
    }
    return driver.context;
}

function TT_Done_Context(exec)
{
    /* points zone */
    exec.maxPoints   = 0;
    exec.maxContours = 0;

    /* free stack */
    exec.stack      = null;
    exec.stackSize  = 0;

    /* free call stack */
    exec.callStack  = null;
    exec.callSize   = 0;
    exec.callTop    = 0;

    /* free glyph code range */
    exec.glyphIns   = null;
    exec.glyphSize  = 0;

    exec.size       = null;
    exec.face       = null;

    return 0;
}

function TT_Load_Context(exec, face, size)
{
    exec.face = face;
    var maxp = face.max_profile;
    exec.size = size;

    if (size != null)
    {
        exec.numFDefs   = size.num_function_defs;
        exec.maxFDefs   = size.max_function_defs;
        exec.numIDefs   = size.num_instruction_defs;
        exec.maxIDefs   = size.max_instruction_defs;
        exec.FDefs      = size.function_defs;
        exec.IDefs      = size.instruction_defs;
        exec.tt_metrics.Copy(size.ttmetrics);
        exec.metrics.Copy(size.metrics);

        exec.maxFunc    = size.max_func;
        exec.maxIns     = size.max_ins;

        for (var i = 0; i < 3; i++)
            coderange_copy(exec.codeRangeTable[i], size.codeRangeTable[i]);

        /* set graphics state */
        size.GS.Copy(exec.GS);

        exec.cvtSize = size.cvt_size;
        exec.cvt     = size.cvt;

        exec.storeSize = size.storage_size;
        exec.storage   = size.storage;

        exec.twilight.Copy(size.twilight);

        /* In case of multi-threading it can happen that the old size object */
        /* no longer exists, thus we must clear all glyph zone references.   */
        exec.zp0.Clear();
        exec.zp1.Clear();
        exec.zp2.Clear();
    }

    /* XXX: We reserve a little more elements on the stack to deal safely */
    /*      with broken fonts like arialbs, courbs, timesbs, etc.         */
    var ret = Update_MaxLONG(exec.memory, exec.stackSize, exec.stack, maxp.maxStackElements + 32);
    if (null != ret)
    {
        exec.stackSize = ret.size;
        exec.stack = ret.block;

        if (ret.err != 0)
            return ret.err;
    }
    
    ret = Update_MaxBYTE(exec.memory, exec.glyphSize, exec.glyphIns, maxp.maxSizeOfInstructions);
    if (ret != null)
    {
        exec.glyphSize = ret.size;
        exec.glyphIns = ret.block;

        if (ret.err != 0)
            return ret.err;
    }

    exec.pts.n_points   = 0;
    exec.pts.n_contours = 0;

    exec.zp1.Copy(exec.pts);
    exec.zp2.Copy(exec.pts);
    exec.zp0.Copy(exec.pts);

    exec.instruction_trap = false;

    return 0;
}

function Update_MaxBYTE(memory, size, buff, new_max)
{
    if (size >= new_max)
        return null;

    return FT_Common.realloc(memory, buff, size, new_max);
}
function Update_MaxLONG(memory, size, buff, new_max)
{
    if (size >= new_max)
        return null;

    return FT_Common.realloc_long(memory, buff, size, new_max);
}

function tt_size_ready_bytecode(size, pedantic)
{
    var error = 0;
    if (!size.bytecode_ready)
    {
        error = tt_size_init_bytecode(size, pedantic);
        if (error != 0)
            return error;
    }

    /* rescale CVT when needed */
    if (!size.cvt_ready)
    {
        var face = size.face;

        /* Scale the cvt values to the new ppem.          */
        /* We use by default the y ppem to scale the CVT. */
        for (var i = 0; i < size.cvt_size; i++)
            size.cvt[i] = FT_MulFix(face.cvt[i], size.ttmetrics.scale);

        /* all twilight points are originally zero */
        for (var i = 0; i < size.twilight.n_points; i++ )
        {
            size.twilight.org[size.twilight._offset_org + i].x = 0;
            size.twilight.org[size.twilight._offset_org + i].y = 0;
            size.twilight.cur[size.twilight._offset_cur + i].x = 0;
            size.twilight.cur[size.twilight._offset_cur + i].y = 0;
        }

        /* clear storage area */
        for (var i = 0; i < size.storage_size; i++)
            size.storage[i] = 0;

        size.GS.default_tt();

        error = tt_size_run_prep(size, pedantic);
        if (error != 0)
            size.cvt_ready = 1;
    }
    return error;
}

function tt_size_init_bytecode(size, pedantic)
{
    var maxp = size.face.max_profile;

    size.bytecode_ready = 1;
    size.cvt_ready      = 0;

    size.max_function_defs    = maxp.maxFunctionDefs;
    size.max_instruction_defs = maxp.maxInstructionDefs;

    size.num_function_defs    = 0;
    size.num_instruction_defs = 0;

    size.max_func = 0;
    size.max_ins  = 0;

    size.cvt_size     = size.face.cvt_size;
    size.storage_size = maxp.maxStorage;

    /* Set default metrics */
    var metrics = size.ttmetrics;
    metrics.rotated   = false;
    metrics.stretched = false;

      /* set default compensation (all 0) */
    for (var i = 0; i < 4; i++)
        metrics.compensations[i] = 0;

    /* allocate function defs, instruction defs, cvt, and storage area */
    size.function_defs = new Array(size.max_function_defs);
    for (var i = 0; i < size.max_function_defs; i++)
        size.function_defs[i] = new TT_DefRecord();

    size.instruction_defs = new Array(size.max_instruction_defs);
    for (var i = 0; i < size.max_instruction_defs; i++)
        size.instruction_defs[i] = new TT_DefRecord();

    size.cvt = new CreateIntArray(size.cvt_size);
    size.storage = new CreateIntArray(size.storage_size);

    /* reserve twilight zone */
    var n_twilight = maxp.maxTwilightPoints;

    /* there are 4 phantom points (do we need this?) */
    n_twilight += 4;

    var error = tt_glyphzone_new(size.face.memory, n_twilight, 0, size.twilight);
    if (error != 0)
    {
        tt_size_done_bytecode(size);
        return error;
    }

    size.twilight.n_points = n_twilight;

    size.GS.default_tt();
    size.face.interpreter = TT_RunIns;

    /* Fine, now run the font program! */
    error = tt_size_run_fpgm(size, pedantic);
    if (error != 0)
        tt_size_done_bytecode(size);

    return error;
}

function tt_size_run_fpgm(size, pedantic)
{
    var face = size.face;
    var exec = null;

    /* debugging instances have their own context */
    if (size.debug)
        exec = size.context;
    else
        exec = face.driver.context;

    if (exec == null)
        return 153;

    TT_Load_Context(exec, face, size);

    exec.callTop = 0;
    exec.top     = 0;

    exec.period    = 64;
    exec.phase     = 0;
    exec.threshold = 0;

    exec.instruction_trap = false;
    exec.F_dot_P          = 0x4000;

    exec.pedantic_hinting = pedantic;

    var metrics = exec.metrics;
    var tt_metrics = exec.tt_metrics;

    metrics.x_ppem   = 0;
    metrics.y_ppem   = 0;
    metrics.x_scale  = 0;
    metrics.y_scale  = 0;

    tt_metrics.ppem  = 0;
    tt_metrics.scale = 0;
    tt_metrics.ratio = 0x10000;

    /* allow font program execution */
    TT_Set_CodeRange(exec, 1, face.font_program, face.font_program_size);

    /* disable CVT and glyph programs coderange */
    TT_Clear_CodeRange(exec, 2);
    TT_Clear_CodeRange(exec, 3);

    var error = 0;
    if (face.font_program_size > 0)
    {
        error = TT_Goto_CodeRange(exec, 1, 0);
        if (error == 0)
            error = face.interpreter(exec);
    }
    else
        error = 0;

    if (error == 0)
        TT_Save_Context(exec, size);

    return error;
}

function TT_Clear_CodeRange(exec, range)
{
    exec.codeRangeTable[range - 1].base = null;
    exec.codeRangeTable[range - 1].size = 0;

    return 0;
}

function TT_Save_Context(exec, size)
{
    /* XXX: Will probably disappear soon with all the code range */
    /*      management, which is now rather obsolete.            */
    /*                                                           */
    size.num_function_defs    = exec.numFDefs;
    size.num_instruction_defs = exec.numIDefs;

    size.max_func = exec.maxFunc;
    size.max_ins  = exec.maxIns;

    for (var i = 0; i < 3; i++)
    {
        size.codeRangeTable[i].base = dublicate_pointer(exec.codeRangeTable[i].base);
        size.codeRangeTable[i].size = exec.codeRangeTable[i].size;
    }

    return 0;
}

function tt_size_done_bytecode(size)
{
    if (size.debug)
    {
        /* the debug context must be deleted by the debugger itself */
        size.context = null;
        size.debug   = false;
    }

    size.cvt = null;
    size.cvt_size = 0;

    /* free storage area */
    size.storage = null;
    size.storage_size = 0;

    /* twilight zone */
    tt_glyphzone_done(size.twilight);

    size.function_defs = null;
    size.instruction_defs = null;

    size.num_function_defs    = 0;
    size.max_function_defs    = 0;
    size.num_instruction_defs = 0;
    size.max_instruction_defs = 0;

    size.max_func = 0;
    size.max_ins  = 0;

    size.bytecode_ready = false;
    size.cvt_ready      = false;
}

function tt_glyphzone_done(zone)
{
    if (zone.memory != null)
    {
        zone.contours = null;
        zone.tags = null;
        zone.cur = null;
        zone.org = null;
        zone.orus = null;

        zone.max_points = zone.n_points   = 0;
        zone.max_contours = zone.n_contours = 0;
        zone.memory = null;
    }
}

function tt_glyphzone_new(memory, maxPoints, maxContours, zone)
{
    zone.Clear();
    zone.memory = memory;

    zone.org = new Array(maxPoints);
    FT_CreateVectorArray(zone.org, 0, maxPoints);

    zone.cur = new Array(maxPoints);
    FT_CreateVectorArray(zone.cur, 0, maxPoints);

    zone.orus = new Array(maxPoints);
    FT_CreateVectorArray(zone.orus, 0, maxPoints);

    zone.tags = CreateIntArray(maxPoints);
    zone.contours = CreateIntArray(maxContours);

    zone.max_points   = maxPoints;
    zone.max_contours = maxContours;
    return 0;
}

function TT_Set_CodeRange(exec, range, base, length)
{
    exec.codeRangeTable[range - 1].base = dublicate_pointer(base);
    exec.codeRangeTable[range - 1].size = length;

    return 0;
}

function TT_Goto_CodeRange(exec, range, IP)
{
    var coderange = exec.codeRangeTable[range - 1];

    /* NOTE: Because the last instruction of a program may be a CALL */
    /*       which will return to the first byte *after* the code    */
    /*       range, we test for IP <= Size instead of IP < Size.     */
    /*                                                               */
    exec.code     = dublicate_pointer(coderange.base);
    exec.codeSize = coderange.size;
    exec.IP       = IP;
    exec.curRange = range;

    return 0;
}

function TT_Run_Context(exec, debug)
{
    var error = TT_Goto_CodeRange(exec, 3, 0);
    if (error != 0)
        return error;

    exec.zp0.Copy(exec.pts);
    exec.zp1.Copy(exec.pts);
    exec.zp2.Copy(exec.pts);

    exec.GS.gep0 = 1;
    exec.GS.gep1 = 1;
    exec.GS.gep2 = 1;

    exec.GS.projVector.x = 0x4000;
    exec.GS.projVector.y = 0x0000;

    exec.GS.freeVector.x = exec.GS.projVector.x;
    exec.GS.freeVector.y = exec.GS.projVector.y;
    exec.GS.dualVector.x = exec.GS.projVector.x;
    exec.GS.dualVector.y = exec.GS.projVector.y;

    if (exec.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_UNPATENTED_HINTING)//#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    {
        exec.GS.both_x_axis = true;
    }//#endif

    exec.GS.round_state = 1;
    exec.GS.loop        = 1;

    /* some glyphs leave something on the stack. so we clean it */
    /* before a new execution.                                  */
    exec.top     = 0;
    exec.callTop = 0;

    return exec.face.interpreter(exec);
}

//--------------------------------------------------------------//

var opcode_length =
[1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,

-1,-2, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,

1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
2, 3, 4, 5,  6, 7, 8, 9,  3, 5, 7, 9, 11,13,15,17,

1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1];

function Pop_Push_PACK(x, y)
{
    return ((x << 4) | y);
}
var Pop_Push_Count =
[
    /* opcodes are gathered in groups of 16 */
    /* please keep the spaces as they are   */

    /*  SVTCA  y  */  Pop_Push_PACK( 0, 0 ),
    /*  SVTCA  x  */  Pop_Push_PACK( 0, 0 ),
    /*  SPvTCA y  */  Pop_Push_PACK( 0, 0 ),
    /*  SPvTCA x  */  Pop_Push_PACK( 0, 0 ),
    /*  SFvTCA y  */  Pop_Push_PACK( 0, 0 ),
    /*  SFvTCA x  */  Pop_Push_PACK( 0, 0 ),
    /*  SPvTL //  */  Pop_Push_PACK( 2, 0 ),
    /*  SPvTL +   */  Pop_Push_PACK( 2, 0 ),
    /*  SFvTL //  */  Pop_Push_PACK( 2, 0 ),
    /*  SFvTL +   */  Pop_Push_PACK( 2, 0 ),
    /*  SPvFS     */  Pop_Push_PACK( 2, 0 ),
    /*  SFvFS     */  Pop_Push_PACK( 2, 0 ),
    /*  GPV       */  Pop_Push_PACK( 0, 2 ),
    /*  GFV       */  Pop_Push_PACK( 0, 2 ),
    /*  SFvTPv    */  Pop_Push_PACK( 0, 0 ),
    /*  ISECT     */  Pop_Push_PACK( 5, 0 ),

    /*  SRP0      */  Pop_Push_PACK( 1, 0 ),
    /*  SRP1      */  Pop_Push_PACK( 1, 0 ),
    /*  SRP2      */  Pop_Push_PACK( 1, 0 ),
    /*  SZP0      */  Pop_Push_PACK( 1, 0 ),
    /*  SZP1      */  Pop_Push_PACK( 1, 0 ),
    /*  SZP2      */  Pop_Push_PACK( 1, 0 ),
    /*  SZPS      */  Pop_Push_PACK( 1, 0 ),
    /*  SLOOP     */  Pop_Push_PACK( 1, 0 ),
    /*  RTG       */  Pop_Push_PACK( 0, 0 ),
    /*  RTHG      */  Pop_Push_PACK( 0, 0 ),
    /*  SMD       */  Pop_Push_PACK( 1, 0 ),
    /*  ELSE      */  Pop_Push_PACK( 0, 0 ),
    /*  JMPR      */  Pop_Push_PACK( 1, 0 ),
    /*  SCvTCi    */  Pop_Push_PACK( 1, 0 ),
    /*  SSwCi     */  Pop_Push_PACK( 1, 0 ),
    /*  SSW       */  Pop_Push_PACK( 1, 0 ),

    /*  DUP       */  Pop_Push_PACK( 1, 2 ),
    /*  POP       */  Pop_Push_PACK( 1, 0 ),
    /*  CLEAR     */  Pop_Push_PACK( 0, 0 ),
    /*  SWAP      */  Pop_Push_PACK( 2, 2 ),
    /*  DEPTH     */  Pop_Push_PACK( 0, 1 ),
    /*  CINDEX    */  Pop_Push_PACK( 1, 1 ),
    /*  MINDEX    */  Pop_Push_PACK( 1, 0 ),
    /*  AlignPTS  */  Pop_Push_PACK( 2, 0 ),
    /*  INS_$28   */  Pop_Push_PACK( 0, 0 ),
    /*  UTP       */  Pop_Push_PACK( 1, 0 ),
    /*  LOOPCALL  */  Pop_Push_PACK( 2, 0 ),
    /*  CALL      */  Pop_Push_PACK( 1, 0 ),
    /*  FDEF      */  Pop_Push_PACK( 1, 0 ),
    /*  ENDF      */  Pop_Push_PACK( 0, 0 ),
    /*  MDAP[0]   */  Pop_Push_PACK( 1, 0 ),
    /*  MDAP[1]   */  Pop_Push_PACK( 1, 0 ),

    /*  IUP[0]    */  Pop_Push_PACK( 0, 0 ),
    /*  IUP[1]    */  Pop_Push_PACK( 0, 0 ),
    /*  SHP[0]    */  Pop_Push_PACK( 0, 0 ),
    /*  SHP[1]    */  Pop_Push_PACK( 0, 0 ),
    /*  SHC[0]    */  Pop_Push_PACK( 1, 0 ),
    /*  SHC[1]    */  Pop_Push_PACK( 1, 0 ),
    /*  SHZ[0]    */  Pop_Push_PACK( 1, 0 ),
    /*  SHZ[1]    */  Pop_Push_PACK( 1, 0 ),
    /*  SHPIX     */  Pop_Push_PACK( 1, 0 ),
    /*  IP        */  Pop_Push_PACK( 0, 0 ),
    /*  MSIRP[0]  */  Pop_Push_PACK( 2, 0 ),
    /*  MSIRP[1]  */  Pop_Push_PACK( 2, 0 ),
    /*  AlignRP   */  Pop_Push_PACK( 0, 0 ),
    /*  RTDG      */  Pop_Push_PACK( 0, 0 ),
    /*  MIAP[0]   */  Pop_Push_PACK( 2, 0 ),
    /*  MIAP[1]   */  Pop_Push_PACK( 2, 0 ),

    /*  NPushB    */  Pop_Push_PACK( 0, 0 ),
    /*  NPushW    */  Pop_Push_PACK( 0, 0 ),
    /*  WS        */  Pop_Push_PACK( 2, 0 ),
    /*  RS        */  Pop_Push_PACK( 1, 1 ),
    /*  WCvtP     */  Pop_Push_PACK( 2, 0 ),
    /*  RCvt      */  Pop_Push_PACK( 1, 1 ),
    /*  GC[0]     */  Pop_Push_PACK( 1, 1 ),
    /*  GC[1]     */  Pop_Push_PACK( 1, 1 ),
    /*  SCFS      */  Pop_Push_PACK( 2, 0 ),
    /*  MD[0]     */  Pop_Push_PACK( 2, 1 ),
    /*  MD[1]     */  Pop_Push_PACK( 2, 1 ),
    /*  MPPEM     */  Pop_Push_PACK( 0, 1 ),
    /*  MPS       */  Pop_Push_PACK( 0, 1 ),
    /*  FlipON    */  Pop_Push_PACK( 0, 0 ),
    /*  FlipOFF   */  Pop_Push_PACK( 0, 0 ),
    /*  DEBUG     */  Pop_Push_PACK( 1, 0 ),

    /*  LT        */  Pop_Push_PACK( 2, 1 ),
    /*  LTEQ      */  Pop_Push_PACK( 2, 1 ),
    /*  GT        */  Pop_Push_PACK( 2, 1 ),
    /*  GTEQ      */  Pop_Push_PACK( 2, 1 ),
    /*  EQ        */  Pop_Push_PACK( 2, 1 ),
    /*  NEQ       */  Pop_Push_PACK( 2, 1 ),
    /*  ODD       */  Pop_Push_PACK( 1, 1 ),
    /*  EVEN      */  Pop_Push_PACK( 1, 1 ),
    /*  IF        */  Pop_Push_PACK( 1, 0 ),
    /*  EIF       */  Pop_Push_PACK( 0, 0 ),
    /*  AND       */  Pop_Push_PACK( 2, 1 ),
    /*  OR        */  Pop_Push_PACK( 2, 1 ),
    /*  NOT       */  Pop_Push_PACK( 1, 1 ),
    /*  DeltaP1   */  Pop_Push_PACK( 1, 0 ),
    /*  SDB       */  Pop_Push_PACK( 1, 0 ),
    /*  SDS       */  Pop_Push_PACK( 1, 0 ),

    /*  ADD       */  Pop_Push_PACK( 2, 1 ),
    /*  SUB       */  Pop_Push_PACK( 2, 1 ),
    /*  DIV       */  Pop_Push_PACK( 2, 1 ),
    /*  MUL       */  Pop_Push_PACK( 2, 1 ),
    /*  ABS       */  Pop_Push_PACK( 1, 1 ),
    /*  NEG       */  Pop_Push_PACK( 1, 1 ),
    /*  FLOOR     */  Pop_Push_PACK( 1, 1 ),
    /*  CEILING   */  Pop_Push_PACK( 1, 1 ),
    /*  ROUND[0]  */  Pop_Push_PACK( 1, 1 ),
    /*  ROUND[1]  */  Pop_Push_PACK( 1, 1 ),
    /*  ROUND[2]  */  Pop_Push_PACK( 1, 1 ),
    /*  ROUND[3]  */  Pop_Push_PACK( 1, 1 ),
    /*  NROUND[0] */  Pop_Push_PACK( 1, 1 ),
    /*  NROUND[1] */  Pop_Push_PACK( 1, 1 ),
    /*  NROUND[2] */  Pop_Push_PACK( 1, 1 ),
    /*  NROUND[3] */  Pop_Push_PACK( 1, 1 ),

    /*  WCvtF     */  Pop_Push_PACK( 2, 0 ),
    /*  DeltaP2   */  Pop_Push_PACK( 1, 0 ),
    /*  DeltaP3   */  Pop_Push_PACK( 1, 0 ),
    /*  DeltaCn[0] */ Pop_Push_PACK( 1, 0 ),
    /*  DeltaCn[1] */ Pop_Push_PACK( 1, 0 ),
    /*  DeltaCn[2] */ Pop_Push_PACK( 1, 0 ),
    /*  SROUND    */  Pop_Push_PACK( 1, 0 ),
    /*  S45Round  */  Pop_Push_PACK( 1, 0 ),
    /*  JROT      */  Pop_Push_PACK( 2, 0 ),
    /*  JROF      */  Pop_Push_PACK( 2, 0 ),
    /*  ROFF      */  Pop_Push_PACK( 0, 0 ),
    /*  INS_$7B   */  Pop_Push_PACK( 0, 0 ),
    /*  RUTG      */  Pop_Push_PACK( 0, 0 ),
    /*  RDTG      */  Pop_Push_PACK( 0, 0 ),
    /*  SANGW     */  Pop_Push_PACK( 1, 0 ),
    /*  AA        */  Pop_Push_PACK( 1, 0 ),

    /*  FlipPT    */  Pop_Push_PACK( 0, 0 ),
    /*  FlipRgON  */  Pop_Push_PACK( 2, 0 ),
    /*  FlipRgOFF */  Pop_Push_PACK( 2, 0 ),
    /*  INS_$83   */  Pop_Push_PACK( 0, 0 ),
    /*  INS_$84   */  Pop_Push_PACK( 0, 0 ),
    /*  ScanCTRL  */  Pop_Push_PACK( 1, 0 ),
    /*  SDPVTL[0] */  Pop_Push_PACK( 2, 0 ),
    /*  SDPVTL[1] */  Pop_Push_PACK( 2, 0 ),
    /*  GetINFO   */  Pop_Push_PACK( 1, 1 ),
    /*  IDEF      */  Pop_Push_PACK( 1, 0 ),
    /*  ROLL      */  Pop_Push_PACK( 3, 3 ),
    /*  MAX       */  Pop_Push_PACK( 2, 1 ),
    /*  MIN       */  Pop_Push_PACK( 2, 1 ),
    /*  ScanTYPE  */  Pop_Push_PACK( 1, 0 ),
    /*  InstCTRL  */  Pop_Push_PACK( 2, 0 ),
    /*  INS_$8F   */  Pop_Push_PACK( 0, 0 ),

    /*  INS_$90  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$91  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$92  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$93  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$94  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$95  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$96  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$97  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$98  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$99  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9A  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9B  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9C  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9D  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9E  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$9F  */   Pop_Push_PACK( 0, 0 ),

    /*  INS_$A0  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A1  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A2  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A3  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A4  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A5  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A6  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A7  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A8  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$A9  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AA  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AB  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AC  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AD  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AE  */   Pop_Push_PACK( 0, 0 ),
    /*  INS_$AF  */   Pop_Push_PACK( 0, 0 ),

    /*  PushB[0]  */  Pop_Push_PACK( 0, 1 ),
    /*  PushB[1]  */  Pop_Push_PACK( 0, 2 ),
    /*  PushB[2]  */  Pop_Push_PACK( 0, 3 ),
    /*  PushB[3]  */  Pop_Push_PACK( 0, 4 ),
    /*  PushB[4]  */  Pop_Push_PACK( 0, 5 ),
    /*  PushB[5]  */  Pop_Push_PACK( 0, 6 ),
    /*  PushB[6]  */  Pop_Push_PACK( 0, 7 ),
    /*  PushB[7]  */  Pop_Push_PACK( 0, 8 ),
    /*  PushW[0]  */  Pop_Push_PACK( 0, 1 ),
    /*  PushW[1]  */  Pop_Push_PACK( 0, 2 ),
    /*  PushW[2]  */  Pop_Push_PACK( 0, 3 ),
    /*  PushW[3]  */  Pop_Push_PACK( 0, 4 ),
    /*  PushW[4]  */  Pop_Push_PACK( 0, 5 ),
    /*  PushW[5]  */  Pop_Push_PACK( 0, 6 ),
    /*  PushW[6]  */  Pop_Push_PACK( 0, 7 ),
    /*  PushW[7]  */  Pop_Push_PACK( 0, 8 ),

    /*  MDRP[00]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[01]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[02]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[03]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[04]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[05]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[06]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[07]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[08]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[09]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[10]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[11]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[12]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[13]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[14]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[15]  */  Pop_Push_PACK( 1, 0 ),

    /*  MDRP[16]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[17]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[18]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[19]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[20]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[21]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[22]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[23]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[24]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[25]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[26]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[27]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[28]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[29]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[30]  */  Pop_Push_PACK( 1, 0 ),
    /*  MDRP[31]  */  Pop_Push_PACK( 1, 0 ),

    /*  MIRP[00]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[01]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[02]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[03]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[04]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[05]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[06]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[07]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[08]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[09]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[10]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[11]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[12]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[13]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[14]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[15]  */  Pop_Push_PACK( 2, 0 ),

    /*  MIRP[16]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[17]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[18]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[19]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[20]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[21]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[22]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[23]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[24]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[25]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[26]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[27]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[28]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[29]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[30]  */  Pop_Push_PACK( 2, 0 ),
    /*  MIRP[31]  */  Pop_Push_PACK( 2, 0 )
];

function GUESS_VECTOR(exc, v)
{
    // TODO: unpatented
}

function Ins_SxVTL(exc, aIdx1, aIdx2, aOpc, Vec)
{
    if ((aIdx1 >= exc.zp2.n_points) || (aIdx2 >= exc.zp1.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = FT_Err_Invalid_Reference;
        return 1;
    }

    var p1 = exc.zp1.cur[exc.zp1._offset_cur + aIdx2];
    var p2 = exc.zp2.cur[exc.zp2._offset_cur + aIdx1];

    var A = p1.x - p2.x;
    var B = p1.y - p2.y;

    /* If p1 == p2, SPVTL and SFVTL behave the same as */
    /* SPVTCA[X] and SFVTCA[X], respectively.          */
    /*                                                 */
    /* Confirmed by Greg Hitchcock.                    */

    if (A == 0 && B == 0)
    {
        A = 0x4000;
        aOpc = 0;
    }

    if ((aOpc & 1) != 0)
    {
        var C =  B;   /* counter clockwise rotation */
        B =  A;
        A = -C;
    }

    Normalize(exc, A, B, Vec);

    return 0;
}

function TT_VecLen(X, Y)
{
    if (X > 2147483647 || Y > 2147483647)
        alert("error");

    var v = new FT_Vector();
    v.x = X;
    v.y = Y;
    return FT_Vector_Length(v);
}

var FT_Hypot = TT_VecLen;

function FT_DivFix14(a, b)
{
    return FT_DivFix(a, b << 2);
}

function Normalize(exc, Vx, Vy, R)
{
    if (Vx > 2147483647 || Vy > 2147483647)
        alert("error");

    if (Math.abs(Vx) < 0x4000 && Math.abs(Vy) < 0x4000)
    {
        if (0 == Vx && 0 == Vy)
            return 0;

        Vx *= 0x4000;
        Vy *= 0x4000;
    }

    var W = FT_Hypot(Vx, Vy);

    R.x = FT_Common.UShort_To_Short(FT_DivFix14(Vx, W) & 0xFFFF);   /* Type conversion */
    R.y = FT_Common.UShort_To_Short(FT_DivFix14(Vy, W) & 0xFFFF);   /* Type conversion */

    return 0;
}

// ------
function Compute_Funcs(exc)
{
    // TODO: unpatented
    if (exc.GS.freeVector.x == 0x4000)
        exc.F_dot_P = exc.GS.projVector.x;
    else if (exc.GS.freeVector.y == 0x4000)
        exc.F_dot_P = exc.GS.projVector.y;
    else
        exc.F_dot_P = (exc.GS.projVector.x * exc.GS.freeVector.x + exc.GS.projVector.y * exc.GS.freeVector.y) >> 14;

    if (exc.GS.projVector.x == 0x4000)
        exc.func_project = Project_x;
    else if (exc.GS.projVector.y == 0x4000)
        exc.func_project = Project_y;
    else
        exc.func_project = Project;

    if (exc.GS.dualVector.x == 0x4000)
        exc.func_dualproj = Project_x;
    else if (exc.GS.dualVector.y == 0x4000)
        exc.func_dualproj = Project_y;
    else
        exc.func_dualproj = Dual_Project;

    exc.func_move      = Direct_Move;
    exc.func_move_orig = Direct_Move_Orig;

    if (exc.F_dot_P == 0x4000)
    {
        if ( exc.GS.freeVector.x == 0x4000 )
        {
            exc.func_move      = Direct_Move_X;
            exc.func_move_orig = Direct_Move_Orig_X;
        }
        else if ( exc.GS.freeVector.y == 0x4000 )
        {
            exc.func_move      = Direct_Move_Y;
            exc.func_move_orig = Direct_Move_Orig_Y;
        }
    }

    /* at small sizes, F_dot_P can become too small, resulting   */
    /* in overflows and `spikes' in a number of glyphs like `w'. */
    if (Math.abs(exc.F_dot_P) < 0x400)
        exc.F_dot_P = 0x4000;

    /* Disable cached aspect ratio */
    exc.tt_metrics.ratio = 0;
}

function Project(exc, dx, dy)
{
    if (dx > 2147483647 || dy > 2147483647)
        alert("error");

    return TT_DotFix14(dx, dy, exc.GS.projVector.x, exc.GS.projVector.y);
}
function Project_x(exc, dx, dy)
{
    if (dx > 2147483647 || dy > 2147483647)
        alert("error");

    return dx;
}
function Project_y(exc, dx, dy)
{
    if (dx > 2147483647 || dy > 2147483647)
        alert("error");

    return dy;
}
function Dual_Project(exc, dx, dy)
{
    if (dx > 2147483647 || dy > 2147483647)
        alert("error");
    
    return TT_DotFix14(dx, dy, exc.GS.dualVector.x, exc.GS.dualVector.y);
}

function Direct_Move(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");

    var v = exc.GS.freeVector.x;
    if (v != 0)
    {
        if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
        {
            if (!exc.ignore_x_mode || (exc.sph_tweak_flags & 1))
            {
                 zone.cur[zone._offset_cur + point].x += FT_MulDiv(distance, v, exc.F_dot_P);
            }
        }
        else
        {
            zone.cur[zone._offset_cur + point].x += FT_MulDiv(distance, v, exc.F_dot_P);
        }

        zone.tags[zone._offset_tags + point] |= 8;
    }

    v = exc.GS.freeVector.y;

    if (v != 0)
    {
        zone.cur[zone._offset_cur + point].y += FT_MulDiv(distance, v, exc.F_dot_P);
        zone.tags[zone._offset_tags + point] |= 16;
    }
}

function Direct_Move_Orig(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");

    var v = exc.GS.freeVector.x;

    if (v != 0)
    {
        zone.org[zone._offset_org + point].x += FT_MulDiv(distance, v, exc.F_dot_P);
    }

    v = exc.GS.freeVector.y;

    if (v != 0)
    {
        zone.org[zone._offset_org + point].y += FT_MulDiv(distance, v, exc.F_dot_P);
    }
}

function Direct_Move_X(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        if (!exc.ignore_x_mode)
        {
            zone.cur[zone._offset_cur + point].x += distance;
        }
        zone.tags[zone._offset_tags + point] |= 8;
    }
    else
    {
        zone.cur[zone._offset_cur + point].x += distance;
        zone.tags[zone._offset_tags + point] |= 8;
    }
}

function Direct_Move_Y(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");

    zone.cur[zone._offset_cur + point].y += distance;
    zone.tags[zone._offset_tags + point] |= 16;
}

function Direct_Move_Orig_X(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");

    zone.org[zone._offset_org + point].x += distance;
}

function Direct_Move_Orig_Y(exc, zone, point, distance)
{
    if (point < 0 || point > 0xFFFF || distance > 2147483647)
        alert("error");
    
    zone.org[zone._offset_org + point].y += distance;
}

// ------

// ------
function Compute_Round(exc, round_mode)
{
    switch (round_mode)
    {
    case TT_Round_Off:
        exc.func_round = Round_None;
        break;

    case TT_Round_To_Grid:
        exc.func_round = Round_To_Grid;
        break;

    case TT_Round_Up_To_Grid:
        exc.func_round = Round_Up_To_Grid;
        break;

    case TT_Round_Down_To_Grid:
        exc.func_round = Round_Down_To_Grid;
        break;

    case TT_Round_To_Half_Grid:
        exc.func_round = Round_To_Half_Grid;
        break;

    case TT_Round_To_Double_Grid:
        exc.func_round = Round_To_Double_Grid;
        break;

    case TT_Round_Super:
        exc.func_round = Round_Super;
        break;

    case TT_Round_Super_45:
        exc.func_round = Round_Super_45;
        break;
    }
}

function Round_None(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");

    var val;
    if (distance >= 0)
    {
        val = distance + compensation;
        if (distance != 0 && val < 0)
            val = 0;
    }
    else
    {
        val = distance - compensation;
        if (val > 0)
            val = 0;
    }
    return val;
}

function Round_To_Grid(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");

    var val;
    if (distance >= 0)
    {
        val = distance + compensation + 32;
        if (distance != 0 && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -FT_PIX_ROUND(compensation - distance);
        if (val > 0)
            val = 0;
    }

    return  val;
}

function Round_Up_To_Grid(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");

    var val;
    if (distance >= 0)
    {
        val = distance + compensation + 63;
        if (distance != 0 && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -FT_PIX_CEIL(compensation - distance);
        if (val > 0)
            val = 0;
    }
    return val;
}

function Round_Down_To_Grid(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");

    var val;
    if (distance >= 0)
    {
        val = distance + compensation;
        if (distance && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -((compensation - distance) & -64);
        if (val > 0)
            val = 0;
    }
    return val;
}

function Round_To_Half_Grid(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");
    
    var val;
    if (distance >= 0)
    {
        val = FT_PIX_FLOOR(distance + compensation) + 32;
        if (distance != 0 && val < 0)
            val = 0;
    }
    else
    {
        val = -(FT_PIX_FLOOR(compensation - distance) + 32);
        if (val > 0)
            val = 0;
    }
    return val;
}

function Round_To_Double_Grid(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");
    
    var val;
    if (distance >= 0)
    {
        val = distance + compensation + 16;
        if (distance != 0 && val > 0)
            val &= ~31;
        else
            val = 0;
    }
    else
    {
        val = -FT_PAD_ROUND(compensation - distance, 32);
        if (val > 0)
            val = 0;
    }

    return val;
}

function Round_Super(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");
    
    var val;
    if (distance >= 0)
    {
        val = (distance - exc.phase + exc.threshold + compensation) & -exc.period;
        if (distance != 0 && val < 0)
            val = 0;
        val += exc.phase;
    }
    else
    {
        val = -((exc.threshold - exc.phase - distance + compensation) & -exc.period);
        if (val > 0)
            val = 0;
        val -= exc.phase;
    }
    return val;
}

function Round_Super_45(exc, distance, compensation)
{
    if (distance > 2147483647 || compensation > 2147483647)
        alert("error");
    
    var val;
    if (distance >= 0)
    {
        val = (((distance - exc.phase + exc.threshold + compensation) / exc.period) >> 0) * exc.period;
        if (distance != 0 && val < 0)
            val = 0;
        val += exc.phase;
    }
    else
    {
        val = -((((exc.threshold - exc.phase - distance + compensation) / exc.period) >> 0) * exc.period);
        if (val > 0)
            val = 0;
        val -= exc.phase;
    }
    return val;
}

function SetSuperRound(exc, GridPeriod, selector)
{
    switch ((selector & 0xC0))
    {
    case 0:
        exc.period = (GridPeriod / 2) >> 0;
        break;
    case 0x40:
        exc.period = GridPeriod;
        break;
    case 0x80:
        exc.period = GridPeriod * 2;
        break;
    /* This opcode is reserved, but... */
    case 0xC0:
        exc.period = GridPeriod;
        break;
    }

    switch ((selector & 0x30))
    {
    case 0:
        exc.phase = 0;
        break;
    case 0x10:
        exc.phase = exc.period >> 2;
        break;
    case 0x20:
        exc.phase = exc.period >> 1;
        break;
    case 0x30:
        exc.phase = (exc.period * 3) >> 2;
        break;
    }

    if ((selector & 0x0F) == 0)
        exc.threshold = exc.period - 1;
    else
        exc.threshold = (((selector & 0x0F) - 4) * exc.period) >> 3;

    exc.period    >>= 8;
    exc.phase     >>= 8;
    exc.threshold >>= 8;
}

// ------

function SkipCode(exc)
{
    exc.IP += exc.length;

    if (exc.IP < exc.codeSize)
    {
        exc.opcode = exc.code.data[exc.code.pos + exc.IP];

        exc.length = opcode_length[exc.opcode];
        if ( exc.length < 0 )
        {
            if ( exc.IP + 1 >= exc.codeSize )
            {
                exc.error = 131;
                return 1;
            }
            exc.length = 2 - exc.length * exc.code.data[exc.code.pos + exc.IP + 1];
        }

        if (exc.IP + exc.length <= exc.codeSize)
            return 0;
    }

    exc.error = 131;
    return 1;
}

function Ins_Goto_CodeRange(exc, aRange, aIP)
{
    if (aRange < 1 || aRange > 3)
    {
        exc.error = 132;
        return 1;
    }

    var range = exc.codeRangeTable[aRange - 1];

    if (range.base == null)     /* invalid coderange */
    {
        exc.error = 138;
        return 1;
    }

    /* NOTE: Because the last instruction of a program may be a CALL */
    /*       which will return to the first byte *after* the code    */
    /*       range, we test for aIP <= Size, instead of aIP < Size.  */

    if (aIP > range.size)
    {
        exc.error = 131;
        return 1;
    }

    exc.code     = dublicate_pointer(range.base);
    exc.codeSize = range.size;
    exc.IP       = aIP;
    exc.curRange = aRange;

    return 0;
}

function Compute_Point_Displacement(exc, x, y, zone, refp)
{
    var ret = {x: x, y: y, refp: refp, err: 0};
    var zp = null;
    var p;

    if (exc.opcode & 1)
    {
        zp = exc.zp0;
        p  = exc.GS.rp1;
    }
    else
    {
        zp = exc.zp1;
        p  = exc.GS.rp2;
    }

    if (p >= zp.n_points)
    {
        if (exc.pedantic_hinting )
            exc.error = 134;
        ret.refp = 0;
        ret.err = 1;
        return ret;
    }

    zone.Copy(zp);
    ret.refp = p;

    var v1 = zp.cur[zp._offset_cur + p];
    var v2 = zp.org[zp._offset_org + p];
    var d = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);

    // TODO : unpatended
    ret.x = FT_MulDiv(d, exc.GS.freeVector.x, exc.F_dot_P);
    ret.y = FT_MulDiv(d, exc.GS.freeVector.y, exc.F_dot_P);

    return ret;
}

function TT_MulFix14(a, b)
{
    a = FT_Common.UintToInt(a);
    b = FT_Common.UintToInt(b);

    var sign = a ^ b;

    if (a < 0)
        a = -a;
    if (b < 0)
        b = -b;

    var ah = ((a >>> 16) & 0xFFFF);
    var al = (a & 0xFFFF);

    var lo = FT_Common.IntToUInt((al * b) & 0xFFFFFFFF);
    var mid = FT_Common.IntToUInt((ah * b) & 0xFFFFFFFF);
    var hi = mid >>> 16;
    mid = FT_Common.IntToUInt((mid << 16) & 0xFFFFFFFF) + (1 << 13); /* rounding */
    lo += mid;

    lo = FT_Common.IntToUInt(lo & 0xFFFFFFFF);

    if (lo < mid)
        hi += 1;

    mid = FT_Common.IntToUInt(((lo >>> 14) | (hi << 18)) & 0xFFFFFFFF);

    //console.log("(" + a + ", " + b + "): " + (sign >= 0 ? mid : -mid));

    return sign >= 0 ? mid : -mid;
}

function TT_DotFix14(ax, ay, bx, by)
{
    ax = FT_Common.UintToInt(ax);
    ay = FT_Common.UintToInt(ay);
    bx = FT_Common.UintToInt(bx);
    by = FT_Common.UintToInt(by);

    /* compute ax*bx as 64-bit value */
    var _l = ((ax & 0xFFFF) * bx) & 0xFFFFFFFF;
    var l = FT_Common.IntToUInt(_l);
    var m = ((ax >> 16) * bx) & 0xFFFFFFFF;

    var lo1 = l + FT_Common.IntToUInt((m << 16) & 0xFFFFFFFF);
    lo1 = FT_Common.IntToUInt(lo1 & 0xFFFFFFFF);

    var hi1 = (m >> 16) + (_l >> 31) + ((lo1 < l) ? 1 : 0);

    /* compute ay*by as 64-bit value */
    _l = ((ay & 0xFFFF) * by) & 0xFFFFFFFF;
    l = FT_Common.IntToUInt(_l);
    m = ((ay >> 16) * by) & 0xFFFFFFFF;

    var lo2 = l + FT_Common.IntToUInt(m << 16);
    lo2 = FT_Common.IntToUInt(lo2 & 0xFFFFFFFF);

    var hi2 = (m >> 16) + (_l >> 31) + ((lo2 < l) ? 1 : 0);

    /* add them */
    var lo = lo1 + lo2;
    lo = FT_Common.IntToUInt(lo & 0xFFFFFFFF);

    var hi = hi1 + hi2 + ((lo < lo1) ? 1 : 0);

    /* divide the result by 2^14 with rounding */
    var s   = hi >> 31;
    l  = lo + FT_Common.IntToUInt(s);
    l = FT_Common.IntToUInt(l & 0xFFFFFFFF);

    hi += s + ((l < lo) ? 1 : 0);
    lo  = l;

    l   = lo + 0x2000;
    l = FT_Common.IntToUInt(l & 0xFFFFFFFF);
    
    hi += ((l < lo) ? 1 : 0);

    //console.log("(" + ax + ", " + ay + ", " + bx + ", " + by + "): " + (((hi << 18) | (l >> 14)) & 0xFFFFFFFF));

    return ((hi << 18) | (l >> 14)) & 0xFFFFFFFF;
}

function Move_Zp2_Point(exc, point, dx, dy, touch)
{
    // TODO: unpatented
    if (exc.GS.freeVector.x != 0)
    {
        exc.zp2.cur[exc.zp2._offset_cur + point].x += dx;
        if (touch)
            exc.zp2.tags[exc.zp2._offset_tags + point] |= 8;
    }

    if (exc.GS.freeVector.y != 0)
    {
        exc.zp2.cur[exc.zp2._offset_cur + point].y += dy;
        if (touch)
            exc.zp2.tags[exc.zp2._offset_tags + point] |= 16;
    }
}
////////////////////////////////////////////////////////
function Ins_SVTCA(exc, args, args_pos)
{
    var A = 0;
    var B = 0x4000;
    if ((exc.opcode & 1) == 1)
    {
        A = 0x4000;
        B = 0;
    }

    exc.GS.freeVector.x = A;
    exc.GS.projVector.x = A;
    exc.GS.dualVector.x = A;

    exc.GS.freeVector.y = B;
    exc.GS.projVector.y = B;
    exc.GS.dualVector.y = B;

    Compute_Funcs(exc);
}

function Ins_SPVTCA(exc, args, args_pos)
{
    var A = 0;
    var B = 0x4000;
    if ((exc.opcode & 1) == 1)
    {
        A = 0x4000;
        B = 0;
    }

    exc.GS.projVector.x = A;
    exc.GS.dualVector.x = A;

    exc.GS.projVector.y = B;
    exc.GS.dualVector.y = B;

    GUESS_VECTOR(exc, exc.GS.freeVector);

    Compute_Funcs(exc);
}

function Ins_SFVTCA(exc, args, args_pos)
{
    var A = 0;
    var B = 0x4000;
    if ((exc.opcode & 1) == 1)
    {
        A = 0x4000;
        B = 0;
    }

    exc.GS.freeVector.x = A;
    exc.GS.freeVector.y = B;

    GUESS_VECTOR(exc, exc.projVector);

    Compute_Funcs(exc);
}

function Ins_SPVTL(exc, args, args_pos)
{
    if (Ins_SxVTL(exc, 0xFFFF & args[args_pos + 1], 0xFFFF & args[args_pos], exc.opcode, exc.GS.projVector) == 0)
    {
        exc.GS.dualVector.x = exc.GS.projVector.x;
        exc.GS.dualVector.y = exc.GS.projVector.y;
        GUESS_VECTOR(exc, exc.GS.freeVector);
        Compute_Funcs(exc);
    }
}

function Ins_SFVTL(exc, args, args_pos)
{
    if (Ins_SxVTL(exc, 0xFFFF & args[args_pos + 1], 0xFFFF & args[args_pos], exc.opcode, exc.GS.freeVector) == 0)
    {
        GUESS_VECTOR(exc, exc.GS.projVector );
        Compute_Funcs(exc);
    }
}

function Ins_SPVFS(exc, args, args_pos)
{
    /* Only use low 16bits, then sign extend */
    var Y = FT_Common.UShort_To_Short(0xFFFF & args[args_pos + 1]);
    var X = FT_Common.UShort_To_Short(0xFFFF & args[args_pos]);

    Normalize(exc, X, Y, exc.GS.projVector);

    exc.GS.dualVector.x = exc.GS.projVector.x;
    exc.GS.dualVector.y = exc.GS.projVector.y;
    GUESS_VECTOR(exc, exc.GS.freeVector );
    Compute_Funcs(exc);
}

function Ins_SFVFS(exc, args, args_pos)
{
    /* Only use low 16bits, then sign extend */
    var Y = FT_Common.UShort_To_Short(0xFFFF & args[args_pos + 1]);
    var X = FT_Common.UShort_To_Short(0xFFFF & args[args_pos]);

    Normalize(exc, X, Y, exc.GS.freeVector);
    GUESS_VECTOR(exc, exc.GS.projVector );
    Compute_Funcs(exc);
}

function Ins_GPV(exc, args, args_pos)
{
    // TODO: unpatented
    args[args_pos] = exc.GS.projVector.x;
    args[args_pos + 1] = exc.GS.projVector.y;
}

function Ins_GFV(exc, args, args_pos)
{
    // TODO: unpatented
    args[args_pos] = exc.GS.freeVector.x;
    args[args_pos + 1] = exc.GS.freeVector.y;
}

function Ins_SFVTPV(exc, args, args_pos)
{
    GUESS_VECTOR(exc, exc.GS.projVector);
    exc.GS.freeVector.x = exc.GS.projVector.x;
    exc.GS.freeVector.y = exc.GS.projVector.y;
    Compute_Funcs(exc);
}

function Ins_ISECT(exc, args, args_pos)
{
    var point = 0xFFFF & args[args_pos];

    var a0 = 0xFFFF & args[args_pos + 1];
    var a1 = 0xFFFF & args[args_pos + 2];
    var b0 = 0xFFFF & args[args_pos + 3];
    var b1 = 0xFFFF & args[args_pos + 4];

    if ((b0 >= exc.zp0.n_points)  ||
        (b1 >= exc.zp0.n_points)  ||
        (a0 >= exc.zp1.n_points)  ||
        (a1 >= exc.zp1.n_points)  ||
        (point >= exc.zp2.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    /* Cramer's rule */

    var off0 = exc.zp0._offset_cur;
    var off1 = exc.zp1._offset_cur;
    var off2 = exc.zp2._offset_cur;

    var dbx = exc.zp0.cur[off0+b1].x - exc.zp0.cur[off0+b0].x;
    var dby = exc.zp0.cur[off0+b1].y - exc.zp0.cur[off0+b0].y;

    var dax = exc.zp1.cur[off1+a1].x - exc.zp1.cur[off1+a0].x;
    var day = exc.zp1.cur[off1+a1].y - exc.zp1.cur[off1+a0].y;

    var dx = exc.zp0.cur[off0+b0].x - exc.zp1.cur[off1+a0].x;
    var dy = exc.zp0.cur[off0+b0].y - exc.zp1.cur[off1+a0].y;

    exc.zp2.tags[exc.zp2._offset_tags + point] |= 24;

    var discriminant = FT_MulDiv(dax, -dby, 0x40) + FT_MulDiv(day, dbx, 0x40);
    var dotproduct   = FT_MulDiv(dax, dbx, 0x40) + FT_MulDiv(day, dby, 0x40);

    /* The discriminant above is actually a cross product of vectors     */
    /* da and db. Together with the dot product, they can be used as     */
    /* surrogates for sine and cosine of the angle between the vectors.  */
    /* Indeed,                                                           */
    /*       dotproduct   = |da||db|cos(angle)                           */
    /*       discriminant = |da||db|sin(angle)     .                     */
    /* We use these equations to reject grazing intersections by         */
    /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */
    if (19 * Math.abs(discriminant) > Math.abs(dotproduct))
    {
        var val = FT_MulDiv(dx, -dby, 0x40) + FT_MulDiv(dy, dbx, 0x40);

        var __x = FT_MulDiv(val, dax, discriminant);
        var __y = FT_MulDiv(val, day, discriminant);

        exc.zp2.cur[off2 + point].x = exc.zp1.cur[off1 + a0].x + __x;
        exc.zp2.cur[off2 + point].y = exc.zp1.cur[off1 + a0].y + __y;
    }
    else
    {
        /* else, take the middle of the middles of A and B */
        exc.zp2.cur[off2 + point].x = (exc.zp1.cur[off1 + a0].x +
                               exc.zp1.cur[off1 + a1].x +
                               exc.zp0.cur[off0 + b0].x +
                               exc.zp0.cur[off0 + b1].x ) >> 2;
        exc.zp2.cur[off2 + point].y = (exc.zp1.cur[off1 + a0].y +
                               exc.zp1.cur[off1 + a1].y +
                               exc.zp0.cur[off0 + b0].y +
                               exc.zp0.cur[off0 + b1].y ) >> 2;
    }
}

function Ins_SRP0(exc, args, args_pos)
{
    exc.GS.rp0 = 0xFFFF & args[args_pos];
}

function Ins_SRP1(exc, args, args_pos)
{
    exc.GS.rp1 = 0xFFFF & args[args_pos];
}

function Ins_SRP2(exc, args, args_pos)
{
    exc.GS.rp2 = 0xFFFF & args[args_pos];
}

function Ins_SZP0(exc, args, args_pos)
{
    switch (args[args_pos])
    {
    case 0:
        exc.zp0.Copy(exc.twilight);
        break;

    case 1:
        exc.zp0.Copy(exc.pts);
        break;

    default:
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    exc.GS.gep0 = 0xFFFF & args[args_pos];
}

function Ins_SZP1(exc, args, args_pos)
{
    switch (args[args_pos])
    {
    case 0:
        exc.zp1.Copy(exc.twilight);
        break;

    case 1:
        exc.zp1.Copy(exc.pts);
        break;

    default:
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    exc.GS.gep1 = 0xFFFF & args[args_pos];
}

function Ins_SZP2(exc, args, args_pos)
{
    switch (args[args_pos])
    {
    case 0:
        exc.zp2.Copy(exc.twilight);
        break;

    case 1:
        exc.zp2.Copy(exc.pts);
        break;

    default:
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    exc.GS.gep2 = 0xFFFF & args[args_pos];
}

function Ins_SZPS(exc, args, args_pos)
{
    switch (args[args_pos])
    {
    case 0:
        exc.zp0.Copy(exc.twilight);
        break;

    case 1:
        exc.zp0.Copy(exc.pts);
        break;

    default:
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    exc.zp1.Copy(exc.zp0);
    exc.zp2.Copy(exc.zp0);

    exc.GS.gep0 = 0xFFFF & args[args_pos];
    exc.GS.gep1 = 0xFFFF & args[args_pos];
    exc.GS.gep2 = 0xFFFF & args[args_pos];
}

function Ins_SLOOP(exc, args, args_pos)
{
    if (args[args_pos] < 0)
        exc.error = 132;
    else
        exc.GS.loop = args[args_pos];
}

function Ins_RTG(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_To_Grid;
    exc.func_round = Round_To_Grid;
}

function Ins_RTHG(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_To_Half_Grid;
    exc.func_round = Round_To_Half_Grid;
}

function Ins_SMD(exc, args, args_pos)
{
    exc.GS.minimum_distance = args[args_pos];
}

function Ins_ELSE(exc, args, args_pos)
{
    var nIfs = 1;

    do
    {
        if (SkipCode(exc) == 1)
            return;

        switch (exc.opcode)
        {
        case 0x58:    /* IF */
            nIfs++;
            break;

        case 0x59:    /* EIF */
            nIfs--;
            break;
        }
    } while ( nIfs != 0 );
}

function Ins_JMPR(exc, args, args_pos)
{
    if (args[args_pos] == 0 && exc.args == 0)
        exc.error = 132;
    exc.IP += args[args_pos];
    if (exc.IP < 0 || (exc.callTop > 0 && exc.IP > exc.callStack[exc.callTop - 1].Def.end))
        exc.error = 132;
    exc.step_ins = 0;
}

function Ins_SCVTCI(exc, args, args_pos)
{
    exc.GS.control_value_cutin = args[args_pos];
}

function Ins_SSWCI(exc, args, args_pos)
{
    exc.GS.single_width_cutin = args[args_pos];
}

function Ins_SSW(exc, args, args_pos)
{
    exc.GS.single_width_value = FT_MulFix(args[args_pos], exc.tt_metrics.scale);
}

function Ins_DUP(exc, args, args_pos)
{
    args[args_pos + 1] = args[args_pos];
}

function Ins_POP(exc, args, args_pos)
{
}

function Ins_CLEAR(exc, args, args_pos)
{
    exc.new_top = 0;
}

function Ins_SWAP(exc, args, args_pos)
{
    var L = args[args_pos];
    args[args_pos] = args[args_pos + 1];
    args[args_pos + 1] = L;
}

function Ins_DEPTH(exc, args, args_pos)
{
    args[args_pos] = exc.top;
}

function Ins_CINDEX(exc, args, args_pos)
{
    var L = args[args_pos];

    if (L <= 0 || L > exc.args)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        args[args_pos] = 0;
    }
    else
        args[args_pos] = exc.stack[exc.args - L];
}

function Ins_MINDEX(exc, args, args_pos)
{
    var L = args[args_pos];

    if (L <= 0 || L > exc.args)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
    }
    else
    {
        var K = exc.stack[exc.args - L];

        var _i1 = exc.args - L;
        var _i2 = exc.args - 1;
        for (var i = _i1; i < _i2; i++)
            exc.stack[i] = exc.stack[i + 1];

        exc.stack[exc.args - 1] = K;
    }
}

function Ins_ALIGNPTS(exc, args, args_pos)
{
    var p1 = 0xFFFF & args[args_pos];
    var p2 = 0xFFFF & args[args_pos + 1];

    if ((p1 >= exc.zp1.n_points) || (p2 >= exc.zp0.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var v1 = exc.zp0.cur[exc.zp0._offset_cur + p2];
    var v2 = exc.zp1.cur[exc.zp1._offset_cur + p1];

    var distance = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y) >> 1;

    exc.func_move(exc, exc.zp1, p1, distance);
    exc.func_move(exc, exc.zp0, p2, -distance);
}

function Ins_UNKNOWN(exc, args, args_pos)
{
    var defs = exc.IDefs;
    var limit = exc.numIDefs;

    for (var def = 0; def < limit; def++)
    {
        if (defs[def].opc == exc.opcode && defs[def].active)
        {
            if (exc.callTop >= exc.callSize)
            {
                exc.error = 130;
                return;
            }

            var call = exc.callStack[exc.callTop++];

            call.Caller_Range = exc.curRange;
            call.Caller_IP    = exc.IP + 1;
            call.Cur_Count    = 1;
            call.Def = defs[def];

            Ins_Goto_CodeRange(defs[def].range, defs[def].start);

            exc.step_ins = false;
            return;
        }
    }

    exc.error = 128;
}
function Ins_UTP(exc, args, args_pos)
{
    var point = 0xFFFF & args[args_pos];

    if (point >= exc.zp0.n_points)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var mask = 0xFF;

    if (exc.GS.freeVector.x != 0)
        mask &= ~8;

    if (exc.GS.freeVector.y != 0)
        mask &= ~16;

    exc.zp0.tags[exc.zp0._offset_tags + point] &= mask;
}

function Ins_LOOPCALL(exc, args, args_pos)
{
    /* first of all, check the index */
    var F = args[args_pos + 1];
    if (F >= (exc.maxFunc + 1))
    {
        exc.error = 134;
        return;
    }

    /* Except for some old Apple fonts, all functions in a TrueType */
    /* font are defined in increasing order, starting from 0.  This */
    /* means that we normally have                                  */
    /*                                                              */
    /*    CUR.maxFunc+1 == CUR.numFDefs                             */
    /*    CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc             */
    /*                                                              */
    /* If this isn't true, we need to look up the function table.   */
    var defs = exc.FDefs;
    var def = F;
    if (exc.maxFunc + 1 != exc.numFDefs || defs[def].opc != F)
    {
        /* look up the FDefs table */
        def = 0;
        var limit = exc.numFDefs;

        while (def < limit && defs[def].opc != F)
            def++;

        if (def == limit)
        {
            exc.error = 134;
            return;
        }
    }

    /* check that the function is active */
    if (!defs[def].active)
    {
        exc.error = 134;
        return;
    }

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        if (exc.ignore_x_mode && (defs[def].sph_fdef_flags & 8))
            return;

        exc.sph_in_func_flags = defs[def].sph_fdef_flags;
    }

    /* check stack */
    if (exc.callTop >= exc.callSize)
    {
        exc.error = 130;
        return;
    }

    if (args[args_pos] > 0)
    {
        var pCrec = exc.callStack[exc.callTop];

        pCrec.Caller_Range = exc.curRange;
        pCrec.Caller_IP    = exc.IP + 1;
        pCrec.Cur_Count    = args[args_pos];
        pCrec.Def          = defs[def];

        exc.callTop++;

        Ins_Goto_CodeRange(exc, defs[def].range, defs[def].start);
        exc.step_ins = false;
    }
}

function Ins_CALL(exc, args, args_pos)
{
    /* first of all, check the index */
    var F = FT_Common.IntToUInt(args[args_pos]);
    if (F >= (exc.maxFunc + 1))
    {
        exc.error = 134;
        return;
    }

    /* Except for some old Apple fonts, all functions in a TrueType */
    /* font are defined in increasing order, starting from 0.  This */
    /* means that we normally have                                  */
    /*                                                              */
    /*    CUR.maxFunc+1 == CUR.numFDefs                             */
    /*    CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc             */
    /*                                                              */
    /* If this isn't true, we need to look up the function table.   */

    var defs = exc.FDefs;
    var def = F;
    if ((exc.maxFunc + 1) != exc.numFDefs || defs[def].opc != F)
    {
        /* look up the FDefs table */
        def   = 0;
        var limit = exc.numFDefs;

        while (def < limit && defs[def].opc != F)
            def++;

        if (def == limit)
        {
            exc.error = 134;
            return;
        }
    }

    /* check that the function is active */
    if (!defs[def].active)
    {
        exc.error = 134;
        return;
    }

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        if (exc.ignore_x_mode &&
            ((exc.iup_called && (exc.sph_tweak_flags & 1024)) ||
                defs[def].sph_fdef_flags & 8))
            return;

        exc.sph_in_func_flags = defs[def].sph_fdef_flags;
    }

    /* check the call stack */
    if (exc.callTop >= exc.callSize)
    {
        exc.error = 130;
        return;
    }

    var pCrec = exc.callStack[exc.callTop];

    pCrec.Caller_Range = exc.curRange;
    pCrec.Caller_IP    = exc.IP + 1;
    pCrec.Cur_Count    = 1;
    pCrec.Def = defs[def];

    exc.callTop++;

    Ins_Goto_CodeRange(exc, defs[def].range, defs[def].start);

    exc.step_ins = false;
}

function Ins_FDEF(exc, args, args_pos)
{
    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    var opcode_pattern = null;
    var opcode_patterns = null;
    var opcode_pointer = null;
    var opcode_size    = null;;
    if (bIsSubpix)
    {
        opcode_pattern = [
            /* #0 inline delta function 1 */
            [
                0x4B, /* PPEM    */
                0x53, /* GTEQ    */
                0x23, /* SWAP    */
                0x4B, /* PPEM    */
                0x51, /* LTEQ    */
                0x5A, /* AND     */
                0x58, /* IF      */
                0x38, /*   SHPIX */
                0x1B, /* ELSE    */
                0x21, /*   POP   */
                0x21, /*   POP   */
                0x59  /* EIF     */
            ],
            /* #1 inline delta function 2 */
            [
                0x4B, /* PPEM    */
                0x54, /* EQ      */
                0x58, /* IF      */
                0x38, /*   SHPIX */
                0x1B, /* ELSE    */
                0x21, /*   POP   */
                0x21, /*   POP   */
                0x59  /* EIF     */
            ],
            /* #2 diagonal stroke function */
            [
                0x20, /* DUP     */
                0x20, /* DUP     */
                0xB0, /* PUSHB_1 */
                /*   1     */
                0x60, /* ADD     */
                0x46, /* GC_cur  */
                0xB0, /* PUSHB_1 */
                /*   64    */
                0x23, /* SWAP    */
                0x42  /* WS      */
            ],
            /* #3 VacuFormRound function */
            [
                0x45, /* RCVT    */
                0x23, /* SWAP    */
                0x46, /* GC_cur  */
                0x60, /* ADD     */
                0x20, /* DUP     */
                0xB0  /* PUSHB_1 */
                /*   38    */
            ],
            /* #4 TTFautohint bytecode (old) */
            [
                0x20, /* DUP     */
                0x64, /* ABS     */
                0xB0, /* PUSHB_1 */
                /*   32    */
                0x60, /* ADD     */
                0x66, /* FLOOR   */
                0x23, /* SWAP    */
                0xB0  /* PUSHB_1 */
            ],
            /* #5 spacing function 1 */
            [
                0x01, /* SVTCA_x */
                0xB0, /* PUSHB_1 */
                /*   24    */
                0x43, /* RS      */
                0x58  /* IF      */
            ],
            /* #6 spacing function 2 */
            [
                0x01, /* SVTCA_x */
                0x18, /* RTG     */
                0xB0, /* PUSHB_1 */
                /*   24    */
                0x43, /* RS      */
                0x58  /* IF      */
            ],
            /* #7 TypeMan Talk DiagEndCtrl function */
            [
                0x01, /* SVTCA_x */
                0x20, /* DUP     */
                0xB0, /* PUSHB_1 */
                /*   3     */
                0x25, /* CINDEX  */
            ],
            /* #8 TypeMan Talk Align */
            [
                0x06, /* SPVTL   */
                0x7D, /* RDTG    */
            ]
        ];
        opcode_patterns = opcode_pattern.length;
        opcode_pointer = [  0, 0, 0, 0, 0, 0, 0, 0, 0 ];
        opcode_size    = [ 12, 8, 8, 6, 7, 4, 5, 4, 2 ];
    }

    /* some font programs are broken enough to redefine functions! */
    /* We will then parse the current table.                       */
    var defs = exc.FDefs;
    var rec = 0;
    var limit = exc.numFDefs;
    var n = args[args_pos];

    for ( ; rec < limit; rec++)
    {
        if (defs[rec].opc == n)
            break;
    }

    if (rec == limit)
    {
        /* check that there is enough room for new functions */
        if (exc.numFDefs >= exc.maxFDefs)
        {
            exc.error = 140;
            return;
        }
        exc.numFDefs++;
    }

    /* Although FDEF takes unsigned 32-bit integer,  */
    /* func # must be within unsigned 16-bit integer */
    if (n > 0xFFFF)
    {
        exc.error = 140;
        return;
    }

    defs[rec].range        = exc.curRange;
    defs[rec].opc          = 0xFFFF & n;
    defs[rec].start        = exc.IP + 1;
    defs[rec].active       = true;
    defs[rec].inline_delta = false;
    defs[rec].sph_fdef_flags = 0x0000;

    if (n > exc.maxFunc)
        exc.maxFunc = 0xFFFF & n;

    if (bIsSubpix)
    {
        /* We don't know for sure these are typeman functions, */
        /* however they are only active when RS 22 is called   */
        if (n >= 64 && n <= 66)
            defs[rec].sph_fdef_flags |= 128;
    }

    /* Now skip the whole function definition. */
    /* We don't allow nested IDEFS & FDEFs.    */

    while (SkipCode(exc) == 0)
    {
        if (bIsSubpix)//#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
        {
            for (var i = 0; i < opcode_patterns; i++)
            {
                if (opcode_pointer[i] < opcode_size[i] && exc.opcode == opcode_pattern[i][opcode_pointer[i]])
                {
                    opcode_pointer[i] += 1;

                    if (opcode_pointer[i] == opcode_size[i])
                    {
                        switch ( i )
                        {
                            case 0:
                                defs[rec].sph_fdef_flags        |= 1;
                                exc.face.sph_found_func_flags   |= 1;
                                break;

                            case 1:
                                defs[rec].sph_fdef_flags        |= 2;
                                exc.face.sph_found_func_flags   |= 2;
                                break;

                            case 2:
                                switch ( n )
                                {
                                    /* needs to be implemented still */
                                    case 58:
                                        defs[rec].sph_fdef_flags        |= 4;
                                        exc.face.sph_found_func_flags   |= 4;
                                }
                                break;

                            case 3:
                                switch ( n )
                                {
                                    case 0:
                                        defs[rec].sph_fdef_flags        |= 8;
                                        exc.face.sph_found_func_flags   |= 8;
                                }
                                break;

                            case 4:
                                /* probably not necessary to detect anymore */
                                defs[rec].sph_fdef_flags            |= 16;
                                exc.face.sph_found_func_flags       |= 16;
                                break;

                            case 5:
                                switch ( n )
                                {
                                    case 0:
                                    case 1:
                                    case 2:
                                    case 4:
                                    case 7:
                                    case 8:
                                        defs[rec].sph_fdef_flags        |= 32;
                                        exc.face.sph_found_func_flags   |= 32;
                                }
                                break;

                            case 6:
                                switch ( n )
                                {
                                    case 0:
                                    case 1:
                                    case 2:
                                    case 4:
                                    case 7:
                                    case 8:
                                        defs[rec].sph_fdef_flags        |= 64;
                                        exc.face.sph_found_func_flags   |= 64;
                                }
                                break;

                            case 7:
                                defs[rec].sph_fdef_flags        |= 256;
                                exc.face.sph_found_func_flags   |= 256;
                                break;

                            case 8:
                                //defs[rec].sph_fdef_flags        |= 256;
                                //exc.face.sph_found_func_flags   |= 256;
                                break;
                        }
                        opcode_pointer[i] = 0;
                    }
                }
                else
                    opcode_pointer[i] = 0;
            }

            /* Set sph_compatibility_mode only when deltas are detected */
            exc.face.sph_compatibility_mode = ((exc.face.sph_found_func_flags & 1) |
                                                (exc.face.sph_found_func_flags & 2));
        } //#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

        switch (exc.opcode)
        {
        case 0x89:    /* IDEF */
        case 0x2C:    /* FDEF */
            exc.error = 137;
            return;
        case 0x2D:   /* ENDF */
            defs[rec].end = exc.IP;
            return;
        default:
            break;
        }
    }
}

function Ins_ENDF(exc, args, args_pos)
{
    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;
    if (bIsSubpix)
        exc.sph_in_func_flags = 0x0000;

    if (exc.callTop <= 0)     /* We encountered an ENDF without a call */
    {
        exc.error = 136;
        return;
    }

    exc.callTop--;
    var pRec = exc.callStack[exc.callTop];

    pRec.Cur_Count--;
    exc.step_ins = false;

    if (pRec.Cur_Count > 0)
    {
        exc.callTop++;
        exc.IP = pRec.Def.start;
    }
    else
    {
        /* Loop through the current function */
        Ins_Goto_CodeRange(exc, pRec.Caller_Range, pRec.Caller_IP);
    }

    /* Exit the current call frame.                      */

    /* NOTE: If the last instruction of a program is a   */
    /*       CALL or LOOPCALL, the return address is     */
    /*       always out of the code range.  This is a    */
    /*       valid address, and it is why we do not test */
    /*       the result of Ins_Goto_CodeRange() here!    */
}

function Ins_MDAP(exc, args, args_pos)
{
    var point = 0xFFFF & args[args_pos];

    if (point >= exc.zp0.n_points)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var distance = 0;
    if ((exc.opcode & 1) != 0)
    {
        var cur_dist = exc.func_project(exc, exc.zp0.cur[exc.zp0._offset_cur + point].x, exc.zp0.cur[exc.zp0._offset_cur + point].y);
        var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

        if (bIsSubpix)
        {
            if (exc.ignore_x_mode && exc.GS.freeVector.x != 0)
            {
                distance = Round_None(exc, cur_dist, exc.tt_metrics.compensations[0]) - cur_dist;
            }
            else
            {
                distance = exc.func_round(exc, cur_dist, exc.tt_metrics.compensations[0]) - cur_dist;
            }
        }
        else
        {
            distance = exc.func_round(exc, cur_dist, exc.tt_metrics.compensations[0]) - cur_dist;
        }
    }

    exc.func_move(exc, exc.zp0, point, distance);

    exc.GS.rp0 = point;
    exc.GS.rp1 = point;
}

// --
function IUP_WorkerRec()
{
    this.orgs = null;   /* original and current coordinate */
    this.curs = null;   /* arrays                          */
    this.orus = null;

    this.off_orgs = 0;
    this.off_curs = 0;
    this.off_orus = 0;

    this.max_points = 0;
    this.is_offset_one = false;
}

function _iup_worker_shift(worker, p1, p2, p)
{
    if (!worker.is_offset_one)
    {
        var dx = worker.curs[worker.off_curs + p].x - worker.orgs[worker.off_orgs + p].x;
        if (dx != 0)
        {
            for (var i = p1; i < p; i++)
                worker.curs[worker.off_curs + i].x += dx;

            for (var i = p + 1; i <= p2; i++)
                worker.curs[worker.off_curs + i].x += dx;
        }
    }
    else
    {
        var dy = worker.curs[worker.off_curs + p].y - worker.orgs[worker.off_orgs + p].y;
        if (dy != 0)
        {
            for (var i = p1; i < p; i++)
                worker.curs[worker.off_curs + i].y += dy;

            for (var i = p + 1; i <= p2; i++)
                worker.curs[worker.off_curs + i].y += dy;
        }
    }
}

function _iup_worker_interpolate(worker, p1, p2, ref1, ref2)
{
    if (!worker.is_offset_one)
    {
        if (p1 > p2)
            return;

        if ((ref1 >= worker.max_points) || (ref2 >= worker.max_points))
            return;

        var orus1 = worker.orus[worker.off_orus + ref1].x;
        var orus2 = worker.orus[worker.off_orus + ref2].x;

        if (orus1 > orus2)
        {
            var tmp_o = orus1;
            orus1 = orus2;
            orus2 = tmp_o;

            var tmp_r = ref1;
            ref1  = ref2;
            ref2  = tmp_r;
        }

        var org1   = worker.orgs[worker.off_orgs + ref1].x;
        var org2   = worker.orgs[worker.off_orgs + ref2].x;
        var delta1 = worker.curs[worker.off_curs + ref1].x - org1;
        var delta2 = worker.curs[worker.off_curs + ref2].x - org2;

        if (orus1 == orus2)
        {
            /* simple shift of untouched points */
            for (var i = p1; i <= p2; i++)
            {
                var x = worker.orgs[worker.off_orgs + i].x;
                if ( x <= org1 )
                    x += delta1;
                else
                    x += delta2;

                worker.curs[worker.off_curs + i].x = x;
            }
        }
        else
        {
            var scale = 0;
            var scale_valid = 0;

            /* interpolation */
            for (var i = p1; i <= p2; i++)
            {
                var x = worker.orgs[worker.off_orgs + i].x;

                if ( x <= org1 )
                    x += delta1;
                else if ( x >= org2 )
                    x += delta2;
                else
                {
                    if ( !scale_valid )
                    {
                        scale_valid = 1;
                        scale = FT_DivFix(org2 + delta2 - (org1 + delta1), orus2 - orus1);
                    }

                    x = (org1 + delta1) + FT_MulFix(worker.orus[worker.off_orus + i].x - orus1, scale);
                }
                worker.curs[worker.off_curs + i].x = x;
            }
        }
    }
    else
    {
        if (p1 > p2)
            return;

        if ((ref1 >= worker.max_points) || (ref2 >= worker.max_points))
            return;

        var orus1 = worker.orus[worker.off_orus + ref1].y;
        var orus2 = worker.orus[worker.off_orus + ref2].y;

        if (orus1 > orus2)
        {
            var tmp_o = orus1;
            orus1 = orus2;
            orus2 = tmp_o;

            var tmp_r = ref1;
            ref1  = ref2;
            ref2  = tmp_r;
        }

        var org1   = worker.orgs[worker.off_orgs + ref1].y;
        var org2   = worker.orgs[worker.off_orgs + ref2].y;
        var delta1 = worker.curs[worker.off_curs + ref1].y - org1;
        var delta2 = worker.curs[worker.off_curs + ref2].y - org2;

        if (orus1 == orus2)
        {
            /* simple shift of untouched points */
            for (var i = p1; i <= p2; i++)
            {
                var y = worker.orgs[worker.off_orgs + i].y;
                if (y <= org1)
                    y += delta1;
                else
                    y += delta2;

                worker.curs[worker.off_curs + i].y = y;
            }
        }
        else
        {
            var scale = 0;
            var scale_valid = 0;

            /* interpolation */
            for (var i = p1; i <= p2; i++)
            {
                var y = worker.orgs[worker.off_orgs + i].y;

                if (y <= org1)
                    y += delta1;
                else if (y >= org2)
                    y += delta2;
                else
                {
                    if ( !scale_valid )
                    {
                        scale_valid = 1;
                        scale = FT_DivFix(org2 + delta2 - (org1 + delta1), orus2 - orus1);
                    }

                    y = (org1 + delta1) + FT_MulFix(worker.orus[worker.off_orus + i].y - orus1, scale);
                }
                worker.curs[worker.off_curs + i].y = y;
            }
        }
    }
}
// --

function Ins_IUP(exc, args, args_pos)
{
    var mask;

    /* ignore empty outlines */
    if (exc.pts.n_contours == 0)
        return;

    var V = new IUP_WorkerRec();
    if (exc.opcode & 1)
    {
        mask   = 8;
        V.orgs = exc.pts.org;
        V.curs = exc.pts.cur;
        V.orus = exc.pts.orus;

        V.off_orgs = exc.pts._offset_org;
        V.off_curs = exc.pts._offset_cur;
        V.off_orus = exc.pts._offset_orus;
    }
    else
    {
        mask = 16;

        V.orgs = exc.pts.org;
        V.curs = exc.pts.cur;
        V.orus = exc.pts.orus;

        V.off_orgs = exc.pts._offset_org;
        V.off_curs = exc.pts._offset_cur;
        V.off_orus = exc.pts._offset_orus;
        V.is_offset_one = true;
    }
    V.max_points = exc.pts.n_points;

    var contour = 0;
    var point   = 0;

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING) // #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode)
        {
            exc.iup_called = 1;
            if (exc.sph_tweak_flags & 32768)
                return;
        }
    } //#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

    do
    {
        var end_point   = exc.pts.contours[exc.pts._offset_contours + contour] - exc.pts.first_point;
        var first_point = point;

        if (end_point >= exc.pts.n_points)
            end_point = exc.pts.n_points - 1;

        while (point <= end_point && (exc.pts.tags[exc.pts._offset_tags + point] & mask) == 0)
            point++;

        if ( point <= end_point )
        {
            var first_touched = point;
            var cur_touched   = point;

            point++;

            while ( point <= end_point )
            {
                if ((exc.pts.tags[exc.pts._offset_tags + point] & mask) != 0)
                {
                    _iup_worker_interpolate(V, cur_touched + 1, point - 1, cur_touched, point);
                    cur_touched = point;
                }

                point++;
            }

            if (cur_touched == first_touched)
                _iup_worker_shift(V, first_point, end_point, cur_touched);
            else
            {
                _iup_worker_interpolate(V, 0xFFFF & (cur_touched + 1), end_point, cur_touched, first_touched);

                if (first_touched > 0)
                    _iup_worker_interpolate(V, first_point, first_touched - 1, cur_touched, first_touched);
            }
        }
        contour++;
    } while (contour < exc.pts.n_contours);
}

function Ins_SHP(exc, args, args_pos)
{
    if (exc.top < exc.GS.loop)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
    }

    var zp = new TT_GlyphZoneRec();

    var dx, dy, refp;
    var ret = Compute_Point_Displacement(exc, dx, dy, zp, refp);
    if (ret.err == 1)
        return;

    dx = ret.x;
    dy = ret.y;
    refp = ret.refp;
    while (exc.GS.loop > 0)
    {
        exc.args--;
        var point = 0xFFFF & exc.stack[exc.args];

        if (point >= exc.zp2.n_points)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
        }
        else
        {
            if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
            {
                if (exc.ignore_x_mode)
                    Move_Zp2_Point(exc, point, 0, dy, true);
                else
                    Move_Zp2_Point(exc, point, dx, dy, true);
            }
            else
                Move_Zp2_Point(exc, point, dx, dy, true);
        }

        exc.GS.loop--;
    }

    exc.GS.loop = 1;
    exc.new_top = exc.args;
}

function Ins_SHC(exc, args, args_pos)
{
    var contour = 0xFFFF & args[args_pos];
    var bounds  = (exc.GS.gep2 == 0) ? 1 : exc.zp2.n_contours;

    if (contour >= bounds)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var zp = new TT_GlyphZoneRec();

    var dx, dy, refp;
    var ret = Compute_Point_Displacement(exc, dx, dy, zp, refp);
    if (ret.err == 1)
        return;

    dx = ret.x;
    dy = ret.y;
    refp = ret.refp;

    var start, limit;
    if (contour == 0)
        start = 0;
    else
        start = 0xFFFF & (exc.zp2.contours[exc.zp2._offset_contours + contour - 1] + 1 - exc.zp2.first_point);

    /* we use the number of points if in the twilight zone */
    if (exc.GS.gep2 == 0)
        limit = exc.zp2.n_points;
    else
        limit = 0xFFFF & (exc.zp2.contours[exc.zp2._offset_contours + contour] - exc.zp2.first_point + 1);

    for (var i = start; i < limit; i++)
    {
        if ((zp.cur != exc.zp2.cur || zp._offset_cur != exc.zp2._offset_cur) || refp != i)
            Move_Zp2_Point(exc, i, dx, dy, true);
    }
}

function Ins_SHZ(exc, args, args_pos)
{
    if (args[args_pos] >= 2)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var zp = new TT_GlyphZoneRec();

    var dx, dy, refp;
    var ret = Compute_Point_Displacement(exc, dx, dy, zp, refp);
    if (ret.err == 1)
        return;

    dx = ret.x;
    dy = ret.y;
    refp = ret.refp;

    /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points.     */
    /*      Twilight zone has no real contours, so use `n_points'. */
    /*      Normal zone's `n_points' includes phantoms, so must    */
    /*      use end of last contour.                               */
    var limit = 0;
    if (exc.GS.gep2 == 0)
        limit = 0xFFFF & exc.zp2.n_points;
    else if (exc.GS.gep2 == 1 && exc.zp2.n_contours > 0)
        limit = 0xFFFF & (exc.zp2.contours[exc.zp2._offset_contours + exc.zp2.n_contours - 1] + 1);

    /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
    for (var i = 0; i < limit; i++)
    {
        if ((zp.cur != exc.zp2.cur || zp._offset_cur != exc.zp2._offset_cur) || refp != i)
            Move_Zp2_Point(exc, i, dx, dy, false);
    }
}

function Ins_SHPIX(exc, args, args_pos)
{
    if (exc.top < (exc.GS.loop + 1))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
        return;
    }

    // TODO: unpatented
    var dx = TT_MulFix14(args[args_pos], exc.GS.freeVector.x);
    var dy = TT_MulFix14(args[args_pos], exc.GS.freeVector.y);

    while (exc.GS.loop > 0)
    {
        exc.args--;
        var point = 0xFFFF & exc.stack[exc.args];

        if (point >= exc.zp2.n_points)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
        }
        else
        {
            if (!exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
            {
                Move_Zp2_Point(exc, point, dx, dy, true);
                exc.GS.loop -= 1;
                continue;
            }

            var B1, B2;
            if (exc.ignore_x_mode)
            {
                /* save point for later comparison */
                if (exc.GS.freeVector.y != 0)
                    B1 = exc.zp2.cur[exc.zp2._offset_cur + point].y;
                else
                    B1 = exc.zp2.cur[exc.zp2._offset_cur + point].x;

                if (!exc.face.sph_compatibility_mode && exc.GS.freeVector.y != 0)
                {
                    Move_Zp2_Point(exc, point, dx, dy, true);

                    /* save new point */
                    if (exc.GS.freeVector.y != 0)
                    {
                        B2 = exc.zp2.cur[exc.zp2._offset_cur + point].y;

                        /* reverse any disallowed moves */
                        if ((exc.sph_tweak_flags & 65536) &&
                            (B1 & 63) != 0 &&
                            (B2 & 63) != 0 &&
                            B1 != B2)
                            Move_Zp2_Point(exc, point, -dx, -dy, true);
                    }
                }
                else if (exc.face.sph_compatibility_mode)
                {
                    if (exc.sph_tweak_flags & 16384)
                    {
                        dx = FT_PIX_ROUND(B1 + dx) - B1;
                        dy = FT_PIX_ROUND(B1 + dy) - B1;
                    }

                    /* skip post-iup deltas */
                    if (exc.iup_called &&
                        ((exc.sph_in_func_flags & 1) ||
                        (exc.sph_in_func_flags & 2)))
                    {
                        exc.GS.loop -= 1;
                        continue;
                    }

                    if (!(exc.sph_tweak_flags & 4) &&
                        ((exc.is_composite && exc.GS.freeVector.y != 0) ||
                        (exc.zp2.tags[exc.zp2._offset_tags + point] & 16) ||
                        (exc.sph_tweak_flags & 32)))
                        Move_Zp2_Point(exc, point, 0, dy, true);

                    /* save new point */
                    if (exc.GS.freeVector.y != 0)
                    {
                        B2 = exc.zp2.cur[exc.zp2._offset_cur + point].y;

                        /* reverse any disallowed moves */
                        if ((B1 & 63) == 0 &&
                            (B2 & 63) != 0 &&
                            B1 != B2)
                            Move_Zp2_Point(exc, point, 0, -dy, true);
                    }
                }
                else if (exc.sph_in_func_flags & 256)
                    Move_Zp2_Point(exc, point, dx, dy, true);
            }
            else
                Move_Zp2_Point(exc, point, dx, dy, true);

            exc.GS.loop--;
        }
    }

    exc.GS.loop = 1;
    exc.new_top = exc.args;
}

function Ins_IP(exc, args, args_pos)
{
    if (exc.top < exc.GS.loop)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
        return;
    }

    /*
     * We need to deal in a special way with the twilight zone.
     * Otherwise, by definition, the value of CUR.twilight.orus[n] is (0,0),
     * for every n.
     */
    var twilight = (exc.GS.gep0 == 0 || exc.GS.gep1 == 0 || exc.GS.gep2 == 0) ? 1 : 0;

    if (exc.GS.rp1 >= exc.zp0.n_points)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
        return;
    }

    var orus_base = null;
    var orus_base_off = 0;
    if (twilight)
    {
        orus_base = exc.zp0.org;
        orus_base_off = exc.zp0._offset_org + exc.GS.rp1;
    }
    else
    {
        orus_base = exc.zp0.orus;
        orus_base_off = exc.zp0._offset_orus + exc.GS.rp1;
    }

    var cur_base = exc.zp0.cur;
    var cur_base_off = exc.zp0._offset_cur + exc.GS.rp1;

    var old_range = 0;
    var cur_range = 0;

    /* XXX: There are some glyphs in some braindead but popular */
    /*      fonts out there (e.g. [aeu]grave in monotype.ttf)   */
    /*      calling IP[] with bad values of rp[12].             */
    /*      Do something sane when this odd thing happens.      */
    if ((exc.GS.rp1 >= exc.zp0.n_points) || (exc.GS.rp2 >= exc.zp1.n_points))
    {
        old_range = 0;
        cur_range = 0;
    }
    else
    {
        var v1, v2;
        if (twilight != 0)
        {
            v1 = exc.zp1.org[exc.zp1._offset_org + exc.GS.rp2];
            v2 = orus_base[orus_base_off];
            old_range = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
        }
        else if (exc.metrics.x_scale == exc.metrics.y_scale)
        {
            v1 = exc.zp1.orus[exc.zp1._offset_orus + exc.GS.rp2];
            v2 = orus_base[orus_base_off];
            old_range = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
        }
        else
        {
            v1 = exc.zp1.orus[exc.zp1._offset_orus + exc.GS.rp2];
            v2 = orus_base[orus_base_off];

            var _x = FT_MulFix(v1.x - v2.x, exc.metrics.x_scale);
            var _y = FT_MulFix(v1.y - v2.y, exc.metrics.y_scale);

            old_range = exc.func_dualproj(exc, _x, _y);
        }

        v1 = exc.zp1.cur[exc.zp1._offset_cur + exc.GS.rp2];
        v2 = cur_base[cur_base_off];

        cur_range = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);
    }

    for ( ; exc.GS.loop > 0; --exc.GS.loop )
    {
        var point = exc.stack[--exc.args];
        var org_dist, cur_dist, new_dist;

        /* check point bounds */
        if (point >= exc.zp2.n_points)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
            continue;
        }

        var v1, v2;
        if (twilight)
        {
            v1 = exc.zp2.org[exc.zp2._offset_org + point];
            v2 = orus_base[orus_base_off];
            org_dist = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
        }
        else if ( exc.metrics.x_scale == exc.metrics.y_scale )
        {
            v1 = exc.zp2.orus[exc.zp2._offset_orus + point];
            v2 = orus_base[orus_base_off];
            org_dist = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
        }
        else
        {
            v1 = exc.zp2.orus[exc.zp2._offset_orus + point];
            v2 = orus_base[orus_base_off];

            var _x = FT_MulFix(v1.x - v2.x, exc.metrics.x_scale);
            var _y = FT_MulFix(v1.y - v2.y, exc.metrics.y_scale);

            org_dist = exc.func_dualproj(exc, _x, _y);
        }

        v1 = exc.zp2.cur[exc.zp2._offset_cur + point];
        v2 = cur_base[cur_base_off];
        cur_dist = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);

        if (org_dist != 0)
        {
            if (old_range != 0)
                new_dist = FT_MulDiv(org_dist, cur_range, old_range);
            else
            {
                /* This is the same as what MS does for the invalid case:  */
                /*                                                         */
                /*   delta = (Original_Pt - Original_RP1) -                */
                /*           (Current_Pt - Current_RP1)                    */
                /*                                                         */
                /* In FreeType speak:                                      */
                /*                                                         */
                /*   new_dist = cur_dist -                                 */
                /*              org_dist - cur_dist;                       */

                new_dist = -org_dist;
            }
        }
        else
            new_dist = 0;

        exc.func_move(exc, exc.zp2, 0xFFFF & point, new_dist - cur_dist);
    }

    exc.GS.loop = 1;
    exc.new_top = exc.args;
}

function Ins_MSIRP(exc, args, args_pos)
{
    var control_value_cutin = 0;

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        control_value_cutin = exc.GS.control_value_cutin;
        if (exc.ignore_x_mode && exc.GS.freeVector.x != 0 && ((exc.sph_tweak_flags & 256) == 0))
            control_value_cutin = 0;
    }

    var point = 0xFFFF & args[args_pos];

    if ((point >= exc.zp1.n_points) || (exc.GS.rp0 >= exc.zp0.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    /* UNDOCUMENTED!  The MS rasterizer does that with */
    /* twilight points (confirmed by Greg Hitchcock)   */
    if (exc.GS.gep1 == 0)
    {
        copy_vector(exc.zp1.org[exc.zp1._offset_org + point], exc.zp0.org[exc.zp0._offset_org + exc.GS.rp0]);
        exc.func_move_orig(exc, exc.zp1, point, args[args_pos + 1]);
        copy_vector(exc.zp1.cur[exc.zp1._offset_cur + point], exc.zp1.org[exc.zp1._offset_org + point]);
    }

    var v1 = exc.zp1.cur[exc.zp1._offset_cur + point];
    var v2 = exc.zp0.cur[exc.zp0._offset_cur + exc.GS.rp0];
    var distance = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING) // #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode && exc.GS.freeVector.x != 0 && (Math.abs(distance - args[args_pos + 1]) >= control_value_cutin))
            distance = args[args_pos + 1];
    } // #endif

    exc.func_move(exc, exc.zp1, point, args[args_pos + 1] - distance);

    exc.GS.rp1 = exc.GS.rp0;
    exc.GS.rp2 = point;

    if ((exc.opcode & 1 ) != 0)
        exc.GS.rp0 = point;
}

function Ins_ALIGNRP(exc, args, args_pos)
{
    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)//#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode && exc.iup_called && (exc.sph_tweak_flags & 512) != 0)
        {
            exc.error = 134;
            exc.GS.loop = 1;
            exc.new_top = exc.args;
            return;
        }
    } // #endif

    if (exc.top < exc.GS.loop || (exc.GS.rp0 >= exc.zp0.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
        return;
    }

    while (exc.GS.loop > 0)
    {
        exc.args--;
        var point = 0xFFFF & exc.stack[exc.args];

        if (point >= exc.zp1.n_points)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
        }
        else
        {
            var v1 = exc.zp1.cur[exc.zp1._offset_cur + point];
            var v2 = exc.zp0.cur[exc.zp0._offset_cur + exc.GS.rp0];
            var distance = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);

            exc.func_move(exc, exc.zp1, point, -distance);
        }
        exc.GS.loop--;
    }

    exc.GS.loop = 1;
    exc.new_top = exc.args;
}

function Ins_RTDG(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_To_Double_Grid;
    exc.func_round = Round_To_Double_Grid;
}

function Ins_MIAP(exc, args, args_pos)
{
    var control_value_cutin = exc.GS.control_value_cutin;
    var cvtEntry            = FT_Common.IntToUInt(args[args_pos + 1]);
    var point               = 0xFFFF & args[args_pos];

    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;
    if (bIsSubpix) // #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode && exc.GS.freeVector.x != 0 && exc.GS.freeVector.y == 0 && (exc.sph_tweak_flags & 256) == 0)
            control_value_cutin = 0;
    } // #endif

    if ((point >= exc.zp0.n_points) || (cvtEntry >= exc.cvtSize))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.rp0 = point;
        exc.GS.rp1 = point;
        return;
    }

    /* UNDOCUMENTED!                                                      */
    /*                                                                    */
    /* The behaviour of an MIAP instruction is quite different when used  */
    /* in the twilight zone.                                              */
    /*                                                                    */
    /* First, no control value cut-in test is performed as it would fail  */
    /* anyway.  Second, the original point, i.e. (org_x,org_y) of         */
    /* zp0.point, is set to the absolute, unrounded distance found in the */
    /* CVT.                                                               */
    /*                                                                    */
    /* This is used in the CVT programs of the Microsoft fonts Arial,     */
    /* Times, etc., in order to re-adjust some key font heights.  It      */
    /* allows the use of the IP instruction in the twilight zone, which   */
    /* otherwise would be invalid according to the specification.         */
    /*                                                                    */
    /* We implement it with a special sequence for the twilight zone.     */
    /* This is a bad hack, but it seems to work.                          */
    /*                                                                    */
    /* Confirmed by Greg Hitchcock.                                       */

    var distance = exc.func_read_cvt(exc, cvtEntry);

    if (exc.GS.gep0 == 0)   /* If in twilight zone */
    {
        if (!bIsSubpix)
        {
            exc.zp0.org[exc.zp0._offset_org + point].x = TT_MulFix14(distance, exc.GS.freeVector.x);
        }

        exc.zp0.org[exc.zp0._offset_org + point].y = TT_MulFix14(distance, exc.GS.freeVector.y),
        exc.zp0.cur[exc.zp0._offset_cur + point].x = exc.zp0.org[exc.zp0._offset_org + point].x;
        exc.zp0.cur[exc.zp0._offset_cur + point].y = exc.zp0.org[exc.zp0._offset_org + point].y;
    }

    if (bIsSubpix)
    {
        if (exc.ignore_x_mode && (exc.sph_tweak_flags & 128) != 0 && distance > 0 && exc.GS.freeVector.y != 0)
            distance = 0;
    }

    var v = exc.zp0.cur[exc.zp0._offset_cur + point];
    var org_dist = exc.func_project(exc, v.x, v.y);

    if ((exc.opcode & 1) != 0)   /* rounding and control cut-in flag */
    {
        if (Math.abs(distance - org_dist) > control_value_cutin)
            distance = org_dist;

        if (bIsSubpix)
        {
            if (exc.ignore_x_mode && exc.GS.freeVector.x != 0)
                distance = Round_None(exc, distance, exc.tt_metrics.compensations[0]);
            else
                distance = exc.func_round(exc, distance, exc.tt_metrics.compensations[0]);
        }
        else
        {
            distance = exc.func_round(exc, distance, exc.tt_metrics.compensations[0]);
        }
    }

    exc.func_move(exc, exc.zp0, point, distance - org_dist);
    
    exc.GS.rp0 = point;
    exc.GS.rp1 = point;
}

function Ins_NPUSHB(exc, args, args_pos)
{
    var L = exc.code.data[exc.code.pos + exc.IP + 1];

    if (L >= (exc.stackSize + 1 - exc.top))
    {
        exc.error = 130;
        return;
    }

    for (var K = 1; K <= L; K++)
        args[args_pos + K - 1] = exc.code.data[exc.code.pos + exc.IP + K + 1];

    exc.new_top += L;
}

// --
function GetShortIns(exc)
{
    /* Reading a byte stream so there is no endianess (DaveP) */
    exc.IP += 2;
    return FT_Common.UShort_To_Short((exc.code.data[exc.code.pos + exc.IP - 2] << 8) + exc.code.data[exc.code.pos + exc.IP - 1]);
}
// --

function Ins_NPUSHW(exc, args, args_pos)
{
    var L = exc.code.data[exc.code.pos + exc.IP + 1];

    if (L >=  (exc.stackSize + 1 - exc.top))
    {
        exc.error = 130;
        return;
    }

    exc.IP += 2;

    for (var K = 0; K < L; K++)
        args[args_pos + K] = GetShortIns(exc);

    exc.step_ins = false;
    exc.new_top += L;
}

function Ins_WS(exc, args, args_pos)
{
    var I = args[args_pos];
    if (I >= exc.storeSize)
    {
        if (exc.pedantic_hinting)
        {
            exc.error = 134;
            return;
        }
    }
    else
        exc.storage[I] = args[args_pos + 1];
}

function Ins_RS(exc, args, args_pos)
{
    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        var I = FT_Common.IntToUInt(args[args_pos]);
        if (I >= exc.storeSize)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
            else
                args[args_pos] = 0;
        }
        else
        {
            /* subpixel hinting - avoid Typeman Dstroke and */
            /* IStroke and Vacuform rounds                  */

            if (exc.ignore_x_mode &&
                ((I == 24) && (exc.face.sph_found_func_flags & (32 | 64))) ||
                ((I == 22) && (exc.sph_in_func_flags & 128)) ||
                ((I == 8) && (exc.face.sph_found_func_flags & 8) && exc.iup_called))
                args[args_pos] = 0;
            else
                args[args_pos] = exc.storage[I];
        }
    }
    else
    {
        var I = FT_Common.IntToUInt(args[args_pos]);

        if (I >= exc.storeSize)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
            else
                args[args_pos] = 0;
        }
        else
            args[args_pos] = exc.storage[I];
    }
}

function Ins_WCVTP(exc, args, args_pos)
{
    var I = args[args_pos];
    if (I >= exc.cvtSize)
    {
        if (exc.pedantic_hinting)
        {
            exc.error = 134;
            return;
        }
    }
    else
        exc.func_write_cvt(exc, I, args[args_pos + 1]);
}

function Ins_RCVT(exc, args, args_pos)
{
    var I = args[args_pos];
    if (I >= exc.cvtSize)
    {
        if (exc.pedantic_hinting)
        {
            exc.error = 134;
            return;
        }
        else
            args[args_pos] = 0;
    }
    else
       args[args_pos] = exc.func_read_cvt(exc, I);
}

function Ins_GC(exc, args, args_pos)
{
    var L = args[args_pos];
    var R = 0;
    if (L >= exc.zp2.n_points)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
    }
    else
    {
        if (exc.opcode & 1)
            R = exc.func_dualproj(exc, exc.zp2.org[exc.zp2._offset_org + L].x, exc.zp2.org[exc.zp2._offset_org + L].y);
        else
            R = exc.func_project(exc, exc.zp2.cur[exc.zp2._offset_cur + L].x, exc.zp2.cur[exc.zp2._offset_cur + L].y);
    }

    args[args_pos] = R;
}

function Ins_SCFS(exc, args, args_pos)
{
    var L = 0xFFFF & args[args_pos];

    if (L >= exc.zp2.n_points)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var K = exc.func_project(exc, exc.zp2.cur[exc.zp2._offset_cur + L].x, exc.zp2.cur[exc.zp2._offset_cur + L].y);

    exc.func_move(exc, exc.zp2, L, args[args_pos + 1] - K);

    /* UNDOCUMENTED!  The MS rasterizer does that with */
    /* twilight points (confirmed by Greg Hitchcock)   */
    if (exc.GS.gep2 == 0)
    {
        exc.zp2.org[exc.zp2._offset_org + L].x = exc.zp2.cur[exc.zp2._offset_cur + L].x;
        exc.zp2.org[exc.zp2._offset_org + L].y = exc.zp2.cur[exc.zp2._offset_cur + L].y;
    }
}

function Ins_MD(exc, args, args_pos)
{
    var D = 0;
    var K = 0xFFFF & args[args_pos + 1];
    var L = 0xFFFF & args[args_pos];

    if ((L >= exc.zp0.n_points) || (K >= exc.zp1.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        D = 0;
    }
    else
    {
        if (exc.opcode & 1)
        {
            var vec1 = exc.zp0.cur[exc.zp0._offset_cur + L];
            var vec2 = exc.zp1.cur[exc.zp1._offset_cur + K];

            D = exc.func_project(exc, vec1.x - vec2.x, vec1.y - vec2.y);
        }
        else
        {
            /* XXX: UNDOCUMENTED: twilight zone special case */
            if (exc.GS.gep0 == 0 || exc.GS.gep1 == 0)
            {
                var vec1 = exc.zp0.org[exc.zp0._offset_org + L];
                var vec2 = exc.zp1.org[exc.zp1._offset_org + K];

                D = exc.func_dualproj(exc, vec1.x - vec2.x, vec1.y - vec2.y);
            }
            else
            {
                var vec1 = exc.zp0.orus[exc.zp0._offset_orus + L];
                var vec2 = exc.zp1.orus[exc.zp1._offset_orus + K];

                if ( exc.metrics.x_scale == exc.metrics.y_scale )
                {
                    /* this should be faster */
                    D = exc.func_dualproj(exc, vec1.x - vec2.x, vec1.y - vec2.y);
                    D = FT_MulFix(D, exc.metrics.x_scale);
                }
                else
                {
                    var vec = new FT_Vector();
                    vec.x = FT_MulFix(vec1.x - vec2.x, exc.metrics.x_scale);
                    vec.y = FT_MulFix(vec1.y - vec2.y, exc.metrics.y_scale);

                    D = exc.func_dualproj(exc, vec.x, vec.y);
                }
            }
        }
    }

    if (exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)//#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */
        if (exc.ignore_x_mode && Math.abs(D) == 64)
            D += 1;
    } //#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

    args[args_pos] = D;
}

// --
function Current_Ppem(exc)
{
    return FT_MulFix(exc.tt_metrics.ppem, Current_Ratio(exc));
}

function Current_Ratio(exc)
{
    if (!exc.tt_metrics.ratio)
    {
        // TODO: unpatented
        if (exc.GS.projVector.y == 0)
            exc.tt_metrics.ratio = exc.tt_metrics.x_ratio;
        else if (exc.GS.projVector.x == 0)
            exc.tt_metrics.ratio = exc.tt_metrics.y_ratio;
        else
        {
            var x = TT_MulFix14(exc.tt_metrics.x_ratio, exc.GS.projVector.x);
            var y = TT_MulFix14(exc.tt_metrics.y_ratio, exc.GS.projVector.y);
            exc.tt_metrics.ratio = FT_Hypot(x, y);
        }
    }
    return exc.tt_metrics.ratio;
}

function FT_MulDiv_No_Round(a, b, c)
{
    var s = 1;
    if ( a < 0 ) { a = -a; s = -1; }
    if ( b < 0 ) { b = -b; s = -s; }
    if ( c < 0 ) { c = -c; s = -s; }

    var d = (c > 0 ? a * b / c : 0x7FFFFFFF);
    d = d >> 0;

    return ( s > 0 ) ? d : -d;
}
// --

function Ins_MPPEM(exc, args, args_pos)
{
    args[args_pos] = Current_Ppem(exc);
}

function Ins_MPS(exc, args, args_pos)
{
    args[args_pos] = Current_Ppem(exc);
}

function Ins_FLIPON(exc, args, args_pos)
{
    exc.GS.auto_flip = true;
}

function Ins_FLIPOFF(exc, args, args_pos)
{
    exc.GS.auto_flip = false;
}

function Ins_DEBUG(exc, args, args_pos)
{
    exc.error = 135;
}

function Ins_LT(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] < args[args_pos + 1]) ? 1 : 0;
}
function Ins_LTEQ(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] <= args[args_pos + 1]) ? 1 : 0;
}
function Ins_GT(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] > args[args_pos + 1]) ? 1 : 0;
}
function Ins_GTEQ(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] >= args[args_pos + 1]) ? 1 : 0;
}
function Ins_EQ(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] == args[args_pos + 1]) ? 1 : 0;
}
function Ins_NEQ(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] != args[args_pos + 1]) ? 1 : 0;
}
function Ins_ODD(exc, args, args_pos)
{
    args[args_pos] = ((exc.func_round(exc, args[args_pos], 0) & 127) == 64) ? 1 : 0;
}
function Ins_EVEN(exc, args, args_pos)
{
    args[args_pos] = ((exc.func_round(exc, args[args_pos], 0) & 127) == 0) ? 1 : 0;
}
function Ins_IF(exc, args, args_pos)
{
    if (args[args_pos] != 0)
        return;

    var nIfs = 1;
    var Out = 0;

    do
    {
        if (SkipCode(exc) == 1)
            return;

        switch ( exc.opcode )
        {
        case 0x58:      /* IF */
            nIfs++;
            break;
        case 0x1B:      /* ELSE */
            Out = (nIfs == 1) ? 1 : 0;
            break;
        case 0x59:      /* EIF */
            nIfs--;
            Out = (nIfs == 0) ? 1 : 0;
            break;
        }
    } while ( Out == 0 );
}
function Ins_EIF(exc, args, args_pos)
{
    // nothing
}
function Ins_AND(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] && args[args_pos + 1]) ? 1 : 0;
}
function Ins_OR(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] || args[args_pos + 1]) ? 1 : 0;
}
function Ins_NOT(exc, args, args_pos)
{
    args[args_pos] = (args[args_pos] != 0) ? 0 : 1;
}
function Ins_SDB(exc, args, args_pos)
{
    exc.GS.delta_base = FT_Common.UShort_To_Short(0xFFFF & args[args_pos]);
}
function Ins_SDS(exc, args, args_pos)
{
    exc.GS.delta_shift = FT_Common.UShort_To_Short(0xFFFF & args[args_pos]);
}

function Ins_ADD(exc, args, args_pos)
{
    args[args_pos] += args[args_pos + 1];
}
function Ins_SUB(exc, args, args_pos)
{
    args[args_pos] -= args[args_pos + 1];
}
function Ins_DIV(exc, args, args_pos)
{
    if (args[args_pos + 1] == 0)
        exc.error = 133;
    else
        args[args_pos] = FT_MulDiv_No_Round(args[args_pos], 64, args[args_pos + 1]);
}
function Ins_MUL(exc, args, args_pos)
{
    args[args_pos] = FT_MulDiv(args[args_pos], args[args_pos + 1], 64);
}
function Ins_ABS(exc, args, args_pos)
{
    args[args_pos] = Math.abs(args[args_pos]);
}
function Ins_NEG(exc, args, args_pos)
{
    args[args_pos] = -args[args_pos];
}
function Ins_FLOOR(exc, args, args_pos)
{
    args[args_pos] = FT_PIX_FLOOR(args[args_pos]);
}
function Ins_CEILING(exc, args, args_pos)
{
    args[args_pos] = FT_PIX_CEIL(args[args_pos]);
}
function Ins_ROUND(exc, args, args_pos)
{
    args[args_pos] = exc.func_round(exc, args[args_pos], exc.tt_metrics.compensations[exc.opcode - 0x68]);
}
function Ins_NROUND(exc, args, args_pos)
{
    args[args_pos] = Round_None(exc, args[args_pos], exc.tt_metrics.compensations[exc.opcode - 0x6C]);
}

function Ins_WCVTF(exc, args, args_pos)
{
    var I = args[args_pos];
    if (I >= exc.cvtSize)
    {
        if (exc.pedantic_hinting)
        {
            exc.error = 134;
            return;
        }
    }
    else
    {
        exc.cvt[I] = FT_MulFix(args[args_pos + 1], exc.tt_metrics.scale);
    }
}

function Ins_DELTAP(exc, args, args_pos)
{
    var A = 0;
    var C = 0;
    var B = 0;
    var B1 = 0;
    var B2 = 0;

    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    if (bIsSubpix)
    {
        if (exc.ignore_x_mode && exc.iup_called && (exc.sph_tweak_flags & 2048) != 0)
        {
            exc.new_top = exc.args;
            return;
        }
    }

    // TODO: unpatented

    var nump = args[args_pos];   /* some points theoretically may occur more
                                   than once, thus UShort isn't enough */
    if (nump < 0)
        nump += 4294967296;

    for (var k = 1; k <= nump; k++)
    {
        if (exc.args < 2)
        {
            if (exc.pedantic_hinting)
                exc.error = 129;
            exc.args = 0;
            exc.new_top = exc.args;
            return;
        }

        exc.args -= 2;

        A = 0xFFFF & exc.stack[exc.args + 1];
        B = exc.stack[exc.args];

        /* XXX: Because some popular fonts contain some invalid DeltaP */
        /*      instructions, we simply ignore them when the stacked   */
        /*      point reference is off limit, rather than returning an */
        /*      error.  As a delta instruction doesn't change a glyph  */
        /*      in great ways, this shouldn't be a problem.            */
        if (A < exc.zp0.n_points)
        {
            C = (B & 0xF0) >> 4;

            switch (exc.opcode)
            {
            case 0x5D:
                break;
            case 0x71:
                C += 16;
                break;
            case 0x72:
                C += 32;
                break;
            }

            C += exc.GS.delta_base;
            if (C < 0)
                C += 4294967296;

            if (Current_Ppem(exc) == C)
            {
                B = (B & 0xF) - 8;
                if (B >= 0)
                    B++;
                B = B * 64 / (1 << exc.GS.delta_shift);
                B = B >> 0;

                if (!bIsSubpix)
                {
                    exc.func_move(exc, exc.zp0, A, B);
                }
                else
                {
                    /*
                    *  Allow delta move if
                    *
                    *  - not using ignore_x_mode rendering
                    *  - glyph is specifically set to allow it
                    *  - glyph is composite and freedom vector is not subpixel vector
                    */
                    if (!exc.ignore_x_mode || (exc.sph_tweak_flags & 2) != 0 ||
                        (exc.is_composite && exc.GS.freeVector.y != 0))
                        exc.func_move(exc, exc.zp0, A, B);
                    else if (exc.ignore_x_mode) /* Otherwise apply subpixel hinting and compatibility mode rules */
                    {
                        if (exc.GS.freeVector.y != 0)
                            B1 = exc.zp0.cur[exc.zp0._offset_cur + A].y;
                        else
                            B1 = exc.zp0.cur[exc.zp0._offset_cur + A].x;

                        /*
                        // Standard Subpixel Hinting:  Allow y move
                        if (!exc.face.sph_compatibility_mode && exc.GS.freeVector.y != 0)
                            exc.func_move(exc, exc.zp0, A, B);
                        // Compatibility Mode: Allow x or y move if point touched in Y direction
                        else */if (exc.face.sph_compatibility_mode && (exc.sph_tweak_flags & 4) == 0)
                        {
                            /* save the y value of the point now; compare after move */
                            B1 = exc.zp0.cur[exc.zp0._offset_cur + A].y;

                            if ((exc.sph_tweak_flags & 16384) != 0)
                                B = FT_PIX_ROUND(B1 + B) - B1;

                            /*
                            *  Allow delta move if using compatibility_mode, IUP has not
                            *  been called, and point is touched on Y.
                            */
                            if (!exc.iup_called && (exc.zp0.tags[exc.zp0._offset_tags + A] & 16) != 0)
                                exc.func_move(exc, exc.zp0, A, B);
                        }

                        B2 = exc.zp0.cur[exc.zp0._offset_cur + A].y;

                        /* Reverse this move if it results in a disallowed move */
                        if (exc.GS.freeVector.y != 0 &&
                            ((exc.face.sph_compatibility_mode && (B1 & 63) == 0 && (B2 & 63) != 0 ) ||
                            ((exc.sph_tweak_flags & 524288) != 0 && (B1 & 63) != 0 && (B2 & 63) != 0)))
                            exc.func_move(exc, exc.zp0, A, -B);
                    }
                }

            }
        }
        else
        {
            if (exc.pedantic_hinting)
                exc.error = 134;
        }
    }

    exc.new_top = exc.args;
}

function Ins_DELTAC(exc, args, args_pos)
{
    // TODO: unpatented
    var nump = args[args_pos];

    for (var k = 1; k <= nump; k++)
    {
        if (exc.args < 2)
        {
            if (exc.pedantic_hinting)
                exc.error = 129;
            exc.args = 0;
            exc.new_top = exc.args;
            return;
        }

        exc.args -= 2;

        var A = FT_Common.IntToUInt(exc.stack[exc.args + 1]);
        var B = exc.stack[exc.args];
        var C = 0;

        if (A >= exc.cvtSize)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
        }
        else
        {
            C = (B & 0xF0) >> 4;

            switch (exc.opcode)
            {
            case 0x73:
                break;
            case 0x74:
                C += 16;
                break;
            case 0x75:
                C += 32;
                break;
            }

            C += exc.GS.delta_base;

            if (Current_Ppem(exc) == C)
            {
                B = (B & 0xF) - 8;
                if (B >= 0)
                    B++;
                B = B * 64 / (1 << exc.GS.delta_shift);
                B = B >> 0;

                exc.func_move_cvt(exc, A, B);
            }
        }
    }
    exc.new_top = exc.args;
}

function Ins_SROUND(exc, args, args_pos)
{
    SetSuperRound(exc, 0x4000, args[args_pos]);
    exc.GS.round_state = TT_Round_Super;
    exc.func_round = Round_Super;
}

function Ins_S45ROUND(exc, args, args_pos)
{
    SetSuperRound(exc, 0x2D41, args[args_pos]);
    exc.GS.round_state = TT_Round_Super_45;
    exc.func_round = Round_Super_45;
}

function Ins_JROT(exc, args, args_pos)
{
    if (args[args_pos + 1] != 0)
    {
        if (args[args_pos] == 0 && exc.args == 0)
            exc.error = 132;
        exc.IP += args[args_pos];
        if (exc.IP < 0 || (exc.callTop > 0 && exc.IP > exc.callStack[exc.callTop - 1].Def.end))
            exc.error = 132;
        exc.step_ins = false;
    }
}

function Ins_JROF(exc, args, args_pos)
{
    if (args[args_pos + 1] == 0)
    {
        if (args[args_pos] == 0 && exc.args == 0)
            exc.error = 132;
        exc.IP += args[args_pos];
        if (exc.IP < 0 || (exc.callTop > 0 && exc.IP > exc.callStack[exc.callTop - 1].Def.end))
            exc.error = 132;
        exc.step_ins = false;
    }
}

function Ins_ROFF(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_Off;
    exc.func_round = Round_None;
}

function Ins_RUTG(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_Up_To_Grid;
    exc.func_round = Round_Up_To_Grid;
}

function Ins_RDTG(exc, args, args_pos)
{
    exc.GS.round_state = TT_Round_Down_To_Grid;
    exc.func_round = Round_Down_To_Grid;
}

function Ins_SANGW(exc, args, args_pos)
{
    /* instruction not supported anymore */
}

function Ins_AA(exc, args, args_pos)
{
    /* intentionally no longer supported */
}

function Ins_FLIPPT(exc, args, args_pos)
{
    if (exc.top < exc.GS.loop)
    {
        if (exc.pedantic_hinting)
            exc.error = 129;

        exc.GS.loop = 1;
        exc.new_top = exc.args;
        return;
    }

    while (exc.GS.loop > 0)
    {
        exc.args--;
        var point = 0xFFFF & exc.stack[exc.args];

        if (point >= exc.pts.n_points)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 134;
                return;
            }
        }
        else
            exc.pts.tags[exc.pts._offset_tags + point] ^= 1;

        exc.GS.loop--;
    }

    exc.GS.loop = 1;
    exc.new_top = exc.args;
}

function Ins_FLIPRGON(exc, args, args_pos)
{
    var K = 0xFFFF & args[args_pos + 1];
    var L = 0xFFFF & args[args_pos];

    if ((K >= exc.pts.n_points) || (L >= exc.pts.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    for (var I = L; I <= K; I++)
        exc.pts.tags[exc.pts._offset_tags + I] |= 1;
}

function Ins_FLIPRGOFF(exc, args, args_pos)
{
    var K = 0xFFFF & args[args_pos + 1];
    var L = 0xFFFF & args[args_pos];

    if ((K >= exc.pts.n_points) || (L >= exc.pts.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    for (var I = L; I <= K; I++)
        exc.pts.tags[exc.pts._offset_tags + I] &= ~1;
}

function Ins_SCANCTRL(exc, args, args_pos)
{
    /* Get Threshold */
    var A = (args[args_pos] & 0xFF);

    if (A == 0xFF)
    {
        exc.GS.scan_control = true;
        return;
    }
    else if (A == 0)
    {
        exc.GS.scan_control = false;
        return;
    }

    if ((args[args_pos] & 0x100 ) != 0 && exc.tt_metrics.ppem <= A)
        exc.GS.scan_control = true;

    if ((args[args_pos] & 0x200 ) != 0 && exc.tt_metrics.rotated)
        exc.GS.scan_control = true;

    if ((args[args_pos] & 0x400 ) != 0 && exc.tt_metrics.stretched)
        exc.GS.scan_control = true;

    if ((args[args_pos] & 0x800 ) != 0 && exc.tt_metrics.ppem > A)
        exc.GS.scan_control = false;

    if ((args[args_pos] & 0x1000 ) != 0 && exc.tt_metrics.rotated)
        exc.GS.scan_control = false;

    if ((args[args_pos] & 0x2000 ) != 0 && exc.tt_metrics.stretched)
        exc.GS.scan_control = false;
}

function Ins_SDPVTL(exc, args, args_pos)
{
    var aOpc = exc.opcode;

    var p1 = 0xFFFF & args[args_pos + 1];
    var p2 = 0xFFFF & args[args_pos];

    if ((p2 >= exc.zp1.n_points) || (p1 >= exc.zp2.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    var v1 = exc.zp1.org[exc.zp1._offset_org + p2];
    var v2 = exc.zp2.org[exc.zp2._offset_org + p1];

    var A = v1.x - v2.x;
    var B = v1.y - v2.y;

    /* If v1 == v2, SDPVTL behaves the same as */
    /* SVTCA[X], respectively.                 */
    /*                                         */
    /* Confirmed by Greg Hitchcock.            */
    if ( A == 0 && B == 0 )
    {
        A    = 0x4000;
        aOpc = 0;
    }

    if ((aOpc & 1) != 0)
    {
        var C =  B;   /* counter clockwise rotation */
        B =  A;
        A = -C;
    }

    Normalize(exc, A, B, exc.GS.dualVector);

    v1 = exc.zp1.cur[exc.zp1._offset_cur + p2];
    v2 = exc.zp2.cur[exc.zp2._offset_cur + p1];

    A = v1.x - v2.x;
    B = v1.y - v2.y;

    if ( A == 0 && B == 0 )
    {
        A    = 0x4000;
        aOpc = 0;
    }

    if ((aOpc & 1) != 0)
    {
        var C =  B;   /* counter clockwise rotation */
        B =  A;
        A = -C;
    }

    Normalize(exc, A, B, exc.GS.projVector);

    GUESS_VECTOR(exc, exc.GS.freeVector);

    Compute_Funcs(exc);
}

function Ins_GETINFO(exc, args, args_pos)
{
    var K = 0;
    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    if (bIsSubpix)
    {
        /********************************/
        /* RASTERIZER VERSION           */
        /* Selector Bit:  0             */
        /* Return Bit(s): 0-7           */
        /*                              */
        if ((args[args_pos] & 1) != 0 && exc.ignore_x_mode)
        {
            K = exc.rasterizer_version;
        }
        else
        {
            if ((args[args_pos] & 1) != 0)
                K = 35;
        }
    }
    else
    {
        if ((args[args_pos] & 1) != 0)
            K = 35;
    }

    /********************************/
    /* GLYPH ROTATED                */
    /* Selector Bit:  1             */
    /* Return Bit(s): 8             */
    /*                              */
    if ((args[args_pos] & 2) != 0 && exc.tt_metrics.rotated)
        K |= 0x80;

    /********************************/
    /* GLYPH STRETCHED              */
    /* Selector Bit:  2             */
    /* Return Bit(s): 9             */
    /*                              */
    if ((args[args_pos] & 4) != 0 && exc.tt_metrics.stretched)
        K |= 1 << 8;

    /********************************/
    /* HINTING FOR GRAYSCALE        */
    /* Selector Bit:  5             */
    /* Return Bit(s): 12            */
    /*                              */
    if ((args[args_pos] & 32) != 0 && exc.grayscale)
        K |= 1 << 12;

    if (bIsSubpix) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode && exc.rasterizer_version >= 35)
        {
            /********************************/
            /* HINTING FOR GRAYSCALE        */
            /* Selector Bit:  5             */
            /* Return Bit(s): 12            */
            /*                              */
            if ((args[args_pos] & 32) != 0 && exc.grayscale_hinting)
                K |= 1 << 12;

            if (exc.rasterizer_version >= 37)
            {
                /********************************/
                /* HINTING FOR SUBPIXEL         */
                /* Selector Bit:  6             */
                /* Return Bit(s): 13            */
                /*                              */
                if ((args[0] & 64) != 0 && exc.subpixel_hinting)
                    K |= 1 << 13;

                /********************************/
                /* COMPATIBLE WIDTHS ENABLED    */
                /* Selector Bit:  7             */
                /* Return Bit(s): 14            */
                /*                              */
                /* Functionality still needs to be added */
                if ((args[0] & 128) != 0 && exc.compatible_widths)
                    K |= 1 << 14;

                /********************************/
                /* SYMMETRICAL SMOOTHING        */
                /* Selector Bit:  8             */
                /* Return Bit(s): 15            */
                /*                              */
                /* Functionality still needs to be added */
                if ((args[0] & 256 ) != 0 && exc.symmetrical_smoothing)
                    K |= 1 << 15;

                /********************************/
                /* HINTING FOR BGR?             */
                /* Selector Bit:  9             */
                /* Return Bit(s): 16            */
                /*                              */
                /* Functionality still needs to be added */
                if ((args[0] & 512) != 0 && exc.bgr)
                    K |= 1 << 16;

                if (exc.rasterizer_version >= 38)
                {
                    /********************************/
                    /* SUBPIXEL POSITIONED?         */
                    /* Selector Bit:  10            */
                    /* Return Bit(s): 17            */
                    /*                              */
                    /* Functionality still needs to be added */
                    if ((args[0] & 1024) != 0 && exc.subpixel_positioned)
                        K |= 1 << 17;
                }
            }
        }
    }//#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
    args[args_pos] = K;
}

function Ins_IDEF(exc, args, args_pos)
{
    /*  First of all, look for the same function in our table */
    var defs = exc.IDefs;
    var _ind_def = 0;
    var limit = exc.numIDefs;
    var def = defs[_ind_def];

    for ( ; _ind_def < limit; _ind_def++)
    {
        def = defs[_ind_def];
        if (def.opc == args[args_pos])
            break;
    }

    if (_ind_def == limit)
    {
        /* check that there is enough room for a new instruction */
        if (exc.numIDefs >= exc.maxIDefs)
        {
            exc.error = 141;
            return;
        }
        exc.numIDefs++;
    }

    /* opcode must be unsigned 8-bit integer */
    if (0 > args[args_pos] || args[args_pos] > 0x00FF)
    {
        exc.error = 141;
        return;
    }

    def.opc    = 0xFF & args[args_pos];
    def.start  = exc.IP + 1;
    def.range  = exc.curRange;
    def.active = true;

    if (args[args_pos] > exc.maxIns)
        exc.maxIns = 0xFF & args[args_pos];

    /* Now skip the whole function definition. */
    /* We don't allow nested IDEFs & FDEFs.    */

    while (SkipCode(exc) == 0)
    {
        switch (exc.opcode)
        {
        case 0x89:   /* IDEF */
        case 0x2C:   /* FDEF */
            exc.error = 137;
            return;
      case 0x2D:   /* ENDF */
            return;
        }
    }
}

function Ins_ROLL(exc, args, args_pos)
{
    var A = args[args_pos + 2];
    var B = args[args_pos + 1];
    var C = args[args_pos];

    args[args_pos + 2] = C;
    args[args_pos + 1] = A;
    args[args_pos] = B;
}

function Ins_MAX(exc, args, args_pos)
{
    if (args[args_pos + 1] > args[args_pos])
        args[args_pos] = args[args_pos + 1];
}

function Ins_MIN(exc, args, args_pos)
{
    if (args[args_pos + 1] < args[args_pos])
        args[args_pos] = args[args_pos + 1];
}

function Ins_SCANTYPE(exc, args, args_pos)
{
    if (args[args_pos] >= 0)
        exc.GS.scan_type = args[args_pos];
}

function Ins_INSTCTRL(exc, args, args_pos)
{
    var K = args[args_pos + 1];
    var L = args[args_pos];

    if (K < 1 || K > 2)
    {
        if (exc.pedantic_hinting)
            exc.error = 134;
        return;
    }

    if (L != 0)
        L = K;

    exc.GS.instruct_control = ((((0xFF & exc.GS.instruct_control) & ~(K & 0xFF)) | (L & 0xFF)) != 0) ? 1 : 0;
}

function Ins_PUSHB(exc, args, args_pos)
{
    var L = 0xFFFF & (exc.opcode - 0xB0 + 1);

    if (L >= (exc.stackSize + 1 - exc.top))
    {
        exc.error = 130;
        return;
    }

    for (var K = 1; K <= L; K++)
        args[args_pos + K - 1] = exc.code.data[exc.code.pos + exc.IP + K];
}

function Ins_PUSHW(exc, args, args_pos)
{
    var L = 0xFFFF & (exc.opcode - 0xB8 + 1);

    if (L >= (exc.stackSize + 1 - exc.top))
    {
        exc.error = 130;
        return;
    }

    exc.IP++;

    for (var K = 0; K < L; K++)
        args[args_pos + K] = GetShortIns(exc);

    exc.step_ins = false;
}

function Ins_MDRP(exc, args, args_pos)
{
    var org_dist, distance;
    var minimum_distance = exc.GS.minimum_distance;

    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    if (bIsSubpix)
    {
        if (exc.ignore_x_mode && exc.GS.freeVector.x != 0 && (exc.sph_tweak_flags & 256) == 0)
            minimum_distance = 0;
    }

    var point = 0xFFFF & args[args_pos];

    if ((point >= exc.zp1.n_points) || (exc.GS.rp0 >= exc.zp0.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.rp1 = exc.GS.rp0;
        exc.GS.rp2 = point;
        if ((exc.opcode & 16) != 0)
            exc.GS.rp0 = point;
        return;
    }

    /* XXX: Is there some undocumented feature while in the */
    /*      twilight zone?                                  */

    var v1, v2;
    /* XXX: UNDOCUMENTED: twilight zone special case */
    if (exc.GS.gep0 == 0 || exc.GS.gep1 == 0)
    {
        v1 = exc.zp1.org[exc.zp1._offset_org + point];
        v2 = exc.zp0.org[exc.zp0._offset_org + exc.GS.rp0];
        org_dist = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
    }
    else
    {
        v1 = exc.zp1.orus[exc.zp1._offset_orus + point];
        v2 = exc.zp0.orus[exc.zp0._offset_orus + exc.GS.rp0];

        if (exc.metrics.x_scale == exc.metrics.y_scale)
        {
            /* this should be faster */
            org_dist = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);
            org_dist = FT_MulFix(org_dist, exc.metrics.x_scale);
        }
        else
        {
            var _x = FT_MulFix(v1.x - v2.x, exc.metrics.x_scale);
            var _y = FT_MulFix(v1.y - v2.y, exc.metrics.y_scale);
            org_dist = exc.func_dualproj(exc, _x, _y);
        }
    }

    /* single width cut-in test */
    if (Math.abs(org_dist - exc.GS.single_width_value) < exc.GS.single_width_cutin)
    {
        if (org_dist >= 0)
            org_dist = exc.GS.single_width_value;
        else
            org_dist = -exc.GS.single_width_value;
    }

    /* round flag */
    if ((exc.opcode & 4) != 0)
    {
        if (bIsSubpix)
        {
            if (exc.ignore_x_mode && exc.GS.freeVector.x != 0)
                distance = Round_None(exc, org_dist, exc.tt_metrics.compensations[exc.opcode & 3]);
            else
                distance = exc.func_round(exc, org_dist, exc.tt_metrics.compensations[exc.opcode & 3]);
        }
        else
        {
            distance = exc.func_round(exc, org_dist, exc.tt_metrics.compensations[exc.opcode & 3]);
        }
    }
    else
        distance = Round_None(exc, org_dist, exc.tt_metrics.compensations[exc.opcode & 3]);

    /* minimum distance flag */
    if ((exc.opcode & 8) != 0)
    {
        if (org_dist >= 0)
        {
            if (distance < minimum_distance)
                distance = minimum_distance;
        }
        else
        {
            if (distance > -minimum_distance)
                distance = -minimum_distance;
        }
    }

    /* now move the point */
    v1 = exc.zp1.cur[exc.zp1._offset_cur + point];
    v2 = exc.zp0.cur[exc.zp0._offset_cur + exc.GS.rp0];
    org_dist = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);
    
    exc.func_move(exc, exc.zp1, point, distance - org_dist);

    exc.GS.rp1 = exc.GS.rp0;
    exc.GS.rp2 = point;
    if ((exc.opcode & 16) != 0)
        exc.GS.rp0 = point;
}

function Ins_MIRP(exc, args, args_pos)
{
    var bIsSubpix = exc.face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    var minimum_distance    = exc.GS.minimum_distance;
    var control_value_cutin = exc.GS.control_value_cutin;
    var point               = 0xFFFF & args[args_pos];
    var cvtEntry            = FT_Common.IntToUInt(args[args_pos + 1] + 1);
    var distance = 0;

    if (bIsSubpix)
    {
        if (exc.ignore_x_mode && exc.GS.freeVector.x != 0 && (exc.sph_tweak_flags & 256) == 0)
            control_value_cutin = minimum_distance = 0;
    }

    /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
    if ((point >= exc.zp1.n_points) || (cvtEntry >= (exc.cvtSize + 1)) || (exc.GS.rp0 >= exc.zp0.n_points))
    {
        if (exc.pedantic_hinting)
            exc.error = 134;

        exc.GS.rp1 = exc.GS.rp0;
        if ((exc.opcode & 16) != 0)
            exc.GS.rp0 = point;
        exc.GS.rp2 = point;
        return;
    }

    var cvt_dist = 0;
    if (cvtEntry == 0)
        cvt_dist = 0;
    else
        cvt_dist = exc.func_read_cvt(exc, cvtEntry - 1);
        
    /* single width test */
    if (Math.abs(cvt_dist - exc.GS.single_width_value) < exc.GS.single_width_cutin)
    {
        if (cvt_dist >= 0)
            cvt_dist =  exc.GS.single_width_value;
        else
            cvt_dist = -exc.GS.single_width_value;
    }

    /* UNDOCUMENTED!  The MS rasterizer does that with */
    /* twilight points (confirmed by Greg Hitchcock)   */
    if (exc.GS.gep1 == 0)
    {
        var _v = exc.zp0.org[exc.zp0._offset_org + exc.GS.rp0];
        exc.zp1.org[exc.zp1._offset_org + point].x = _v.x + TT_MulFix14(cvt_dist, exc.GS.freeVector.x);
        exc.zp1.org[exc.zp1._offset_org + point].y = _v.y + TT_MulFix14(cvt_dist, exc.GS.freeVector.y);

        copy_vector(exc.zp1.cur[exc.zp1._offset_cur + point], exc.zp1.org[exc.zp1._offset_org + point]);
    }

    var v1 = exc.zp1.org[exc.zp1._offset_org + point];
    var v2 = exc.zp0.org[exc.zp0._offset_org + exc.GS.rp0];

    var org_dist = exc.func_dualproj(exc, v1.x - v2.x, v1.y - v2.y);

    v1 = exc.zp1.cur[exc.zp1._offset_cur + point];
    v2 = exc.zp0.cur[exc.zp0._offset_cur + exc.GS.rp0];

    var cur_dist = exc.func_project(exc, v1.x - v2.x, v1.y - v2.y);

    /* auto-flip test */
    if (exc.GS.auto_flip)
    {
        if ((org_dist ^ cvt_dist) < 0)
            cvt_dist = -cvt_dist;
    }

    if (bIsSubpix) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (exc.ignore_x_mode && exc.GS.freeVector.y != 0 && (exc.sph_tweak_flags & 262144) != 0)
        {
            if (cur_dist < -64)
                cvt_dist -= 16;
            else if (cur_dist > 64 && cur_dist < 84)
                cvt_dist += 32;
        }
    } //#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
    /* control value cut-in and round */
    if ((exc.opcode & 4) != 0)
    {
        /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
        /*      refer to the same zone.                                  */
        if (exc.GS.gep0 == exc.GS.gep1)
        {
            /* XXX: According to Greg Hitchcock, the following wording is */
            /*      the right one:                                        */
            /*                                                            */
            /*        When the absolute difference between the value in   */
            /*        the table [CVT] and the measurement directly from   */
            /*        the outline is _greater_ than the cut_in value, the */
            /*        outline measurement is used.                        */
            /*                                                            */
            /*      This is from `instgly.doc'.  The description in       */
            /*      `ttinst2.doc', version 1.66, is thus incorrect since  */
            /*      it implies `>=' instead of `>'.                       */

            if (Math.abs(cvt_dist - org_dist) > control_value_cutin)
                cvt_dist = org_dist;
        }

        distance = exc.func_round(exc, cvt_dist, exc.tt_metrics.compensations[exc.opcode & 3]);
    }
    else
    {
        if (bIsSubpix)
        {
            /* do cvt cut-in always in MIRP for sph */
            if (exc.ignore_x_mode && exc.GS.gep0 == exc.GS.gep1)
            {
                if (Math.abs(cvt_dist - org_dist) > control_value_cutin)
                    cvt_dist = org_dist;
            }
        }
        distance = Round_None(exc, cvt_dist, exc.tt_metrics.compensations[exc.opcode & 3]);
    }

    /* minimum distance test */
    if ((exc.opcode & 8) != 0)
    {
        if (org_dist >= 0)
        {
            if (distance < minimum_distance)
                distance = minimum_distance;
        }
        else
        {
            if (distance > -minimum_distance)
                distance = -minimum_distance;
        }
    }

    if (!bIsSubpix)
    {
        exc.func_move(exc, exc.zp1, point, distance - cur_dist);
    }
    else
    {
        var B1 = exc.zp1.cur[exc.zp1._offset_cur + point].y;

        /* Round moves if necessary */
        if (exc.ignore_x_mode && exc.GS.freeVector.y != 0 && (exc.sph_tweak_flags & 16384) != 0)
            distance = FT_PIX_ROUND(B1 + distance - cur_dist) - B1 + cur_dist;

        if (exc.ignore_x_mode && exc.GS.freeVector.y != 0 && (exc.opcode & 16) == 0 && (exc.opcode & 8) == 0 && (exc.sph_tweak_flags & 8) != 0)
            distance += 64;

        exc.func_move(exc, exc.zp1, point, distance - cur_dist);

        var B2 = exc.zp1.cur[exc.zp1._offset_cur + point].y;

        var reverse_move = false;
        /* Reverse move if necessary */
        if (exc.ignore_x_mode)
        {
            if (exc.face.sph_compatibility_mode &&
                exc.GS.freeVector.y != 0 &&
                ( B1 & 63 ) == 0 &&
                ( B2 & 63 ) != 0)
                reverse_move = true;

            if ((exc.sph_tweak_flags & 65536) &&
                exc.GS.freeVector.y != 0 &&
                ( B2 & 63 ) != 0 &&
                ( B1 & 63 ) != 0)
                reverse_move = true;
        }

        if (reverse_move)
            exc.func_move(exc, exc.zp1, point, -(distance - cur_dist));
    }

    exc.GS.rp1 = exc.GS.rp0;
    if ((exc.opcode & 16) != 0)
        exc.GS.rp0 = point;
    exc.GS.rp2 = point;
}

var Instruct_Dispatch =
[
    /* Opcodes are gathered in groups of 16. */
    /* Please keep the spaces as they are.   */

    /*  SVTCA  y  */  Ins_SVTCA,
    /*  SVTCA  x  */  Ins_SVTCA,
    /*  SPvTCA y  */  Ins_SPVTCA,
    /*  SPvTCA x  */  Ins_SPVTCA,
    /*  SFvTCA y  */  Ins_SFVTCA,
    /*  SFvTCA x  */  Ins_SFVTCA,
    /*  SPvTL //  */  Ins_SPVTL,
    /*  SPvTL +   */  Ins_SPVTL,
    /*  SFvTL //  */  Ins_SFVTL,
    /*  SFvTL +   */  Ins_SFVTL,
    /*  SPvFS     */  Ins_SPVFS,
    /*  SFvFS     */  Ins_SFVFS,
    /*  GPV       */  Ins_GPV,
    /*  GFV       */  Ins_GFV,
    /*  SFvTPv    */  Ins_SFVTPV,
    /*  ISECT     */  Ins_ISECT,

    /*  SRP0      */  Ins_SRP0,
    /*  SRP1      */  Ins_SRP1,
    /*  SRP2      */  Ins_SRP2,
    /*  SZP0      */  Ins_SZP0,
    /*  SZP1      */  Ins_SZP1,
    /*  SZP2      */  Ins_SZP2,
    /*  SZPS      */  Ins_SZPS,
    /*  SLOOP     */  Ins_SLOOP,
    /*  RTG       */  Ins_RTG,
    /*  RTHG      */  Ins_RTHG,
    /*  SMD       */  Ins_SMD,
    /*  ELSE      */  Ins_ELSE,
    /*  JMPR      */  Ins_JMPR,
    /*  SCvTCi    */  Ins_SCVTCI,
    /*  SSwCi     */  Ins_SSWCI,
    /*  SSW       */  Ins_SSW,

    /*  DUP       */  Ins_DUP,
    /*  POP       */  Ins_POP,
    /*  CLEAR     */  Ins_CLEAR,
    /*  SWAP      */  Ins_SWAP,
    /*  DEPTH     */  Ins_DEPTH,
    /*  CINDEX    */  Ins_CINDEX,
    /*  MINDEX    */  Ins_MINDEX,
    /*  AlignPTS  */  Ins_ALIGNPTS,
    /*  INS_0x28  */  Ins_UNKNOWN,
    /*  UTP       */  Ins_UTP,
    /*  LOOPCALL  */  Ins_LOOPCALL,
    /*  CALL      */  Ins_CALL,
    /*  FDEF      */  Ins_FDEF,
    /*  ENDF      */  Ins_ENDF,
    /*  MDAP[0]   */  Ins_MDAP,
    /*  MDAP[1]   */  Ins_MDAP,

    /*  IUP[0]    */  Ins_IUP,
    /*  IUP[1]    */  Ins_IUP,
    /*  SHP[0]    */  Ins_SHP,
    /*  SHP[1]    */  Ins_SHP,
    /*  SHC[0]    */  Ins_SHC,
    /*  SHC[1]    */  Ins_SHC,
    /*  SHZ[0]    */  Ins_SHZ,
    /*  SHZ[1]    */  Ins_SHZ,
    /*  SHPIX     */  Ins_SHPIX,
    /*  IP        */  Ins_IP,
    /*  MSIRP[0]  */  Ins_MSIRP,
    /*  MSIRP[1]  */  Ins_MSIRP,
    /*  AlignRP   */  Ins_ALIGNRP,
    /*  RTDG      */  Ins_RTDG,
    /*  MIAP[0]   */  Ins_MIAP,
    /*  MIAP[1]   */  Ins_MIAP,

    /*  NPushB    */  Ins_NPUSHB,
    /*  NPushW    */  Ins_NPUSHW,
    /*  WS        */  Ins_WS,
    /*  RS        */  Ins_RS,
    /*  WCvtP     */  Ins_WCVTP,
    /*  RCvt      */  Ins_RCVT,
    /*  GC[0]     */  Ins_GC,
    /*  GC[1]     */  Ins_GC,
    /*  SCFS      */  Ins_SCFS,
    /*  MD[0]     */  Ins_MD,
    /*  MD[1]     */  Ins_MD,
    /*  MPPEM     */  Ins_MPPEM,
    /*  MPS       */  Ins_MPS,
    /*  FlipON    */  Ins_FLIPON,
    /*  FlipOFF   */  Ins_FLIPOFF,
    /*  DEBUG     */  Ins_DEBUG,

    /*  LT        */  Ins_LT,
    /*  LTEQ      */  Ins_LTEQ,
    /*  GT        */  Ins_GT,
    /*  GTEQ      */  Ins_GTEQ,
    /*  EQ        */  Ins_EQ,
    /*  NEQ       */  Ins_NEQ,
    /*  ODD       */  Ins_ODD,
    /*  EVEN      */  Ins_EVEN,
    /*  IF        */  Ins_IF,
    /*  EIF       */  Ins_EIF,
    /*  AND       */  Ins_AND,
    /*  OR        */  Ins_OR,
    /*  NOT       */  Ins_NOT,
    /*  DeltaP1   */  Ins_DELTAP,
    /*  SDB       */  Ins_SDB,
    /*  SDS       */  Ins_SDS,

    /*  ADD       */  Ins_ADD,
    /*  SUB       */  Ins_SUB,
    /*  DIV       */  Ins_DIV,
    /*  MUL       */  Ins_MUL,
    /*  ABS       */  Ins_ABS,
    /*  NEG       */  Ins_NEG,
    /*  FLOOR     */  Ins_FLOOR,
    /*  CEILING   */  Ins_CEILING,
    /*  ROUND[0]  */  Ins_ROUND,
    /*  ROUND[1]  */  Ins_ROUND,
    /*  ROUND[2]  */  Ins_ROUND,
    /*  ROUND[3]  */  Ins_ROUND,
    /*  NROUND[0] */  Ins_NROUND,
    /*  NROUND[1] */  Ins_NROUND,
    /*  NROUND[2] */  Ins_NROUND,
    /*  NROUND[3] */  Ins_NROUND,

    /*  WCvtF     */  Ins_WCVTF,
    /*  DeltaP2   */  Ins_DELTAP,
    /*  DeltaP3   */  Ins_DELTAP,
    /*  DeltaCn[0] */ Ins_DELTAC,
    /*  DeltaCn[1] */ Ins_DELTAC,
    /*  DeltaCn[2] */ Ins_DELTAC,
    /*  SROUND    */  Ins_SROUND,
    /*  S45Round  */  Ins_S45ROUND,
    /*  JROT      */  Ins_JROT,
    /*  JROF      */  Ins_JROF,
    /*  ROFF      */  Ins_ROFF,
    /*  INS_0x7B  */  Ins_UNKNOWN,
    /*  RUTG      */  Ins_RUTG,
    /*  RDTG      */  Ins_RDTG,
    /*  SANGW     */  Ins_SANGW,
    /*  AA        */  Ins_AA,

    /*  FlipPT    */  Ins_FLIPPT,
    /*  FlipRgON  */  Ins_FLIPRGON,
    /*  FlipRgOFF */  Ins_FLIPRGOFF,
    /*  INS_0x83  */  Ins_UNKNOWN,
    /*  INS_0x84  */  Ins_UNKNOWN,
    /*  ScanCTRL  */  Ins_SCANCTRL,
    /*  SDPVTL[0] */  Ins_SDPVTL,
    /*  SDPVTL[1] */  Ins_SDPVTL,
    /*  GetINFO   */  Ins_GETINFO,
    /*  IDEF      */  Ins_IDEF,
    /*  ROLL      */  Ins_ROLL,
    /*  MAX       */  Ins_MAX,
    /*  MIN       */  Ins_MIN,
    /*  ScanTYPE  */  Ins_SCANTYPE,
    /*  InstCTRL  */  Ins_INSTCTRL,
    /*  INS_0x8F  */  Ins_UNKNOWN,

    /*  INS_0x90  */   Ins_UNKNOWN,
    /*  INS_0x91  */   Ins_UNKNOWN,
    /*  INS_0x92  */   Ins_UNKNOWN,
    /*  INS_0x93  */   Ins_UNKNOWN,
    /*  INS_0x94  */   Ins_UNKNOWN,
    /*  INS_0x95  */   Ins_UNKNOWN,
    /*  INS_0x96  */   Ins_UNKNOWN,
    /*  INS_0x97  */   Ins_UNKNOWN,
    /*  INS_0x98  */   Ins_UNKNOWN,
    /*  INS_0x99  */   Ins_UNKNOWN,
    /*  INS_0x9A  */   Ins_UNKNOWN,
    /*  INS_0x9B  */   Ins_UNKNOWN,
    /*  INS_0x9C  */   Ins_UNKNOWN,
    /*  INS_0x9D  */   Ins_UNKNOWN,
    /*  INS_0x9E  */   Ins_UNKNOWN,
    /*  INS_0x9F  */   Ins_UNKNOWN,

    /*  INS_0xA0  */   Ins_UNKNOWN,
    /*  INS_0xA1  */   Ins_UNKNOWN,
    /*  INS_0xA2  */   Ins_UNKNOWN,
    /*  INS_0xA3  */   Ins_UNKNOWN,
    /*  INS_0xA4  */   Ins_UNKNOWN,
    /*  INS_0xA5  */   Ins_UNKNOWN,
    /*  INS_0xA6  */   Ins_UNKNOWN,
    /*  INS_0xA7  */   Ins_UNKNOWN,
    /*  INS_0xA8  */   Ins_UNKNOWN,
    /*  INS_0xA9  */   Ins_UNKNOWN,
    /*  INS_0xAA  */   Ins_UNKNOWN,
    /*  INS_0xAB  */   Ins_UNKNOWN,
    /*  INS_0xAC  */   Ins_UNKNOWN,
    /*  INS_0xAD  */   Ins_UNKNOWN,
    /*  INS_0xAE  */   Ins_UNKNOWN,
    /*  INS_0xAF  */   Ins_UNKNOWN,

    /*  PushB[0]  */  Ins_PUSHB,
    /*  PushB[1]  */  Ins_PUSHB,
    /*  PushB[2]  */  Ins_PUSHB,
    /*  PushB[3]  */  Ins_PUSHB,
    /*  PushB[4]  */  Ins_PUSHB,
    /*  PushB[5]  */  Ins_PUSHB,
    /*  PushB[6]  */  Ins_PUSHB,
    /*  PushB[7]  */  Ins_PUSHB,
    /*  PushW[0]  */  Ins_PUSHW,
    /*  PushW[1]  */  Ins_PUSHW,
    /*  PushW[2]  */  Ins_PUSHW,
    /*  PushW[3]  */  Ins_PUSHW,
    /*  PushW[4]  */  Ins_PUSHW,
    /*  PushW[5]  */  Ins_PUSHW,
    /*  PushW[6]  */  Ins_PUSHW,
    /*  PushW[7]  */  Ins_PUSHW,

    /*  MDRP[00]  */  Ins_MDRP,
    /*  MDRP[01]  */  Ins_MDRP,
    /*  MDRP[02]  */  Ins_MDRP,
    /*  MDRP[03]  */  Ins_MDRP,
    /*  MDRP[04]  */  Ins_MDRP,
    /*  MDRP[05]  */  Ins_MDRP,
    /*  MDRP[06]  */  Ins_MDRP,
    /*  MDRP[07]  */  Ins_MDRP,
    /*  MDRP[08]  */  Ins_MDRP,
    /*  MDRP[09]  */  Ins_MDRP,
    /*  MDRP[10]  */  Ins_MDRP,
    /*  MDRP[11]  */  Ins_MDRP,
    /*  MDRP[12]  */  Ins_MDRP,
    /*  MDRP[13]  */  Ins_MDRP,
    /*  MDRP[14]  */  Ins_MDRP,
    /*  MDRP[15]  */  Ins_MDRP,

    /*  MDRP[16]  */  Ins_MDRP,
    /*  MDRP[17]  */  Ins_MDRP,
    /*  MDRP[18]  */  Ins_MDRP,
    /*  MDRP[19]  */  Ins_MDRP,
    /*  MDRP[20]  */  Ins_MDRP,
    /*  MDRP[21]  */  Ins_MDRP,
    /*  MDRP[22]  */  Ins_MDRP,
    /*  MDRP[23]  */  Ins_MDRP,
    /*  MDRP[24]  */  Ins_MDRP,
    /*  MDRP[25]  */  Ins_MDRP,
    /*  MDRP[26]  */  Ins_MDRP,
    /*  MDRP[27]  */  Ins_MDRP,
    /*  MDRP[28]  */  Ins_MDRP,
    /*  MDRP[29]  */  Ins_MDRP,
    /*  MDRP[30]  */  Ins_MDRP,
    /*  MDRP[31]  */  Ins_MDRP,

    /*  MIRP[00]  */  Ins_MIRP,
    /*  MIRP[01]  */  Ins_MIRP,
    /*  MIRP[02]  */  Ins_MIRP,
    /*  MIRP[03]  */  Ins_MIRP,
    /*  MIRP[04]  */  Ins_MIRP,
    /*  MIRP[05]  */  Ins_MIRP,
    /*  MIRP[06]  */  Ins_MIRP,
    /*  MIRP[07]  */  Ins_MIRP,
    /*  MIRP[08]  */  Ins_MIRP,
    /*  MIRP[09]  */  Ins_MIRP,
    /*  MIRP[10]  */  Ins_MIRP,
    /*  MIRP[11]  */  Ins_MIRP,
    /*  MIRP[12]  */  Ins_MIRP,
    /*  MIRP[13]  */  Ins_MIRP,
    /*  MIRP[14]  */  Ins_MIRP,
    /*  MIRP[15]  */  Ins_MIRP,

    /*  MIRP[16]  */  Ins_MIRP,
    /*  MIRP[17]  */  Ins_MIRP,
    /*  MIRP[18]  */  Ins_MIRP,
    /*  MIRP[19]  */  Ins_MIRP,
    /*  MIRP[20]  */  Ins_MIRP,
    /*  MIRP[21]  */  Ins_MIRP,
    /*  MIRP[22]  */  Ins_MIRP,
    /*  MIRP[23]  */  Ins_MIRP,
    /*  MIRP[24]  */  Ins_MIRP,
    /*  MIRP[25]  */  Ins_MIRP,
    /*  MIRP[26]  */  Ins_MIRP,
    /*  MIRP[27]  */  Ins_MIRP,
    /*  MIRP[28]  */  Ins_MIRP,
    /*  MIRP[29]  */  Ins_MIRP,
    /*  MIRP[30]  */  Ins_MIRP,
    /*  MIRP[31]  */  Ins_MIRP
];

function TT_RunIns(exc)
{
    var ins_counter = 0;  /* executed instructions counter */

    var _tt_hints = exc.face.driver.library.tt_hint_props;
    if (_tt_hints.TT_CONFIG_OPTION_SUBPIXEL_HINTING) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        exc.iup_called = false;
    }//#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

    var opcode_pattern = [
        [
            0x06, /* SPVTL   */
            0x7D, /* RDTG    */
        ]
    ];
    var opcode_pointer = [ 0 ];
    var opcode_size    = [ 1 ];

    /* set CVT functions */
    exc.tt_metrics.ratio = 0;
    if (exc.metrics.x_ppem != exc.metrics.y_ppem)
    {
        /* non-square pixels, use the stretched routines */
        exc.func_read_cvt  = Read_CVT_Stretched;
        exc.func_write_cvt = Write_CVT_Stretched;
        exc.func_move_cvt  = Move_CVT_Stretched;
    }
    else
    {
        /* square pixels, use normal routines */
        exc.func_read_cvt  = Read_CVT;
        exc.func_write_cvt = Write_CVT;
        exc.func_move_cvt  = Move_CVT;
    }

    Compute_Funcs(exc);
    Compute_Round(exc, exc.GS.round_state);

    do
    {
        exc.opcode = exc.code.data[exc.code.pos + exc.IP];

        exc.length = opcode_length[exc.opcode];
        if (exc.length < 0)
        {
            if (exc.IP + 1 >= exc.codeSize)
                return SetErrorAndReturn(exc, 131);

            exc.length = 2 - exc.length * exc.code.data[exc.code.pos + exc.IP + 1];
        }

        if (exc.IP + exc.length > exc.codeSize)
            return SetErrorAndReturn(exc, 131);

        /* First, let's check for empty stack and overflow */
        exc.args = exc.top - (Pop_Push_Count[exc.opcode] >> 4);

        /* `args' is the top of the stack once arguments have been popped. */
        /* One can also interpret it as the index of the last argument.    */
        if (exc.args < 0)
        {
            if (exc.pedantic_hinting)
            {
                exc.error = 129;
                return SetErrorAndReturn(exc);
            }

            /* push zeroes onto the stack */
            var __l = Pop_Push_Count[exc.opcode] >> 4;
            for (var i = 0; i < __l; i++)
                exc.stack[i] = 0;
            exc.args = 0;
        }

        exc.new_top = exc.args + (Pop_Push_Count[exc.opcode] & 15);

        /* `new_top' is the new top of the stack, after the instruction's */
        /* execution.  `top' will be set to `new_top' after the `switch'  */
        /* statement.                                                     */
        if (exc.new_top > exc.stackSize)
        {
            exc.error = 130;
            return SetErrorAndReturn(exc);
        }

        exc.step_ins = true;
        exc.error    = 0;

        if (_tt_hints.TT_CONFIG_OPTION_SUBPIXEL_HINTING) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
        {
            for (var i_patt = 0; i_patt < opcode_pattern.length; i_patt++)
            {
                if (opcode_pointer[i_patt] < opcode_size[i_patt] &&
                    exc.opcode == opcode_pattern[i_patt][opcode_pointer[i_patt]])
                {
                    opcode_pointer[i_patt] += 1;

                    if ( opcode_pointer[i_patt] == opcode_size[i_patt] )
                    {
                        switch ( i_patt )
                        {
                            case 0:
                                break;
                        }
                        opcode_pointer[i_patt] = 0;
                    }
                }
                else
                    opcode_pointer[i_patt] = 0;
            }
        }//#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

        Instruct_Dispatch[exc.opcode](exc, exc.stack, exc.args);
 
        var bSuiteLabel = false;
        if (exc.error != 0)
        {
            switch (exc.error)
            {
                case 128: /* looking for redefined instructions */
                {
                    var defs  = exc.IDefs;
                    var limit = exc.numIDefs;

                    for (var def = 0; def < limit; def++)
                    {
                        var _def = defs[def];
                        if (def.active && exc.opcode == def.opc)
                        {
                            if (exc.callTop >= exc.callSize)
                            {
                                exc.error = 134;
                                return SetErrorAndReturn(exc);
                            }

                            var callrec = exc.callStack[exc.callTop];

                            callrec.Caller_Range = exc.curRange;
                            callrec.Caller_IP    = exc.IP + 1;
                            callrec.Cur_Count    = 1;
                            callrec.Def          = _def;

                            if (Ins_Goto_CodeRange(def.range, def.start) == 1)
                                return SetErrorAndReturn(exc);

                            bSuiteLabel = true;
                            break;
                        }
                    }
                }

                if (!bSuiteLabel)
                {
                    exc.error = 128;
                    return SetErrorAndReturn(exc);
                }

            default:
                if (!bSuiteLabel)
                    return SetErrorAndReturn(exc);
            }
        }

        if (!bSuiteLabel)
        {
            exc.top = exc.new_top;

            if (exc.step_ins)
                exc.IP += exc.length;

            /* increment instruction counter and check if we didn't */
            /* run this program for too long (e.g. infinite loops). */
            if (++ins_counter > 1000000)
                return 139;
        }

        if (exc.IP >= exc.codeSize)
        {
            if (exc.callTop > 0)
            {
                exc.error = 131;
                return SetErrorAndReturn(exc);
            }
            else
                return 0;
        }

    } while (!exc.instruction_trap);

    return 0;
}

function SetErrorAndReturn(exc, err)
{
    if (undefined !== err)
        exc.error = err;

    if (exc.error != 0 && !exc.instruction_trap)
    {
        exc.size.cvt_ready = false;
    }

    return exc.error;
}

function Read_CVT(exc, idx)
{
    return exc.cvt[idx];
}

function Read_CVT_Stretched(exc, idx)
{
    return FT_MulFix(exc.cvt[idx], Current_Ratio(exc));
}

function Write_CVT(exc, idx, value)
{
    exc.cvt[idx] = value;
}

function Write_CVT_Stretched(exc, idx, value)
{
    exc.cvt[idx] = FT_DivFix(value, Current_Ratio(exc));
}

function Move_CVT(exc, idx, value)
{
    exc.cvt[idx] += value;
}

function Move_CVT_Stretched(exc, idx, value)
{
    exc.cvt[idx] += FT_DivFix(value, Current_Ratio(exc));
}

/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
function CSubpixHintingHacks()
{
    this.Correct_Init_Obj = function(_class)
    {
        for (var prop in _class)
        {
            if (!_class.hasOwnProperty(prop))
                continue;

            var _arr = _class[prop];
            var _obj = {};

            var _len = _arr.length;
            for (var i = 0; i < _len; i++)
                _obj[_arr[i]] = true;

            _class[prop] = _obj;
        }
    };

    this.FAMILY_CLASS_Rules =
    {
        "MS Legacy Fonts" : [
            "Aharoni",
            "Andale Mono",
            "Andalus",
            "Angsana New",
            "AngsanaUPC",
            "Arabic Transparent",
            "Arial Black",
            "Arial Narrow",
            "Arial Unicode MS",
            "Arial",
            "Batang",
            "Browallia New",
            "BrowalliaUPC",
            "Comic Sans MS",
            "Cordia New",
            "CordiaUPC",
            "Courier New",
            "DFKai-SB",
            "David Transparent",
            "David",
            "DilleniaUPC",
            "Estrangelo Edessa",
            "EucrosiaUPC",
            "FangSong_GB2312",
            "Fixed Miriam Transparent",
            "FrankRuehl",
            "Franklin Gothic Medium",
            "FreesiaUPC",
            "Garamond",
            "Gautami",
            "Georgia",
            "Gulim",
            "Impact",
            "IrisUPC",
            "JasmineUPC",
            "KaiTi_GB2312",
            "KodchiangUPC",
            "Latha",
            "Levenim MT",
            "LilyUPC",
            "Lucida Console",
            "Lucida Sans Unicode",
            "MS Gothic",
            "MS Mincho",
            "MV Boli",
            "Mangal",
            "Marlett",
            "Microsoft Sans Serif",
            "Mingliu",
            "Miriam Fixed",
            "Miriam Transparent",
            "Miriam",
            "Narkisim",
            "Palatino Linotype",
            "Raavi",
            "Rod Transparent",
            "Rod",
            "Shruti",
            "SimHei",
            "Simplified Arabic Fixed",
            "Simplified Arabic",
            "Simsun",
            "Sylfaen",
            "Symbol",
            "Tahoma",
            "Times New Roman",
            "Traditional Arabic",
            "Trebuchet MS",
            "Tunga",
            "Verdana",
            "Webdings",
            "Wingdings"
        ],
        "Core MS Legacy Fonts" : [
            "Arial Black",
            "Arial Narrow",
            "Arial Unicode MS",
            "Arial",
            "Comic Sans MS",
            "Courier New",
            "Garamond",
            "Georgia",
            "Impact",
            "Lucida Console",
            "Lucida Sans Unicode",
            "Microsoft Sans Serif",
            "Palatino Linotype",
            "Tahoma",
            "Times New Roman",
            "Trebuchet MS",
            "Verdana"
        ],
        "Apple Legacy Fonts" : [
            "Geneva",
            "Times",
            "Monaco",
            "Century",
            "Chalkboard",
            "Lobster",
            "Century Gothic",
            "Optima",
            "Lucida Grande",
            "Gill Sans",
            "Baskerville",
            "Helvetica",
            "Helvetica Neue"
        ],
        "Legacy Sans Fonts" : [
            "Andale Mono",
            "Arial Unicode MS",
            "Arial",
            "Century Gothic",
            "Comic Sans MS",
            "Franklin Gothic Medium",
            "Geneva",
            "Lucida Console",
            "Lucida Grande",
            "Lucida Sans Unicode",
            "Lucida Sans Typewriter",
            "Microsoft Sans Serif",
            "Monaco",
            "Tahoma",
            "Trebuchet MS",
            "Verdana"
        ],
        "Misc Legacy Fonts" : [
            "Dark Courier"
        ],
        "Verdana Clones" : [
            "DejaVu Sans",
            "Bitstream Vera Sans"
        ],
        "Verdana and Clones" : [
            "DejaVu Sans",
            "Bitstream Vera Sans",
            "Verdana"
        ]
    };

    this.Correct_Init_Obj(this.FAMILY_CLASS_Rules);

    this.STYLE_CLASS_Rules =
    {
        "Regular Class" : [
            "Regular",
            "Book",
            "Medium",
            "Roman",
            "Normal"
        ],
        "Regular/Italic Class" : [
            "Regular",
            "Book",
            "Medium",
            "Italic",
            "Oblique",
            "Roman",
            "Normal"
        ],
        "Bold/BoldItalic Class" : [
            "Bold",
            "Bold Italic",
            "Black"
        ],
        "Bold/Italic/BoldItalic Class" : [
            "Bold",
            "Bold Italic",
            "Black",
            "Italic",
            "Oblique"
        ],
        "Regular/Bold Class" : [
            "Regular",
            "Book",
            "Medium",
            "Normal",
            "Roman",
            "Bold",
            "Black"
        ]
    };

    this.Correct_Init_Obj(this.STYLE_CLASS_Rules);

    /***************************************************************/
    /***************************************************************/
    /***************************************************************/
    this._create_SPH_TweakRule = function(family, ppem, style, glyph)
    {
        var _ret = new SPH_TweakRule();
        _ret.family = family;
        _ret.ppem = ppem;
        _ret.style = style;
        _ret.glyph = glyph;
        return _ret;
    };
    this._create_SPH_ScaleRule = function(family, ppem, style, glyph, scale)
    {
        var _ret = new SPH_ScaleRule();
        _ret.family = family;
        _ret.ppem = ppem;
        _ret.style = style;
        _ret.glyph = glyph;
        _ret.scale = scale;
        return _ret;
    };

    /* Force special legacy fixes for fonts.                                 */
    this.COMPATIBILITY_MODE_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Don't do subpixel (ignore_x_mode) hinting; do normal hinting.         */
    this.PIXEL_HINTING_Rules = [
        /* these characters are almost always safe */
        this._create_SPH_TweakRule("Courier New", 12, "Italic", "z".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 11, "Italic", "z".charCodeAt(0))
    ];

    /* Subpixel hinting ignores SHPIX rules on X.  Force SHPIX for these.    */
    this.DO_SHPIX_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Skip Y moves that start with a point that is not on a Y pixel         */
    /* boundary and don't move that point to a Y pixel boundary.             */
    this.SKIP_NONPIXEL_Y_MOVES_Rules = [
        /* fix vwxyz thinness*/
        this._create_SPH_TweakRule("Consolas", 0, "", 0),
        /* Fix thin middle stems */
        this._create_SPH_TweakRule("Core MS Legacy Fonts", 0, "Regular", 0),
        /* Cyrillic small letter I */
        this._create_SPH_TweakRule("Legacy Sans Fonts", 0, "", 0),
        /* Fix artifacts with some Regular & Bold */
        this._create_SPH_TweakRule("Verdana Clones", 0, "", 0)
    ];

    this.SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions = [
        /* Fixes < and > */
        this._create_SPH_TweakRule("Courier New", 0, "Regular", 0)
    ];

    this.SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules = [
        /* Maintain thickness of diagonal in 'N' */
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular/Bold Class", "N".charCodeAt(0)),
        this._create_SPH_TweakRule("Georgia", 0, "Regular/Bold Class", "N".charCodeAt(0))
    ];

    /* Skip Y moves that move a point off a Y pixel boundary.                */
    this.SKIP_OFFPIXEL_Y_MOVES_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    this.SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Round moves that don't move a point to a Y pixel boundary.            */
    this.ROUND_NONPIXEL_Y_MOVES_Rules = [
        /* Droid font instructions don't snap Y to pixels */
        this._create_SPH_TweakRule("Droid Sans", 0, "Regular/Italic Class", 0),
        this._create_SPH_TweakRule("Droid Sans Mono", 0, "", 0)
    ];

    this.ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Allow a Direct_Move along X freedom vector if matched.                */
    this.ALLOW_X_DMOVE_Rules = [
        /* Fixes vanishing diagonal in 4 */
        this._create_SPH_TweakRule("Verdana", 0, "Regular", "4".charCodeAt(0))
    ];

    this.RASTERIZER_35_Rules = [
        /* This seems to be the only way to make these look good */
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "i".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "j".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "r".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "a".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "n".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 0, "Regular", "p".charCodeAt(0)),
        this._create_SPH_TweakRule("Times", 0, "", 0)
    ];

    /* Don't round to the subpixel grid.  Round to pixel grid.               */
    this.NORMAL_ROUND_Rules = [
        /* Fix serif thickness for certain ppems */
        /* Can probably be generalized somehow   */
        this._create_SPH_TweakRule("Courier New", 0, "", 0)
    ];

    /* Skip IUP instructions if matched.                                     */
    this.SKIP_IUP_Rules = [
        this._create_SPH_TweakRule("Arial", 13, "Regular", "a".charCodeAt(0))
    ];

    /* Skip MIAP Twilight hack if matched.                                   */
    this.MIAP_HACK_Rules = [
        this._create_SPH_TweakRule("Geneva", 12, "", 0)
    ];

    /* Skip DELTAP instructions if matched.                                  */
    this.ALWAYS_SKIP_DELTAP_Rules = [
        this._create_SPH_TweakRule("Georgia", 0, "Regular", "k".charCodeAt(0)),
        /* fix various problems with e in different versions */
        this._create_SPH_TweakRule("Trebuchet MS", 14, "Regular", "e".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 13, "Regular", "e".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 15, "Regular", "e".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 0, "Italic", "v".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 0, "Italic", "w".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 0, "Regular", "Y".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 11, "Regular", "s".charCodeAt(0)),
        /* prevent problems with '3' and others */
        this._create_SPH_TweakRule("Verdana", 10, "Regular", 0),
        this._create_SPH_TweakRule("Verdana", 9, "Regular", 0),
        /* Cyrillic small letter short I */
        this._create_SPH_TweakRule("Legacy Sans Fonts", 0, "", 0x438),
        this._create_SPH_TweakRule("Legacy Sans Fonts", 0, "", 0x439),
        this._create_SPH_TweakRule("Arial", 10, "Regular", "6".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 0, "Bold/BoldItalic Class", "a".charCodeAt(0)),
        /* Make horizontal stems consistent with the rest */
        this._create_SPH_TweakRule("Arial", 24, "Bold", "a".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 25, "Bold", "a".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 24, "Bold", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 25, "Bold", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 34, "Bold", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 35, "Bold", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 36, "Bold", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 25, "Regular", "s".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 26, "Regular", "s".charCodeAt(0))
    ];

    /* Always do DELTAP instructions if matched.                             */
    this.ALWAYS_DO_DELTAP_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Don't allow ALIGNRP after IUP.                                        */
    this.NO_ALIGNRP_AFTER_IUP_Rules = [
        /* Prevent creation of dents in outline */
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Don't allow DELTAP after IUP.                                         */
    this.NO_DELTAP_AFTER_IUP_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* Don't allow CALL after IUP.                                           */
    this.NO_CALL_AFTER_IUP_Rules = [
        /* Prevent creation of dents in outline */
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    /* De-embolden these glyphs slightly.                                    */
    this.DEEMBOLDEN_Rules = [
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "A".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "W".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "w".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "M".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "X".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "K".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "x".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "z".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 0, "Bold", "v".charCodeAt(0))
    ];

    /* Embolden these glyphs slightly.                                       */
    this.EMBOLDEN_Rules = [
        this._create_SPH_TweakRule("Courier New", 0, "Regular", 0),
        this._create_SPH_TweakRule("Courier New", 0, "Italic", 0)
    ];

    /* This is a CVT hack that makes thick horizontal stems on 2, 5, 7       */
    /* similar to Windows XP.                                                */
    this.TIMES_NEW_ROMAN_HACK_Rules = [
        this._create_SPH_TweakRule("Times New Roman", 16, "Italic", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Italic", "5".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Italic", "7".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular", "5".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular", "7".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Italic", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Italic", "5".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Italic", "7".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Regular", "5".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 17, "Regular", "7".charCodeAt(0))
    ];

    /* This fudges distance on 2 to get rid of the vanishing stem issue.     */
    /* A real solution to this is certainly welcome.                         */
    this.COURIER_NEW_2_HACK_Rules = [
        this._create_SPH_TweakRule("Courier New", 10, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 11, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 12, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 13, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 14, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 15, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 16, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 17, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 18, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 19, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 20, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 21, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 22, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 23, "Regular", "2".charCodeAt(0)),
        this._create_SPH_TweakRule("Courier New", 24, "Regular", "2".charCodeAt(0))
    ];


    this.FORCE_NATURAL_WIDTHS = false;

    /* Use compatible widths with these glyphs.  Compatible widths is always */
    /* on when doing B/W TrueType instructing, but is used selectively here, */
    /* typically on glyphs with 3 or more vertical stems.                    */
    this.COMPATIBLE_WIDTHS_Rules = [
        this._create_SPH_TweakRule("Arial Unicode MS", 12, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial Unicode MS", 14, "Regular Class", "m".charCodeAt(0)),
        /* Cyrillic small letter sha */
        this._create_SPH_TweakRule("Arial", 10, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Arial", 11, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Arial", 12, "Regular Class", "m".charCodeAt(0)),
        /* Cyrillic small letter sha */
        this._create_SPH_TweakRule("Arial", 12, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Arial", 13, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Arial", 14, "Regular Class", "m".charCodeAt(0)),
        /* Cyrillic small letter sha */
        this._create_SPH_TweakRule("Arial", 14, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Arial", 15, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Arial", 17, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("DejaVu Sans", 15, "Regular Class", 0),
        this._create_SPH_TweakRule("Microsoft Sans Serif", 11, "Regular Class", 0),
        this._create_SPH_TweakRule("Microsoft Sans Serif", 12, "Regular Class", 0),
        this._create_SPH_TweakRule("Segoe UI", 11, "Regular Class", 0),
        this._create_SPH_TweakRule("Monaco", 0, "Regular Class", 0),
        this._create_SPH_TweakRule("Segoe UI", 12, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Segoe UI", 14, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Tahoma", 11, "Regular Class", 0),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular Class", "c".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular Class", "o".charCodeAt(0)),
        this._create_SPH_TweakRule("Times New Roman", 16, "Regular Class", "w".charCodeAt(0)),
        this._create_SPH_TweakRule("Trebuchet MS", 11, "Regular Class", 0),
        this._create_SPH_TweakRule("Trebuchet MS", 12, "Regular Class", 0),
        this._create_SPH_TweakRule("Trebuchet MS", 14, "Regular Class", 0),
        this._create_SPH_TweakRule("Trebuchet MS", 15, "Regular Class", 0),
        this._create_SPH_TweakRule("Ubuntu", 12, "Regular Class", "m".charCodeAt(0)),
        /* Cyrillic small letter sha */
        this._create_SPH_TweakRule("Verdana", 10, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Verdana", 11, "Regular Class", 0x448),
        this._create_SPH_TweakRule("Verdana and Clones", 12, "Regular Class", "i".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 12, "Regular Class", "j".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 12, "Regular Class", "l".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 12, "Regular Class", "m".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 13, "Regular Class", "i".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 13, "Regular Class", "j".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 13, "Regular Class", "l".charCodeAt(0)),
        this._create_SPH_TweakRule("Verdana and Clones", 14, "Regular Class", "m".charCodeAt(0))
    ];


    /* Scaling slightly in the x-direction prior to hinting results in       */
    /* more visually pleasing glyphs in certain cases.                       */
    /* This sometimes needs to be coordinated with compatible width rules.   */
    /* A value of 1000 corresponds to a scaled value of 1.0.                 */
    this.X_SCALING_Rules = [
        this._create_SPH_ScaleRule("DejaVu Sans", 12, "Regular Class", "m".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Verdana and Clones", 12, "Regular Class", "a".charCodeAt(0), 1100),
        this._create_SPH_ScaleRule("Verdana and Clones", 13, "Regular Class", "a".charCodeAt(0), 1050),
        this._create_SPH_ScaleRule("Arial", 11, "Regular Class", "m".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("Arial", 12, "Regular Class", "m".charCodeAt(0), 1050),
        /* Cyrillic small letter el */
        this._create_SPH_ScaleRule("Arial", 13, "Regular Class", 0x43B, 950),
        this._create_SPH_ScaleRule("Arial", 13, "Regular Class", "o".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Arial", 13, "Regular Class", "e".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Arial", 14, "Regular Class", "m".charCodeAt(0), 950),
        /* Cyrillic small letter el */
        this._create_SPH_ScaleRule("Arial", 15, "Regular Class", 0x43B, 925),
        this._create_SPH_ScaleRule("Bitstream Vera Sans", 10, "Regular/Italic Class", 0, 1100),
        this._create_SPH_ScaleRule("Bitstream Vera Sans", 12, "Regular/Italic Class", 0, 1050),
        this._create_SPH_ScaleRule("Bitstream Vera Sans", 16, "Regular Class", 0, 1050),
        this._create_SPH_ScaleRule("Bitstream Vera Sans", 9, "Regular/Italic Class", 0, 1050),
        this._create_SPH_ScaleRule("DejaVu Sans", 12, "Regular Class", "l".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("DejaVu Sans", 12, "Regular Class", "i".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("DejaVu Sans", 12, "Regular Class", "j".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("DejaVu Sans", 13, "Regular Class", "l".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("DejaVu Sans", 13, "Regular Class", "i".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("DejaVu Sans", 13, "Regular Class", "j".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("DejaVu Sans", 10, "Regular/Italic Class", 0, 1100),
        this._create_SPH_ScaleRule("DejaVu Sans", 12, "Regular/Italic Class", 0, 1050),
        this._create_SPH_ScaleRule("Georgia", 10, "", 0, 1050),
        this._create_SPH_ScaleRule("Georgia", 11, "", 0, 1100),
        this._create_SPH_ScaleRule("Georgia", 12, "", 0, 1025),
        this._create_SPH_ScaleRule("Georgia", 13, "", 0, 1050),
        this._create_SPH_ScaleRule("Georgia", 16, "", 0, 1050),
        this._create_SPH_ScaleRule("Georgia", 17, "", 0, 1030),
        this._create_SPH_ScaleRule("Liberation Sans", 12, "Regular Class", "m".charCodeAt(0), 1100),
        this._create_SPH_ScaleRule("Lucida Grande", 11, "Regular Class", "m".charCodeAt(0), 1100),
        this._create_SPH_ScaleRule("Microsoft Sans Serif", 11, "Regular Class", "m".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Microsoft Sans Serif", 12, "Regular Class", "m".charCodeAt(0), 1050),
        this._create_SPH_ScaleRule("Segoe UI", 12, "Regular Class", "H".charCodeAt(0), 1050),
        this._create_SPH_ScaleRule("Segoe UI", 12, "Regular Class", "m".charCodeAt(0), 1050),
        this._create_SPH_ScaleRule("Segoe UI", 14, "Regular Class", "m".charCodeAt(0), 1050),
        this._create_SPH_ScaleRule("Tahoma", 11, "Regular Class", "i".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("Tahoma", 11, "Regular Class", "l".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("Tahoma", 11, "Regular Class", "j".charCodeAt(0), 900),
        this._create_SPH_ScaleRule("Tahoma", 11, "Regular Class", "m".charCodeAt(0), 918),
        this._create_SPH_ScaleRule("Verdana", 10, "Regular/Italic Class", 0, 1100),
        this._create_SPH_ScaleRule("Verdana", 12, "Regular Class", "m".charCodeAt(0), 975),
        this._create_SPH_ScaleRule("Verdana", 12, "Regular/Italic Class", 0, 1050),
        this._create_SPH_ScaleRule("Verdana", 13, "Regular/Italic Class", "i".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Verdana", 13, "Regular/Italic Class", "j".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Verdana", 13, "Regular/Italic Class", "l".charCodeAt(0), 950),
        this._create_SPH_ScaleRule("Verdana", 16, "Regular Class", 0, 1050),
        this._create_SPH_ScaleRule("Verdana", 9, "Regular/Italic Class", 0, 1050),
        this._create_SPH_ScaleRule("Times New Roman", 16, "Regular Class", "m".charCodeAt(0), 918),
        this._create_SPH_ScaleRule("Trebuchet MS", 11, "Regular Class", "m".charCodeAt(0), 800),
        this._create_SPH_ScaleRule("Trebuchet MS", 12, "Regular Class", "m".charCodeAt(0), 800)
    ];

    /*
    this.COMPATIBLE_WIDTHS_Rules = [
        this._create_SPH_TweakRule("-", 0, "", 0)
    ];

    this.X_SCALING_Rules = [
        this._create_SPH_ScaleRule("-", 0, "", 0, 1000)
    ];
    */

    this.TWEAK_RULES = function(_loader, _glyph_index, _rules, _flag)
    {
        var face = _loader.face;
        if (this.sph_test_tweak(face, face.family_name, _loader.size.metrics.x_ppem, face.style_name, _glyph_index, _rules, _rules.length))
            _loader.exec.sph_tweak_flags |= _flag;
    };
    this.TWEAK_RULES_EXCEPTIONS = function(_loader, _glyph_index, _rules, _flag)
    {
        var face = _loader.face;
        if (this.sph_test_tweak(face, face.family_name, _loader.size.metrics.x_ppem, face.style_name, _glyph_index, _rules, _rules.length))
            _loader.exec.sph_tweak_flags &= ~_flag;
    };

    this.is_member_of_family_class = function(detected_font_name, rule_font_name)
    {
        /* Does font name match rule family? */
        if (detected_font_name == rule_font_name)
            return true;

        /* Is font name a wildcard ""? */
        if (rule_font_name == "")
            return true;

        var _fcr = this.FAMILY_CLASS_Rules[rule_font_name];
        if (undefined !== _fcr)
        {
            if (undefined !== _fcr[detected_font_name])
                return true;
        }

        return false;
    };

    this.is_member_of_style_class = function(detected_font_style, rule_font_style)
    {
        /* Does font style match rule style? */
        if (detected_font_style == rule_font_style)
            return true;

        /* Is font style a wildcard ""? */
        if (rule_font_style == "")
            return true;

        /* Is font style contained in a class list? */
        var _scr = this.STYLE_CLASS_Rules[rule_font_style];
        if (undefined !== _scr)
        {
            if (undefined !== _scr[detected_font_style])
                return true;
        }

        return false;
    };

    this.sph_test_tweak = function(face, family, ppem, style, glyph_index, rule, num_rules)
    {
        /* rule checks may be able to be optimized further */
        for (var i = 0; i < num_rules; i++)
        {
            if (family != "" && this.is_member_of_family_class(family, rule[i].family))
            {
                if (rule[i].ppem == 0 || rule[i].ppem == ppem)
                {
                    if (style != "" && this.is_member_of_style_class(style, rule[i].style))
                    {
                        if (rule[i].glyph == 0 || FT_Get_Char_Index(face, rule[i].glyph) == glyph_index)
                            return true;
                    }
                }
            }
        }
        return false;
    };

    this.scale_test_tweak = function(face, family, ppem, style, glyph_index, rule, num_rules)
    {
        /* rule checks may be able to be optimized further */
        for (var i = 0; i < num_rules; i++)
        {
            if (family != "" && this.is_member_of_family_class(family, rule[i].family))
            {
                if (rule[i].ppem == 0 || rule[i].ppem == ppem)
                {
                    if (style != "" && this.is_member_of_style_class(style, rule[i].style))
                    {
                        if (rule[i].glyph == 0 || FT_Get_Char_Index(face, rule[i].glyph) == glyph_index)
                            return rule[i].scale;
                    }
                }
            }
        }
        return 1000;
    };

    this.sph_test_tweak_x_scaling = function(face, family, ppem, style, glyph_index)
    {
        return this.scale_test_tweak(face, family, ppem, style, glyph_index, this.X_SCALING_Rules, this.X_SCALING_Rules.length);
    };

    this.sph_set_tweaks = function(loader, glyph_index)
    {
        var face = loader.face;

        /* don't apply rules if style isn't set */
        if (face.style_name == "")
            return;

        this.TWEAK_RULES(loader, glyph_index, this.PIXEL_HINTING_Rules, 4096);

        if (loader.exec.sph_tweak_flags & 4096)
        {
            loader.exec.ignore_x_mode = false;
            return;
        }

        this.TWEAK_RULES(loader, glyph_index, this.ALLOW_X_DMOVE_Rules, 1);
        this.TWEAK_RULES(loader, glyph_index, this.ALWAYS_DO_DELTAP_Rules, 2);
        this.TWEAK_RULES(loader, glyph_index, this.ALWAYS_SKIP_DELTAP_Rules, 4);
        this.TWEAK_RULES(loader, glyph_index, this.DEEMBOLDEN_Rules, 16);
        this.TWEAK_RULES(loader, glyph_index, this.DO_SHPIX_Rules, 32);
        this.TWEAK_RULES(loader, glyph_index, this.EMBOLDEN_Rules, 64);
        this.TWEAK_RULES(loader, glyph_index, this.MIAP_HACK_Rules, 128);
        this.TWEAK_RULES(loader, glyph_index, this.NORMAL_ROUND_Rules, 256);
        this.TWEAK_RULES(loader, glyph_index, this.NO_ALIGNRP_AFTER_IUP_Rules, 512);
        this.TWEAK_RULES(loader, glyph_index, this.NO_CALL_AFTER_IUP_Rules, 1024);
        this.TWEAK_RULES(loader, glyph_index, this.NO_DELTAP_AFTER_IUP_Rules, 2048);
        this.TWEAK_RULES(loader, glyph_index, this.RASTERIZER_35_Rules, 8192);
        this.TWEAK_RULES(loader, glyph_index, this.SKIP_IUP_Rules, 32768);

        this.TWEAK_RULES(loader, glyph_index, this.SKIP_OFFPIXEL_Y_MOVES_Rules, 131072);
        this.TWEAK_RULES_EXCEPTIONS(loader, glyph_index, this.SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions, 131072);

        this.TWEAK_RULES(loader, glyph_index, this.SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules, 524288);

        this.TWEAK_RULES(loader, glyph_index, this.SKIP_NONPIXEL_Y_MOVES_Rules, 65536);
        this.TWEAK_RULES_EXCEPTIONS(loader, glyph_index, this.SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions, 65536);

        this.TWEAK_RULES(loader, glyph_index, this.ROUND_NONPIXEL_Y_MOVES_Rules, 16384);
        this.TWEAK_RULES_EXCEPTIONS(loader, glyph_index, this.ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions, 16384);

        if (loader.exec.sph_tweak_flags & 8192)
        {
            if (loader.exec.rasterizer_version != 35)
            {
                loader.exec.rasterizer_version  = 35;
                loader.exec.size.cvt_ready      = false;

                tt_size_ready_bytecode(loader.exec.size, (loader.load_flags & 128) != 0);
            }
            else
                loader.exec.rasterizer_version  = 35;
        }
        else
        {
            if (loader.exec.rasterizer_version != 38)
            {
                loader.exec.rasterizer_version  = 38;
                loader.exec.size.cvt_ready      = false;

                tt_size_ready_bytecode(loader.exec.size, (loader.load_flags & 128) != 0);
            }
            else
                loader.exec.rasterizer_version = 38;
        }

        if ((loader.load_flags & 2) == 0)
        {
            this.TWEAK_RULES(loader, glyph_index, this.TIMES_NEW_ROMAN_HACK_Rules, 262144);
            this.TWEAK_RULES(loader, glyph_index, this.COURIER_NEW_2_HACK_Rules, 8);
        }

        if (this.sph_test_tweak(face, face.family_name, loader.size.metrics.x_ppem, face.style_name, glyph_index, this.COMPATIBILITY_MODE_Rules, this.COMPATIBILITY_MODE_Rules.length))
            loader.exec.face.sph_compatibility_mode = true;

        if (((loader.load_flags & 2) == 0) && !loader.exec.compatible_widths)
        {
            if (this.sph_test_tweak(face, face.family_name, loader.size.metrics.x_ppem, face.style_name, glyph_index, this.COMPATIBLE_WIDTHS_Rules, this.COMPATIBLE_WIDTHS_Rules.length))
                loader.exec.face.sph_compatibility_mode = true;
        }
    };
}

var global_SubpixHintingHacks = new CSubpixHintingHacks();
/******************************************************************************/
// classes
/******************************************************************************/
function TT_Size_Metrics()
{
    this.x_ratio = 0;
    this.y_ratio = 0;

    this.ppem   = 0;
    this.ratio  = 0;
    this.scale  = 0;

    this.compensations = new Array(4);

    this.valid = 0;

    this.rotated = 0;
    this.stretched = 0;
}
TT_Size_Metrics.prototype =
{
    Copy : function(src)
    {
        this.x_ratio = src.x_ratio;
        this.y_ratio = src.y_ratio;

        this.ppem   = src.ppem;
        this.ratio  = src.ratio;
        this.scale  = src.scale;

        this.compensations[0] = src.compensations[0];
        this.compensations[1] = src.compensations[1];
        this.compensations[2] = src.compensations[2];
        this.compensations[3] = src.compensations[3];

        this.valid = src.valid;

        this.rotated = src.rotated;
        this.stretched = src.stretched;
    }
};

function TT_DefRecord()
{
    this.range  = 0;        /* in which code range is it located?     */
    this.start  = 0;        /* where does it start?                   */
    this.end    = 0;          /* where does it end?                     */
    this.opc    = 0;          /* function #, or instruction code        */
    this.active = false;       /* is it active?                          */
    this.inline_delta = false; /* is function that defines inline delta? */
}

function TT_CodeRange()
{
    this.base = null;
    this.size = 0;
}

function TT_SizeRec()
{
    this.face = null;
    this.generic = null;
    this.metrics = new FT_Size_Metrics();
    this.internal = null;

    /* we have our own copy of metrics so that we can modify */
    /* it without affecting auto-hinting (when used)         */
    this._metrics = new FT_Size_Metrics();

    this.ttmetrics = new TT_Size_Metrics();

    this.strike_index = 0;
    //#ifdef TT_USE_BYTECODE_INTERPRETER
    this.num_function_defs = 0; /* number of function definitions */
    this.max_function_defs = 0;
    this.function_defs = null;     /* table of function definitions  */

    this.num_instruction_defs = 0;  /* number of ins. definitions */
    this.max_instruction_defs = 0;
    this.instruction_defs =  null;      /* table of ins. definitions  */

    this.max_func = 0;
    this.max_ins = 0;

    this.codeRangeTable = new Array(3);
    this.codeRangeTable[0] = new TT_CodeRange();
    this.codeRangeTable[1] = new TT_CodeRange();
    this.codeRangeTable[2] = new TT_CodeRange();

    this.GS = new TT_GraphicsState();

    this.cvt_size = 0;      /* the scaled control value table */
    this.cvt = null;

    this.storage_size = 0; /* The storage area is now part of */
    this.storage = null;      /* the instance                    */

    this.twilight = new TT_GlyphZoneRec();     /* The instance's twilight zone    */

    /* debugging variables */

    /* When using the debugger, we must keep the */
    /* execution context tied to the instance    */
    /* object rather than asking it on demand.   */
    this.debug = false;
    this.context = null;

    this.bytecode_ready = false;
    this.cvt_ready = false;
    this.ttfautohinted = false;
    //#endif
}
function TT_Face()
{
    this.num_faces = 0;
    this.face_index = 0;

    this.face_flags = 0;
    this.style_flags = 0;

    this.num_glyphs = 0;

    this.family_name = "";
    this.style_name = "";

    this.num_fixed_sizes = 0;
    this.available_sizes = null;

    this.num_charmaps = 0;
    this.charmaps = null;

    this.generic = new FT_Generic();

    /*# The following member variables (down to `underline_thickness') */
    /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
    /*# for bitmap fonts.                                              */
    this.bbox = new FT_BBox();

    this.units_per_EM = 0;
    this.ascender = 0;
    this.descender = 0;
    this.height = null;

    this.max_advance_width = 0;
    this.max_advance_height = 0;

    this.underline_position = 0;
    this.underline_thickness = 0;

    this.glyph = null;
    this.size = null;
    this.charmap = null;

    /*@private begin */
    this.driver = null;
    this.memory = null;
    this.stream = null;

    this.sizes_list = [];

    this.autohint = [];
    this.extensions = null;

    this.internal = null;
    /*@private end */

    this.ttc_header = new TTC_HeaderRec();

    this.format_tag = 0;
    this.num_tables = 0;
    this.dir_tables = null;

    this.header = new TT_Header();
    this.horizontal = new TT_HoriHeader();

    this.max_profile = new TT_MaxProfile();
    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.max_components = 0;
    //#endif

    this.vertical_info = false;
    this.vertical = new TT_VertHeader();

    this.num_names = 0;
    this.name_table = new TT_NameTableRec();

    this.os2 = new TT_OS2();
    this.postscript = new TT_Postscript();

    this.cmap_table = null;
    this.cmap_size = 0;

    this.sfnt = null;
    this.psnames = null;

    // horizontal device metrics
    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.hdmx = new TT_HdmxRec();
    //#endif

    // grid-fitting and scaling table
    this.gasp = new TT_Gasp(); // the `gasp' table

    // PCL 5 tabl
    this.pclt = new TT_PCLT();

    // embedded bitmaps support
    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.num_sbit_strikes = 0;
    this.sbit_strikes = null;
    //#endif

    this.num_sbit_scales = 0;
    this.sbit_scales = null;

    // postscript names table
    this.postscript_names = new TT_Post_NamesRec();

    // TrueType-specific fields (ignored by the OTF-Type2 driver)
    // the glyph locations
    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.num_locations_stub = 0;
    this.glyph_locations_stub = null;
    //#endif

    // the font program, if any
    this.font_program_size = 0;
    this.font_program = null;

    // the cvt program, if any
    this.cvt_program_size = 0;
    this.cvt_program = null;

    // the original, unscaled, control value table
    this.cvt_size = 0;
    this.cvt = null;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    // the format 0 kerning table, if any
    this.num_kern_pairs = 0;
    this.kern_table_index = 0;
    this.kern_pairs = [];
    //#endif

    // A pointer to the bytecode interpreter to use.  This is also
    // used to hook the debugger for the `ttdebug' utility.
    this.interpreter = null;

    //#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    // Use unpatented hinting only.
    this.unpatented_hinting = false;
    //#endif

    // Other tables or fields. This is used by derivative formats like OpenType.
    this.extra = new FT_Generic();

    this.postscript_name = "";

    // since version 2.1.8, but was originally placed after
    // `glyph_locations_stub'
    this.glyf_len = 0;

    // since version 2.1.8, but was originally placed before `extra'
    //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    this.doblend = 0;
    this.blend = null;
    //#endif

    // since version 2.2

    this.horz_metrics = null;
    this.horz_metrics_size = 0;

    this.vert_metrics = null;
    this.vert_metrics_size = 0;

    this.num_locations = 0; // in broken TTF, gid > 0xFFFF
    this.glyph_locations = null;

    this.hdmx_table = null;
    this.hdmx_table_size = 0;
    this.hdmx_record_count = 0;
    this.hdmx_record_size = 0;
    this.hdmx_record_sizes = null;

    this.sbit_table = null;
    this.sbit_table_size = 0;
    this.sbit_num_strikes = 0;

    this.kern_table = null;
    this.kern_table_size = 0;
    this.num_kern_tables = 0;
    this.kern_avail_bits = 0;
    this.kern_order_bits = 0;

    //#ifdef TT_CONFIG_OPTION_BDF
    this.bdf = new TT_BDFRec();
    //#endif

    // since 2.3.0
    this.horz_metrics_offset = 0;
    this.vert_metrics_offset = 0;

    this.goto_table = null;
    this.access_glyph_frame = null;
    this.forget_glyph_frame = null;
    this.read_glyph_header = null;
    this.read_simple_glyph = null;
    this.read_composite_glyph = null;

    this.sph_found_func_flags = 0;  /* special functions found */
                                    /* for this face           */
    this.sph_compatibility_mode = false;
}
/******************************************************************************/
// gxvar
/******************************************************************************/
function GX_AVarCorrespondenceRec()
{
    this.fromCoord  = 0;
    this.toCoord    = 0;
}
function GX_AVarSegmentRec()
{
    this.pairCount = 0;
    this.correspondence = null;
}
function GX_BlendRec()
{
    this.num_axis = 0;
    this.normalizedcoords = null;

    this.mmvar = null;
    this.mmvar_len = 0;

    this.avar_checked = 0;
    this.avar_segment = null;

    this.tuplecount = 0;
    this.tuplecoords = null;

    this.gv_glyphcnt = 0;
    this.glyphoffsets = null;
}
function GX_GVar_Head()
{
    this.version = 0;
    this.axisCount = 0;
    this.globalCoordCount = 0;
    this.offsetToCoord = 0;
    this.glyphCount = 0;
    this.flags = 0;
    this.offsetToData = 0;
}
function GX_FVar_Head()
{
    this.version = 0;
    this.offsetToData = 0;
    this.countSizePairs = 0;
    this.axisCount = 0;
    this.axisSize = 0;
    this.instanceCount = 0;
    this.instanceSize = 0;
}
function GX_FVar_Axis()
{
    this.axisTag = 0;
    this.minValue = 0;
    this.defaultValue = 0;
    this.maxValue = 0;
    this.flags = 0;
    this.nameID = 0;
}
function ft_var_readpackedpoints(stream)
{
    var points = null;
    var point_cnt = 0;

    var n;
    var runcnt;
    var j;
    var first;
    point_cnt = n = stream.GetUChar();

    if (n == 0)
        return {points:points, point_cnt:point_cnt};

    if (n & 0x80)
        n = stream.GetUChar() | ((n & 0x7F) << 8);

    points = new Array(n);
    var i = 0;
    while (i < n)
    {
        runcnt = stream.GetUChar();
        if (runcnt & 0x80)
        {
            runcnt = runcnt & 0x7F;
            first  = points[i++] = stream.GetUShort();

            if (runcnt < 1 || i + runcnt >= n)
                return {points:points, point_cnt:point_cnt};

            /* first point not included in runcount */
            for (j = 0; j < runcnt; ++j)
            {
                first += stream.GetUShort();
                points[i++] = first & 0xFFFF;
            }
        }
        else
        {
            first = points[i++] = stream.GetUChar();

            if (runcnt < 1 || i + runcnt >= n)
                return {points:points, point_cnt:point_cnt};

            for (j = 0; j < runcnt; ++j)
            {
                first += stream.GetUChar();
                points[i++] = first & 0xFFFF;
            }
        }
    }

    return {points:points, point_cnt:point_cnt};
}
function ft_var_readpackeddeltas(stream, delta_cnt)
{
    var runcnt;
    var j;

    var deltas = new Array(delta_cnt);
    var i = 0;
    while (i < delta_cnt)
    {
        runcnt = stream.GetUChar();
        if ((runcnt & 128)!=0)
        {
            for (j = 0;j <= (runcnt & 63) && i < delta_cnt;j++)
                deltas[i++] = 0;
        }
        else if ((runcnt & 64)!=0)
        {
            for (j = 0;j <= (runcnt & 63) && i < delta_cnt;j++)
                deltas[i++] = stream.GetShort();
        }
        else
        {
            for (j = 0;j <= (runcnt & 63) && i < delta_cnt;j++)
                deltas[i++] = stream.GetChar();
        }

        if (j <= (runcnt & 63))
        {
            deltas = null;
            return null;
        }
    }
    return deltas;
}
function ft_var_load_avar(face)
{
    var stream = face.stream;
    var blend  = face.blend;
    var segment = null;
    var i, j;

    blend.avar_checked = 1;
    var table_len = face.goto_table(face, 1635148146, stream);
    var error = FT_Error;
    if (error != 0)
        return;

    error = stream.EnterFrame(table_len);
    if (error != 0)
        return;

    var version   = stream.GetLong();
    var axisCount = stream.GetLong();

    if (version != 0x00010000 || axisCount != blend.mmvar.num_axis)
    {
        stream.ExitFrame();
        return;
    }

    blend.avar_segment = new Array(axisCount);
    for (i=0;i<axisCount;i++)
        blend.avar_segment[i] = new GX_AVarSegmentRec();

    for (i = 0; i < axisCount; ++i)
    {
        segment = blend.avar_segment[i];
        segment.pairCount = stream.GetUShort();
        segment.correspondence = new Array(segment.pairCount);

        for (j = 0; j < segment.pairCount; ++j)
        {
            segment.correspondence[j].fromCoord = stream.GetShort() << 2;
            segment.correspondence[j].toCoord = stream.GetShort() << 2;
        }
    }
    stream.ExitFrame();
}
function ft_var_load_gvar(face)
{
    var stream = face.stream;
    var blend  = face.blend;
    var error = 0;
    var i, j;
    var gvar_head = new GX_GVar_Head();

    var table_len = face.goto_table(face, 1735811442, stream);
    error = FT_Error;
    if (error != 0)
        return error;

    var gvar_start = stream.pos;

    error = stream.EnterFrame(20);
    if (error != 0)
        return error;

    gvar_head.version = stream.GetLong();
    gvar_head.axisCount = stream.GetUShort();
    gvar_head.globalCoordCount = stream.GetUShort();
    gvar_head.offsetToCoord = stream.GetULong();
    gvar_head.glyphCount = stream.GetUShort();
    gvar_head.flags = stream.GetUShort();
    gvar_head.offsetToData = stream.GetULong();

    stream.ExitFrame();

    blend.tuplecount  = gvar_head.globalCoordCount;
    blend.gv_glyphcnt = gvar_head.glyphCount;
    var offsetToData = gvar_start + gvar_head.offsetToData;

    if (gvar_head.version != 0x00010000 || gvar_head.axisCount != blend.mmvar.num_axis)
        return 8;

    blend.glyphoffsets = new Array(blend.gv_glyphcnt + 1);
    if ((gvar_head.flags & 1) != 0)
    {
        error = stream.EnterFrame((blend.gv_glyphcnt + 1)*4);
        if (error != 0)
            return error;

        for (i = 0; i <= blend.gv_glyphcnt; ++i)
            blend.glyphoffsets[i] = offsetToData + stream.GetLong();

        stream.ExitFrame();
    }
    else
    {
        error = stream.EnterFrame((blend.gv_glyphcnt + 1)*2);
        if (error != 0)
            return error;

        for (i = 0; i <= blend.gv_glyphcnt; ++i)
            blend.glyphoffsets[i] = offsetToData + stream.GetUShort() * 2;

        stream.ExitFrame();
    }

    if (blend.tuplecount != 0)
    {
        blend.tuplecoords = new Array(gvar_head.axisCount * blend.tuplecount);
        error = stream.Seek(gvar_start + gvar_head.offsetToCoord);
        if (error != 0)
            return error;

        error = stream.EnterFrame(blend.tuplecount * gvar_head.axisCount * 2);

        for (i = 0; i < blend.tuplecount; i++)
        {
            for (j=0;j<gvar_head.axisCount;j++)
            {
                blend.tuplecoords[i * gvar_head.axisCount + j] = stream.GetShort() << 2;
            }
        }

        stream.ExitFrame();
    }
    return error;
}
function ft_var_apply_tuple(blend, tupleIndex, tuple_coords, im_start_coords, im_end_coords)
{
    var apply = 0x10000;
    var temp = 0;
    var i = 0;
    var c = blend.num_axis;
    var nn = blend.normalizedcoords;
    for (; i < c;i++)
    {
        if (tuple_coords[i] == 0)
            continue;
        else if (nn[i] == 0 || (nn[i] < 0 && tuple_coords[i] > 0) || (nn[i] > 0 && tuple_coords[i] < 0))
        {
            apply = 0;
            break;
        }
        else if (0 == (tupleIndex & 16384))
            apply = FT_MulDiv(apply, nn[i] > 0 ? nn[i] : -nn[i], 0x10000);

        else if (nn[i] <= im_start_coords[i] || nn[i] >= im_end_coords[i])
        {
            apply = 0;
            break;
        }
        else if (nn[i] < tuple_coords[i])
        {
            temp = FT_MulDiv(nn[i] - im_start_coords[i], 0x10000, tuple_coords[i] - im_start_coords[i]);
            apply = FT_MulDiv(apply, temp, 0x10000);
        }
        else
        {
            temp = FT_MulDiv(im_end_coords[i] - nn[i], 0x10000, im_end_coords[i] - tuple_coords[i]);
            apply = FT_MulDiv(apply, temp, 0x10000);
        }
    }
    return apply;
}
function TT_Get_MM_Var(face, bIsRet)
{
    var stream = face.stream;
    var table_len;
    FT_Error = 0;
    var fvar_start;
    var i, j;
    var mmvar = null;
    var fvar_head = new GX_FVar_Head();

    if (face.blend == null)
    {
        table_len = face.goto_table(face, 1735811442, stream);
        if (FT_Error != 0)
            return null;

        table_len = face.goto_table(face, 1719034226, stream);
        if (FT_Error != 0)
            return null;

        fvar_start = stream.pos;

        FT_Error = stream.EnterFrame(16);
        if (FT_Error != 0)
            return null;
        fvar_head.version = stream.GetULong();
        fvar_head.offsetToData = stream.GetUShort();
        fvar_head.countSizePairs = stream.GetUShort();
        fvar_head.axisCount = stream.GetUShort();
        fvar_head.axisSize = stream.GetUShort();
        fvar_head.instanceCount = stream.GetUShort();
        fvar_head.instanceSize = stream.GetUShort();
        stream.ExitFrame();


        if (fvar_head.version != 0x00010000 || fvar_head.countSizePairs != 2 || fvar_head.axisSize != 20 ||
                fvar_head.axisCount > 0x3FFE || fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || fvar_head.instanceCount > 0x7EFF ||
            fvar_head.offsetToData + fvar_head.axisCount * 20 + fvar_head.instanceCount * fvar_head.instanceSize > table_len)
        {
            FT_Error = 8;
            return null;
        }

        face.blend = new GX_BlendRec();
        face.blend.mmvar_len = 0;
        mmvar = new FT_MM_Var();

        face.blend.mmvar = mmvar;

        mmvar.num_axis = fvar_head.axisCount;
        mmvar.num_designs = 0xFFFFFFFF;
        mmvar.num_namedstyles = fvar_head.instanceCount;
        mmvar.axis = new Array(mmvar.num_axis);
        mmvar.namedstyle = new Array(mmvar.num_namedstyles);

        FT_Error = stream.EnterFrame(fvar_start + fvar_head.offsetToData);
        if (FT_Error != 0)
            return null;

        var mass_a = mmvar.axis;
        var count_a = mmvar.num_axis;
        for (i = 0; i < count_a; i++)
        {
            mass_a[i] = new FT_Var_Axis();
            var a = mass_a[i];

            FT_Error = stream.EnterFrame(20);
            if (FT_Error != 0)
                return null;

            a.tag = stream.GetULong();
            a.minimum = stream.GetULong();
            a.def = stream.GetULong();
            a.maximum = stream.GetULong();
            stream.Skip(2);
            a.strid = stream.GetUShort();

            var tag = a.tag;
            a.name = "";
            a.name += String.fromCharCode((tag >>> 24) & 0xFF);
            a.name += String.fromCharCode((tag >>> 16) & 0xFF);
            a.name += String.fromCharCode((tag >>> 8) & 0xFF);
            a.name += String.fromCharCode(tag & 0xFF);

            stream.ExitFrame();
        }

        if (0 == bIsRet)
            return null;

        var mass_s = mmvar.namedstyle;
        var count_s = mmvar.num_namedstyles;
        for (i = 0;i < count_s;i++)
        {
            FT_Error = stream.EnterFrame(4 + 4 * count_a);
            if (FT_Error != 0)
                return null;

            mass_s[i] = new FT_Var_Named_Style();
            var s = mass_s[i];

            s.strid = stream.GetUShort();
            stream.Skip(2);

            s.coords = new Array(count_a);
            for (j = 0; j < count_a; j++)
                s.coords[j] = stream.GetULong();

            stream.ExitFrame();
        }
    }

    mmvar = null;
    mmvar = face.blend.mmvar.dublicate();

    mass_a = mmvar.axis;
    count_a = mmvar.num_axis;
    for (i = 0; i < count_a; i++)
    {
        var a = mass_a[i];

        if (a.tag == 2003265652)
            a.name = "Weight";
        else if (a.tag == 2003072104)
            a.name = "Width";
        else if (a.tag == 1869640570)
            a.name = "OpticalSize";
        else if (a.tag == 1936486004)
            a.name = "Slant";
    }

    return mmvar;
}
function TT_Set_MM_Blend(face, num_coords, coords)
{
    var error = 0;
    face.doblend = 0;

    if (face.blend == null)
    {
        TT_Get_MM_Var(face, 0);
        if (FT_Error != 0)
            return FT_Error;
    }
    var manageCvt = 0;

    var blend = face.blend;
    var mmvar = blend.mmvar;

    if (num_coords != mmvar.num_axis)
        return 6;

    for (var i = 0; i < num_coords; i++)
    {
        if (coords[i] < -0x00010000 || coords[i] > 0x00010000)
            return 6;
    }

    if (blend.glyphoffsets == null)
    {
        error = ft_var_load_gvar(face);
        if (error != 0)
            return error;
    }

    if (blend.normalizedcoords == null)
    {
        blend.normalizedcoords = new Array(num_coords);
        manageCvt = 1;
    }
    else
    {
        manageCvt = 0;
        for (var i = 0; i < num_coords; ++i)
        {
            if (blend.normalizedcoords[i] != coords[i])
            {
                manageCvt = 2;
                break;
            }
        }
    }

    blend.num_axis = num_coords;
    for (var i = 0;i < num_coords; i++)
        blend.normalizedcoords[i] = coords[i];
    face.doblend = 1;
    if (face.cvt != null)
    {
        switch (manageCvt)
        {
            case 2:
                tt_face_load_cvt(face, face.stream);
                break;

            case 1:
                tt_face_vary_cvt(face, face.stream);
                break;

            case 0:
                break;
        }
    }
    return error;
}
function TT_Set_Var_Design(face, num_coords, coords)
{
    var error = 0;
    var i, j;
    var a = null;
    var av = null;
    if (face.blend == null)
    {
        TT_Get_MM_Var(face, 0);
        if (FT_Error != 0)
            return FT_Error;
    }

    var blend = face.blend;
    var mmvar = blend.mmvar;

    if (num_coords != mmvar.num_axis)
        return 6;

    var _c = mmvar.num_axis;
    var normalized = new Array(_c);

    for (i = 0; i < _c; i++)
    {
        a = mmvar.axis[i];
        if (coords[i] > a.maximum || coords[i] < a.minimum)
            return 6;
        
        if (coords[i] < a.def)
        {
            normalized[i] = -FT_MulDiv(coords[i] - a.def, 0x10000, a.minimum - a.def);
        }
        else if (a.maximum == a.def)
            normalized[i] = 0;
        else
        {
            normalized[i] = FT_MulDiv(coords[i] - a.def, 0x10000, a.maximum - a.def);
        }
    }

    if (0 == blend.avar_checked)
        ft_var_load_avar(face);

    if (blend.avar_segment != null)
    {
        for (i = 0; i < _c; i++)
        {
            av = blend.avar_segment[i];
            var _coords = av.correspondence;
            for (j = 1; j < av.pairCount; j++)
            if (normalized[i] < _coords[j].fromCoord)
            {
                var mem = FT_MulDiv(normalized[i] - _coords[j - 1].fromCoord, 0x10000, _coords[j].fromCoord - _coords[j - 1].fromCoord);
                normalized[i] = FT_MulDiv(mem, _coords[j].toCoord - _coords[j - 1].toCoord, 0x10000) + _coords[j - 1].toCoord;
                break;
            }
        }
    }

    error = TT_Set_MM_Blend(face, num_coords, normalized);
    normalized = null;
    return error;
}
function tt_face_vary_cvt(face, stream)
{
    var error = 0;
    var here;
    var i, j;
    var blend = face.blend;
    var point_count;
    var localpoints;
    var deltas;

    if (blend == null)
        return 0;

    if (face.cvt == null)
        return 0;

    var table_len = face.goto_table(face, 1668702578, stream);
    error = FT_Error;
    if (error != 0)
        return 0;

    error = stream.EnterFrame(table_len);
    if (error != 0)
        return 0;

    var table_start = stream.cur;
    if (0x00010000 != stream.GetLong())
    {
        stream.ExitFrame();
        return 0;
    }

    var num_axis = blend.num_axis;
    var tuple_coords = new Array(num_axis);
    var im_start_coords = new Array(num_axis);
    var im_end_coords = new Array(num_axis);

    var tupleCount   = stream.GetUShort();
    var offsetToData = table_start + stream.GetUShort();

    for (i = 0; i < (tupleCount & 0xFFF); ++i)
    {
        var tupleDataSize = stream.GetUShort();
        var tupleIndex    = stream.GetUShort();

        if ((tupleIndex & 32768) != 0)
        {
            for (j = 0; j < num_axis; j++)
                tuple_coords[j] = stream.GetShort() << 2;
        }
        else
        {
            if ((tupleIndex & 16384) != 0)
            {
                stream.Skip(4 * num_axis);
            }
            offsetToData += tupleDataSize;
            continue;
        }
        if ((tupleIndex & 16384) != 0)
        {
            for (j = 0; j < num_axis; j++)
                im_start_coords[j] = stream.GetShort() << 2;
            for (j = 0; j < num_axis; j++)
                im_end_coords[j] = stream.GetShort() << 2;
        }

        var apply = ft_var_apply_tuple(blend, tupleIndex, tuple_coords, im_start_coords, im_end_coords);
        if (apply == 0 || 0 == (tupleIndex & 8192))
        {
            offsetToData += tupleDataSize;
            continue;
        }

        here = stream.cur;
        stream.cur = offsetToData;

        var __mem1 = ft_var_readpackedpoints(stream);
        localpoints = __mem1.points;
        point_count = __mem1.point_cnt;
        var cvt_size = face.cvt_size;
        deltas = ft_var_readpackeddeltas(stream, point_count == 0 ? cvt_size : point_count);
        if (deltas == null)
        {
        }
        else if (localpoints == null)
        {
            for (j = 0; j < cvt_size; j++)
                face.cvt[j] = (face.cvt[j] + FT_MulFix(deltas[j], apply)) & 0xFFFF;
        }
        else
        {
            for (j = 0; j < point_count; ++j)
            {
                var pindex = localpoints[j];
                face.cvt[pindex] = (face.cvt[pindex] + FT_MulFix(deltas[j], apply)) & 0xFFFF;
            }
        }

        localpoints = null;
        deltas = null;
        offsetToData += tupleDataSize;
        stream.cur = here;
    }

    stream.ExitFrame();

    tuple_coords = null;
    im_start_coords = null;
    im_end_coords = null;

    return error;
}
function TT_Vary_Get_Glyph_Deltas(face, glyph_index, n_points)
{
    var stream = face.stream;
    var blend  = face.blend;
    var error;
    var here;
    var i, j;
    var point_count, spoint_count = 0;
    var sharedpoints = null;
    var localpoints  = null;
    var points = null;
    var deltas_x = null, deltas_y = null;


    if (face.doblend == 0 || blend == null)
    {
        FT_Error = 6;
        return null;
    }

    var delta_xy = new Array(n_points);
    for (i=0;i<n_points;i++)
        delta_xy[i] = new FT_Vector();
    
    if (glyph_index >= blend.gv_glyphcnt || blend.glyphoffsets[glyph_index] == blend.glyphoffsets[glyph_index + 1])
        return 0;

    error = stream.Seek(blend.glyphoffsets[glyph_index]);
    if (error == 0)
        error = stream.EnterFrame(blend.glyphoffsets[glyph_index + 1] - blend.glyphoffsets[glyph_index]);

    if (error != 0)
    {
        delta_xy = null;
        FT_Error = error;
        return null;
    }

    var glyph_start = stream.cur;

    var num_axis = blend.num_axis;
    var tuple_coords = new Array(num_axis);
    var im_start_coords = new Array(num_axis);
    var im_end_coords = new Array(num_axis);

    var tupleCount   = stream.GetUShort();
    var offsetToData = glyph_start + stream.GetUShort();

    if ((tupleCount & 32768) != 0)
    {
        here = stream.cur;
        stream.cur = offsetToData;

        var __mem = ft_var_readpackedpoints(stream);
        sharedpoints = __mem.points;
        spoint_count = __mem.point_cnt;
        offsetToData = stream.cur;
        stream.cur = here;
    }

    for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
    {
        var tupleDataSize = stream.GetUShort();
        var tupleIndex    = stream.GetUShort();

        if (0 != (tupleIndex & 32768))
        {
            for (j = 0; j < num_axis; j++)
                tuple_coords[j] = stream.GetShort() << 2;
        }
        else if (((tupleIndex & 4095) != 0) >= blend.tuplecount)
        {
            FT_Error = 8;
            tuple_coords = null;
            im_start_coords = null;
            im_end_coords = null;
            delta_xy = null;
            stream.ExitFrame();
            return null;
        }
        else
        {
            var _src = blend.tuplecoords;
            var _s_start = (tupleIndex & 0xFFF) * num_axis;
            for (j=0;j<num_axis;j++)
                tuple_coords[j] = _src[_s_start+j];
        }

        if (0 != (tupleIndex & 16384))
        {
            for (j = 0; j < num_axis; j++)
                im_start_coords[j] = stream.GetShort() << 2;
            for (j = 0; j < num_axis; j++)
                im_end_coords[j] = stream.GetShort() << 2;
        }

        var apply = ft_var_apply_tuple(blend, tupleIndex, tuple_coords, im_start_coords, im_end_coords);
        if (apply == 0)
        {
            offsetToData += tupleDataSize;
            continue;
        }

        here = stream.cur;
        if (0 != (tupleIndex & 8192))
        {
            stream.cur = offsetToData;
            var __mem = ft_var_readpackedpoints(stream);
            localpoints = __mem.points;
            point_count = __mem.point_cnt;
            points = localpoints;
        }
        else
        {
            points      = sharedpoints;
            point_count = spoint_count;
        }

        deltas_x = ft_var_readpackeddeltas(stream, point_count == 0 ? n_points : point_count);
        deltas_y = ft_var_readpackeddeltas(stream, point_count == 0 ? n_points : point_count);

        if (points == null || deltas_y == null || deltas_x == null)
        {
        }
        else if (points == null)
        {
            for (j = 0; j < n_points; j++)
            {
                delta_xy[j].x += FT_MulFix(deltas_x[j], apply);
                delta_xy[j].y += FT_MulFix(deltas_y[j], apply);
            }
        }
        else
        {
            for (j = 0; j < point_count; j++)
            {
                if (localpoints[j] >= n_points)
                    continue;

                delta_xy[localpoints[j]].x += FT_MulFix(deltas_x[j], apply);
                delta_xy[localpoints[j]].y += FT_MulFix(deltas_y[j], apply);
            }
        }

        localpoints = null;
        deltas_x = null;
        deltas_y = null;

        offsetToData += tupleDataSize;
        stream.cur = here;
    }

    tuple_coords = null;
    im_start_coords = null;
    im_end_coords = null;
    delta_xy = null;
    stream.ExitFrame();
    if (error != 0)
        delta_xy = null;

    FT_Error = error;
    return delta_xy;
}
function tt_done_blend(memory, blend)
{
}
/******************************************************************************/
// glyphloader
/******************************************************************************/
function load_sbit_image(size, glyph, glyph_index, load_flags)
{
    var metrics = new TT_SBit_MetricsRec();

    var face   = glyph.face;
    var stream = face.stream;
    var error = face.sfnt.load_sbit_image(face, size.strike_index, glyph_index, load_flags, stream, glyph.bitmap, metrics);
    if (error == 0)
    {
        glyph.outline.n_points   = 0;
        glyph.outline.n_contours = 0;

        glyph.metrics.width  = metrics.width  << 6;
        glyph.metrics.height = metrics.height << 6;

        glyph.metrics.horiBearingX = metrics.horiBearingX << 6;
        glyph.metrics.horiBearingY = metrics.horiBearingY << 6;
        glyph.metrics.horiAdvance  = metrics.horiAdvance  << 6;

        glyph.metrics.vertBearingX = metrics.vertBearingX << 6;
        glyph.metrics.vertBearingY = metrics.vertBearingY << 6;
        glyph.metrics.vertAdvance  = metrics.vertAdvance  << 6;

        glyph.format = 1651078259;

        if ((load_flags & 16) != 0)
        {
            glyph.bitmap_left = metrics.vertBearingX;
            glyph.bitmap_top  = metrics.vertBearingY;
        }
        else
        {
            glyph.bitmap_left = metrics.horiBearingX;
            glyph.bitmap_top  = metrics.horiBearingY;
        }
    }

    return error;
}
function TT_Get_HMetrics(face, idx)
{
    return face.sfnt.get_metrics(face, 0, idx);
}
function TT_Get_VMetrics(face, idx, yMax)
{
    if (face.vertical_info === true)
        return face.sfnt.get_metrics(face, 1, idx);
    else if (face.os2.version != 0xFFFF)
        return {bearing : (face.os2.sTypoAscender - yMax), advance : (face.os2.sTypoAscender - face.os2.sTypoDescender)};
    else
        return {bearing : (face.horizontal.Ascender - yMax), advance : (face.horizontal.Ascender - face.horizontal.Descender)};

    return {bearing : 0, advance : face.units_per_EM};
}

function tt_get_metrics(loader, glyph_index)
{
    var face = loader.face;

    var h = TT_Get_HMetrics(face, glyph_index);
    var v = TT_Get_VMetrics(face, glyph_index, loader.bbox.yMax);

    loader.left_bearing = h.bearing;
    loader.advance      = h.advance;
    loader.top_bearing  = v.bearing;
    loader.vadvance     = v.advance;

    if (face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (loader.exec != null)
            loader.exec.sph_tweak_flags = 0;

        /* this may not be the right place for this, but it works */
        if (loader.exec != null && loader.exec.ignore_x_mode)
            global_SubpixHintingHacks.sph_set_tweaks(loader, glyph_index);
    }//#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

    if (loader.linear_def == 0)
    {
        loader.linear_def = 1;
        loader.linear = h.advance;
    }
}
function tt_get_metrics_incr_overrides(loader, glyph_index)
{
    var face = loader.face;

    var h = TT_Get_HMetrics(face, glyph_index);
    var v = TT_Get_VMetrics(face, glyph_index, 0);

    if (face.internal.incremental_interface && face.internal.incremental_interface.funcs.get_glyph_metrics)
    {
        var metrics = new FT_Incremental_MetricsRec();

        metrics.bearing_x = loader.left_bearing;
        metrics.bearing_y = 0;
        metrics.advance   = loader.advance;
        metrics.advance_v = 0;

        var error = face.internal.incremental_interface.funcs.get_glyph_metrics(face.internal.incremental_interface.object,
            glyph_index, false, metrics);
        if (error != 0)
            return 0;

        loader.left_bearing = metrics.bearing_x;
        loader.advance      = metrics.advance;
        loader.top_bearing  = v.tsb;
        loader.vadvance     = v.ah;

        if (0 == loader.linear_def)
        {
            loader.linear_def = 1;
            loader.linear = h.aw;
        }
    }
    return 0;
}
function translate_array(n, coords, delta_x, delta_y)
{
    if (delta_x != 0)
        for (var k = 0; k < n; k++)
            coords[k].x += delta_x;

    if (delta_y != 0)
        for (var k = 0; k < n; k++)
            coords[k].y += delta_y;
}
function translate_array_ex(n, coords, start_p, delta_x, delta_y)
{
    if (delta_x != 0)
        for (var k = 0; k < n; k++)
            coords[start_p + k].x += delta_x;

    if (delta_y != 0)
        for (var k = 0; k < n; k++)
            coords[start_p + k].y += delta_y;
}

function TT_Access_Glyph_Frame(loader, glyph_index, offset, byte_count)
{
    var error = 0;
    var stream = loader.stream;

    error = stream.Seek(offset);
    if (error == 0)
        error = stream.EnterFrame(byte_count);

    if (error != 0)
        return error;

    loader.cursor = stream.cur;
    loader.limit  = stream.size;

    return 0;
}

function TT_Forget_Glyph_Frame(loader)
{
    loader.stream.ExitFrame();
}

function TT_Load_Glyph_Header(loader)
{
    var stream = loader.stream;

    if (stream.cur + 10 > stream.size)
        return 20;

    loader.n_contours = stream.GetShort();
    loader.bbox.xMin = stream.GetShort();
    loader.bbox.yMin = stream.GetShort();
    loader.bbox.xMax = stream.GetShort();
    loader.bbox.yMax = stream.GetShort();

    loader.cursor += 10;

    return 0;
}

function TT_Load_Simple_Glyph(load)
{
    var error = 0;
    var p = new FT_Stream(load.stream.data, load.stream.size);
    p.cur = load.cursor;

    var gloader = load.gloader;
    var n_contours = load.n_contours;
    var face = load.face;
    var n_ins;
    var n_points;

    var c, count;
    var x;
    var prev_cont;
    var xy_size = 0;

    error = FT_GLYPHLOADER_CHECK_POINTS(gloader, 0, n_contours);
    if (error != 0)
        return error;

    if (null == gloader.base.outline.contours)
    {
        error = FT_GLYPHLOADER_CHECK_POINTS(gloader, 0, n_contours);
    }

    var conts = gloader.base.outline.contours;
    var cont = gloader.current.outline.contours;
    var cont_limit = cont + n_contours;

    if (n_contours >= 0xFFF || p.cur + (n_contours + 1) * 2 > load.limit)
        return 20;

    prev_cont = p.GetUShort();

    if (n_contours > 0)
        conts[cont] = prev_cont;

    for (cont++; cont < cont_limit; cont++)
    {
        conts[cont] = p.GetUShort();
        if (conts[cont] <= prev_cont)
            return 8;
        prev_cont = conts[cont];
    }

    n_points = 0;
    if (n_contours > 0)
    {
        n_points = conts[cont-1] + 1;
        if (n_points < 0)
            return 20;
    }

    /* note that we will add four phantom points later */
    error = FT_GLYPHLOADER_CHECK_POINTS(gloader, n_points + 4, 0);
    if (error != 0)
        return error;

    var outline = gloader.current.outline;
    for (cont = outline.contours + 1; cont < cont_limit; cont++)
        if (conts[cont-1] >= conts[cont])
            return 20;

    load.glyph.control_len  = 0;
    load.glyph.control_data = 0;

    if (p.cur + 2 > load.limit)
        return 20;

    n_ins = p.GetUShort();
    if (n_ins > face.max_profile.maxSizeOfInstructions)
        return 22;

    if ((load.limit - p.cur) < n_ins)
        return 22;

    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if (face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER)
    {
        if ((load.load_flags & 2) == 0)
        {
            load.glyph.control_len  = n_ins;
            load.glyph.control_data = dublicate_pointer(load.exec.glyphIns);

			if (load.glyph.control_data != null)
			{
				var _dd = load.glyph.control_data.data;
				var _dc = load.glyph.control_data.pos;

				for (var j = 0; j < n_ins; j++)
					_dd[_dc + j] = p.data[p.cur + j];
			}
        }
    }

    p.cur += n_ins;

    var flags = gloader.base.outline.tags;
    var flag = outline.tags;
    var flag_limit = flag + n_points;

    var limit = load.limit;
    while (flag < flag_limit)
    {
        if (p.cur + 1 > limit)
            return 20;

        flags[flag++] = c = p.GetUChar();
        if ((c & 8)!=0)
        {
            if (p.cur + 1 > limit)
                return 20;

            count = p.GetUChar();
            if (flag + count > flag_limit)
                return 20;

            for (; count > 0; count--)
                flags[flag++] = c;
        }
    }

    var vecs = gloader.base.outline.points;
    var vec = outline.points;
    var vec_limit = vec + n_points;
    flag = outline.tags;
    x = 0;

    if (p.cur + xy_size > limit)
        return 20;

    for ( ; vec < vec_limit; vec++, flag++ )
    {
        var y = 0;
        var f = flags[flag];

        if ((f & 2)!=0)
        {
            if (p.cur + 1 > limit)
                return 20;

            y = p.GetUChar();
            if (( f & 16 ) == 0)
                y = -y;
        }
        else if (( f & 16 ) == 0)
        {
            if (p.cur + 2 > limit)
                return 20;

            y = p.GetShort();
        }

        x += y;
        vecs[vec].x = x;
        flags[flag]  = (f & ~( 2 | 16 )) & 0xFF;
    }

    vec = outline.points;
    flag = outline.tags;
    x = 0;

    for ( ; vec < vec_limit; vec++, flag++)
    {
        var y = 0;
        var f = flags[flag];

        if ((f & 4)!=0)
        {
            if (p.cur + 1 > limit)
                return 20;

            y = p.GetUChar();
            if ((f & 32) == 0)
                y = -y;
        }
        else if ((f & 32) == 0)
        {
            if (p.cur + 2 > limit)
                return 20;

            y = p.GetShort();
        }

        x += y;
        vecs[vec].y = x;
        flags[flag]  = (f & 1) & 0xFF;
    }

    outline.n_points   = n_points & 0xFFFF;
    outline.n_contours = n_contours & 0xFFFF;

    load.cursor = p.cur;
    return error;
}

function TT_Load_Composite_Glyph(loader)
{
    var error = 0;
    var s = new FT_Stream(loader.stream.data, loader.stream.size);
    s.cur = loader.cursor;
    var size_read = loader.limit - loader.cursor;
    var count_read = 0;

    var gloader = loader.gloader;
    var subglyph = null;
    var num_subglyphs = 0;

    do
    {
        var xx, xy, yy, yx;
        var count;

        error = FT_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs + 1);
        if (error != 0)
            return error;

        if (count_read + 4 > size_read)
            return 21;

        subglyph = gloader.base.subglyphs[gloader.current.subglyphs + num_subglyphs];

        subglyph.arg1 = subglyph.arg2 = 0;

        subglyph.flags = s.GetUShort();
        subglyph.index = s.GetUShort();

        count_read += 4;

        count = 2;
        if (subglyph.flags & 1)
            count += 2;
        if (subglyph.flags & 8)
            count += 2;
        else if (subglyph.flags & 64)
            count += 4;
        else if (subglyph.flags & 128)
            count += 8;

        if (count_read + count > size_read)
            return 21;

        if (subglyph.flags & 1)
        {
            subglyph.arg1 = s.GetShort();
            subglyph.arg2 = s.GetShort();
        }
        else
        {
            subglyph.arg1 = s.GetChar();
            subglyph.arg2 = s.GetChar();
        }

        xx = yy = 0x10000;
        xy = yx = 0;

        if (subglyph.flags & 8)
        {
            xx = s.GetShort() << 2;
            yy = xx;
        }
        else if (subglyph.flags & 64)
        {
            xx = s.GetShort() << 2;
            yy = s.GetShort() << 2;
        }
        else if (subglyph.flags & 128)
        {
            xx = s.GetShort() << 2;
            yx = s.GetShort() << 2;
            xy = s.GetShort() << 2;
            yy = s.GetShort() << 2;
        }

        subglyph.transform.xx = xx;
        subglyph.transform.xy = xy;
        subglyph.transform.yx = yx;
        subglyph.transform.yy = yy;

        num_subglyphs++;

        count_read += count;

    } while (subglyph.flags & 32);

    gloader.current.num_subglyphs = num_subglyphs;

    if (loader.face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER)
    {
        loader.ins_pos = loader.stream.pos + s.cur - loader.limit;
    }
    
    loader.cursor = s.cur;
    return error
}

function TT_Process_Simple_Glyph(loader)
{
    var gloader = loader.gloader;
    var error = 0;

    var outline  = gloader.current.outline;
    var base  = gloader.base.outline;
    var n_points = outline.n_points;

    var points = base.points;
    var p_s = outline.points;
    var s = p_s + n_points;
    var tags = base.tags;
    var t_s = outline.tags;
    var t = t_s + n_points;

    points[s    ].x = loader.pp1.x;
    points[s + 1].x = loader.pp2.x;
    points[s + 2].x = loader.pp3.x;
    points[s + 3].x = loader.pp4.x;
    points[s    ].y = loader.pp1.y;
    points[s + 1].y = loader.pp2.y;
    points[s + 2].y = loader.pp3.y;
    points[s + 3].y = loader.pp4.y;

    tags[t    ] = 0;
    tags[t + 1] = 0;
    tags[t + 2] = 0;
    tags[t + 3] = 0;

    n_points += 4;
    s += 4;

    //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    if (loader.face.doblend != 0)
    {
        var deltas = TT_Vary_Get_Glyph_Deltas(loader.face, loader.glyph_index, n_points);
        error = FT_Error;
        if (error != 0)
            return error;

        for (var i = 0; i < n_points; i++)
        {
            points[i + p_s].x += deltas[i].x;
            points[i + p_s].y += deltas[i].y;
        }

        deltas = null;
    }
    //#endif

    if ((loader.load_flags & 2) == 0)
    {
        tt_prepare_zone_cur(loader.zone, gloader, 0, 0);

        var _p_len = loader.zone.n_points + 4;

        var _orus = loader.zone.orus;
        var _cur = loader.zone.cur;
        for (var i = 0; i < _p_len; i++)
        {
            _orus[loader.zone._offset_orus + i].x = _cur[loader.zone._offset_cur + i].x;
            _orus[loader.zone._offset_orus + i].y = _cur[loader.zone._offset_cur + i].y;
        }
    }

    var _face = loader.face;
    if (_face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
    {
        var x_scale_factor = 1000;
        var ppem = loader.size.metrics.x_ppem;
        if ((loader.load_flags & 2) == 0)
        {
            x_scale_factor = global_SubpixHintingHacks.sph_test_tweak_x_scaling(_face, _face.family_name, ppem, _face.style_name, loader.glyph_index);
        }

        if ((loader.load_flags & 1) == 0 || x_scale_factor != 1000)
        {
            var x_scale = FT_MulDiv(loader.size.metrics.x_scale, x_scale_factor, 1000);
            var y_scale = loader.size.metrics.y_scale;

            /* compensate for any scaling by de/emboldening; */
            /* the amount was determined via experimentation */
            if (x_scale_factor != 1000 && ppem > 11)
                FT_Outline_EmboldenXY(outline, FT_MulFix(1280 * ppem, 1000 - x_scale_factor), 0);

            for (var vec = 0 ; vec < n_points; vec++)
            {
                points[vec + p_s].x = FT_MulFix(points[vec + p_s].x, x_scale);
                points[vec + p_s].y = FT_MulFix(points[vec + p_s].y, y_scale);
            }

            loader.pp1.x = points[s - 4].x;
            loader.pp2.x = points[s - 3].x;
            loader.pp3.x = points[s - 2].x;
            loader.pp4.x = points[s - 1].x;
            loader.pp1.y = points[s - 4].y;
            loader.pp2.y = points[s - 3].y;
            loader.pp3.y = points[s - 2].y;
            loader.pp4.y = points[s - 1].y;
        }
    }
    else
    {
        if ((loader.load_flags & 1) == 0)
        {
            var x_scale = loader.size.metrics.x_scale;
            var y_scale = loader.size.metrics.y_scale;

            for (var vec = 0 ; vec < n_points; vec++)
            {
                points[vec + p_s].x = FT_MulFix(points[vec + p_s].x, x_scale);
                points[vec + p_s].y = FT_MulFix(points[vec + p_s].y, y_scale);
            }

            loader.pp1.x = points[s - 4].x;
            loader.pp2.x = points[s - 3].x;
            loader.pp3.x = points[s - 2].x;
            loader.pp4.x = points[s - 1].x;
            loader.pp1.y = points[s - 4].y;
            loader.pp2.y = points[s - 3].y;
            loader.pp3.y = points[s - 2].y;
            loader.pp4.y = points[s - 1].y;
        }
    }

    if ((loader.load_flags & 2) == 0)
    {
        loader.zone.n_points += 4;
        error = TT_Hint_Glyph(loader, false);
    }

    return error;
}

function TT_Process_Composite_Component(loader, subglyph, start_point, num_base_points)
{
    var gloader = loader.gloader;
    var base_vecs = gloader.base.outline.points;
    var base_vec = 0;
    var num_points = gloader.base.outline.n_points;
    var x, y;

    var have_scale = (0 == (subglyph.flags & (8 | 64 | 128))) ? 0 : 1;

    if (have_scale != 0)
    {
        for (var i = num_base_points; i < num_points; i++)
            FT_Vector_Transform(base_vecs[i], subglyph.transform);
    }

    if (0 == (subglyph.flags & 2))
    {
        var k = subglyph.arg1;
        var l = subglyph.arg2;
        k += start_point;
        l += num_base_points;
        if (k >= num_base_points || l >= num_points)
            return 21;

        var p1 = gloader.base.outline.points[k];
        var p2 = gloader.base.outline.points[l];

        x = p1.x - p2.x;
        y = p1.y - p2.y;
    }
    else
    {
        x = subglyph.arg1;
        y = subglyph.arg2;

        if (x == 0 && y == 0)
            return 0;

        if (have_scale != 0 && 0 != (subglyph.flags & 2048))
        {
            var mac_xscale = FT_SqrtFixed(FT_MulFix(subglyph.transform.xx, subglyph.transform.xx) +
                                            FT_MulFix(subglyph.transform.xy, subglyph.transform.xy));
            var mac_yscale = FT_SqrtFixed(FT_MulFix(subglyph.transform.yy, subglyph.transform.yy) +
                                            FT_MulFix(subglyph.transform.yx, subglyph.transform.yx));


            x = FT_MulFix(x, mac_xscale);
            y = FT_MulFix(y, mac_yscale);
        }

        if (0 == (loader.load_flags & 1))
        {
            var x_scale = loader.size.metrics.x_scale;
            var y_scale = loader.size.metrics.y_scale;

            x = FT_MulFix(x, x_scale);
            y = FT_MulFix(y, y_scale);

            if (subglyph.flags & 4)
            {
                x = FT_PIX_ROUND(x);
                y = FT_PIX_ROUND(y);
            }
        }
    }

    if (x != 0)
    {
        for (var j=num_base_points;j<num_points;j++)
            base_vecs[j].x += x;
    }
    if (y != 0)
    {
        for (var j=num_base_points;j<num_points;j++)
            base_vecs[j].y += y;
    }
    
    return 0;
}

function TT_Process_Composite_Glyph(loader, start_point, start_contour)
{
    var outline = loader.gloader.base.outline;

    /* make room for phantom points */
    var error = FT_GLYPHLOADER_CHECK_POINTS(loader.gloader, outline.n_points + 4, 0);
    if (error != 0)
        return error;

    var _points = outline.points;
    var _n_points = outline.n_points;
    var _tags = outline.tags;

    _points[_n_points].x = loader.pp1.x;
    _points[_n_points].y = loader.pp1.y;

    _points[_n_points + 1].x = loader.pp2.x;
    _points[_n_points + 1].y = loader.pp2.y;

    _points[_n_points + 2].x = loader.pp3.x;
    _points[_n_points + 2].y = loader.pp3.y;

    _points[_n_points + 3].x = loader.pp4.x;
    _points[_n_points + 3].y = loader.pp4.y;

    _tags[_n_points] = 0;
    _tags[_n_points + 1] = 0;
    _tags[_n_points + 2] = 0;
    _tags[_n_points + 3] = 0;

    if (loader.face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER) //#ifdef TT_USE_BYTECODE_INTERPRETER
    {
        /* TT_Load_Composite_Glyph only gives us the offset of instructions */
        /* so we read them here                                             */
        var stream = loader.stream;
        error = stream.Seek(loader.ins_pos);
        if (error != 0)
            return error;

        var n_ins = stream.ReadUShort();
        error = FT_Error;
        FT_Error = 0;
        if (error != 0)
            return error;

        /* check it */
        var max_ins = loader.face.max_profile.maxSizeOfInstructions;
        if (n_ins > max_ins)
        {
            /* acroread ignores this field, so we only do a rough safety check */
            if (n_ins > loader.byte_len)
                return 22;
        
            var ret = Update_MaxBYTE(loader.exec.memory, loader.exec.glyphSize, loader.exec.glyphIns, n_ins);
            if (ret != null)
            {
                loader.exec.glyphSize = ret.size;
                loader.exec.glyphIns = ret.block;
            }

            if ( error )
                return error;
        }
        else if (n_ins == 0)
            return 0;

        error = stream.Read(loader.exec.glyphIns, n_ins);
        if (error != 0)
            return error;

        loader.glyph.control_data = dublicate_pointer(loader.exec.glyphIns);
        loader.glyph.control_len = n_ins;
    }
    //#endif

    tt_prepare_zone(loader.zone, loader.gloader.base, start_point, start_contour);

    /* Some points are likely touched during execution of  */
    /* instructions on components.  So let's untouch them. */
    for (var i = start_point; i < loader.zone.n_points; i++)
        loader.zone.tags[i] &= ~24;

    loader.zone.n_points += 4;

    return TT_Hint_Glyph(loader, true);
}

function TT_Hint_Glyph(loader, is_composite)
{
    var zone = loader.zone;
    var n_ins = 0;

    var _tt_hints = loader.face.driver.library.tt_hint_props;
    if (_tt_hints.TT_USE_BYTECODE_INTERPRETER) //#ifdef TT_USE_BYTECODE_INTERPRETER
    {
        n_ins = loader.glyph.control_len;
    }
    //#endif

    var origin = zone.cur[zone._offset_cur + zone.n_points - 4].x;
    origin = FT_PIX_ROUND(origin) - origin;
    if (origin != 0)
        translate_array_ex(zone.n_points, zone.cur, zone._offset_cur, origin, 0);

    if (_tt_hints.TT_USE_BYTECODE_INTERPRETER) //#ifdef TT_USE_BYTECODE_INTERPRETER
    {
        /* save original point position in org */
        if (n_ins > 0)
        {
            var _arr_d = zone.org;
            var _arr_s = zone.cur;
            var _count = zone.n_points;
            var _d_s = zone._offset_org;
            var _s_s = zone._offset_cur;

            for (var i = 0; i < _count; i++)
            {
                _arr_d[i + _d_s].x = _arr_s[i + _s_s].x;
                _arr_d[i + _d_s].y = _arr_s[i + _s_s].y;
            }
        }

        /* Reset graphics state. */
        loader.size.GS.Copy(loader.exec.GS);

        /* XXX: UNDOCUMENTED! Hinting instructions of a composite glyph */
        /*      completely refer to the (already) hinted subglyphs.     */
        if (is_composite)
        {
            loader.exec.metrics.x_scale = 1 << 16;
            loader.exec.metrics.y_scale = 1 << 16;

            var _arr_d = zone.orus;
            var _arr_s = zone.cur;
            var _count = zone.n_points;
            var _d_s = zone._offset_orus;
            var _s_s = zone._offset_cur;

            for (var i = 0; i < _count; i++)
            {
                _arr_d[i + _d_s].x = _arr_s[i + _s_s].x;
                _arr_d[i + _d_s].y = _arr_s[i + _s_s].y;
            }
        }
        else
        {
            loader.exec.metrics.x_scale = loader.size.metrics.x_scale;
            loader.exec.metrics.y_scale = loader.size.metrics.y_scale;
        }
    }//#endif

    /* round pp2 and pp4 */
    zone.cur[zone._offset_cur + zone.n_points - 3].x = FT_PIX_ROUND(zone.cur[zone._offset_cur + zone.n_points - 3].x);
    zone.cur[zone._offset_cur + zone.n_points - 1].y = FT_PIX_ROUND(zone.cur[zone._offset_cur + zone.n_points - 1].y);

    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if ( n_ins > 0 )
    {
        var base_outline = loader.gloader.base.outline;
        var current_outline = loader.gloader.current.outline;

        var error = TT_Set_CodeRange(loader.exec, 3, loader.exec.glyphIns, n_ins);
        if (error)
            return error;

        loader.exec.is_composite = is_composite;
        loader.exec.pts.Copy(zone);

        var debug = (!(loader.load_flags & 1) && loader.size.debug) ? true : false;

        error = TT_Run_Context(loader.exec, debug);
        if (error && loader.exec.pedantic_hinting)
            return error;

        /* store drop-out mode in bits 5-7; set bit 2 also as a marker */
        base_outline.tags[current_outline.tags] |= (loader.exec.GS.scan_type << 5) | 4;
    }
    //#endif

    /* save glyph phantom points */
    if (!loader.preserve_pps)
    {
        var _off = zone.n_points + zone._offset_cur;

        loader.pp1.x = zone.cur[_off - 4].x;
        loader.pp1.y = zone.cur[_off - 4].y;

        loader.pp2.x = zone.cur[_off - 3].x;
        loader.pp2.y = zone.cur[_off - 3].y;

        loader.pp3.x = zone.cur[_off - 2].x;
        loader.pp3.y = zone.cur[_off - 2].y;

        loader.pp4.x = zone.cur[_off - 1].x;
        loader.pp4.y = zone.cur[_off - 1].y;
    }

    if (_tt_hints.TT_CONFIG_OPTION_SUBPIXEL_HINTING) //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
    {
        if (loader.exec.sph_tweak_flags & 16)
            FT_Outline_EmboldenXY(loader.gloader.current.outline, -24, 0);
        else if (loader.exec.sph_tweak_flags & 64)
            FT_Outline_EmboldenXY(loader.gloader.current.outline, 24, 0);
    }//#endif
    return 0;
}

function tt_prepare_zone(zone, load, start_point, start_contour)
{
    zone.n_points    = (load.outline.n_points - start_point) & 0xFFFF;
    zone.n_contours  = FT_Common.UShort_To_Short((load.outline.n_contours - start_contour) & 0xFFFF);

    zone.org = load.extra_points;
    zone._offset_org = start_point;

    zone.cur = load.outline.points;
    zone._offset_cur = start_point;

    zone.orus = load.extra_points;
    zone._offset_orus = load.extra_points2 + start_point;

    zone.tags = load.outline.tags;
    zone._offset_tags = start_point;

    zone.contours = load.outline.contours;
    zone._offset_contours = start_contour;

    zone.first_point = start_point;
}

function tt_prepare_zone_cur(zone, load, start_point, start_contour)
{
    var _base = load.base;
    var _cur = load.current;

    zone.n_points    = (_cur.outline.n_points - start_point) & 0xFFFF;
    zone.n_contours  = FT_Common.UShort_To_Short((_cur.outline.n_contours - start_contour) & 0xFFFF);

    zone.org = _base.extra_points;
    zone._offset_org = _cur.extra_points + start_point;

    zone.cur = _base.outline.points;
    zone._offset_cur = _cur.outline.points + start_point;

    zone.orus = _base.extra_points;
    zone._offset_orus = _cur.extra_points2 + start_point;

    zone.tags = _base.outline.tags;
    zone._offset_tags = _cur.outline.tags + start_point;

    zone.contours = _base.outline.contours;
    zone._offset_contours = _cur.outline.contours + start_contour;

    zone.first_point = start_point;
}

function tt_size_run_prep(size, pedantic)
{
    var face = size.face;
    var exec;
    var error = 0;

    /* debugging instances have their own context */
    if (size.debug)
        exec = size.context;
    else
        exec = face.driver.context;

    if (exec == null)
        return 153;

    TT_Load_Context(exec, face, size);

    exec.callTop = 0;
    exec.top     = 0;

    exec.instruction_trap = false;
    exec.pedantic_hinting = pedantic;

    TT_Set_CodeRange(exec, 2, face.cvt_program, face.cvt_program_size);
    TT_Clear_CodeRange(exec, 3);

    if (face.cvt_program_size > 0)
    {
        error = TT_Goto_CodeRange(exec, 2, 0);

        if (!error && !size.debug)
        {
            error = face.interpreter(exec);
        }
    }
    else
        error = 0;

    /* save as default graphics state */
    exec.GS.Copy(size.GS);

    // _DEBUG!
    /*
    var __arr = exec.stack;
    var __len = exec.stackSize;
    for (var __i = 0; __i < __len; ++__i)
        console.log("" + __i + ": " + __arr[__i]);
    */
    //

    TT_Save_Context(exec, size);
    return error;
}

function tt_loader_init(loader, size, glyph, load_flags, glyf_table_only)
{
    var face = glyph.face;
    var stream = face.stream;
    var pedantic = (0 == (load_flags & 128)) ? 0 : 1;

    loader.Clear();

    var bIsHint = face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER;
    var bIsSubpixHint = face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING;

    if (bIsHint)//#ifdef TT_USE_BYTECODE_INTERPRETER
    {/* load execution context */
        if ((load_flags & 2) == 0 && !glyf_table_only)
        {
            var grayscale = false;

            //#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
            var subpixel_hinting = false;
            var grayscale_hinting = false;
            //#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */

            if (!size.cvt_ready)
            {
                var error = tt_size_ready_bytecode(size, pedantic);
                if (error != 0)
                    return error;
            }

            /* query new execution context */
            var exec = size.debug ? size.context : face.driver.context;
            if (exec == null)
                return 153;

            if (bIsSubpixHint) // #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
            {
                subpixel_hinting = ((FT_LOAD_TARGET_MODE(load_flags) != 2) && true);

                if (subpixel_hinting)
                    grayscale = grayscale_hinting = false;
                else if (false)
                {
                    grayscale = grayscale_hinting = true;
                    subpixel_hinting = false;
                }

                if ((face.face_flags & 8192) != 0)
                    subpixel_hinting = grayscale_hinting = false;

                exec.ignore_x_mode = subpixel_hinting || grayscale_hinting;
                exec.rasterizer_version = 38;
                if (exec.sph_tweak_flags & 8192)
                    exec.rasterizer_version = 35;

                if (true)
                {
                    exec.compatible_widths     = false;
                    exec.symmetrical_smoothing = false;
                    exec.bgr                   = false;
                    exec.subpixel_positioned   = true;
                }
                else
                {
                    /*
                    exec.compatible_widths = (FT_LOAD_TARGET_MODE(load_flags) !=  FT_Common.TT_LOAD_COMPATIBLE_WIDTHS);
                    exec.symmetrical_smoothing = (FT_LOAD_TARGET_MODE(load_flags) != FT_Common.TT_LOAD_SYMMETRICAL_SMOOTHING);
                    exec.bgr = (FT_LOAD_TARGET_MODE(load_flags) != FT_Common.TT_LOAD_BGR);
                    exec.subpixel_positioned = (FT_LOAD_TARGET_MODE(load_flags) != FT_Common.TT_LOAD_SUBPIXEL_POSITIONED);
                    */
                }
            }/* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
            else
            {
                grayscale = (FT_LOAD_TARGET_MODE(load_flags) != 2);
            }/* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */

            TT_Load_Context(exec, face, size);

            if (bIsSubpixHint)//#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
            {
                /* a change from mono to subpixel rendering (and vice versa) */
                /* requires a re-execution of the CVT program                */
                if (subpixel_hinting != exec.subpixel_hinting)
                {
                    exec.subpixel_hinting = subpixel_hinting;

                    for (var i = 0; i < size.cvt_size; i++)
                        size.cvt[i] = FT_MulFix(face.cvt[i], size.ttmetrics.scale);

                    tt_size_run_prep(size, pedantic);
                }

                /* a change from mono to grayscale rendering (and vice versa) */
                /* requires a re-execution of the CVT program                 */
                if (grayscale != exec.grayscale_hinting)
                {
                    exec.grayscale_hinting = grayscale_hinting;

                    for (var i = 0; i < size.cvt_size; i++)
                        size.cvt[i] = FT_MulFix(face.cvt[i], size.ttmetrics.scale);
                    tt_size_run_prep( size, pedantic );
                }
            }/* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
            else
            {
                /* a change from mono to grayscale rendering (and vice versa) */
                /* requires a re-execution of the CVT program                 */
                if (grayscale != exec.grayscale)
                {
                    exec.grayscale = grayscale;

                    for (i = 0; i < size.cvt_size; i++)
                        size.cvt[i] = FT_MulFix(face.cvt[i], size.ttmetrics.scale);

                    tt_size_run_prep(size, pedantic);
                }
            }//#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */

            /* see whether the cvt program has disabled hinting */
            if (exec.GS.instruct_control & 1)
                load_flags |= 2;

            /* load default graphics state -- if needed */
            if (exec.GS.instruct_control & 2)
                exec.GS.default_tt();

            exec.pedantic_hinting = ((load_flags & 128) != 0) ? true : false;
            loader.exec = exec;
            loader.instructions = dublicate_pointer(exec.glyphIns);
        }
    }//#endif /* TT_USE_BYTECODE_INTERPRETER */

    if (face.internal.incremental_interface)
        loader.glyf_offset = 0;
    else
    {
        face.goto_table(face, 1735162214, stream);
        var error = FT_Error;
        FT_Error = 0;
        if (error == 142)
            loader.glyf_offset = 0;
        else if ( error )
            return error;
        else
            loader.glyf_offset = stream.pos;
    }

    if (0 == glyf_table_only)
    {
        var gloader = glyph.internal.loader;
        FT_GlyphLoader_Rewind(gloader);
        loader.gloader = gloader;
    }

    loader.load_flags = load_flags;

    loader.face = face;
    loader.size = size;
    loader.glyph = glyph;
    loader.stream = stream;

    return 0;
}

function tt_face_get_device_metrics(face, ppem, gindex)
{
    var _ret = null;
    var record_size = face.hdmx_record_size;

    for (var nn = 0; nn < face.hdmx_record_count; nn++ )
    {
        if ( face.hdmx_record_sizes[nn] == ppem )
        {
            gindex += 2;
            if (gindex < record_size)
            {
                _ret = dublicate_pointer(face.hdmx_table);
                _ret.pos += (8 + nn * record_size + gindex);
            }

            break;
        }
    }

    return _ret;
}

function compute_glyph_metrics(loader, glyph_index)
{
    var bbox = new FT_BBox();
    var face = loader.face;
    var y_scale = 0x10000;
    var glyph = loader.glyph;
    var size = loader.size;

    if ((loader.load_flags & 1) == 0)
        y_scale = size.metrics.y_scale;

    if (glyph.format != 1668246896)
        FT_Outline_Get_CBox(glyph.outline, bbox);
    else
    {
        bbox.xMin = loader.bbox.xMin;
        bbox.yMin = loader.bbox.yMin;
        bbox.xMax = loader.bbox.xMax;
        bbox.yMax = loader.bbox.yMax;
    }

    glyph.linearHoriAdvance = loader.linear;

    glyph.metrics.horiBearingX = bbox.xMin;
    glyph.metrics.horiBearingY = bbox.yMax;
    glyph.metrics.horiAdvance  = loader.pp2.x - loader.pp1.x;

    if (face.postscript.isFixedPitch == 0 && (loader.load_flags & 2) == 0)
    {
        var widthp = tt_face_get_device_metrics(face, loader.size.metrics.x_ppem, glyph_index);

        if (face.driver.library.tt_hint_props.TT_CONFIG_OPTION_SUBPIXEL_HINTING)
        {
            var ignore_x_mode = (FT_LOAD_TARGET_MODE(loader.load_flags) != 2);

            if (null != widthp && ((ignore_x_mode && loader.exec.compatible_widths) || !ignore_x_mode || false))
                glyph.metrics.horiAdvance = widthp.data[widthp.pos] << 6;
        }
        else
        {
            if (null != widthp)
                glyph.metrics.horiAdvance = widthp.data[widthp.pos] << 6;
        }
    }

    glyph.metrics.width  = bbox.xMax - bbox.xMin;
    glyph.metrics.height = bbox.yMax - bbox.yMin;

    var top;
    var advance;

    if (face.vertical_info === true && face.vertical.number_Of_VMetrics > 0)
    {
        top = FT_DivFix(loader.pp3.y - bbox.yMax, y_scale);

        if (loader.pp3.y <= loader.pp4.y)
            advance = 0;
        else
            advance = FT_DivFix(loader.pp3.y - loader.pp4.y, y_scale);
    }
    else
    {
        var height = FT_DivFix(bbox.yMax - bbox.yMin, y_scale);
        if (face.os2.version != 0xFFFF)
            advance = face.os2.sTypoAscender - face.os2.sTypoDescender;
        else
            advance = face.horizontal.Ascender - face.horizontal.Descender;

        top = parseInt((advance - height)/2);
    }

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    var metrics = new FT_Incremental_MetricsRec();

    var incr = face.internal.incremental_interface;
    if (incr && incr.funcs.get_glyph_metrics)
    {
        metrics.bearing_x = 0;
        metrics.bearing_y = top;
        metrics.advance   = advance;

        var error = incr.funcs.get_glyph_metrics(incr.object, glyph_index, true, metrics);
        if (error != 0)
            return error;

        top     = metrics.bearing_y;
        advance = metrics.advance;
    }
    //#endif

    glyph.linearVertAdvance = advance;

    /* scale the metrics */
    if (0 == (loader.load_flags & 1))
    {
        top     = FT_MulFix(top, y_scale);
        advance = FT_MulFix(advance, y_scale);
    }

    glyph.metrics.vertBearingX = glyph.metrics.horiBearingX - parseInt(glyph.metrics.horiAdvance / 2);
    glyph.metrics.vertBearingY = top;
    glyph.metrics.vertAdvance  = advance;

    return 0;
}
function TT_Load_Glyph(size, glyph, glyph_index, load_flags)
{
    var loader = new TT_LoaderRec();
    var face = glyph.face;
    var error = 0;

    //#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    if (size.strike_index != 0xFFFFFFFF && ((load_flags & 8) == 0))
    {
        error = load_sbit_image(size, glyph, glyph_index, load_flags);
        if (error == 0)
        {
            if ((face.face_flags & 1) != 0)
            {
                tt_loader_init(loader, size, glyph, load_flags, true);
                load_truetype_glyph(loader, glyph_index, 0, true);
                glyph.linearHoriAdvance = loader.linear;
                glyph.linearVertAdvance = loader.top_bearing + loader.bbox.yMax - loader.vadvance;
            }

            return 0;
        }
    }
    //#endif

    if ((0 == (load_flags & 1)) && (size.ttmetrics.valid == 0))
        return 36;

    if ((load_flags & 16384) != 0)
        return 6;

    error = tt_loader_init(loader, size, glyph, load_flags, false);
    if (error != 0)
        return error;

    glyph.format        = 1869968492;
    glyph.num_subglyphs = 0;
    glyph.outline.flags = 0;

    /* main loading loop */
    error = load_truetype_glyph(loader, glyph_index, 0, false);
    if (error == 0)
    {
        if (glyph.format == 1668246896)
        {
            glyph.num_subglyphs = loader.gloader.base.num_subglyphs;
            glyph.subglyphs     = loader.gloader.base.subglyphs;
        }
        else
        {
            EquatingOutline(glyph.outline, loader.gloader.base.outline);
            glyph.outline.flags &= ~512;

            if (loader.pp1.x != 0)
                FT_Outline_Translate(glyph.outline, -loader.pp1.x, 0);
        }

        if (face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER && ((load_flags & 2) == 0))
        {
            if (loader.exec.GS.scan_control)
            {
                /* convert scan conversion mode to FT_OUTLINE_XXX flags */
                switch (loader.exec.GS.scan_type)
                {
                case 0: /* simple drop-outs including stubs */
                    glyph.outline.flags |= 32;
                    break;
                case 1: /* simple drop-outs excluding stubs */
                    /* nothing; it's the default rendering mode */
                    break;
                case 4: /* smart drop-outs including stubs */
                    glyph.outline.flags |= (16 | 32);
                    break;
                case 5: /* smart drop-outs excluding stubs  */
                    glyph.outline.flags |= 16;
                    break;

                default: /* no drop-out control */
                    glyph.outline.flags |= 8;
                    break;
                }
            }
            else
                glyph.outline.flags |= 8;
        }
        compute_glyph_metrics(loader, glyph_index);
    }

    if ((0 == (load_flags & 1)) && size.metrics.y_ppem < 24)
        glyph.outline.flags |= 256;

    return error;
}

function load_truetype_glyph(loader, glyph_index, recurse_count, header_only)
{
    var error = 0;
    var x_scale, y_scale;
    var offset;
    var face = loader.face;
    var gloader = loader.gloader;
    var opened_frame = 0;

    var glyph_data;
    var glyph_data_loaded = 0;

    if (recurse_count > 1 && recurse_count > face.max_profile.maxComponentDepth )
        return 21;

    if (glyph_index >= face.num_glyphs)
        error = 16;

    loader.glyph_index = glyph_index;

    if ((loader.load_flags & 1) == 0)
    {
        x_scale = loader.size.metrics.x_scale;
        y_scale = loader.size.metrics.y_scale;
    }
    else
    {
        x_scale = 0x10000;
        y_scale = 0x10000;
    }

    tt_get_metrics(loader, glyph_index);

    if (face.internal.incremental_interface != null)
    {
        glyph_data = face.internal.incremental_interface.funcs.get_glyph_data(face.internal.incremental_interface.object, glyph_index);
        error = FT_Error;
        if (error != 0)
            return error;

        glyph_data_loaded = 1;
        offset = 0;
        loader.byte_len  = glyph_data.length;

        loader.stream = new FT_Stream(glyph_data.pointer, glyph_data.length);
    }
    else
    {
        var __mem = tt_face_get_location(face, glyph_index);
        offset = __mem.loc;
        loader.byte_len = __mem.size;
    }

    if (loader.byte_len > 0)
    {
        if (0 == loader.glyf_offset && null == face.internal.incremental_interface)
            return 8;

        error = face.access_glyph_frame(loader, glyph_index, loader.glyf_offset + offset, loader.byte_len);
        if (error != 0)
            return error;

        opened_frame = 1;

        error = face.read_glyph_header(loader);
        if (error != 0 || header_only)
            return error;
    }

    var subpixel_ = (loader.exec && loader.exec.subpixel_hinting) ? true : false;
    var grayscale_ = (loader.exec && loader.exec.grayscale_hinting) ? true : false;
    var use_aw_2_  = (subpixel_ && grayscale_);

    if (loader.byte_len == 0 || loader.n_contours == 0)
    {
        loader.bbox.xMin = 0;
        loader.bbox.xMax = 0;
        loader.bbox.yMin = 0;
        loader.bbox.yMax = 0;

        if (header_only == 1)
            return error;

        loader.pp1.x = loader.bbox.xMin - loader.left_bearing;
        loader.pp1.y = 0;
        loader.pp2.x = loader.pp1.x + loader.advance;
        loader.pp2.y = 0;
        loader.pp3.x = use_aw_2_ ? (loader.advance >> 0) : 0;
        loader.pp3.y = loader.top_bearing + loader.bbox.yMax;
        loader.pp4.x = use_aw_2_ ? (loader.advance >> 0) : 0;
        loader.pp4.y = loader.pp3.y - loader.vadvance;

        //#ifdef FT_CONFIG_OPTION_INCREMENTAL
        tt_get_metrics_incr_overrides(loader, glyph_index);
        //#endif

        //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
        if (loader.face.doblend != 0)
        {
            var deltas = TT_Vary_Get_Glyph_Deltas(loader.face, glyph_index, 4);
            error = FT_Error;
            if (error != 0)
                return error;

            loader.pp1.x += deltas[0].x; loader.pp1.y += deltas[0].y;
            loader.pp2.x += deltas[1].x; loader.pp2.y += deltas[1].y;
            loader.pp3.x += deltas[2].x; loader.pp3.y += deltas[2].y;
            loader.pp4.x += deltas[3].x; loader.pp4.y += deltas[3].y;

            deltas = null;
        }
        //#endif

        if ((loader.load_flags & 1) == 0)
        {
            loader.pp1.x = FT_MulFix(loader.pp1.x, x_scale);
            loader.pp2.x = FT_MulFix(loader.pp2.x, x_scale);
            loader.pp3.y = FT_MulFix(loader.pp3.y, y_scale);
            loader.pp4.y = FT_MulFix(loader.pp4.y, y_scale);
        }

        error = 0;
        return error;
    }

    loader.pp1.x = loader.bbox.xMin - loader.left_bearing;
    loader.pp1.y = 0;
    loader.pp2.x = loader.pp1.x + loader.advance;
    loader.pp2.y = 0;
    loader.pp3.x = use_aw_2_ ? (loader.advance >> 0) : 0;
    loader.pp3.y = loader.top_bearing + loader.bbox.yMax;
    loader.pp4.x = use_aw_2_ ? (loader.advance >> 0) : 0;
    loader.pp4.y = loader.pp3.y - loader.vadvance;

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    tt_get_metrics_incr_overrides(loader, glyph_index);
    //#endif

    if (loader.n_contours > 0)
    {
        error = face.read_simple_glyph( loader );
        if (error != 0)
            return error;

        face.forget_glyph_frame(loader);
        opened_frame = 0;

        error = TT_Process_Simple_Glyph(loader);
        if (error != 0)
            return error;

        FT_GlyphLoader_Add(gloader);
    }
    else if (loader.n_contours == -1)
    {
        var start_point = gloader.base.outline.n_points;
        var start_contour = gloader.base.outline.n_contours;

        error = face.read_composite_glyph(loader);
        if (error != 0)
            return error;

        var ins_pos = loader.ins_pos;

        face.forget_glyph_frame( loader );
        opened_frame = 0;

        //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT

        if (face.doblend != 0)
        {
            var deltas = TT_Vary_Get_Glyph_Deltas(face, glyph_index, gloader.current.num_subglyphs + 4);
            error = FT_Error;
            if (error != 0)
                return error;

            var subglyph = gloader.current.subglyphs[gloader.base.num_subglyphs];
            var limit = gloader.current.num_subglyphs;

            var i = 0;
            for (; i < limit; ++i, ++subglyph )
            {
                if (subglyph.flags & 2)
                {
                    subglyph.arg1 += (deltas[i].x & 0xFFFF);
                    subglyph.arg2 += (deltas[i].y & 0xFFFF);
                }
            }

            loader.pp1.x += deltas[i + 0].x; loader.pp1.y += deltas[i + 0].y;
            loader.pp2.x += deltas[i + 1].x; loader.pp2.y += deltas[i + 1].y;
            loader.pp3.x += deltas[i + 2].x; loader.pp3.y += deltas[i + 2].y;
            loader.pp4.x += deltas[i + 3].x; loader.pp4.y += deltas[i + 3].y;

            deltas = null;
        }
        //#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */

        if ((loader.load_flags & 1) == 0)
        {
            loader.pp1.x = FT_MulFix(loader.pp1.x, x_scale);
            loader.pp2.x = FT_MulFix(loader.pp2.x, x_scale);
            loader.pp3.y = FT_MulFix(loader.pp3.y, y_scale);
            loader.pp4.y = FT_MulFix(loader.pp4.y, y_scale);
        }

        if ((loader.load_flags & 1024) != 0)
        {
            FT_GlyphLoader_Add( gloader );
            loader.glyph.format = 1668246896;

            return error;
        }

        var n, num_base_points;
        var subglyph       = 0;

        var num_points     = start_point;
        var num_subglyphs  = gloader.current.num_subglyphs;
        var num_base_subgs = gloader.base.num_subglyphs;

        var old_stream = loader.stream;
        var old_byte_len = loader.byte_len;

        FT_GlyphLoader_Add(gloader);

        for ( n = 0; n < num_subglyphs; n++ )
        {
            var pp = new Array(4);
            subglyph = gloader.base.subglyphs[num_base_subgs + n];

            pp[0] = loader.pp1;
            pp[1] = loader.pp2;
            pp[2] = loader.pp3;
            pp[3] = loader.pp4;

            num_base_points = gloader.base.outline.n_points;

            error = load_truetype_glyph(loader, subglyph.index, recurse_count + 1, 0);
            if (error != 0)
                return error;

            subglyph = gloader.base.subglyphs[num_base_subgs + n];

            if (0 == (subglyph.flags & 0x0200))
            {
                loader.pp1 = pp[0];
                loader.pp2 = pp[1];
                loader.pp3 = pp[2];
                loader.pp4 = pp[3];
            }

            if (gloader.base.outline.n_points == num_base_points)
                continue;

            TT_Process_Composite_Component(loader, subglyph, start_point, num_base_points);
        }

        loader.stream   = old_stream;
        loader.byte_len = old_byte_len;

        loader.ins_pos = ins_pos;

        var bIsSubFlags = true;
        if (bIsSubFlags && face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER)
            bIsSubFlags = ((subglyph.flags & 256) != 0);

        if (((loader.load_flags & 2) == 0) && bIsSubFlags && num_points > start_point)
            TT_Process_Composite_Glyph(loader, start_point, start_contour);
    }
    else
        return 20;

    if (opened_frame == 1)
        face.forget_glyph_frame(loader);

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (glyph_data_loaded != 0)
    {
        face.internal.incremental_interface.funcs.free_glyph_data(face.internal.incremental_interface.object, glyph_data);
        glyph_data = null;
    }
    //#endif
    return error;
}
function TT_Init_Glyph_Loading(face)
{
    face.access_glyph_frame   = TT_Access_Glyph_Frame;
    face.read_glyph_header    = TT_Load_Glyph_Header;
    face.read_simple_glyph    = TT_Load_Simple_Glyph;
    face.read_composite_glyph = TT_Load_Composite_Glyph;
    face.forget_glyph_frame   = TT_Forget_Glyph_Frame;
}
/******************************************************************************/
// driver
/******************************************************************************/
var tt_service_truetype_engine = new FT_Service_TrueTypeEngineRec(0);
var tt_service_truetype_glyf = new FT_Service_TTGlyfRec(tt_face_get_location);
var tt_service_gx_multi_masters = new FT_Service_MultiMastersRec(null,null,TT_Set_MM_Blend,TT_Get_MM_Var,TT_Set_Var_Design);
var tt_services = new Array(4);
tt_services[0] = new FT_ServiceDescRec(FT_SERVICE_ID_XF86_NAME,FT_XF86_FORMAT_TRUETYPE);
tt_services[1] = new FT_ServiceDescRec(FT_SERVICE_ID_MULTI_MASTERS,tt_service_gx_multi_masters);
tt_services[2] = new FT_ServiceDescRec(FT_SERVICE_ID_TRUETYPE_ENGINE,tt_service_truetype_engine);
tt_services[3] = new FT_ServiceDescRec(FT_SERVICE_ID_TT_GLYF,tt_service_truetype_glyf);

var FT_TT_SERVICES_GET                  = tt_services;
var FT_TT_SERVICE_GX_MULTI_MASTERS_GET  = tt_service_gx_multi_masters;
var FT_TT_SERVICE_TRUETYPE_GLYF_GET     = tt_service_truetype_glyf;

function tt_check_trickyness_sfnt_ids(face)
{
    var checksum = 0;
    var i = 0;
    var j = 0;
    var k = 0;
    var num_matched_ids = new Array(13);
    for (var l = 0; l < 13; l++)
        num_matched_ids[l] = 0;

    var TRICK_SFNT_ID_cvt = 0;
    var TRICK_SFNT_ID_fpgm = 1;
    var TRICK_SFNT_ID_prep = 2;

    var has_cvt  = false;
    var has_fpgm = false;
    var has_prep = false;

    var sfnt_id = face.driver.sfnt_id;

    for (i = 0; i < face.num_tables; i++)
    {
        checksum = 0;
        switch(face.dir_tables[i].Tag)
        {
            case 1668707360:
                k = TRICK_SFNT_ID_cvt;
                has_cvt  = true;
                break;

            case 1718642541:
                k = TRICK_SFNT_ID_fpgm;
                has_fpgm = true;
                break;

            case 1886545264:
                k = TRICK_SFNT_ID_prep;
                has_prep = true;
                break;

            default:
                continue;
        }

        for ( j = 0; j < 13; j++ )
            if ( face.dir_tables[i].Length == sfnt_id[j][k].Length )
            {
                if (checksum != 0)
                    checksum = tt_get_sfnt_checksum(face, i);

                if (sfnt_id[j][k].CheckSum == checksum)
                    num_matched_ids[j]++;

                if (num_matched_ids[j] == 3)
                    return true;
            }
    }
    for (j = 0; j < 13; j++)
    {
        if ( !has_cvt  && !sfnt_id[j][TRICK_SFNT_ID_cvt].Length )
            num_matched_ids[j] ++;
        if ( !has_fpgm && !sfnt_id[j][TRICK_SFNT_ID_fpgm].Length )
            num_matched_ids[j] ++;
        if ( !has_prep && !sfnt_id[j][TRICK_SFNT_ID_prep].Length )
            num_matched_ids[j] ++;
        if (num_matched_ids[j] == 3)
            return true;
    }
    return false;
}
function tt_get_sfnt_checksum(face, i)
{
    if (!face.goto_table)
        return 0;

    var len = face.goto_table(face, face.dir_tables[i].Tag, face.stream);
        return 0;

    if (FT_Error != 0)
        return 0;

    return tt_synth_sfnt_checksum(face.stream, face.dir_tables[i].Length);
}
function tt_synth_sfnt_checksum(stream, length)
{
    var error = 0;
    var checksum = 0;
    var i = 0;

    var len = length;
    if (0 != stream.EnterFrame(len))
        return 0;

    for ( ; len > 3; len -= 4 )
        checksum += stream.GetULong();

    for ( i = 3; len > 0; len --,i--)
        checksum += (stream.GetUChar() << (i * 8));

    stream.ExitFrame();
    return checksum;
}
function tt_check_trickyness(face)
{
    if (!face)
        return false;

    if ((face.family_name != "") && (tt_check_trickyness_family(face.family_name) === true))
        return true;

    return tt_check_trickyness_sfnt_ids(face);
}
function tt_check_trickyness_family(name)
{
    if (name == "DFKaiSho-SB")
        return true;
    if (name == "DFKaiShu")
        return true;
    if (name == "DFKai-SB")
        return true;
    if (name == "HuaTianKaiTi?")
        return true;
    if (name == "HuaTianSongTi?")
        return true;
    if (name == "MingLiU")
        return true;
    if (name == "PMingLiU")
        return true;
    if (name == "MingLi43")
        return true;
    return false;
}
function tt_face_load_hdmx(face, stream)
{
    var error = 0;
    var version = 0;
    var nn = 0;
    var num_records = 0;
    var table_size = 0;
    var record_size = 0;

    table_size = face.goto_table(face, 1751412088, stream);
    if ( (FT_Error != 0) || table_size < 8 )
    {
		FT_Error = 0;
		return 0;
	}

    face.hdmx_table = new CPointer();
    error = stream.ExtractFrame(table_size, face.hdmx_table);
    if (error != 0)
        return error;

    var p = dublicate_pointer(face.hdmx_table);
    var end = p.pos + table_size;

    version     = FT_NEXT_USHORT(p);
    num_records = FT_NEXT_USHORT(p);
    record_size = FT_NEXT_ULONG(p);

    if (record_size >= 0xFFFF0000)
        record_size &= 0xFFFF;

    if (version != 0 || num_records > 255 || record_size > 0x10001)
    {
        error = 3;
        face.hdmx_table = null;
        face.hdmx_table_size = 0;
        stream.ReleaseFrame();
        return error;
    }

    if (0 != num_records)
        face.hdmx_record_sizes = new Array(num_records);

    for ( nn = 0; nn < num_records; nn++ )
    {
        if ( (p.pos + record_size) > end )
            break;

        face.hdmx_record_sizes[nn] = p.data[p.pos];
        p.pos += record_size;
    }

    face.hdmx_record_count = nn;
    face.hdmx_table_size   = table_size;
    face.hdmx_record_size  = record_size;

    return error;
}

function tt_face_load_loca(face, stream)
{
    var error = 0;
    var table_len = 0;
    var shift = 0;

    face.glyf_len = face.goto_table(face, 1735162214, stream);
    error = FT_Error;

    if ( error == 142)
        face.glyf_len = 0;
    else if (error != 0)
        return error;

    table_len = face.goto_table(face, 1819239265, stream);
    error = FT_Error;
    if (error != 0)
    {
        error = 144;
        return error;
    }

    if (face.header.Index_To_Loc_Format != 0)
    {
        shift = 2;
        if (table_len >= 0x40000)
        {
            error = 8;
            return error;
        }
        face.num_locations = table_len >>> shift;
    }
    else
    {
        shift = 1;

        if (table_len >= 0x20000)
        {
            error = 8;
            return error;
        }
        face.num_locations = table_len >>> shift;
    }

    if (face.num_locations != (face.num_glyphs + 1))
    {
        if (face.num_locations <= face.num_glyphs)
        {
            var new_loca_len = (face.num_glyphs + 1) << shift;

            var pos = stream.pos;
            var dist = 0x7FFFFFFF;

            var entry = 0;
            for (; entry < face.num_tables; entry++)
            {
                var diff = face.dir_tables[entry].Offset - pos;
                if (diff > 0 && diff < dist)
                    dist = diff;
            }

            if (entry == face.num_tables)
            {
                dist = stream.size - pos;
            }

            if (new_loca_len <= dist)
            {
                face.num_locations = face.num_glyphs + 1;
                table_len          = new_loca_len;
            }
        }
    }

    face.glyph_locations = new CPointer();
    error = stream.ExtractFrame(table_len, face.glyph_locations);
    return error;
}
function tt_face_get_location(face, gindex)
{
    var pos1 = 0;
    var pos2 = 0;
    var asize = 0;

    pos1 = pos2 = 0;
    var p = new CPointer();
    p.data = face.glyph_locations.data;
    var pos_s = face.glyph_locations.pos;
    var p_limit = 0;
    if (gindex < face.num_locations)
    {
        if (face.header.Index_To_Loc_Format != 0)
        {
            p.pos = pos_s + gindex * 4;
            p_limit = pos_s + face.num_locations * 4;

            pos1 = FT_NEXT_ULONG(p);
            pos2 = pos1;

            if (p.pos + 4 <= p_limit)
                pos2 = FT_NEXT_ULONG(p);
        }
        else
        {
            p.pos  = pos_s + gindex * 2;
            p_limit = pos_s + face.num_locations * 2;

            pos1 = FT_NEXT_USHORT(p);
            pos2 = pos1;

            if (p.pos + 2 <= p_limit)
                pos2 = FT_NEXT_USHORT(p);

            pos1 <<= 1;
            pos2 <<= 1;
        }
    }

    if (pos1 >= face.glyf_len)
        return {loc:0,size:0};

    if (pos2 >= face.glyf_len)
        pos2 = face.glyf_len;

    if (pos2 >= pos1)
        asize = pos2 - pos1;
    else
        asize = face.glyf_len - pos1;

    return {loc:pos1,size:asize};
}
function tt_face_load_cvt(face, stream)
{
    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if (face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
    {
        var table_len = face.goto_table(face, 1668707360, stream);
        var error = FT_Error;
		FT_Error = 0;

        if (error != 0)
        {
            face.cvt_size = 0;
            face.cvt = null;
            return 0;
        }

        face.cvt_size = table_len / 2;
        face.cvt = CreateIntArray(face.cvt_size);

        error = stream.EnterFrame(table_len);
        if (error != 0)
            return error;

        for (var i = 0; i < face.cvt_size; i++)
            face.cvt[i] = stream.GetShort();
        
        //#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
        if (face.doblend)
            error = tt_face_vary_cvt(face, stream);
        //#endif

        return error;
    }
    //#endif
    return 0;
}
function tt_face_load_fpgm(face, stream)
{
    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if (face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
    {
        /* The font program is optional */
        var table_len = face.goto_table(face, 1718642541, stream);
        var error = FT_Error;
		FT_Error = 0;
		
        if (error != 0)
        {
            face.font_program = null;
            face.font_program_size = 0;
            error = 0;
            return error;
        }
        else
        {
            face.font_program_size = table_len;
            face.font_program = new CPointer();
            return stream.ExtractFrame(table_len, face.font_program);
        }
    }
    //#endif
    return 0;
}
function tt_face_load_prep(face, stream)
{
    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if (face.driver.library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
    {
        var table_len = face.goto_table(face, 1886545264, stream);
        var error = FT_Error;
		FT_Error = 0;
		
        if (error != 0)
        {
            face.cvt_program = null;
            face.cvt_program_size = 0;
            return 0;
        }
        else
        {
            face.cvt_program_size = table_len;
            face.cvt_program = new CPointer();
            return stream.ExtractFrame(table_len, face.cvt_program);
        }
    }
    //#endif
    return 0;
}
function tt_check_single_notdef(face)
{
    var result = false;

    var asize = 0;
    var i = 0;
    var glyph_index = 0;
    var count = 0;

    for( i = 0; i < face.num_locations; i++ )
    {
        asize = tt_face_get_location(face, i).size;
        if ( asize > 0 )
        {
            count += 1;
            if ( count > 1 )
                break;
            glyph_index = i;
        }
    }

    /* Only have a single outline. */
    if (count == 1)
    {
        if (glyph_index == 0)
            result = true;
        else
        {
            /* FIXME: Need to test glyphname == .notdef ? */
            FT_Error = 0;
            var buffer = g_memory.Alloc(10);
            buffer.pos = 0;

            FT_Get_Glyph_Name(face, glyph_index, buffer, 8);
            var buf = "";
            for (var i = 0; i < 10; i++)
            {
                buf += String.fromCharCode(buffer.data[i]);
            }

            if (FT_Error != 0 && buf[0] == '.' && (0 == _strncmp(buf,".notdef",8)))
                result = true;
        }
    }

    FT_Error = 0;
    return result;
}

function tt_driver_init(ttdriver)
{
    var _tt_hint_props = ttdriver.library.tt_hint_props;
    if (_tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
    {
        if (!TT_New_Context(ttdriver))
            153;
    }
    return 0;
}
function tt_driver_done(ttdriver)
{
    var _tt_hint_props = ttdriver.library.tt_hint_props;
    if (_tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
    {
        if (ttdriver.context != null)
        {
            TT_Done_Context(ttdriver.context);
            ttdriver.context = null;
        }
    }
}
function tt_get_interface(driver, tt_interface)
{
    var result = ft_service_list_lookup(FT_TT_SERVICES_GET, tt_interface);
    if (result != null)
        return result;

    if (driver == null)
        return null;

    var sfntd = driver.library.FT_Get_Module("sfnt");
    if (null != sfntd)
    {
        var sfnt = sfntd.clazz.module_interface;
        if (null != sfnt)
            return sfnt.get_interface(driver, tt_interface);
    }
    return null;
}
function tt_face_init(stream, face, face_index)
{
    var error = 0;
    var library = face.driver.library;
    var sfnt = library.FT_Get_Module_Interface("sfnt");

    error = stream.Seek(0);
    if (error != 0)
        return error;

    error = sfnt.init_face(stream, face, face_index);
    if (error != 0)
        return error;

    if (face.format_tag != 0x00010000 && face.format_tag != 0x00020000 && face.format_tag != 1953658213)
    {
        error = 2;
        return error;
    }

    if (library.tt_hint_props.TT_USE_BYTECODE_INTERPRETER || true)
        face.face_flags |= 2048;

    if ( face_index < 0 )
        return 0;

    error = sfnt.load_face(stream, face, face_index);
    if (error != 0)
        return error;

    if (tt_check_trickyness(face) === true)
        face.face_flags |= 8192;

    error = tt_face_load_hdmx(face, stream);
    if (error != 0)
        return error;

    if ((face.face_flags & 1) != 0)
    {
        //#ifdef FT_CONFIG_OPTION_INCREMENTAL
        if (null == face.internal.incremental_interface)
            error = tt_face_load_loca(face, stream);
        if (error == 0)
            error = tt_face_load_cvt(face, stream);
        if (error == 0)
            error = tt_face_load_fpgm(face, stream);
        if (error == 0)
            error = tt_face_load_prep(face, stream);

        /* Check the scalable flag based on `loca'. */
        if ((null == face.internal.incremental_interface) && (0 != face.num_fixed_sizes) && (face.glyph_locations != null) &&
            tt_check_single_notdef(face))
        {
            face.face_flags &= ~1;
        }
        //#endif
    }

    //#if defined( TT_CONFIG_OPTION_UNPATENTED_HINTING )
    // TODO:
    //#endif

    TT_Init_Glyph_Loading(face);
    return error;
}
function tt_face_done(face)
{
}
function tt_size_init()
{
    var size  = new TT_SizeRec();

    //#ifdef TT_USE_BYTECODE_INTERPRETER
    size.bytecode_ready = 0;
    size.cvt_ready      = 0;
    //#endif

    size.ttmetrics.valid = 0;
    size.strike_index = 0xFFFFFFFF;

    return size;
}
function tt_size_done(size)
{
    //#ifdef TT_USE_BYTECODE_INTERPRETER
    if (size.bytecode_ready)
        tt_size_done_bytecode(size);
    //#endif
    size.ttmetrics.valid = 0;
}
function tt_slot_init(slot)
{
    return FT_GlyphLoader_CreateExtra(slot.internal.loader);
}
function Load_Glyph(slot, size, glyph_index, load_flags)
{
    var face = slot.face;
    var error = 0;

    if (!slot)
        return 37;

    if (!size)
        return 36;

    if (!face)
        return 6;

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (glyph_index >= face.num_glyphs && (null == face.internal.incremental_interface))
        return 6;

    if ((load_flags & 2) != 0)
    {
        if ((face.face_flags & 8192) != 0)
            load_flags &= ~2;

        if ((load_flags & 32768) != 0)
            load_flags |= 2;
    }

    if ((load_flags & (1024 | 1)) != 0)
    {
        load_flags |= (8 | 1);

        if ((face.face_flags & 8192) == 0)
            load_flags |= 2;
    }

    error = TT_Load_Glyph(size, slot, glyph_index, load_flags);
    return error;
}
function tt_get_kerning(face, left_glyph, right_glyph, kerning)
{
    kerning.x = 0;
    kerning.y = 0;

    if (face.sfnt)
        kerning.x = face.sfnt.get_kerning(face, left_glyph, right_glyph);

    return 0;
}
function tt_get_advances(face, start, count, flags, advances)
{
    /* XXX: TODO: check for sbits */
    if ((face.face_flags & 16) != 0)
    {
        for (var nn = 0; nn < count; nn++)
        {
            /* since we don't need `tsb', we use zero for `yMax' parameter */
            var res = TT_Get_VMetrics(face, start + nn, 0);
            advances[nn] = res.ah;
        }
    }
    else
    {
        for (var nn = 0; nn < count; nn++)
        {
            var res = TT_Get_HMetrics(face, start);
            advances[nn] = res.aw;
        }
    }

    return 0;
}
function tt_size_request(size, req)
{
    var error = 0;

    //#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    if ((size.face.face_flags & 2) != 0)
    {
        var sfnt = size.face.sfnt;
        var strike_index = sfnt.set_sbit_strike(size.face, req);

        error = FT_Error;
        FT_Error = 0;

        if (error != 0)
            size.strike_index = 0xFFFFFFFF;
    else
        return this.select_size(size, strike_index);
    }
    //#endif

    FT_Request_Metrics(size.face, req);

    if ((size.face.face_flags & 1) != 0)
    {
        error = tt_size_reset(size);

        var s = size._metrics;
        var d = size.metrics;

        d.x_ppem = s.x_ppem;
        d.y_ppem = s.y_ppem;

        d.x_scale = s.x_scale;
        d.y_scale = s.y_scale;

        d.ascender = s.ascender;
        d.descender = s.descender;
        d.height = s.height;
        d.max_advance = s.max_advance;
    }

    return error;
}
function tt_size_select(size, strike_index)
{
    var ttface = size.face;
    var error = 0;
    size.strike_index = strike_index;

    if ((ttface.face_flags & 1) != 0)
    {
        FT_Select_Metrics(ttface, strike_index);
        tt_size_reset(size);
    }
    else
    {
        var sfnt = ttface.sfnt;
        var metrics = size.metrics;

        error = sfnt.load_strike_metrics(ttface, strike_index, metrics);
        if (error != 0)
            size.strike_index = 0xFFFFFFFF;
    }
    return error;
}

function tt_size_reset(size)
{
    size.ttmetrics.valid = 0;
    var face = size.face;

    var d = size._metrics;
    var s = size.metrics;

    d.x_ppem = s.x_ppem;
    d.y_ppem = s.y_ppem;

    d.x_scale = s.x_scale;
    d.y_scale = s.y_scale;

    d.ascender = s.ascender;
    d.descender = s.descender;
    d.height = s.height;
    d.max_advance = s.max_advance;

    if (d.x_ppem < 1 || d.y_ppem < 1)
        return 151;

    if (face.header.Flags & 8)
    {
        d.x_scale = FT_DivFix(d.x_ppem << 6, face.units_per_EM);
        d.y_scale = FT_DivFix(d.y_ppem << 6, face.units_per_EM);

        d.ascender = FT_PIX_ROUND(FT_MulFix(face.ascender, d.y_scale));
        d.descender = FT_PIX_ROUND(FT_MulFix(face.descender, d.y_scale));
        d.height = FT_PIX_ROUND(FT_MulFix(face.height, d.y_scale));
        d.max_advance = FT_PIX_ROUND(FT_MulFix(face.max_advance_width, d.x_scale));
    }

    var tt = size.ttmetrics;
    /* compute new transformation */
    if (d.x_ppem >= d.y_ppem)
    {
        tt.scale   = d.x_scale;
        tt.ppem    = d.x_ppem;
        tt.x_ratio = 0x10000;
        tt.y_ratio = FT_MulDiv(d.y_ppem, 0x10000, d.x_ppem);
    }
    else
    {
        tt.scale   = d.y_scale;
        tt.ppem    = d.y_ppem;
        tt.x_ratio = FT_MulDiv(d.x_ppem, 0x10000, d.y_ppem);
        tt.y_ratio = 0x10000;
    }

    //#ifdef TT_USE_BYTECODE_INTERPRETER
    // TODO:
    //#endif

    size.ttmetrics.valid = 1;
    return 0;
}

function TT_Driver_Class()
{
    this.flags = 0x101;
    this.name = "truetype";
    this.version = 0x10000;
    this.requires = 0x20000;

    this.module_interface = null;

    this.init = tt_driver_init;
    this.done = tt_driver_done;
    this.get_interface = tt_get_interface;

    this.face_object_size = 0;
    this.size_object_size = 0;
    this.slot_object_size = 0;

    this.init_face = tt_face_init;
    this.done_face = tt_face_done;

    this.init_size = tt_size_init;
    this.done_size = tt_size_done;

    this.init_slot = tt_slot_init;
    this.done_slot = null;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.set_char_sizes = ft_stub_set_char_sizes;
    this.set_pixel_sizes = ft_stub_set_pixel_sizes;
    //#endif

    this.load_glyph = Load_Glyph;

    this.get_kerning = tt_get_kerning;
    this.attach_file = null;
    this.get_advances = tt_get_advances;

    this.request_size = tt_size_request;
    this.select_size = tt_size_select;
}

function tt_sfnt_id_rec(c,l)
{
    this.CheckSum = c;
    this.Length = l;
}

function TT_Driver()
{
    this.clazz = null;      // FT_Module_Class
    this.library = null;    // FT_Library
    this.memory = null;     // FT_Memory
    this.generic = null;    // FT_Generic

    this.clazz = new TT_Driver_Class();
    this.faces_list = [];
    this.extensions = null;
    this.glyph_loader = null;

    this.context = null;
    this.zone = new TT_GlyphZoneRec();
    this.extension_component = null;

    this.open_face = function(stream, face_index)
    {
        if (null == this.sfnt_id)
        {
            this.sfnt_id = new Array(13);
            this.sfnt_id[0] = new Array(3);
            this.sfnt_id[0][0] = new tt_sfnt_id_rec(0x05bcf058, 0x000002e4);
            this.sfnt_id[0][1] = new tt_sfnt_id_rec(0x28233bf1, 0x000087c4);
            this.sfnt_id[0][2] = new tt_sfnt_id_rec(0xa344a1ea, 0x000001e1);

            this.sfnt_id[1] = new Array(3);
            this.sfnt_id[1][0] = new tt_sfnt_id_rec(0x05bcf058, 0x000002e4);
            this.sfnt_id[1][1] = new tt_sfnt_id_rec(0x28233bf1, 0x000087c4);
            this.sfnt_id[1][2] = new tt_sfnt_id_rec(0xa344a1eb, 0x000001e1);

            this.sfnt_id[2] = new Array(3);
            this.sfnt_id[2][0] = new tt_sfnt_id_rec(0x11e5ead4, 0x00000350);
            this.sfnt_id[2][1] = new tt_sfnt_id_rec(0x5a30ca3b, 0x00009063);
            this.sfnt_id[2][2] = new tt_sfnt_id_rec(0x13a42602, 0x0000007e);

            this.sfnt_id[3] = new Array(3);
            this.sfnt_id[3][0] = new tt_sfnt_id_rec(0xfffbfffc, 0x00000008);
            this.sfnt_id[3][1] = new tt_sfnt_id_rec(0x9c9e48b8, 0x0000bea2);
            this.sfnt_id[3][2] = new tt_sfnt_id_rec(0x70020112, 0x00000008);

            this.sfnt_id[4] = new Array(3);
            this.sfnt_id[4][0] = new tt_sfnt_id_rec(0xfffbfffc, 0x00000008);
            this.sfnt_id[4][1] = new tt_sfnt_id_rec(0x0a5a0483, 0x00017c39);
            this.sfnt_id[4][2] = new tt_sfnt_id_rec(0x70020112, 0x00000008);

            this.sfnt_id[5] = new Array(3);
            this.sfnt_id[5][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[5][1] = new tt_sfnt_id_rec(0x40c92555, 0x000000e5);
            this.sfnt_id[5][2] = new tt_sfnt_id_rec(0xa39b58e3, 0x0000117c);

            this.sfnt_id[6] = new Array(3);
            this.sfnt_id[6][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[6][1] = new tt_sfnt_id_rec(0x33c41652, 0x000000e5);
            this.sfnt_id[6][2] = new tt_sfnt_id_rec(0x26d6c52a, 0x00000f6a);

            this.sfnt_id[7] = new Array(3);
            this.sfnt_id[7][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[7][1] = new tt_sfnt_id_rec(0x6db1651d, 0x0000019d);
            this.sfnt_id[7][2] = new tt_sfnt_id_rec(0x6c6e4b03, 0x00002492);

            this.sfnt_id[8] = new Array(3);
            this.sfnt_id[8][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[8][1] = new tt_sfnt_id_rec(0x40c92555, 0x000000e5);
            this.sfnt_id[8][2] = new tt_sfnt_id_rec(0xde51fad0, 0x0000117c);

            this.sfnt_id[9] = new Array(3);
            this.sfnt_id[9][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[9][1] = new tt_sfnt_id_rec(0x85e47664, 0x000000e5);
            this.sfnt_id[9][2] = new tt_sfnt_id_rec(0xa6c62831, 0x00001caa);

            this.sfnt_id[10] = new Array(3);
            this.sfnt_id[10][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[10][1] = new tt_sfnt_id_rec(0x2d891cfd, 0x0000019d);
            this.sfnt_id[10][2] = new tt_sfnt_id_rec(0xa0604633, 0x00001de8);

            this.sfnt_id[11] = new Array(3);
            this.sfnt_id[11][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[11][1] = new tt_sfnt_id_rec(0x40aa774c, 0x000001cb);
            this.sfnt_id[11][2] = new tt_sfnt_id_rec(0x9b5caa96, 0x00001f9a);

            this.sfnt_id[12] = new Array(3);
            this.sfnt_id[12][0] = new tt_sfnt_id_rec(0x00000000, 0x00000000);
            this.sfnt_id[12][1] = new tt_sfnt_id_rec(0x0d3de9cb, 0x00000141);
            this.sfnt_id[12][2] = new tt_sfnt_id_rec(0xd4127766, 0x00002280);
        }

        FT_Error = 0;
        var face = new TT_Face();
        var internal = new FT_Face_Internal();

        face.driver = this;
        face.memory = this.memory;
        face.stream = stream;

        //#ifdef FT_CONFIG_OPTION_INCREMENTAL
        face.internal = internal;
        face.internal.incremental_interface = null;
        //#endif

        var err1 = this.clazz.init_face(stream, face, face_index);

        if (err1 != 0)
        {
            face = null;
            FT_Error = err1;
            return null;
        }

        var err2 = find_unicode_charmap(face);
        if (err2 != 0 && err2 != 38)
        {
            face = null;
            FT_Error = err2;
            return null;
        }

		FT_Error = 0;
        return face;
    }
}

function create_tt_driver(library)
{
    var driver = new TT_Driver();
    driver.library = library;
    driver.memory = library.Memory;

    driver.clazz = new TT_Driver_Class();
    return driver;
}
/******************************************************************************/
// cfftypes
/******************************************************************************/
function CFF_IndexRec()
{
    this.stream = null;
    this.start = 0;
    this.count = 0;
    this.off_size = 0;
    this.data_offset = 0;
    this.data_size = 0;

    this.offsets = null;
    this.bytes = null;

    this.clear = function()
    {
        this.stream = null;
        this.start = 0;
        this.count = 0;
        this.off_size = 0;
        this.data_offset = 0;
        this.data_size = 0;

        this.offsets = null;
        this.bytes = null;
    }
}

function CFF_EncodingRec()
{
    this.format = 0;
    this.offset = 0;

    this.count = 0;
    this.sids = CreateIntArray(256);
    this.codes = CreateIntArray(256);
}

function CFF_CharsetRec()
{
    this.format = 0;
    this.offset = 0;

    this.sids = null;
    this.cids = null;

    this.max_cid = 0;
    this.num_glyphs = 0;
}

function CFF_FontRecDictRec()
{
    this.version = 0;
    this.notice = 0;
    this.copyright = 0;
    this.full_name = 0;
    this.family_name = 0;
    this.weight = 0;
    this.is_fixed_pitch = 0;
    this.italic_angle = 0;
    this.underline_position = 0;
    this.underline_thickness = 0;
    this.paint_type = 0;
    this.charstring_type = 0;
    this.font_matrix = new FT_Matrix();
    this.has_font_matrix = 0;
    this.units_per_em = 0;
    this.font_offset = new FT_Vector();
    this.unique_id = 0;
    this.font_bbox = new FT_BBox();
    this.stroke_width = 0;
    this.charset_offset = 0;
    this.encoding_offset = 0;
    this.charstrings_offset = 0;
    this.private_offset = 0;
    this.private_size = 0;
    this.synthetic_base = 0;
    this.embedded_postscript = 0;

    this.cid_registry = 0;
    this.cid_ordering = 0;
    this.cid_supplement = 0;

    this.cid_font_version = 0;
    this.cid_font_revision = 0;
    this.cid_font_type = 0;
    this.cid_count = 0;
    this.cid_uid_base = 0;
    this.cid_fd_array_offset = 0;
    this.cid_fd_select_offset = 0;
    this.cid_font_name = 0;

    this.clear = function()
    {
        this.version = 0;
        this.notice = 0;
        this.copyright = 0;
        this.full_name = 0;
        this.family_name = 0;
        this.weight = 0;
        this.is_fixed_pitch = 0;
        this.italic_angle = 0;
        this.underline_position = 0;
        this.underline_thickness = 0;
        this.paint_type = 0;
        this.charstring_type = 0;

        this.font_matrix.xx = 0;
        this.font_matrix.xy = 0;
        this.font_matrix.yx = 0;
        this.font_matrix.yy = 0;

        this.has_font_matrix = 0;
        this.units_per_em = 0;

        this.font_offset.x = 0;
        this.font_offset.y = 0;

        this.unique_id = 0;

        this.font_bbox.xMin = 0;
        this.font_bbox.yMin = 0;
        this.font_bbox.xMax = 0;
        this.font_bbox.yMax = 0;

        this.stroke_width = 0;
        this.charset_offset = 0;
        this.encoding_offset = 0;
        this.charstrings_offset = 0;
        this.private_offset = 0;
        this.private_size = 0;
        this.synthetic_base = 0;
        this.embedded_postscript = 0;

        this.cid_registry = 0;
        this.cid_ordering = 0;
        this.cid_supplement = 0;

        this.cid_font_version = 0;
        this.cid_font_revision = 0;
        this.cid_font_type = 0;
        this.cid_count = 0;
        this.cid_uid_base = 0;
        this.cid_fd_array_offset = 0;
        this.cid_fd_select_offset = 0;
        this.cid_font_name = 0;
    }
}

function CFF_PrivateRec()
{
    this.num_blue_values = 0;
    this.num_other_blues = 0;
    this.num_family_blues = 0;
    this.num_family_other_blues = 0;

    this.blue_values = CreateIntArray(14);
    this.other_blues = CreateIntArray(10);
    this.family_blues = CreateIntArray(14);
    this.family_other_blues = CreateIntArray(10);

    this.blue_scale = 0;
    this.blue_shift = 0;
    this.blue_fuzz = 0;
    this.standard_width = 0;
    this.standard_height = 0;

    this.num_snap_widths = 0;
    this.num_snap_heights = 0;
    this.snap_widths = CreateIntArray(13);
    this.snap_heights = CreateIntArray(13);
    this.force_bold = 0;
    this.force_bold_threshold = 0;
    this.lenIV = 0;
    this.language_group = 0;
    this.expansion_factor = 0;
    this.initial_random_seed = 0;
    this.local_subrs_offset = 0;
    this.default_width = 0;
    this.nominal_width = 0;

    this.clear = function()
    {
        this.num_blue_values = 0;
        this.num_other_blues = 0;
        this.num_family_blues = 0;
        this.num_family_other_blues = 0;

        for (var i = 0; i < 14; i++)
            this.blue_values[i] = 0;
        for (var i = 0; i < 10; i++)
            this.other_blues[i] = 0;
        for (var i = 0; i < 14; i++)
            this.family_blues[i] = 0;
        for (var i = 0; i < 10; i++)
            this.family_other_blues[i] = 0;

        this.blue_scale = 0;
        this.blue_shift = 0;
        this.blue_fuzz = 0;
        this.standard_width = 0;
        this.standard_height = 0;

        this.num_snap_widths = 0;
        this.num_snap_heights = 0;

        for (var i = 0; i < 13; i++)
            this.snap_widths[i] = 0;
        for (var i = 0; i < 13; i++)
            this.snap_heights[i] = 0;

        this.force_bold = 0;
        this.force_bold_threshold = 0;
        this.lenIV = 0;
        this.language_group = 0;
        this.expansion_factor = 0;
        this.initial_random_seed = 0;
        this.local_subrs_offset = 0;
        this.default_width = 0;
        this.nominal_width = 0;
    }
}

function CFF_FDSelectRec()
{
    this.format = 0;
    this.range_count = 0;

    /* that's the table, taken from the file `as is' */
    this.data = null;
    this.data_size = 0;

    /* small cache for format 3 only */
    this.cache_first = 0;
    this.cache_count = 0;
    this.cache_fd = 0;
}

/* A SubFont packs a font dict and a private dict together.  They are */
/* needed to support CID-keyed CFF fonts.                             */
function CFF_SubFontRec()
{
    this.font_dict = new CFF_FontRecDictRec();
    this.private_dict = new CFF_PrivateRec();

    this.local_subrs_index = new CFF_IndexRec();
    this.local_subrs = []; /* array of pointers into Local Subrs INDEX data */
}

function CFF_FontRec()
{
    this.stream = null;
    this.memory = null;
    this.num_faces = 0;
    this.num_glyphs = 0;

    this.version_major = 0;
    this.version_minor = 0;
    this.header_size = 0;
    this.absolute_offsize = 0;


    this.name_index = new CFF_IndexRec();
    this.top_dict_index = new CFF_IndexRec();
    this.global_subrs_index = new CFF_IndexRec();

    this.encoding = new CFF_EncodingRec();
    this.charset = new CFF_CharsetRec();

    this.charstrings_index = new CFF_IndexRec();
    this.font_dict_index = new CFF_IndexRec();
    this.private_index = new CFF_IndexRec();
    this.local_subrs_index = new CFF_IndexRec();

    this.font_name = "";

    /* array of pointers into Global Subrs INDEX data */
    this.global_subrs = [];

    /* array of pointers into String INDEX data stored at string_pool */
    this.num_strings = 0;
    this.strings = null;
    this.string_pool = null;

    this.top_font = new CFF_SubFontRec();
    this.num_subfonts = 0;
    this.subfonts = new Array(256);
    for (var i = 0; i < 256; i++)
        this.subfonts[i] = null;

    this.fd_select = new CFF_FDSelectRec();

    /* interface to PostScript hinter */
    this.pshinter = null;

    /* interface to Postscript Names service */
    this.psnames = null;

    /* since version 2.3.0 */
    this.font_info = null;   /* font info dictionary */

    /* since version 2.3.6 */
    this.registry = "";
    this.ordering = "";
}

function CFF_ParserRec()
{
    this.library = null;
    this.start = null;
    this.limit = 0;
    this.cursor = null;

    this.stack = new Array(96 + 1);
    this.top = 0;

    this.object_code = 0;
    this.object = 0;

    this.clear = function()
    {
        this.library = null;
        this.start = null;
        this.limit = 0;
        this.cursor = null;

        for (var i = 0; i < (96 + 1); i++)
            this.stack[i] = 0;
        this.top = 0;

        this.object_code = 0;
        this.object = 0;
    }
}

function CFF_Field_Handler()
{
    this.kind = 0;
    this.code = 0;
    this.offset = 0;
    this.size = 0;
    this.reader = null;
    this.array_max = 0;
    this.count_offset = 0;

    this.set_field = null;
    this.set_field_count = null;
}

function create_cff_field(_kind, _code, _reader, _array_max, _set_field, _set_field_count)
{
    var ret = new CFF_Field_Handler();
    ret.kind = _kind;
    ret.code = _code;
    ret.offset = 0;
    ret.size = 0;
    ret.reader = _reader;
    ret.array_max = _array_max;
    ret.count_offset = 0;

    ret.set_field = _set_field;
    ret.set_field_count = _set_field_count;

    return ret;
}
/******************************************************************************/
// cffparse
/******************************************************************************/
function cff_parser_init(parser, code, object, library)
{
    parser.clear();
    
    parser.top = 0;
    parser.object_code = code;
    parser.object = object;
    parser.library = library;
}

function cff_parse_integer(start, limit)
{
    var p = dublicate_pointer(start);
    var v   = p.data[p.pos];
    p.pos++;
    var val = 0;

    if (v == 28)
    {
        if (p.pos + 2 > limit)
            return 0;

        val = FT_Common.UShort_To_Short((p.data[p.pos] << 8) | p.data[p.pos + 1]);
        p.pos += 2;
    }
    else if ( v == 29 )
    {
        if (p.pos + 4 > limit)
            return 0;

        val = (p.data[p.pos] << 24) | (p.data[p.pos + 1] << 16) | (p.data[p.pos + 2] << 8) | p.data[p.pos + 3];
        p.pos += 4;
    }
    else if (v < 247)
    {
        val = v - 139;
    }
    else if (v < 251)
    {
        if (p.pos + 1 > limit)
            return 0;

        val = (v - 247) * 256 + p.data[p.pos] + 108;
        p.pos++;
    }
    else
    {
        if (p + 1 > limit)
            return 0;

        val = -(v - 251) * 256 - p.data[p.pos] - 108;
        p.pos++;
    }
    return val;
}

var power_tens = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000];

function cff_parse_real(start, limit, power_ten, is_scaling)
{
    var scaling = 0;
    var result = 0;

    var p = dublicate_pointer(start);
    var nib = 0;
    var phase = 0;

    var sign = 0, exponent_sign = 0;

    var number   = 0;
    var exponent = 0;

    var exponent_add    = 0;
    var integer_length  = 0;
    var fraction_length = 0;

    /* First of all, read the integer part. */
    phase = 4;

    for (;;)
    {
        /* If we entered this iteration with phase == 4, we need to */
        /* read a new byte.  This also skips past the initial 0x1E. */
        if (phase)
        {
            p.pos++;

            /* Make sure we don't read past the end. */
            if (p.pos >= limit)
                return { res : result, scaling: scaling };
        }

        /* Get the nibble. */
        nib = (p.data[p.pos] >> phase) & 0xF;
        phase = 4 - phase;

        if (nib == 0xE)
            sign = 1;
        else if (nib > 9)
            break;
        else
        {
            /* Increase exponent if we can't add the digit. */
            if (number >= 0xCCCCCCC)
                exponent_add++;
            /* Skip leading zeros. */
            else if ( nib || number )
            {
                integer_length++;
                number = number * 10 + nib;
            }
        }
    }

    /* Read fraction part, if any. */
    if (nib == 0xa)
    {
        for (;;)
        {
            /* If we entered this iteration with phase == 4, we need */
            /* to read a new byte.                                   */
            if (phase)
            {
                p.pos++;

                /* Make sure we don't read past the end. */
                if (p.pos >= limit)
                    return { res : result, scaling: scaling };
            }

            /* Get the nibble. */
            nib   = (p.data[p.pos] >> phase) & 0xF;
            phase = 4 - phase;
            if (nib >= 10)
                break;

            /* Skip leading zeros if possible. */
            if (!nib && !number)
                exponent_add--;
            /* Only add digit if we don't overflow. */
            else if (number < 0xCCCCCCC && fraction_length < 9)
            {
                fraction_length++;
                number = number * 10 + nib;
            }
        }
    }

    /* Read exponent, if any. */
    if (nib == 12)
    {
        exponent_sign = 1;
        nib           = 11;
    }

    if (nib == 11)
    {
        for (;;)
        {
            /* If we entered this iteration with phase == 4, */
            /* we need to read a new byte.                   */
            if (phase)
            {
                p.pos++;

                /* Make sure we don't read past the end. */
                if (p.pos >= limit)
                    return { res : result, scaling: scaling };
            }

            /* Get the nibble. */
            nib   = (p.data[p.pos] >> phase) & 0xF;
            phase = 4 - phase;
            if (nib >= 10)
                break;

            exponent = exponent * 10 + nib;

            /* Arbitrarily limit exponent. */
            if (exponent > 1000)
                return { res : result, scaling: scaling };
        }

        if (exponent_sign)
            exponent = -exponent;
    }

    /* We don't check `power_ten' and `exponent_add'. */
    exponent += power_ten + exponent_add;

    if (is_scaling != 0)
    {
        /* Only use `fraction_length'. */
        fraction_length += integer_length;
        exponent        += integer_length;

        if (fraction_length <= 5)
        {
            if (number > 0x7FFF)
            {
                result  = FT_DivFix(number, 10);
                scaling = exponent - fraction_length + 1;
            }
            else
            {
                if (exponent > 0)
                {
                    /* Make `scaling' as small as possible. */
                    var new_fraction_length = Math.min(exponent, 5);
                    exponent -= new_fraction_length;
                    var shift = new_fraction_length - fraction_length;

                    number *= power_tens[shift];
                    if (number > 0x7FFF)
                    {
                        number /= 10;
                        exponent += 1;
                    }
                }
                else
                    exponent -= fraction_length;

                result = number << 16;
                scaling = exponent;
            }
        }
        else
        {
            if ((number / power_tens[fraction_length - 5]) > 0x7FFF)
            {
                result = FT_DivFix(number, power_tens[fraction_length - 4]);
                scaling = exponent - 4;
            }
            else
            {
                result = FT_DivFix(number, power_tens[fraction_length - 5]);
                scaling = exponent - 5;
            }
        }
    }
    else
    {
        integer_length  += exponent;
        fraction_length -= exponent;

        /* Check for overflow and underflow. */
        if (Math.abs(integer_length) > 5)
            return { res : result, scaling: scaling };

        /* Remove non-significant digits. */
        if (integer_length < 0)
        {
            number /= power_tens[-integer_length];
            fraction_length += integer_length;
        }

        /* this can only happen if exponent was non-zero */
        if (fraction_length == 10)
        {
            number /= 10;
            fraction_length -= 1;
        }

        /* Convert into 16.16 format. */
        if (fraction_length > 0)
        {
            if ((number / power_tens[fraction_length]) > 0x7FFF)
                return { res : result, scaling: scaling };

            result = FT_DivFix(number, power_tens[fraction_length]);
        }
        else
        {
            number *= power_tens[-fraction_length];

            if (number > 0x7FFF)
                return { res : result, scaling: scaling };

            result = number << 16;
        }
    }

    if (sign)
        result = -result;

    return { res : result, scaling: scaling };
}

function cff_parse_num(d, start)
{
    var _d0 = d[start];
    var _d1 = d[start + 1];
    if (_d0.data[_d0.pos] == 30)
        return cff_parse_real(_d0, _d1.pos, 0, false).res >> 16;
    return cff_parse_integer(_d0, _d1.pos);
}

function cff_parse_fixed(d, start)
{
    var _d0 = d[start];
    var _d1 = d[start + 1];
    if (_d0.data[_d0.pos] == 30)
        return cff_parse_real(_d0, _d1.pos, 0, false).res;
    return cff_parse_integer(_d0, _d1.pos) << 16;
}

function cff_parse_fixed_scaled(d, start, scaling)
{
    var _d0 = d[start];
    var _d1 = d[start + 1];
    if (_d0.data[_d0.pos] == 30)
        return cff_parse_real(_d0, _d1.pos, scaling, false).res;

    return (cff_parse_integer(_d0, _d1.pos) * power_tens[scaling]) << 16;
}

function cff_parse_fixed_dynamic(d, start)
{
    var _d0 = d[start];
    var _d1 = d[start + 1];
    if (_d0.data[_d0.pos] == 30)
        return cff_parse_real(_d0, _d1.pos, 0, true);

    var integer_length = 0;
    var number = cff_parse_integer(_d0, _d1.pos);

    var scaling = 0;
    if (number > 0x7FFF)
    {
        for (integer_length = 5; integer_length < 10; integer_length++)
            if (number < power_tens[integer_length])
                break;

        if ((number / power_tens[integer_length - 5]) > 0x7FFF)
        {
            scaling = integer_length - 4;
            var result = FT_DivFix(number, power_tens[integer_length - 4]);
            return { res : result, scaling: scaling };
        }
        else
        {
            scaling = integer_length - 5;
            var result = FT_DivFix(number, power_tens[integer_length - 5]);
            return { res : result, scaling: scaling };
        }
    }
    else
    {
        return { res : number << 16, scaling: 0 };
    }
}

function cff_parse_font_matrix(parser)
{
    var dict = parser.object;
    var matrix = dict.font_matrix;
    var offset = dict.font_offset;
    //FT_ULong*        upm    = &dict.units_per_em;
    var data = parser.stack;
    var start = 0;
    var error = 161;

    if (parser.top >= 6)
    {
        var scaling = 0;
        error = 0;

        dict.has_font_matrix = 1;
        /* We expect a well-formed font matrix, this is, the matrix elements */
        /* `xx' and `yy' are of approximately the same magnitude.  To avoid  */
        /* loss of precision, we use the magnitude of element `xx' to scale  */
        /* all other elements.  The scaling factor is then contained in the  */
        /* `units_per_em' value.                                             */

        var ret = cff_parse_fixed_dynamic(data, start++);
        scaling = ret.scaling;
        matrix.xx = ret.res;

        scaling = -scaling;
        if (scaling < 0 || scaling > 9)
        {
            /* Return default matrix in case of unlikely values. */
            matrix.xx = 0x10000;
            matrix.yx = 0;
            matrix.xy = 0;
            matrix.yy = 0x10000;
            offset.x  = 0;
            offset.y  = 0;
            dict.units_per_em = 1;

            return error;
        }

        matrix.yx = cff_parse_fixed_scaled(data, start++, scaling);
        matrix.xy = cff_parse_fixed_scaled(data, start++, scaling);
        matrix.yy = cff_parse_fixed_scaled(data, start++, scaling);
        offset.x  = cff_parse_fixed_scaled(data, start++, scaling);
        offset.y  = cff_parse_fixed_scaled(data, start, scaling);

        dict.units_per_em = power_tens[scaling];
    }

    return error;
}

function cff_parse_font_bbox(parser)
{
    var dict = parser.object;
    var bbox = dict.font_bbox;
    var data = parser.stack;

    if (parser.top >= 4)
    {
        var start = 0;

        bbox.xMin = FT_RoundFix(cff_parse_fixed(data, start++));
        bbox.yMin = FT_RoundFix(cff_parse_fixed(data, start++));
        bbox.xMax = FT_RoundFix(cff_parse_fixed(data, start++));
        bbox.yMax = FT_RoundFix(cff_parse_fixed(data, start));

        return 0;
    }
    return 161;
}

function cff_parse_private_dict(parser)
{
    var dict = parser.object;
    var data = parser.stack;

    if (parser.top >= 2)
    {
        dict.private_size   = cff_parse_num(data, 0);
        dict.private_offset = cff_parse_num(data, 1);

        return 0;
    }
    return 161;
}

function cff_parse_cid_ros(parser)
{
    var dict = parser.object;
    var data = parser.stack;

    if (parser.top >= 3)
    {
        dict.cid_registry = FT_Common.IntToUInt(cff_parse_num(data, 0));
        dict.cid_ordering = FT_Common.IntToUInt(cff_parse_num(data, 1));

        dict.cid_supplement = cff_parse_num(data, 2);
        return 0;
    }
    return 161;
}

function cff_parser_run(parser, start, limit)
{
    var p = dublicate_pointer(start);
    var error = 0;

    parser.top    = 0;
    parser.start  = start;
    parser.limit  = limit;
    parser.cursor = dublicate_pointer(start);

    var tops = parser.stack;
    while (p.pos < limit)
    {
        var v = p.data[p.pos];

        if (v >= 27 && v != 31)
        {
            /* it's a number; we will push its position on the stack */
            if (parser.top >= 96)
                return 6;

            tops[parser.top++] = dublicate_pointer(p);

            /* now, skip it */
            if (v == 30)
            {
                /* skip real number */
                p.pos++;
                for (;;)
                {
                    /* An unterminated floating point number at the */
                    /* end of a dictionary is invalid but harmless. */
                    if (p.pos >= limit)
                        return error;
                    v = p.data[p.pos] >> 4;
                    if (v == 15)
                        break;
                    v = p.data[p.pos] & 0xF;
                    if (v == 15)
                        break;
                    p.pos++;
                }
            }
            else if (v == 28)
                p.pos += 2;
            else if (v == 29)
                p.pos += 4;
            else if (v > 246)
                p.pos += 1;
        }
        else
        {
            /* This is not a number, hence it's an operator.  Compute its code */
            /* and look for it in our current list.                            */

            var num_args = parser.top;
            var code = v;

            tops[parser.top++] = dublicate_pointer(p);
            if (v == 12)
            {
                /* two byte operator */
                p.pos++;
                if (p.pos >= limit)
                    return 6;

                code = 0x100 | p.data[p.pos];
            }
            code = code | parser.object_code;

            var fields = cff_field_handlers;
            for (var field_ = 0; field_ < fields.length; field_++)
            {
                var field = fields[field_];
                if (field.code == FT_Common.UintToInt(code))
                {
                    /* we found our field's handler; read it */
                    var val;

                    /* check that we have enough arguments -- except for */
                    /* delta encoded arrays, which can be empty          */
                    if (field.kind != 6 && num_args < 1)
                        return 6;

                    switch (field.kind)
                    {
                        case 5:
                        case 4:
                        case 1:
                            val = cff_parse_num(parser.stack, 0);
                            fire_t1_field(parser.object, val, field);
                            break;

                        case 2:
                            val = cff_parse_fixed(parser.stack, 0);
                            fire_t1_field(parser.object, val, field);
                            break;

                        case 3:
                            val = cff_parse_fixed_scaled(parser.stack, 0, 3);
                            fire_t1_field(parser.object, val, field);
                            break;

                        case 6:
                            var data = parser.stack;
                            var _data_pos = 0;

                            if (num_args > field.array_max)
                                num_args = field.array_max;

                            fire_t1_field_count(parser.object, num_args, field);
                            val = 0;
                            field.offset = 0;
                            while (num_args > 0)
                            {
                                val += cff_parse_num(data, _data_pos++);
                                fire_t1_field(parser.object, val, field);
                                field.offset++;
                                num_args--;
                            }

                            break;

                        default:  /* callback */
                            error = field.reader(parser);
                            if ( error )
                                return error;
                    }

                    break;
                }
            }

            /* this is an unknown operator, or it is unsupported; */
            /* we will ignore it for now.                         */
            /* clear stack */
            parser.top = 0;
        }
        p.pos++;
    }

    return error;
}

/******************************************************************************/
// cfftoken
/******************************************************************************/
var cff_field_handlers = new Array(51);

// CFF_FontRecDictRec
cff_field_handlers[0] = create_cff_field(4, 0 | 4096, null, 0, function(obj, val, f) { obj.version = val }, undefined);
cff_field_handlers[1] = create_cff_field(4, 1 | 4096, null, 0, function(obj, val, f) { obj.notice = val }, undefined);
cff_field_handlers[2] = create_cff_field(4, 0x100 | 4096, null, 0, function(obj, val, f) { obj.copyright = val }, undefined);
cff_field_handlers[3] = create_cff_field(4, 2 | 4096, null, 0, function(obj, val, f) { obj.full_name = val }, undefined);
cff_field_handlers[4] = create_cff_field(4, 3 | 4096, null, 0, function(obj, val, f) { obj.family_name = val }, undefined);
cff_field_handlers[5] = create_cff_field(4, 4 | 4096, null, 0, function(obj, val, f) { obj.weight = val }, undefined);

cff_field_handlers[6] = create_cff_field(5, 0x101 | 4096, null, 0, function(obj, val, f) { obj.is_fixed_pitch = val }, undefined);

cff_field_handlers[7] = create_cff_field(2, 0x102 | 4096, null, 0, function(obj, val, f) { obj.italic_angle = val }, undefined);
cff_field_handlers[8] = create_cff_field(2, 0x103 | 4096, null, 0, function(obj, val, f) { obj.underline_position = val }, undefined);
cff_field_handlers[9] = create_cff_field(2, 0x104 | 4096, null, 0, function(obj, val, f) { obj.underline_thickness = val }, undefined);

cff_field_handlers[10] = create_cff_field(1, 0x105 | 4096, null, 0, function(obj, val, f) { obj.paint_type = val }, undefined);
cff_field_handlers[11] = create_cff_field(1, 0x106 | 4096, null, 0, function(obj, val, f) { obj.charstring_type = val }, undefined);

cff_field_handlers[12] = create_cff_field(7, 0x107 | 4096, cff_parse_font_matrix, 0, undefined, undefined);

cff_field_handlers[13] = create_cff_field(1, 13 | 4096, null, 0, function(obj, val, f) { obj.unique_id = val }, undefined);

cff_field_handlers[14] = create_cff_field(7, 5 | 4096, cff_parse_font_bbox, 0, undefined, undefined);

cff_field_handlers[15] = create_cff_field(1, 0x108 | 4096, null, 0, function(obj, val, f) { obj.stroke_width = val }, undefined);
cff_field_handlers[16] = create_cff_field(1, 15 | 4096, null, 0, function(obj, val, f) { obj.charset_offset = val }, undefined);
cff_field_handlers[17] = create_cff_field(1, 16 | 4096, null, 0, function(obj, val, f) { obj.encoding_offset = val }, undefined);
cff_field_handlers[18] = create_cff_field(1, 17 | 4096, null, 0, function(obj, val, f) { obj.charstrings_offset = val }, undefined);

cff_field_handlers[19] = create_cff_field(7, 18 | 4096, cff_parse_private_dict, 0, undefined, undefined);

cff_field_handlers[20] = create_cff_field(1, 0x114 | 4096, null, 0, function(obj, val, f) { obj.synthetic_base = val }, undefined);

cff_field_handlers[21] = create_cff_field(4, 0x115 | 4096, null, 0, function(obj, val, f) { obj.embedded_postscript = val }, undefined);

cff_field_handlers[22] = create_cff_field(7, 0x11E | 4096, cff_parse_cid_ros, 0, undefined, undefined);

cff_field_handlers[23] = create_cff_field(1, 0x11F | 4096, null, 0, function(obj, val, f) { obj.cid_font_version = val }, undefined);
cff_field_handlers[24] = create_cff_field(1, 0x120 | 4096, null, 0, function(obj, val, f) { obj.cid_font_revision = val }, undefined);
cff_field_handlers[25] = create_cff_field(1, 0x121 | 4096, null, 0, function(obj, val, f) { obj.cid_font_type = val }, undefined);
cff_field_handlers[26] = create_cff_field(1, 0x122 | 4096, null, 0, function(obj, val, f) { obj.cid_count = val }, undefined);
cff_field_handlers[27] = create_cff_field(1, 0x123 | 4096, null, 0, function(obj, val, f) { obj.cid_uid_base = val }, undefined);
cff_field_handlers[28] = create_cff_field(1, 0x124 | 4096, null, 0, function(obj, val, f) { obj.cid_fd_array_offset = val }, undefined);
cff_field_handlers[29] = create_cff_field(1, 0x125 | 4096, null, 0, function(obj, val, f) { obj.cid_fd_select_offset = val }, undefined);

cff_field_handlers[30] = create_cff_field(4, 0x126 | 4096, null, 0, function(obj, val, f) { obj.cid_font_name = val }, undefined);

// CFF_PrivateRec
cff_field_handlers[31] = create_cff_field(6, 6 | 8192, null, 14, function(obj, val, f) { obj.blue_values[f.offset] = val }, function(obj, val, f) { obj.num_blue_values = val });
cff_field_handlers[32] = create_cff_field(6, 7 | 8192, null, 10, function(obj, val, f) { obj.other_blues[f.offset] = val }, function(obj, val, f) { obj.num_other_blues = val });
cff_field_handlers[33] = create_cff_field(6, 8 | 8192, null, 14, function(obj, val, f) { obj.family_blues[f.offset] = val }, function(obj, val, f) { obj.num_family_blues = val });
cff_field_handlers[34] = create_cff_field(6, 9 | 8192, null, 10, function(obj, val, f) { obj.family_other_blues[f.offset] = val }, function(obj, val, f) { obj.num_family_other_blues = val });

cff_field_handlers[35] = create_cff_field(3, 0x109 | 8192, null, 0, function(obj, val, f) { obj.blue_scale = val }, undefined);

cff_field_handlers[36] = create_cff_field(1, 0x10A | 8192, null, 0, function(obj, val, f) { obj.blue_shift = val }, undefined);
cff_field_handlers[37] = create_cff_field(1, 0x10B | 8192, null, 0, function(obj, val, f) { obj.blue_fuzz = val }, undefined);
cff_field_handlers[38] = create_cff_field(1, 10 | 8192, null, 0, function(obj, val, f) { obj.standard_width = val }, undefined);
cff_field_handlers[39] = create_cff_field(1, 11 | 8192, null, 0, function(obj, val, f) { obj.standard_height = val }, undefined);

cff_field_handlers[40] = create_cff_field(6, 0x10C | 8192, null, 13, function(obj, val, f) { obj.snap_widths[f.offset] = val }, function(obj, val, f) { obj.num_snap_widths = val });
cff_field_handlers[41] = create_cff_field(6, 0x10D | 8192, null, 13, function(obj, val, f) { obj.snap_heights[f.offset] = val }, function(obj, val, f) { obj.num_snap_heights = val });

cff_field_handlers[42] = create_cff_field(5, 0x10E | 8192, null, 0, function(obj, val, f) { obj.force_bold = val }, undefined);
cff_field_handlers[43] = create_cff_field(2, 0x10F | 8192, null, 0, function(obj, val, f) { obj.force_bold_threshold = val }, undefined);

cff_field_handlers[44] = create_cff_field(1, 0x110 | 8192, null, 0, function(obj, val, f) { obj.lenIV = val }, undefined);
cff_field_handlers[45] = create_cff_field(1, 0x111 | 8192, null, 0, function(obj, val, f) { obj.language_group = val }, undefined);

cff_field_handlers[46] = create_cff_field(2, 0x112 | 8192, null, 0, function(obj, val, f) { obj.expansion_factor = val }, undefined);

cff_field_handlers[47] = create_cff_field(1, 0x113 | 8192, null, 0, function(obj, val, f) { obj.initial_random_seed = val }, undefined);
cff_field_handlers[48] = create_cff_field(1, 19 | 8192, null, 0, function(obj, val, f) { obj.local_subrs_offset = val }, undefined);
cff_field_handlers[49] = create_cff_field(1, 20 | 8192, null, 0, function(obj, val, f) { obj.default_width = val }, undefined);
cff_field_handlers[50] = create_cff_field(1, 21 | 8192, null, 0, function(obj, val, f) { obj.nominal_width = val }, undefined);

/******************************************************************************/
// cffload
/******************************************************************************/
var cff_isoadobe_charset =
[
    0,   1,   2,   3,   4,   5,   6,   7,
    8,   9,  10,  11,  12,  13,  14,  15,
    16,  17,  18,  19,  20,  21,  22,  23,
    24,  25,  26,  27,  28,  29,  30,  31,
    32,  33,  34,  35,  36,  37,  38,  39,
    40,  41,  42,  43,  44,  45,  46,  47,
    48,  49,  50,  51,  52,  53,  54,  55,
    56,  57,  58,  59,  60,  61,  62,  63,
    64,  65,  66,  67,  68,  69,  70,  71,
    72,  73,  74,  75,  76,  77,  78,  79,
    80,  81,  82,  83,  84,  85,  86,  87,
    88,  89,  90,  91,  92,  93,  94,  95,
    96,  97,  98,  99, 100, 101, 102, 103,
    104, 105, 106, 107, 108, 109, 110, 111,
    112, 113, 114, 115, 116, 117, 118, 119,
    120, 121, 122, 123, 124, 125, 126, 127,
    128, 129, 130, 131, 132, 133, 134, 135,
    136, 137, 138, 139, 140, 141, 142, 143,
    144, 145, 146, 147, 148, 149, 150, 151,
    152, 153, 154, 155, 156, 157, 158, 159,
    160, 161, 162, 163, 164, 165, 166, 167,
    168, 169, 170, 171, 172, 173, 174, 175,
    176, 177, 178, 179, 180, 181, 182, 183,
    184, 185, 186, 187, 188, 189, 190, 191,
    192, 193, 194, 195, 196, 197, 198, 199,
    200, 201, 202, 203, 204, 205, 206, 207,
    208, 209, 210, 211, 212, 213, 214, 215,
    216, 217, 218, 219, 220, 221, 222, 223,
    224, 225, 226, 227, 228
];

var cff_expert_charset =
[
    0,   1, 229, 230, 231, 232, 233, 234,
    235, 236, 237, 238,  13,  14,  15,  99,
    239, 240, 241, 242, 243, 244, 245, 246,
    247, 248,  27,  28, 249, 250, 251, 252,
    253, 254, 255, 256, 257, 258, 259, 260,
    261, 262, 263, 264, 265, 266, 109, 110,
    267, 268, 269, 270, 271, 272, 273, 274,
    275, 276, 277, 278, 279, 280, 281, 282,
    283, 284, 285, 286, 287, 288, 289, 290,
    291, 292, 293, 294, 295, 296, 297, 298,
    299, 300, 301, 302, 303, 304, 305, 306,
    307, 308, 309, 310, 311, 312, 313, 314,
    315, 316, 317, 318, 158, 155, 163, 319,
    320, 321, 322, 323, 324, 325, 326, 150,
    164, 169, 327, 328, 329, 330, 331, 332,
    333, 334, 335, 336, 337, 338, 339, 340,
    341, 342, 343, 344, 345, 346, 347, 348,
    349, 350, 351, 352, 353, 354, 355, 356,
    357, 358, 359, 360, 361, 362, 363, 364,
    365, 366, 367, 368, 369, 370, 371, 372,
    373, 374, 375, 376, 377, 378
];

var cff_expertsubset_charset =
[
    0,   1, 231, 232, 235, 236, 237, 238,
    13,  14,  15,  99, 239, 240, 241, 242,
    243, 244, 245, 246, 247, 248,  27,  28,
    249, 250, 251, 253, 254, 255, 256, 257,
    258, 259, 260, 261, 262, 263, 264, 265,
    266, 109, 110, 267, 268, 269, 270, 272,
    300, 301, 302, 305, 314, 315, 158, 155,
    163, 320, 321, 322, 323, 324, 325, 326,
    150, 164, 169, 327, 328, 329, 330, 331,
    332, 333, 334, 335, 336, 337, 338, 339,
    340, 341, 342, 343, 344, 345, 346
];

var cff_standard_encoding =
[
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    1,   2,   3,   4,   5,   6,   7,   8,
    9,  10,  11,  12,  13,  14,  15,  16,
    17,  18,  19,  20,  21,  22,  23,  24,
    25,  26,  27,  28,  29,  30,  31,  32,
    33,  34,  35,  36,  37,  38,  39,  40,
    41,  42,  43,  44,  45,  46,  47,  48,
    49,  50,  51,  52,  53,  54,  55,  56,
    57,  58,  59,  60,  61,  62,  63,  64,
    65,  66,  67,  68,  69,  70,  71,  72,
    73,  74,  75,  76,  77,  78,  79,  80,
    81,  82,  83,  84,  85,  86,  87,  88,
    89,  90,  91,  92,  93,  94,  95,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,  96,  97,  98,  99, 100, 101, 102,
    103, 104, 105, 106, 107, 108, 109, 110,
    0, 111, 112, 113, 114,   0, 115, 116,
    117, 118, 119, 120, 121, 122,   0, 123,
    0, 124, 125, 126, 127, 128, 129, 130,
    131,   0, 132, 133,   0, 134, 135, 136,
    137,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0, 138,   0, 139,   0,   0,   0,   0,
    140, 141, 142, 143,   0,   0,   0,   0,
    0, 144,   0,   0,   0, 145,   0,   0,
    146, 147, 148, 149,   0,   0,   0,   0
];

var cff_expert_encoding =
[
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    1, 229, 230,   0, 231, 232, 233, 234,
    235, 236, 237, 238,  13,  14,  15,  99,
    239, 240, 241, 242, 243, 244, 245, 246,
    247, 248,  27,  28, 249, 250, 251, 252,
    0, 253, 254, 255, 256, 257,   0,   0,
    0, 258,   0,   0, 259, 260, 261, 262,
    0,   0, 263, 264, 265,   0, 266, 109,
    110, 267, 268, 269,   0, 270, 271, 272,
    273, 274, 275, 276, 277, 278, 279, 280,
    281, 282, 283, 284, 285, 286, 287, 288,
    289, 290, 291, 292, 293, 294, 295, 296,
    297, 298, 299, 300, 301, 302, 303,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,
    0, 304, 305, 306,   0,   0, 307, 308,
    309, 310, 311,   0, 312,   0,   0, 312,
    0,   0, 314, 315,   0,   0, 316, 317,
    318,   0,   0,   0, 158, 155, 163, 319,
    320, 321, 322, 323, 324, 325,   0,   0,
    326, 150, 164, 169, 327, 328, 329, 330,
    331, 332, 333, 334, 335, 336, 337, 338,
    339, 340, 341, 342, 343, 344, 345, 346,
    347, 348, 349, 350, 351, 352, 353, 354,
    355, 356, 357, 358, 359, 360, 361, 362,
    363, 364, 365, 366, 367, 368, 369, 370,
    371, 372, 373, 374, 375, 376, 377, 378
];

function cff_get_standard_encoding(charcode)
{
    return (charcode < 256 ? cff_standard_encoding[charcode] : 0);
}

function cff_index_read_offset(idx)
{
    var result = 0;
    var tmp = g_memory.Alloc(4);
    var error = idx.stream.Read(tmp, idx.off_size);

    if (error == 0)
    {
        for (var nn = 0; nn < idx.off_size; nn++)
            result = (result << 8) | tmp.data[nn];

        result = FT_Common.IntToUInt(result);
    }

    return { err : error, result: result };
}

function cff_index_init(idx, stream, load)
{
    idx.clear();
    idx.stream = stream;
    idx.start  = stream.pos;

    var count = stream.ReadUShort();
    var error = FT_Error;
    if (error != 0)
        return error;

    if (0 == count)
        return error;

    var offsize = stream.ReadUChar();
    if (error != 0)
        return error;

    if (offsize < 1 || offsize > 4)
        return 8;

    idx.count    = count;
    idx.off_size = offsize;
    var size = (count + 1) * offsize;

    idx.data_offset = idx.start + 3 + size;

    error = stream.Skip(size - offsize);
    if (error != 0)
        return error;

    var _err = cff_index_read_offset(idx);
    error = _err.err;
    size = _err.result;
    if (error != 0)
        return error;

    if (size == 0)
        return 8;

    idx.data_size = --size;

    if (load)
    {
        /* load the data */
        idx.bytes = new CPointer();
        error = stream.ExtractFrame(size, idx.bytes);
    }
    else
    {
        /* skip the data */
        error = stream.Skip(size);
    }

    return error;
}

function cff_index_done(idx)
{
    if (idx.stream != null)
    {
        idx.clear();
    }
}

function cff_index_load_offsets(idx)
{
    var error = 0;
    var stream = idx.stream;

    if (idx.count > 0 && idx.offsets == null)
    {
        var offsize = idx.off_size;
        var data_size = (idx.count + 1) * offsize;

        error = stream.Seek(idx.start + 3);
        if (error == 0)
            error = stream.EnterFrame(data_size);

        if (error != 0)
            return error;

        idx.offsets = CreateUIntArray(idx.count + 1);

        var poff = 0;
        var p = stream.cur;
        var p_end  = p + data_size;

        var _s = stream.data;
        var _d = idx.offsets;

        switch (offsize)
        {
            case 1:
                for (; p < p_end; p++, poff++)
                    _d[poff] = _s[p];
                break;

            case 2:
                for (; p < p_end; p += 2, poff++)
                    _d[poff] = (_s[p] << 8 | _s[p+1]);
                break;

            case 3:
                for (; p < p_end; p += 3, poff++)
                    _d[poff] = (_s[p] << 16 | _s[p+1] << 8 | _s[p+2]);
                break;

            default:
                for (; p < p_end; p += 4, poff++)
                {
                    var r = (_s[p] << 24 | _s[p+1] << 16 | _s[p+2] << 8 | _s[p+3]);
                    if (r<0)
                        r+=4294967296;
                    _d[poff] = r;
                }
        }

        stream.ExitFrame();
    }

    return error;
}

function cff_index_get_pointers(idx, table, pool, is_pool)
{
    var error = 0;

    if (idx.offsets == null)
    {
        error = cff_index_load_offsets(idx);
        if (error != 0)
            return { err: error, table: table, pool: pool };
    }

    if (idx.count > 0)
    {
        var t = new Array(idx.count + 1);
        var new_bytes = g_memory.Alloc(idx.data_size + idx.count);

        var extra = 0;
        var org_bytes = idx.bytes;

        /* at this point, `idx->offsets' can't be NULL */
        var cur_offset = idx.offsets[0] - 1;

        /* sanity check */
        if (cur_offset >= idx.data_size)
        {
            cur_offset = 0;
        }

        if (is_pool == 0)
        {
            t[0] = dublicate_pointer(org_bytes);
            t[0].pos += cur_offset;
        }
        else
        {
            t[0] = dublicate_pointer(new_bytes);
            t[0].pos += cur_offset;
        }

        for (var n = 1; n <= idx.count; n++)
        {
            var next_offset = idx.offsets[n] - 1;

            /* empty slot + two sanity checks for invalid offset tables */
            if (next_offset == 0 || next_offset < cur_offset || (next_offset >= idx.data_size && n < idx.count))
                next_offset = cur_offset;

            if (is_pool == 0)
            {
                t[n] = dublicate_pointer(org_bytes);
                t[n].pos += next_offset;
            }
            else
            {
                t[n] = dublicate_pointer(new_bytes);
                t[n].pos += next_offset + extra;

                if (next_offset != cur_offset)
                {
                    var _d = t[n - 1];
                    var _s = dublicate_pointer(org_bytes);
                    _s.pos += cur_offset;
                    var _l = t[n].pos - t[n-1].pos;

                    for (var i = 0; i < _l; i++)
                        _d.data[_d.pos + i] = _s.data[_s.pos + i];

                    t[n].data[t[n].pos] = 0;
                    t[n].pos += 1;
                    extra++;
                }
            }

            cur_offset = next_offset;
        }

        return { err: error, table: t, pool: new_bytes };
    }

    return { err: error, table: table, pool: pool };
}

function cff_index_access_element(idx, element)
{
    var error = 0;
    var pbytes = null;
    var pbyte_len = 0;

    if (idx && idx.count > element)
    {
        /* compute start and end offsets */
        var stream = idx.stream;
        var off1, off2 = 0;


        /* load offsets from file or the offset table */
        if (idx.offsets == null)
        {
            var pos = element * idx.off_size;

            error = stream.Seek(idx.start + 3 + pos);
            if (error != 0)
                return { err: error, bytes: pbytes, len: pbyte_len };

            var ret = cff_index_read_offset(idx);
            error = ret.err;
            off1 = ret.result;
            if (error != 0)
                return { err: error, bytes: pbytes, len: pbyte_len };

            if (off1 != 0)
            {
                do
                {
                    element++;
                    ret = cff_index_read_offset(idx);
                    error = ret.err;
                    off2 = ret.result;
                }
                while (off2 == 0 && element < idx.count);
            }
        }
        else   /* use offsets table */
        {
            off1 = idx.offsets[element];
            if (off1)
            {
                do
                {
                    element++;
                    off2 = idx.offsets[element];

                } while (off2 == 0 && element < idx.count);
            }
        }

        /* XXX: should check off2 does not exceed the end of this entry; */
        /*      at present, only truncate off2 at the end of this stream */
        if (off2 > stream.size + 1 || idx.data_offset > stream.size - off2 + 1)
        {
            off2 = stream.size - idx.data_offset + 1;
        }

        /* access element */
        if (off1 && off2 > off1)
        {
            pbyte_len = off2 - off1;

            if (idx.bytes != null)
            {
                /* this index was completely loaded in memory, that's easy */
                pbytes = dublicate_pointer(idx.bytes);
                pbytes.pos += (off1 - 1);
            }
            else
            {
                /* this index is still on disk/file, access it through a frame */
                error = stream.Seek(idx.data_offset + off1 - 1);
                if (error == 0)
                {
                    pbytes = new CPointer();
                    error = stream.ExtractFrame(off2 - off1, pbytes);
                }
            }
        }
        else
        {
            /* empty index element */
            pbytes = null;
            pbyte_len = 0;
        }
    }
    else
        error = 6;

    return { err: error, bytes: pbytes, len: pbyte_len };
}

function cff_index_forget_element(idx, pbytes)
{
    if (idx.bytes == null)
    {
        idx.stream.ReleaseFrame();
        pbytes = null;
    }
}

function cff_index_get_name(font, element)
{
    var idx = font.name_index;
    var ret = cff_index_access_element(idx, element);
    if (0 != ret.err)
        return null;

    var name = "";
    var _len = ret.len;
    var _d = ret.bytes;
    for (var i = 0; i < _len; i++)
        name += String.fromCharCode(_d.data[_d.pos + i]);

    cff_index_forget_element(idx, ret.bytes);
    return name;
}

function cff_index_get_string(font, element)
{
    return (element < font.num_strings) ? FT_PEEK_String1(font.strings[element], 1000) : null;
}

function cff_index_get_sid_string(font, sid)
{
    /* value 0xFFFFU indicates a missing dictionary entry */
    if (sid == 0xFFFF)
        return null;

    /* if it is not a standard string, return it */
    if (sid > 390)
        return cff_index_get_string(font, sid - 391);

    /* CID-keyed CFF fonts don't have glyph names */
    if (font.psnames == null)
        return null;

    /* this is a standard string */
    return font.psnames.adobe_std_strings(sid);
}

function CFF_Done_FD_Select(fdselect, stream)
{
    if (fdselect.data)
    {
        stream.ReleaseFrame();
        fdselect.data = null;
    }

    fdselect.data_size   = 0;
    fdselect.format      = 0;
    fdselect.range_count = 0;
}

function CFF_Load_FD_Select(fdselect, num_glyphs, stream, offset)
{
    var error = stream.Seek(offset);
    if (error != 0)
        return error;

    var format = stream.ReadUChar();
    error = FT_Error;
    if (error != 0)
        return error;

    fdselect.format      = format;
    fdselect.cache_count = 0;   /* clear cache */

    switch (format)
    {
        case 0:     /* format 0, that's simple */
            fdselect.data_size = num_glyphs;
            fdselect.data = new CPointer();
            error = stream.ExtractFrame(fdselect.data_size, fdselect.data);
            break;
        case 3:     /* format 3, a tad more complex */
            var num_ranges = stream.ReadUShort();
            error = FT_Error;
            if (error != 0)
                return error;

            fdselect.data_size = num_ranges * 3 + 2;
            fdselect.data = new CPointer();
            error = stream.ExtractFrame(fdselect.data_size, fdselect.data);
            break;

        default:    /* hmm... that's wrong */
            error = 3;
    }

    return error;
}

function cff_fd_select_get(fdselect, glyph_index)
{
    var fd = 0;
    switch (fdselect.format)
    {
        case 0:
            fd = fdselect.data.data[fdselect.data.pos + glyph_index];
            break;

        case 3:
            /* first, compare to cache */
            if ((glyph_index - fdselect.cache_first) < fdselect.cache_count)
            {
                fd = fdselect.cache_fd;
                break;
            }

            /* then, lookup the ranges array */
            var p = dublicate_pointer(fdselect.data);
            var p_limit = p.pos + fdselect.data_size;

            var first = FT_NEXT_USHORT(p);
            do
            {
                if (glyph_index < first)
                    break;

                var fd2 = p.data[p.pos];
                p.pos++;
                var limit = FT_NEXT_USHORT(p);

                if (glyph_index < limit)
                {
                    fd = fd2;

                    /* update cache */
                    fdselect.cache_first = first;
                    fdselect.cache_count = limit-first;
                    fdselect.cache_fd    = fd2;
                    break;
                }
                first = limit;

            } while ( p < p_limit );
            break;

        default:
            break;
    }

    return fd;
}

function cff_charset_compute_cids(charset, num_glyphs, memory)
{
    var error = 0;
    var max_cid = 0;

    if (charset.max_cid > 0)
        return error;

    for (var i = 0; i < num_glyphs; i++)
    {
        if (charset.sids[i] > max_cid)
            max_cid = charset.sids[i];
    }

    charset.cids = CreateUIntArray(max_cid + 1);

    /* When multiple GIDs map to the same CID, we choose the lowest */
    /* GID.  This is not described in any spec, but it matches the  */
    /* behaviour of recent Acroread versions.                       */
    for (var j = num_glyphs - 1; j >= 0 ; j--)
        charset.cids[charset.sids[j]] = j;

    charset.max_cid    = max_cid;
    charset.num_glyphs = num_glyphs;

    return error;
}

function cff_charset_cid_to_gindex(charset, cid)
{
    if (cid <= charset.max_cid)
        return charset.cids[cid];
    return 0;
}

function cff_charset_free_cids(charset, memory)
{
    charset.cids = null;
    charset.max_cid = 0;
}

function cff_charset_done(charset, stream)
{
    cff_charset_free_cids(charset, stream.memory);

    charset.sids = null;
    charset.format = 0;
    charset.offset = 0;
}

function cff_charset_load(charset, num_glyphs, stream, base_offset, offset, invert)
{
    var error = 0;
    var glyph_sid = 0;

    /* If the the offset is greater than 2, we have to parse the */
    /* charset table.                                            */
    if (offset > 2)
    {
        charset.offset = base_offset + offset;

        error = stream.Seek(charset.offset);
        if (error != 0)
        {
            charset.sids = null;
            charset.cids = null;
            charset.format = 0;
            charset.offset = 0;
            return error;
        }

        charset.format = stream.ReadUChar();
        error = FT_Error;
        if (error != 0)
        {
            charset.sids = null;
            charset.cids = null;
            charset.format = 0;
            charset.offset = 0;
            return error;
        }

        charset.sids = CreateUIntArray(num_glyphs);

        /* assign the .notdef glyph */
        charset.sids[0] = 0;

        switch (charset.format)
        {
            case 0:
                if (num_glyphs > 0)
                {
                    error = stream.EnterFrame((num_glyphs - 1) * 2);
                    if (error != 0)
                    {
                        charset.sids = null;
                        charset.cids = null;
                        charset.format = 0;
                        charset.offset = 0;
                        return error;
                    }

                    for (j = 1; j < num_glyphs; j++)
                        charset.sids[j] = stream.GetUShort();

                    stream.ExitFrame();
                }
                break;

            case 1:
            case 2:
                var nleft = 0;
                var j = 1;
                while (j < num_glyphs)
                {
                    glyph_sid = stream.ReadUShort();
                    error = FT_Error;
                    if (error != 0)
                    {
                        charset.sids = null;
                        charset.cids = null;
                        charset.format = 0;
                        charset.offset = 0;
                        return error;
                    }

                    if (charset.format == 2)
                    {
                        nleft = stream.ReadUShort();
                        error = FT_Error;
                        if (error != 0)
                        {
                            charset.sids = null;
                            charset.cids = null;
                            charset.format = 0;
                            charset.offset = 0;
                            return error;
                        }
                    }
                    else
                    {
                        nleft = stream.ReadUChar();
                        error = FT_Error;
                        if (error != 0)
                        {
                            charset.sids = null;
                            charset.cids = null;
                            charset.format = 0;
                            charset.offset = 0;
                            return error;
                        }
                    }

                    /* try to rescue some of the SIDs if `nleft' is too large */
                    if (glyph_sid > (0xFFFF - nleft))
                    {
                        nleft = (0xFFFF - glyph_sid);
                    }

                    /* Fill in the range of sids -- `nleft + 1' glyphs. */
                    for (i = 0; j < num_glyphs && i <= nleft; i++, j++, glyph_sid++)
                        charset.sids[j] = glyph_sid;
                }
                break;

            default:
                charset.sids = null;
                charset.cids = null;
                charset.format = 0;
                charset.offset = 0;
                return 3;
        }
    }
    else
    {
        /* Parse default tables corresponding to offset == 0, 1, or 2.  */
        /* CFF specification intimates the following:                   */
        /*                                                              */
        /* In order to use a predefined charset, the following must be  */
        /* true: The charset constructed for the glyphs in the font's   */
        /* charstrings dictionary must match the predefined charset in  */
        /* the first num_glyphs.                                        */
        charset.offset = offset;  /* record charset type */

        switch (offset)
        {
        case 0:
            if (num_glyphs > 229)
            {
                charset.sids = null;
                charset.cids = null;
                charset.format = 0;
                charset.offset = 0;
                return 3;
            }

            charset.sids = CreateUIntArray(num_glyphs);
            for (var k = 0; k < num_glyphs; k++)
                charset.sids[k] = cff_isoadobe_charset[k];

            break;

        case 1:
            if (num_glyphs > 166)
            {
                charset.sids = null;
                charset.cids = null;
                charset.format = 0;
                charset.offset = 0;
                return 3;
            }

            charset.sids = CreateUIntArray(num_glyphs);
            for (var k = 0; k < num_glyphs; k++)
                charset.sids[k] = cff_expert_charset[k];

            break;

        case 2:
            if (num_glyphs > 87)
            {
                charset.sids = null;
                charset.cids = null;
                charset.format = 0;
                charset.offset = 0;
                return 3;
            }

            charset.sids = CreateUIntArray(num_glyphs);
            for (var k = 0; k < num_glyphs; k++)
                charset.sids[k] = cff_expertsubset_charset[k];

            break;

        default:
            charset.sids = null;
            charset.cids = null;
            charset.format = 0;
            charset.offset = 0;
            return 3;
        }
    }

    /* we have to invert the `sids' array for subsetted CID-keyed fonts */
    if (invert)
        error = cff_charset_compute_cids(charset, num_glyphs, stream.memory);

    if (error != 0)
    {
        charset.sids = null;
        charset.cids = null;
        charset.format = 0;
        charset.offset = 0;
    }
    return error;
}

function cff_encoding_done(encoding)
{
    encoding.format = 0;
    encoding.offset = 0;
    encoding.count  = 0;
}

function cff_encoding_load(encoding, charset, num_glyphs, stream, base_offset, offset)
{
    var error = 0;
    var count = 0;
    var j = 0;
    var glyph_sid = 0;
    var glyph_code = 0;


    /* Check for charset->sids.  If we do not have this, we fail. */
    if (charset.sids == null)
        return 3;

    /* Zero out the code to gid/sid mappings. */
    for (j = 0; j < 256; j++)
    {
        encoding.sids[j] = 0;
        encoding.codes[j] = 0;
    }

    /* Note: The encoding table in a CFF font is indexed by glyph index;  */
    /* the first encoded glyph index is 1.  Hence, we read the character  */
    /* code (`glyph_code') at index j and make the assignment:            */
    /*                                                                    */
    /*    encoding->codes[glyph_code] = j + 1                             */
    /*                                                                    */
    /* We also make the assignment:                                       */
    /*                                                                    */
    /*    encoding->sids[glyph_code] = charset->sids[j + 1]               */
    /*                                                                    */
    /* This gives us both a code to GID and a code to SID mapping.        */
    if (offset > 1)
    {
        encoding.offset = base_offset + offset;

        error = stream.Seek(encoding.offset);
        if (error != 0)
            return error;
        encoding.format = stream.ReadUChar();
        error = FT_Error;
        if (error != 0)
            return error;
        count = stream.ReadUChar();
        error = FT_Error;
        if (error != 0)
            return error;

        switch (encoding.format & 0x7F)
        {
            case 0:
                encoding.count = count + 1;
                error = stream.EnterFrame(count);
                if (error != 0)
                    return error;

                var p = stream.cur;
                for (j = 1; j <= count; j++)
                {
                    glyph_code = stream.data[p];
                    p++;
                    /* Make sure j is not too big. */
                    if (j < num_glyphs)
                    {
                        /* Assign code to GID mapping. */
                        encoding.codes[glyph_code] = j;
                        /* Assign code to SID mapping. */
                        encoding.sids[glyph_code] = charset.sids[j];
                    }
                }
                stream.ExitFrame();
                break;

            case 1:
                var nleft = 0;
                var i = 1;
                encoding.count = 0;

                /* Parse the Format1 ranges. */
                for (j = 0;  j < count; j++, i += nleft)
                {
                    /* Read the first glyph code of the range. */
                    glyph_code = stream.ReadUChar();
                    error = FT_Error;
                    if (error != 0)
                        return error;

                    nleft = stream.ReadUChar();
                    error = FT_Error;
                    if (error != 0)
                        return error;
                    /* Read the number of codes in the range. */
                    /* Increment nleft, so we read `nleft + 1' codes/sids. */
                    nleft++;

                    /* compute max number of character codes */
                    if (nleft > encoding.count)
                        encoding.count = nleft;

                    /* Fill in the range of codes/sids. */
                    for (var k = i; k < nleft + i; k++, glyph_code++)
                    {
                        /* Make sure k is not too big. */
                        if (k < num_glyphs && glyph_code < 256)
                        {
                            /* Assign code to GID mapping. */
                            encoding.codes[glyph_code] = k;
                            /* Assign code to SID mapping. */
                            encoding.sids[glyph_code] = charset.sids[k];
                        }
                    }
                }

                /* simple check; one never knows what can be found in a font */
                if (encoding.count > 256)
                    encoding.count = 256;

                break;

            default:
                return 3;
        }

        /* Parse supplemental encodings, if any. */
        if (encoding.format & 0x80)
        {
            count = stream.ReadUChar();
            error = FT_Error;
            if (error != 0)
                return error;

            for (j = 0; j < count; j++)
            {
                /* Read supplemental glyph code. */
                glyph_code = stream.ReadUChar();
                error = FT_Error;
                if (error != 0)
                    return error;

                glyph_sid = stream.ReadUShort();
                error = FT_Error;
                if (error != 0)
                    return error;

                /* Assign code to SID mapping. */
                encoding.sids[glyph_code] = glyph_sid;

                /* First, look up GID which has been assigned to */
                /* SID glyph_sid.                                */
                for (var gindex = 0; gindex < num_glyphs; gindex++)
                {
                    if (charset.sids[gindex] == glyph_sid)
                    {
                        encoding.codes[glyph_code] = gindex;
                        break;
                    }
                }
            }
        }
    }
    else
    {
        /* We take into account the fact a CFF font can use a predefined */
        /* encoding without containing all of the glyphs encoded by this */
        /* encoding (see the note at the end of section 12 in the CFF    */
        /* specification).                                               */

        switch (offset)
        {
        case 0:
        case 1:
            var src = (offset == 0) ? cff_standard_encoding : cff_expert_encoding;
            for (var k = 0; k < 256; k++)
                encoding.sids[k] = src[k];

            encoding.count = 0;
            error = cff_charset_compute_cids(charset, num_glyphs, stream.memory);
            if (error != 0)
                return error;

            for (j = 0; j < 256; j++)
            {
                var sid = encoding.sids[j];
                var gid = 0;


                if (sid)
                    gid = cff_charset_cid_to_gindex(charset, sid);

                if ( gid != 0 )
                {
                    encoding.codes[j] = gid;
                    encoding.count    = j + 1;
                }
                else
                {
                    encoding.codes[j] = 0;
                    encoding.sids [j] = 0;
                }
            }
            break;

        default:
            return 3;
        }
    }

    return error;
}

function cff_subfont_load(font, idx, font_index, stream, base_offset, library)
{
    var error = 0;
    var parser = new CFF_ParserRec();
    var top = font.font_dict;
    var priv = font.private_dict;

    cff_parser_init(parser, 4096, font.font_dict, library);

    /* set defaults */
    top.clear();

    top.underline_position  = -100 << 16;
    top.underline_thickness = 50 << 16;
    top.charstring_type     = 2;
    top.font_matrix.xx      = 0x10000;
    top.font_matrix.yy      = 0x10000;
    top.cid_count           = 8720;

    /* we use the implementation specific SID value 0xFFFF to indicate */
    /* missing entries                                                 */
    top.version             = 0xFFFF;
    top.notice              = 0xFFFF;
    top.copyright           = 0xFFFF;
    top.full_name           = 0xFFFF;
    top.family_name         = 0xFFFF;
    top.weight              = 0xFFFF;
    top.embedded_postscript = 0xFFFF;

    top.cid_registry        = 0xFFFF;
    top.cid_ordering        = 0xFFFF;
    top.cid_font_name       = 0xFFFF;

    var ret = cff_index_access_element(idx, font_index);
    error = ret.err;
    var dict = ret.bytes;
    var dict_len = ret.len;

    if (error == 0)
    {
        error = cff_parser_run(parser, dict, dict.pos + dict_len);
    }

    cff_index_forget_element(idx, dict);

    if (error != 0)
        return error;

    /* if it is a CID font, we stop there */
    if (top.cid_registry != 0xFFFF)
        return error;

    /* parse the private dictionary, if any */
    if (top.private_offset != 0 && top.private_size != 0)
    {
        priv.clear();

        priv.blue_shift       = 7;
        priv.blue_fuzz        = 1;
        priv.lenIV            = -1;
        priv.expansion_factor = parseInt(0.06 * 0x10000);
        priv.blue_scale       = parseInt(0.039625 * 0x10000 * 1000);

        cff_parser_init(parser, 8192, priv, library);

        error = stream.Seek(base_offset + font.font_dict.private_offset);
        if (error != 0)
            return error;

        error = stream.EnterFrame(font.font_dict.private_size);
        if (error != 0)
            return error;

        var curs = new CPointer();
        curs.data = stream.data;
        curs.pos = stream.cur;
        error = cff_parser_run(parser, curs, curs.pos + font.font_dict.private_size);

        stream.ExitFrame();
        if (error != 0)
            return error;

        /* ensure that `num_blue_values' is even */
        priv.num_blue_values &= ~1;
    }

    /* read the local subrs, if any */
    if (priv.local_subrs_offset != 0)
    {
        error = stream.Seek(base_offset + top.private_offset + priv.local_subrs_offset);
        if (error != 0)
            return error;

        error = cff_index_init(font.local_subrs_index, stream, 1);
        if (error != 0)
            return error;

        ret = cff_index_get_pointers(font.local_subrs_index, font.local_subrs, null, 0);
        error = ret.err;
        font.local_subrs = ret.table;
        if (error != 0)
            return error;
    }

    return error;
}

function cff_subfont_done(memory, subfont)
{
    if (subfont != null)
    {
        cff_index_done(subfont.local_subrs_index);
        subfont.local_subrs = null;
    }
}

function cff_font_load(library, stream, face_index, font, pure_cff)
{
    var error = 0;
    var memory = stream.memory;
    var base_offset = stream.pos;
    var dict = font.top_font.font_dict;
    var string_index = new CFF_IndexRec();

    // TODO: font.clear();
    error = stream.EnterFrame(4);
    if (error != 0)
        return error;

    /* read CFF font header */
    font.version_major = stream.GetUChar();
    font.version_minor = stream.GetUChar();
    font.header_size = stream.GetUChar();
    font.absolute_offsize = stream.GetUChar();

    stream.ExitFrame();

    /* check format */
    if (font.version_major != 1 || font.header_size < 4 || font.absolute_offsize > 4)
        return 2;

    /* skip the rest of the header */
    error = stream.Skip(font.header_size - 4);
    if (error != 0)
        return error;

    /* read the name, top dict, string and global subrs index */
    error = cff_index_init(font.name_index, stream, 0);
    if (error == 0)
        error = cff_index_init(font.font_dict_index, stream, 0);
    if (error == 0)
        error = cff_index_init(string_index, stream, 1);
    if (error == 0)
        error = cff_index_init(font.global_subrs_index, stream, 1);

    if (error != 0)
        return error;

    var ret = cff_index_get_pointers(string_index, font.strings, font.string_pool, 1);
    error = ret.err;
    font.strings = ret.table;
    font.string_pool = ret.pool;

    font.num_strings = string_index.count;

    /* well, we don't really forget the `disabled' fonts... */
    font.num_faces = font.name_index.count;
    if (face_index >= font.num_faces)
    {
        error = 6;
    }

    /* in case of a font format check, simply exit now */
    if (face_index < 0)
        return error;

    /* now, parse the top-level font dictionary */
    error = cff_subfont_load(font.top_font, font.font_dict_index, face_index, stream, base_offset, library);
    if (error != 0)
        return error;

    error = stream.Seek(base_offset + dict.charstrings_offset);
    if (error != 0)
        return error;

    error = cff_index_init(font.charstrings_index, stream, 0);
    if (error != 0)
        return error;

    /* now, check for a CID font */
    if (dict.cid_registry != 0xFFFF)
    {
        var fd_index = new CFF_IndexRec();
        var sub = null;

        /* this is a CID-keyed font, we must now allocate a table of */
        /* sub-fonts, then load each of them separately              */
        error = stream.Seek(base_offset + dict.cid_fd_array_offset);
        if (error != 0)
            return error;

        error = cff_index_init(fd_index, stream, 0);
        if (error != 0)
            return error;

        if (fd_index.count > 256)
        {
            cff_index_done(fd_index);
        }
        else
        {
            var _is_break = 0;
            /* allocate & read each font dict independently */
            font.num_subfonts = fd_index.count;
            sub = new Array(fd_index.count);

            for (var idx = 0; idx < fd_index.count; idx++)
                sub[idx] = new CFF_SubFontRec();

            /* set up pointer table */
            for (var idx = 0; idx < fd_index.count; idx++)
                font.subfonts[idx] = sub[idx];

            /* now load each subfont independently */
            for (var idx = 0; idx < fd_index.count; idx++)
            {
                error = cff_subfont_load(font.subfonts[idx], fd_index, idx, stream, base_offset, library);
                if (error != 0)
                {
                    cff_index_done(fd_index);
                    _is_break = 1;
                }
            }

            /* now load the FD Select array */
            if (_is_break == 0)
            {
                error = CFF_Load_FD_Select(font.fd_select, font.charstrings_index.count, stream, base_offset + dict.cid_fd_select_offset);
            }

            cff_index_done(fd_index);

            if (error != 0)
                return error;
        }
    }
    else
        font.num_subfonts = 0;

    /* read the charstrings index now */
    if (dict.charstrings_offset == 0)
        return 3;


    font.num_glyphs = font.charstrings_index.count;

    ret = cff_index_get_pointers(font.global_subrs_index, font.global_subrs, null, 0);
    error = ret.err;
    font.global_subrs = ret.table;

    if (error != 0)
        return error;

    /* read the Charset and Encoding tables if available */
    if (font.num_glyphs > 0)
    {
        var invert = (dict.cid_registry != 0xFFFF && pure_cff) ? 1 : 0;

        error = cff_charset_load(font.charset, font.num_glyphs, stream, base_offset, dict.charset_offset, invert);
        if (error != 0)
            return error;

        /* CID-keyed CFFs don't have an encoding */
        if (dict.cid_registry == 0xFFFF)
        {
            error = cff_encoding_load(font.encoding, font.charset, font.num_glyphs, stream, base_offset, dict.encoding_offset);
            if (error != 0)
                return error;
        }
    }

    /* get the font name (/CIDFontName for CID-keyed fonts, */
    /* /FontName otherwise)                                 */
    font.font_name = cff_index_get_name(font, face_index);

    cff_index_done(string_index);
    return error;
}

function cff_font_done(font)
{
    var memory = font.memory;

    cff_index_done(font.global_subrs_index);
    cff_index_done(font.font_dict_index );
    cff_index_done(font.name_index);
    cff_index_done(font.charstrings_index);

    /* release font dictionaries, but only if working with */
    /* a CID keyed CFF font                                */
    if (font.num_subfonts > 0)
    {
        for (var idx = 0; idx < font.num_subfonts; idx++)
            cff_subfont_done(memory, font.subfonts[idx]);

        /* the subfonts array has been allocated as a single block */
        font.subfonts[0] = null;
    }

    cff_encoding_done(font.encoding);
    cff_charset_done(font.charset, font.stream);

    cff_subfont_done(memory, font.top_font);

    CFF_Done_FD_Select(font.fd_select, font.stream);

    font.font_info = null;

    font.strings = null;
    font.string_pool = null;
}

/******************************************************************************/
// cffcmaps
/******************************************************************************/
function CFF_CMapStdRec()
{
    this.cmap = new FT_CMapRec();
    this.gids = null;

    this.type = 1;
}

function cff_cmap_encoding_init(cmap)
{
    cmap.gids = __FT_CMapRec(cmap).charmap.face.extra.data.encoding.codes;
    return 0;
}

function cff_cmap_encoding_done(cmap)
{
    cmap.gids = null;
}

function cff_cmap_encoding_char_index(cmap, char_code)
{
    if (char_code < 256)
        return cmap.gids[char_code];

    return 0;
}

function cff_cmap_encoding_char_next(cmap, pchar_code)
{
    var char_code = pchar_code;
    var ret = {gindex: 0, char_code: 0};

    if (char_code < 255)
    {
        var code = char_code + 1;

        for (;;)
        {
            if (code >= 256)
                break;

            ret.gindex = cmap.gids[code];
            if (ret.gindex != 0)
            {
                ret.char_code = code;
                break;
            }
            code++;
        }
    }
    return ret;
}

var cff_cmap_encoding_class_rec = create_cmap_class_rec(101,cff_cmap_encoding_init,cff_cmap_encoding_done,cff_cmap_encoding_char_index,cff_cmap_encoding_char_next,null,null,null,null,null);

function cff_sid_to_glyph_name(face, idx)
{
    var cff = face.extra.data;
    var sid = cff.charset.sids[idx];

    return cff_index_get_sid_string(cff, sid);
}

function cff_cmap_unicode_init(unicodes)
{
    var face = __FT_CharmapRec(unicodes).face;
    var cff = face.extra.data;
    var charset = cff.charset;
    var psnames = cff.psnames;

    /* can't build Unicode map for CID-keyed font */
    /* because we don't know glyph names.         */
    if (charset.sids == null)
        return 163;

    return psnames.unicodes_init(face.memory, unicodes, cff.num_glyphs, cff_sid_to_glyph_name, null, face);
}

function cff_cmap_unicode_done(unicodes)
{
    unicodes.maps = null;
    unicodes.num_maps = 0;
}

function cff_cmap_unicode_char_index(unicodes, char_code)
{
    var face = __FT_CharmapRec(unicodes).face;
    var psnames = face.extra.data.psnames;

    return psnames.unicodes_char_index(unicodes, char_code);
}

function cff_cmap_unicode_char_next(unicodes, pchar_code)
{
    var face = __FT_CharmapRec(unicodes).face;
    var psnames = face.extra.data.psnames;

    return psnames.unicodes_char_next(unicodes, pchar_code);
}

var cff_cmap_unicode_class_rec = create_cmap_class_rec(102,cff_cmap_unicode_init,cff_cmap_unicode_done,cff_cmap_unicode_char_index,cff_cmap_unicode_char_next,null,null,null,null,null);

/******************************************************************************/
// cffobjs
/******************************************************************************/

function CFF_SizeRec()
{
    this.face = null;
    this.generic = null;
    this.metrics = new FT_Size_Metrics();
    this.internal = null;
    
    this.strike_index = 0;    /* 0xFFFFFFFF to indicate invalid */
}

function CFF_GlyphSlotRec()
{
    this.root = new FT_GlyphSlot();

    this.hint = 0;
    this.scaled = 0;

    this.x_scale = 0;
    this.y_scale = 0;
}

function CFF_InternalRec()
{
    this.topfont = null;
    this.subfonts = new Array(256);
}

function CFF_Transform()
{
    this.xx = 0;
    this.xy = 0;
    this.yx = 0;
    this.yy = 0;

    this.ox = 0;
    this.oy = 0;
}

function cff_size_get_globals_funcs(size)
{
    var face = size.face;
    var font = face.extra.data;
    var pshinter = font.pshinter;

    var module = face.driver.library.FT_Get_Module("pshinter");
    return (module != null && pshinter != null && pshinter.get_globals_funcs != null) ? pshinter.get_globals_funcs(module) : null;
}

function cff_size_done(cffsize)
{
    var internal = cffsize.internal;
    if (internal != null)
    {
        var funcs = cff_size_get_globals_funcs(size);
        if (funcs != null)
        {
            funcs.destroy(internal.topfont);

            for (var i = font.num_subfonts; i > 0; i--)
                funcs.destroy(internal.subfonts[i - 1]);
        }
        /* `internal' is freed by destroy_size (in ftobjs.c) */
    }
}

function cff_make_private_dict(subfont, priv)
{
    var cpriv = subfont.private_dict;
    var n = 0;
    priv.clear();

    var count = priv.num_blue_values = cpriv.num_blue_values;
    for (n = 0; n < count; n++)
        priv.blue_values[n] = cpriv.blue_values[n];

    count = priv.num_other_blues = cpriv.num_other_blues;
    for (n = 0; n < count; n++)
        priv.other_blues[n] = cpriv.other_blues[n];

    count = priv.num_family_blues = cpriv.num_family_blues;
    for (n = 0; n < count; n++)
        priv.family_blues[n] = cpriv.family_blues[n];

    count = priv.num_family_other_blues = cpriv.num_family_other_blues;
    for (n = 0; n < count; n++)
        priv.family_other_blues[n] = cpriv.family_other_blues[n];

    priv.blue_scale = cpriv.blue_scale;
    priv.blue_shift = cpriv.blue_shift;
    priv.blue_fuzz  = cpriv.blue_fuzz;

    priv.standard_width[0]  = cpriv.standard_width;
    priv.standard_height[0] = cpriv.standard_height;

    count = priv.num_snap_widths = cpriv.num_snap_widths;
    for (n = 0; n < count; n++)
        priv.snap_widths[n] = cpriv.snap_widths[n];

    count = priv.num_snap_heights = cpriv.num_snap_heights;
    for (n = 0; n < count; n++)
        priv.snap_heights[n] = cpriv.snap_heights[n];

    priv.force_bold     = cpriv.force_bold;
    priv.language_group = cpriv.language_group;
    priv.lenIV          = cpriv.lenIV;
}

function cff_size_init()
{
    var size = new CFF_SizeRec();
    var funcs = null;//cff_size_get_globals_funcs(size);

    if (funcs != null)
    {
        // TODO: global funcs...
    }

    FT_Error = 0;
    size.strike_index = 0xFFFFFFFF;
    return size;
}

function cff_size_select(size, strike_index)
{
    size.strike_index = strike_index;
    FT_Select_Metrics(size.face, strike_index);

    var funcs = cff_size_get_globals_funcs(size);

    if (funcs != null)
    {
        // TODO: global funcs...
    }

    return 0;
}

function cff_size_request(size, req)
{
    var face = size.face;
    if (0 != (face.face_flags & 2))
    {
        var sfnt = face.sfnt;
        
        var strike_index = sfnt.set_sbit_strike(face, req);
        if (FT_Error != 0)
            size.strike_index = 0xFFFFFFFF;
        else
            return cff_size_select(size, strike_index);
    }

    FT_Request_Metrics(size.face, req);

    var funcs = cff_size_get_globals_funcs(size);
    if (funcs != null)
    {
        // TODO: global funcs...
    }
    return 0;
}

function cff_slot_done(slot)
{
    slot.internal.glyph_hints = null;
}

function cff_slot_init(slot)
{
    var face     = slot.face;
    var font     = face.extra.data;
    var pshinter = font.pshinter;

    if (pshinter != null)
    {
        var module = face.driver.library.FT_Get_Module("pshinter");
        if (module != null)
        {
            slot.internal.glyph_hints = pshinter.get_t2_funcs(module);
        }
    }
    return 0;
}

function cff_strcpy(memory, source)
{
    var result = source;
    return result;
}

function remove_subset_prefix(name)
{
    var idx = 0;
    var length = name.length + 1;
    var continue_search = 1;

    while (continue_search)
    {
        if (length >= 7 && name.charCodeAt(6) == 43)
        {
            for (idx = 0; idx < 6; idx++)
            {
                /* ASCII uppercase letters */
                var c = name.charCodeAt(idx);
                if (!(65 <= c && c <= 90))
                    continue_search = 0;
            }

            if (continue_search)
            {
                name = name.substring(7);
                length -= 7;
            }
        }
        else
            continue_search = 0;
    }

    return name;
}

function remove_style(family_name, style_name)
{
    var family_name_length = family_name.length;
    var style_name_length  = style_name.length;

    if (family_name_length > style_name_length)
    {
        var idx = 0;
        for (idx = 1; idx <= style_name_length; idx++)
        {
            if (family_name.charCodeAt(family_name_length - idx) != style_name.charCodeAt(style_name_length - idx))
                break;
        }

        if (idx > style_name_length)
        {
            /* family_name ends with style_name; remove it */
            idx = family_name_length - style_name_length - 1;

            /* also remove special characters     */
            /* between real family name and style */
            while (idx > 0)
            {
                var c = family_name.charCodeAt(idx);
                if (c == 45 || c == 32 || c == 95 || c == 43)
                    break;
                
                --idx;
            }

            if (idx > 0)
                family_name = family_name.substring(0, idx);
        }
    }

    return family_name;
}

function cff_face_init(stream, face, face_index, num_params, params)
{
    var pure_cff = 1;
    var sfnt_format = 0;
    var flags = 0;
    var library = face.driver.library;

    var sfnt = library.FT_Get_Module_Interface("sfnt");
    if (sfnt == null)
        return 2;

    var psnames = FT_FACE_FIND_GLOBAL_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_CMAPS);
    var pshinter = library.FT_Get_Module_Interface("pshinter");

    /* create input stream from resource */
    var error = stream.Seek(0);
    if (error != 0)
        return error;

    /* check whether we have a valid OpenType file */
    error = sfnt.init_face(stream, face, face_index, num_params, params);
    if (error == 0)
    {
        if (face.format_tag != 1330926671)
            return 2;

        /* if we are performing a simple font format check, exit immediately */
        if (face_index < 0)
            return 0;

        /* UNDOCUMENTED!  A CFF in an SFNT can have only a single font. */
        if (face_index > 0)
            return 6;

        sfnt_format = 1;

        /* now, the font can be either an OpenType/CFF font, or an SVG CEF */
        /* font; in the latter case it doesn't have a `head' table         */
        face.goto_table(face, 1751474532, stream);
        error = FT_Error;
        if (error == 0)
        {
            pure_cff = 0;

            /* load font directory */
            error = sfnt.load_face(stream, face, 0);
            if (error != 0)
                return error;
        }
        else
        {
            /* load the `cmap' table explicitly */
            error = sfnt.load_cmap(face, stream);
            if (error != 0)
                return error;

            /* XXX: we don't load the GPOS table, as OpenType Layout     */
            /* support will be added later to a layout library on top of */
            /* FreeType 2                                                */
        }

        /* now load the CFF part of the file */
        face.goto_table(face, 1128678944, stream);
        error = FT_Error;
        if (error != 0)
            return error;
    }
    else
    {
        /* rewind to start of file; we are going to load a pure-CFF font */
        error = stream.Seek(0);
        if (error != 0)
            return error;
    }

    /* now load and parse the CFF table in the file */
    var cff = new CFF_FontRec();
    face.extra.data = cff;
    
    error = cff_font_load(library, stream, face_index, cff, pure_cff);
    if (error != 0)
        return error;

    cff.pshinter = pshinter;
    cff.psnames  = psnames;

    face.face_index = face_index;

    /* Complement the root flags with some interesting information. */
    /* Note that this is only necessary for pure CFF and CEF fonts; */
    /* SFNT based fonts use the `name' table instead.               */
    face.num_glyphs = cff.num_glyphs;
    var dict = cff.top_font.font_dict;

    /* we need the `PSNames' module for CFF and CEF formats */
    /* which aren't CID-keyed                               */
    if (dict.cid_registry == 0xFFFF && psnames == null)
        return 2;

    if (dict.has_font_matrix == 0)
        dict.units_per_em = pure_cff ? 1000 : face.units_per_EM;

    /* Normalize the font matrix so that `matrix->xx' is 1; the */
    /* scaling is done with `units_per_em' then (at this point, */
    /* it already contains the scaling factor, but without      */
    /* normalization of the matrix).                            */
    /*                                                          */
    /* Note that the offsets must be expressed in integer font  */
    /* units.                                                   */
    var matrix = dict.font_matrix;
    var offset = dict.font_offset;
    var temp = Math.abs(matrix.yy);

    if (temp != 0x10000)
    {
        dict.units_per_em = FT_DivFix(dict.units_per_em, temp);

        matrix.xx = FT_DivFix(matrix.xx, temp);
        matrix.yx = FT_DivFix(matrix.yx, temp);
        matrix.xy = FT_DivFix(matrix.xy, temp);
        matrix.yy = FT_DivFix(matrix.yy, temp);
        offset.x  = FT_DivFix(offset.x,  temp);
        offset.y  = FT_DivFix(offset.y,  temp);
    }

    offset.x >>= 16;
    offset.y >>= 16;

    for (var i = cff.num_subfonts; i > 0; i--)
    {
        var sub = cff.subfonts[i - 1].font_dict;
        var top = cff.top_font.font_dict;

        if (sub.has_font_matrix == 1)
        {
            var scaling = 0;
            /* if we have a top-level matrix, */
            /* concatenate the subfont matrix */

            if (top.has_font_matrix == 1)
            {
                if (top.units_per_em > 1 && sub.units_per_em > 1)
                    scaling = Math.min(top.units_per_em, sub.units_per_em);
                else
                    scaling = 1;

                FT_Matrix_Multiply_Scaled(top.font_matrix, sub.font_matrix, scaling);
                FT_Vector_Transform_Scaled(sub.font_offset, top.font_matrix, scaling);

                sub.units_per_em = FT_MulDiv(sub.units_per_em, top.units_per_em, scaling);
            }
        }
        else
        {
            sub.font_matrix = top.font_matrix;
            sub.font_offset = top.font_offset;

            sub.units_per_em = top.units_per_em;
        }

        matrix = sub.font_matrix;
        offset = sub.font_offset;
        temp = Math.abs(matrix.yy);

        if (temp != 0x10000)
        {
            sub.units_per_em = FT_DivFix(sub.units_per_em, temp);

            matrix.xx = FT_DivFix(matrix.xx, temp);
            matrix.yx = FT_DivFix(matrix.yx, temp);
            matrix.xy = FT_DivFix(matrix.xy, temp);
            matrix.yy = FT_DivFix(matrix.yy, temp);
            offset.x  = FT_DivFix(offset.x,  temp);
            offset.y  = FT_DivFix(offset.y,  temp);
        }

        offset.x >>= 16;
        offset.y >>= 16;
    }

    if (pure_cff == 1)
    {
        var style_name = null;

        /* set up num_faces */
        face.num_faces = cff.num_faces;

        /* compute number of glyphs */
        if (dict.cid_registry != 0xFFFF)
            face.num_glyphs = cff.charset.max_cid + 1;
        else
            face.num_glyphs = cff.charstrings_index.count;

        /* set global bbox, as well as EM size */
        face.bbox.xMin = dict.font_bbox.xMin >> 16;
        face.bbox.yMin = dict.font_bbox.yMin >> 16;
        /* no `U' suffix here to 0xFFFF! */
        face.bbox.xMax = (dict.font_bbox.xMax + 0xFFFF) >> 16;
        face.bbox.yMax = (dict.font_bbox.yMax + 0xFFFF) >> 16;

        face.units_per_EM = dict.units_per_em & 0xFFFF;

        face.ascender  = face.bbox.yMax;
        face.descender = face.bbox.yMin;

        face.height = ((face.units_per_EM * 12) / 10);
        if (face.height < (face.ascender - face.descender))
            face.height = (face.ascender - face.descender);

        face.underline_position = (dict.underline_position >> 16);
        face.underline_thickness = (dict.underline_thickness >> 16);

        /* retrieve font family & style name */
        face.family_name = cff_index_get_name(cff, face_index);
        if (face.family_name != null)
        {
            var full = cff_index_get_sid_string(cff, dict.full_name);
            var fullp = 0;
            var family = face.family_name;
            var family_name = null;

            face.family_name = remove_subset_prefix(face.family_name);

            if (dict.family_name != 0)
            {
                family_name = cff_index_get_sid_string(cff, dict.family_name);
                if (family_name != null)
                    family = family_name;
            }

            var familyp = 0;
            var full_len = (full == null) ? 0 : full.length;
            var family_len = family.length;

            /* We try to extract the style name from the full name.   */
            /* We need to ignore spaces and dashes during the search. */
            if (full != null && family != null)
            {
                while (fullp < full_len)
                {
                    var _c1 = full.charCodeAt(fullp);
                    var _c2 = 0;
                    if (familyp < family_len)
                        _c2 = family.charCodeAt(familyp);

                    /* skip common characters at the start of both strings */
                    if (_c1 == _c2)
                    {
                        familyp++;
                        fullp++;
                        continue;
                    }

                    if (_c1 == 32 || _c1 == 45)
                    {
                        fullp++;
                        continue;
                    }

                    if (_c2 == 32 || _c2 == 45)
                    {
                        familyp++;
                        continue;
                    }

                    if (familyp >= family_len && fullp < full_len)
                    {
                        /* The full name begins with the same characters as the  */
                        /* family name, with spaces and dashes removed.  In this */
                        /* case, the remaining string in `fullp' will be used as */
                        /* the style name.                                       */
                        style_name = full.substring(fullp);

                        /* remove the style part from the family name (if present) */
                        face.family_name = remove_style(face.family_name, style_name);
                    }
                    break;
                }
            }
        }
        else
        {
            var cid_font_name = cff_index_get_sid_string(cff, dict.cid_font_name);
            /* do we have a `/FontName' for a CID-keyed font? */
            if (cid_font_name != null)
                face.family_name = cff_strcpy(memory, cid_font_name);
        }

        if (style_name != null)
            face.style_name = style_name;
        else /* assume "Regular" style if we don't know better */
            face.style_name = "Regular";

        /*******************************************************************/
        /*                                                                 */
        /* Compute face flags.                                             */
        /*                                                                 */
        flags = (1 | 16 | 2048);

        if (sfnt_format != 0)
            flags |= 8;

        /* fixed width font? */
        if (dict.is_fixed_pitch != 0)
            flags |= 4;

        /* XXX: WE DO NOT SUPPORT KERNING METRICS IN THE GPOS TABLE FOR NOW */

        face.face_flags = flags;
        /*******************************************************************/
        /*                                                                 */
        /* Compute style flags.                                            */
        /*                                                                 */
        flags = 0;

        if (dict.italic_angle != 0)
            flags |= 1;

        var weight = cff_index_get_sid_string(cff, dict.weight);
        if (weight != null)
            if (weight == "Bold" || weight == "Black")
                flags |= 2;

        /* double check */
        if (0 == (flags & 2) && face.style_name != null)
            if (!_strncmp(face.style_name, "Bold", 4) || !_strncmp(face.style_name, "Black", 5))
                flags |= 2;

        face.style_flags = flags;
    }


    //#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES
    /* CID-keyed CFF fonts don't have glyph names -- the SFNT loader */
    /* has unset this flag because of the 3.0 `post' table.          */
    if (dict.cid_registry == 0xFFFF)
        face.face_flags |= 512;
    //#endif

    if (dict.cid_registry != 0xFFFF && pure_cff == 1)
        face.face_flags |= 4096;


    /*******************************************************************/
    /*                                                                 */
    /* Compute char maps.                                              */
    /*                                                                 */

    /* Try to synthesize a Unicode charmap if there is none available */
    /* already.  If an OpenType font contains a Unicode "cmap", we    */
    /* will use it, whatever be in the CFF part of the file.          */
    var cmaprec = new FT_CharMapRec();
    var cmap = null;
    var nn = 0;
    var encoding = cff.encoding;

    var is_skip_unicode = 0;
    for (nn = 0; nn < face.num_charmaps; nn++)
    {
        cmap = face.charmaps[nn];

        /* Windows Unicode? */
        if (cmap.platform_id == 3 && cmap.encoding_id == 1)
            is_skip_unicode = 1;

        /* Apple Unicode platform id? */
        if (cmap.platform_id == 0)
            is_skip_unicode = 1; /* Apple Unicode */
    }

    if (0 == is_skip_unicode)
    {
        /* since CID-keyed fonts don't contain glyph names, we can't */
        /* construct a cmap                                          */
        if (pure_cff == 1 && cff.top_font.font_dict.cid_registry != 0xFFFF)
            return error;

        //#ifdef FT_MAX_CHARMAP_CACHEABLE
        if (nn + 1 > 15)
            return error;
        //#endif

        /* we didn't find a Unicode charmap -- synthesize one */
        cmaprec.face        = face;
        cmaprec.platform_id = 3;
        cmaprec.encoding_id = 1;
        cmaprec.encoding    = 1970170211;

        nn = face.num_charmaps;

        FT_CMap_New(FT_CFF_CMAP_UNICODE_CLASS_REC_GET, null, cmaprec);
        error = FT_Error;
        if (error && 163 != error)
            return error;
        error = 0;

        /* if no Unicode charmap was previously selected, select this one */
        if (face.charmap == null && nn != face.num_charmaps)
            face.charmap = face.charmaps[nn];
    }

    //#ifdef FT_MAX_CHARMAP_CACHEABLE
    if (nn > 15)
        return error;
    //#endif

    if (encoding.count > 0)
    {
        var clazz = null;

        cmaprec.face = face;
        cmaprec.platform_id = 7;  /* Adobe platform id */

        if (encoding.offset == 0)
        {
            cmaprec.encoding_id = 0;
            cmaprec.encoding    = 1094995778;
            clazz               = FT_CFF_CMAP_ENCODING_CLASS_REC_GET;
        }
        else if (encoding.offset == 1)
        {
            cmaprec.encoding_id = 1;
            cmaprec.encoding    = 1094992453;
            clazz               = FT_CFF_CMAP_ENCODING_CLASS_REC_GET;
        }
        else
        {
            cmaprec.encoding_id = 2;
            cmaprec.encoding    = 1094992451;
            clazz               = FT_CFF_CMAP_ENCODING_CLASS_REC_GET;
        }

        FT_CMap_New(clazz, null, cmaprec);
        error = FT_Error;
    }

    return error;
}

function cff_face_done(face)
{
    if (face == null)
        return;

    var sfnt = face.sfnt;

    if (sfnt != null)
        sfnt.done_face(face);

    var cff = face.extra.data;
    if (cff != null)
    {
        cff_font_done(cff);
        face.extra.data = null;
    }
}

function cff_driver_init(module)
{
    return 0;
}

function cff_driver_done(module)
{
}

/******************************************************************************/
// cffgload
/******************************************************************************/

var cff_argument_counts =
[
    0,  /* unknown */

    2 | 128 | 64, /* rmoveto */
    1 | 128 | 64,
    1 | 128 | 64,

    0 | 32, /* rlineto */
    0 | 32,
    0 | 32,

    0 | 32, /* rrcurveto */
    0 | 32,
    0 | 32,
    0 | 32,
    0 | 32,
    0 | 32,
    0 | 32,

    13, /* flex */
    7,
    9,
    11,

    0 | 128, /* endchar */

    2 | 128, /* hstem */
    2 | 128,
    2 | 128,
    2 | 128,

    0 | 128, /* hintmask */
    0 | 128, /* cntrmask */
    0, /* dotsection */

    1, /* abs */
    2,
    2,
    2,
    1,
    0,
    2,
    1,

    1, /* blend */

    1, /* drop */
    2,
    1,
    2,
    1,

    2, /* put */
    1,
    4,
    3,

    2, /* and */
    2,
    1,
    2,
    4,

    1, /* callsubr */
    1,
    0,

    2, /* hsbw */
    0,
    0,
    0,
    5, /* seac */
    4, /* sbw */
    2  /* setcurrentpoint */
];

function CFF_Builder()
{
    this.memory = null;
    this.face = null;
    this.glyph = null;
    this.loader = null;
    this.base = null;
    this.current = null;

    this.pos_x = 0;
    this.pos_y = 0;

    this.left_bearing = new FT_Vector();
    this.advance = new FT_Vector();

    this.bbox = new FT_BBox();
    this.path_begun = 0;
    this.load_points = 0;
    this.no_recurse = 0;

    this.metrics_only = 0;

    this.hints_funcs = null;
    this.hints_globals = null;

    this.clear = function()
    {
        this.memory = null;
        this.face = null;
        this.glyph = null;
        this.loader = null;
        this.base = null;
        this.current = null;

        this.pos_x = 0;
        this.pos_y = 0;

        this.left_bearing.x = 0;
        this.left_bearing.y = 0;

        this.advance.x = 0;
        this.advance.y = 0;

        this.bbox.xMin = 0;
        this.bbox.yMin = 0;
        this.bbox.xMax = 0;
        this.bbox.yMax = 0;

        this.path_begun = 0;
        this.load_points = 0;
        this.no_recurse = 0;

        this.metrics_only = 0;

        this.hints_funcs = null;
        this.hints_globals = null;
    }
}

function CFF_Decoder_Zone()
{
    this.base = null;
    this.limit = 0;
    this.cursor = 0;
}

function CFF_Decoder()
{
    this.builder = new CFF_Builder();
    this.cff = null;

    this.stack = CreateIntArray(48 + 1);
    this.top = 0;

    this.zones = new Array(32 + 1);
    for (var i = 0; i < (32 + 1); i++)
        this.zones[i] = new CFF_Decoder_Zone();

    this.zone = 0;

    this.flex_state = 0;
    this.num_flex_vectors = 0;

    this.flex_vectors = new Array(7);
    for (var i = 0; i < 7; i++)
        this.flex_vectors[i] = new FT_Vector();

    this.glyph_width = 0;
    this.nominal_width = 0;

    this.read_width = 0;
    this.width_only = 0;
    this.num_hints = 0;
    this.buildchar = CreateIntArray(32);

    this.num_locals = 0;
    this.num_globals = 0;

    this.locals_bias = 0;
    this.globals_bias = 0;

    this.locals = null;
    this.globals = null;

    this.glyph_names = null;   /* for pure CFF fonts only  */
    this.num_glyphs = 0;    /* number of glyphs in font */

    this.hint_mode = 0;

    this.seac = 0;

    this.clear = function()
    {
        this.builder.clear();
        this.cff = null;

        this.stack = CreateIntArray(48 + 1);
        for (var i = 0; i < (48 + 1); i++)
            this.stack[i] = 0;
        this.top = 0;

        for (var i = 0; i < (32 + 1); i++)
        {
            var p = this.zones[i];
            p.base = null;
            p.cursor = 0;
            p.limit = 0;
        }

        this.zone = 0;

        this.flex_state = 0;
        this.num_flex_vectors = 0;

        for (var i = 0; i < 7; i++)
        {
            this.flex_vectors[i].x = 0;
            this.flex_vectors[i].y = 0;
        }

        this.glyph_width = 0;
        this.nominal_width = 0;

        this.read_width = 0;
        this.width_only = 0;
        this.num_hints = 0;

        for (var i = 0; i < 32; i++)
            this.buildchar[i] = 0;

        this.num_locals = 0;
        this.num_globals = 0;

        this.locals_bias = 0;
        this.globals_bias = 0;

        this.locals = null;
        this.globals = null;

        this.glyph_names = null;   /* for pure CFF fonts only  */
        this.num_glyphs = 0;    /* number of glyphs in font */

        this.hint_mode = 0;

        this.seac = 0;
    }
}

function cff_builder_init(builder, face, size, glyph, hinting)
{
    builder.path_begun  = 0;
    builder.path_begun  = 0;
    builder.load_points = 1;

    builder.face   = face;
    builder.glyph  = glyph;
    builder.memory = face.memory;

    if (glyph != null)
    {
        var loader = glyph.internal.loader;

        builder.loader = loader;
        builder.base = loader.base.outline;
        builder.current = loader.current.outline;
        FT_GlyphLoader_Rewind(loader);

        builder.hints_globals = null;
        builder.hints_funcs = null;

        if (hinting && size != null)
        {
            var internal = size.internal;

            builder.hints_globals = internal.topfont;
            builder.hints_funcs = glyph.internal.glyph_hints;
        }
    }

    builder.pos_x = 0;
    builder.pos_y = 0;

    builder.left_bearing.x = 0;
    builder.left_bearing.y = 0;
    builder.advance.x      = 0;
    builder.advance.y      = 0;
}

function cff_builder_done(builder)
{
    var glyph = builder.glyph;
    if (glyph != null)
        EquatingOutline(glyph.outline, builder.base);
}

function cff_compute_bias(in_charstring_type, num_subrs)
{
    if (in_charstring_type == 1)
        return 0;
    else if (num_subrs < 1240)
        return 107;
    else if (num_subrs < 33900)
        return 1131;

    return 32768;
}

function cff_decoder_init(decoder, face, size, slot, hinting, hint_mode)
{
    var cff = face.extra.data;
    /* clear everything */
    decoder.clear();

    /* initialize builder */
    cff_builder_init(decoder.builder, face, size, slot, hinting);

    /* initialize Type2 decoder */
    decoder.cff = cff;
    decoder.num_globals = cff.global_subrs_index.count;
    decoder.globals = cff.global_subrs;
    decoder.globals_bias = cff_compute_bias(cff.top_font.font_dict.charstring_type, decoder.num_globals);

    decoder.hint_mode    = hint_mode;
}

function cff_decoder_prepare(decoder, size, glyph_index)
{
    var builder = decoder.builder;
    var cff = builder.face.extra.data;
    var sub = cff.top_font;

    /* manage CID fonts */
    if (cff.num_subfonts != 0)
    {
        var fd_index = cff_fd_select_get(cff.fd_select, glyph_index);

        if (fd_index >= cff.num_subfonts)
            return 3;

        sub = cff.subfonts[fd_index];

        if (builder.hints_funcs != null && size != null)
        {
            var internal = size.internal;

            /* for CFFs without subfonts, this value has already been set */
            builder.hints_globals = internal.subfonts[fd_index];
        }
    }

    decoder.num_locals = sub.local_subrs_index.count;
    decoder.locals = sub.local_subrs;
    decoder.locals_bias = cff_compute_bias(decoder.cff.top_font.font_dict.charstring_type, decoder.num_locals);

    decoder.glyph_width   = sub.private_dict.default_width;
    decoder.nominal_width = sub.private_dict.nominal_width;

    return 0;
}

function _cff_check_points(builder, count)
{
    return FT_GLYPHLOADER_CHECK_POINTS(builder.loader, count, 0);
}

function cff_builder_add_point(builder, x, y, flag)
{
    var base = builder.base;
    var outline = builder.current;

    if (builder.load_points = 1)
    {
        var point = base.points[outline.points + outline.n_points];
        var control = base.tags[outline.tags + outline.n_points];

        point.x = x >> 16;
        point.y = y >> 16;
        base.tags[outline.tags + outline.n_points] = ((flag != 0) ? 1 : 2);
    }

    outline.n_points++;
}

function cff_builder_add_point1(builder, x, y)
{
    var error = _cff_check_points(builder, 1);
    if (error == 0)
        cff_builder_add_point(builder, x, y, 1);

    return error;
}

function cff_builder_add_contour(builder)
{
    if (builder.load_points == 0)
    {
        builder.current.n_contours++;
        return 0;
    }

    var base = builder.base;
    var outline = builder.current;
    var error = FT_GLYPHLOADER_CHECK_POINTS(builder.loader, 0, 1);
    if (error == 0)
    {
        if (outline.n_contours > 0)
            base.contours[outline.contours + outline.n_contours - 1] = (outline.n_points - 1);

        outline.n_contours++;
    }

    return error;
}

function cff_builder_start_point(builder, x, y)
{
    var error = 0;
    if (builder.path_begun == 0)
    {
        builder.path_begun = 1;
        error = cff_builder_add_contour(builder);
        if (error == 0)
            error = cff_builder_add_point1(builder, x, y);
    }
    return error;
}

function cff_builder_close_contour(builder)
{
    var base = builder.base;
    var outline = builder.current;

    if (outline == null)
        return;

    var first = (outline.n_contours <= 1) ? 0 : base.contours[outline.contours + outline.n_contours - 2] + 1;

    /* We must not include the last point in the path if it */
    /* is located on the first point.                       */
    if (outline.n_points > 1)
    {
        var p1 = base.points[outline.points + first];
        var p2 = base.points[outline.points + outline.n_points - 1];
        var control = base.tags[outline.tags + outline.n_points - 1];

        /* `delete' last point only if it coincides with the first    */
        /* point and if it is not a control point (which can happen). */
        if (p1.x == p2.x && p1.y == p2.y)
            if (control == 1)
                outline.n_points--;
    }

    if (outline.n_contours > 0)
    {
        /* Don't add contours only consisting of one point, i.e., */
        /* check whether begin point and last point are the same. */
        if (first == outline.n_points - 1)
        {
            outline.n_contours--;
            outline.n_points--;
        }
        else
            base.contours[outline.contours + outline.n_contours - 1] = (outline.n_points - 1);
    }
}

function cff_lookup_glyph_by_stdcharcode(cff, charcode)
{
    /* CID-keyed fonts don't have glyph names */
    if (null == cff.charset.sids)
        return -1;

    /* check range of standard char code */
    if (charcode < 0 || charcode > 255)
        return -1;

    /* Get code to SID mapping from `cff_standard_encoding'. */
    var glyph_sid = cff_get_standard_encoding(charcode);

    for (var n = 0; n < cff.num_glyphs; n++)
    {
        if (cff.charset.sids[n] == glyph_sid)
            return n;
    }

    return -1;
}

function cff_get_glyph_data(face, glyph_index)
{
    var cff = face.extra.data;
    return cff_index_access_element(cff.charstrings_index, glyph_index);
}

function cff_free_glyph_data(face, pointer, length)
{
    var cff = face.extra.data;
    cff_index_forget_element(cff.charstrings_index, pointer);
}

function cff_operator_seac(decoder, asb, adx, ady, bchar, achar)
{
    var builder = decoder.builder;
    var bchar_index, achar_index;
    var face = builder.face;

    if (decoder.seac != 0)
        return 160;

    adx += decoder.builder.left_bearing.x;
    ady += decoder.builder.left_bearing.y;

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL
    /* Incremental fonts don't necessarily have valid charsets.        */
    /* They use the character code, not the glyph index, in this case. */
    if (face.internal.incremental_interface != null)
    {
        bchar_index = bchar;
        achar_index = achar;
    }
    else //#endif /* FT_CONFIG_OPTION_INCREMENTAL */
    {
        var cff = face.extra.data;
        bchar_index = cff_lookup_glyph_by_stdcharcode(cff, bchar);
        achar_index = cff_lookup_glyph_by_stdcharcode(cff, achar);
    }

    if (bchar_index < 0 || achar_index < 0)
        return 160;

    /* If we are trying to load a composite glyph, do not load the */
    /* accent character and return the array of subglyphs.         */
    if (builder.no_recurse != 0)
    {
        var glyph  = builder.glyph;
        var loader = glyph.internal.loader;

        /* reallocate subglyph array if necessary */
        error = FT_GlyphLoader_CheckSubGlyphs(loader, 2);
        if (error != 0)
            return error;

        var subg = loader.base.subglyphs[loader.current.subglyphs];

        /* subglyph 0 = base character */
        subg.index = bchar_index;
        subg.flags = 2 | 512;
        subg.arg1  = 0;
        subg.arg2  = 0;

        subg = loader.base.subglyphs[loader.current.subglyphs + 1];

        /* subglyph 1 = accent character */
        subg.index = achar_index;
        subg.flags = 2;
        subg.arg1 = (adx >> 16);
        subg.arg2 = (ady >> 16);

        /* set up remaining glyph fields */
        glyph.num_subglyphs = 2;
        glyph.subglyphs     = loader.base.subglyphs;
        glyph.format = 1668246896;

        loader.current.num_subglyphs = 2;
    }

    FT_GlyphLoader_Prepare(builder.loader);

    /* First load `bchar' in builder */
    var ret = cff_get_glyph_data(face, bchar_index);
    var error = ret.err;
    var charstring = ret.bytes;
    var charstring_len = ret.len;
    if (error == 0)
    {
        /* the seac operator must not be nested */
        decoder.seac = 1;
        error = cff_decoder_parse_charstrings(decoder, charstring, charstring_len);
        decoder.seac = 0;

        cff_free_glyph_data(face, charstring, charstring_len);

        if (error != 0)
            return error;
    }

    /* Save the left bearing, advance and glyph width of the base */
    /* character as they will be erased by the next load.         */

    var left_bearing = dublicate_vector(builder.left_bearing);
    var advance = dublicate_vector(builder.advance);
    var glyph_width = decoder.glyph_width;

    builder.left_bearing.x = 0;
    builder.left_bearing.y = 0;

    builder.pos_x = adx - asb;
    builder.pos_y = ady;

    /* Now load `achar' on top of the base outline. */
    ret = cff_get_glyph_data(face, achar_index);
    error = ret.err;
    charstring = ret.bytes;
    charstring_len = ret.len;
    if (error == 0)
    {
        /* the seac operator must not be nested */
        decoder.seac = 1;
        error = cff_decoder_parse_charstrings(decoder, charstring, charstring_len);
        decoder.seac = 0;

        cff_free_glyph_data(face, charstring, charstring_len);

        if (error != 0)
            return error;
    }

    /* Restore the left side bearing, advance and glyph width */
    /* of the base character.                                 */
    builder.left_bearing.x = left_bearing.x;
    builder.left_bearing.y = left_bearing.y;
    builder.advance.x = advance.x;
    builder.advance.y = advance.y;

    decoder.glyph_width  = glyph_width;

    builder.pos_x = 0;
    builder.pos_y = 0;

    return error;
}

function cff_decoder_parse_charstrings(decoder, charstring_base, charstring_len)
{
    var builder = decoder.builder;
    var charstring_type = decoder.cff.top_font.font_dict.charstring_type;

    /* set default width */
    decoder.num_hints  = 0;
    decoder.read_width = 1;

    /* compute random seed from stack address of parameter */
    var seed = parseInt(Math.random() * 0xFFFF);
    if (seed == 0)
        seed = 0x7384;

    /* initialize the decoder */
    decoder.top = 0;
    decoder.zone = 0;

    var zones = decoder.zones;
    var zone = 0;

    var tops = decoder.stack;
    var stack = decoder.top;

    var hinter = builder.hints_funcs;

    builder.path_begun = 0;

    zones[zone].base = dublicate_pointer(charstring_base);
    var limit = zones[zone].limit = charstring_base.pos + charstring_len;
    zones[zone].cursor = 0;
    var ip = dublicate_pointer(charstring_base);

    var error = 0;

    var x = builder.pos_x;
    var y = builder.pos_y;

    /* begin hints recording session, if any */
    if (hinter != null)
        hinter.open(hinter.hints);

    /* now execute loop */
    while (ip.pos < limit)
    {
        var op = 0;
        /********************************************************************/
        /*                                                                  */
        /* Decode operator or operand                                       */
        /*                                                                  */
        var v = ip.data[ip.pos];
        ip.pos++;
        if (v >= 32 || v == 28)
        {
            var shift = 16;
            var val = 0;

            /* this is an operand, push it on the stack */
            if (v == 28)
            {
                if (ip.pos + 1 >= limit)
                    return 3;
                val = ((ip.data[ip.pos] << 8) | ip.data[ip.pos + 1]);
                if (val > 32767)
                    val = val - 65536;
                ip.pos += 2;
            }
            else if (v < 247)
                val = v - 139;
            else if (v < 251)
            {
                if (ip.pos >= limit)
                    return 3;
                val = (v - 247) * 256 + ip.data[ip.pos] + 108;
                ip.pos++;
            }
            else if (v < 255)
            {
                if (ip.pos >= limit)
                    return 3;
                val = -(v - 251) * 256 - ip.data[ip.pos] - 108;
                ip.pos++;
            }
            else
            {
                if (ip.pos + 3 >= limit)
                    return 3;
                val = (ip.data[ip.pos] << 24 )| (ip.data[ip.pos + 1] << 16) | (ip.data[ip.po + 2] << 8) | ip.data[ip.pos + 3];
                ip.pos += 4;
                if (charstring_type == 2)
                    shift = 0;
            }
            if (decoder.top >= 48)
                return 130;

            val <<= shift;
            tops[decoder.top] = val;
            decoder.top++;
        }
        else
        {
            /* The specification says that normally arguments are to be taken */
            /* from the bottom of the stack.  However, this seems not to be   */
            /* correct, at least for Acroread 7.0.8 on GNU/Linux: It pops the */
            /* arguments similar to a PS interpreter.                         */
            var args = decoder.top;
            var num_args = args;

            /* find operator */
            op = 0;

            switch (v)
            {
                case 1:
                    op = 19;
                    break;
                case 3:
                    op = 20;
                    break;
                case 4:
                    op = 3;
                    break;
                case 5:
                    op = 4;
                    break;
                case 6:
                    op = 5;
                    break;
                case 7:
                    op = 6;
                    break;
                case 8:
                    op = 7;
                    break;
                case 9:
                    op = 53;
                    break;
                case 10:
                    op = 49;
                    break;
                case 11:
                    op = 51;
                    break;
                case 12:
                {
                    if (ip.pos >= limit)
                        return 3;
                    v = ip.data[ip.pos];
                    ip.pos++;
                    switch (v)
                    {
                        case 0:
                            op = 25;
                            break;
                        case 1: /* this is actually the Type1 vstem3 operator */
                            op = 20;
                            break;
                        case 2: /* this is actually the Type1 hstem3 operator */
                            op = 19;
                            break;
                        case 3:
                            op = 44;
                            break;
                        case 4:
                            op = 45;
                            break;
                        case 5:
                            op = 46;
                            break;
                        case 6:
                            op = 56;
                            break;
                        case 7:
                            op = 57;
                            break;
                        case 8:
                            op = 42;
                            break;
                        case 9:
                            op = 26;
                            break;
                        case 10:
                            op = 27;
                            break;
                        case 11:
                            op = 28;
                            break;
                        case 12:
                            op = 29;
                            break;
                        case 13:
                            op = 43;
                            break;
                        case 14:
                            op = 30;
                            break;
                        case 15:
                            op = 47;
                            break;
                        case 16:
                            op = 54;
                            break;
                        case 17:
                            op = 55;
                            break;
                        case 18:
                            op = 35;
                            break;
                        case 20:
                            op = 40;
                            break;
                        case 21:
                            op = 41;
                            break;
                        case 22:
                            op = 48;
                            break;
                        case 23:
                            op = 31;
                            break;
                        case 24:
                            op = 32;
                            break;
                        case 26:
                            op = 33;
                            break;
                        case 27:
                            op = 39;
                            break;
                        case 28:
                            op = 36;
                            break;
                        case 29:
                            op = 37;
                            break;
                        case 30:
                            op = 38;
                            break;
                        case 33:
                            op = 58;
                            break;
                        case 34:
                            op = 15;
                            break;
                        case 35:
                            op = 14;
                            break;
                        case 36:
                            op = 16;
                            break;
                        case 37:
                            op = 17;
                            break;
                        default:
                            break;
                    }
                    break;
                }
                case 13:
                    op = 52;
                    break;
                case 14:
                    op = 18;
                    break;
                case 16:
                    op = 34;
                    break;
                case 18:
                    op = 21;
                    break;
                case 19:
                    op = 23;
                    break;
                case 20:
                    op = 24;
                    break;
                case 21:
                    op = 1;
                    break;
                case 22:
                    op = 2;
                    break;
                case 23:
                    op = 22;
                    break;
                case 24:
                    op = 10;
                    break;
                case 25:
                    op = 11;
                    break;
                case 26:
                    op = 13;
                    break;
                case 27:
                    op = 8;
                    break;
                case 29:
                    op = 50;
                    break;
                case 30:
                    op = 12;
                    break;
                case 31:
                    op = 9;
                    break;
                default:
                    break;
            }

            if (op == 0)
                continue;

            /* check arguments */
            var req_args = cff_argument_counts[op];
            if ((req_args & 128) != 0)
            {
                if (num_args > 0 && decoder.read_width != 0)
                {
                    /* If `nominal_width' is non-zero, the number is really a      */
                    /* difference against `nominal_width'.  Else, the number here  */
                    /* is truly a width, not a difference against `nominal_width'. */
                    /* If the font does not set `nominal_width', then              */
                    /* `nominal_width' defaults to zero, and so we can set         */
                    /* `glyph_width' to `nominal_width' plus number on the stack   */
                    /* -- for either case.                                         */
                    var set_width_ok = 0;
                    switch (op)
                    {
                        case 2:
                        case 3:
                            set_width_ok = num_args & 2;
                            break;

                        case 19:
                        case 20:
                        case 21:
                        case 22:
                        case 1:
                        case 23:
                        case 24:
                            set_width_ok = num_args & 1;
                            break;

                        case 18:
                            /* If there is a width specified for endchar, we either have */
                            /* 1 argument or 5 arguments.  We like to argue.             */
                            set_width_ok = ((num_args == 5) || (num_args == 1)) ? 1 : 0;
                            break;

                        default:
                            FT_Common.set_width_ok = 0;
                            break;
                    }

                    if (set_width_ok != 0)
                    {
                        decoder.glyph_width = decoder.nominal_width + (tops[stack] >> 16);

                        if (decoder.width_only != 0)
                        {
                            /* we only want the advance width; stop here */
                            break;
                        }

                        /* Consumed an argument. */
                        num_args--;
                    }
                }

                decoder.read_width = 0;
                req_args = 0;
            }

            req_args &= 0x000F;
            if (num_args < req_args)
                return 129;
            args     -= req_args;
            num_args -= req_args;

            /* At this point, `args' points to the first argument of the  */
            /* operand in case `req_args' isn't zero.  Otherwise, we have */
            /* to adjust `args' manually.                                 */

            /* Note that we only pop arguments from the stack which we    */
            /* really need and can digest so that we can continue in case */
            /* of superfluous stack elements.                             */

            switch (op)
            {
                case 19:
                case 20:
                case 21:
                case 22:
                    /* the number of arguments is always even here */
                    if (hinter != null)
                        hinter.stems(hinter.hints, (op == 19 || op == 21) ? 1 : 0, num_args / 2, tops[args - (num_args & ~1)]);

                    decoder.num_hints += num_args / 2;
                    args = stack;
                    break;

                case 23:
                case 24:
                    /* implement vstem when needed --                        */
                    /* the specification doesn't say it, but this also works */
                    /* with the 'cntrmask' operator                          */
                    /*                                                       */
                    if (num_args > 0)
                    {
                        if (hinter != null)
                            hinter.stems(hinter.hints, 0, num_args / 2, tops[args - (num_args & ~1)]);

                        decoder.num_hints += num_args / 2;
                    }

                    /* In a valid charstring there must be at least one byte */
                    /* after `hintmask' or `cntrmask' (e.g., for a `return'  */
                    /* instruction).  Additionally, there must be space for  */
                    /* `num_hints' bits.                                     */
                    if ((ip.pos + ((decoder.num_hints + 7) >> 3)) >= limit)
                        return 3;

                    if (hinter != null)
                    {
                        if (op == 23)
                            hinter.hintmask(hinter.hints, builder.current.n_points, decoder.num_hints, ip);
                        else
                            hinter.counter(hinter.hints, decoder.num_hints, ip);
                    }

                    ip.pos += (decoder.num_hints + 7 ) >> 3;
                    args = stack;
                    break;

                case 1:
                    cff_builder_close_contour(builder);
                    builder.path_begun = 0;
                    x += tops[args - 2];
                    y += tops[args - 1];
                    args = stack;
                    break;

                case 3:
                    cff_builder_close_contour(builder);
                    builder.path_begun = 0;
                    y += tops[args - 1];
                    args = stack;
                    break;

                case 2:
                    cff_builder_close_contour(builder);
                    builder.path_begun = 0;
                    x += tops[args - 1];
                    args = stack;
                    break;

                case 4:
                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, parseInt(num_args / 2)) != 0)
                        return error;

                    if (num_args < 2)
                        return 129;

                    args -= num_args & ~1;
                    while (args < decoder.top)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 2;
                    }
                    args = stack;
                    break;

                case 5:
                case 6:
                {
                    var phase = (op == 5) ? 1 : 0;

                    if (num_args < 0)
                        return 129;

                    /* there exist subsetted fonts (found in PDFs) */
                    /* which call `hlineto' without arguments      */
                    if (num_args == 0)
                        break;

                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, num_args))
                        return error;

                    args = stack;
                    while (args < decoder.top)
                    {
                        if (phase == 1)
                            x += tops[args];
                        else
                            y += tops[args];

                        if (cff_builder_add_point1(builder, x, y) != 0)
                            return error;

                        args++;
                        phase ^= 1;
                    }
                    args = stack;
                    break;
                }

                case 7:
                {
                    if (num_args < 6)
                        return FT_Error.FT_Err_Too_Few_Arguments;

                    var nargs = num_args - num_args % 6;

                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, parseInt(nargs / 2)))
                        return error;

                    args -= nargs;
                    while (args < decoder.top)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 2];
                        y += tops[args + 3];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 4];
                        y += tops[args + 5];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 6;
                    }
                    args = stack;
                    break;
                }

                case 13:
                {
                    if (num_args < 4)
                        return 129;

                    /* if num_args isn't of the form 4n or 4n+1, */
                    /* we reduce it to 4n+1                      */
                    var nargs = num_args - num_args % 4;
                    if ((num_args - nargs) > 0)
                        nargs += 1;

                    if (cff_builder_start_point(builder, x, y) != 0)
                        return error;

                    args -= nargs;
                    if ((nargs & 1) != 0)
                    {
                        x += tops[args];
                        args++;
                        nargs--;
                    }

                    if (_cff_check_points(builder, 3 * parseInt(nargs / 4)))
                        return error;

                    while (args < decoder.top)
                    {
                        y += tops[args];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 1];
                        y += tops[args + 2];
                        cff_builder_add_point(builder, x, y, 0);
                        y += tops[args + 3];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 4;
                    }
                    args = stack;
                    break;
                }

                case 8:
                {
                    if (num_args < 4)
                        return 129;

                    /* if num_args isn't of the form 4n or 4n+1, */
                    /* we reduce it to 4n+1                      */
                    var nargs = num_args - num_args % 4;
                    if ((num_args - nargs) > 0)
                        nargs += 1;

                    if (cff_builder_start_point(builder, x, y) != 0)
                        return error;

                    args -= nargs;
                    if ((nargs & 1) != 0)
                    {
                        y += tops[args];
                        args++;
                        nargs--;
                    }

                    if (_cff_check_points(builder, 3 * parseInt(nargs / 4)))
                        return error;

                    while (args < decoder.top)
                    {
                        x += tops[args];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 1];
                        y += tops[args + 2];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 3];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 4;
                    }
                    args = stack;
                    break;
                }

                case 12:
                case 9:
                {
                    if (cff_builder_start_point(builder, x, y) != 0)
                        return error;

                    if (num_args < 4)
                        return 129;

                    /* if num_args isn't of the form 8n, 8n+1, 8n+4, or 8n+5, */
                    /* we reduce it to the largest one which fits             */
                    var nargs = num_args - num_args % 4;
                    if ((num_args - nargs) > 0)
                        nargs += 1;

                    args -= nargs;
                    if (_cff_check_points(builder, parseInt(nargs / 4) * 3))
                        return 129;

                    var phase = (op == 9) ? 1 : 0;

                    while (nargs >= 4)
                    {
                        nargs -= 4;
                        if (phase == 1)
                        {
                            x += tops[args];
                            cff_builder_add_point(builder, x, y, 0);
                            x += tops[args + 1];
                            y += tops[args + 2];
                            cff_builder_add_point(builder, x, y, 0);
                            y += tops[args + 3];
                            if (nargs == 1)
                                x += tops[args + 4];
                            cff_builder_add_point(builder, x, y, 1);
                        }
                        else
                        {
                            y += tops[args];
                            cff_builder_add_point(builder, x, y, 0);
                            x += tops[args + 1];
                            y += tops[args + 2];
                            cff_builder_add_point(builder, x, y, 0);
                            x += tops[args + 3];
                            if (nargs == 1)
                                y += tops[args + 4];
                            cff_builder_add_point(builder, x, y, 1);
                        }
                        args  += 4;
                        phase ^= 1;
                    }
                    args = stack;
                    break;
                }

                case 11:
                {
                    if (num_args < 8)
                        return 129;

                    var nargs = num_args & ~1;
                    var num_lines = parseInt((nargs - 6) / 2);

                    if (cff_builder_start_point(builder, x, y) || _cff_check_points(builder, num_lines + 3))
                        return error;

                    args -= nargs;

                    /* first, add the line segments */
                    while (num_lines > 0)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 2;
                        num_lines--;
                    }

                    /* then the curve */
                    x += tops[args];
                    y += tops[args + 1];
                    cff_builder_add_point(builder, x, y, 0);
                    x += tops[args + 2];
                    y += tops[args + 3];
                    cff_builder_add_point(builder, x, y, 0);
                    x += tops[args + 4];
                    y += tops[args + 5];
                    cff_builder_add_point(builder, x, y, 1);
                    args = stack;
                    break;
                }

                case 10:
                {
                    if ( num_args < 8 )
                        return 129;

                    var nargs  = num_args - 2;
                    nargs      = nargs - nargs % 6 + 2;
                    var num_curves = parseInt((nargs - 2) / 6);

                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, num_curves * 3 + 2))
                        return error;

                    args -= nargs;

                    /* first, add the curves */
                    while (num_curves > 0)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 2];
                        y += tops[args + 3];
                        cff_builder_add_point(builder, x, y, 0);
                        x += tops[args + 4];
                        y += tops[args + 5];
                        cff_builder_add_point(builder, x, y, 1);
                        args += 6;
                        num_curves--;
                    }

                    /* then the final line */
                    x += tops[args];
                    y += tops[args + 1];
                    cff_builder_add_point(builder, x, y, 1);
                    args = stack;

                    break;
                }

                case 16:
                {
                    /* adding five more points: 4 control points, 1 on-curve point */
                    /* -- make sure we have enough space for the start point if it */
                    /* needs to be added                                           */
                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, 6))
                        return error;

                    /* record the starting point's y position for later use */
                    var start_y = y;

                    /* first control point */
                    x += tops[args];
                    y += tops[args + 1];
                    cff_builder_add_point(builder, x, y, 0);

                    /* second control point */
                    x += tops[args + 2];
                    y += tops[args + 3];
                    cff_builder_add_point(builder, x, y, 0);

                    /* join point; on curve, with y-value the same as the last */
                    /* control point's y-value                                 */
                    x += tops[args + 4];
                    cff_builder_add_point(builder, x, y, 1);

                    /* third control point, with y-value the same as the join */
                    /* point's y-value                                        */
                    x += tops[args + 5];
                    cff_builder_add_point(builder, x, y, 0);

                    /* fourth control point */
                    x += tops[args + 6];
                    y += tops[args + 7];
                    cff_builder_add_point(builder, x, y, 0);

                    /* ending point, with y-value the same as the start   */
                    x += tops[args + 8];
                    y  = start_y;
                    cff_builder_add_point(builder, x, y, 1);

                    args = stack;
                    break;
                }

                case 15:
                {
                    /* adding six more points; 4 control points, 2 on-curve points */
                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, 6))
                        return error;

                    /* record the starting point's y-position for later use */
                    var start_y = y;

                    /* first control point */
                    x += tops[args];
                    cff_builder_add_point(builder, x, y, 0);

                    /* second control point */
                    x += tops[args + 1];
                    y += tops[args + 2];
                    cff_builder_add_point(builder, x, y, 0);

                    /* join point; on curve, with y-value the same as the last */
                    /* control point's y-value                                 */
                    x += tops[args + 3];
                    cff_builder_add_point(builder, x, y, 1);

                    /* third control point, with y-value the same as the join */
                    /* point's y-value                                        */
                    x += tops[args + 4];
                    cff_builder_add_point(builder, x, y, 0);

                    /* fourth control point */
                    x += tops[args + 5];
                    y  = start_y;
                    cff_builder_add_point(builder, x, y, 0);

                    /* ending point, with y-value the same as the start point's */
                    /* y-value -- we don't add this point, though               */
                    x += tops[args + 6];
                    cff_builder_add_point(builder, x, y, 1);

                    args = stack;
                    break;
                }

                case 17:
                {
                    /* alter use                    */
                    var dx = 0, dy = 0;   /* used in horizontal/vertical  */

                    /* adding six more points; 4 control points, 2 on-curve points */
                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, 6) != 0)
                        return error;

                    /* record the starting point's x, y position for later use */
                    var start_x = x;
                    var start_y = y;

                    /* XXX: figure out whether this is supposed to be a horizontal */
                    /*      or vertical flex; the Type 2 specification is vague... */

                    var temp = args;
                    var count = 0;

                    /* grab up to the last argument */
                    for (count = 5; count > 0; count--)
                    {
                        dx += tops[temp];
                        dy += tops[temp + 1];
                        temp += 2;
                    }

                    if (dx < 0)
                        dx = -dx;
                    if (dy < 0)
                        dy = -dy;

                    /* strange test, but here it is... */
                    var horizontal = (dx > dy) ? 1 : 0;

                    for (count = 5; count > 0; count--)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, (count == 3) ? 1 : 0);
                        args += 2;
                    }

                    /* is last operand an x- or y-delta? */
                    if (horizontal == 1)
                    {
                        x += tops[args];
                        y  = start_y;
                    }
                    else
                    {
                        x  = start_x;
                        y += tops[args];
                    }

                    cff_builder_add_point(builder, x, y, 1);

                    args = stack;
                    break;
                }

                case 14:
                {
                    if (cff_builder_start_point(builder, x, y) != 0 || _cff_check_points(builder, 6))
                        return error;

                    for (var count = 6; count > 0; count--)
                    {
                        x += tops[args];
                        y += tops[args + 1];
                        cff_builder_add_point(builder, x, y, (count == 4 || count == 1) ? 1 : 0);
                        args += 2;
                    }

                    args = stack;
                    break;
                }

                case 56:
                    error = cff_operator_seac(decoder, tops[args], tops[args + 1], tops[args + 2], (tops[args + 3] >> 16), (tops[args + 4] >> 16));

                    /* add current outline to the glyph slot */
                    FT_GlyphLoader_Add(builder.loader);
                    return error;

                case 18:
                    /* We are going to emulate the seac operator. */
                    if (num_args >= 4)
                    {
                        /* Save glyph width so that the subglyphs don't overwrite it. */
                        var glyph_width = decoder.glyph_width;

                        error = cff_operator_seac(decoder, 0, tops[args - 4], tops[args - 3], (tops[args - 2] >> 16), (tops[args - 1] >> 16));
                        decoder.glyph_width = glyph_width;
                    }
                    else
                    {
                        cff_builder_close_contour(builder);

                        /* close hints recording session */
                        if (hinter != null)
                        {
                            if (hinter.close(hinter.hints, builder.current.n_points))
                                return 3;

                            /* apply hints to the loaded glyph outline now */
                            hinter.apply(hinter.hints, builder.current, builder.hints_globals, decoder.hint_mode);
                        }

                        /* add current outline to the glyph slot */
                        FT_GlyphLoader_Add(builder.loader);
                    }

                    /* return now! */
                    return error;

                case 26:
                    if (tops[args] < 0)
                        tops[args] = -tops[args];
                    args++;
                    break;

                case 27:
                    tops[args] += tops[args + 1];
                    args++;
                    break;

                case 28:
                    tops[args] -= tops[args + 1];
                    args++;
                    break;

                case 29:
                    tops[args] = FT_DivFix(tops[args], tops[args + 1]);
                    args++;
                    break;

                case 30:
                    tops[args] = -tops[args];
                    args++;
                    break;

                case 31:
                {
                    var Rand = seed;
                    if (Rand >= 0x8000)
                        Rand++;

                    tops[args] = Rand;
                    seed = FT_MulFix(seed, 0x10000 - seed);
                    if (seed == 0)
                        seed += 0x2873;
                    args++;

                    break;
                }

                case 32:
                    topsargs[0] = FT_MulFix( args[0], args[1] );
                    args++;
                    break;

                case 33:
                    if (tops[args] > 0)
                    {
                        var count = 9;
                        var root = tops[args];
                        var new_root = 0;
                        for (;;)
                        {
                            new_root = (root + FT_DivFix(tops[args], root) + 1) >> 1;
                            if (new_root == root || count <= 0)
                                break;
                            root = new_root;
                        }
                        tops[args] = new_root;
                    }
                    else
                        tops[args] = 0;
                    args++;
                    break;

                case 35:
                    /* nothing */
                    break;

                case 36:
                {
                    var tmp = tops[args];
                    tops[args] = tops[args + 1];
                    tops[args + 1] = tmp;
                    args += 2;

                    break;
                }

                case 37:
                {
                    var idx = (tops[args] >> 16);

                    if (idx < 0)
                        idx = 0;
                    else if (idx > num_args - 2)
                        idx = num_args - 2;
                    tops[args] = tops[args-(idx + 1)];
                    args++;

                    break;
                }

                case 38:
                {
                    var count = (tops[args] >> 16);
                    var idx = (tops[args + 1] >> 16);

                    if (count <= 0)
                        count = 1;

                    args -= count;
                    if (args < stack)
                        return 129;

                    if (idx >= 0)
                    {
                        while (idx > 0)
                        {
                            var tmp = tops[args + count - 1];
                            for (var i = count - 2; i >= 0; i--)
                                tops[args + i + 1] = tops[args + i];
                            tops[args] = tmp;
                            idx--;
                        }
                    }
                    else
                    {
                        while (idx < 0)
                        {
                            var tmp = tops[args];
                            for (var i = 0; i < count - 1; i++)
                                tops[args + i] = tops[args + i + 1];
                            tops[args + count - 1] = tmp;
                            idx++;
                        }
                    }
                    args += count;

                    break;
                }

                case 39:
                    tops[args + 1] = tops[args];
                    args += 2;
                    break;

                case 40:
                {
                    var val = tops[args];
                    var idx = (tops[args + 1] >> 16);

                    if (idx >= 0 && idx < 32)
                        decoder.buildchar[idx] = val;

                    break;
                }

                case 41:
                {
                    var idx = (tops[args] >> 16);
                    var val = 0;

                    if (idx >= 0 && idx < 32)
                        val = decoder.buildchar[idx];

                    tops[args] = val;
                    args++;

                    break;
                }

                case 42:
                    return 7;

                case 43:
                    return 7;

                case 25:
                    /* this operator is deprecated and ignored by the parser */
                    break;

                case 53:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */
                    args = stack;
                    break;

                case 52:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */
                    decoder.glyph_width = decoder.nominal_width + (tops[args + 1] >> 16);

                    decoder.builder.left_bearing.x = tops[args];
                    decoder.builder.left_bearing.y = 0;

                    x = decoder.builder.pos_x + tops[args];
                    y = decoder.builder.pos_y;
                    args = stack;
                    break;

                case 57:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */
                    decoder.glyph_width = decoder.nominal_width + (tops[args + 2] >> 16);

                    decoder.builder.left_bearing.x = tops[args];
                    decoder.builder.left_bearing.y = tops[args + 1];

                    x = decoder.builder.pos_x + tops[args];
                    y = decoder.builder.pos_y + tops[args + 1];
                    args = stack;
                    break;

                case 58:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */
                    x = decoder.builder.pos_x + tops[args];
                    y = decoder.builder.pos_y + tops[args + 1];
                    args = stack;
                    break;

                case 54:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */

                    /* subsequent `pop' operands should add the arguments,       */
                    /* this is the implementation described for `unknown' other  */
                    /* subroutines in the Type1 spec.                            */
                    /*                                                           */
                    /* XXX Fix return arguments (see discussion below).          */
                    args -= 2 + (tops[args - 2] >> 16);
                    if (args < stack)
                        return 129;
                    break;

                case 55:
                    /* this is an invalid Type 2 operator; however, there        */
                    /* exist fonts which are incorrectly converted from probably */
                    /* Type 1 to CFF, and some parsers seem to accept it         */

                    /* XXX Increasing `args' is wrong: After a certain number of */
                    /* `pop's we get a stack overflow.  Reason for doing it is   */
                    /* code like this (actually found in a CFF font):            */
                    /*                                                           */
                    /*   17 1 3 callothersubr                                    */
                    /*   pop                                                     */
                    /*   callsubr                                                */
                    /*                                                           */
                    /* Since we handle `callothersubr' as a no-op, and           */
                    /* `callsubr' needs at least one argument, `pop' can't be a  */
                    /* no-op too as it basically should be.                      */
                    /*                                                           */
                    /* The right solution would be to provide real support for   */
                    /* `callothersubr' as done in `t1decode.c', however, given   */
                    /* the fact that CFF fonts with `pop' are invalid, it is     */
                    /* questionable whether it is worth the time.                */
                    args++;
                    break;

                case 44:
                {
                    var cond = (tops[args] != 0 && tops[args + 1] != 0) ? 1 : 0;

                    tops[args] = (cond == 1) ? 0x10000 : 0;
                    args++;

                    break;
                }
                case 45:
                {
                    var cond = (tops[args] != 0 || tops[args + 1]) ? 1 : 0;

                    tops[args] = (cond == 1) ? 0x10000 : 0;
                    args++;

                    break;
                }

                case 47:
                {
                    tops[args] = (tops[args] == 0) ? 0x10000 : 0;
                    args++;

                    break;
                }

                case 48:
                {
                    if (tops[args + 2] > tops[args + 3])
                        tops[args] = tops[args + 1];
                    args++;

                    break;
                }

                case 49:
                {
                    var idx = ((tops[args] >> 16) + decoder.locals_bias);

                    if (idx >= decoder.num_locals)
                        return 3;

                    if (zone >= 32)
                        return 3;

                    zones[zone].cursor = ip.pos - zones[zone].base.pos;  /* save current instruction pointer */

                    zone++;
                    zones[zone].base = dublicate_pointer(decoder.locals[idx]);
                    zones[zone].limit = (decoder.locals[idx + 1] != null) ? decoder.locals[idx + 1].pos : zones[zone].base.pos;
                    zones[zone].cursor = 0;

                    if (zones[zone].base == null || zones[zone].limit == zones[zone].base.pos)
                        return 3;

                    decoder.zone = zone;
                    ip = dublicate_pointer(zones[zone].base);
                    limit = zones[zone].limit;

                    break;
                }

                case 50:
                {
                    var idx = ((tops[args] >> 16) + decoder.globals_bias);

                    if (idx >= decoder.num_globals)
                        return 3;

                    if (zone >= 32)
                        return 3;

                    zones[zone].cursor = ip.pos - zones[zone].base.pos;  /* save current instruction pointer */

                    zone++;
                    zones[zone].base = dublicate_pointer(decoder.globals[idx]);
                    zones[zone].limit = (decoder.globals[idx + 1] != null) ? decoder.globals[idx + 1].pos : zones[zone].base.pos;
                    zones[zone].cursor = 0;

                    if (zones[zone].base == null || zones[zone].limit == zones[zone].base.pos)
                        return 3;

                    decoder.zone = zone;
                    ip = dublicate_pointer(zones[zone].base);
                    limit = zones[zone].limit;

                    break;
                }

                case 51:
                    if (decoder.zone <= 0)
                        return 3;

                    decoder.zone--;
                    zone  = decoder.zone;
                    ip = dublicate_pointer(zones[zone].base);
                    ip.pos += zones[zone].cursor;
                    limit = zones[zone].limit;
                    break;

                default:
                    return 7;
            }

            decoder.top = args;

            if (decoder.top >= 48)
                return 130;

        } /* general operator processing */

    } /* while ip < limit */

    return error;
}

function cff_slot_load(glyph, size, glyph_index, load_flags)
{
    var face = glyph.face;
    var cff  = face.extra.data;

    var font_matrix = null;
    var font_offset = null;

    var hinting = 0;
    var force_scaling = 0;

    /* in a CID-keyed font, consider `glyph_index' as a CID and map */
    /* it immediately to the real glyph_index -- if it isn't a      */
    /* subsetted font, glyph_indices and CIDs are identical, though */
    if (cff.top_font.font_dict.cid_registry != 0xFFFF && cff.charset.cids != null)
    {
        /* don't handle CID 0 (.notdef) which is directly mapped to GID 0 */
        if (glyph_index != 0)
        {
            glyph_index = cff_charset_cid_to_gindex(cff.charset, glyph_index);
            if (glyph_index == 0)
                return 6;
        }
    }
    else if (glyph_index >= cff.num_glyphs)
        return 6;

    if ((load_flags & 1024) != 0)
        load_flags |= (1 | 2);

    glyph.x_scale = 0x10000;
    glyph.y_scale = 0x10000;
    if (size != 0)
    {
        glyph.x_scale = size.metrics.x_scale;
        glyph.y_scale = size.metrics.y_scale;
    }

    var error = 0;

    //#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS

    /* try to load embedded bitmap if any              */
    /*                                                 */
    /* XXX: The convention should be emphasized in     */
    /*      the documents because it can be confusing. */
    if (size != null)
    {
        var cff_face = size.face;
        var sfnt = cff_face.sfnt;
        var stream = cff_face.stream;

        if (size.strike_index != 0xFFFFFFFF && sfnt.load_eblc != null && (load_flags & 8) == 0)
        {
            var metrics = new TT_SBit_MetricsRec();

            error = sfnt.load_sbit_image(face, size.strike_index, glyph_index, load_flags, stream, glyph.bitmap, metrics);

            if (error == 0)
            {
                glyph.outline.n_points   = 0;
                glyph.outline.n_contours = 0;

                glyph.metrics.width  = metrics.width << 6;
                glyph.metrics.height = metrics.height << 6;

                glyph.metrics.horiBearingX = metrics.horiBearingX << 6;
                glyph.metrics.horiBearingY = metrics.horiBearingY << 6;
                glyph.metrics.horiAdvance  = metrics.horiAdvance  << 6;

                glyph.metrics.vertBearingX = metrics.vertBearingX << 6;
                glyph.metrics.vertBearingY = metrics.vertBearingY << 6;
                glyph.metrics.vertAdvance  = metrics.vertAdvance  << 6;

                glyph.format = 1651078259;

                if (load_flags & 16)
                {
                    glyph.bitmap_left = metrics.vertBearingX;
                    glyph.bitmap_top  = metrics.vertBearingY;
                }
                else
                {
                    glyph.bitmap_left = metrics.horiBearingX;
                    glyph.bitmap_top  = metrics.horiBearingY;
                }
                return error;
            }
        }
    }

    //#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */

    /* return immediately if we only want the embedded bitmaps */
    if ((load_flags & 16384) != 0)
        return 6;

    /* if we have a CID subfont, use its matrix (which has already */
    /* been multiplied with the root matrix)                       */

    /* this scaling is only relevant if the PS hinter isn't active */
    if (cff.num_subfonts > 0)
    {
        var fd_index = cff_fd_select_get(cff.fd_select, glyph_index);

        if (fd_index >= cff.num_subfonts)
            fd_index = (cff.num_subfonts - 1);

        var top_upm = cff.top_font.font_dict.units_per_em;
        var sub_upm = cff.subfonts[fd_index].font_dict.units_per_em;

        font_matrix = dublicate_matrix(cff.subfonts[fd_index].font_dict.font_matrix);
        font_offset = dublicate_vector(cff.subfonts[fd_index].font_dict.font_offset);

        if (top_upm != sub_upm)
        {
            glyph.x_scale = FT_MulDiv(glyph.x_scale, top_upm, sub_upm);
            glyph.y_scale = FT_MulDiv(glyph.y_scale, top_upm, sub_upm);

            force_scaling = 1;
        }
    }
    else
    {
        font_matrix = dublicate_matrix(cff.top_font.font_dict.font_matrix);
        font_offset = dublicate_vector(cff.top_font.font_dict.font_offset);
    }

    glyph.outline.n_points   = 0;
    glyph.outline.n_contours = 0;

    hinting = ((load_flags & 1) == 0 && (load_flags & 2) == 0) ? 1 : 0;

    glyph.format = 1869968492;  /* by default */

    var decoder = new CFF_Decoder();
    cff_decoder_init(decoder, face, size, glyph, hinting, FT_LOAD_TARGET_MODE(load_flags));

    if ((load_flags & 256) != 0)
        decoder.width_only = 1;

    decoder.builder.no_recurse = ((load_flags & 1024) != 0) ? 1 : 0;

    /* now load the unscaled outline */
    var ret = cff_get_glyph_data(face, glyph_index);
    error = ret.err;
    var charstring = ret.bytes;
    var charstring_len = ret.len;

    if (error == 0)
    {
        error = cff_decoder_prepare(decoder, size, glyph_index);
        if (error == 0)
        {
            error = cff_decoder_parse_charstrings(decoder, charstring, charstring_len);
            cff_free_glyph_data(face, charstring, charstring_len);
            charstring = null;

            if (error == 0)
            {
                //#ifdef FT_CONFIG_OPTION_INCREMENTAL
                /* Control data and length may not be available for incremental */
                /* fonts.                                                       */
                if (face.internal.incremental_interface != null)
                {
                    glyph.control_data = null;
                    glyph.control_len = 0;
                }
                else //#endif /* FT_CONFIG_OPTION_INCREMENTAL */
                {
                    var csindex = cff.charstrings_index;

                    if (csindex.offsets != null)
                    {
                        glyph.control_data = dublicate_pointer(csindex.bytes);
                        glyph.control_data.pos += csindex.offsets[glyph_index] - 1;
                        glyph.control_len = charstring_len;
                    }

                }
                cff_builder_done(decoder.builder);
            }
        }
    }
    /* XXX: anything to do for broken glyph entry? */

    //#ifdef FT_CONFIG_OPTION_INCREMENTAL

    /* Incremental fonts can optionally override the metrics. */
    if (error == 0 && face.internal.incremental_interface != null && face.internal.incremental_interface.funcs.get_glyph_metrics != null)
    {
        var metrics = new FT_Incremental_MetricsRec();

        metrics.bearing_x = decoder.builder.left_bearing.x;
        metrics.bearing_y = 0;
        metrics.advance   = decoder.builder.advance.x;
        metrics.advance_v = decoder.builder.advance.y;

        error = face.internal.incremental_interface.funcs.get_glyph_metrics(face.internal.incremental_interface.object, glyph_index, 0, metrics);

        decoder.builder.left_bearing.x = metrics.bearing_x;
        decoder.builder.advance.x      = metrics.advance;
        decoder.builder.advance.y      = metrics.advance_v;
    }

    //#endif /* FT_CONFIG_OPTION_INCREMENTAL */

    if (error == 0)
    {
        /* Now, set the metrics -- this is rather simple, as   */
        /* the left side bearing is the xMin, and the top side */
        /* bearing the yMax.                                   */

        /* For composite glyphs, return only left side bearing and */
        /* advance width.                                          */
        if ((load_flags & 1024) != 0)
        {
            var internal = glyph.internal;

            glyph.metrics.horiBearingX = decoder.builder.left_bearing.x;
            glyph.metrics.horiAdvance  = decoder.glyph_width;
            internal.glyph_matrix           = dublicate_matrix(font_matrix);
            internal.glyph_delta            = dublicate_matrix(font_offset);
            internal.glyph_transformed      = 1;
        }
        else
        {
            var cbox = new FT_BBox();
            var metrics = glyph.metrics;
            var advance = new FT_Vector();
            var has_vertical_info = 0;

            /* copy the _unscaled_ advance width */
            metrics.horiAdvance = decoder.glyph_width;
            glyph.linearHoriAdvance = decoder.glyph_width;
            glyph.internal.glyph_transformed = 0;

            has_vertical_info = (face.vertical_info && face.vertical.number_Of_VMetrics > 0 && face.vertical.long_metrics) ? 1 : 0;

            /* get the vertical metrics from the vtmx table if we have one */
            if (has_vertical_info == 1)
            {
                var _ret = face.sfnt.get_metrics(face, 1, glyph_index);
                metrics.vertBearingY = _ret.bearing;
                metrics.vertAdvance  = _ret.advance;
            }
            else
            {
                /* make up vertical ones */
                if (face.os2.version != 0xFFFF)
                    metrics.vertAdvance = (face.os2.sTypoAscender - face.os2.sTypoDescender);
                else
                    metrics.vertAdvance = (face.horizontal.Ascender - face.horizontal.Descender);
            }

            glyph.linearVertAdvance = metrics.vertAdvance;

            glyph.format = 1869968492;

            glyph.outline.flags = 0;
            if (size != null && size.metrics.y_ppem < 24)
                glyph.outline.flags |= 256;

            glyph.outline.flags |= 4;

            if (!(font_matrix.xx == 0x10000 && font_matrix.yy == 0x10000 && font_matrix.xy == 0 && font_matrix.yx == 0))
                FT_Outline_Transform(glyph.outline, font_matrix);

            if (!(font_offset.x == 0 && font_offset.y == 0))
                FT_Outline_Translate(glyph.outline, font_offset.x, font_offset.y);

            advance.x = metrics.horiAdvance;
            advance.y = 0;
            FT_Vector_Transform(advance, font_matrix);
            metrics.horiAdvance = advance.x + font_offset.x;

            advance.x = 0;
            advance.y = metrics.vertAdvance;
            FT_Vector_Transform(advance, font_matrix);
            metrics.vertAdvance = advance.y + font_offset.y;

            if ((load_flags & 1) == 0 || force_scaling == 1)
            {
                /* scale the outline and the metrics */
                var cur = glyph.outline;
                var vecs = cur.points;
                var _vec = 0;
                var x_scale = glyph.x_scale;
                var y_scale = glyph.y_scale;

                /* First of all, scale the points */
                if (hinting == 0 || decoder.builder.hints_funcs == null)
                    for (var n = cur.n_points; n > 0; n--, _vec++)
                    {
                        vecs[_vec].x = FT_MulFix(vecs[_vec].x, x_scale);
                        vecs[_vec].y = FT_MulFix(vecs[_vec].y, y_scale);
                    }

                /* Then scale the metrics */
                metrics.horiAdvance = FT_MulFix(metrics.horiAdvance, x_scale);
                metrics.vertAdvance = FT_MulFix(metrics.vertAdvance, y_scale);
            }

            /* compute the other metrics */
            FT_Outline_Get_CBox(glyph.outline, cbox);

            metrics.width = cbox.xMax - cbox.xMin;
            metrics.height = cbox.yMax - cbox.yMin;

            metrics.horiBearingX = cbox.xMin;
            metrics.horiBearingY = cbox.yMax;

            if (has_vertical_info == 1)
                metrics.vertBearingX = metrics.horiBearingX - parseInt(metrics.horiAdvance / 2);
            else
            {
                if ((load_flags & 16) != 0)
                    ft_synthesize_vertical_metrics(metrics, metrics.vertAdvance);
            }
        }
    }

    return error;
}

/******************************************************************************/
// driver
/******************************************************************************/
function cff_get_kerning(face, left_glyph, right_glyph, kerning)
{
    var sfnt = face.sfnt;

    kerning.x = 0;
    kerning.y = 0;

    if (sfnt != null)
        kerning.x = sfnt.get_kerning(face, left_glyph, right_glyph);

    return 0;
}

function cff_Load_Glyph(slot, cffsize, glyph_index, load_flags)
{
    var size = cffsize;
    if (slot == null)
        return 37;

    /* check whether we want a scaled outline or bitmap */
    if (size == null)
        load_flags |= 1 | 2;

    /* reset the size object if necessary */
    if (load_flags & 1)
        size = null;

    if (size != null)
    {
        /* these two objects must have the same parent */
        if (cffsize.face != slot.face)
            return 35;
    }

    /* now load the glyph outline if necessary */
    var error = cff_slot_load(slot, size, glyph_index, load_flags);

    /* force drop-out mode to 2 - irrelevant now */
    /* slot->outline.dropout_mode = 2; */
    return error;
}

function cff_get_advances(face, start, count, flags, advances)
{
    var slot = face.glyph;
    var error = 0;
    flags |= 256;

    for (var nn = 0; nn < count; nn++)
    {
        error = cff_Load_Glyph(slot, face.size, start + nn, flags);
        if (error )
            break;

        advances[nn] = (flags & 16) ? slot.linearVertAdvance : slot.linearHoriAdvance;
    }

    return error;
}

function cff_get_glyph_name(face, glyph_index, buffer, buffer_max)
{
    var font = face.extra.data;

    if (font.psnames == null)
        return 2;

    /* first, locate the sid in the charset table */
    var sid = font.charset.sids[glyph_index];

    /* now, lookup the name itself */
    var gname = cff_index_get_sid_string(font, sid);

    if (gname != null)
        _mem_strcpyn1(buffer, gname, buffer_max);
        
    return 0;
}

function cff_get_name_index(face, glyph_name)
{
    var cff = face.extra.data;
    var charset = cf.charset;

    var psnames = FT_FACE_FIND_GLOBAL_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_CMAPS);
    if (psnames == null)
        return 0;

    var sid = 0;
    var name = "";
    for (var i = 0; i < cff.num_glyphs; i++)
    {
        sid = charset.sids[i];

        if (sid > 390)
            name = cff_index_get_string(cff, sid - 391);
        else
            name = psnames.adobe_std_strings(sid);

        if (name == null || name == "")
            continue;

        if (glyph_name == name)
            return i;
    }
    return 0;
}

var cff_service_glyph_dict = new FT_Service_GlyphDictRec(cff_get_glyph_name, cff_get_name_index);

function cff_ps_has_glyph_names(face)
{
    return ((face.face_flags & 512) > 0) ? 1 : 0;
}

function cff_ps_get_font_info(face)
{
    var cff = face.extra.data;
    var font_info = null;
    if (cff != null && cff.font_info == null)
    {
        var dict = cff.top_font.font_dict;
        font_info = new PS_FontInfoRec();

        font_info.version = cff_index_get_sid_string(cff, dict.version);
        font_info.notice = cff_index_get_sid_string(cff, dict.notice);
        font_info.full_name = cff_index_get_sid_string(cff, dict.full_name);
        font_info.family_name = cff_index_get_sid_string(cff, dict.family_name);
        font_info.weight = cff_index_get_sid_string(cff, dict.weight);

        font_info.italic_angle        = dict.italic_angle;
        font_info.is_fixed_pitch      = dict.is_fixed_pitch;
        font_info.underline_position  = dict.underline_position;
        font_info.underline_thickness = dict.underline_thickness;

        cff.font_info = font_info;
    }
    return { err : 0, font_info : font_info };
}

var cff_service_ps_info = new FT_Service_PsInfoRec(cff_ps_get_font_info, null, cff_ps_has_glyph_names, null, null);

function cff_get_ps_name(face)
{
    return face.extra.data.font_name;
}

var cff_service_ps_name = new FT_Service_PsFontNameRec(cff_get_ps_name);

function cff_get_cmap_info(charmap, cmap_info)
{
    var cmap = charmap.cmap;
    var library = __FT_CMapRec(cmap).charmap.face.driver.library;

    cmap_info.language = 0;
    cmap_info.format = 0;

    var error = 0;
    if (cmap.clazz != FT_CFF_CMAP_ENCODING_CLASS_REC_GET && cmap.clazz != FT_CFF_CMAP_UNICODE_CLASS_REC_GET)
    {
        var sfnt = library.FT_Get_Module("sfnt");
        var service = ft_module_get_service(sfnt, FT_SERVICE_ID_TT_CMAP);

        if (service != null && service.get_cmap_info != null)
            error = service.get_cmap_info(charmap, cmap_info);
    }
    return error;
}

var cff_service_get_cmap_info = new FT_Service_TTCMapsRec(cff_get_cmap_info);

function cff_get_ros(face, registry, ordering, supplement)
{
    var ret = {err : 0, registry : "", ordering : "", supplement : 0};
    var cff = face.extra.data;

    if (cff != null)
    {
        var dict = cff.top_font.font_dict;

        if (dict.cid_registry == 0xFFFF)
        {
            ret.err = 6;
            return ret;
        }

        if (cff.registry == null)
            cff.registry = cff_index_get_sid_string(cff, dict.cid_registry);
        ret.registry = cff.registry;

        if (cff.ordering == null)
            cff.ordering = cff_index_get_sid_string(cff, dict.cid_ordering);
        ret.ordering = cff.ordering;

        /*
         * XXX: According to Adobe TechNote #5176, the supplement in CFF
         *      can be a real number. We truncate it to fit public API
         *      since freetype-2.3.6.
         */
        ret.supplement = dict.cid_supplement;
    }

    return error;
}

function cff_get_is_cid(face)
{
    var ret = {err : 0, is_cid : 0};
    var cff = face.extra.data;

    if (cff != null)
    {
        var dict = cff.top_font.font_dict;

        if (dict.cid_registry != 0xFFFF)
            ret.is_cid = 1;
    }

    return ret;
}

function cff_get_cid_from_glyph_index(face, glyph_index)
{
    var ret = { err : 0, cid : 0};
    var cff = face.extra.data;

    if (cff != null)
    {
        var dict = cff.top_font.font_dict;

        if (dict.cid_registry == 0xFFFF || glyph_index > cff.num_glyphs)
        {
            ret.err = 6;
            return ret;
        }

        ret.cid = cff.charset.sids[glyph_index];
    }
    return ret;
}

var cff_service_cid_info = new FT_Service_CIDRec(cff_get_ros, cff_get_is_cid, cff_get_cid_from_glyph_index);

var FT_CFF_SERVICE_PS_INFO_GET         = cff_service_ps_info;
var FT_CFF_SERVICE_GLYPH_DICT_GET      = cff_service_glyph_dict;
var FT_CFF_SERVICE_PS_NAME_GET         = cff_service_ps_name;
var FT_CFF_SERVICE_GET_CMAP_INFO_GET   = cff_service_get_cmap_info;
var FT_CFF_SERVICE_CID_INFO_GET        = cff_service_cid_info;
var FT_CFF_CMAP_ENCODING_CLASS_REC_GET = cff_cmap_encoding_class_rec;
var FT_CFF_CMAP_UNICODE_CLASS_REC_GET  = cff_cmap_unicode_class_rec;
var FT_CFF_FIELD_HANDLERS_GET          = cff_field_handlers;

var cff_services = new Array(6);
cff_services[0] = new FT_ServiceDescRec(FT_SERVICE_ID_XF86_NAME,FT_XF86_FORMAT_CFF);
cff_services[1] = new FT_ServiceDescRec(FT_SERVICE_ID_POSTSCRIPT_INFO,FT_CFF_SERVICE_PS_INFO_GET);
cff_services[2] = new FT_ServiceDescRec(FT_SERVICE_ID_POSTSCRIPT_FONT_NAME,FT_CFF_SERVICE_PS_NAME_GET);
cff_services[3] = new FT_ServiceDescRec(FT_SERVICE_ID_GLYPH_DICT,FT_CFF_SERVICE_GLYPH_DICT_GET);
cff_services[4] = new FT_ServiceDescRec(FT_SERVICE_ID_TT_CMAP,FT_CFF_SERVICE_GET_CMAP_INFO_GET);
cff_services[5] = new FT_ServiceDescRec(FT_SERVICE_ID_CID,FT_CFF_SERVICE_CID_INFO_GET);

var FT_CFF_SERVICES_GET = cff_services;

function cff_get_interface(driver, module_interface)
{
    var result = ft_service_list_lookup(FT_CFF_SERVICES_GET, module_interface);
    if (result != null)
        return result;

    if (driver == null)
        return null;

    /* we pass our request to the `sfnt' module */
    var sfnt = driver.library.FT_Get_Module("sfnt");
    if (sfnt == null)
        return null;

    return sfnt.clazz.get_interface(sfnt, module_interface);
}

function CFF_Driver_Class()
{
    this.flags = 0x501;
    this.name = "cff";
    this.version = 0x10000;
    this.requires = 0x20000;

    this.module_interface = null;

    this.init = cff_driver_init;
    this.done = cff_driver_done;
    this.get_interface = cff_get_interface;

    this.face_object_size = 0;
    this.size_object_size = 0;
    this.slot_object_size = 0;

    this.init_face = cff_face_init;
    this.done_face = cff_face_done;

    this.init_size = cff_size_init;
    this.done_size = cff_size_done;

    this.init_slot = cff_slot_init;
    this.done_slot = cff_slot_done;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.set_char_sizes = ft_stub_set_char_sizes;
    this.set_pixel_sizes = ft_stub_set_pixel_sizes;
    //#endif

    this.load_glyph = cff_Load_Glyph;

    this.get_kerning = cff_get_kerning;
    this.attach_file = null;
    this.get_advances = cff_get_advances;

    this.request_size = cff_size_request;
    this.select_size = cff_size_select;
}

function CFF_Driver()
{
    this.clazz = null;      // FT_Module_Class
    this.library = null;    // FT_Library
    this.memory = null;     // FT_Memory
    this.generic = null;    // FT_Generic

    this.clazz = new CFF_Driver_Class();
    this.faces_list = [];
    this.extensions = null;
    this.glyph_loader = null;

    this.extension_component = null;

    this.open_face = function(stream, face_index)
    {
        FT_Error = 0;
        var face = new TT_Face();
        var internal = new FT_Face_Internal();

        face.driver = this;
        face.memory = this.memory;
        face.stream = stream;

        //#ifdef FT_CONFIG_OPTION_INCREMENTAL
        face.internal = internal;
        face.internal.incremental_interface = null;
        //#endif

        var err1 = this.clazz.init_face(stream, face, face_index);

        if (err1 != 0)
        {
            face = null;
            FT_Error = err1;
            return null;
        }

        var err2 = find_unicode_charmap(face);
        if (err2 != 0 && err2 != 38)
        {
            face = null;
            FT_Error = err2;
            return null;
        }

		FT_Error = 0;
        return face;
    }
}

function create_cff_driver(library)
{
    var driver = new CFF_Driver();
    driver.library = library;
    driver.memory = library.Memory;

    return driver;
}
/******************************************************************************/
// afm
/******************************************************************************/
function T1_Done_Metrics(memory, fi)
{
    fi.KernPairs = null;
    fi.NumKernPair = 0;

    fi.TrackKerns = null;
    fi.NumTrackKern = 0;
}

function t1_get_index(name, len, user_data)
{
    var type1 = user_data;

    /* PS string/name length must be < 16-bit */
    if (len > 0xFFFF)
        return 0;

    for (var n = 0; n < type1.num_glyphs; n++)
    {
        var gname = type1.glyph_names[n];

        if (gname == name)
            return n;
    }

    return 0;
}

function compare_kern_pairs(a, b)
{
    var index1 = (a.index1 << 16) | a.index2;
    var index2 = (b.index2 << 16) | b.index2;

    if (index1 > index2)
        return 1;
    else if (index1 < index2)
        return -1;
    else
        return 0;
}

function T1_Read_PFM(t1_face, stream, fi)
{
    var error = 0;
    var start = new CPointer();
    start.data = stream.data;
    start.pos = stream.cur;

    var limit = stream.limit;
    var p = dublicate_pointer(start);

    /* Figure out how long the width table is.          */
    /* This info is a little-endian short at offset 99. */
    p.pos = start.pos + 99;
    if (p.pos + 2 > limit)
        return 3;

    var width_table_length = FT_PEEK_USHORT_LE(p);

    p.pos += 18 + width_table_length;
    if (p.pos + 0x12 > limit || FT_PEEK_USHORT_LE(p) < 0x12)
        return error;

    /* Kerning offset is 14 bytes from start of extensions table. */
    p.pos += 14;
    p.pos = start.pos + FT_PEEK_ULONG_LE(p);

    if (p.pos == start.pos)
        return error;

    if (p.pos + 2 > limit)
        return 2;

    fi.NumKernPair = FT_PEEK_USHORT_LE(p);
    p.pos += 2;
    if (p.pos + 4 * fi.NumKernPair > limit)
        return 3;

    /* Actually, kerning pairs are simply optional! */
    if (fi.NumKernPair == 0)
        return error;

    fi.KernPairs = new Array(fi.NumKernPair);
    for (var i = 0; i < fi.KernPairs; i++)
    {
        fi.KernPairs[i] = new AFM_KernPairRec();
    }

    /* now, read each kern pair */
    var kp = 0;
    limit = p.pos + 4 * fi.NumKernPair;

    /* PFM kerning data are stored by encoding rather than glyph index, */
    /* so find the PostScript charmap of this font and install it       */
    /* temporarily.  If we find no PostScript charmap, then just use    */
    /* the default and hope it is the right one.                        */
    var oldcharmap = t1_face.charmap;
    var charmap = null;

    for (var n = 0; n < t1_face.num_charmaps; n++)
    {
        charmap = t1_face.charmaps[n];
        /* check against PostScript pseudo platform */
        if (__FT_CharmapRec(charmap).platform_id == 7)
        {
            error = FT_Set_Charmap(t1_face, charmap);
            if (error)
            {
                fi.KernPairs = null;
                return error;
            }
            break;
        }
    }

    /* Kerning info is stored as:             */
    /*                                        */
    /*   encoding of first glyph (1 byte)     */
    /*   encoding of second glyph (1 byte)    */
    /*   offset (little-endian short)         */
    for (; p.pos < limit; p.pos += 4)
    {
        var _t = fi.KernPairs[kp];

        _t.index1 = FT_Get_Char_Index(t1_face, p.data[p.pos]);
        _t.index2 = FT_Get_Char_Index(t1_face, p.data[p.data + 1]);

        p.pos += 2;
        _t.x = FT_PEEK_SHORT_LE(p);
        p.pos -= 2;
        _t.y = 0;

        kp++;
    }

    if (oldcharmap != null)
        error = FT_Set_Charmap(t1_face, oldcharmap);

    if (error != 0)
    {
        fi.KernPairs = null;
        return error;
    }

    /* now, sort the kern pairs according to their glyph indices */
    ft_qsort(fi.KernPairs, fi.NumKernPair, compare_kern_pairs);

    return error;
}

function T1_Read_Metrics(t1_face, stream)
{
    var parser = new AFM_ParserRec();

    var fi = new AFM_FontInfoRec();
    var t1_font = t1_face.type1;

    var error = stream.EnterFrame(stream.size);
    
    fi.FontBBox = dublicate_bbox(t1_font.font_bbox);
    fi.Ascender  = t1_font.font_bbox.yMax;
    fi.Descender = t1_font.font_bbox.yMin;

    var psaux = t1_face.psaux;
    if (psaux != null && psaux.afm_parser_funcs != null)
    {
        var cur = new CPointer();
        cur.pos = stream.cur;
        cur.data = stream.data;
        error = psaux.afm_parser_funcs.init(parser, g_memory, cur, stream.size);

        if (error == 0)
        {
            parser.FontInfo  = fi;
            parser.get_index = t1_get_index;
            parser.user_data = t1_face.type1;

            error = psaux.afm_parser_funcs.parse(parser);
            psaux.afm_parser_funcs.done(parser);
        }
    }

    if (error == 2)
    {
        var start = new CPointer();
        start.pos = stream.cur;
        start.data = stream.data;

        /* MS Windows allows versions up to 0x3FF without complaining */
        if (stream.size > 6 && start.data[start.pos + 1] < 4)
        {
            start.pos += 2;
            if (FT_PEEK_ULONG_LE(start) == stream.size)
            {
                error = T1_Read_PFM(t1_face, stream, fi);
            }
        }
    }

    if (error == 0)
    {
        t1_font.font_bbox = dublicate_bbox(fi.FontBBox);

        t1_face.bbox.xMin = fi.FontBBox.xMin >> 16;
        t1_face.bbox.yMin = fi.FontBBox.yMin >> 16;
        /* no `U' suffix here to 0xFFFF! */
        t1_face.bbox.xMax = (fi.FontBBox.xMax + 0xFFFF) >> 16;
        t1_face.bbox.yMax = (fi.FontBBox.yMax + 0xFFFF) >> 16;

        /* no `U' suffix here to 0x8000! */
        t1_face.ascender  = ((fi.Ascender  + 0x8000) >> 16);
        t1_face.descender = ((fi.Descender + 0x8000) >> 16);

        if (fi.NumKernPair == 0)
        {
            t1_face.face_flags |= 64;
            t1_face.afm_data = fi;
            fi = null;
        }
    }

    stream.ExitFrame();

    if (fi != null)
    {
        T1_Done_Metrics(g_memory, fi);
        fi = null;
    }
    return error;
}

function T1_Get_Kerning(fi, glyph1, glyph2)
{
    var kerning = new FT_Vector();
    var min = 0;
    var mid = 0;
    var max = fi.NumKernPair - 1;
    var idx = (glyph1 << 16) | glyph2;

    /* simple binary search */
    var pairs = fi.KernPairs;

    while (min <= max)
    {
        mid  = min + (max - min) / 2;
        var midi = (pairs[mid].index1) << 16 | pairs[mid].index2;

        if (midi == idx)
        {
            kerning.x = mid.x;
            kerning.y = mid.y;

            return kerning;
        }

        if (midi < idx)
            min = mid + 1;
        else
            max = mid - 1;
    }

    kerning.x = 0;
    kerning.y = 0;

    return kerning;
}

function T1_Get_Track_Kerning(face, ptsize, degree, kerning)
{
    var fi = face.afm_data;
    var ret = { err: 6, kerning: kerning };

    if (fi == null)
    {
        ret.err = 6;
        return ret;
    }

    var count = fi.NumTrackKern;
    for (var i = 0; i < count; i++)
    {
        var tk = fi.TrackKerns[i];

        if (tk.degree != degree)
            continue;

        if (ptsize < tk.min_ptsize)
        {
            ret.kerning = tk.min_kern;
        }
        else if (ptsize > tk.max_ptsize)
        {
            ret.kerning = tk.max_kern;
        }
        else
        {
            ret.kerning = FT_MulDiv(ptsize - tk.min_ptsize, tk.max_kern - tk.min_kern, tk.max_ptsize - tk.min_ptsize) + tk.min_kern;
        }
    }

    return ret;
}

/******************************************************************************/
// gload
/******************************************************************************/
function T1_Parse_Glyph_And_Get_Char_String(decoder, glyph_index, char_string)
{
    var face = decoder.builder.face;
    var type1 = face.type1;
    var error = 0;

    decoder.font_matrix = dublicate_matrix(type1.font_matrix);
    decoder.font_offset = dublicate_vector(type1.font_offset);

    char_string.pointer = dublicate_pointer(type1.charstrings[glyph_index]);
    char_string.length  = type1.charstrings_len[glyph_index];

    if (error == 0)
        error = decoder.funcs.parse_charstrings(decoder, char_string.pointer, char_string.length);

    return error;
}

function T1_Parse_Glyph(decoder, glyph_index)
{
    var glyph_data = new FT_Data();
    var error = T1_Parse_Glyph_And_Get_Char_String(decoder, glyph_index, glyph_data);
    return error;
}

function T1_Compute_Max_Advance(face)
{
    var ret = { err : 0, max_advance : 0 };

    var type1 = face.type1;
    var psaux = face.psaux;

    /* initialize load decoder */
    var decoder = new T1_DecoderRec();
    
    ret.err = psaux.t1_decoder_funcs.init(decoder, face, null, null, type1.glyph_names, face.blend, 0, 0, T1_Parse_Glyph);
    if (ret.err != 0)
        return ret;

        decoder.builder.metrics_only = 1;
    decoder.builder.load_points  = 0;

    decoder.num_subrs     = type1.num_subrs;
    decoder.subrs         = type1.subrs;
    decoder.subrs_len     = type1.subrs_len;

    decoder.buildchar     = face.buildchar;
    decoder.len_buildchar = face.len_buildchar;

    /* for each glyph, parse the glyph charstring and extract */
    /* the advance width                                      */
    var _count = type1.num_glyphs;
    for (var glyph_index = 0; glyph_index < _count; glyph_index++)
    {
        if (glyph_index == 342)
        {
            var tt = 0;
            tt += 5;
            tt -= 89;
            tt += 8;
        }

        /* now get load the unscaled outline */
        ret.err = T1_Parse_Glyph(decoder, glyph_index);
        if (glyph_index == 0 || decoder.builder.advance.x > ret.max_advance)
            ret.max_advance = decoder.builder.advance.x;
    }

    psaux.t1_decoder_funcs.done(decoder);
    ret.err = 0;
    return ret;
}

function T1_Get_Advances(face, first, count, load_flags, advances)
{
    if (load_flags & 16)
    {
        for ( nn = 0; nn < count; nn++ )
            advances[nn] = 0;

        return 0;
    }

    var decoder = new T1_DecoderRec();
    var psaux = face.psaux;
    var type1 = face.type1;

    var error = psaux.t1_decoder_funcs.init(decoder, face, null, null, type1.glyph_names, face.blend, 0, 0, T1_Parse_Glyph);
    if (error != 0)
        return error;

    decoder.builder.metrics_only = 1;
    decoder.builder.load_points  = 0;

    decoder.num_subrs = type1.num_subrs;
    decoder.subrs     = type1.subrs;
    decoder.subrs_len = type1.subrs_len;

    decoder.buildchar     = face.buildchar;
    decoder.len_buildchar = face.len_buildchar;

    for (var nn = 0; nn < count; nn++)
    {
        error = T1_Parse_Glyph(decoder, first + nn);
        if (error == 0)
            advances[nn] = FT_RoundFix(decoder.builder.advance.x) >> 16;
        else
            advances[nn] = 0;
    }

    return 0;
}

function T1_Load_Glyph(_glyph, size, glyph_index, load_flags)
{
    var glyph = _glyph.base_root;

    var decoder = new T1_DecoderRec();
    var face = _glyph.face;
    var type1 = face.type1;
    var psaux = face.psaux;
    var decoder_funcs = psaux.t1_decoder_funcs;

    var glyph_data = new FT_Data();
    var must_finish_decoder = 0;

    if (glyph_index >= face.num_glyphs)
        return 6;

    if (load_flags & 1024)
        load_flags |= (1 | 2);

    if (size != null)
    {
        glyph.x_scale = size.metrics.x_scale;
        glyph.y_scale = size.metrics.y_scale;
    }
    else
    {
        glyph.x_scale = 0x10000;
        glyph.y_scale = 0x10000;
    }

    _glyph.outline.n_points   = 0;
    _glyph.outline.n_contours = 0;

    var hinting = ((load_flags & 1) == 0 && (load_flags & 2) == 0);
    _glyph.format = 1869968492;

    var error = decoder_funcs.init(decoder, face, size, glyph, type1.glyph_names, face.blend, hinting, FT_LOAD_TARGET_MODE(load_flags), T1_Parse_Glyph);
    if (error != 0)
        return error;

    must_finish_decoder = 1;

    decoder.builder.no_recurse = ((load_flags & 1024) != 0) ? 1 : 0;

    decoder.num_subrs     = type1.num_subrs;
    decoder.subrs         = type1.subrs;
    decoder.subrs_len     = type1.subrs_len;

    decoder.buildchar     = face.buildchar;
    decoder.len_buildchar = face.len_buildchar;

    /* now load the unscaled outline */
    error = T1_Parse_Glyph_And_Get_Char_String(decoder, glyph_index, glyph_data);
    if (error != 0)
    {
        decoder_funcs.done(decoder);
        return error;
    }

    var font_matrix = dublicate_matrix(decoder.font_matrix);
    var font_offset = dublicate_vector(decoder.font_offset);

    /* save new glyph tables */
    decoder_funcs.done(decoder);
    
    must_finish_decoder = 0;

    /* now, set the metrics -- this is rather simple, as   */
    /* the left side bearing is the xMin, and the top side */
    /* bearing the yMax                                    */
    if (error == 0)
    {
        glyph.root.outline.flags &= 1;
        glyph.root.outline.flags |= 4;

        /* for composite glyphs, return only left side bearing and */
        /* advance width                                           */
        if ((load_flags & 1024) != 0)
        {
            var internal = glyph.root.internal;

            glyph.root.metrics.horiBearingX = FT_RoundFix(decoder.builder.left_bearing.x) >> 16;
            glyph.root.metrics.horiAdvance = FT_RoundFix(decoder.builder.advance.x);

            internal.glyph_matrix      = dublicate_matrix(font_matrix);
            internal.glyph_delta       = dublicate_vector(font_offset);
            internal.glyph_transformed = 1;
        }
        else
        {
            var cbox = new FT_BBox();
            var metrics = glyph.root.metrics;
            var advance = new FT_Vector();

            /* copy the _unscaled_ advance width */
            metrics.horiAdvance = FT_RoundFix(decoder.builder.advance.x) >> 16;
            glyph.root.linearHoriAdvance = FT_RoundFix(decoder.builder.advance.x) >> 16;
            glyph.root.internal.glyph_transformed = 0;

            if ((load_flags & 16) != 0)
            {
                /* make up vertical ones */
                metrics.vertAdvance = (face.type1.font_bbox.yMax - face.type1.font_bbox.yMin) >> 16;
                glyph.root.linearVertAdvance = metrics.vertAdvance;
            }
            else
            {
                metrics.vertAdvance = FT_RoundFix(decoder.builder.advance.y) >> 16;
                glyph.root.linearVertAdvance = FT_RoundFix(decoder.builder.advance.y) >> 16;
            }

            glyph.root.format = 1869968492;

            if (size != null && size.metrics.y_ppem < 24)
                glyph.root.outline.flags |= 256;

            /* apply the font matrix, if any */
            if (font_matrix.xx != 0x10000 || font_matrix.yy != font_matrix.xx || font_matrix.xy != 0 || font_matrix.yx != 0)
                FT_Outline_Transform(glyph.root.outline, font_matrix);

            if (font_offset.x || font_offset.y)
                FT_Outline_Translate(glyph.root.outline, font_offset.x, font_offset.y);

            advance.x = metrics.horiAdvance;
            advance.y = 0;
            FT_Vector_Transform(advance, font_matrix);
            metrics.horiAdvance = advance.x + font_offset.x;
            advance.x = 0;
            advance.y = metrics.vertAdvance;
            FT_Vector_Transform(advance, font_matrix);
            metrics.vertAdvance = advance.y + font_offset.y;

            if ((load_flags & 1) == 0)
            {
                /* scale the outline and the metrics */
                var cur_p_c = decoder.builder.base.n_points;
                var points = decoder.builder.base.points;
                var vec = 0;
                var x_scale = glyph.x_scale;
                var y_scale = glyph.y_scale;


                /* First of all, scale the points, if we are not hinting */
                if (hinting == 0 || decoder.builder.hints_funcs == null)
                {
                    for (var n = cur_p_c; n > 0; n--, vec++)
                    {
                        points[vec].x = FT_MulFix(points[vec].x, x_scale);
                        points[vec].y = FT_MulFix(points[vec].y, y_scale);
                    }
                }

                /* Then scale the metrics */
                metrics.horiAdvance = FT_MulFix(metrics.horiAdvance, x_scale);
                metrics.vertAdvance = FT_MulFix(metrics.vertAdvance, y_scale);
            }

            /* compute the other metrics */
            FT_Outline_Get_CBox(glyph.root.outline, cbox);

            metrics.width  = cbox.xMax - cbox.xMin;
            metrics.height = cbox.yMax - cbox.yMin;

            metrics.horiBearingX = cbox.xMin;
            metrics.horiBearingY = cbox.yMax;

            if ((load_flags & 16) != 0)
            {
                /* make up vertical ones */
                ft_synthesize_vertical_metrics(metrics, metrics.vertAdvance);
            }
        }

        /* Set control data to the glyph charstrings.  Note that this is */
        /* _not_ zero-terminated.                                        */
        glyph.root.control_data = dublicate_pointer(glyph_data.pointer);
        glyph.root.control_len  = glyph_data.length;
    }

    return error;
}

/******************************************************************************/
// parse
/******************************************************************************/
function T1_ParserRec()
{
    this.root = new PS_ParserRec();
    this.stream = null;

    this.base_dict = null;
    this.base_len = 0;

    this.private_dict = null;
    this.private_len = 0;

    this.in_pfb = 0;
    this.in_memory = 0;
    this.single_block = 0;

    this.clear = function()
    {
        this.root.clear();
        this.stream = null;

        this.base_dict = null;
        this.base_len = 0;

        this.private_dict = null;
        this.private_len = 0;

        this.in_pfb = 0;
        this.in_memory = 0;
        this.single_block = 0;
    }
}

function read_pfb_tag(stream)
{
    var ret = { tag: 0, size: 0, err: 0 };

    var tag = stream.ReadUShort();
    ret.err = FT_Error;
    if (ret.err == 0)
    {
        if (tag == 0x8001 || tag == 0x8002)
        {
            var size = stream.ReadULongLE();
            ret.err = FT_Error;
            if (ret.err == 0)
                ret.size = size;
        }
        ret.tag = tag;
    }
    return ret;
}

function check_type1_format(stream, header_string, header_length)
{
    var error = stream.Seek(0);
    if (error != 0)
        return error;


    var ret = read_pfb_tag(stream);
    if (ret.err != 0)
        return ret.err;

    /* We assume that the first segment in a PFB is always encoded as   */
    /* text.  This might be wrong (and the specification doesn't insist */
    /* on that), but we have never seen a counterexample.               */
    if (ret.tag != 0x8001 && 0 != stream.Seek(0))
        return error;

    error = stream.EnterFrame(header_length);
    if (error == 0)
    {
        for (var i = 0; i < header_length; i++)
        {
            if (header_string.charCodeAt(i) != stream.data[stream.cur + i])
                return 2;
        }

        stream.ExitFrame();
    }
    return error;
}

function T1_New_Parser(parser, stream, memory, psaux)
{
    psaux.ps_parser_funcs.init(parser.root, null, 0, memory);

    parser.stream       = stream;
    parser.base_len     = 0;
    parser.base_dict    = null;
    parser.private_len  = 0;
    parser.private_dict = null;
    parser.in_pfb       = 0;
    parser.in_memory    = 0;
    parser.single_block = 0;

    /* check the header format */
    var error = check_type1_format(stream, "%!PS-AdobeFont", 14);
    if (error != 0)
    {
        if (error != 2)
            return error;

        error = check_type1_format(stream, "%!FontType", 10);
        if (error != 0)
            return error;
    }

    error = stream.Seek(0);
    if (error != 0)
        return error;

    var ret = read_pfb_tag(stream);
    if (ret.err != 0)
        return ret.err;

    var size = ret.size;

    if (ret.tag != 0x8001)
    {
        /* assume that this is a PFA file for now; an error will */
        /* be produced later when more things are checked        */
        error = stream.Seek(0);
        if (error != 0)
            return error;
        size = stream.size;
    }
    else
        parser.in_pfb = 1;

    /* now, try to load `size' bytes of the `base' dictionary we */
    /* found previously                                          */
    /* if it is a memory-based resource, set up pointers */
    parser.base_dict = new CPointer();
    parser.base_dict.data = stream.data;
    parser.base_dict.pos = stream.pos;
    parser.base_len = size;
    parser.in_memory = 1;

    error = stream.Skip(size);
    if (error != 0)
        return error;

    parser.root.base   = dublicate_pointer(parser.base_dict);
    parser.root.cursor = dublicate_pointer(parser.root.base);
    parser.root.limit  = parser.root.base.pos + parser.base_len;

    return error;
}

function T1_Finalize_Parser(parser)
{
    parser.private_dict = null;
    parser.base_dict = null;

    parser.root.funcs.done(parser.root);
}

function T1_Get_Private_Dict(parser, psaux)
{
    var stream = parser.stream;
    var memory = parser.root.memory;

    var error = 0;
    var size = 0;

    if (parser.in_pfb == 1)
    {
        /* in the case of the PFB format, the private dictionary can be  */
        /* made of several segments.  We thus first read the number of   */
        /* segments to compute the total size of the private dictionary  */
        /* then re-read them into memory.                                */
        var start_pos = stream.pos;
        parser.private_len = 0;
        for (;;)
        {
            var ret = read_pfb_tag(stream);
            if (ret.err != 0)
                return ret.err;

            if (ret.tag != 0x8002)
                break;

            parser.private_len += ret.size;

            error = stream.Skip(ret.size);
        }

        /* Check that we have a private dictionary there */
        /* and allocate private dictionary buffer        */
        if (parser.private_len == 0)
            return 3;

        error = stream.Seek(start_pos);
        if (error != 0)
            return error;

        parser.private_dict = memory.Alloc(parser.private_len);
        parser.private_len = 0;
        var p = dublicate_pointer(parser.private_dict);
        for (;;)
        {
            var ret = read_pfb_tag(stream);
            if (ret.err != 0 || ret.tag != 0x8002)
            {
                error = 0;
                break;
            }

            p.pos = parser.private_len;
            error = stream.Read(p, ret.size);

            if (error != 0)
                return error;

            parser.private_len += ret.size;
        }
    }
    else
    {
        /* We have already `loaded' the whole PFA font file into memory; */
        /* if this is a memory resource, allocate a new block to hold    */
        /* the private dict.  Otherwise, simply overwrite into the base  */
        /* dictionary block in the heap.                                 */

        /* first of all, look at the `eexec' keyword */
        var cur = dublicate_pointer(parser.base_dict);
        var limit = cur.pos + parser.base_len;
        var c = 0;

        var _go_to_found = 0;

        while (true)
        {
            for (;;)
            {
                c = cur.data[cur.pos];
                if (c == 101 && cur.pos + 9 < limit)  /* 9 = 5 letters for `eexec' + */
                {
                    if (cur.data[cur.pos + 1] == 101 && cur.data[cur.pos + 2] == 120 &&
                        cur.data[cur.pos + 3] == 101 && cur.data[cur.pos + 4] == 99)
                        break;
                }
                cur.pos++;
                if (cur.pos >= limit)
                    return 3;
            }

            /* check whether `eexec' was real -- it could be in a comment */
            /* or string (as e.g. in u003043t.gsf from ghostscript)       */
            parser.root.cursor = dublicate_pointer(parser.base_dict);
            parser.root.limit  = cur.pos + 9;

            cur = dublicate_pointer(parser.root.cursor);

            limit = parser.root.limit;

            while (cur.pos < limit)
            {
                if (cur.data[cur.pos] == 101 && _strncmp_data(cur, "eexec", 5) == 0)
                {
                    _go_to_found = 1;
                    break;
                }

                parser.root.funcs.skip_PS_token(parser.root);
                if (parser.root.error != 0)
                    break;
                parser.root.funcs.skip_spaces(parser.root);
                cur.pos = parser.root.cursor.pos;
            }

            if (_go_to_found == 1)
                break;

            /* we haven't found the correct `eexec'; go back and continue */
            /* searching                                                  */
            cur.pos   = limit;
            limit = parser.base_dict.pos + parser.base_len;
        }

        /* now determine where to write the _encrypted_ binary private  */
        /* dictionary.  We overwrite the base dictionary for disk-based */
        /* resources and allocate a new block otherwise                 */
        parser.root.limit = parser.base_dict.pos + parser.base_len;

        parser.root.funcs.skip_PS_token(parser.root);
        cur.pos = parser.root.cursor.pos;

        /* according to the Type1 spec, the first cipher byte must not be  */
        /* an ASCII whitespace character code (blank, tab, carriage return */
        /* or line feed).  We have seen Type 1 fonts with two line feed    */
        /* characters...  So skip now all whitespace character codes.      */
        while (cur.pos < limit  && (cur.data[cur.pos] == 32 || cur.data[cur.pos] == 9 || cur.data[cur.pos] == 13 || cur.data[cur.pos] == 10))
            cur.pos++;
        if (cur.pos >= limit)
            return 3;

        size = parser.base_len - (cur.pos - parser.base_dict.pos);

        if (parser.in_memory == 1)
        {
            /* note that we allocate one more byte to put a terminating `0' */
            parser.private_dict = memory.Alloc(size + 1);
            parser.private_len = size;
        }
        else
        {
            parser.single_block = 1;
            parser.private_dict = dublicate_pointer(parser.base_dict);
            parser.private_len  = size;
            parser.base_dict    = null;
            parser.base_len     = 0;
        }

        /* now determine whether the private dictionary is encoded in binary */
        /* or hexadecimal ASCII format -- decode it accordingly              */

        /* we need to access the next 4 bytes (after the final \r following */
        /* the `eexec' keyword); if they all are hexadecimal digits, then   */
        /* we have a case of ASCII storage                                  */

        if (_isxdigit(cur[0]) && _isxdigit(cur[1]) && _isxdigit(cur[2]) && _isxdigit(cur[3]))
        {
            /* ASCII hexadecimal encoding */
            parser.root.cursor = cur.pos;
            var ret = psaux.ps_parser_funcs.to_bytes(parser.root, parser.private_dict, parser.private_len, 0);
            parser.private_len = ret.num_bytes;

            /* put a safeguard */
            parser.private_dict[len] = 0;
        }
        else
        {
            var mem = memory.Alloc(size);
            for (var i = 0; i < size; i++)
                mem.data[i] = cur.data[cur.pos + i];

            var _p = parser.private_dict.data;
            var _n = parser.private_dict.pos;
            for (var i = 0; i < size; i++)
                _p[_n + i] = mem.data[i];

            mem = null;
        }

    }

    /* we now decrypt the encoded binary private dictionary */
    var _mem = dublicate_pointer(parser.private_dict);
    psaux.t1_decrypt(_mem, parser.private_len, 55665);

    /* replace the four random bytes at the beginning with whitespace */
    var _d = parser.private_dict.data;
    _d[0] = 32;
    _d[1] = 32;
    _d[2] = 32;
    _d[3] = 32;

    parser.root.base   = dublicate_pointer(parser.private_dict);
    parser.root.cursor = dublicate_pointer(parser.private_dict);
    parser.root.limit  = parser.private_dict.pos + parser.private_len;

    return error;
}

/******************************************************************************/
// objs
/******************************************************************************/
var T1_SizeRec = FT_Size;

function T1_GlyphSlotRec()
{
    this.root = new FT_GlyphSlot();
    this.root.base_root = this;

    this.hint = 0;
    this.scaled = 0;

    this.max_points = 0;
    this.max_contours = 0;

    this.x_scale = 0;
    this.y_scale = 0;
}

function T1_Size_Get_Globals_Funcs(size)
{
    return null;

    var pshinter = size.face.pshinter;
    var module = size.face.driver.library.FT_Get_Module("pshinter");
    if (module != null && pshinter != null && pshinter.get_globals_funcs != null)
        return pshinter.get_globals_funcs(module);
    return null;
}

function T1_Size_Done(size)
{
    if (size.internal != null)
    {
        var funcs = T1_Size_Get_Globals_Funcs(size);
        if (funcs != null)
            funcs.destroy(size.internal);

        size.internal = null;
    }
}

function T1_Size_Init()
{
    var size  = new FT_Size();
    var funcs = T1_Size_Get_Globals_Funcs(size);

    if (funcs != null)
    {
        // TODO: (hints)
    }

    return size;
}

function T1_Size_Request(size, req)
{
    var funcs = T1_Size_Get_Globals_Funcs(size);
    FT_Request_Metrics(size.face, req);

    if (funcs != null)
        funcs.set_scale(size.internal, size.metrics.x_scale, size.metrics.y_scale, 0, 0);

    return 0;
}

function T1_GlyphSlot_Done(slot)
{
    slot.internal.glyph_hints = null;
}

function T1_GlyphSlot_Init(slot)
{
    var face = slot.face;
    var pshinter = face.pshinter;

    if (pshinter != null)
    {
        var module = face.driver.library.FT_Get_Module("pshinter");
        if (module != null)
        {
            var funcs = pshinter.get_t1_funcs(module);
            slot.internal.glyph_hints = funcs;
        }
    }
    return 0;
}

function T1_Face_Done(face)
{
    if (face == null)
        return;

    var memory = face.memory;
    var type1 = face.type1;

    //#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
    /* release multiple masters information */
    if (face.buildchar != null)
    {
        face.buildchar = null;
        face.len_buildchar = 0;
    }

    T1_Done_Blend(face);
    face.blend = 0;
    //#endif

    /* release font info strings */
    var info = type1.font_info;

    info.version = null;
    info.notice = null;
    info.full_name = null;
    info.family_name = null;
    info.weight = null;

    /* release top dictionary */
    type1.charstrings_len = null;
    type1.charstrings = null;
    type1.glyph_names = null;

    type1.subrs = null;
    type1.subrs_len = null;

    type1.subrs_block = null;
    type1.charstrings_block = null;
    type1.glyph_names_block = null;

    type1.encoding.char_index = null;
    type1.encoding.char_name = null;
    type1.font_name = null;

    //#ifndef T1_CONFIG_OPTION_NO_AFM
    /* release afm data if present */
    if (face.afm_data != null)
    {
        T1_Done_Metrics(memory, face.afm_data);
        face.afm_data = null;
    }
    //#endif

    face.family_name = "";
    face.style_name  = "";
}

function T1_Face_Init(stream, face, face_index, num_params, params)
{
    var type1 = face.type1;
    var info = type1.font_info;

    face.num_faces = 1;

    var psnames = FT_FACE_FIND_GLOBAL_SERVICE(face, FT_SERVICE_ID_POSTSCRIPT_CMAPS);
    face.psnames = psnames;

    var psaux = face.driver.library.FT_Get_Module_Interface("psaux");
    face.psaux = psaux;

    face.pshinter = face.driver.library.FT_Get_Module_Interface("pshinter");

    /* open the tokenizer; this will also check the font format */
    var error = T1_Open_Face(face);
    if (error != 0)
        return error;

    /* if we just wanted to check the format, leave successfully now */
    if (face_index < 0)
        return error;

    /* check the face index */
    if (face_index > 0)
        return 6;

    /* now load the font program into the face object */
    /* initialize the face object fields */
    /* set up root face fields */
    face.num_glyphs = type1.num_glyphs;
    face.face_index = 0;

    face.face_flags = 1 | 16 | 512 | 2048;

    if (info.is_fixed_pitch == 1)
        face.face_flags |= 4;

    if (face.blend != null)
        face.face_flags |= 256;

    /* XXX: TODO -- add kerning with .afm support */
    /* The following code to extract the family and the style is very   */
    /* simplistic and might get some things wrong.  For a full-featured */
    /* algorithm you might have a look at the whitepaper given at       */
    /*                                                                  */
    /*   http://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */

    /* get style name -- be careful, some broken fonts only */
    /* have a `/FontName' dictionary entry!                 */
    face.family_name = info.family_name;
    face.style_name = null;

    if (face.family_name != null)
    {
        var full = 0;
        var family = 0;

        var full_len = info.full_name.length;
        var family_len = face.family_name.length;

        if (full < full_len)
        {
            var the_same = 1;
            while (full < full_len)
            {
                var _1 = info.full_name.charCodeAt(full);
                var _2 = (family < family_len) ? face.family_name.charCodeAt(family) : 0;

                if (_1 == _2)
                {
                    family++;
                    full++;
                }
                else
                {
                    if (_1 == 32 || _1 == 45)
                        full++;
                    else if (_2 == 32 || _2 == 45)
                        family++;
                    else
                    {
                        the_same = 0;

                        if (family == family_len)
                            face.style_name = full;
                        break;
                    }
                }
            }

            if (the_same == 1)
                face.style_name = "Regular";
        }
    }
    else
    {
        /* do we have a `/FontName'? */
        if (type1.font_name != null)
            face.family_name = type1.font_name;
    }

    if (face.style_name != null)
    {
        if (info.weight != null)
            face.style_name = info.weight;
        else
            face.style_name = "Regular";
    }

    /* compute style flags */
    face.style_flags = 0;
    if (info.italic_angle != 0)
        face.style_flags |= 1;
    if (info.weight != null)
    {
        if (info.weight == "Bold" || info.weight == "Black")
            face.style_flags |= 2;
    }

    /* no embedded bitmap support */
    face.num_fixed_sizes = 0;
    face.available_sizes = 0;

    face.bbox.xMin = type1.font_bbox.xMin >> 16;
    face.bbox.yMin = type1.font_bbox.yMin >> 16;
    /* no `U' suffix here to 0xFFFF! */
    face.bbox.xMax = (type1.font_bbox.xMax + 0xFFFF) >> 16;
    face.bbox.yMax = (type1.font_bbox.yMax + 0xFFFF) >> 16;

    /* Set units_per_EM if we didn't set it in parse_font_matrix. */
    if (face.units_per_EM == 0)
        face.units_per_EM = 1000;

    face.ascender  = face.bbox.yMax;
    face.descender = face.bbox.yMin;

    face.height = parseInt((face.units_per_EM * 12) / 10);
    if (face.height < (face.ascender - face.descender))
        face.height = (face.ascender - face.descender);

    /* now compute the maximum advance width */
    face.max_advance_width = (face.bbox.xMax);

    var ret = T1_Compute_Max_Advance(face);

    /* in case of error, keep the standard width */
    if (ret.err == 0)
        face.max_advance_width = FT_RoundFix(ret.max_advance) >> 16;
    else
        error = 0;   /* clear error */

    face.max_advance_height = face.height;

    face.underline_position  = info.underline_position;
    face.underline_thickness = info.underline_thickness;

    if (psnames != null && psaux != null)
    {
        var cmap_classes = psaux.t1_cmap_classes;
        var charmap = new FT_CharMapRec();

        charmap.face = face;

        /* first of all, try to synthesize a Unicode charmap */
        charmap.platform_id = 3;
        charmap.encoding_id = 1;
        charmap.encoding    = 1970170211;

        var __cmap = FT_CMap_New(cmap_classes.unicode, null, charmap);
        __cmap = null;
        error = FT_Error;
        FT_Error = 0;
        if (error != 0 && 163 != error)
            return error;
        error = 0;

        /* now, generate an Adobe Standard encoding when appropriate */
        charmap.platform_id = 7;
        var clazz = null;

        switch (type1.encoding_type)
        {
            case 2:
                charmap.encoding    = 1094995778;
                charmap.encoding_id = 0;
                clazz               = cmap_classes.standard;
                break;

            case 4:
                charmap.encoding    = 1094992453;
                charmap.encoding_id = 1;
                clazz               = cmap_classes.expert;
                break;

            case 1:
                charmap.encoding    = 1094992451;
                charmap.encoding_id = 2;
                clazz               = cmap_classes.custom;
                break;

            case 3:
                charmap.encoding    = 1818326065;
                charmap.encoding_id = 3;
                clazz               = cmap_classes.unicode;
                break;

            default:
                break;
        }

        if (clazz != null)
        {
            __cmap = FT_CMap_New(clazz, null, charmap);
            error = FT_Error;
            __cmap = null;
        }
    }

    return error;
}

function T1_Driver_Init(driver)
{
    return 0;
}

function T1_Driver_Done(driver)
{
}

/******************************************************************************/
// load
/******************************************************************************/

function T1_Loader()
{
    this.parser = new T1_ParserRec();

    this.num_chars = 0;
    this.encoding_table = new PS_TableRec();
    /* encoding character names         */

    this.num_glyphs = 0;
    this.glyph_names = new PS_TableRec();
    this.charstrings = new PS_TableRec();
    this.swap_table = new PS_TableRec();

    this.num_subrs = 0;
    this.subrs = new PS_TableRec();
    this.fontdata = 0;

    this.keywords_encountered = 0;

    this.clear = function()
    {
        this.parser.clear();

        this.num_chars = 0;
        this.encoding_table.clear();
        /* encoding character names         */

        this.num_glyphs = 0;
        this.glyph_names.clear();
        this.charstrings.clear();
        this.swap_table.clear();

        this.num_subrs = 0;
        this.subrs.clear();
        this.fontdata = 0;

        this.keywords_encountered = 0;
    }
}

function t1_allocate_blend(face, num_designs, num_axis)
{
    var blend = face.blend;
    if (blend == null)
    {
        blend = new PS_BlendRec();
        blend.num_default_design_vector = 0;
        face.blend = blend;
    }

    /* allocate design data if needed */
    if (num_designs > 0)
    {
        if (blend.num_designs == 0)
        {
            blend.font_infos[0] = face.type1.font_info;
            blend.privates[0] = face.type1.private_dict;
            blend.bboxes[0] = face.type1.font_bbox;

            for (var i = 1; i <= num_designs; i++)
            {
                blend.font_infos[i] = new PS_FontInfoRec();
                blend.privates[i] = new PS_PrivateRec();
                blend.bboxes[i] = new FT_BBox();
            }

            blend.weight_vector = CreateIntArray(num_designs * 2);
            blend.default_weight_vector = num_designs;

            blend.num_designs = num_designs;
        }
        else if (blend.num_designs != num_designs)
            return 3;
    }

    /* allocate axis data if needed */
    if (num_axis > 0)
    {
        if (blend.num_axis != 0 && blend.num_axis != num_axis)
            return 3;

        blend.num_axis = num_axis;
    }

    /* allocate the blend design pos table if needed */
    num_designs = blend.num_designs;
    num_axis    = blend.num_axis;
    if (num_designs && num_axis && blend.design_pos[0] == null)
    {
        for (var i = 0; i < num_designs; i++)
            blend.design_pos[i] = CreateIntArray(num_axis);
    }
    return 0;
}

function T1_Get_Multi_Master(face, master)
{
    var blend = face.blend;
    if (null == blend)
        return 6;

    master.num_axis = blend.num_axis;
    master.num_designs = blend.num_designs;

    for (var n = 0; n < blend.num_axis; n++)
    {
        var axis = master.axis[n];
        var map = blend.design_map[n];

        axis.name = blend.axis_names[n];
        axis.minimum = map.design_points[0];
        axis.maximum = map.design_points[map.num_points - 1];
    }
    return 0;
}

function mm_axis_unmap(axismap, ncv)
{
    if (ncv <= axismap.blend_points[0])
        return (axismap.design_points[0] << 16);

    for (var j = 1; j < axismap.num_points; j++)
    {
        if (ncv <= axismap.blend_points[j])
        {
            var t = FT_MulDiv(ncv - axismap.blend_points[j - 1], 0x10000, axismap.blend_points[j] - axismap.blend_points[j - 1]);
            return (axismap.design_points[j - 1] << 16) + FT_MulDiv(t, axismap.design_points[j] - axismap.design_points[j - 1], 1);
        }
    }

    return (axismap.design_points[axismap.num_points - 1] << 16);
}

function mm_weights_unmap(weights, w_s, axiscoords, a_s, axis_count)
{
    if (axis_count == 1)
        axiscoords[a_s] = weights[w_s + 1];
    else if (axis_count == 2)
    {
        axiscoords[a_s] = weights[w_s + 3] + weights[w_s + 1];
        axiscoords[a_s + 1] = weights[w_s + 3] + weights[w_s + 2];
    }
    else if (axis_count == 3)
    {
        axiscoords[a_s] = weights[w_s + 7] + weights[w_s + 5] + weights[w_s + 3] + weights[w_s + 1];
        axiscoords[a_s + 1] = weights[w_s + 7] + weights[w_s + 6] + weights[w_s + 3] + weights[w_s + 2];
        axiscoords[a_s + 2] = weights[w_s + 7] + weights[w_s + 6] + weights[w_s + 5] + weights[w_s + 4];
    }
    else
    {
        axiscoords[a_s] = weights[w_s + 15] + weights[w_s + 13] + weights[w_s + 11] + weights[w_s + 9] + weights[w_s + 7] + weights[w_s + 5] + weights[w_s + 3] + weights[w_s + 1];
        axiscoords[a_s + 1] = weights[w_s + 15] + weights[w_s + 14] + weights[w_s + 11] + weights[w_s + 10] + weights[w_s + 7] + weights[w_s + 6] + weights[w_s + 3] + weights[w_s + 2];
        axiscoords[a_s + 2] = weights[w_s + 15] + weights[w_s + 14] + weights[w_s + 13] + weights[w_s + 12] + weights[w_s + 7] + weights[w_s + 6] + weights[w_s + 5] + weights[w_s + 4];
        axiscoords[a_s + 3] = weights[w_s + 15] + weights[w_s + 14] + weights[w_s + 13] + weights[w_s + 12] + weights[w_s + 11] + weights[w_s + 10] + weights[w_s + 9] + weights[w_s + 8];
    }
}

function T1_Get_MM_Var(face)
{
    var mmvar = new FT_MM_Var();
    var mmaster = new FT_Multi_Master();
    var axiscoords = CreateIntArray(4);
    var blend = face.blend;

    var error = T1_Get_Multi_Master(face, mmaster);
    if (error != 0)
        return { err: error, mm : null };

    var _num_axis = master.num_axis;
    mmvar.axis = new Array(_num_axis);
    for (var i = 0; i < _num_axis; i++)
        mmvar.axis[i] = new FT_Var_Axis();

    mmvar.num_axis = mmaster.num_axis;
    mmvar.num_designs = mmaster.num_designs;
    mmvar.num_namedstyles = 0xFFFFFFFF;                /* Does not apply */
    /* Point to axes after MM_Var struct */
    mmvar.namedstyle = null;

    for (var i = 0 ; i < _num_axis; i++)
    {
        var _axis = mmvar.axis[i];

        _axis.name = mmaster.axis[i].name;
        _axis.minimum = (mmaster.axis[i].minimum << 16);
        _axis.maximum = (mmaster.axis[i].maximum << 16);
        _axis.def = parseInt((_axis.minimum + _axis.maximum) / 2);
        /* Does not apply.  But this value is in range */
        _axis.strid = 0xFFFFFFFF;
        _axis.tag = 0xFFFFFFFF;

        if (_axis.name == "Weight")
            _axis.tag = 2003265652;//FT_MAKE_TAG("w", "g", "h", "t");
        else if (_axis.name == "Width")
            _axis.tag = 2003072104;//FT_MAKE_TAG("w", "d", "t", "h");
        else if (_axis.name == "OpticalSize")
            _axis.tag = 1869640570;//FT_MAKE_TAG("o", "p", "s", "z");
    }

    if (blend.num_designs == (1 << blend.num_axis))
    {
        mm_weights_unmap(blend.default_weight_vector, 0, axiscoords, 0, blend.num_axis);

        for (var i = 0; i < _num_axis; i++)
            mmvar.axis[i].def = mm_axis_unmap(blend.design_map[i], axiscoords[i]);
    }

    return { err : 0, mm : mmvar };
}

function T1_Set_MM_Blend(face, num_coords, coords)
{
    var blend = face.blend;
    if (blend != null)
        return 6;

    if (blend != null && blend.num_axis == num_coords)
    {
        for (var n = 0; n < blend.num_designs; n++)
        {
            var result = 0x10000;  /* 1.0 fixed */

            for (var m = 0; m < blend.num_axis; m++)
            {
                var factor = coords[m];
                if (factor < 0)
                    factor = 0;
                if (factor > 0x10000)
                    factor = 0x10000;
                if (( n & (1 << m)) == 0)
                    factor = 0x10000 - factor;

                result = FT_MulFix(result, factor);
            }
            blend.weight_vector[n] = result;
        }
    }

    return 0;
}

function T1_Set_MM_Design(face, num_coords, coords)
{
    var blend = face.blend;
    if (null == blend || blend.num_axis != num_coords)
        return 6;

    /* compute the blend coordinates through the blend design map */
    var final_blends = CreateIntArray(16);

    for (var n = 0; n < blend.num_axis; n++)
    {
        var design = coords[n];
        var the_blend = 0;
        var map = blend.design_map[n];
        var designs = map.design_points;
        var blends = map.blend_points;
        var before = -1, after = -1;

        var is_go_to_found = 0;
        for (var p = 0; p < map.num_points; p++)
        {
            var p_design = designs[p];

            /* exact match? */
            if (design == p_design)
            {
                the_blend = blends[p];
                is_go_to_found = 1;
                break;
            }

            if (design < p_design)
            {
                after = p;
                break;
            }

            before = p;
        }

        if (0 == is_go_to_found)
        {
            /* now interpolate if necessary */
            if (before < 0)
                the_blend = blends[0];
            else if ( after < 0 )
                the_blend = blends[map.num_points - 1];
            else
                the_blend = FT_MulDiv(design - designs[before], blends[after] - blends[before], designs[after] - designs[before]);
        }

        final_blends[n] = the_blend;
    }

    var error = T1_Set_MM_Blend(face, num_coords, final_blends);
    return error;
}

function T1_Set_Var_Design(face, num_coords, coords)
{
    var lcoords = CreateIntArray(4);          /* maximum axis count is 4 */

    if (num_coords <= 4 && num_coords > 0)
    {
        for (var i = 0; i < num_coords; i++)
            lcoords[i] = FT_RoundFix(coords[i]) >> 16;
        var error = T1_Set_MM_Design(face, num_coords, lcoords);
        return error;
    }
    return 6;
}

function T1_Done_Blend(face)
{
    face.blend = null;
}

function t1_parse_blend_axis_types(face, loader)
{
    var axis_tokens = new Array(4);
    for (var i = 0; i < 4; i++)
        axis_tokens[i] = new T1_TokenRec();

    /* take an array of objects */
    var num_axis = loader.parser.root.funcs.to_token_array(loader.parser.root, axis_tokens, 4);
    if (num_axis < 0)
    {
        loader.parser.root.error = 162;
        return;
    }
    if (num_axis == 0 || num_axis > 4)
    {
        loader.parser.root.error = 3;
        return;
    }

    /* allocate blend if necessary */
    var error = t1_allocate_blend(face, 0, num_axis);
    if (error != 0)
    {
        loader.parser.root.error = error;
        return;
    }

    var blend = face.blend;
    
    /* each token is an immediate containing the name of the axis */
    for (var n = 0; n < num_axis; n++)
    {
        var token = axis_tokens[n];
        
        /* skip first slash, if any */
        if (token.start.data[token.start.pos] == 47)
            token.start.pos++;

        var len = token.limit - token.start.pos;
        if (len == 0)
        {
            loader.parser.root.error = 3;
            return;
        }

        blend.axis_names[n] = "";
        for (var i = 0; i < len; i++)
        {
            blend.axis_names[n] += String.fromCharCode(token.start.data[token.start.pos + n]);
        }
    }

    loader.parser.root.error = error;
}

function t1_parse_blend_design_positions(face, loader)
{
    var design_tokens = new Array(16);
    for (var i = 0; i < 16; i++)
        design_tokens[i] = new T1_TokenRec();

    var parser = loader.parser;

    /* get the array of design tokens -- compute number of designs */
    var num_designs = parser.root.funcs.to_token_array(parser.root, design_tokens, 16);
    if (num_designs < 0)
    {
        parser.root.error = 162;
        return;
    }
    if (num_designs == 0 || num_designs > 16)
    {
        loader.parser.root.error = 3;
        return;
    }

    var old_cursor = parser.root.cursor;
    var old_limit = parser.root.limit;

    var blend = face.blend;
    var num_axis = 0;  /* make compiler happy */

    var error = 0;
    for (var n = 0; n < num_designs; n++)
    {
        /* read axis/coordinates tokens */
        var token = design_tokens[n];
        parser.root.cursor = token.start.pos;
        parser.root.limit = token.limit;

        var axis_tokens = new Array(4);
        for (var i = 0; i < 4; i++)
            axis_tokens[i] = new T1_TokenRec();
        
        var n_axis = parser.root.funcs.to_token_array(parser.root, axis_tokens, 4);

        if (n == 0)
        {
            if (n_axis <= 0 || n_axis > 4)
            {
                loader.parser.root.error = 3;
                return;
            }

            num_axis = n_axis;
            error = t1_allocate_blend( face, num_designs, num_axis );
            if (error != 0)
            {
                loader.parser.root.error = error;
                return;
            }
            blend = face.blend;
        }
        else if (n_axis != num_axis)
        {
            loader.parser.root.error = 3;
            return;
        }

        /* now read each axis token into the design position */
        for (var axis = 0; axis < n_axis; axis++)
        {
            var token2 = axis_tokens[axis];

            parser.root.cursor = token2.start.pos;
            parser.root.limit  = token2.limit;
            blend.design_pos[n][axis] = parser.root.funcs.to_fixed(parser, 0);
        }
    }

    parser.root.cursor = old_cursor;
    parser.root.limit  = old_limit;

    parser.root.error = error;
}

function t1_parse_blend_design_map(face, loader)
{
    var parser = loader.parser;

    var axis_tokens = new Array(4);
    for (var i = 0; i < 4; i++)
        axis_tokens[i] = new T1_TokenRec();

    var num_axis = parser.root.funcs.to_token_array(parser.root, axis_tokens, 4);

    if (num_axis < 0)
    {
        parser.root.error = 162;
        return;
    }
    if (num_axis == 0 || num_axis > 4)
    {
        parser.root.error = 3;
        return;
    }

    var old_cursor = parser.root.cursor;
    var old_limit = parser.root.limit;

    var error = t1_allocate_blend(face, 0, num_axis);
    if (error != 0)
    {
        parser.root.error = error;
        return;
    }
    var blend = face.blend;

    /* now read each axis design map */
    for (var n = 0; n < num_axis; n++)
    {
        var map = blend.design_map[n];

        var point_tokens = new Array(20);
        for (var i = 0; i < 20; i++)
            point_tokens[i] = new T1_TokenRec();

        var axis_token = axis_tokens[n];

        parser.root.cursor = axis_token.start.pos;
        parser.root.limit  = axis_token.limit;

        var num_points = parser.root.funcs.to_token_array(parser.root, point_tokens, 20);

        if (num_points <= 0 || num_points > 20)
        {
            parser.root.error = 3;
            return;
        }

        /* allocate design map data */
        map.design_points = CreateIntArray(num_points);
        map.blend_points = CreateIntArray(num_points);
        map.num_points = num_points;

        for (var p = 0; p < num_points; p++)
        {
            var point_token = point_tokens[p];

            /* don't include delimiting brackets */
            parser.root.cursor = point_token.start.pos + 1;
            parser.root.limit = point_token.limit - 1;

            map.design_points[p] = parser.root.funcs.to_int(parser.root);
            map.blend_points [p] = parser.root.funcs.to_fixed(parser.root, 0);
        }
    }

    parser.root.cursor = old_cursor;
    parser.root.limit  = old_limit;

    parser.root.error = error;
}

function t1_parse_weight_vector(face, loader)
{
    var design_tokens = new Array(16);
    for (var i = 0; i < 16; i++)
        design_tokens[i] = new T1_TokenRec();

    var parser = loader.parser;
    var blend = face.blend;
    var num_designs = parser.root.funcs.to_token_array(parser.root, design_tokens, 16);
    if (num_designs < 0)
    {
        parser.root.error = 162;
        return;
    }
    if (num_designs == 0 || num_designs > 16)
    {
        parser.root.error = 3;
        return;
    }

    var error = 0;
    if (blend == null || blend.num_designs == 0)
    {
        error = t1_allocate_blend(face, num_designs, 0 );
        if (error != 0)
        {
            parser.root.error = error;
            return;
        }
        blend = face.blend;
    }
    else if (blend.num_designs != num_designs)
    {
        parser.root.error = 3;
        return;
    }

    var old_cursor = parser.root.cursor;
    var old_limit = parser.root.limit;

    for (var n = 0; n < num_designs; n++)
    {
        var token = design_tokens[n];
        parser.root.cursor = token.start.pos;
        parser.root.limit = token.limit;

        var _temp = parser.root.funcs.to_fixed(parser.root, 0);
        blend.weight_vector[n] = _temp;
        blend.weight_vector[blend.default_weight_vector + n] = _temp;
    }

    parser.root.cursor = old_cursor;
    parser.root.limit  = old_limit;

    parser.root.error = error;
}

function t1_parse_buildchar(face, loader)
{
    face.len_buildchar = loader.parser.funcs.to_fixed_array(loader.parser.root, 0, null, 0);
}

function t1_load_keyword(face, loader, field)
{
    var blend = face.blend;
    if (blend != null && blend.num_designs == 0)
        blend = null;

    /* if the keyword has a dedicated callback, call it */
    if (field.type == 10)
    {
        field.reader(face, loader);
        return loader.parser.root.error;
    }

    var max_objects = 0;
    var arr = null;

    /* now, the keyword is either a simple field, or a table of fields; */
    /* we are now going to take care of it                              */
    switch (field.location)
    {
        case 3:
            if (blend != null)
            {
                arr = blend.font_infos;
                max_objects = blend.num_designs;
            }
            else
            {
                arr = new Array(1);
                arr[0] = face.type1.font_info;
                max_objects  = 0;
            }
            break;

        case 2:
            arr = new Array(1);
            arr[0] = face.type1.font_extra;
            max_objects  = 0;
            break;

        case 4:
            if (blend != null)
            {
                arr = blend.privates;
                max_objects = blend.num_designs;
            }
            else
            {
                arr = new Array(1);
                arr[0] = face.type1.private_dict;
                max_objects = 0;
            }
            break;

        case 5:
            if (blend != null)
            {
                arr = blend.bboxes;
                max_objects = blend.num_designs;
            }
            else
            {
                arr = new Array(1);
                arr[0] = face.type1.font_bbox;
                max_objects = 0;
            }
            break;

        case 6:
            arr = new Array(1);
            arr[0] = loader;
            max_objects  = 0;
            break;

        case 7:
            arr = new Array(1);
            arr[0] = face;
            max_objects  = 0;
            break;

        case 8:
            arr = new Array(1);
            arr[0] = loader;
            max_objects  = 0;
            break;

        default:
            arr = new Array(1);
            arr[0] = face.type1;
            max_objects  = 0;
    }

    if (field.type == 8 || field.type == 9)
        return loader.parser.root.funcs.load_field_table(loader.parser.root, field, arr, max_objects, 0);

    return loader.parser.root.funcs.load_field(loader.parser.root, field, arr, max_objects, 0);
}

function t1_parse_private(face, loader)
{
    loader.keywords_encountered |= 1;
}

function _t1_read_binary_data(parser)
{
    var ret = { size: 0, base: null, err: 0 };

    var limit = parser.root.limit;

    /* the binary data has one of the following formats */
    /*                                                  */
    /*   `size' [white*] RD white ....... ND            */
    /*   `size' [white*] -| white ....... |-            */
    /*                                                  */

    parser.root.funcs.skip_spaces(parser.root);
    var cur = dublicate_pointer(parser.root.cursor);

    if (cur.pos < limit && (cur.data[cur.pos] >= 48) && (cur.data[cur.pos] <= 57))
    {
        var s = parser.root.funcs.to_int(parser.root);
        parser.root.funcs.skip_PS_token(parser.root);

        /* there is only one whitespace char after the */
        /* `RD' or `-|' token                          */
        ret.base = dublicate_pointer(parser.root.cursor);
        ret.base.pos += 1;

        if (s >= 0 && s < (limit - ret.base.pos))
        {
            parser.root.cursor.pos += (s + 1);
            ret.size = s;
            ret.err = (parser.root.error == 0) ? 1 : 0;
            return ret;
        }
    }

    parser.root.error = 3;
    return ret;
}

function t1_parse_font_matrix(face, loader)
{
    var parser = loader.parser;
    var matrix = face.type1.font_matrix;
    var offset = face.type1.font_offset;

    var temp = CreateIntArray(6);

    var result = parser.root.funcs.to_fixed_array(parser.root, 6, temp, 3);

    if (result < 0)
    {
        parser.root.error = 3;
        return;
    }

    var temp_scale = Math.abs(temp[3]);

    if (temp_scale == 0)
    {
        parser.root.error = 3;
        return;
    }

    /* Set Units per EM based on FontMatrix values.  We set the value to */
    /* 1000 / temp_scale, because temp_scale was already multiplied by   */
    /* 1000 (in t1_tofixed, from psobjs.c).                              */
    face.units_per_EM = (FT_DivFix(1000 * 0x10000, temp_scale) >> 16);

    /* we need to scale the values by 1.0/temp_scale */
    if (temp_scale != 0x10000)
    {
        temp[0] = FT_DivFix(temp[0], temp_scale);
        temp[1] = FT_DivFix(temp[1], temp_scale);
        temp[2] = FT_DivFix(temp[2], temp_scale);
        temp[4] = FT_DivFix(temp[4], temp_scale);
        temp[5] = FT_DivFix(temp[5], temp_scale);
        temp[3] = temp[3] < 0 ? -0x10000 : 0x10000;
    }

    matrix.xx = temp[0];
    matrix.yx = temp[1];
    matrix.xy = temp[2];
    matrix.yy = temp[3];

    /* note that the offsets must be expressed in integer font units */
    offset.x = temp[4] >> 16;
    offset.y = temp[5] >> 16;
}

function t1_parse_encoding(face, loader)
{
    var parser = loader.parser;
    var limit = parser.root.limit;

    var memory = parser.root.memory;

    var psaux = face.psaux;

    parser.root.funcs.skip_spaces(parser.root);
    var cur = dublicate_pointer(parser.root.cursor);
    if (cur.pos >= limit)
    {
        parser.root.error = 3;
        return;
    }

    /* if we have a number or `[', the encoding is an array, */
    /* and we must load it now                               */
    var _cur_v = cur.data[cur.pos];
    if ((_cur_v >= 48 && _cur_v <= 57) || _cur_v == 91)
    {
        var encode = face.type1.encoding;
        var count, n;
        var char_table = loader.encoding_table;
        var only_immediates = 0;

        /* read the number of entries in the encoding; should be 256 */
        if (_cur_v == 91)
        {
            count = 256;
            only_immediates = 1;
            parser.root.cursor.pos++;
        }
        else
            count = parser.root.funcs.to_int(parser.root);

        parser.root.funcs.skip_spaces(parser.root);
        if (parser.root.cursor.pos >= limit)
            return;

        /* we use a T1_Table to store our charnames */
        loader.num_chars = encode.num_chars = count;

        encode.char_index = CreateUIntArray(count);
        encode.char_name = CreateNullArray(count);
        var error = psaux.ps_table_funcs.init(char_table, count, memory);
        if (error != 0)
        {
            parser.root.error = error;
            return;
        }

        /* We need to `zero' out encoding_table.elements */
        for (n = 0; n < count; n++)
        {
            var notdef_name = memory.Alloc(8);
            notdef_name.data[0] = 46;
            notdef_name.data[1] = 110;
            notdef_name.data[2] = 111;
            notdef_name.data[3] = 116;
            notdef_name.data[4] = 100;
            notdef_name.data[5] = 101;
            notdef_name.data[6] = 102;
            notdef_name.data[7] = 0;

            char_table.funcs.add(char_table, n, notdef_name, 8)
        }

        /* Now we need to read records of the form                */
        /*                                                        */
        /*   ... charcode /charname ...                           */
        /*                                                        */
        /* for each entry in our table.                           */
        /*                                                        */
        /* We simply look for a number followed by an immediate   */
        /* name.  Note that this ignores correctly the sequence   */
        /* that is often seen in type1 fonts:                     */
        /*                                                        */
        /*   0 1 255 { 1 index exch /.notdef put } for dup        */
        /*                                                        */
        /* used to clean the encoding array before anything else. */
        /*                                                        */
        /* Alternatively, if the array is directly given as       */
        /*                                                        */
        /*   /Encoding [ ... ]                                    */
        /*                                                        */
        /* we only read immediates.                               */

        n = 0;
        parser.root.funcs.skip_spaces(parser.root);

        while (parser.root.cursor.pos < limit)
        {
            cur.pos = parser.root.cursor.pos;

            /* we stop when we encounter a `def' or `]' */
            if (cur.data[cur.pos] == 100 && cur.pos + 3 < limit)
            {
                if (cur.data[cur.pos + 1] == 101 && cur.data[cur.pos + 2] == 102 && IS_PS_DELIM(cur.data[cur.pos + 3]))
                {
                    cur.pos += 3;
                    break;
                }
            }
            if (cur.data[cur.pos] == 93)
            {
                cur.pos++;
                break;
            }

            /* check whether we've found an entry */
            if (((cur.data[cur.pos] >= 48) && (cur.data[cur.pos] <= 57)) || (only_immediates == 1))
            {
                var charcode = 0;

                if (only_immediates == 1)
                    charcode = n;
                else
                {
                    charcode = parser.root.funcs.to_int(parser.root);
                    parser.root.funcs.skip_spaces(parser.root);
                }

                cur.pos = parser.root.cursor.pos;

                if (cur.data[cur.pos] == 47 && (cur.pos + 2) < limit && (n < count))
                {
                    cur.pos++;
                    parser.root.cursor.pos = cur.pos;
                    parser.root.funcs.skip_PS_token(parser.root);
                    if (parser.root.error != 0)
                        return;

                    var len = parser.root.cursor.pos - cur.pos;

                    parser.root.error = char_table.funcs.add(char_table, charcode, cur, len + 1);
                    if (parser.root.error != 0)
                        return;
                    char_table.elements[charcode][len] = 0;

                    n++;
                }
                else if (only_immediates == 1)
                {
                    /* Since the current position is not updated for           */
                    /* immediates-only mode we would get an infinite loop if   */
                    /* we don't do anything here.                              */
                    /*                                                         */
                    /* This encoding array is not valid according to the type1 */
                    /* specification (it might be an encoding for a CID type1  */
                    /* font, however), so we conclude that this font is NOT a  */
                    /* type1 font.                                             */
                    parser.root.error = 2;
                    return;
                }
            }
            else
            {
                parser.root.funcs.skip_PS_token(parser.root);
                if (parser.root.error != 0)
                    return;
            }

            parser.root.funcs.skip_spaces(parser.root);
        }

        face.type1.encoding_type = 1;
        parser.root.cursor.pos = cur.pos;
    }
    else
    {
        if (cur.pos + 17 < limit && _strncmp_data(cur, "StandardEncoding", 16) == 0)
            face.type1.encoding_type = 2;
        else if (cur + 15 < limit && _strncmp_data(cur, "ExpertEncoding", 14) == 0)
            face.type1.encoding_type = 4;
        else if (cur + 18 < limit && _strncmp_data(cur, "ISOLatin1Encoding", 17) == 0)
            face.type1.encoding_type = 3;
        else
            parser.root.error = 162;
    }
}

function t1_parse_subrs(face, loader)
{
    var parser = loader.parser;
    var table = loader.subrs;
    var memory = parser.root.memory;

    var psaux = face.psaux;
    parser.root.funcs.skip_spaces(parser.root);

    /* test for empty array */
    if (parser.root.cursor.pos < parser.root.limit && parser.root.cursor.data[parser.root.cursor.pos] == 91)
    {
        parser.root.funcs.skip_PS_token(parser.root);
        parser.root.funcs.skip_spaces(parser.root);

        if (parser.root.cursor.pos >= parser.root.limit || parser.root.cursor.data[parser.root.cursor.pos] != 93)
        parser.root.error = 3;
        return;
    }

    var num_subrs = parser.root.funcs.to_int(parser.root);

    /* position the parser right before the `dup' of the first subr */
    parser.root.funcs.skip_PS_token(parser.root);
    if (parser.root.error != 0)
        return;

    parser.root.funcs.skip_spaces(parser.root);

    var error = 0;
    /* initialize subrs array -- with synthetic fonts it is possible */
    /* we get here twice                                             */
    if (loader.num_subrs == 0)
    {
        error = psaux.ps_table_funcs.init(table, num_subrs, memory);
        if (error != 0)
        {
            parser.root.error = 0;
            return;
        }
    }

    /* the format is simple:   */
    /*                         */
    /*   `index' + binary data */
    /*                         */
    for (;;)
    {
        /* If the next token isn't `dup' we are done. */
        if (_strncmp_data(parser.root.cursor, "dup", 3) != 0)
            break;

        parser.root.funcs.skip_PS_token(parser.root);

        var idx = parser.root.funcs.to_int(parser.root);

        var ret = _t1_read_binary_data(parser);

        var size = ret.size;
        var base = ret.base;
        if (0 == ret.err)
            return;

        /* The binary string is followed by one token, e.g. `NP' */
        /* (bound to `noaccess put') or by two separate tokens:  */
        /* `noaccess' & `put'.  We position the parser right     */
        /* before the next `dup', if any.                        */
        parser.root.funcs.skip_PS_token(parser.root);
        if (parser.root.error != 0)
            return;

        parser.root.funcs.skip_spaces(parser.root);

        if (_strncmp_data(parser.root.cursor, "put", 3) == 0)
        {
            parser.root.funcs.skip_PS_token(parser.root);
            parser.root.funcs.skip_spaces(parser);
        }

        /* with synthetic fonts it is possible we get here twice */
        if (loader.num_subrs != 0)
            continue;

        /* some fonts use a value of -1 for lenIV to indicate that */
        /* the charstrings are unencoded                           */
        /*                                                         */
        /* thanks to Tom Kacvinsky for pointing this out           */
        /*                                                         */
        if (face.type1.private_dict.lenIV >= 0)
        {
            /* some fonts define empty subr records -- this is not totally */
            /* compliant to the specification (which says they should at   */
            /* least contain a `return'), but we support them anyway       */
            if (size < face.type1.private_dict.lenIV)
            {
                parser.root.error = 3;
                return;
            }

            /* t1_decrypt() shouldn't write to base -- make temporary copy */
            var temp = memory.Alloc(size);
            for (var i = 0; i < size; i++)
            {
                temp.data[i] = base.data[base.pos + i];
            }

            psaux.t1_decrypt(temp, size, 4330);
            size -= face.type1.private_dict.lenIV;

            temp.pos += face.type1.private_dict.lenIV;
            error = table.funcs.add(table, idx, temp, size);

            temp = null;
        }
        else
            error = table.funcs.add(table, idx, base, size);

        if (error != 0)
        {
            parser.root.error = error;
        }
    }

    if (loader.num_subrs == 0)
        loader.num_subrs = num_subrs;
}

function t1_parse_charstrings(face, loader)
{
    var parser = loader.parser;
    var code_table = loader.charstrings;
    var name_table = loader.glyph_names;
    var swap_table = loader.swap_table;
    var memory = parser.root.memory;

    var psaux = face.psaux;

    var limit = parser.root.limit;
    var n = 0;
    var notdef_index = 0;
    var notdef_found = 0;

    var num_glyphs = parser.root.funcs.to_int(parser.root);
    /* some fonts like Optima-Oblique not only define the /CharStrings */
    /* array but access it also                                        */
    if (num_glyphs == 0 || parser.root.error != 0)
        return;

    /* initialize tables, leaving space for addition of .notdef, */
    /* if necessary, and a few other glyphs to handle buggy      */
    /* fonts which have more glyphs than specified.              */

    /* for some non-standard fonts like `Optima' which provides  */
    /* different outlines depending on the resolution it is      */
    /* possible to get here twice                                */

    var root = parser.root;

    var error = 0;
    if (loader.num_glyphs == 0)
    {
        error = psaux.ps_table_funcs.init(code_table, num_glyphs + 1 + 5, memory);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = psaux.ps_table_funcs.init(name_table, num_glyphs + 1 + 5, memory);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        /* Initialize table for swapping index notdef_index and */
        /* index 0 names and codes (if necessary).              */
        error = psaux.ps_table_funcs.init(swap_table, 4, memory);
        if (error != 0)
        {
            root.error = error;
            return error;
        }
    }

    n = 0;

    for (;;)
    {
        /* the format is simple:        */
        /*   `/glyphname' + binary data */

        root.funcs.skip_spaces(root);

        var cur = dublicate_pointer(root.cursor);
        if (cur.pos >= limit)
            break;

        /* we stop when we find a `def' or `end' keyword */
        if (cur.pos + 3 < limit && IS_PS_DELIM(cur.data[cur.pos + 3]))
        {
            if (cur.data[cur.pos] == 100 && cur.data[cur.pos + 1] == 101 && cur.data[cur.pos + 2] == 102)
            {
                /* There are fonts which have this: */
                /*                                  */
                /*   /CharStrings 118 dict def      */
                /*   Private begin                  */
                /*   CharStrings begin              */
                /*   ...                            */
                /*                                  */
                /* To catch this we ignore `def' if */
                /* no charstring has actually been  */
                /* seen.                            */
                if (n != 0)
                    break;
            }

            if (cur.data[cur.pos] == 101 && cur.data[cur.pos + 1] == 110 && cur.data[cur.pos + 2] == 100)
                break;
        }

        root.funcs.skip_PS_token(root);
        if (root.error != 0)
            return;

        if (cur.data[cur.pos] == 47)
        {
            if (cur.pos + 1 >= limit)
            {
                root.error = 3;
                return;
            }

            cur.pos++;                              /* skip `/' */
            var len = root.cursor.pos - cur.pos;

            var ret = _t1_read_binary_data(parser);

            if (ret.err == 0)
                return;

            var size = ret.size;
            var base = ret.base;

            /* for some non-standard fonts like `Optima' which provides */
            /* different outlines depending on the resolution it is     */
            /* possible to get here twice                               */
            if (loader.num_glyphs != 0)
                continue;

            error = name_table.funcs.add(name_table, n, cur, len + 1);
            if (error != 0)
            {
                root.error = error;
                return;
            }

            /* add a trailing zero to the name table */
            name_table.elements[n].data[name_table.elements[n].pos + len] = 0;

            /* record index of /.notdef */
            if (cur.data[cur.pos] == 46 && _strcmp_data(".notdef", name_table.elements[n]) == 0)
            {
                notdef_index = n;
                notdef_found = 1;
            }

            if (face.type1.private_dict.lenIV >= 0 && (n < (num_glyphs + 5)))
            {
                if (size <= face.type1.private_dict.lenIV)
                {
                    root.error = 3;
                    return;
                }

                /* t1_decrypt() shouldn't write to base -- make temporary copy */
                var temp = memory.Alloc(size);

                for (var i = 0; i < size; i++)
                    temp.data[i] = base.data[base.pos + i];


                psaux.t1_decrypt(temp, size, 4330);
                size -= face.type1.private_dict.lenIV;

                temp.pos += face.type1.private_dict.lenIV;
                error = code_table.funcs.add(code_table, n, temp, size);

                temp = null;
            }
            else
                error = code_table.funcs.add(code_table, n, base, size);

            if (error != 0)
            {
                root.error = error;
                return;
            }

            n++;
        }
    }

    loader.num_glyphs = n;

    /* if /.notdef is found but does not occupy index 0, do our magic. */
    if (notdef_found != 0 && _strcmp_data(".notdef", name_table.elements[0]))
    {
        /* Swap glyph in index 0 with /.notdef glyph.  First, add index 0  */
        /* name and code entries to swap_table.  Then place notdef_index   */
        /* name and code entries into swap_table.  Then swap name and code */
        /* entries at indices notdef_index and 0 using values stored in    */
        /* swap_table.                                                     */

        /* Index 0 name */
        error = swap_table.funcs.add(swap_table, 0, name_table.elements[0], name_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        /* Index 0 code */
        error = swap_table.funcs.add(swap_table, 1, code_table.elements[0], code_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        /* Index notdef_index name */
        error = swap_table.funcs.add(swap_table, 2, name_table.elements[notdef_index], name_table.lengths[notdef_index]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        /* Index notdef_index code */
        error = swap_table.funcs.add(swap_table, 3, code_table.elements[notdef_index], code_table.lengths[notdef_index]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = name_table.funcs.add(name_table, notdef_index, swap_table.elements[0], swap_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = code_table.funcs.add(code_table, notdef_index, swap_table.elements[1], swap_table.lengths[1]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = name_table.funcs.add(name_table, 0, swap_table.elements[2], swap_table.lengths[2]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = code_table.funcs.add(code_table, 0, swap_table.elements[3], swap_table.lengths[3]);
        if (error != 0)
        {
            root.error = error;
            return;
        }
    }
    else if (notdef_found == 0)
    {
        /* notdef_index is already 0, or /.notdef is undefined in   */
        /* charstrings dictionary.  Worry about /.notdef undefined. */
        /* We take index 0 and add it to the end of the table(s)    */
        /* and add our own /.notdef glyph to index 0.               */

        /* 0 333 hsbw endchar */
        var notdef_glyph = memory.Alloc(5);
        notdef_glyph.data[0] = 0x8B;
        notdef_glyph.data[1] = 0xF7;
        notdef_glyph.data[2] = 0xE1;
        notdef_glyph.data[3] = 0x0D;
        notdef_glyph.data[4] = 0x0E;

        var notdef_name = memory.Alloc(8);
        notdef_name.data[0] = 46;
        notdef_name.data[1] = 110;
        notdef_name.data[2] = 111;
        notdef_name.data[3] = 116;
        notdef_name.data[4] = 100;
        notdef_name.data[5] = 101;
        notdef_name.data[6] = 102;
        notdef_name.data[7] = 0;

        error = swap_table.funcs.add(swap_table, 0, name_table.elements[0], name_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = swap_table.funcs.add(swap_table, 1, code_table.elements[0], code_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = name_table.funcs.add(name_table, 0, notdef_name, 8);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = code_table.funcs.add(code_table, 0, notdef_glyph, 5);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = name_table.funcs.add(name_table, n, swap_table.elements[0], swap_table.lengths[0]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        error = code_table.funcs.add(code_table, n, swap_table.elements[1], swap_table.lengths[1]);
        if (error != 0)
        {
            root.error = error;
            return;
        }

        /* we added a glyph. */
        loader.num_glyphs += 1;
    }
}

function _t1_parse_dict(face, loader, base, size)
{
    var parser = loader.parser;
    var have_integer = 0;

    parser.root.cursor = dublicate_pointer(base);
    parser.root.limit  = base.pos + size;
    parser.root.error  = 0;

    var limit = parser.root.limit;

    parser.root.funcs.skip_spaces(parser.root);

    var start_binary = dublicate_pointer(parser.root.cursor);
    
    while (parser.root.cursor.pos < limit)
    {
        var cur = dublicate_pointer(parser.root.cursor);

        /* look for `eexec' */
        if (IS_PS_TOKEN(cur, limit, "eexec", 6) == 1)
            break;

        /* look for `closefile' which ends the eexec section */
        else if (IS_PS_TOKEN(cur, limit, "closefile", 10) == 1)
            break;

        /* in a synthetic font the base font starts after a           */
        /* `FontDictionary' token that is placed after a Private dict */
        else if (IS_PS_TOKEN(cur, limit, "FontDirectory", 14) == 1)
        {
            if (loader.keywords_encountered & 1)
                loader.keywords_encountered |= 2;
            parser.root.cursor.pos += 13;
        }
        /* check whether we have an integer */
        else if (cur.data[cur.pos] >= 48 && cur.data[cur.pos] <= 57)
        {
            start_binary.pos = cur.pos;
            parser.root.funcs.skip_PS_token(parser.root);
            if (parser.root.error != 0)
                return parser.root.error;
            have_integer = 1;
        }

        /* in valid Type 1 fonts we don't see `RD' or `-|' directly */
        /* since those tokens are handled by parse_subrs and        */
        /* parse_charstrings                                        */
        else if (cur.data[cur.pos] == 82 && ((cur.pos + 6) < limit) && cur.data[cur.pos + 1] == 68 && have_integer == 1)
        {
            parser.root.cursor.pos = start_binary.pos;

            var ret = _t1_read_binary_data(parser);
            if (0 == ret.err)
                return 3;
            have_integer = 0;
        }
        else if (cur.data[cur.pos] == 45 && ((cur.pos + 6) < limit) && cur.data[cur.pos + 1] == 124 &&  have_integer == 1)
        {
            var ret = _t1_read_binary_data(parser);
            if (0 == ret.err)
                return 3;
            have_integer = 0;
        }

        /* look for immediates */
        else if (cur.data[cur.pos] == 47 && ((cur.pos + 2) < limit))
        {
            cur.pos++;

            parser.root.cursor.pos = cur.pos;
            parser.root.funcs.skip_PS_token(parser.root);
            if (parser.root.error != 0)
                return parser.root.error;

            var len = parser.root.cursor.pos - cur.pos;

            if (len > 0 && len < 22 && parser.root.cursor.pos < limit)
            {
                /* now compare the immediate name to the keyword table */
                for (var _i = 0; _i < t1_keywords.length; _i++)
                {
                    var keyword = t1_keywords[_i];
                    var name = keyword.ident;
                    if (name == null)
                        break;

                    if (cur.data[cur.pos] == name.charCodeAt(0) && len == name.length && _strncmp_data(cur, name, len) == 0)
                    {
                        var dict = ((loader.keywords_encountered & 1) != 0) ? 2 : 1;

                        if ((dict & keyword.dict) == 0)
                        {
                            break;
                        }

                        if (((loader.keywords_encountered & 2) == 0) || (name == "CharStrings"))
                        {
                            parser.root.error = t1_load_keyword(face, loader, keyword);
                            if (parser.root.error != 0)
                            {
                                if (parser.root.error == 162)
                                    parser.root.error = 0;
                                else
                                    return parser.root.error;
                            }
                        }
                        break;
                    }
                }
            }

            have_integer = 0;
        }
        else
        {
            parser.root.funcs.skip_PS_token(parser.root);
            if (parser.root.error != 0)
                return parser.root.error;
            have_integer = 0;
        }

        parser.root.funcs.skip_spaces(parser.root);
    }

    return parser.root.error;
}

function t1_init_loader(loader, face)
{
    loader.clear();

    loader.num_glyphs = 0;
    loader.num_chars  = 0;

    /* initialize the tables -- simply set their `init' field to 0 */
    loader.encoding_table.init  = 0;
    loader.charstrings.init     = 0;
    loader.glyph_names.init     = 0;
    loader.subrs.init           = 0;
    loader.swap_table.init      = 0;
    loader.fontdata             = 0;
    loader.keywords_encountered = 0;
}

function t1_done_loader(loader)
{
    var parser = loader.parser;

    /* finalize tables */
    if (loader.encoding_table.funcs.release != null)
        loader.encoding_table.funcs.release(loader.encoding_table);

    if (loader.charstrings.funcs.release != null)
        loader.charstrings.funcs.release(loader.charstrings);

    if (loader.glyph_names.funcs.release != null)
        loader.glyph_names.funcs.release(loader.glyph_names);

    if (loader.swap_table.funcs.release != null)
        loader.swap_table.funcs.release(loader.swap_table);

    if (loader.subrs.funcs.release != null)
        loader.subrs.funcs.release(loader.subrs);

    /* finalize parser */
    T1_Finalize_Parser(parser);
}

function T1_Open_Face(face)
{
    var loader = new T1_Loader();
    var type1 = face.type1;
    var priv = type1.private_dict;

    var psaux = face.psaux;

    t1_init_loader(loader, face);

    /* default values */
    face.ndv_idx          = -1;
    face.cdv_idx          = -1;
    face.len_buildchar    = 0;

    priv.blue_shift       = 7;
    priv.blue_fuzz        = 1;
    priv.lenIV            = 4;
    priv.expansion_factor = parseInt(0.06 * 0x10000);
    priv.blue_scale       = parseInt(0.039625 * 0x10000 * 1000);

    var parser = loader.parser;
    var error = T1_New_Parser(parser, face.stream, face.memory, psaux);
    if (error != 0)
    {
        t1_done_loader(loader);
        return error;
    }

    error = _t1_parse_dict(face, loader, parser.base_dict, parser.base_len);
    if (error != 0)
    {
        t1_done_loader(loader);
        return error;
    }

    error = T1_Get_Private_Dict(parser, psaux);
    if (error != 0)
    {
        t1_done_loader(loader);
        return error;
    }

    error = _t1_parse_dict(face, loader, parser.private_dict, parser.private_len);
    if (error != 0)
    {
        t1_done_loader(loader);
        return error;
    }

    /* ensure even-ness of `num_blue_values' */
    priv.num_blue_values &= 0xFFFFFFFE;

    //#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT

    if (face.blend != null && face.blend.num_default_design_vector != 0 && face.blend.num_default_design_vector != face.blend.num_axis)
    {
        face.blend.num_default_design_vector = 0;
    }

    /* the following can happen for MM instances; we then treat the */
    /* font as a normal PS font                                     */
    if (face.blend != null && (face.blend.num_designs == 0 || face.blend.num_axis == 0))
        T1_Done_Blend(face);

    /* another safety check */
    if (face.blend != null)
    {
        for (var i = 0; i < face.blend.num_axis; i++)
        {
            if (face.blend.design_map[i].num_points == 0)
            {
                T1_Done_Blend(face);
                break;
            }
        }
    }

    if (face.blend != null)
    {
        if (face.len_buildchar > 0)
        {
            face.buildchar = CreateIntArray(face.len_buildchar);
        }
    }
    else
    {
        face.len_buildchar = 0;
    }

    //#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */

    /* now, propagate the subrs, charstrings, and glyphnames tables */
    /* to the Type1 data                                            */
    type1.num_glyphs = loader.num_glyphs;

    if (loader.subrs.init != null)
    {
        loader.subrs.init = null;
        type1.num_subrs   = loader.num_subrs;
        type1.subrs_block = loader.subrs.block;
        type1.subrs       = loader.subrs.elements;
        type1.subrs_len   = loader.subrs.lengths;
    }

    if (loader.charstrings.init == 0)
    {
        error = 3;
    }

    loader.charstrings.init  = 0;
    type1.charstrings_block = loader.charstrings.block;
    type1.charstrings       = loader.charstrings.elements;
    type1.charstrings_len   = loader.charstrings.lengths;

    /* we copy the glyph names `block' and `elements' fields; */
    /* the `lengths' field must be released later             */
    type1.glyph_names_block = loader.glyph_names.block;
    type1.glyph_names = loader.glyph_names.elements;

    for (var ii = 0; ii < type1.num_glyphs; ii++)
    {
        var s = "";
        var p = type1.glyph_names[ii];
        var indC = 0;
        while (true)
        {
            var _c = p.data[p.pos + indC];
            indC++;

            if (_c == 0)
                break;

            s += String.fromCharCode(_c);
        }
        type1.glyph_names[ii] = s;
    }

    loader.glyph_names.block = 0;
    loader.glyph_names.elements = 0;

    /* we must now build type1.encoding when we have a custom array */
    if (type1.encoding_type == 1)
    {
        /* OK, we do the following: for each element in the encoding  */
        /* table, look up the index of the glyph having the same name */
        /* the index is then stored in type1.encoding.char_index, and */
        /* the name to type1.encoding.char_name                       */

        var min_char = 0;
        var max_char = 0;

        var charcode = 0;
        for ( ; charcode < loader.encoding_table.max_elems; charcode++)
        {
            type1.encoding.char_index[charcode] = 0;
            type1.encoding.char_name[charcode] = ".notdef";

            var char_name = loader.encoding_table.elements[charcode];
            if (char_name != null)
            {
                for (var idx = 0; idx < type1.num_glyphs; idx++)
                {
                    var glyph_name = type1.glyph_names[idx];
                    if (_strncmp_data(char_name, glyph_name, glyph_name.length) == 0)
                    {
                        type1.encoding.char_index[charcode] = idx;
                        type1.encoding.char_name[charcode] = glyph_name;

                        /* Change min/max encoded char only if glyph name is */
                        /* not /.notdef                                      */
                        if (".notdef" != glyph_name)
                        {
                            if (charcode < min_char)
                                min_char = charcode;
                            if (charcode >= max_char)
                                max_char = charcode + 1;
                        }
                        break;
                    }
                }
            }
        }

        type1.encoding.code_first = min_char;
        type1.encoding.code_last  = max_char;
        type1.encoding.num_chars  = loader.num_chars;
    }

    t1_done_loader(loader);
    return error;
}

/******************************************************************************/
// tokens
/******************************************************************************/
var t1_keywords = new Array(45);
// PS_FontInfoRec
t1_keywords[0] = create_t1_field4("version", 3, 5, 1, function(obj, val, f) { obj.version = val}, undefined);
t1_keywords[1] = create_t1_field4("Notice", 3, 5, 1, function(obj, val, f) { obj.notice = val}, undefined);
t1_keywords[2] = create_t1_field4("FullName", 3, 5, 1, function(obj, val, f) { obj.full_name = val}, undefined);
t1_keywords[3] = create_t1_field4("FamilyName", 3, 5, 1, function(obj, val, f) { obj.family_name = val}, undefined);
t1_keywords[4] = create_t1_field4("Weight", 3, 5, 1, function(obj, val, f) { obj.weight = val}, undefined);
t1_keywords[5] = create_t1_field4("ItalicAngle", 3, 2, 1, function(obj, val, f) { obj.italic_angle = val}, undefined);
t1_keywords[6] = create_t1_field4("isFixedPitch", 3, 1, 1, function(obj, val, f) { obj.is_fixed_pitch = val}, undefined);
t1_keywords[7] = create_t1_field4("UnderlinePosition", 3, 2, 1, function(obj, val, f) { obj.underline_position = val}, undefined);
t1_keywords[8] = create_t1_field4("UnderlineThickness", 3, 2, 1, function(obj, val, f) { obj.underline_thickness = val}, undefined);

// PS_FontExtraRec
t1_keywords[9] = create_t1_field4("FSType", 2, 2, 1, function(obj, val, f) { obj.fs_type = val}, undefined);

// PS_PrivateRec
t1_keywords[10] = create_t1_field4("UniqueID", 4, 2, 1 | 2, function(obj, val, f) { obj.unique_id = val}, undefined);
t1_keywords[11] = create_t1_field4("lenIV", 4, 2, 2, function(obj, val, f) { obj.lenIV = val}, undefined);
t1_keywords[12] = create_t1_field4("LanguageGroup", 4, 2, 2, function(obj, val, f) { obj.language_group = val}, undefined);
t1_keywords[13] = create_t1_field4("password", 4, 2, 2, function(obj, val, f) { obj.password = val}, undefined);

t1_keywords[14] = create_t1_field4("BlueScale", 4, 4, 2, function(obj, val, f) { obj.blue_scale = val}, undefined);
t1_keywords[15] = create_t1_field4("BlueShift", 4, 2, 2, function(obj, val, f) { obj.blue_shift = val}, undefined);
t1_keywords[16] = create_t1_field4("BlueFuzz", 4, 2, 2, function(obj, val, f) { obj.blue_fuzz = val}, undefined);

t1_keywords[17] = create_t1_field5("BlueValues", 4, 8, 14, 2, function(obj, val, f) { obj.blue_values[f.offset] = val}, function(obj, val, f) { obj.num_blue_values = val});
t1_keywords[18] = create_t1_field5("OtherBlues", 4, 8, 10, 2, function(obj, val, f) { obj.other_blues[f.offset] = val}, function(obj, val, f) { obj.num_other_blues = val});
t1_keywords[19] = create_t1_field5("FamilyBlues", 4, 8, 14, 2, function(obj, val, f) { obj.family_blues[f.offset] = val}, function(obj, val, f) { obj.num_family_blues = val});
t1_keywords[20] = create_t1_field5("FamilyOtherBlues", 4, 8, 10, 2, function(obj, val, f) { obj.family_other_blues[f.offset] = val}, function(obj, val, f) { obj.num_family_other_blues = val});

t1_keywords[21] = create_t1_field5("StdHW", 4, 8, 1, 2, function(obj, val, f) { obj.standard_width[0] = val}, undefined);
t1_keywords[22] = create_t1_field5("StdVW", 4, 8, 1, 2, function(obj, val, f) { obj.standard_height[0] = val}, undefined);
t1_keywords[23] = create_t1_field5("MinFeature", 4, 8, 2, 2, function(obj, val, f) { obj.min_feature[f.offset] = val}, undefined);

t1_keywords[24] = create_t1_field5("StemSnapH", 4, 8, 12, 2, function(obj, val, f) { obj.snap_widths[f.offset] = val}, function(obj, val, f) { obj.num_snap_widths = val});
t1_keywords[25] = create_t1_field5("StemSnapV", 4, 8, 12, 2, function(obj, val, f) { obj.snap_heights[f.offset] = val}, function(obj, val, f) { obj.num_snap_heights = val});

t1_keywords[26] = create_t1_field4("ForceBold", 4, 1, 2, function(obj, val, f) { obj.force_bold = val}, undefined);

// T1_FontRec
t1_keywords[27] = create_t1_field4("FontName", 1, 6, 1, function(obj, val, f) { obj.font_name = val}, undefined);
t1_keywords[28] = create_t1_field4("PaintType", 1, 2, 1, function(obj, val, f) { obj.paint_type = val}, undefined);
t1_keywords[29] = create_t1_field4("FontType", 1, 2, 1, function(obj, val, f) { obj.font_type = val}, undefined);
t1_keywords[30] = create_t1_field4("StrokeWidth", 1, 3, 1, function(obj, val, f) { obj.stroke_width = val}, undefined);

// FT_BBox
t1_keywords[31] = create_t1_field4("FontBBox", 5, 7, 1, undefined, undefined);

// T1_FaceRec
t1_keywords[32] = create_t1_field4("NDV", 7, 2, 2, function(obj, val, f) { obj.ndv_idx = val}, undefined);
t1_keywords[33] = create_t1_field4("CDV", 7, 3, 2, function(obj, val, f) { obj.cdv_idx = val}, undefined);

// PS_BlendRec
t1_keywords[34] = create_t1_field5("CDV", 8, 3, 16, 1, function(obj, val, f) { obj.default_design_vector[f.offset] = val}, function(obj, val, f) { obj.num_default_design_vector = val});

// callbacks
t1_keywords[35] = create_t1_field("FontMatrix", 8, 10, t1_parse_font_matrix, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[36] = create_t1_field("Encoding", 8, 10, t1_parse_encoding, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[37] = create_t1_field("Subrs", 8, 10, t1_parse_subrs, 0, -1, 0, 0, 2, undefined, undefined);
t1_keywords[38] = create_t1_field("CharStrings", 8, 10, t1_parse_charstrings, 0, -1, 0, 0, 2, undefined, undefined);
t1_keywords[39] = create_t1_field("Private", 8, 10, t1_parse_private, 0, -1, 0, 0, 1, undefined, undefined);

t1_keywords[40] = create_t1_field("BlendDesignPositions", 8, 10, t1_parse_blend_design_positions, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[41] = create_t1_field("BlendDesignMap", 8, 10, t1_parse_blend_design_map, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[42] = create_t1_field("BlendAxisTypes", 8, 10, t1_parse_blend_axis_types, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[43] = create_t1_field("WeightVector", 8, 10, t1_parse_weight_vector, 0, -1, 0, 0, 1, undefined, undefined);
t1_keywords[44] = create_t1_field("BuildCharArray", 8, 10, t1_parse_buildchar, 0, -1, 0, 0, 2, undefined, undefined);

/******************************************************************************/
// driver
/******************************************************************************/

function t1_get_glyph_name(face, glyph_index, buffer, buffer_max)
{
    return face.type1.glyph_names[glyph_index];
    //_mem_strcpyn1(buffer, face.type1.glyph_names[glyph_index], buffer_max);
    //return 0;
}

function t1_get_name_index(face, glyph_name)
{
    var _count = face.type1.num_glyphs;
    for (var i = 0; i < _count; i++)
    {
        if (face.type1.glyph_names[i] == glyph_name)
            return i;
    }
    return 0;
}

var t1_service_glyph_dict = new FT_Service_GlyphDictRec(t1_get_glyph_name, t1_get_name_index);

function t1_get_ps_name(face)
{
    return face.type1.font_name;
}
var t1_service_ps_name = new FT_Service_PsFontNameRec(t1_get_ps_name);

var t1_service_multi_masters = new FT_Service_MultiMastersRec(T1_Get_Multi_Master, T1_Set_MM_Design, T1_Set_MM_Blend, T1_Get_MM_Var, T1_Set_Var_Design);

function t1_ps_get_font_info(face)
{
    FT_Error = 0;
    return face.type1.font_info.CreateDublicate();
}
function t1_ps_get_font_extra(face)
{
    FT_Error = 0;
    return face.type1.font_extra.CreateDublicate();
}
function t1_ps_has_glyph_names(face)
{
    return 1;
}
function t1_ps_get_font_private(face)
{
    FT_Error = 0;
    return face.type1.private_dict;
}

function t1_ps_get_font_value(face, key, idx)
{
    var retval = -1;
    var value = null;
    var type1 = face.type1;

    switch (key)
    {
        case 0:
            value = type1.font_type;
            break;

        case 1:
            switch (idx)
            {
                case 0:
                    value = type1.font_matrix.xx;
                    break;
                case 1:
                    value = type1.font_matrix.xy;
                    break;
                case 2:
                    value = type1.font_matrix.yx;
                    break;
                case 3:
                    value = type1.font_matrix.yy;
                    break;
            }
            break;

        case 2:
            switch (idx)
            {
                case 0:
                    value = type1.font_bbox.xMin;
                    break;
                case 1:
                    value = type1.font_bbox.yMin;
                    break;
                case 2:
                    value = type1.font_bbox.xMax;
                    break;
                case 3:
                    value = type1.font_bbox.yMax;
                    break;
            }
            break;

        case 3:
            value = type1.paint_type;
            break;

        case 4:
            value = type1.font_name;
            break;

        case 5:
            value = type1.private_dict.unique_id;
            break;

        case 6:
            value = type1.num_glyphs;
            break;

        case 7:
            if (idx < type1.num_glyphs)
            {
                value = type1.glyph_names[idx];
            }
            break;

        case 8:
            if (idx < type1.num_glyphs)
            {
                value = type1.charstrings[idx];
            }
            break;

        case 9:
            value = type1.encoding_type;
            break;

        case 10:
            if (type1.encoding_type == 1 && idx < type1.encoding.num_chars)
            {
                value = type1.encoding.char_name[idx];
            }
            break;

        case 11:
            value = type1.num_subrs;
            break;

        case 12:
            if (idx < type1.num_subrs)
            {
                value = type1.subrs[idx];
            }
            break;

        case 13:
            value = type1.private_dict.standard_width[0];
            break;

        case 14:
            value = type1.private_dict.standard_height[0];
            break;

        case 15:
            value = type1.private_dict.num_blue_values;
            break;

        case 16:
            if (idx < type1.private_dict.num_blue_values)
            {
                value = type1.private_dict.blue_values[idx];
            }
            break;

        case 24:
            value = type1.private_dict.blue_scale;
            break;

        case 17:
            value = type1.private_dict.blue_fuzz;
            break;

        case 25:
            value = type1.private_dict.blue_shift;
            break;

        case 18:
            value = type1.private_dict.num_other_blues;
            break;

        case 19:
            if (idx < type1.private_dict.num_other_blues)
            {
                value = type1.private_dict.other_blues[idx];
            }
            break;

        case 20:
            value = type1.private_dict.num_family_blues;
            break;

        case 21:
            if (idx < type1.private_dict.num_family_blues)
            {
                value = type1.private_dict.family_blues[idx];
            }
            break;

        case 22:
            value = type1.private_dict.num_family_other_blues;
            break;

        case 23:
            if (idx < type1.private_dict.num_family_other_blues)
            {
                value = type1.private_dict.family_other_blues[idx];
            }
            break;

        case 26:
            value = type1.private_dict.num_snap_widths;
            break;

        case 27:
            if (idx < type1.private_dict.num_snap_widths)
            {
                value = type1.private_dict.snap_widths[idx];
            }
            break;

        case 28:
            value = type1.private_dict.num_snap_heights;
            break;

        case 29:
            if (idx < type1.private_dict.num_snap_heights)
            {
                value = type1.private_dict.snap_heights[idx];
            }
            break;

        case 31:
            value = type1.private_dict.round_stem_up;
            break;

        case 30:
            value = type1.private_dict.force_bold;
            break;

        case 32:
            value = type1.private_dict.min_feature[idx];
            break;

        case 33:
            value = type1.private_dict.lenIV;
            break;

        case 34:
            value = type1.private_dict.password;
            break;

        case 35:
            value= type1.private_dict.language_group;
            break;

        case 41:
            value = type1.font_info.is_fixed_pitch;
            break;

        case 42:
            value = type1.font_info.underline_position;
            break;

        case 43:
            value = type1.font_info.underline_thickness;
            break;

        case 44:
            value = type1.font_extra.fs_type;
            break;

        case 36:
            value = type1.font_info.version;
            break;

        case 37:
            value = type1.font_info.notice;
            break;

        case 38:
            value = type1.font_info.full_name;
            break;

        case 39:
            value = type1.font_info.family_name;
            break;

        case 40:
            value = type1.font_info.weight;
            break;

        case 45:
            value = type1.font_info.italic_angle;
            break;

        default:
            break;
    }

    return retval;
}

var t1_service_ps_info = new FT_Service_PsInfoRec(t1_ps_get_font_info, t1_ps_get_font_extra, t1_ps_has_glyph_names, t1_ps_get_font_private, t1_ps_get_font_value);
var t1_service_kerning = new FT_Service_KerningRec(T1_Get_Track_Kerning);

var t1_services = new Array(6);
t1_services[0] = new FT_ServiceDescRec(FT_SERVICE_ID_POSTSCRIPT_FONT_NAME,t1_service_ps_name);
t1_services[1] = new FT_ServiceDescRec(FT_SERVICE_ID_GLYPH_DICT,t1_service_glyph_dict);
t1_services[2] = new FT_ServiceDescRec(FT_SERVICE_ID_XF86_NAME,FT_XF86_FORMAT_TYPE_1);
t1_services[3] = new FT_ServiceDescRec(FT_SERVICE_ID_POSTSCRIPT_INFO,t1_service_ps_info);
t1_services[4] = new FT_ServiceDescRec(FT_SERVICE_ID_KERNING,t1_service_kerning);
t1_services[5] = new FT_ServiceDescRec(FT_SERVICE_ID_MULTI_MASTERS,t1_service_multi_masters);

function Get_Interface(driver, t1_interface)
{
    return ft_service_list_lookup(t1_services, t1_interface);
}

function Get_Kerning(face, left_glyph, right_glyph, kerning)
{
    kerning.x = 0;
    kerning.y = 0;

    if (face.afm_data != null)
        T1_Get_Kerning(face.afm_data, left_glyph, right_glyph, kerning);

    return 0;
}

function T1_Driver_Class()
{
    this.flags = 0x501;
    this.name = "type1";
    this.version = 0x10000;
    this.requires = 0x20000;

    this.module_interface = null;

    this.init = T1_Driver_Init;
    this.done = T1_Driver_Done;
    this.get_interface = Get_Interface;

    this.face_object_size = 0;
    this.size_object_size = 0;
    this.slot_object_size = 0;

    this.init_face = T1_Face_Init;
    this.done_face = T1_Face_Done;

    this.init_size = T1_Size_Init;
    this.done_size = T1_Size_Done;

    this.init_slot = T1_GlyphSlot_Init;
    this.done_slot = T1_GlyphSlot_Done;

    //#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    this.set_char_sizes = ft_stub_set_char_sizes;
    this.set_pixel_sizes = ft_stub_set_pixel_sizes;
    //#endif

    this.load_glyph = T1_Load_Glyph;

    this.get_kerning = Get_Kerning;
    this.attach_file = T1_Read_Metrics;
    this.get_advances = T1_Get_Advances;

    this.request_size = T1_Size_Request;
    this.select_size = null;
}

function T1_Driver()
{
    this.clazz = null;      // FT_Module_Class
    this.library = null;    // FT_Library
    this.memory = null;     // FT_Memory
    this.generic = null;    // FT_Generic

    this.clazz = new T1_Driver_Class();
    this.faces_list = [];
    this.extensions = null;
    this.glyph_loader = null;

    this.open_face = function(stream, face_index)
    {
        FT_Error = 0;
        var face = new T1_Face();
        var internal = new FT_Face_Internal();

        face.driver = this;
        face.memory = this.memory;
        face.stream = stream;

        //#ifdef FT_CONFIG_OPTION_INCREMENTAL
        face.internal = internal;
        face.internal.incremental_interface = null;
        //#endif

        var err1 = this.clazz.init_face(stream, face, face_index);

        if (err1 != 0)
        {
            face = null;
            FT_Error = err1;
            return null;
        }

        var err2 = find_unicode_charmap(face);
        if (err2 != 0 && err2 != 38)
        {
            face = null;
            FT_Error = err2;
            return null;
        }

		FT_Error = 0;
        return face;
    }
}

function create_t1_driver(library)
{
    var driver = new T1_Driver();
    driver.library = library;
    driver.memory = library.Memory;

    driver.clazz = new T1_Driver_Class();
    return driver;
}
var FT_Error = 0;
function FT_Library()
{
    this.Memory = null;
    this.generic = new FT_Generic();
    this.version_major = 2;
    this.version_minor = 4;
    this.version_patch = 8;

    this.modules = [];
    this.renderers = [];
    this.cur_renderer = null;
    this.auto_hinter = null;

    this.raster_pool_size = 16384;
    this.raster_pool = null;

    this.m_bUseCIDs = true;

    this.tt_hint_props = new LibraryHintingParams();

    // #ifdef subpixel
    //FT_LcdFilter             lcd_filter;
    //FT_Int                   lcd_extra;        /* number of extra pixels */
    //FT_Byte                  lcd_weights[7];   /* filter weights, if any */
    //FT_Bitmap_LcdFilterFunc  lcd_filter_func;  /* filtering callback     */
    // #endif

    //#ifdef FT_CONFIG_OPTION_PIC
    //FT_PIC_Container   pic_container;
    //#endif

    this.Init = function()
    {
        this.Memory = new FT_Memory();
        //this.raster_pool = this.Memory.Alloc(this.raster_pool_size);
        // теперь пул для каждого рендерера свой
        // и он хранится непосредственно в рендерере.
        // если не создавать постоянно рендереры - то смысл такой же. У нас так и есть.
        this.FT_Add_Default_Modules();
    }
    this.FT_Add_Module = function(module)
    {
        var clazz = module.clazz;
        if (clazz == null)
            return 6;

        var version_control = (this.version_major << 16 | this.version_minor);
        if (clazz.requires > version_control)
            return 4;

        var mod_count = this.modules.length;
        for (var i = 0; i < mod_count; i++)
        {
            var _module = this.modules[i];
            if (_module.clazz.name == clazz.name)
            {
                if (clazz.version < _module.clazz.version)
                    return 5;
                this.modules.splice(i,1);
                break;
            }
        }

        var error = 0;
        if ((clazz.flags & 2) != 0)
        {
            error = this.ft_add_renderer(module);
            if (0 != error)
            {
                //delete module;
                return error;
            }
        }
        if ((clazz.flags & 4) != 0)
        {
            this.auto_hinter = module;
        }
        if ((clazz.flags & 1) != 0)
        {
            if (0 == (clazz.flags & 512))
            {
                module.glyph_loader = new FT_GlyphLoader();
            }
        }

        if (null != clazz.init)
            clazz.init(module);

        this.modules[mod_count] = module;
        return error;
    }
    this.FT_Add_Default_Modules = function()
    {
        // drivers
        var driver_tt = create_tt_driver(this);
        var driver_cff = create_cff_driver(this);
        var driver_t1 = create_t1_driver(this);
        //var driver_cid = create_cid_driver(this);
        this.FT_Add_Module(driver_tt);
        this.FT_Add_Module(driver_cff);
        this.FT_Add_Module(driver_t1);
        //this.FT_Add_Module(driver_cid);

        // modules
        var mod_ps_names = create_psnames_module(this);
        var mod_psaux = create_psaux_module(this);
        var mod_sfnt = create_sfnt_module(this);
        this.FT_Add_Module(mod_ps_names);
        this.FT_Add_Module(mod_psaux);
        this.FT_Add_Module(mod_sfnt);

        // autohinter
        this.auto_hinter = null;

        // renderers
        var mod_rend1 = create_renderer_smooth_module(this);
        this.FT_Add_Module(mod_rend1);
    }
    this.ft_add_renderer = function(module)
    {
        var error = 0;
        var clazz = module.clazz;

        module.glyph_format = clazz.glyph_format;

        if ((clazz.glyph_format == 1869968492) && (clazz.raster_class.raster_new))
        {
            module.raster = new TRaster();
            error = clazz.raster_class.raster_new(this.Memory, module.raster);

            if (error != 0)
                return error;

            module.raster_render = clazz.raster_class.raster_render;
            module.render = clazz.render_glyph;
        }

        this.renderers[this.renderers.length] = module;

        this.ft_set_current_renderer();
        return error;
    }
    this.ft_set_current_renderer = function()
    {
        this.cur_renderer = null;
        var count_r = this.renderers.length;
        for (var i = 0; i < count_r; i++)
        {
            if (this.renderers[i].glyph_format == 1869968492)
            {
                this.cur_renderer = this.renderers[i];
                return;
            }
        }
    }
    this.FT_Open_Face = function(args, face_index)
    {
        var face = null;
        var external_stream = (args.stream && (args.flags & 0x02) != 0) ? true : false;
        if (external_stream === true)
        {
            var stream = args.stream;

            // пробегаем по всем драйверам - и ищем первый, который сможет открыть
            var dr_len = this.modules.length;
            for (var i = 0; i < dr_len; i++)
            {
                if (0 == (this.modules[i].clazz.flags & 1))
                    continue;

                var driver = this.modules[i];
                face = driver.open_face(stream, face_index);

                if (0 != FT_Error)
                {
                    //#ifdef FT_CONFIG_OPTION_MAC_FONTS
                    if (driver.clazz.name == "truetype" && ((FT_Error & 0xFF) == 142))
                    {
                        face = this.open_face_PS_from_sfnt_stream(stream, face_index);
                        if (FT_Error != 0)
                            return null;
                    }
                    //#endif
                }
                else
                {
                    break;
                }

                if ((FT_Error & 0xFF) != 2)
                {
                    if (((FT_Error & 0xFF) == 85) ||
                        ((FT_Error & 0xFF) == 81))
                    {
                        //#ifdef FT_CONFIG_OPTION_MAC_FONTS
                        face = this.load_mac_face(stream, face_index);
                        if (FT_Error == 0)
                            return face;
                        FT_Error = 2;
                        face = null;
                        return face;
                        //#endif
                    }
                }
            }

            if (null == face)
                return face;

            face.face_flags |= (1 << 10);
            face.driver.faces_list[face.driver.faces_list.length] = face;

            if (face_index >= 0)
            {
                var slot = FT_New_GlyphSlot(face);
                slot = null;
                if (FT_Error != 0)
                {
                    face = null;
                    return null;
                }

                /* finally, allocate a size object for the face */
                {
                    var size = FT_New_Size(face);
                    if (FT_Error != 0)
                    {
                        face = null;
                        return null;
                    }

                    face.size = size;
                }
            }

            /* some checks */

            if ((face.face_flags & 1) != 0)
            {
                if (face.height < 0)
                    face.height = -face.height;

                if ((face.face_flags & (1 << 5)) == 0)
                    face.max_advance_height = face.height;
            }

            if ((face.face_flags & (1 << 1)) != 0)
            {
                var _num = face.num_fixed_sizes;
                for (var i = 0; i < _num; i++)
                {
                    var bsize = face.available_sizes[i];

                    if (bsize.height < 0)
                        bsize.height = -bsize.height;
                    if (bsize.x_ppem < 0)
                        bsize.x_ppem = -bsize.x_ppem;
                    if (bsize.y_ppem < 0)
                        bsize.y_ppem = -bsize.y_ppem;
                }
            }

            /* initialize internal face data */
            {
                var internal = face.internal;

                internal.transform_matrix.xx = 0x10000;
                internal.transform_matrix.xy = 0;
                internal.transform_matrix.yx = 0;
                internal.transform_matrix.yy = 0x10000;

                internal.transform_delta.x = 0;
                internal.transform_delta.y = 0;
            }

            FT_Error = 0;
            return face;
        }
        // пока открываем только из уже созданного стрима
        FT_Error = 6;
        return null;
    }

    this.open_face_PS_from_sfnt_stream = function(stream, face_index)
    {
    }
    this.load_mac_face = function(stream, face_index)
    {
    }

    this.FT_Get_Module_Interface = function(name)
    {
        var module = this.FT_Get_Module(name);
        return (null != module) ? module.clazz.module_interface : null;
    }
    this.FT_Get_Module = function(name)
    {
        var count = this.modules.length;
        for (var i = 0; i < count; i++)
        {
            if (this.modules[i].clazz.name == name)
                return this.modules[i];
        }
        return null;
    }
}

// API
function FT_Select_Size(face, strike_index)
{
    if (!face || (face.face_flags & 2) == 0)
        return 35;

    if (strike_index < 0 || strike_index >= face.num_fixed_sizes)
        return 6;

    var clazz = face.driver.clazz;
    if (clazz.select_size)
    {
        return clazz.select_size(face.size, strike_index);
    }

    FT_Select_Metrics(face, strike_index);
    return 0;
}

function FT_Select_Metrics(face, strike_index)
{
    var metrics = face.size.metrics;
    var bsize   = face.available_sizes[strike_index];

    metrics.x_ppem = (FT_Common.IntToUInt(bsize.x_ppem + 32) >>> 6) & 0xFFFF;
    metrics.y_ppem = (FT_Common.IntToUInt(bsize.y_ppem + 32) >>> 6) & 0xFFFF;

    if ((face.face_flags & 1) != 0)
    {
        metrics.x_scale = FT_DivFix(bsize.x_ppem, face.units_per_EM);
        metrics.y_scale = FT_DivFix(bsize.y_ppem, face.units_per_EM);

        ft_recompute_scaled_metrics(face, metrics);
    }
    else
    {
        metrics.x_scale     = 1 << 16;
        metrics.y_scale     = 1 << 16;
        metrics.ascender    = bsize.y_ppem;
        metrics.descender   = 0;
        metrics.height      = bsize.height << 6;
        metrics.max_advance = bsize.x_ppem;
    }
}

function ft_recompute_scaled_metrics(face, metrics)
{
    metrics.ascender    = FT_PIX_CEIL(FT_MulFix(face.ascender, metrics.y_scale));
    metrics.descender   = FT_PIX_FLOOR(FT_MulFix(face.descender, metrics.y_scale));
    metrics.height      = FT_PIX_ROUND(FT_MulFix(face.height, metrics.y_scale));
    metrics.max_advance = FT_PIX_ROUND(FT_MulFix(face.max_advance_width, metrics.x_scale));
}

function FT_Request_Size(face, req)
{
    var strike_index = 0;

    if (!face)
        return 35;

    if (!req || req.width < 0 || req.height < 0 || req.type >= 5)
        return 6;

    var clazz = face.driver.clazz;

    if (clazz.request_size)
    {
        return clazz.request_size(face.size, req);
    }

    if (((face.face_flags & 1) == 0) && ((face.face_flags & 2) != 0))
    {
        FT_Error = 0;
        strike_index = FT_Match_Size(face, req, 0);
        var error = FT_Error;
        FT_Error = 0;
        if (error != 0)
            return error;

        return FT_Select_Size(face, strike_index);
    }

    FT_Request_Metrics(face, req);
    return 0;
}

function FT_Request_Metrics(face, req)
{
    var metrics = face.size.metrics;

    if ((face.face_flags & 1) != 0)
    {
        var w = 0, h = 0, scaled_w = 0, scaled_h = 0;

        switch (req.type)
        {
            case 0:
                w = h = face.units_per_EM;
                break;

            case 1:
                w = h = face.ascender - face.descender;
                break;

            case 2:
                w = face.bbox.xMax - face.bbox.xMin;
                h = face.bbox.yMax - face.bbox.yMin;
                break;

            case 3:
                w = face.max_advance_width;
                h = face.ascender - face.descender;
                break;

            case 4:
                metrics.x_scale = req.width;
                metrics.y_scale = req.height;
                if (metrics.x_scale == 0)
                    metrics.x_scale = metrics.y_scale;
                else if (metrics.y_scale == 0)
                    metrics.y_scale = metrics.x_scale;

                scaled_w = FT_MulFix(face.units_per_EM, metrics.x_scale);
                scaled_h = FT_MulFix(face.units_per_EM, metrics.y_scale);

                metrics.x_ppem = (FT_Common.IntToUInt(scaled_w + 32) >>> 6) & 0xFFFF;
                metrics.y_ppem = (FT_Common.IntToUInt(scaled_h + 32) >>> 6) & 0xFFFF;

                ft_recompute_scaled_metrics( face, metrics );
                return;

            case 5:
                break;
        }

        /* to be on the safe side */
        if (w < 0)
            w = -w;

        if (h < 0)
            h = -h;

        scaled_w = FT_REQUEST_WIDTH(req);
        scaled_h = FT_REQUEST_HEIGHT(req);

        if (req.width != 0)
        {
            metrics.x_scale = FT_DivFix(scaled_w, w);

            if (req.height != 0)
            {
                metrics.y_scale = FT_DivFix(scaled_h, h);

                if (req.type == 3)
                {
                    if (metrics.y_scale > metrics.x_scale)
                        metrics.y_scale = metrics.x_scale;
                    else
                        metrics.x_scale = metrics.y_scale;
                }
            }
            else
            {
                metrics.y_scale = metrics.x_scale;
                scaled_h = FT_MulDiv(scaled_w, h, w);
            }
        }
        else
        {
            metrics.x_scale = metrics.y_scale = FT_DivFix(scaled_h, h);
            scaled_w = FT_MulDiv(scaled_h, w, h);
        }

        if (req.type != 0)
        {
            scaled_w = FT_MulFix(face.units_per_EM, metrics.x_scale);
            scaled_h = FT_MulFix(face.units_per_EM, metrics.y_scale);
        }

        metrics.x_ppem = (FT_Common.IntToUInt(scaled_w + 32) >>> 6) & 0xFFFF;
        metrics.y_ppem = (FT_Common.IntToUInt(scaled_h + 32) >>> 6) & 0xFFFF;

        ft_recompute_scaled_metrics(face, metrics);
    }
    else
    {
        metrics.x_ppem = 0;
        metrics.y_ppem = 0;

        metrics.ascender = 0;
        metrics.descender = 0;
        metrics.height = 0;
        metrics.max_advance = 0;

        metrics.x_scale = 1 << 16;
        metrics.y_scale = 1 << 16;
    }
}

function FT_REQUEST_WIDTH(req)
{
    return (req.horiResolution != 0) ? parseInt((req.width * req.horiResolution + 36) / 72) : req.width;
}

function FT_REQUEST_HEIGHT(req)
{
    return (req.vertResolution != 0) ? parseInt((req.height * req.vertResolution + 36) / 72) : req.height;
}

function FT_Match_Size(face, req, ignore_width)
{
    if ((face.face_flags & 2) == 0)
    {
        FT_Error = 35;
        return 0;
    }

    if (req.type != 0)
    {
        FT_Error = 7;
        return 0;
    }

    var w = FT_REQUEST_WIDTH(req);
    var h = FT_REQUEST_HEIGHT(req);

    if (req.width != 0 && req.height == 0)
        h = w;
    else if (req.width == 0 && req.height != 0)
        w = h;

    w = FT_PIX_ROUND(w);
    h = FT_PIX_ROUND(h);

    var num_fs = face.num_fixed_sizes;
    for (var i = 0; i < num_fs; i++)
    {
        var bsize = face.available_sizes[i];

        if (h != FT_PIX_ROUND(bsize.y_ppem))
            continue;

        if (w == FT_PIX_ROUND(bsize.x_ppem) || (ignore_width != 0))
        {
            FT_Error = 0;
            return i;
        }
    }

    FT_Error = 23;
    return 0;
}

function FT_Set_Char_Size(face, char_width, char_height, horz_resolution, vert_resolution)
{
    var req = new FT_Size_RequestRec();

    if (0 == char_width)
        char_width = char_height;
    else if (0 == char_height)
        char_height = char_width;

    if (0 == horz_resolution)
        horz_resolution = vert_resolution;
    else if (0 == vert_resolution)
        vert_resolution = horz_resolution;

    if (char_width < 64)
        char_width = 64;
    if (char_height < 64)
        char_height = 64;

    if (0 == horz_resolution)
        horz_resolution = vert_resolution = 72;

    req.type           = 0;
    req.width          = char_width;
    req.height         = char_height;
    req.horiResolution = horz_resolution;
    req.vertResolution = vert_resolution;

    return FT_Request_Size(face, req);
}

function FT_Set_Pixel_Sizes(face, pixel_width, pixel_height)
{
    var req = new FT_Size_RequestRec();

    if (pixel_width == 0)
        pixel_width = pixel_height;
    else if (pixel_height == 0)
        pixel_height = pixel_width;

    if (pixel_width < 1)
        pixel_width = 1;
    if (pixel_height < 1)
        pixel_height = 1;

    /* use `>=' to avoid potential compiler warning on 16bit platforms */
    if (pixel_width >= 0xFFFF)
        pixel_width = 0xFFFF;
    if (pixel_height >= 0xFFFF)
        pixel_height = 0xFFFF;

    req.type           = 0;
    req.width          = pixel_width << 6;
    req.height         = pixel_height << 6;
    req.horiResolution = 0;
    req.vertResolution = 0;

    return FT_Request_Size(face, req);
}

function FT_Get_First_Char(face)
{
    var result = {gindex:0,char_code:0};
    if (face && face.charmap && face.num_glyphs != 0)
    {
        result.gindex = FT_Get_Char_Index(face, 0);
        if (result.gindex == 0 || result.gindex >= face.num_glyphs)
            result = FT_Get_Next_Char(face, 0);
    }
    return result;
}

function FT_Get_Next_Char(face, charcode)
{
    var result = {gindex:0,char_code:0};
    if (face && face.charmap && face.num_glyphs != 0)
    {
        var code = charcode;
        var cmap = face.charmap;
        var _cmap = __FT_CMapRec(cmap);

        do
        {
            var _clazz = _cmap.clazz.clazz;
            if (undefined == _clazz)
                _clazz = _cmap.clazz;

            result = _clazz.char_next(cmap, code);
            code = result.char_code;
        } while (result.gindex >= face.num_glyphs);

        result.char_code = (result.gindex == 0) ? 0 : code;
    }
    return result;
}

function FT_Get_Char_Index(face, charcode)
{
    var result = 0;
    if (face && face.charmap)
    {
        var _clazz = __FT_CMapRec(face.charmap).clazz;
        if (undefined != _clazz.clazz)
            _clazz = _clazz.clazz;

        result = _clazz.char_index(face.charmap, charcode);
    }
    return result;
}

function FT_Get_Charmap_Index(charmap)
{
    if (!charmap || !charmap.face)
        return -1;

    var i = 0;
    for (; i < charmap.face.num_charmaps; i++)
        if (charmap.face.charmaps[i] == charmap)
            break;

    //#ifdef FT_MAX_CHARMAP_CACHEABLE
    if (i > 15)
        return -i;
    //#endif
    return i;
}

function FT_Set_Charmap(face, cmap)
{
    if (!face)
        return 35;

    var len = face.num_charmaps;
    if (0 == len)
        return 38;

    if (FT_Get_CMap_Format(cmap) == 14)
        return 6;

    for (var i = 0; i < len; i++)
    {
        if (face.charmaps[i] == cmap)
        {
            //#ifdef FT_MAX_CHARMAP_CACHEABLE
            if (i > 15)
                continue;
            //#endif
            face.charmap = face.charmaps[i];
            return 0;
        }
    }
    return 6;
}

function FT_Get_CMap_Format(cmap)
{
    var charmap = __FT_CharmapRec(cmap);
    if (!charmap || !charmap.face)
        return -1;

    var service = FT_FACE_FIND_SERVICE(charmap.face, "tt-cmaps");

    if (service == null)
        return -1;

    var cmap_info = new TT_CMapInfo();
    service.get_cmap_info(cmap, cmap_info);
    if (FT_Error != 0)
        return -1;
    
    return cmap_info.format;
}


function FT_Glyph_Get_CBox(glyph, bbox_mode, acbox)
{
    acbox.xMin = acbox.yMin = acbox.xMax = acbox.yMax = 0;

    if (!glyph || !glyph.clazz)
        return;

    var clazz = glyph.clazz;
    if (!clazz.glyph_bbox)
        return;

    clazz.glyph_bbox(glyph, acbox);

    if (bbox_mode == 1 || bbox_mode == 3)
    {
        acbox.xMin = FT_PIX_FLOOR(acbox.xMin);
        acbox.yMin = FT_PIX_FLOOR(acbox.yMin);
        acbox.xMax = FT_PIX_CEIL(acbox.xMax);
        acbox.yMax = FT_PIX_CEIL(acbox.yMax);
    }

    if (bbox_mode == 2 || bbox_mode == 3)
    {
        acbox.xMin >>= 6;
        acbox.yMin >>= 6;
        acbox.xMax >>= 6;
        acbox.yMax >>= 6;
    }
}

function FT_Get_Glyph(slot)
{
    FT_Error = 0;
    var clazz = null;

    if (!slot)
        return 37;

    if (slot.format == 1651078259)
        clazz = ft_bitmap_glyph_class;
    else if (slot.format == 1869968492)
        clazz = ft_outline_glyph_class;
    else
    {
        var render = FT_Lookup_Renderer(slot.library, slot.format);
        if (render)
            clazz = render.glyph_class;
    }

    if (!clazz)
    {
        FT_Error = 18;
        return null;
    }

    var glyph = ft_new_glyph(slot.library, clazz);
    if (FT_Error != 0)
        return null;

    glyph.advance.x = slot.advance.x << 10;
    glyph.advance.y = slot.advance.y << 10;

    FT_Error = clazz.glyph_init(glyph, slot);

    if (FT_Error != 0)
    {
        FT_Done_Glyph(glyph);
        glyph = null;
    }
    else
        return glyph;

    return null;
}

function FT_Done_Glyph(glyph)
{
    return 0;
}

function FT_Get_Name_Index(face, glyph_name)
{
    if (face && ((face.face_flags & 512) != 0))
    {
        var service = face.internal.services.service_GLYPH_DICT;
        if (null != service)
            service = FT_FACE_FIND_SERVICE(face, "glyph-dict");

        if (service && service.name_index)
            return service.name_index(face, glyph_name);
    }
    return 0;
}
function FT_Get_Glyph_Name(face, glyph_index, buffer, buffer_max)
{
    if (face && (glyph_index <= face.num_glyphs) && ((face.face_flags & 512) != 0))
    {
        var service = face.internal.services.service_GLYPH_DICT;
        if (null != service)
            service = FT_FACE_FIND_SERVICE(face, "glyph-dict");

        if (service && service.get_name)
            return service.get_name(face, glyph_index, buffer, buffer_max);
    }
    return 0;
}

function FT_Get_X11_Font_Format(face)
{
    if (face)
        return FT_FACE_FIND_SERVICE(face, "xf86-driver-name");
    return null;
}

function FT_Lookup_Renderer(library, format)
{
    var r = library.renderers;
    var c = r.length;

    for (var i = 0; i < c; i++)
    {
        if (r[i].glyph_format == format)
            return r[i];
    }

    return null;
}

function FT_Set_Renderer(library, renderer)
{
    if (!library)
        return 33;

    if (!renderer)
        return 6;

    if (renderer.glyph_format == 1869968492)
        library.cur_renderer = renderer;

    return 0;
}

function ft_lookup_glyph_renderer(slot)
{
    var library = slot.library;
    var res = library.cur_renderer;

    if (!res || (res.glyph_format != slot.format))
        res = FT_Lookup_Renderer(library, slot.format);

    return res;
}

function ft_set_current_renderer(library)
{
    library.cur_renderer = FT_Lookup_Renderer(library, 1869968492);
}



function FT_Load_Glyph(face, glyph_index, load_flags)
{
    if (!face || !face.size || !face.glyph)
        return 35;

    var slot = face.glyph;
    ft_glyphslot_clear(slot);

    var driver  = face.driver;
    var library = driver.library;
    var hinter  = library.auto_hinter;
    var autohint = false;

    /* resolve load flags dependencies */
    if (load_flags & 1024)
        load_flags |= (1 | 2048);

    if (load_flags & 1)
    {
        load_flags |= (2 | 8);
        load_flags &= ~4;
    }

    // if (hinter && ...){}
    // TODO:

    if (true === autohint)
    {
        // TODO:
    }
    else
    {
        var error = driver.clazz.load_glyph(slot, face.size, glyph_index, load_flags);
        if (error != 0)
            return error;

        if (slot.format == 1869968492)
        {
            /* check that the loaded outline is correct */
            error = FT_Outline_Check(slot.outline);
            if (error != 0)
                return error;

            //#ifdef GRID_FIT_METRICS
            if (0 == (load_flags & 2))
                ft_glyphslot_grid_fit_metrics( slot, ((load_flags & 16) != 0) ? true : false);
            //#endif
        }
    }

    /* compute the advance */
    if (0 != (load_flags & 16))
    {
        slot.advance.x = 0;
        slot.advance.y = slot.metrics.vertAdvance;
    }
    else
    {
        slot.advance.x = slot.metrics.horiAdvance;
        slot.advance.y = 0;
    }

    /* compute the linear advance in 16.16 pixels */
    if ((load_flags & 8192) == 0 && (0 != (face.face_flags & 1)))
    {
        var metrics = face.size.metrics;

        /* it's tricky! */
        slot.linearHoriAdvance = FT_MulDiv(slot.linearHoriAdvance, metrics.x_scale, 64);
        slot.linearVertAdvance = FT_MulDiv(slot.linearVertAdvance, metrics.y_scale, 64);
    }

    if ((load_flags & 2048) == 0)
    {
        var internal = face.internal;

        /* now, transform the glyph image if needed */
        if (internal.transform_flags != 0)
        {
            /* get renderer */
            var renderer = ft_lookup_glyph_renderer(slot);
            if (renderer != null)
                error = renderer.clazz.transform_glyph(renderer, slot, internal.transform_matrix, internal.transform_delta);
            else if (slot.format == 1869968492)
            {
                /* apply `standard' transformation if no renderer is available */
                if ((internal.transform_flags & 1) != 0)
                    FT_Outline_Transform(slot.outline, internal.transform_matrix);

                if ((internal.transform_flags & 2) != 0)
                    FT_Outline_Translate(slot.outline, internal.transform_delta.x, internal.transform_delta.y);
            }

            /* transform advance */
            FT_Vector_Transform(slot.advance, internal.transform_matrix);
        }
    }

    /* do we need to render the image now? */
    if ((error == 0) && (slot.format != 1651078259) && (slot.format != 1668246896) && ((load_flags & 4) != 0))
    {
        var mode = FT_LOAD_TARGET_MODE(load_flags);

        if (mode == 0 && (load_flags & 4096))
            mode = 2;

        error = FT_Render_Glyph(slot, mode);
    }
    return error;
}

function FT_Load_Char(face, char_code, load_flags)
{
    var glyph_index = 0;

    if ( !face )
        return 35;

    glyph_index = char_code;
    if (face.charmap)
        glyph_index = FT_Get_Char_Index(face, char_code);

    return FT_Load_Glyph(face, glyph_index, load_flags);
}

function FT_Set_Transform(face, matrix, delta)
{
    if (!face)
        return;

    var internal = face.internal;
    internal.transform_flags = 0;

    var m = internal.transform_matrix;
    if (!matrix)
    {
        m.xx = 0x10000;
        m.xy = 0;
        m.yx = 0;
        m.yy = 0x10000;
    }
    else
    {
        m.xx = matrix.xx;
        m.xy = matrix.xy;
        m.yx = matrix.yx;
        m.yy = matrix.yy;
    }

    /* set transform_flags bit flag 0 if `matrix' isn't the identity */
    if ((m.xy | m.yx) != 0 || m.xx != 0x10000 || m.yy != 0x10000)
        internal.transform_flags |= 1;

    var d = internal.transform_delta;
    if (!delta)
    {
        d.x = 0;
        d.y = 0;
    }
    else
    {
        d.x = delta.x;
        d.y = delta.y;
    }

    /* set transform_flags bit flag 1 if `delta' isn't the null vector */
    if ((d.x | d.y) != 0)
        internal.transform_flags |= 2;
}

function FT_Render_Glyph(slot, render_mode)
{
    if (!slot || !slot.face)
        return 6;

    var library = slot.library;
    return FT_Render_Glyph_Internal(library, slot, render_mode);
}

function FT_Render_Glyph_Internal(library, slot, render_mode)
{
    var error = 0;
    var renderer = null;
    var update = false;

    /* if it is already a bitmap, no need to do anything */
    switch (slot.format)
    {
        case 1651078259:   /* already a bitmap, don't do anything */
            break;

        default:
        {
            /* small shortcut for the very common case */
            if (slot.format == 1869968492)
            {
                renderer = library.cur_renderer;
            }
            else
            {
                renderer = FT_Lookup_Renderer(library, slot.format);
            }

            error = 7;
            while (renderer != null)
            {
                error = renderer.render(renderer, slot, render_mode, null);
                if ((error == 0) || ((error & 0xFF) != 19))
                    break;

                renderer = FT_Lookup_Renderer(library, slot.format);
                update   = true;
            }
            if (error == 0 && update && renderer)
                FT_Set_Renderer(library, renderer);
        }
    }

    return error;
}

function FT_Get_Kerning(face, left_glyph, right_glyph, kern_mode, akerning)
{
    var error = 0;

    if (!face)
        return 35;

    if (!akerning)
        return 6;

    var driver = face.driver;

    akerning.x = 0;
    akerning.y = 0;

    if (driver.clazz.get_kerning)
    {
        error = driver.clazz.get_kerning(face, left_glyph, right_glyph, akerning);
        if (error == 0)
        {
            if (kern_mode != 2)
            {
                akerning.x = FT_MulFix(akerning.x, face.size.metrics.x_scale);
                akerning.y = FT_MulFix(akerning.y, face.size.metrics.y_scale);

                if (kern_mode != 1)
                {
                    if (face.size.metrics.x_ppem < 25)
                        akerning.x = FT_MulDiv(akerning.x, face.size.metrics.x_ppem, 25);
                    if (face.size.metrics.y_ppem < 25)
                        akerning.y = FT_MulDiv(akerning.y, face.size.metrics.y_ppem, 25);

                    akerning.x = FT_PIX_ROUND(akerning.x);
                    akerning.y = FT_PIX_ROUND(akerning.y);
                }
            }
        }
    }

    return error;
}

function FT_New_GlyphSlot(face)
{
    if (!face || !face.driver)
        return 6;

    var slot = null;
    var _slot = null;

    if (face.driver.clazz.name == "type1")
    {
        _slot = new T1_GlyphSlotRec();
        slot = _slot.root;
    }
    else
    {
        _slot = new FT_GlyphSlot();
        slot = _slot;
    }

    slot.face = face;
    FT_Error = ft_glyphslot_init(slot);
    if (FT_Error != 0)
    {
        ft_glyphslot_done(slot);
        return null;
    }

    slot.next  = face.glyph;
    face.glyph = slot;
    return slot;
}

function FT_GlyphLoader_New(memory)
{
    var loader = new FT_GlyphLoader();
    loader.memory = memory;
    return loader;
}

function ft_glyphslot_done(slot)
{
    return 0;
}

function FT_New_Size(face)
{
    if (!face)
        return 35;

    if (!face.driver)
        return 34;

    var size = null;
    var clazz  = face.driver.clazz;
    
    FT_Error = 0;
    if (clazz.init_size)
    {
        size = clazz.init_size();
    }
    size.face = face;

    if (FT_Error == 0)
    {
        face.sizes_list[face.sizes_list.length] = size;
        return size;
    }

    return null;
}
function FT_CMap_New(clazz, init_data, charmap)
{
    FT_Error = 0;
    
    if (clazz == null || charmap == null || charmap.face == null)
    {
        FT_Error = 6;
        return null;
    }

    var _clazz = clazz.clazz;
    var face = charmap.face;
    var cmap = null;

    if (undefined != clazz.format)
    {
        switch (clazz.format)
        {
            case 4:
                cmap = new TT_CMap4Rec();
                break;
            case 12:
                cmap = new TT_CMap12Rec();
                break;
            case 13:
                cmap = new TT_CMap13Rec();
                break;
            case 14:
                cmap = new TT_CMap14Rec();
                break;
            default:
                cmap = new TT_CMapRec();
                break;
        }
    }
    else
    {
        switch (clazz.size)
        {
            case 101:
            case 102:
                cmap = new CFF_CMapStdRec();
                break;
            default:
                cmap = new TT_CMapRec();
        }
    }

    var cmap_ = __FT_CMapRec(cmap);
    if (undefined == _clazz)
        _clazz = clazz;

    if (null != cmap)
    {
        var charmap_= cmap_.charmap;
        charmap_.face = charmap.face;
        charmap_.encoding = charmap.encoding;
        charmap_.platform_id = charmap.platform_id;
        charmap_.encoding_id = charmap.encoding_id;

        cmap_.clazz = clazz;
        if (clazz.init != 0)
        {
            var error = _clazz.init(cmap, init_data);
            if (error != 0)
            {
                FT_Error = error;
                cmap = null;
                cmap_ = null;
                return null;
            }
        }
        if (null == face.charmaps)
            face.charmaps = new Array(1);
        face.charmaps[face.num_charmaps++] = cmap;
    }

    return cmap;
}

  //--------------------------------------------------------export----------------------------------------------------
  window['AscFonts'] = window['AscFonts'] || {};
  window['AscFonts'].FT_Common = FT_Common;
  window['AscFonts'].FT_Stream = FT_Stream;
  window['AscFonts'].g_memory = g_memory;
  window['AscFonts'].FT_Get_Sfnt_Table = FT_Get_Sfnt_Table;
  window['AscFonts'].FT_BBox = FT_BBox;
  window['AscFonts'].FT_Matrix = FT_Matrix;
  window['AscFonts'].FT_Open_Args = FT_Open_Args;
  window['AscFonts'].__FT_CharmapRec = __FT_CharmapRec;
  window['AscFonts'].FT_Outline_Decompose = FT_Outline_Decompose;
  window['AscFonts'].FT_Library = FT_Library;
  window['AscFonts'].FT_Set_Char_Size = FT_Set_Char_Size;
  window['AscFonts'].FT_Get_Char_Index = FT_Get_Char_Index;
  window['AscFonts'].FT_Set_Charmap = FT_Set_Charmap;
  window['AscFonts'].FT_Glyph_Get_CBox = FT_Glyph_Get_CBox;
  window['AscFonts'].FT_Get_Glyph = FT_Get_Glyph;
  window['AscFonts'].FT_Done_Glyph = FT_Done_Glyph;
  window['AscFonts'].FT_Load_Glyph = FT_Load_Glyph;
  window['AscFonts'].FT_Set_Transform = FT_Set_Transform;
  window['AscFonts'].FT_Render_Glyph = FT_Render_Glyph;
  window['AscFonts'].raster_memory = raster_memory;
  window['AscFonts'].FT_Get_Charmap_Index = FT_Get_Charmap_Index;
})(window);
