FontDriver.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / MS / Internal / FontFace / FontDriver.cs / 1 / FontDriver.cs

                            using System; 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; 
using System.IO;
using System.Runtime.CompilerServices; 
using System.Runtime.InteropServices; 
using System.Security.Permissions;
using System.Security; 
using System.Text;
using System.Windows;
using System.Windows.Markup;    // for XmlLanguage
using System.Windows.Media; 

using MS.Internal; 
using MS.Internal.PresentationCore; 
using MS.Utility;
using MS.Internal.FontCache; 

using MS.Internal.FontRasterization;

using Adobe.CffRasterizer; 

 
// Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. 
#pragma warning disable 1634, 1691
 

namespace MS.Internal.FontFace
{
    ///  
    /// Font technology.
    ///  
    internal enum FontTechnology 
    {
        // this enum need to be kept in order of preference that we want to use with duplicate font face, 
        // highest value will win in case of duplicate
        PostscriptOpenType,
        TrueType,
        TrueTypeCollection 
    }
 
    internal class TrueTypeFontDriver 
    {
        #region Font constants, structures and enumerations 

        /* OS/2 table */

        private const int OFF_OS2_version               = 0; 
//      private const int OFF_OS2_xAvgCharWidth         = 2;
        private const int OFF_OS2_usWeightClass         = 4; 
        private const int OFF_OS2_usWidthClass          = 6; 
        private const int OFF_OS2_fsType                = 8;
//      private const int OFF_OS2_ySubscriptXSize       = 10; 
//      private const int OFF_OS2_ySubscriptYSize       = 12;
//      private const int OFF_OS2_ySubscriptXOffset     = 14;
//      private const int OFF_OS2_ySubscriptYOffset     = 16;
//      private const int OFF_OS2_ySuperScriptXSize     = 18; 
//      private const int OFF_OS2_ySuperScriptYSize     = 20;
//      private const int OFF_OS2_ySuperScriptXOffset   = 22; 
//      private const int OFF_OS2_ySuperScriptYOffset   = 24; 
        private const int OFF_OS2_yStrikeOutSize        = 26;
        private const int OFF_OS2_yStrikeOutPosition    = 28; 
//      private const int OFF_OS2_sFamilyClass          = 30;
//      private const int OFF_OS2_Panose                = 32;
//      private const int OFF_OS2_ulCharRange           = 42;
//      private const int OFF_OS2_achVendID             = 58; 
        private const int OFF_OS2_usSelection           = 62;
//      private const int OFF_OS2_usFirstChar           = 64; 
//      private const int OFF_OS2_usLastChar            = 66; 
        private const int OFF_OS2_sTypoAscender         = 68;
        private const int OFF_OS2_sTypoDescender        = 70; 
        private const int OFF_OS2_sTypoLineGap          = 72;
        private const int OFF_OS2_usWinAscent           = 74;
        private const int OFF_OS2_usWinDescent          = 76;
//      private const int OFF_OS2_ulCodePageRange1      = 78; 
//      private const int OFF_OS2_ulCodePageRange2      = 82;
//      fields below are valid when version >= 2 
        private const int OFF_OS2_sxHeight              = 86; 
        private const int OFF_OS2_sCapHeight            = 88;
 
        [Flags]
        private enum Os2SelectionFlags
        {
            Italic = 1, 
            Underscore = 2,
            Negative = 4, 
            Outlined = 8, 
            Strikeout = 16,
            Bold = 32, 
            Regular = 64,
            DontUseWinLineMetrics = 128,
            WeightWidthSlopeOnly = 256,
            Oblique = 512 
        }
 
        private static class Os2EmbeddingFlags 
        {
            public const ushort RestrictedLicense = 0x0002; 
            public const ushort PreviewAndPrint = 0x0004;
            public const ushort Editable = 0x0008;

            // The font is installable if all bits in the InstallableMask are set to zero. 
            public const ushort InstallableMask = RestrictedLicense | PreviewAndPrint | Editable;
 
            public const ushort NoSubsetting = 0x0100; 
            public const ushort BitmapOnly = 0x0200;
        } 


        /* head table */
 
        private const int OFF_head_flags                = 16;
        private const int OFF_head_unitsPerEm           = 18; 
        private const int OFF_head_macStyle             = 44; 
        private const int OFF_head_indexToLocFormat     = 50;
 
        [Flags]
        private enum HeadFlags
        {
            OptimizedForClearType = 8192 // bit 13 
        }
 
        /* hhea table */ 

        private const int OFF_hhea_ascender             = 4; 
        private const int OFF_hhea_descender            = 6;
        private const int OFF_hhea_lineGap              = 8;
        private const int OFF_hhea_numberOfHMetrics     = 34;
 
        // maxp table
        private const int OFF_maxp_numGlyphs            = 4; 
 
        /* post table */
 
        private const int OFF_post_underlinePosition    = 8;
        private const int OFF_post_underlineThickness   = 10;

        /* PCLT table */ 

        private const int OFF_PCLT_xHeight              = 10; 
        private const int OFF_PCLT_CapHeight            = 16; 

 
        // glyf table

        private const int OFF_glyf_xMin                 = 2;
        private const int OFF_glyf_yMin                 = 4; 
        private const int OFF_glyf_xMax                 = 6;
        private const int OFF_glyf_yMax                 = 8; 
 
        // vhea table
 
        private const int OFF_vhea_numOfLongVerMetrics  = 34;

        // eblc table
 
        private const int OFF_eblc_numSizes             = 4;
        private const int OFF_eblc_bitmapSizeTables     = 8; 
 
        private const int EblcSizeOfBitmapSizeTable     = 48;
 
        // within bitmapSizeTable
        private const int EblcIndexSubTableArrayOffset  = 0;
        private const int EblcNumberOfIndexSubTables    = 8;
 
        // within index subtable
        private const int EblcFirstGlyph                = 0; 
        private const int EblcLastGlyph                 = 2; 
        private const int EblcSizeOfIndexSubTable       = 8;
 
        private class EastAsianLanguageCandidate
        {
            public EastAsianLanguageCandidate(int languageIndex)
            { 
                _languageIndex = languageIndex;
            } 
 
            public void AddCodepoint(int index)
            { 
                _codepointsFound |= (1 << index);
            }

            public int CodePoints 
            {
                get 
                { 
                    return _codepointsFound;
                } 
            }

            public int LanguageIndex
            { 
                get
                { 
                    return _languageIndex; 
                }
            } 

            private int _languageIndex;
            private int _codepointsFound;
        } 

 
        [Flags] 
        private enum MacStyleFlags
        { 
            Bold = 1,
            Italic = 2,
            Underline = 4,
            Outline = 8, 
            Shadow = 16,
            Condensed = 32, 
            Extended = 64 
        }
 
        internal struct ParsedNameTable
        {
            // LocalizedName arrays generated by FontDriver are sorted by OriginalLCID value.
            internal LocalizedName[]    familyNames; 
            internal LocalizedName[]    win32FamilyNames;
            internal LocalizedName[]    faceNames; 
            internal LocalizedName[]    win32faceNames; 

            internal LocalizedName[]    versionStrings; 
            internal LocalizedName[]    copyrights;
            internal LocalizedName[]    manufacturerNames;
            internal LocalizedName[]    trademarks;
            internal LocalizedName[]    designerNames; 
            internal LocalizedName[]    descriptions;
            internal LocalizedName[]    vendorUrls; 
            internal LocalizedName[]    designerUrls; 
            internal LocalizedName[]    licenseDescriptions;
            internal LocalizedName[]    sampleTexts; 

            internal double             version;
        }
 
        /// 
        /// BasicFontFaceInfo contains properties required for font enumeration and style matching 
        ///  
        internal struct BasicFontFaceInfo
        { 
            internal ParsedNameTable    nameTable;

            internal int                faceIndex;
            internal FontStyle          style; 
            internal FontWeight         weight;
            internal FontStretch        stretch; 
            internal bool               symbol; 

            internal ushort             designEmHeight; 
            internal ushort             designCellAscent;
            internal ushort             designCellDescent;
            internal int                designLineSpacing;
        } 

 
        private struct DirectoryEntry 
        {
            internal TrueTypeTags   tag; 
            internal CheckedPointer pointer;
        }

        private enum TrueTypeTags : int 
        {
            CharToIndexMap      = 0x636d6170,        /* 'cmap' */ 
            ControlValue        = 0x63767420,        /* 'cvt ' */ 
            BitmapData          = 0x45424454,        /* 'EBDT' */
            BitmapLocation      = 0x45424c43,        /* 'EBLC' */ 
            BitmapScale         = 0x45425343,        /* 'EBSC' */
            Editor0             = 0x65647430,        /* 'edt0' */
            Editor1             = 0x65647431,        /* 'edt1' */
            Encryption          = 0x63727970,        /* 'cryp' */ 
            FontHeader          = 0x68656164,        /* 'head' */
            FontProgram         = 0x6670676d,        /* 'fpgm' */ 
            GridfitAndScanProc  = 0x67617370,        /* 'gasp' */ 
            GlyphDirectory      = 0x67646972,        /* 'gdir' */
            GlyphData           = 0x676c7966,        /* 'glyf' */ 
            HoriDeviceMetrics   = 0x68646d78,        /* 'hdmx' */
            HoriHeader          = 0x68686561,        /* 'hhea' */
            HorizontalMetrics   = 0x686d7478,        /* 'hmtx' */
            IndexToLoc          = 0x6c6f6361,        /* 'loca' */ 
            Kerning             = 0x6b65726e,        /* 'kern' */
            LinearThreshold     = 0x4c545348,        /* 'LTSH' */ 
            MaxProfile          = 0x6d617870,        /* 'maxp' */ 
            NamingTable         = 0x6e616d65,        /* 'name' */
            OS_2                = 0x4f532f32,        /* 'OS/2' */ 
            Postscript          = 0x706f7374,        /* 'post' */
            PreProgram          = 0x70726570,        /* 'prep' */
            VertDeviceMetrics   = 0x56444d58,        /* 'VDMX' */
            VertHeader          = 0x76686561,        /* 'vhea' */ 
            VerticalMetrics     = 0x766d7478,        /* 'vmtx' */
            PCLT                = 0x50434C54,        /* 'PCLT' */ 
            TTO_GSUB            = 0x47535542,        /* 'GSUB' */ 
            TTO_GPOS            = 0x47504F53,        /* 'GPOS' */
            TTO_GDEF            = 0x47444546,        /* 'GDEF' */ 
            TTO_BASE            = 0x42415345,        /* 'BASE' */
            TTO_JSTF            = 0x4A535446,        /* 'JSTF' */
            OTTO                = 0x4f54544f,        // Adobe OpenType 'OTTO'
            TTC_TTCF            = 0x74746366         // 'ttcf' 
        }
 
        // Name table constants: http://www.microsoft.com/typography/otspec/name.htm 

        private enum NameTableNameID 
        {
            CopyrightNotice      = 0,
            FontFamilyName       = 1,
            FontSubfamilyName    = 2, 
            UniqueFontIdentifier = 3,
            FullFontName         = 4, 
            Version              = 5, 
            PostscriptName       = 6,
            Trademark            = 7, 
            ManufacturerName     = 8,
            Designer             = 9,
            Description          = 10,
            URLVendor            = 11, 
            URLDesigner          = 12,
            LicenseDescription   = 13, 
            LicenseInfoURL       = 14, 
            Reserved             = 15,
            PreferredFamily      = 16, 
            PreferredSubfamily   = 17,
            CompatibleFullName   = 18,
            SampleText           = 19,
            PostScriptCIDName    = 20 
        }
 
        private enum PlatformID : ushort 
        {
            Unicode   = 0, 
            Macintosh = 1,  // Only MacRoman character set supported
            ISO       = 2,  // Deprecated since OpenType 1.3, not supported
            Microsoft = 3,
            Custom    = 4   // Not supported 
        }
 
        private enum NameTableMicrosoftEncodingID : ushort 
        {
            Symbol         = 0, 
            UnicodeBMPOnly = 1,
            ShiftJIS       = 2,
            PRC            = 3,
            Big5           = 4, 
            Wansung        = 5,
            Johab          = 6, 
            // the gap is intentional, values from 7 to 9 are reserved per OpenType spec 
            Unicode        = 10
        } 

        /// 
        /// CharacterEncoding represents all supported platform and encoding combinations.
        /// Platforms and encodings are used by both the name and cmap tables, 
        /// although not all combinations are used by both tables.
        /// 
        /// Where multiple cmaps are available, higher numbered encodings are chosen 
        /// in preference to lower numbered encodings.
        ///  

        private enum CharacterEncoding
        {
                             // plat  enc  cmap range 
            Unknown    = 0,  // ====  ===  ==========
            MacRoman   = 1,  //    1    0 
            Unicode    = 2,  //    0  any 
            MsSymbol   = 3,  //    3    0  up to 2^8  characters at U+F000
            MsShiftJis = 4,  //    3    2  up to 2^16 characters 
            MsPrc      = 5,  //    3    3  up to 2^16 characters
            MsBig5     = 6,  //    3    4  up to 2^16 characters
            MsWansung  = 7,  //    3    5  up to 2^16 characters
            MsUcs2     = 8,  //    3    1  up to 2^16 characters, doesn't include surrogates 
            MsUcs4     = 9,  //    3   10  full Unicode repertoire, includes surrogates
        } 
 
        private struct MetricSearchList
        { 
            internal static readonly ushort[,] xHeight =
            {
                {   0x78, 100 },  // basic Latin, lower case letter x
                {  0x3C4, 100 },  // Greek, small letter tau 
                {  0x433, 100 },  // Cyrillic, small letter ghe
                {  0x578, 100 },  // Armenian, small letter vo 
                {  0x5DD,  75 },  // Hebrew, letter final mem 
                {  0x62F, 100 },  // Arabic, letter dal
                {  0x717, 100 },  // Syriac, letter he 
                {  0x784, 100 },  // Thaana, letter baa
                {  0x930,  50 },  // Devaganari, 1/2 of letter ra
                {  0xA30,  50 },  // Gurmukhi, 1/2 of letter ra
                {  0xAA0,  50 },  // Gujarati, 1/2 of letter ttha 
                {  0xB20,  50 },  // Oriya, 1/2 of letter ttha
                {  0xBB0,  50 },  // Tamil, 1/2 of letter ra 
                {  0xC10, 100 },  // Telugo, letter ai 
                {  0xC89,  50 },  // Kannada, 1/2 of letter U
                {  0xD30,  66 },  // Malayalam, 2/3 of letter ra 
                {  0xDB0,  50 },  // Sinhala, 1/2 of letter mahaapraanadayanna
                {  0xE20,  75 },  // Thai, 75% of letter pho samphao
                {  0xEC0,  50 },  // Lao, 1/2 of vowel sign E
                {  0xF40, 100 },  // Tibetan, letter ka 
                { 0x1010, 100 },  // Myanmar, letter ta
                { 0x10F2, 100 },  // Georgian, letter hie 
                { 0x1100,  50 },  // Hangul Jamo, 1/2 of choseong kieok 
                { 0x1210,  50 },  // Ethiopic, 1/2 of syllable hha
                { 0x13A0,  50 },  // Cherokee, 1/2 of letter A 
                { 0x14C0, 100 },  // Unified Canadian Aboriginal Syllabics, ne
                { 0x1681, 100 },  // Ogham, letter beith
                { 0x17A0,  50 },  // Khmer, 1/2 of letter ha
                { 0x1884, 100 }   // Mongolian, letter ali gali inverted ubadama 
            };
 
            internal static readonly ushort[,] capsHeight = 
            {
                { 0x48, 100 },  // basic Latin, upper case letter H 
            };
        }

        private struct EastAsianLanguages 
        {
            internal static readonly char[,] RepresentativeCodepoints = 
            { 
                // Hiragana
                { '\u3044', '\u3046', '\u3093', '\u3057', '\u306E', '\u304B' }, 

                // Hangul
                { '\uC774', '\uB2E4', '\uB294', '\uC758', '\uC5D0', '\uD558' },
 
                // CHS
                { '\u6C49', '\u5B57', '\u4E2D', '\u7684', '\u4E2A', '\u4EEC' }, 
 
                // CHT
                { '\u6F22', '\u5011', '\u4E09', '\u4E86', '\u5B78', '\u7232' } 
            };
        }

        ///  
        /// Font families that look too thin with default text contrast settings.
        /// For such fonts we increase text contrast value by 2. 
        /// The list must be sorted alphabetically. 
        /// 
        private static readonly string[] _thinFontFamilyNames = 
            {
                "Courier New",
                "Fixed Miriam Transparent",
                "Miriam Fixed", 
                "Rod",
                "Rod Transparent", 
                "Simplified Arabic Fixed" 
            };
 
        private static readonly CultureInfo EnglishUSCulture = CultureInfo.GetCultureInfo("en-us");


 
        #endregion
 
        #region Byte, Short, Long etc. accesss to CheckedPointers 

        ///  
        /// The follwoing APIs extract OpenType variable types from OpenType font
        /// files. OpenType variables are stored big-endian, and the type are named
        /// as follows:
        ///     Byte   -  signed     8 bit 
        ///     UShort -  unsigned   16 bit
        ///     Short  -  signed     16 bit 
        ///     ULong  -  unsigned   32 bit 
        ///     Long   -  signed     32 bit
        ///  
        ///
        /// 
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks
        ///     TreatAsSafe: This code is Ok to expose 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        private static byte ReadOpenTypeByte(CheckedPointer pointer) 
        {
            unsafe 
            {
                byte * readBuffer = (byte *)pointer.Probe(0, 1);
                return readBuffer[0];
            } 
        }
 
 
        /// 
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks 
        ///     TreatAsSafe: This code is Ok to expose
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        private static ushort ReadOpenTypeUShort(CheckedPointer pointer) 
        {
            unsafe 
            { 
                byte * readBuffer = (byte *)pointer.Probe(0, 2);
                ushort result = (ushort)((readBuffer[0] << 8) + readBuffer[1]); 
                return result;
            }
        }
 

        ///  
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks 
        ///     TreatAsSafe: This code is Ok to expose
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private static short ReadOpenTypeShort(CheckedPointer pointer)
        {
            unsafe 
            {
                byte * readBuffer = (byte *)pointer.Probe(0, 2); 
                short result = (short)((readBuffer[0] << 8) + readBuffer[1]); 
                return result;
            } 
        }


        ///  
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks
        ///     TreatAsSafe: This code IS Ok to expose 
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private static int ReadOpenTypeLong(CheckedPointer pointer) 
        {
            unsafe
            {
                byte * readBuffer = (byte *)pointer.Probe(0, 4); 
                int result = (int)((((((readBuffer[0] << 8) + readBuffer[1]) << 8) + readBuffer[2]) << 8) + readBuffer[3]);
                return result; 
            } 
        }
 

        /// 
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks
        ///     TreatAsSafe: This code is ok to expose 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        private static uint ReadOpenTypeULong(CheckedPointer pointer) 
        {
            unsafe 
            {
                byte * readBuffer = (byte *)pointer.Probe(0, 4);
                uint result = (uint)((((((readBuffer[0] << 8) + readBuffer[1]) << 8) + readBuffer[2]) << 8) + readBuffer[3]);
                return result; 
            }
        } 
 
        #endregion Byte, Short, Long etc. accesss to CheckedPointers
 
        #region Constructor and general helpers

        /// 
        ///    Critical:  constructs data for a checked pointer. 
        /// 
        [SecurityCritical] 
        internal TrueTypeFontDriver(UnmanagedMemoryStream unmanagedMemoryStream, Uri sourceUri) 
        {
            _sourceUri = sourceUri; 
            _unmanagedMemoryStream = unmanagedMemoryStream;
            _fileStream = new CheckedPointer(unmanagedMemoryStream);

            try 
            {
                CheckedPointer seekPosition = _fileStream; 
 
                TrueTypeTags typeTag = (TrueTypeTags)ReadOpenTypeLong(seekPosition);
                seekPosition += 4; 

                if (typeTag == TrueTypeTags.TTC_TTCF)
                {
                    // this is a TTC file, we need to decode the ttc header 
                    _technology = FontTechnology.TrueTypeCollection;
                    seekPosition += 4; // skip version 
                    _numFaces = ReadOpenTypeLong(seekPosition); 
                }
                else if (typeTag == TrueTypeTags.OTTO) 
                {
                    _technology = FontTechnology.PostscriptOpenType;
                    _numFaces = 1;
                } 
                else
                { 
                    _technology = FontTechnology.TrueType; 
                    _numFaces = 1;
                } 
            }
            catch (ArgumentOutOfRangeException e)
            {
                // convert exceptions from CheckedPointer to FileFormatException 
                throw new FileFormatException(SourceUri, e);
            } 
        } 

        internal void SetFace(int faceIndex) 
        {
            if (_technology == FontTechnology.TrueTypeCollection)
            {
                if (faceIndex < 0 || faceIndex >= _numFaces) 
                    throw new ArgumentOutOfRangeException("faceIndex");
            } 
            else 
            {
                if (faceIndex != 0) 
                    throw new ArgumentOutOfRangeException("faceIndex", SR.Get(SRID.FaceIndexValidOnlyForTTC));
            }

            try 
            {
                CheckedPointer seekPosition = _fileStream + 4; 
 
                if (_technology == FontTechnology.TrueTypeCollection)
                { 
                    // this is a TTC file, we need to decode the ttc header

                    // skip version, num faces, OffsetTable array
                    seekPosition += (4 + 4 + 4 * faceIndex); 

                    _directoryOffset = ReadOpenTypeLong(seekPosition); 
 
                    seekPosition = _fileStream + (_directoryOffset + 4);
                    // 4 means that we skip the version number 
                }

                _faceIndex = faceIndex;
 
                int numTables = ReadOpenTypeUShort(seekPosition);
                seekPosition += 2; 
 
                // quick check for malformed fonts, see if numTables is too large
                // file size should be >= sizeof(offset table) + numTables * (sizeof(directory entry) + minimum table size (4)) 
                long minimumFileSize = (4 + 2 + 2 + 2 + 2) + numTables * (4 + 4 + 4 + 4 + 4);
                if (_fileStream.Size < minimumFileSize)
                {
                    throw new FileFormatException(SourceUri); 
                }
 
                _tableDirectory = new DirectoryEntry[numTables]; 

                // skip searchRange, entrySelector and rangeShift 
                seekPosition += 6;

                // I can't use foreach here because C# disallows modifying the current value
                for (int i = 0; i < _tableDirectory.Length; ++i) 
                {
                    _tableDirectory[i].tag = (TrueTypeTags)ReadOpenTypeLong(seekPosition); 
                    seekPosition += 8; // skip checksum 
                    int offset = ReadOpenTypeLong(seekPosition);
                    seekPosition += 4; 
                    int length = ReadOpenTypeLong(seekPosition);
                    seekPosition += 4;

                    _tableDirectory[i].pointer = _fileStream.CheckedProbe(offset, length); 
                }
            } 
            catch (ArgumentOutOfRangeException e) 
            {
                // convert exceptions from CheckedPointer to FileFormatException 
                throw new FileFormatException(SourceUri, e);
            }
        }
 
        /// 
        ///     Critical: This code calls to create a checked pointer and might return a null checked pointer 
        ///     All consumers need to validate is null before using it. 
        ///     TreatAsSafe: This information is ok to expose
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private CheckedPointer GetTable(TrueTypeTags tag)
        {
            foreach (DirectoryEntry directoryEntry in _tableDirectory) 
            {
                if (tag == directoryEntry.tag) 
                    return directoryEntry.pointer; 
            }
            return new CheckedPointer(); 
        }

        #endregion
 
        #region Public methods and properties
 
        internal int NumFaces 
        {
            get 
            {
                return _numFaces;
            }
        } 

        private Uri SourceUri 
        { 
            get
            { 
                return _sourceUri;
            }
        }
 
        /// 
        /// Create font subset that includes glyphs in the input collection. 
        ///  
        ///
        ///  TreatAsSafe: This API could be public in terms of security as it demands unmanaged code 
        ///  Critical: Does an elevation by calling TrueTypeSubsetter which we are treating as equivalent to
        ///            unsafe native methods
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        internal byte[] ComputeFontSubset(ICollection glyphs)
        { 
            SecurityHelper.DemandUnmanagedCode(); 

            int fileSize = _fileStream.Size; 
            unsafe
            {
                void * fontData = _fileStream.Probe(0, fileSize);
 
                // Since we currently don't have a way to subset CFF fonts, just return a copy of the font.
                if (_technology == FontTechnology.PostscriptOpenType) 
                { 
                    byte[] fontCopy = new byte[fileSize];
                    Marshal.Copy((IntPtr)fontData, fontCopy, 0, fileSize); 
                    return fontCopy;
                }

                ushort [] glyphArray; 
                if (glyphs == null || glyphs.Count == 0)
                    glyphArray = null; 
                else 
                {
                    glyphArray = new ushort[glyphs.Count]; 
                    glyphs.CopyTo(glyphArray, 0);
                }

                return TrueTypeSubsetter.ComputeSubset(fontData, fileSize, SourceUri, _directoryOffset, glyphArray); 
            }
        } 
 
        internal void GetBasicFontFaceInfo(ref BasicFontFaceInfo basicFontFaceInfo, out bool skipFontDifferentiation)
        { 
            try
            {
                CheckedPointer headTable = GetTable(TrueTypeTags.FontHeader);
                CheckedPointer os2Table = GetTable(TrueTypeTags.OS_2); 
                CheckedPointer hheaTable = GetTable(TrueTypeTags.HoriHeader);
 
                if (headTable.IsNull || hheaTable.IsNull) 
                {
                    throw new FileFormatException(SourceUri); 
                }

                basicFontFaceInfo.faceIndex = _faceIndex;
 
                ReadStyles(os2Table, headTable, out basicFontFaceInfo.style, out basicFontFaceInfo.weight, out basicFontFaceInfo.stretch, out skipFontDifferentiation);
 
                DecodeNameTable(ref basicFontFaceInfo.nameTable); 

                basicFontFaceInfo.symbol = DecodeCmapTable(null); 

                ReadBasicMetrics(
                    headTable,
                    os2Table, 
                    hheaTable,
                    out basicFontFaceInfo.designEmHeight, 
                    out basicFontFaceInfo.designCellAscent, 
                    out basicFontFaceInfo.designCellDescent,
                    out basicFontFaceInfo.designLineSpacing 
                );
            }
            catch (ArgumentOutOfRangeException e)
            { 
                // convert exceptions from CheckedPointer to FileFormatException
                throw new FileFormatException(SourceUri, e); 
            } 
        }
 
        /// 
        ///     Critical: This code sets glyphcount which is used to index into unmanaged pointers
        ///     TreatAsSafe: The value is computed from cache and gettable entries all of which are tracked
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        internal void GetLayoutFontFaceInfo(FontFaceLayoutInfo cache) 
        { 
            try
            { 
                cache.FontTechnology = _technology;

                CheckedPointer hheaTable = GetTable(TrueTypeTags.HoriHeader);
                CheckedPointer headTable = GetTable(TrueTypeTags.FontHeader); 
                CheckedPointer os2Table = GetTable(TrueTypeTags.OS_2);
                CheckedPointer hmtxTable = GetTable(TrueTypeTags.HorizontalMetrics); 
                CheckedPointer postTable = GetTable(TrueTypeTags.Postscript); 

                if (headTable.IsNull || hheaTable.IsNull || hmtxTable.IsNull) 
                {
                    throw new FileFormatException(SourceUri);
                }
 
                ushort numberOfHMetrics = ReadOpenTypeUShort(hheaTable + OFF_hhea_numberOfHMetrics);
                if (numberOfHMetrics == 0) 
                    throw new FileFormatException(SourceUri); 

                // deduce the number of glyphs from the size of the hmtxTable, should be the same as from maxp 
                cache.GlyphCount = (ushort)((hmtxTable.Size - numberOfHMetrics * 2) / 2);

                FontStyle fontStyle;
                FontWeight fontWeight; 
                FontStretch fontStretch;
                bool skipFontDifferentiation; 
                ReadStyles(os2Table, headTable, out fontStyle, out fontWeight, out fontStretch, out skipFontDifferentiation); 
                cache.Style = fontStyle;
                cache.Weight = fontWeight; 
                cache.Stretch = fontStretch;

                // cmap decoding and initialization of special glyph values
                cache.Symbol = DecodeCmapTable(cache); 

                ushort blankGlyph; 
                cache.CharacterMap.TryGetValue(' ', out blankGlyph); 

                // If the font doesn't support space character, we'll use glyph index zero, 
                // which has a square box shape in most fonts.

                cache.BlankGlyph = blankGlyph;
 
                ushort invalidGlyph;
 
                // dotted circle 
                if (!cache.CharacterMap.TryGetValue(0x25CC, out invalidGlyph))
                { 
                    // dotted circle not found, try NBSP
                    if (!cache.CharacterMap.TryGetValue(0x00A0, out invalidGlyph))
                    {
                        invalidGlyph = cache.BlankGlyph; // default to the blank glyph 
                    }
                } 
                cache.InvalidGlyph = invalidGlyph; 

                ushort unitsPerEm, designCellAscent, designCellDescent; 
                int designLineSpacing;
                ReadBasicMetrics(
                    headTable,
                    os2Table, 
                    hheaTable,
                    out unitsPerEm, 
                    out designCellAscent, 
                    out designCellDescent,
                    out designLineSpacing 
                );
                cache.DesignEmHeight = unitsPerEm;
                cache.DesignCellAscent = designCellAscent;
                cache.DesignCellDescent = designCellDescent; 

                if (!postTable.IsNull) 
                { 
                    cache.UnderlinePosition = ReadOpenTypeShort(postTable + OFF_post_underlinePosition);
                    ushort underlineThickness = ReadOpenTypeUShort(postTable + OFF_post_underlineThickness); 

                    // Correct zero underline thickness (happens with "Bodoni MT Condensed")
                    // to a reasonable default value taken from Arial.
                    if (underlineThickness == 0) 
                        underlineThickness = (ushort)((unitsPerEm + 7)/14);
                    cache.UnderlineThickness = underlineThickness; 
                } 
                else
                { 
                    // correct misssing underline metrics to reasonable default values from Arial
                    cache.UnderlinePosition = (short)(-((unitsPerEm + 5) / 10));
                    cache.UnderlineThickness = (ushort)((unitsPerEm + 7)/14);
                } 

                ushort typoAscent, typoDescent; 
 
                if (!os2Table.IsNull)
                { 
                    typoAscent = ReadOpenTypeUShort(os2Table + OFF_OS2_sTypoAscender);
                    int temp = -ReadOpenTypeShort(os2Table + OFF_OS2_sTypoDescender);
                    if (temp < 0)
                    { 
                        /* a few existing fonts have the sign for this value reversed */
                        temp = - temp; 
                    } 
                    typoDescent = (ushort)temp;
 
                    // some fonts have invalid values for typoAscent and typoDescent
                    if (typoAscent > ushort.MaxValue / 2 || typoDescent > ushort.MaxValue / 2)
                    {
                        typoAscent = cache.DesignCellAscent; 
                        typoDescent = cache.DesignCellDescent;
                    } 
 
                    ushort strikeThroughThickness = ReadOpenTypeUShort(os2Table + OFF_OS2_yStrikeOutSize);
                    if (strikeThroughThickness == 0) 
                        strikeThroughThickness = cache.UnderlineThickness;
                    cache.StrikethroughThickness = strikeThroughThickness;
                    cache.StrikethroughPosition = (short)ReadOpenTypeUShort(os2Table + OFF_OS2_yStrikeOutPosition);
                } 
                else
                { 
                    typoAscent = cache.DesignCellAscent; 
                    typoDescent = cache.DesignCellDescent;
                    cache.StrikethroughThickness = cache.UnderlineThickness; 
                    cache.StrikethroughPosition = (short)(unitsPerEm / 3);
                }

                // we don't use normalized em ascent and descent yet 
                // but we want to keep this code in case we need it
                // design em descent and ascent come from typo ascent and descent 
                // 
                // normalization of designEmAscent and designEmDescent to make their sum correspond to designEmHeight as per CSS3 spec
                // 
                //            if ((designEmDescent + cache.DesignEmAscent) != 0)
                //            {
                //                designEmDescent = (ushort)((designEmDescent * unitsPerEm) /
                //                    (designEmDescent + cache.DesignEmAscent)); 
                //            }
                //            else 
                //            { 
                //                /* in this rare malformed font case, default to 20% for the descender */
                //                designEmDescent = (ushort)(unitsPerEm * 20 / 100); 
                //            }
                //            cache.DesignEmAscent = (ushort)(unitsPerEm - designEmDescent);

                ushort indexToLocFormat = ReadOpenTypeUShort(headTable + OFF_head_indexToLocFormat); 
                ReadAdvances(cache, hmtxTable, numberOfHMetrics, indexToLocFormat, typoAscent, typoDescent);
 
                ReadRenderingHints(headTable, cache); 

                cache.EmbeddingRights = ReadFontEmbeddingRights(os2Table); 

                ComputeHeights(os2Table, unitsPerEm, cache);

                ParsedNameTable nameTable = new ParsedNameTable(); 

                DecodeNameTable(ref nameTable); 
                cache.AddLocalizedNames(ref nameTable, skipFontDifferentiation); 

                ComputeFontContrastAdjustment(ref nameTable, cache); 
            }
            catch (ArgumentOutOfRangeException e)
            {
                // convert exceptions from CheckedPointer to FileFormatException 
                throw new FileFormatException(SourceUri, e);
            } 
        } 

        ///  
        ///     Critical: This code writes into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void GetShapingFontFaceInfo(FontFaceLayoutInfo cache)
        { 
            try 
            {
                cache.SetGsub(GetTable(TrueTypeTags.TTO_GSUB)); 
                cache.SetGpos(GetTable(TrueTypeTags.TTO_GPOS));
                cache.SetGdef(GetTable(TrueTypeTags.TTO_GDEF));
                cache.SetJstf(GetTable(TrueTypeTags.TTO_JSTF));
            } 
            catch (ArgumentOutOfRangeException e)
            { 
                // convert exceptions from CheckedPointer to FileFormatException 
                throw new FileFormatException(SourceUri, e);
            } 
        }

        #endregion Public methods and properties
 
        #region Character mapping and language encoding handling
 
        ///  
        ///
        /// TrueType/OpenType fonts support a wide range of encodings for both 
        /// names and character to glyph maps.
        ///
        /// We provide one class (CharacterToUnicodeMapper) that encapsulates all
        /// decoding required by 'name' and 'cmap' tables. 
        ///
        /// Fonts identify which encoding is being used for a name or a cmap through 
        /// a pair of enums: 'PlatformId' and 'EncodingId'. The static method 
        /// 'PlatformIdAndEncodingIdToCharacterEncoding' maps from supported combinations
        /// of PatformId and EncodingId to a single internal enum 'CharacterEncoding'. 
        ///
        /// Returns CharacterEncoding.Unknown for unrecognised/unsupported combinations
        /// of platform id and encoding id.
        /// 
        /// 
 
        private static CharacterEncoding PlatformIdAndEncodingIdToCharacterEncoding( 
            PlatformID currentPlatform,
            ushort     currentEncoding) 
        {
            switch (currentPlatform)
            {
                case PlatformID.Microsoft: 
                    switch ((NameTableMicrosoftEncodingID)currentEncoding)
                    { 
                        case NameTableMicrosoftEncodingID.Symbol: 
                            return CharacterEncoding.MsSymbol;
 
                        case NameTableMicrosoftEncodingID.UnicodeBMPOnly:
                            return CharacterEncoding.MsUcs2;

                        case NameTableMicrosoftEncodingID.Unicode: 
                            return CharacterEncoding.MsUcs4;
 
                        case NameTableMicrosoftEncodingID.ShiftJIS: 
                            return CharacterEncoding.MsShiftJis;
 
                        case NameTableMicrosoftEncodingID.PRC:
                            return CharacterEncoding.MsPrc;

                        case NameTableMicrosoftEncodingID.Big5: 
                            return CharacterEncoding.MsBig5;
 
                        case NameTableMicrosoftEncodingID.Wansung: 
                            return CharacterEncoding.MsWansung;
                    } 
                    break;

                case PlatformID.Unicode:
                    return CharacterEncoding.Unicode; 

                case PlatformID.Macintosh: 
                    if (currentEncoding == 0) // Macintosh Roman encoding 
                        return CharacterEncoding.MacRoman;
                    break; 
            }
            return CharacterEncoding.Unknown;
        }
 
        private static CultureInfo MapTrueTypeLangIdToCulture(PlatformID platformID, ushort languageID)
        { 
            switch (platformID) 
            {
                case PlatformID.Macintosh: 
                    // handle English Macintosh language ID
                    if (languageID == 0)
                        return EnglishUSCulture;
 
                    // ignore the rest of the language IDs
                    return null; 
 
                case PlatformID.Microsoft:
                    // in Microsoft case we have 1 to 1 mapping 

                    if (languageID == 0x0F00)
                    {
                        // There is an invalid entry with this lang id in times.ttf on Windows XP and before. 
                        // Don't throw a first chance exception in that case.
                        return null; 
                    } 
                    try
                    { 
                        return CultureInfo.GetCultureInfo(languageID);
                    }
                    catch (ArgumentException)
                    {   // don't skip the font because of incorrect culture IDs 
                        return null;
                    } 
 
                case PlatformID.ISO:
                case PlatformID.Custom: 
                case PlatformID.Unicode:
                default:
                    // these 3 platforms cannot have name table entries and therefore don't have language ID
                    return null; 
            }
        } 
 

 
        // MacRomanUpperToUnicode mapping table - for Mac Roman encoded cmaps and names.
        // MacRoman 0-127 is identical to Unicode 0-127.
        // MacRoman 128-255 uses a mapping specified by Apple and published on
        // the Unicode web site at: 
        //    http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT
        // 
        // Note that although NLS provides a mapping for Mac Roman using the magic 
        // encoding number 10000, that mapping is wrong for Apple codepoint 0xBD:
        // it maps Greek omega to electronic symbol 'ohms'. 

        private static readonly int[] MacRomanUpperToUnicode = new int[]
        {
            0x0000C4, 0x0000C5, 0x0000C7, 0x0000C9, 0x0000D1, 0x0000D6, 0x0000DC, 0x0000E1,   // 0x80 - 0x87 
            0x0000E0, 0x0000E2, 0x0000E4, 0x0000E3, 0x0000E5, 0x0000E7, 0x0000E9, 0x0000E8,   // 0x88 - 0x8F
            0x0000EA, 0x0000EB, 0x0000ED, 0x0000EC, 0x0000EE, 0x0000EF, 0x0000F1, 0x0000F3,   // 0x90 - 0x97 
            0x0000F2, 0x0000F4, 0x0000F6, 0x0000F5, 0x0000FA, 0x0000F9, 0x0000FB, 0x0000FC,   // 0x98 - 0x9F 
            0x002020, 0x0000B0, 0x0000A2, 0x0000A3, 0x0000A7, 0x002022, 0x0000B6, 0x0000DF,   // 0xA0 - 0xA7
            0x0000AE, 0x0000A9, 0x002122, 0x0000B4, 0x0000A8, 0x002260, 0x0000C6, 0x0000D8,   // 0xA8 - 0xAF 
            0x00221E, 0x0000B1, 0x002264, 0x002265, 0x0000A5, 0x0000B5, 0x002202, 0x002211,   // 0xB0 - 0xB7
            0x00220F, 0x0003C0, 0x00222B, 0x0000AA, 0x0000BA, 0x0003A9, 0x0000E6, 0x0000F8,   // 0xB8 - 0xBF
            0x0000BF, 0x0000A1, 0x0000AC, 0x00221A, 0x000192, 0x002248, 0x002206, 0x0000AB,   // 0xC0 - 0xC7
            0x0000BB, 0x002026, 0x0000A0, 0x0000C0, 0x0000C3, 0x0000D5, 0x000152, 0x000153,   // 0xC8 - 0xCF 
            0x002013, 0x002014, 0x00201C, 0x00201D, 0x002018, 0x002019, 0x0000F7, 0x0025CA,   // 0xD0 - 0xD7
            0x0000FF, 0x000178, 0x002044, 0x0020AC, 0x002039, 0x00203A, 0x00FB01, 0x00FB02,   // 0xD8 - 0xDF 
            0x002021, 0x0000B7, 0x00201A, 0x00201E, 0x002030, 0x0000C2, 0x0000CA, 0x0000C1,   // 0xE0 - 0xE7 
            0x0000CB, 0x0000C8, 0x0000CD, 0x0000CE, 0x0000CF, 0x0000CC, 0x0000D3, 0x0000D4,   // 0xE8 - 0xEF
            0x00F8FF, 0x0000D2, 0x0000DA, 0x0000DB, 0x0000D9, 0x000131, 0x0002C6, 0x0002DC,   // 0xF0 - 0xF7 
            0x0000AF, 0x0002D8, 0x0002D9, 0x0002DA, 0x0000B8, 0x0002DD, 0x0002DB, 0x0002C7    // 0xF8 - 0xFF
        };

 
        /// 
        /// Subclasses of CharacterToUnicodeMapper are passed to string conversion 
        /// and cmap parser routines to translate characters represented in various 
        /// codepages in font files to standard Unicode encoding.
        ///  
        abstract internal class CharacterToUnicodeMapper
        {
            /// 
            /// MapCharacterToUnicode - used by the cmap table parsers to determine 
            /// which entry in the Avalon Unicode character map to update for
            /// a given cmap codepoint to glyph mapping. 
            /// Returns -1 when no mapping exists 
            /// 
            public abstract int MapCharacterToUnicode(int character); 

            /// 
            /// MapBytesToUtf16 - used by the name table parsers to map a
            /// name string to UTF-16. 
            /// Returns null if any part of the string could not be mapped.
            ///  
            public abstract string MapBytesToUtf16(byte[] bytes, int length); 
        }
 

        /// 
        /// NullMapper - handles data that is already nominally in Unicode form.
        /// Byte arrays are expected to contain UTF-16 data in big endian 
        /// format.
        ///  
        internal class NullMapper : CharacterToUnicodeMapper 
        {
            public NullMapper(){} 

            public override int MapCharacterToUnicode(int character)
            {
                return character; 
            }
 
            // Name tables that already using Unicode are handled directly in ParseUtf16Name. 
            public override string MapBytesToUtf16(byte[] bytes, int length)
            { 
                Invariant.Assert(false);
                return null;
            }
        } 

 
        ///  
        /// MacRomanToUnicodeMapper - Handle Mac Roman encoded cmaps and names.
        /// MacRoman 0-127 is identical to Unicode 0-127. 
        /// MacRoman 128-255 uses a mapping specified by Apple and published on
        /// the Unicode web site at:
        ///    http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT
        /// 
        /// Note that although NLS provides a mapping for Mac Roman using the magic
        /// encoding number 10000, that mapping is wrong for Apple codepoint 0xBD. 
        ///  
        internal class MacRomanToUnicodeMapper : CharacterToUnicodeMapper
        { 
            public MacRomanToUnicodeMapper(){}


            public override int MapCharacterToUnicode(int character) 
            {
                if (character < 0 || character >= 256) 
                    return -1; 

                if (character < 128) 
                {
                    return character;
                }
                else 
                {
                    return MacRomanUpperToUnicode[character-128]; 
                } 
            }
 

            public override string MapBytesToUtf16(byte[] bytes, int length)
            {
                char[] result = new char[length]; 
                for (int i=0; i 
        /// WindowsAnsiToUnicodeMapper - Handle ansi Windows platform cmaps and names.
        /// All ansi Windows platform codepages are handled through the 
        /// System.Text.Encoding NLS class. 
        /// 
        internal class WindowsAnsiToUnicodeMapper : CharacterToUnicodeMapper 
        {
            private Encoding                       _encoding;
            private DecoderFallbackWithFailureFlag _decoderFallback;
 

            public WindowsAnsiToUnicodeMapper(int codepage) 
            { 
                try
                { 
                    _decoderFallback = new DecoderFallbackWithFailureFlag();
                    _encoding = Encoding.GetEncoding(codepage, EncoderFallback.ExceptionFallback, _decoderFallback);
                }
                catch (ArgumentException) 
                {
                    _encoding = null; 
                } 
                catch (NotSupportedException)
                { 
                    _encoding = null;
                }
            }
 

            public override int MapCharacterToUnicode(int character) 
            { 
                if (_encoding == null || character < 0 || character >= 65536)
                { 
                    return -1;
                }

                _decoderFallback.HasFailed = false; 

                byte[] bytes; 
                if (character < 256) 
                {
                    bytes = new byte[1]; 
                    bytes[0] = (byte)character;
                }
                else
                { 
                    bytes = new byte[2];
                    bytes[0] = (byte)(character >> 8); 
                    bytes[1] = (byte)(character & 0xff); 
                }
 
                char[] chars = _encoding.GetChars(bytes);
                if (_decoderFallback.HasFailed)
                {
                    // return missing characters as -1 (not a valid unicode codepoint) 
                    return -1;
                } 
                else 
                {
                    if (chars.Length == 1) 
                    {
                        return (int)chars[0];
                    }
                    else 
                    {
                        // Not in the basic multilingual plane. 
                        // For a good conversion this will be a surrogate pair, from which 
                        // we need to determine the UTF-32 codepoint.
                        // For a bad conversion, instead of failing, NLS may just copy the 2 input 
                        // bytes over as 2 output chars. We treat this case as a failure.
                        if (    (chars.Length != 2)
                            ||  (chars[0] < 0xD800)
                            ||  (chars[0] > 0xDBFF) 
                            ||  (chars[1] < 0xDC00)
                            ||  (chars[1] > 0xDFFF)) 
                        { 
                            // Not a valid Unicode codepoint
                            return -1; 
                        }
                        else
                        {
                            return 0x10000 + ((((int)chars[0]) & 0x3FF) << 10) + (((int)chars[1]) & 0x3FF); 
                        }
                    } 
                } 
            }
 

            public override string MapBytesToUtf16(byte[] bytes, int length)
            {
                if (_encoding == null) 
                {
                    return null; 
                } 

                _decoderFallback.HasFailed = false; 

                string convertedString = _encoding.GetString(bytes, 0, length);

                if (_decoderFallback.HasFailed) 
                {
                    return null; 
                } 
                else
                { 
                    return convertedString;
                }
            }
        } 

 
        private static CharacterToUnicodeMapper GetCharacterToUnicodeMapperForCharacterEncoding(CharacterEncoding characterEncoding) 
        {
            switch (characterEncoding) 
            {
                case CharacterEncoding.MsUcs2:
                case CharacterEncoding.MsUcs4:
                case CharacterEncoding.Unicode: 
                case CharacterEncoding.MsSymbol:     return new NullMapper();
 
                case CharacterEncoding.MacRoman:     return new MacRomanToUnicodeMapper(); 
                case CharacterEncoding.MsShiftJis:   return new WindowsAnsiToUnicodeMapper(932);
                case CharacterEncoding.MsPrc:        return new WindowsAnsiToUnicodeMapper(936); 
                case CharacterEncoding.MsBig5:       return new WindowsAnsiToUnicodeMapper(950);
                case CharacterEncoding.MsWansung:    return new WindowsAnsiToUnicodeMapper(949);
            }
 
            Invariant.Assert(false);
            return null; 
        } 

 
        private void LookForBestEncoding(
            CheckedPointer        table,
            ushort                encodingCount,
            out int               glyphTableOffset, 
            out CharacterEncoding characterEncoding)
        { 
            PlatformID platform = PlatformID.Unicode; 
            ushort     encoding = 0;
 
            glyphTableOffset  = 0;
            characterEncoding = CharacterEncoding.Unknown;

            const int offsetToPlatform = 4; 
            const int offsetToEncoding = 6;
            const int offsetToOffset = 8; 
            const int recordSize = 8; 

            for (int i = 0; i < encodingCount; i++) 
            {
                PlatformID currentPlatform = (PlatformID)ReadOpenTypeUShort(table + offsetToPlatform + i * recordSize);
                ushort currentEncoding = ReadOpenTypeUShort(table + offsetToEncoding + i * recordSize);
                int offset = ReadOpenTypeLong(table + offsetToOffset + i * recordSize); 

                // Look for the best available table in the order of priority from the CharacterEncoding enum 
 
                CharacterEncoding candidateTableType = PlatformIdAndEncodingIdToCharacterEncoding(currentPlatform, currentEncoding);
 
                if (candidateTableType > characterEncoding)
                {
                    characterEncoding = candidateTableType;
                    glyphTableOffset  = offset; 
                    platform          = currentPlatform;
                    encoding          = currentEncoding; 
                } 
            }
        } 

        #endregion Character mapping and language encoding handling

 
        #region OS/2 and head decoding
 
        private void ReadStyles( 
            CheckedPointer  os2Table,
            CheckedPointer  headTable, 
            out FontStyle   fontStyle,
            out FontWeight  fontWeight,
            out FontStretch fontStretch,
            out bool        skipFontDifferentiation 
            )
        { 
            fontStyle = FontStyles.Normal; 
            fontWeight = FontWeights.Normal;
            fontStretch = FontStretches.Normal; 
            skipFontDifferentiation = false;

            if (!os2Table.IsNull)
            { 
                Os2SelectionFlags os2SelectionFlags = (Os2SelectionFlags)ReadOpenTypeUShort(os2Table + OFF_OS2_usSelection);
                if ((os2SelectionFlags & Os2SelectionFlags.Oblique) != 0) 
                { 
                    fontStyle = FontStyles.Oblique;
                } 
                else if ((os2SelectionFlags & Os2SelectionFlags.Italic) != 0)
                {
                    fontStyle = FontStyles.Italic;
                } 
                if ((os2SelectionFlags & Os2SelectionFlags.WeightWidthSlopeOnly) != 0)
                { 
                    skipFontDifferentiation = true; 
                }
                int usWeightClass = ReadOpenTypeUShort(os2Table + OFF_OS2_usWeightClass); 
                if (1 <= usWeightClass && usWeightClass <= 9)
                {
                    // Numerous existing fonts have the weight wrong, a value between 1 and 9 instead of between 100 and 900.
                    usWeightClass *= 100; 
                }
 
                fontWeight = FontWeight.FromOpenTypeWeight(usWeightClass); 

                ushort usWidthClass = ReadOpenTypeUShort(os2Table + OFF_OS2_usWidthClass); 
                fontStretch = FontStretch.FromOpenTypeStretch(usWidthClass);
            }
            else
            { 
                MacStyleFlags macStyleFlags = (MacStyleFlags)ReadOpenTypeUShort(headTable + OFF_head_macStyle);
                if ((macStyleFlags & MacStyleFlags.Italic) != 0) 
                { 
                    fontStyle = FontStyles.Italic;
                } 
                if ((macStyleFlags & MacStyleFlags.Bold) != 0)
                {
                    fontWeight = FontWeights.Bold;
                } 
                if ((macStyleFlags & MacStyleFlags.Condensed) != 0)
                { 
                    fontStretch = FontStretches.Condensed; 
                }
                if ((macStyleFlags & MacStyleFlags.Extended) != 0) 
                {
                    fontStretch = FontStretches.Expanded;
                }
            } 
        }
 
        private static ushort IntToUshort(int n) 
        {
            if (n >= 0 && n <= ushort.MaxValue) 
                return (ushort)n;
            else if (n < 0)
                return 0;
            else 
                return ushort.MaxValue;
        } 
 
        private void ReadBasicMetrics(
            CheckedPointer  headTable, 
            CheckedPointer  os2Table,
            CheckedPointer  hheaTable,
            out ushort      designEmHeight,
            out ushort      designCellAscent, 
            out ushort      designCellDescent,
            out int         designLineSpacing 
            ) 
        {
            designEmHeight = ReadOpenTypeUShort(headTable + OFF_head_unitsPerEm); 

            if (designEmHeight == 0)
            {
                throw new FileFormatException(SourceUri); 
            }
 
            if (!os2Table.IsNull && 
                ((Os2SelectionFlags)ReadOpenTypeUShort(os2Table + OFF_OS2_usSelection) & Os2SelectionFlags.DontUseWinLineMetrics) != 0)
            { 
                // The font specifies that the sTypoAscender, sTypoDescender, and sTypoLineGap fields are valid and
                // should be used instead of winAscent and winDescent.
                int typoAscender = ReadOpenTypeShort(os2Table + OFF_OS2_sTypoAscender);
                int typoDescender = ReadOpenTypeShort(os2Table + OFF_OS2_sTypoDescender); 
                int typoLineGap = ReadOpenTypeShort(os2Table + OFF_OS2_sTypoLineGap);
 
                // We include the line gap in the ascent so that white space is distributed above the line. (Note that 
                // the typo line gap is a different concept than "external leading".)
                designCellAscent = IntToUshort(typoAscender + typoLineGap); 

                // Typo descent is a signed value where the positive direction is up. It is therefore typically negative.
                // A signed typo descent would be quite unusual as it would indicate the descender was above the baseline.
                designCellDescent = IntToUshort(-typoDescender); 

                designLineSpacing = typoAscender + typoLineGap - typoDescender; 
            } 
            else
            { 
                // get the ascender field
                int ascender = ReadOpenTypeUShort(hheaTable + OFF_hhea_ascender);

                // get the descender field; this is measured in the same direction as ascender and is therefore 
                // normally negative whereas we want a positive value; however some fonts get the sign wrong
                // so instead of just negating we take the absolute value. 
                int descender = Math.Abs((int)ReadOpenTypeShort(hheaTable + OFF_hhea_descender)); 

                // get the lineGap field and make sure it's >= 0 
                int lineGap = Math.Max(0, (int)ReadOpenTypeShort(hheaTable + OFF_hhea_lineGap));

                if (!os2Table.IsNull)
                { 
                    // we could use sTypoAscender, sTypoDescender, and sTypoLineGap which are supposed to represent
                    // optimal typographic values not constrained by backwards compatibility; however, many fonts get 
                    // these fields wrong or get them right only for Latin text; therefore we use the more reliable 
                    // platform-specific Windows values. We take the absolute value of the win32descent in case some
                    // fonts get the sign wrong. 
                    int winAscent = ReadOpenTypeUShort(os2Table + OFF_OS2_usWinAscent);
                    int winDescent = Math.Abs((int)ReadOpenTypeShort(os2Table + OFF_OS2_usWinDescent));

                    designCellAscent = (ushort)winAscent; 
                    designCellDescent = (ushort)winDescent;
 
                    // The following calculation for designLineSpacing is per [....]. The default line spacing 
                    // should be the sum of the Mac ascender, descender, and lineGap unless the resulting value would
                    // be less than the cell height (winAscent + winDescent) in which case we use the cell height. 
                    // See also http://www.microsoft.com/typography/otspec/recom.htm.
                    //
                    // Note that in theory it's valid for the baseline-to-baseline distance to be less than the cell
                    // height. However, Windows has never allowed this for Truetype fonts, and fonts built for Windows 
                    // sometimes rely on this behavior and get the hha values wrong or set them all to zero.
                    // 
                    designLineSpacing = Math.Max( 
                        lineGap + ascender + descender,
                        winAscent + winDescent 
                        );
                }
                else
                { 
                    designCellAscent = (ushort)ascender;
                    designCellDescent = (ushort)descender; 
                    designLineSpacing = ascender + descender + lineGap; 
                }
            } 
        }

        /// 
        /// Decode gasp table and decide whether the font is a legacy East Asian font. 
        /// This is need to determine the best rendering method.
        ///  
        ///  
        ///     Critical: This code writes into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReadRenderingHints(CheckedPointer headTable, FontFaceLayoutInfo cache)
        { 
            CheckedPointer gaspTable = GetTable(TrueTypeTags.GridfitAndScanProc);
            if (!gaspTable.IsNull) 
            { 
                // skip table version
                gaspTable += 2; 

                // read number of GASP ranges
                ushort numRanges = ReadOpenTypeUShort(gaspTable);
                gaspTable += 2; 

                FontFaceLayoutInfo.GaspRange [] gaspRanges = new FontFaceLayoutInfo.GaspRange[numRanges]; 
                for (int i = 0; i < numRanges; ++i) 
                {
                    gaspRanges[i].ppem = ReadOpenTypeUShort(gaspTable); 
                    gaspTable += 2;
                    gaspRanges[i].flags = (FontFaceLayoutInfo.GaspFlags)ReadOpenTypeUShort(gaspTable);
                    gaspTable += 2;
                } 
                cache.GaspRanges = gaspRanges;
            } 
 
            List languagesFound = new List(4);
 
            // inspect cmap to determine whether the font is East Asian or not
            for (int i = 0; i < EastAsianLanguages.RepresentativeCodepoints.GetLength(0); ++i)
            {
                bool allCodePointsPresent = true; 
                for (int j = 0; j < EastAsianLanguages.RepresentativeCodepoints.GetLength(1); ++j)
                { 
                    if (!cache.CharacterMap.ContainsKey(EastAsianLanguages.RepresentativeCodepoints[i, j])) 
                    {
                        allCodePointsPresent = false; 
                        break;
                    }
                }
                if (allCodePointsPresent) 
                    languagesFound.Add(new EastAsianLanguageCandidate(i));
            } 
 
            if (languagesFound.Count == 0)
            { 
                // this is not an East Asian font
                cache.FontRenderingHints = FontFaceLayoutInfo.RenderingHints.Regular;
                return;
            } 

            Debug.Assert(languagesFound.Count <= EastAsianLanguages.RepresentativeCodepoints.GetLength(0)); 
 
            ushort headFlags = ReadOpenTypeUShort(headTable + OFF_head_flags);
            if ((headFlags & (ushort)HeadFlags.OptimizedForClearType) != 0) 
            {
                // this is a ClearType hinted East Asian font
                cache.FontRenderingHints = FontFaceLayoutInfo.RenderingHints.Regular;
                return; 
            }
 
            CheckedPointer eblcTable = GetTable(TrueTypeTags.BitmapLocation); 
            if (eblcTable.IsNull)
            { 
                // this is a regular font
                cache.FontRenderingHints = FontFaceLayoutInfo.RenderingHints.Regular;
                return;
            } 

            // extract all glyph ranges that have embedded bitmaps 
            int numBitmapSizeTables = (int)ReadOpenTypeULong(eblcTable + OFF_eblc_numSizes); 

            CheckedPointer bitmapSizeTable = eblcTable + OFF_eblc_bitmapSizeTables; 
            for (int i = 0; i < numBitmapSizeTables; ++i, bitmapSizeTable += EblcSizeOfBitmapSizeTable)
            {
                int numberOfIndexSubTables = (int)ReadOpenTypeULong(bitmapSizeTable + EblcNumberOfIndexSubTables);
                int indexSubTableArrayOffset = (int)ReadOpenTypeULong(bitmapSizeTable + EblcIndexSubTableArrayOffset); 

                CheckedPointer subTable = eblcTable + indexSubTableArrayOffset; 
                for (int j = 0; j < numberOfIndexSubTables; ++j, subTable += EblcSizeOfIndexSubTable) 
                {
                    ushort firstGlyphIndex = ReadOpenTypeUShort(subTable + EblcFirstGlyph); 
                    ushort lastGlyphIndex = ReadOpenTypeUShort(subTable + EblcLastGlyph);

                    // now we know that glyph range [firstGlyphIndex, lastGlyphIndex] has embedded bitmaps in it
                    foreach (EastAsianLanguageCandidate lang in languagesFound) 
                    {
                        for (int k = 0; k < EastAsianLanguages.RepresentativeCodepoints.GetLength(1); ++k) 
                        { 
                            char c = EastAsianLanguages.RepresentativeCodepoints[lang.LanguageIndex, k];
 
                            // PERF: we could perform cmap lookup in advance in case doing it on every
                            // iteration is determined to be too slow.
                            ushort g;
 
                            // finally, check if the glyph is in the embedded bitmap range
                            if (cache.CharacterMap.TryGetValue(c, out g) && 
                                firstGlyphIndex <= g && 
                                g <= lastGlyphIndex)
                            { 
                                lang.AddCodepoint(k);
                            }
                        }
                    } 
                }
            } 
            // now, if the font has enough embedded bitmaps to represent a language 
            // we treat it as a legacy East Asian font. Otherwise, it's a Regular font.
            foreach (EastAsianLanguageCandidate lang in languagesFound) 
            {
                Debug.Assert(EastAsianLanguages.RepresentativeCodepoints.GetLength(1) == 6);

                // If all of lower 6 bits are set, this means that all of the 6 codepoints 
                // have embedded bitmaps. We treat such fonts as legacy East Asian fonts.
                if (lang.CodePoints == 63) 
                { 
                    cache.FontRenderingHints = FontFaceLayoutInfo.RenderingHints.LegacyEastAsian;
                    return; 
                }
            }

            cache.FontRenderingHints = FontFaceLayoutInfo.RenderingHints.Regular; 
            return;
        } 
 
        /// 
        /// Computes the contrast adjustment value for this font. 
        /// The value is used to fine tune the text contrast value used by glyph rendering code.
        /// 
        /// 
        ///     Critical: This code writes into FontFaceLayoutInfo. 
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private void ComputeFontContrastAdjustment(ref ParsedNameTable nameTable, FontFaceLayoutInfo cache)
        { 
            if (_technology == FontTechnology.PostscriptOpenType)
                cache.FontContrastAdjustment = -1;
            else
            { 
                // For "Courier New" and similar fonts, bump up the contrast value by 2.
                // This is similar to what GDI and GDI+ ClearType code does to achieve more readable glyphs for these thin fonts. 
                // One difference is that we do this for the whole font family, not just for regular faces. 
                // This achieves more consistent look across different weights.
                short fontContrastAdjustment = 0; 
                LocalizedName[] familyNames = nameTable.familyNames;
                if (familyNames == null)
                    familyNames = nameTable.win32FamilyNames;
 
                if (familyNames != null)
                { 
                    foreach (LocalizedName name in familyNames) 
                    {
                        if (Array.BinarySearch(_thinFontFamilyNames, name.Name, StringComparer.OrdinalIgnoreCase) >= 0) 
                            fontContrastAdjustment = 1;
                    }
                }
                cache.FontContrastAdjustment = fontContrastAdjustment; 
            }
        } 
 
        /// 
        /// Analyzes os/2 fsType value and construct FontEmbeddingRight enum value from it. 
        /// 
        /// 
        ///     Critical: This code writes critical information into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        FontEmbeddingRight ReadFontEmbeddingRights(CheckedPointer os2Table) 
        {
            // If there is no os/2 table, default to restricted font. 
            // This is the precedence that has been set by T2Embed, Word, etc.
            // No one has complained about this because these fonts are generally lower in quality and are less likely to be embedded.
            FontEmbeddingRight rights = FontEmbeddingRight.RestrictedLicense;
            if (!os2Table.IsNull) 
            {
                ushort fsType = ReadOpenTypeUShort(os2Table + OFF_OS2_fsType); 
 
                // Start with the most restrictive flags.
                // In case a font uses conflicting flags, 
                // expose the least restrictive combination in order to be compatible with existing applications.

                if ((fsType & Os2EmbeddingFlags.InstallableMask) == 0)
                { 
                    // The font is installable if all bits in the InstallableMask are set to zero.
                    switch (fsType & (Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly)) 
                    { 
                        case 0:
                            rights = FontEmbeddingRight.Installable; 
                            break;
                        case Os2EmbeddingFlags.NoSubsetting:
                            rights = FontEmbeddingRight.InstallableButNoSubsetting;
                            break; 
                        case Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.InstallableButWithBitmapsOnly; 
                            break; 
                        case Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.InstallableButNoSubsettingAndWithBitmapsOnly; 
                            break;
                    }
                }
                else if ((fsType & Os2EmbeddingFlags.Editable) != 0) 
                {
                    switch (fsType & (Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly)) 
                    { 
                        case 0:
                            rights = FontEmbeddingRight.Editable; 
                            break;
                        case Os2EmbeddingFlags.NoSubsetting:
                            rights = FontEmbeddingRight.EditableButNoSubsetting;
                            break; 
                        case Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.EditableButWithBitmapsOnly; 
                            break; 
                        case Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.EditableButNoSubsettingAndWithBitmapsOnly; 
                            break;
                    }
                }
                else if ((fsType & Os2EmbeddingFlags.PreviewAndPrint) != 0) 
                {
                    switch (fsType & (Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly)) 
                    { 
                        case 0:
                            rights = FontEmbeddingRight.PreviewAndPrint; 
                            break;
                        case Os2EmbeddingFlags.NoSubsetting:
                            rights = FontEmbeddingRight.PreviewAndPrintButNoSubsetting;
                            break; 
                        case Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.PreviewAndPrintButWithBitmapsOnly; 
                            break; 
                        case Os2EmbeddingFlags.NoSubsetting | Os2EmbeddingFlags.BitmapOnly:
                            rights = FontEmbeddingRight.PreviewAndPrintButNoSubsettingAndWithBitmapsOnly; 
                            break;
                    }
                }
                else 
                {
                    // Otherwise, the font either has Os2EmbeddingFlags.RestrictedLicense set, or 
                    // it has a reserved bit 0 set, which is invalid per specification. 
                    // Either way, rights should remain FontEmbeddingRight.RestrictedLicense.
                } 
            }
            return rights;
        }
 
        #endregion OS/2 and head decoding
 
 
        #region Name table decoding
 
        private class NameCollection
        {
            internal class LocalizedNameCandidate
            { 
                // key
                internal CultureInfo       culture;        // culture info for the name 
                internal CharacterEncoding characterEncoding;  // the glyph encoding of the candidate 

                // data 
                internal int            offset;         // offset to the name in the file
                internal int            length;         // length of the name in the file
            }
 
            private static string ParseUtf16Name(LocalizedNameCandidate name, CheckedPointer nameTable)
            { 
                int stringLength = name.length / 2; 
                StringBuilder sb = new StringBuilder(stringLength);
 
                int offsetToString = ReadOpenTypeUShort(nameTable + 4) + name.offset;

                for (int i = 0; i < stringLength; i ++)
                { 
                    ushort c = ReadOpenTypeUShort(nameTable + (offsetToString + i * 2));
                    if (c == 0) 
                        break; 
                    sb.Append((char)c);
                } 

                return sb.ToString();
            }
 

            ///  
            ///     Critical: This code calls into probe and also has an unsafe code block 
            ///     TreatAsSafe: Converts a string to ANSI and checks for NUll value of checked pointer
            ///  

            [SecurityCritical,SecurityTreatAsSafe]
            private static string Parse8BitCodepageName(LocalizedNameCandidate name, CheckedPointer nameTable)
            { 
                CharacterToUnicodeMapper mapper = GetCharacterToUnicodeMapperForCharacterEncoding(name.characterEncoding);
 
                int offsetToString = ReadOpenTypeUShort(nameTable + 4) + name.offset; 

                // for DBCS legacy encoding, the name may be encoded in a format where every character takes two bytes 
                // and single byte character have zero for the high byte, we need to skip all those null bytes before
                // calling the Ansi to Unicode conversion

                byte[] bytes = new byte[name.length]; 
                int ansiLength = 0;
 
                unsafe 
                {
                    byte * nameTablePointer = (byte *)nameTable.Probe(offsetToString, name.length); 
                    for (int originalIndex = 0; originalIndex < name.length; originalIndex++)
                    {
                        if (nameTablePointer[originalIndex] != 0)
                        { 
                            bytes[ansiLength] = nameTablePointer[originalIndex];
                            ansiLength++; 
                        } 
                    }
                } 

                return mapper.MapBytesToUtf16(bytes, ansiLength);
            }
 

            internal void AddNameEntry( 
                PlatformID     platformID, 
                ushort         encodingID,
                ushort         languageID, 
                int            tableIndex,
                CheckedPointer nameTable
                )
            { 
                LocalizedNameCandidate newName = new LocalizedNameCandidate();
                newName.culture = MapTrueTypeLangIdToCulture(platformID, languageID); 
 
                if (newName.culture == null) // we don't recognize this LCID
                { 
                    return;
                }

                newName.characterEncoding = PlatformIdAndEncodingIdToCharacterEncoding(platformID, encodingID); 

                if (newName.characterEncoding == CharacterEncoding.Unknown) 
                { 
                    return;
                } 

                int index = _names.BinarySearch(newName, _candidateComparer);
                if (index < 0)
                { 
                    // no previous entry with this lcid was found, add a new one in sorted order
                    _names.Insert(~index, newName); 
                } 
                else
                { 
                    LocalizedNameCandidate oldName = _names[index];

                    // see if we have a better glyph table type match
                    if (newName.characterEncoding <= oldName.characterEncoding) 
                        return;
 
                    // replace the old name with a better one 
                    _names[index] = newName;
                } 
                newName.length = ReadOpenTypeUShort(nameTable + (14 + tableIndex * 12));
                newName.offset = ReadOpenTypeUShort(nameTable + (16 + tableIndex * 12));
            }
 
            /// 
            /// ConvertString converts a string from its format in a font name table to UTF-16. 
            /// Returns null if the glyph table type is invalid. 
            /// 
            private string ParseNameString(LocalizedNameCandidate name, CheckedPointer nameTable) 
            {
                switch (name.characterEncoding)
                {
                    // The following encodongs all imply that names are stored in UTF16 
                    case CharacterEncoding.MsUcs4:
                    case CharacterEncoding.MsSymbol: 
                    case CharacterEncoding.MsUcs2: 
                    case CharacterEncoding.Unicode:
                    case CharacterEncoding.MsShiftJis: 
                        // ShiftJis appears in this list because GDI is decoding the name table of shiftjis fonts
                        // as Unicode (even though it decodes the name table for Big5, Wansung, GB fonts as DBCS Ansi).
                        // Since GDI is doing like this and legacy fonts are build like this, I need to continue the legacy
                        return ParseUtf16Name(name, nameTable); 

                    case CharacterEncoding.MacRoman: 
                    case CharacterEncoding.MsPrc: 
                    case CharacterEncoding.MsBig5:
                    case CharacterEncoding.MsWansung: 
                        // Single or double byte 8-bit based character set
                        return Parse8BitCodepageName(name, nameTable);

                    default: 
                        return null;      // Unknown encoding
                } 
            } 

            internal string ConvertVersion(CheckedPointer nameTable) 
            {
                LocalizedNameCandidate nameCandidate = null;
                foreach (LocalizedNameCandidate name in _names)
                { 
                    nameCandidate = name;
                    if (name.culture.Equals(EnglishUSCulture)) 
                        break; 
                }
                if (nameCandidate == null) 
                    return null;

                return ParseNameString(nameCandidate, nameTable);
            } 

 
            internal LocalizedName [] ConvertAllNames(CheckedPointer nameTable) 
            {
                if (_names.Count == 0) 
                    return null;

                // Count how many strings are in MS supported formats.
 
                int supportedNameCount=0;
                for (int i=0; i<_names.Count; i++) 
                { 
                    if (ParseNameString(_names[i], nameTable) != null)
                    { 
                        supportedNameCount++;
                    }
                }
 
                if (supportedNameCount == 0)
                    return null; 
 
                // Allocate and fill in the localizedNames array.
 
                LocalizedName [] localizedNames = new LocalizedName[supportedNameCount];
                int j=0;
                for (int i=0; i<_names.Count; i++)
                { 
                    string convertedString = ParseNameString(_names[i], nameTable);
                    if (convertedString != null) 
                    { 
                        // Conversion succeeded: add converted name to publicly accessible names.
                        // Make sure to pass the original culture LCID, as we rely on the LocalizedNameCandidate being sorted by it 
                        // later on in the FontFaceLayoutInfo code when we perform binary search. Please see ConvertNames and FindLCID methods.
                        localizedNames[j++] = new LocalizedName(XmlLanguage.GetLanguage(_names[i].culture.IetfLanguageTag), convertedString, _names[i].culture.LCID);
                    }
                } 

                return localizedNames; 
            } 

 
            private class CandidateComparer : IComparer
            {
                #region IComparer Members
 
                int IComparer.Compare(LocalizedNameCandidate x, LocalizedNameCandidate y)
                { 
                    // The sort function below is used only to detect duplicate CultureInfo objects. 
                    int xlcid = x.culture.LCID;
                    int ylcid = y.culture.LCID; 
                    return xlcid - ylcid;
                }

                #endregion 
            }
 
            // the list contains LocalizedNameCandidate entries sorted by LCID 
            private List _names = new List(2);
 
            private static CandidateComparer _candidateComparer = new CandidateComparer();
        }

        // this function is written in the way compatible with the legacy code 
        // because version number in the name table can be written in many ways
        // please don't try to "optimize" or "fix" it without talking 
        // to one of TrueType experts 
        private double VersionToDouble(string versionString)
        { 
            if (versionString == null)
                return 0.0;

            double version = 0.0; 
            string subString;
            int i = 0; 
            int start = versionString.Length; 

            while (i < versionString.Length) 
            {
                if (Char.IsDigit(versionString, i))
                {
                    start = i; 
                    break;
                } 
                i++; 
            }
 
            if (start < versionString.Length)
            {
                i++;
                while ((i < versionString.Length) && Char.IsDigit(versionString, i)) 
                {
                    i++; 
                } 
                if ((i < versionString.Length) && (versionString[i] == '.'))
                { 
                    i++;
                    while ((i < versionString.Length) && Char.IsDigit(versionString, i))
                    {
                        i++; 
                    }
                } 
                subString = versionString.Substring(start, i - start); 

                // the version string is always formatted using English number format 
                if (!double.TryParse(subString, NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out version))
                    version = 0.0;
            }
            return version; 
        }
 
        private void DecodeNameTable(ref ParsedNameTable parsedNameTable) 
        {
            // extracting information from the name table 

            CheckedPointer nameTable = GetTable(TrueTypeTags.NamingTable);

            if (nameTable.IsNull) 
            {
                throw new FileFormatException(SourceUri); 
            } 

            ushort numberOfNameRecords = ReadOpenTypeUShort(nameTable + 2); 

            if (numberOfNameRecords == 0)
            {
                throw new FileFormatException(SourceUri); 
            }
 
            NameCollection fontFamilyName = new NameCollection(); 
            NameCollection fontSubfamilyName = new NameCollection();
            NameCollection win32FamilyName = new NameCollection(); 
            NameCollection win32SubfamilyName = new NameCollection();
            NameCollection versionStrings = new NameCollection();
            NameCollection copyrights = new NameCollection();
            NameCollection manufacturerNames = new NameCollection(); 
            NameCollection trademarks = new NameCollection();
            NameCollection designerNames = new NameCollection(); 
            NameCollection descriptions = new NameCollection(); 
            NameCollection vendorUrls = new NameCollection();
            NameCollection designerUrls = new NameCollection(); 
            NameCollection licenseDescriptions = new NameCollection();
            NameCollection sampleTexts = new NameCollection();

            for (int i = 0; i < numberOfNameRecords; i++) 
            {
                PlatformID platformID = (PlatformID)ReadOpenTypeUShort(nameTable + (6 + i * 12)); 
                ushort specificID = ReadOpenTypeUShort(nameTable + (8 + i * 12)); 
                ushort languageID = ReadOpenTypeUShort(nameTable + (10 + i * 12));
                ushort nameID = ReadOpenTypeUShort(nameTable + (12 + i * 12)); 

                switch ((NameTableNameID)nameID)
                {
                    case NameTableNameID.FontFamilyName: 
                        win32FamilyName.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 
 
                    case NameTableNameID.FontSubfamilyName:
                        win32SubfamilyName.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break;

                    case NameTableNameID.PreferredFamily:
                        fontFamilyName.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break;
 
                    case NameTableNameID.PreferredSubfamily: 
                        fontSubfamilyName.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 

                    case NameTableNameID.Version:
                        versionStrings.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 

                    case NameTableNameID.CopyrightNotice: 
                        copyrights.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break;
 
                    case NameTableNameID.Trademark:
                        trademarks.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break;
 
                    case NameTableNameID.ManufacturerName:
                        manufacturerNames.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break; 

                    case NameTableNameID.Designer: 
                        designerNames.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break;

                    case NameTableNameID.Description: 
                        descriptions.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 
 
                    case NameTableNameID.URLVendor:
                        vendorUrls.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break;

                    case NameTableNameID.URLDesigner:
                        designerUrls.AddNameEntry(platformID, specificID, languageID, i, nameTable); 
                        break;
 
                    case NameTableNameID.LicenseDescription: 
                        licenseDescriptions.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 

                    case NameTableNameID.SampleText:
                        sampleTexts.AddNameEntry(platformID, specificID, languageID, i, nameTable);
                        break; 
                }
            } 
 
            parsedNameTable.familyNames         = fontFamilyName.ConvertAllNames(nameTable);
            parsedNameTable.win32FamilyNames    = win32FamilyName.ConvertAllNames(nameTable); 
            parsedNameTable.faceNames           = fontSubfamilyName.ConvertAllNames(nameTable);
            parsedNameTable.win32faceNames      = win32SubfamilyName.ConvertAllNames(nameTable);
            parsedNameTable.versionStrings      = versionStrings.ConvertAllNames(nameTable);
            parsedNameTable.copyrights          = copyrights.ConvertAllNames(nameTable); 
            parsedNameTable.manufacturerNames   = manufacturerNames.ConvertAllNames(nameTable);
            parsedNameTable.trademarks          = trademarks.ConvertAllNames(nameTable); 
            parsedNameTable.designerNames       = designerNames.ConvertAllNames(nameTable); 
            parsedNameTable.descriptions        = descriptions.ConvertAllNames(nameTable);
            parsedNameTable.vendorUrls          = vendorUrls.ConvertAllNames(nameTable); 
            parsedNameTable.designerUrls        = designerUrls.ConvertAllNames(nameTable);
            parsedNameTable.licenseDescriptions = licenseDescriptions.ConvertAllNames(nameTable);
            parsedNameTable.sampleTexts         = sampleTexts.ConvertAllNames(nameTable);
 
            parsedNameTable.version             = VersionToDouble(versionStrings.ConvertVersion(nameTable));
        } 
 
        #endregion Name table decoding
 
        #region Cmap table decoding

        // CMAP format parsers
 
        /// 
        /// Format 0 - a simple array of 256 bytes 
        ///  
        /// 
        ///     Critical: This code writes into cmap. 
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
 
        private void ReadCmapFormat0(
            CheckedPointer            cmapTable, 
            int                       glyphTableOffset, 
            CharacterToUnicodeMapper  mapper,
            FontFaceLayoutInfo.IntMap characterMap 
        )
        {
            int bytesOffset = glyphTableOffset + 6;
 
            for (int ch=0; ch<255; ch++)
            { 
                int glyph = ReadOpenTypeByte(cmapTable + bytesOffset + ch); 
                int unicode = mapper.MapCharacterToUnicode(ch);
                if (unicode != -1) // Skip codepoints not representable in Unicode 
                {
                    characterMap.SetCharacterEntry(unicode, unchecked((ushort)glyph));
                }
            } 
        }
 
 
        /// 
        ///     Critical: This code writes into cmap. 
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReadCmapFormat2( 
            CheckedPointer            cmapTable,
            int                       glyphTableOffset, 
            CharacterToUnicodeMapper  mapper, 
            FontFaceLayoutInfo.IntMap characterMap
            ) 
        {
            int subHeaderOffset = glyphTableOffset + 6 + (256 * 2);

            for (int ii = 0; ii < 256; ii++) 
            {
                int jj            = ReadOpenTypeUShort(cmapTable + (glyphTableOffset + 6 + ii * 2));   // SubHeaderKeys 
                int firstCode     = ReadOpenTypeUShort(cmapTable + (subHeaderOffset + jj)); 
                int entryCount    = ReadOpenTypeUShort(cmapTable + (subHeaderOffset + 2 + jj));
                short idDelta     = ReadOpenTypeShort (cmapTable + (subHeaderOffset + 4 + jj)); 
                int idRangeOffset = ReadOpenTypeUShort(cmapTable + (subHeaderOffset + 6 + jj));

                int glyphArrayOffset = subHeaderOffset + jj + 6 + idRangeOffset;
 
                if (jj == 0)
                { 
                    int unicode = mapper.MapCharacterToUnicode(ii); 
                    // Special case: single bte codepoint not valid for this codepage.
                    // Fonts sometimes provide glyphs in these areas, for example the 
                    // control code areas in CP950 are reserved, but fonts may provide
                    // a representation.
                    if (unicode == -1)
                    { 
                        // No mapping required.
                        unicode = ii; 
                    } 

                    ushort hGlyph = ReadOpenTypeUShort(cmapTable + (glyphArrayOffset + (ii - firstCode) * 2)); 
                    if (hGlyph != 0)
                    {
                        characterMap.SetCharacterEntry(unicode, hGlyph);
                    } 
                }
                else 
                { 
                    for( jj = firstCode ; jj < firstCode + entryCount ; jj++ )
                    { 
                        int unicode = mapper.MapCharacterToUnicode((ii<<8)+jj);
                        if (unicode != -1) // Skip CMAP entries that don't map to Unicode
                        {
                            int hGlyph = ReadOpenTypeUShort(cmapTable + (glyphArrayOffset + (jj - firstCode) * 2)); 
                            if( hGlyph != 0 )
                            { 
                                characterMap.SetCharacterEntry(unicode, unchecked((ushort)(hGlyph + idDelta))); 
                            }
                        } 
                    }
                }
            }
        } 

 
        ////    MapCmapFormat4Glyph - Interpret Truetype CMAP type 4 range details 
        //
        //      Implements format 4 of the TrueType cmap table - 'Segment 
        //      mapping to delta values' described in chapter 2 of the 'TrueType
        //      1.0 Font Files Rev. 1.66' document.

        private ushort MapCmapFormat4Glyph( 
            CheckedPointer  cmapTable,
            int             wc,   // Character 
            int             offsetToCurrentIdRange, 
            int             idRangeOffset,
            int             startCount, 
            short           idDelta
            )
        {
            int g; 
            int offsetToGlyph;
 
            if (wc >= 0xffff) 
            {
 
                // Don't map U+0FFFF as some fonts (Pristina) don't map it
                // correctly and cause an AV in a subsequent lookup.

                return 0; 
            }
 
            if (idRangeOffset != 0) 
            {
                offsetToGlyph = idRangeOffset 
                    + (wc - startCount) * 2
                    + offsetToCurrentIdRange;

                g = ReadOpenTypeUShort(cmapTable + offsetToGlyph); 

                if (g != 0) 
                { 
                    g += idDelta;
                } 
            }
            else
            {
                g = wc + idDelta; 
            }
 
            return unchecked((ushort)g); 
        }
 

        /// 
        ///     Critical: This code writes into cmap.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void ReadCmapFormat4( 
            CheckedPointer            cmapTable,
            int                       glyphTableOffset, 
            CharacterToUnicodeMapper  mapper,
            FontFaceLayoutInfo.IntMap characterMapInfo
            )
        { 
            ushort segCount = (ushort)(ReadOpenTypeUShort(cmapTable + (glyphTableOffset + 6)) / 2);
 
            int endCountOffset     = glyphTableOffset + 14; 
            int startCountOffset   = endCountOffset   + (segCount + 1) * 2;
            int idDeltaOffset      = startCountOffset + (segCount * 2); 
            int idRangeTableOffset = idDeltaOffset    + (segCount * 2);


            // Loop through the segments mapping glyphs 

            int i; 
            int endCount = ReadOpenTypeUShort(cmapTable + endCountOffset); 

            for (i=0; i 0 &&  startCount < endCount) 
                {
                    startCount = endCount + 1; 
                }

                endCount = ReadOpenTypeUShort(cmapTable + (endCountOffset + i * 2));
 
                // Some font tools generate an final invalid mapping from codepoint FFFF.
                // Since FFFF is invalid in all codepages, we ignore it. 
 
                if (endCount == 0xffff)
                { 
                    endCount--;
                }

                for (int characterCode = startCount; characterCode <= endCount; characterCode++) 
                {
                    int unicode = mapper.MapCharacterToUnicode(characterCode); 
                    if (unicode != -1)  // Skip codepoints not representable in Unicode 
                    {
                        characterMapInfo.SetCharacterEntry( 
                            unicode,
                            MapCmapFormat4Glyph(cmapTable, characterCode, idRangeTableOffset + (i*2), idRangeOffset, startCount, idDelta));
                    }
                } 
            }
        } 
 

        ///  
        /// Format 6: Trimmed table mapping
        ///
        /// Type Name Description
        /// USHORT format Format number is set to 6. 
        /// USHORT length This is the length in bytes of the subtable.
        /// USHORT language Please see "Note on the language field in 'cmap' subtables" in this document. 
        /// USHORT firstCode First character code of subrange. 
        /// USHORT entryCount Number of character codes in subrange.
        /// USHORT glyphIdArray [entryCount] Array of glyph index values for character codes in the range. 
        ///
        ///
        /// The firstCode and entryCount values specify a subrange (beginning at
        /// firstCode,length = entryCount) within the range of possible character 
        /// codes.  Codes outside of this subrange are mapped to glyph index 0.  The
        /// offset of the code (from the first code) within this subrange is used as 
        /// index to the glyphIdArray, which provides the glyph index value. 
        /// 
        /// 
        /// 
        ///     Critical: This code writes into cmap.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReadCmapFormat6( 
            CheckedPointer            cmapTable, 
            int                       glyphTableOffset,
            CharacterToUnicodeMapper  mapper, 
            FontFaceLayoutInfo.IntMap characterMap
            )
        {
            int firstCode  = ReadOpenTypeUShort(cmapTable + glyphTableOffset + 6); 
            int entryCount = ReadOpenTypeUShort(cmapTable + glyphTableOffset + 8);
 
            for (int i = 0; i < entryCount; i++) 
            {
                int unicode = mapper.MapCharacterToUnicode(firstCode + i); 
                if (unicode != -1) // Skip codepoints not representable in Unicode
                {
                    ushort glyph = ReadOpenTypeUShort(cmapTable + glyphTableOffset + 10 + (firstCode+i)*2);
                    if (glyph != 0) 
                    {
                        characterMap.SetCharacterEntry(unicode, glyph); 
                    } 
                }
            } 
        }


        ///  
        ///     Critical: This code writes into cmap.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReadCmapFormat12( 
            CheckedPointer              cmapTable,
            int                         glyphTableOffset,
            FontFaceLayoutInfo.IntMap   characterMapInfo
            ) 
        {
            int groupCount = ReadOpenTypeLong(cmapTable + (glyphTableOffset + 12)); 
 
            // Iterate through groups filling in cmap table
 
            for (int i=0; i < groupCount; i++)
            {
                int startCharCode = ReadOpenTypeLong(cmapTable + (glyphTableOffset + 16 + i * 12));
                int endCharCode   = ReadOpenTypeLong(cmapTable + (glyphTableOffset + 20 + i * 12)); 
                int startGlyphID  = ReadOpenTypeLong(cmapTable + (glyphTableOffset + 24 + i * 12));
 
                for (int unicode = startCharCode; unicode <= endCharCode; unicode++) 
                {
                    characterMapInfo.SetCharacterEntry(unicode, unchecked((ushort)(startGlyphID + unicode - startCharCode))); 
                }
            }
        }
 

 
        ///  
        /// CrossPopulateSymbolFontCodepoints - duplicate codepoints between ranges
        /// 0-ff and f000-f0ff. 
        ///
        /// Any codepoints present in one but not both ranges are copied
        /// into the other range.
        /// 
        /// The OpenType standard recommends but does not require that symbol fonts
        /// are encoded from f000 to f0ff. GDI duplicates these codepoints into the 
        /// range 0-ff since most apps (and therefore most textual data) expect to 
        /// use 0-ff rather than f000-f0ff.
        /// 
        /// However, since most apps and OS APIs process carriage return (U+000d),
        /// line feed (U+000a) and maybe tab (U+0009) specially, any symbol at these
        /// positions is difficult to display, so apps needing access to such symbols
        /// generally use the f000-f0ff range when displaying symbols. For this 
        /// reason, to support symbol fonts that present their symbols in the
        /// range 0-ff, it is also necessary to copy codepoints 0-ff up to f000-f0ff. 
        /// 
        /// This code never overwrites a codepont already defined by the font cmap table,
        /// only codepoints that are undefined (i.e. with glyph index zero). 
        /// 
        /// 
        ///     Critical: This code writes into cmap.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void CrossPopulateSymbolFontCodepoints( 
            FontFaceLayoutInfo.IntMap characterMap
        ) 
        {
            for (int i=0; i<256; i++)
            {
                ushort lowGlyphIndex; 
                bool lowExists = characterMap.TryGetValue(i, out lowGlyphIndex);
 
                ushort highGlyphIndex; 
                bool highExists = characterMap.TryGetValue(i + 0xf000, out highGlyphIndex);
 
                if (highExists && !lowExists)
                {
                    characterMap.SetCharacterEntry(i, highGlyphIndex);
                } 
                else if (lowExists && !highExists)
                { 
                    characterMap.SetCharacterEntry(i + 0xf000, lowGlyphIndex); 
                }
            } 
        }


        ///  
        /// Decodes the cmap table
        ///  
        /// Font cache structure to fill in 
        /// Whether the font has Symbol encoding
        ///  
        ///     Critical: This code calls into SetGlypCount which is used as an index into
        ///     an unmanaged structure
        ///     TreatAsSafe: It retrieves this from cache.GlyphCount which is tracked
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private bool DecodeCmapTable(FontFaceLayoutInfo cache) 
        { 
            CheckedPointer cmapTable = GetTable(TrueTypeTags.CharToIndexMap);
            if (cmapTable.IsNull) 
            {
                throw new FileFormatException(SourceUri);
            }
 
            ushort encodingCount = ReadOpenTypeUShort(cmapTable + 2);
 
            int               glyphTableOffset; 
            CharacterEncoding characterEncoding;
 
            LookForBestEncoding(
                cmapTable,
                encodingCount,
                out glyphTableOffset, 
                out characterEncoding
            ); 
 
            // when we're interested in only basic font face info, cache is null
            if (cache == null) 
            {
                return characterEncoding == CharacterEncoding.MsSymbol;
            }
 

            FontFaceLayoutInfo.IntMap characterMapInfo = cache.CharacterMap; 
            characterMapInfo.SetGlyphCount(cache.GlyphCount); 

            ushort format = ReadOpenTypeUShort(cmapTable + glyphTableOffset); 

            switch(characterEncoding)
            {
                case CharacterEncoding.Unknown: 
                    throw new FileFormatException(SourceUri);
 
                case CharacterEncoding.MsSymbol: 
                case CharacterEncoding.MsUcs2:
                case CharacterEncoding.MsUcs4: 
                case CharacterEncoding.Unicode:

                    switch(format)
                    { 
                        case 4:  ReadCmapFormat4(cmapTable, glyphTableOffset, GetCharacterToUnicodeMapperForCharacterEncoding(characterEncoding), characterMapInfo);  break;
                        case 12: ReadCmapFormat12(cmapTable, glyphTableOffset, characterMapInfo);                    break; 
                        default: throw new FileFormatException(SourceUri); 
                    }
                    break; 


                case CharacterEncoding.MsShiftJis:
                case CharacterEncoding.MsPrc: 
                case CharacterEncoding.MsBig5:
                case CharacterEncoding.MsWansung: 
 
                    switch(format)
                    { 
                        case 2:  ReadCmapFormat2(cmapTable, glyphTableOffset, GetCharacterToUnicodeMapperForCharacterEncoding(characterEncoding), characterMapInfo);  break;
                        case 4:  ReadCmapFormat4(cmapTable, glyphTableOffset, GetCharacterToUnicodeMapperForCharacterEncoding(characterEncoding), characterMapInfo);  break;
                        default: throw new FileFormatException(SourceUri);
                    } 
                    break;
 
 
                case CharacterEncoding.MacRoman:
                    switch(format) 
                    {
                        case 0:  ReadCmapFormat0(cmapTable, glyphTableOffset, new MacRomanToUnicodeMapper(), characterMapInfo); break;
                        case 6:  ReadCmapFormat6(cmapTable, glyphTableOffset, new MacRomanToUnicodeMapper(), characterMapInfo); break;
                        default: throw new FileFormatException(SourceUri); 
                    }
                    break; 
 
                default:  throw new FileFormatException(SourceUri);
            } 


            // Symbol fonts have codepoints cross-populated between 0-ff and f000-f0ff.
 
            if (characterEncoding == CharacterEncoding.MsSymbol)
            { 
                CrossPopulateSymbolFontCodepoints(characterMapInfo); 
            }
 

            return characterEncoding == CharacterEncoding.MsSymbol;
        }
 

#endregion 
 
        #region Horizontal and vertical metrics
 
        private bool ValidatexHeight(ushort designEmHeight, ref short xHeight)
        {
            if (xHeight <= designEmHeight * 10 / 100 ||
                xHeight >= designEmHeight * 90 / 100) 
            {
                xHeight = 0; 
                return false; 
            }
            return true; 
        }

        private bool ValidateCapsHeight(ushort designEmHeight, ref short capsHeight)
        { 
            if (capsHeight <= designEmHeight * 10 / 100 ||
                capsHeight >  designEmHeight) 
            { 
                capsHeight = 0;
                return false; 
            }
            return true;
        }
 
        private bool ValidateHeights(ushort designEmHeight, ref short xHeight, ref short capsHeight)
        { 
            return ValidatexHeight(designEmHeight, ref xHeight) && ValidateCapsHeight(designEmHeight, ref capsHeight); 
        }
 
        /// 
        ///     Critical: This code writes into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ComputeHeights(CheckedPointer os2Table, ushort unitsPerEm, FontFaceLayoutInfo cache) 
        { 
            short xHeight = 0;
            short capsHeight = 0; 
            bool validHeights = false;

            // first, try to get xHeight and capsHeight from OS/2
            if (!os2Table.IsNull) 
            {
                ushort os2version = ReadOpenTypeUShort(os2Table + OFF_OS2_version); 
                if (os2version >= 2) 
                {
                    xHeight = ReadOpenTypeShort(os2Table + OFF_OS2_sxHeight); 
                    capsHeight = ReadOpenTypeShort(os2Table + OFF_OS2_sCapHeight);
                    validHeights = ValidateHeights(unitsPerEm, ref xHeight, ref capsHeight);
                }
            } 

            // in case there was a problem, try to get xHeight and capsHeight from PCLT 
 
            if (!validHeights)
            { 
                CheckedPointer pcltTable = GetTable(TrueTypeTags.PCLT);

                if (!pcltTable.IsNull)
                { 
                    if (xHeight == 0)
                        xHeight = unchecked((short)ReadOpenTypeUShort(pcltTable + OFF_PCLT_xHeight)); 
                    if (capsHeight == 0) 
                        capsHeight = unchecked((short)ReadOpenTypeUShort(pcltTable + OFF_PCLT_CapHeight));
                    validHeights = ValidateHeights(unitsPerEm, ref xHeight, ref capsHeight); 
                }
            }

            if (!validHeights && !cache.Symbol) 
            {
                if (xHeight == 0) 
                { 
                    for (int i = 0; i < MetricSearchList.xHeight.GetLength(0); i++)
                    { 
                        ushort glyphIndex;

                        if (!cache.CharacterMap.TryGetValue((int)MetricSearchList.xHeight[i, 0], out glyphIndex))
                            continue; 

                        // bsb = ah - (tsb + ymax - ymin) implies 
                        // ymax = ah - bsb - tsb + ymin, and ymin = -baseline 

                        long yMax = (long) 
                            cache.GetAdvanceHeight(glyphIndex) -
                            cache.GetBottomSidebearing(glyphIndex) -
                            cache.GetTopSidebearing(glyphIndex) -
                            cache.GetBaseline(glyphIndex); 

                        yMax = yMax * MetricSearchList.xHeight[i, 1] / 100; 
 
                        xHeight = unchecked((short)yMax);
                        if (ValidatexHeight(unitsPerEm, ref xHeight)) 
                            break;
                    }
                }
                if (capsHeight == 0) 
                {
                    for (int i = 0; i < MetricSearchList.capsHeight.GetLength(0); i++) 
                    { 
                        ushort glyphIndex;
 
                        if (!cache.CharacterMap.TryGetValue((int)MetricSearchList.capsHeight[i, 0], out glyphIndex))
                            continue;

                        // bsb = ah - (tsb + ymax - ymin) implies 
                        // ymax = ah - bsb - tsb + ymin, and ymin = -baseline
 
                        long yMax = (long) 
                            cache.GetAdvanceHeight(glyphIndex) -
                            cache.GetBottomSidebearing(glyphIndex) - 
                            cache.GetTopSidebearing(glyphIndex) -
                            cache.GetBaseline(glyphIndex);

                        yMax = yMax * MetricSearchList.capsHeight[i, 1] / 100; 

                        capsHeight = unchecked((short)yMax); 
                        if (ValidateCapsHeight(unitsPerEm, ref capsHeight)) 
                            break;
                    } 
                }
            }
            // set the values to reasonable defaults
            // if still unable to obtain them from the font 
            if (xHeight == 0)
            { 
                // times.ttf is 45%, arial.ttf is 51%, micross.ttf is 52% 
                xHeight = unchecked((short)(unitsPerEm * 50 / 100));
            } 
            if (capsHeight == 0)
            {
                // times.ttf is 66%, arial.ttf is 71%, micross.ttf is 72%
                capsHeight = unchecked((short)(unitsPerEm * 70 / 100)); 
            }
            cache.xHeight = xHeight; 
            cache.CapsHeight = capsHeight; 
        }
 

        /// 
        ///     Critical: This code writes into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void ReadHmtx(FontFaceLayoutInfo cache, CheckedPointer hmtxTable, ushort numberOfMetrics) 
        {
            for (ushort i = 0; i < numberOfMetrics; i++) 
            {
                cache.SetAdvanceWidth(i, ReadOpenTypeUShort(hmtxTable));
                hmtxTable += 2;
                cache.SetLeftSidebearing(i, ReadOpenTypeShort(hmtxTable)); 
                hmtxTable += 2;
            } 
 
            ushort fixedAdvance = cache.GetAdvanceWidth(unchecked((ushort)(numberOfMetrics - 1)));
 
            for (ushort i = numberOfMetrics; i < cache.GlyphCount; i++)
            {
                cache.SetAdvanceWidth(i, fixedAdvance);
                cache.SetLeftSidebearing(i, ReadOpenTypeShort(hmtxTable)); 
                hmtxTable += 2;
            } 
        } 

        ///  
        /// Read metrics from vmtx if it's present
        /// 
        /// Font cache structure
        /// Whether Vmtx was present in the font 
        /// 
        ///     Critical: This code writes into FontFaceLayoutInfo. 
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool ReadVmtx(FontFaceLayoutInfo cache)
        {
            CheckedPointer vheaTable = GetTable(TrueTypeTags.VertHeader);
            CheckedPointer vmtxTable = GetTable(TrueTypeTags.VerticalMetrics); 

            if (vheaTable.IsNull || vmtxTable.IsNull) 
                return false; 

            ushort numberOfMetrics = ReadOpenTypeUShort(vheaTable + OFF_vhea_numOfLongVerMetrics); 

            for (ushort i = 0; i < numberOfMetrics; i++)
            {
                cache.SetAdvanceHeight(i, ReadOpenTypeUShort(vmtxTable)); 
                vmtxTable += 2;
                cache.SetTopSidebearing(i, ReadOpenTypeShort(vmtxTable)); 
                vmtxTable += 2; 
            }
 
            ushort fixedAdvance = cache.GetAdvanceHeight(unchecked((ushort)(numberOfMetrics - 1)));

            for (ushort i = numberOfMetrics; i < cache.GlyphCount; i++)
            { 
                cache.SetAdvanceHeight(i, fixedAdvance);
                cache.SetTopSidebearing(i, ReadOpenTypeShort(vmtxTable)); 
                vmtxTable += 2; 
            }
            return true; 
        }

        /// 
        /// Prevent JIT from inlining this method, so that PresentationCFFRasterizer.dll and PresentationCFFRasterizerNative.dll are loaded on demand. 
        /// 
        ///  
        ///     Critical: This code writes into FontFaceLayoutInfo. 
        ///     TreatAsSafe: It does this only using font data and not user defined parameters.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        [MethodImpl(MethodImplOptions.NoInlining)]
        private void ReadCFFMetrics(
            FontFaceLayoutInfo cache, 
            bool vmtxPresent,
            ushort typoAscent, 
            ushort typoDescent) 
        {
            using (OTFRasterizer otfRasterizer = new OTFRasterizer()) 
            {
                ushort numberOfGlyphs = otfRasterizer.NewFont(_unmanagedMemoryStream, SourceUri, _faceIndex);
                if (numberOfGlyphs != cache.GlyphCount)
                    throw new FileFormatException(SourceUri); 

                MS.Internal.FontRasterization.Transform tform = new MS.Internal.FontRasterization.Transform(); 
                tform.a01 = tform.a10 = 0; 
                tform.a00 = tform.a11 = cache.DesignEmHeight * 0x10000;
 
                otfRasterizer.NewTransform(
                    12,
                    tform,
                    OverscaleMode.None, 
                    RenderingFlags.None);
 
                GlyphMetrics glyphMetrics = new GlyphMetrics(); 

                for (ushort i = 0; i < numberOfGlyphs; ++i) 
                {
                    otfRasterizer.NewGlyph(i);
                    otfRasterizer.GetMetrics(out glyphMetrics);
 
                    // per http://www.microsoft.com/typography/otspec/hmtx.htm
                    // rsb = aw - (lsb + xmax - xmin) 
                    short rsb = (short)(cache.GetAdvanceWidth(i) - ((int)cache.GetLeftSidebearing(i) + glyphMetrics.width)); 
                    cache.SetRightSidebearing(i, rsb);
 
                    int yMax = glyphMetrics.horizontalOrigin.y;
                    int yMin = yMax - glyphMetrics.height;

                    if (!vmtxPresent) 
                    {
                        // There's no vmtx - fallback appropriately 
 
                        // Win 9x uses the typographic height (typo ascender - typo descender),
                        // but NT uses the cell height (cell ascender + cell descender). 

                        // Which shall we use? The problem with the cell height is that in a
                        // multilingual font it may be much taller than the East Asian glyphs,
                        // causing the common case (East Asian vertical text) to appear too 
                        // widely spaced. The problem with the typographic height is that it
                        // includes little or no extra space for diacritic marks. 
 
                        // Choice: use the Typographic height: It is best for FE, and the font
                        // can fix non East Asian diacritic cases if it wishes by providing a vmtx. 

                        cache.SetAdvanceHeight(i, (ushort)(typoAscent + typoDescent));
                        cache.SetTopSidebearing(i, unchecked((short)(typoAscent - yMax)));
                    } 

                    // bsb = ah - (tsb + ymax - ymin) 
                    short bsb = unchecked((short)(cache.GetAdvanceHeight(i) - ((int)cache.GetTopSidebearing(i) + glyphMetrics.height))); 
                    cache.SetBottomSidebearing(i, bsb);
 
                    short baseline = unchecked((short)-yMin);
                    cache.SetBaseline(i, baseline);
                }
            } 
        }
 
        ///  
        ///     Critical: This code writes into FontFaceLayoutInfo.
        ///     TreatAsSafe: It does this only using font data and not user defined parameters. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReadGlyfMetrics(
            FontFaceLayoutInfo cache, 
            ushort indexToLocFormat,
            bool vmtxPresent, 
            ushort typoAscent, 
            ushort typoDescent)
        { 
            CheckedPointer locaTable = GetTable(TrueTypeTags.IndexToLoc);
            CheckedPointer glyphTable = GetTable(TrueTypeTags.GlyphData);

            // For TrueType fonts we can get ideal glyph metrics from 'glyf' table. 
            // For CFF fonts we need to call into the CFF rasterizer.
            if (locaTable.IsNull || glyphTable.IsNull) 
            { 
                ReadCFFMetrics(cache, vmtxPresent, typoAscent, typoDescent);
                return; 
            }

            for (ushort i = 0; i < cache.GlyphCount; ++i)
            { 
                short xMin, xMax, yMin, yMax;
                try 
                { 
                    // extract glyph bounding box
                    int offset, nextGlyphOffset; 
                    if (indexToLocFormat == 0) // short format
                    {
                        offset = 2 * ReadOpenTypeUShort(locaTable + i * 2);
                        nextGlyphOffset = 2 * ReadOpenTypeUShort(locaTable + (i + 1) * 2); 
                    }
                    else // long format 
                    { 
                        offset = ReadOpenTypeLong(locaTable + i * 4);
                        nextGlyphOffset = ReadOpenTypeLong(locaTable + (i + 1) * 4); 
                    }

                    // per http://www.microsoft.com/typography/otspec/loca.htm
                    // a glyph without an outline has the same value for its offset 
                    // as the next glyph
                    if (nextGlyphOffset == offset) 
                    { 
                        xMin = xMax = yMin = yMax = 0;
                    } 
                    else
                    {
                        xMin = ReadOpenTypeShort(glyphTable + (offset + OFF_glyf_xMin));
                        xMax = ReadOpenTypeShort(glyphTable + (offset + OFF_glyf_xMax)); 
                        yMin = ReadOpenTypeShort(glyphTable + (offset + OFF_glyf_yMin));
                        yMax = ReadOpenTypeShort(glyphTable + (offset + OFF_glyf_yMax)); 
                    } 
                }
                catch (ArgumentOutOfRangeException) 
                {
                    // loca table entry points outside of glyf table
                    // fill metrics with zeroes
                    xMin = xMax = yMin = yMax = 0; 
                }
 
                // per http://www.microsoft.com/typography/otspec/hmtx.htm 
                // rsb = aw - (lsb + xmax - xmin)
                short rsb = (short)(cache.GetAdvanceWidth(i) - ((int)cache.GetLeftSidebearing(i) + xMax - xMin)); 
                cache.SetRightSidebearing(i, rsb);

                if (!vmtxPresent)
                { 
                    // There's no vmtx - fallback appropriately
 
                    // Win 9x uses the typographic height (typo ascender - typo descender), 
                    // but NT uses the cell height (cell ascender + cell descender).
 
                    // Which shall we use? The problem with the cell height is that in a
                    // multilingual font it may be much taller than the East Asian glyphs,
                    // causing the common case (East Asian vertical text) to appear too
                    // widely spaced. The problem with the typographic height is that it 
                    // includes little or no extra space for diacritic marks.
 
                    // Choice: use the Typographic height: It is best for FE, and the font 
                    // can fix non East Asian diacritic cases if it wishes by providing a vmtx.
 
                    cache.SetAdvanceHeight(i, (ushort)(typoAscent + typoDescent));
                    cache.SetTopSidebearing(i, unchecked((short)(typoAscent - yMax)));
                }
 
                // bsb = ah - (tsb + ymax - ymin)
                short bsb = unchecked((short)(cache.GetAdvanceHeight(i) - ((int)cache.GetTopSidebearing(i) + yMax - yMin))); 
                cache.SetBottomSidebearing(i, bsb); 

                short baseline = unchecked((short)-yMin); 
                cache.SetBaseline(i, baseline);
            }
        }
 
        // reads glyph advance widths and sidebearings from font tables
        // see http://www.microsoft.com/typography/otspec/hmtx.htm for details 
        private void ReadAdvances( 
            FontFaceLayoutInfo  cache,
            CheckedPointer      hmtxTable, 
            ushort              numberOfMetrics,
            ushort              indexToLocFormat,
            ushort              typoAscent,
            ushort              typoDescent 
            )
        { 
            cache.CreateAdvanceWidthsArray(); 

            // fill in advanceWidth and left sidebearing 
            ReadHmtx(cache, hmtxTable, numberOfMetrics);

            // fill in advanceHeight and top sidebearing
            bool vmtxPresent = ReadVmtx(cache); 

            // fill the rest from 'glyf' table in TrueType case 
            ReadGlyfMetrics(cache, indexToLocFormat, vmtxPresent, typoAscent, typoDescent); 
        }
 
        #endregion

        #region Fields
 
        // file-specific state
        private CheckedPointer          _fileStream; 
        private UnmanagedMemoryStream   _unmanagedMemoryStream; 
        private Uri                     _sourceUri;
        private int                     _numFaces; 
        private FontTechnology          _technology;

        // face-specific state
        private int _faceIndex; 
        private int _directoryOffset; // table directory offset for TTC, 0 for TTF
        private DirectoryEntry[] _tableDirectory; 
 
        #endregion
    } 
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK