FamilyCollection.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 / FontCache / FamilyCollection.cs / 1 / FamilyCollection.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Description: FamilyCollection font cache element class is responsible for 
// storing the mapping between a folder and font families in it.
// 
// History: 
//  07/23/2003 : [....] - Big rewrite to change cache structure.
//  03/04/2004 : [....] - Cache layout and interface changes for font enumeration. 
//  11/04/2005 : [....] - Refactoring to support font disambiguation.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.ComponentModel;
using System.Diagnostics; 
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security; 
using System.Security.Permissions;
using System.Windows; 
using System.Windows.Markup;    // for XmlLanguage 
using System.Windows.Media;
 
using MS.Win32;
using MS.Utility;
using MS.Internal;
using MS.Internal.FontFace; 
using MS.Internal.PresentationCore;
 
// 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.FontCache
{
    /// 
    /// FamilyCollection font cache element class is responsible for 
    /// storing the mapping between a folder and font families in it
    ///  
    [FriendAccessAllowed] 
    internal class FamilyCollection : IFontCacheElement
    { 
        //-----------------------------------------------------
        //
        //  Constructors
        // 
        //-----------------------------------------------------
 
        #region Constructors 

        ///  
        /// Creates a font family collection cache element from a canonical font family reference.
        /// 
        /// Absolute Uri of a folder
        /// Indicates folderUri is constructed internally by WPF code 
        /// and specifies the Windows Fonts folder or a file in the Windows Fonts folder.
        ///  
        /// Critical -  The ability to control the place fonts are loaded from is critical. 
        ///
        ///             The folderUri parameter is critical as it may contain privileged information 
        ///             (i.e., the location of Windows Fonts); it is passed to the FontSourceCollection
        ///             constructor which is declared critical and guarantees not to disclose the URI.
        ///
        ///             The isWindowsFonts parameter is critical for set as it is used to make a 
        ///             security decision (i.e., whether to assert read access); it is passed to the
        ///             FontSourceCollection constructor and also assigned to the _isWindowsFonts 
        ///             field which is declared critical. 
        ///
        ///             Callers should only specify isWindowsFonts=true if the URI comes from internal 
        ///             WPF code, NOT if it comes from the client. E.g., we want FontFamily="Arial" and
        ///             FontFamily="arial.ttf#Arial" to work in partial trust, therefore isWindowsFonts
        ///             is true so we assert read access. But FontFamily="file:///c:/windows/fonts/#Arial"
        ///             should NOT work in partial trust (even -- or especially -- if the URI is right), 
        ///             as this would enable partial trust clients to guess the location of Windows Fonts
        ///             through trial and error. 
        ///  
        [SecurityCritical]
        internal FamilyCollection(Uri folderUri, bool isWindowsFonts) 
        {
            _isWindowsFonts = isWindowsFonts;
            _fontFolder = new FontSourceCollection(folderUri, isWindowsFonts);
            _fontFolderUriString = _fontFolder.GetUriString(); 
        }
 
        ///  
        /// This contructor is for shared cache reconstructing element from a key.
        ///  
        /// Key containing folder name.
        /// 
        /// Critical -  Calls into the critical RetrieveKey method.
        ///  
        [SecurityCritical]
        internal FamilyCollection(CheckedPointer key) 
        { 
            RetrieveKey(key);
            Debug.Assert(_fontFolder != null); 
        }


 
        #endregion Constructors
 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //-----------------------------------------------------
 
        #region Internal methods
 
        ///  
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks
        ///     although functionally it simply does a string compare 
        ///     TreatAsSafe: This code is safe to call functionally it
        ///     is ok to expose since it only compares two pointers also Probe does the pointer checks
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        bool IFontCacheElement.Match(CheckedPointer p)
        { 
            unsafe 
            {
                Layout * l = (Layout *)p.Probe(0, sizeof(Layout)); 
                if (Util.StringSize(_fontFolderUriString) != l->folderUriSize)
                    return false;

                if (_fontFolder.TimeStamp != l->timeStamp) 
                    return false;
 
                if (_isWindowsFonts != l->isDefaultSystemFontsFolder) 
                    return false;
 
                return Util.StringEqualIgnoreCase(p + sizeof(Layout), _fontFolderUriString);
            }
        }
 
        /// 
        ///     Critical: This code reads from the critical member _fontFolderUriString. 
        ///     TreatAsSafe: This code is a part of the internal font cache code that sends miss reports 
        ///     to the service process. The critical value is not exposed via public managed APIs.
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private void StoreKeyInternal(CheckedPointer d, out int realSize)
        {
            d.WriteBool(_isWindowsFonts); 
            d += (Layout.OffsetToFolderUriSize - Layout.OffsetToKey);
            realSize = (Layout.OffsetToFolderUriSize - Layout.OffsetToKey) + 
                Util.StringAndLengthCopyToCheckedPointer(d, _fontFolderUriString); 
        }
 
        public void StoreKey(CheckedPointer d, out int realSize)
        {
            Debug.Assert(!_fontFolder.IsAppSpecific);
            StoreKeyInternal(d, out realSize); 
        }
 
        ///  
        ///     Critical:  Calls critical GetUriString method.
        ///  
        [SecurityCritical]
        public void RetrieveKey(CheckedPointer p)
        {
            _isWindowsFonts = p.ReadBool(); 
            p += (Layout.OffsetToFolderUriSize - Layout.OffsetToKey);
            string folderName = Util.StringAndLengthCopyFromCheckedPointer(p); 
            _fontFolder = new FontSourceCollection(new Uri(folderName, UriKind.Absolute), _isWindowsFonts); 
            _fontFolderUriString = _fontFolder.GetUriString();
        } 

        int IFontCacheElement.Type
        {
            get 
            {
                return 2; 
            } 
        }
 
        bool IFontCacheElement.IsAppSpecific
        {
            get
            { 
                return _fontFolder.IsAppSpecific;
            } 
        } 

        ///  
        ///     Critical: Calls into probe which is critical and also has unsafe code blocks
        ///     TreatAsSafe: This code is safe to call functionally it
        ///     is ok to expose since it only compares two pointers also Probe does the pointer checks
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        void IFontCacheElement.GetData(CheckedPointer block, ElementCacher cacher) 
        { 
            unsafe
            { 
                _data = (Layout *)block.Probe(0, sizeof(Layout));
            }
            _cacher = cacher;
        } 

        ///  
        ///     Critical: Accesses _fontFolderUriString critical field. 
        ///     TreatAsSafe: The field is used only to compute its size.
        ///  
        int IFontCacheElement.Size
        {
            [SecurityCritical, SecurityTreatAsSafe]
            get 
            {
                unsafe 
                { 
                    return sizeof(Layout) + Util.StringSize(_fontFolderUriString);
                } 
            }
        }

        int IFontCacheElement.GetHashCode() 
        {
            return HashFn.HashScramble(_fontFolder.GetHashCode()); 
        } 

        ///  
        ///        Critical:This code calls into unsafe code blocks.
        ///        TreatAsSafe: This code is ok to expose.Also the call to probe is boundary checked.
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        void IFontCacheElement.AddToCache(CheckedPointer newPointer, ElementCacher cacher)
        { 
            _cacher = cacher; 

            Invariant.Assert(!newPointer.IsNull); 

            unsafe
            {
                _data = (Layout*)newPointer.Probe(0, sizeof(Layout)); 
                Debug.Assert(_fontFolder.TimeStamp != 0);
                _data->timeStamp = _fontFolder.TimeStamp; 
 
                int keySize;
                StoreKeyInternal(newPointer + Layout.OffsetToKey, out keySize); 

                // Make sure folderUriSize doesn't get out of sync with OffsetToKey.
                // We use Debug.Assert instead of Invariant.Assert because
                // this condition will result in an immediate crash on debug builds. 
                Debug.Assert(_data->folderUriSize == Util.StringSize(_fontFolderUriString));
 
                // This list is needed to pass intermediate family collection from BuildFamilyList to AddFamiliesToCache. 
                // The keys are of type LocalizedName, the values are of type BaseFamily.
                SortedDictionary familyNameList; 

                // This dictionary contains mappings from strings to cache offsets that point to them.
                // The goal is to reduce cache size by having strings stored only once.
                // This forces us to save all used strings on the 1st pass (BuildFamilyList), 
                // so that we have all names in the cache by the time we add individual families there.
                // The strings come from 3 different places: 
                // 1. Family names (both composite and physical). 
                // 2. Physical face names.
                // 3. Composite FontFamilyMap tables. 
                // PERF: The values in the dictionary are of type 'int', we use 'object' to avoid
                // instantiating SortedList template with the value type int.
                // We can move from object to int if CLR pre-instantiates SortedList of object, int in its own DLLs.
                // We have to use sorted list instead of a regular dictionary because we want to be able to update values 
                // without reordering the list or creating new elements.
                SortedList frequentStrings; 
 
                // List of physical and composite family objects.
                List familyList; 

                BuildFamilyList(out familyList, out familyNameList, out frequentStrings);
                if (familyList == null)
                { 
                    _data->offsetToCachedFamiliesAndNames = _cacher.Alloc(
                        CachedFamiliesAndNames.GetTotalSize(0, 0)); 
 
                    CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
                    cachedFamiliesAndNames->numberOfFamilyNames = 0; 
                    cachedFamiliesAndNames->numberOfFontFamilies = 0;
                }
                else
                { 
                    FontDifferentiator.ResolveFaceConflicts(familyList, frequentStrings, this);
 
                    _data->offsetToCachedFamiliesAndNames = _cacher.Alloc( 
                        CachedFamiliesAndNames.GetTotalSize(familyList.Count, familyNameList.Count));
 
                    CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
                    cachedFamiliesAndNames->numberOfFamilyNames = familyNameList.Count;
                    cachedFamiliesAndNames->numberOfFontFamilies = familyList.Count;
 
                    Debug.Assert(cachedFamiliesAndNames->numberOfFamilyNames >= cachedFamiliesAndNames->numberOfFontFamilies);
 
                    AddFamiliesToCache( 
                        CachedFamiliesAndNames.GetCachedFamilyNames(cachedFamiliesAndNames),
                        CachedFamiliesAndNames.GetCachedFamilies(cachedFamiliesAndNames), 
                        familyList.Count,
                        familyNameList,
                        frequentStrings);
                } 
            }
        } 
 
        /// 
        ///  Critical: This function accesses critical ElementCacher object. 
        ///  TreatAsSafe: It uses the object only to obtain the obsolete flag.
        /// 
        internal bool IsObsolete
        { 
            [SecurityCritical, SecurityTreatAsSafe]
            get 
            { 
                return _cacher.IsObsolete();
            } 
        }

        /// 
        ///  Critical:This function has unsafe code blocks and returns cached font families. It also accesses cacher. 
        ///  TreatAsSafe: This information is ok to give out
        ///  
        [SecurityCritical,SecurityTreatAsSafe] 
        internal unsafe CachedFontFamily LookupFamily(
            string          familyName, 
            ref FontStyle   fontStyle,
            ref FontWeight  fontWeight,
            ref FontStretch fontStretch
            ) 
        {
            if (familyName == null || familyName.Length == 0) 
                return new CachedFontFamily(this, null, 0); 

            CachedName * cachedFamilyNames; 
            int familyNameCount;

            GetCachedFamilyNames(out cachedFamilyNames, out familyNameCount);
 
            int familyNameIndex = 0;
 
            int familyOffset = LookupLongestName( 
                familyName,
                ref familyNameIndex, 
                cachedFamilyNames,
                familyNameCount
            );
            if (familyOffset == Util.nullOffset) 
                return new CachedFontFamily(this, null, 0);
 
            // The function guarantees that familyNameIndex is valid before calling LookupLongestName, 
            // and LookupLongestName guarantees that familyNameIndex is either within the string or at the end of it
            // by tracking searchNameIndex and longestPartialMatch variables. 
            // This is a performance sensitive code path, and we perform redundant checks only in the debug version.
            Debug.Assert(0 <= familyNameIndex && familyNameIndex <= familyName.Length);

            CheckedPointer mapping = _cacher.Mapping; 
            CachedFamily* family = (CachedFamily*)mapping.Probe(familyOffset, sizeof(CachedFamily));
 
            if (familyNameIndex == familyName.Length) 
                return new CachedFontFamily(this, family, mapping.Size - familyOffset);
 
            // see if the input string contains style information
            // The face name must be separated from family name by a single space.
            // We set the same requirement as GDI.
            if (familyName[familyNameIndex] == ' ' && family->familyType == FamilyCollection.FamilyType.Physical) 
            {
                ++familyNameIndex; 
                CachedPhysicalFamily* cachedPhysicalFamily = (CachedPhysicalFamily*)family; 
                CachedPhysicalFace* cachedFace = LookupFace(
                    familyName, 
                    ref familyNameIndex,
                    cachedPhysicalFamily
                );
                // There should be no trailing characters after the style name. 
                if (cachedFace != null && familyNameIndex == familyName.Length)
                { 
                    fontStyle = cachedFace->style; 
                    fontWeight = cachedFace->weight;
                    fontStretch = cachedFace->stretch; 
                    return new CachedFontFamily(this, family, mapping.Size - familyOffset);
                }
            }
            return new CachedFontFamily(this, null, 0); 
        }
 
 
        /// 
        ///  Critical:This code accesses a pointer and passes it to a critical function 
        /// 
        [SecurityCritical]
        internal unsafe string GetFamilyName(CachedFamily * cachedFamily)
        { 
            return new LocalizedNameDictionary(this, cachedFamily).OrdinaryName;
        } 
 
        private unsafe struct FamilyEnumerator : IEnumerator, IEnumerable
        { 
            private int                 _familyCount;
            /// 
            ///     Critical: This code holds reference to a pointer
            ///  
            [SecurityCritical]
            private unsafe int *        _familyPointers; 
            private FamilyCollection    _familyCollection; 
            private int                 _currentFamily;
 
            /// 
            ///     Critical: This code stores reference to a pointer and accesses _Data which is critical
            ///     TreatAsSafe: The pointer is not exposed and the member that holds the pointer is critical
            ///  
            [SecurityCritical,SecurityTreatAsSafe]
            internal FamilyEnumerator(FamilyCollection familyCollection) 
            { 
                _familyCollection = familyCollection;
                _currentFamily = -1; 

                unsafe
                {
                    familyCollection.GetCachedFamilies(out _familyPointers, out _familyCount); 
                }
            } 
 
            #region IEnumerator Members
 
            public bool MoveNext()
            {
                ++_currentFamily;
                if (_currentFamily >= _familyCount) 
                {
                    // prevent cycling 
                    _currentFamily = _familyCount; 
                    return false;
                } 
                return true;
            }

            ///  
            ///    Critical: This function uses pointers and calls critical CachedFontFamily constructor
            ///    TreatAsSafe: This information is ok to expose 
            ///  
            CachedFontFamily IEnumerator.Current
            { 
                [SecurityCritical,SecurityTreatAsSafe]
                get
                {
                    // Disable "Property get methods should not throw exceptions" because IEnumerator<>.Current semantics 
                    // are to throw an exception if enumerator is positioned before the first or after the last element.
#pragma warning disable 6503 
                    if (_currentFamily < 0 || _currentFamily >= _familyCount) 
                    {
                        throw new InvalidOperationException(); 
                    }

                    CheckedPointer familyPointer = _familyCollection._cacher.Mapping + _familyPointers[_currentFamily];
 
                    unsafe
                    { 
                        return new CachedFontFamily( 
                            _familyCollection,
                            (CachedFamily*)familyPointer.Probe(0, sizeof(CachedFamily)), 
                            familyPointer.Size
                            );
                    }
#pragma warning restore 6503 
                }
            } 
 
            #endregion
 
            #region IEnumerator Members

            object IEnumerator.Current
            { 
                get
                { 
                    return ((IEnumerator)this).Current; 
                }
            } 

            public void Reset()
            {
                _currentFamily = -1; 
            }
 
            #endregion 

 
            #region IDisposable Members

            public void Dispose() {}
 
            #endregion
 
            #region IEnumerable Members 

            IEnumerator IEnumerable.GetEnumerator() 
            {
                return this as IEnumerator;
            }
 
            #endregion
 
            #region IEnumerable Members 

            IEnumerator IEnumerable.GetEnumerator() 
            {
                return ((IEnumerable)this).GetEnumerator();
            }
 
            #endregion
        } 
 
        internal IEnumerable GetFontFamilies()
        { 
            return new FamilyEnumerator(this);
        }

        ///  
        ///  Critical: This function has an unsafe code block.
        ///  SecurityTreatAsSafe: This function only returns the number of font families in a folder. 
        ///  
        internal int FamilyCount
        { 
            [SecurityCritical, SecurityTreatAsSafe]
            get
            {
                unsafe 
                {
                    return GetCachedFamiliesAndNames()->numberOfFontFamilies; 
                } 
            }
        } 

        /// 
        ///  Critical:This function has unsafe code blocks and returns cached font face. It also accesses cacher.
        ///              It returns a pointer 
        /// 
        [SecurityCritical] 
        private unsafe CachedPhysicalFace * LookupFace( 
            string styleName,
            ref int styleNameIndex, 
            CachedPhysicalFamily * cachedPhysicalFamily
        )
        {
            Debug.Assert(cachedPhysicalFamily->familyType == FamilyType.Physical); 
            int faceOffset = LookupLongestName(
                styleName, 
                ref styleNameIndex, 
                (CachedName *)((byte *)cachedPhysicalFamily + sizeof(CachedPhysicalFamily)),
                cachedPhysicalFamily->numberOfFaceNames 
            );
            if (faceOffset == Util.nullOffset)
                return null;
            CachedPhysicalFace * face = (CachedPhysicalFace *)_cacher.Mapping.Probe(faceOffset,sizeof(CachedPhysicalFace)); 
            return face;
        } 
 
        /// 
        ///    Critical: This accesses a pointer and is unsafe because it returns a URI 
        /// 
        [SecurityCritical]
        internal unsafe Uri GetFontUri(CachedPhysicalFace * cachedFace)
        { 
            string fontUri = Util.StringCopyFromUncheckedPointer(
                (byte*)cachedFace + sizeof(CachedPhysicalFace), 
                cachedFace->fontUriSize 
            );
            return Util.CombineUriWithFaceIndex(fontUri, cachedFace->faceIndex); 
        }

        /// 
        ///  Critical:This function has unsafe code blocks and returns cached font face. It also accesses cacher. 
        ///  TreatAsSafe: This infomation is ok to give out.
        ///  CachedFontFamily cannot be created with Null family. Which ensures validity of the pointer. 
        ///  Taking that into account there is also the risk of dereferencing from elementcacher which is mitigated 
        ///  by bounds checking in element cacher
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        internal unsafe CachedFontFace GetCachedFace(CachedFontFamily cachedFamily, int faceIndex)
        {
            Invariant.Assert(faceIndex < cachedFamily.NumberOfFaces); 
            if (cachedFamily.IsPhysical)
            { 
                CachedPhysicalFamily * cachedPhysicalFamily = cachedFamily.PhysicalFamily; 
                int * facePointers = (int *)((byte *)cachedPhysicalFamily +
                    sizeof(CachedPhysicalFamily) + 
                    cachedPhysicalFamily->numberOfFaceNames * sizeof(CachedName)
                );

                CheckedPointer facePointer = _cacher.Mapping + facePointers[faceIndex]; 

                return new CachedFontFace( 
                    this, 
                    (CachedFace*)facePointer.Probe(0, sizeof(CachedFace)),
                    facePointer.Size 
                    );
            }
            Debug.Assert(cachedFamily.IsComposite);
            CachedCompositeFamily * cachedCompositeFamily = cachedFamily.CompositeFamily; 

            int faceOffset = cachedCompositeFamily->OffsetToFamilyTypeface + (faceIndex * sizeof(CachedCompositeFace)); 
            CheckedPointer cachedFace = cachedFamily.CheckedPointer + faceOffset; 

            return new CachedFontFace( 
                this,
                (CachedFace*)cachedFace.Probe(0, sizeof(CachedFace)),
                cachedFace.Size
                ); 
        }
 
        ///  
        ///  Critical:This function has unsafe code blocks . It also accesses cacher and returns a pointer.
        ///  Functionally it looks up the CachedFamilyMap and retrieves the ranges that match the criteria. 
        ///  The risk here is of being called with bogus pointers since there is minimal pointer checking while dereferencing.
        ///  The risk is substantially mitigated by the bounds checking employed in ElementCacher for the return value.
        /// 
        [SecurityCritical] 
        internal unsafe ushort* GetFamilyMapRanges(CachedCompositeFamily* cachedFamily, CultureInfo culture, out int lengthOfRanges)
        { 
            CachedFamilyMapRangeList* rangeList = (CachedFamilyMapRangeList*)((byte*)cachedFamily + cachedFamily->OffsetToFamilyMapRangeLists); 
            CachedFamilyMapRangeList* defaultList = rangeList + (cachedFamily->numberOfFamilyMapRangeLists - 1);
 
            // Look for the first range list with a matching culture, according to the usual matching rules.
            // The lists are sorted such that the most specific cultures come first.
            for (; rangeList != defaultList; ++rangeList)
            { 
                XmlLanguage familyMapLanguage = GetFamilyMapLanguageFromOffset(rangeList->offsetToIetfLanguageTag);
                if (familyMapLanguage.RangeIncludes(culture)) 
                    break; 
            }
 
            // We either found a matching culture or rangeList == defaultList.
            lengthOfRanges = rangeList->lengthOfRanges;
            return (ushort*)((byte*)rangeList + rangeList->offsetToRanges);
        } 

        ///  
        ///  Critical:This function has unsafe code blocks and returns a pointer 
        ///  Functionally it returns that family map for a character. The risk is
        ///  in dereferencing the ranges and CachedFamilyPointers since they could be 
        ///  bogus. The calls lower down to InRange are inherently safe.
        /// 
        [SecurityCritical]
        internal unsafe CachedFamilyMap* GetFamilyMapOfChar( 
            CachedCompositeFamily* cachedFamily,
            ushort* ranges, 
            int lengthOfRanges, 
            int ch)
        { 
            CachedFamilyMap* familyMaps = (CachedFamilyMap*)((byte*)cachedFamily + cachedFamily->OffsetToFamilyMaps);
            for (int i = 0; i < lengthOfRanges; i += 2)
            {
                int begin = ranges[i]; 
                int end = ranges[i + 1];
                for (int j = begin; j < end; ++j) 
                { 
                    if (InRange(familyMaps + j, ch))
                        return familyMaps + j; 
                }
            }
            return null;
        } 

        ///  
        ///  Critical:This code accesses a pointer and passes it to a critical function 
        /// 
        [SecurityCritical] 
        internal unsafe IDictionary GetLocalizedNameDictionary(CachedFamily* cachedFamily)
        {
            return new LocalizedNameDictionary(this, cachedFamily);
        } 

        #endregion Internal methods 
 
        //------------------------------------------------------
        // 
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        ///  
        /// Critical - as this calls FontSource.GetStream which can read files in Windows
        ///            fonts directory under an elevation. 
        /// Safe - as this just adds the data to the font cache.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void AddCompositeFamilyToList(FontSource fontSource, List familyList, SortedDictionary familyNameList, SortedList frequentStrings) 
        {
            CompositeFontInfo fontInfo; 
            using (Stream fileStream = fontSource.GetStream()) 
            {
                fontInfo = CompositeFontParser.LoadXml(fileStream); 
            }

            foreach (KeyValuePair familyEntry in fontInfo.FamilyNames)
            { 
                LocalizedName familyName = new LocalizedName(familyEntry.Key, familyEntry.Value);
 
                // If there is already a font with the same familyName, ignore the subsequent ones. 
                if (familyNameList.ContainsKey(familyName))
                    return; 
            }

            CompositeFamily compositeFamily = new CompositeFamily();
            familyList.Add(compositeFamily); 
            compositeFamily.compositeFontInfo = fontInfo;
 
            // add family names to the list 
            foreach (KeyValuePair familyEntry in fontInfo.FamilyNames)
            { 
                LocalizedName familyName = new LocalizedName(familyEntry.Key, familyEntry.Value);
                familyNameList.Add(familyName, compositeFamily);

                SaveLocalizedString(familyName, frequentStrings); 
                compositeFamily.familyNames.Add(familyName, null);
            } 
 
            // add cmap table cultures to the culture list; FamilyMapLanguages may be
            // null if all the family maps are culture-independent 
            if (fontInfo.FamilyMapLanguages != null)
            {
                foreach (XmlLanguage familyMapLanguage in fontInfo.FamilyMapLanguages)
                { 
                    SaveRegularString(familyMapLanguage.IetfLanguageTag, frequentStrings);
                } 
            } 
        }
 
        internal void SaveLocalizedString(LocalizedName name, SortedList frequentStrings)
        {
            SaveRegularString(name.Language.IetfLanguageTag, frequentStrings);
            SaveRegularString(name.Name, frequentStrings); 
        }
 
        private void SaveRegularString(string name, SortedList frequentStrings) 
        {
            frequentStrings[name] = Util.nullOffset; 
        }

        /// 
        ///     Critical - as this obtains UnmanagedMemoryStream from FontSource which is critical data. 
        ///     Safe - as this just adds the data to the font cache.
        ///  
        [SecurityCritical,SecurityTreatAsSafe] 
        private void AddPhysicalFamilyToList(FontSource fontSource, List familyList, SortedDictionary familyNameList, SortedList frequentStrings)
        { 
            // get all the faces out of this file and add them to the list
            TrueTypeFontDriver.BasicFontFaceInfo basicFontFaceInfo = new TrueTypeFontDriver.BasicFontFaceInfo();

            UnmanagedMemoryStream pinnedFontSource = fontSource.GetUnmanagedStream(); 
            try
            { 
                TrueTypeFontDriver ttd = new TrueTypeFontDriver(pinnedFontSource, fontSource.Uri); 

                for (int currentFace = 0; currentFace < ttd.NumFaces; ++currentFace) 
                {
                    ttd.SetFace(currentFace);

                    // figure out font family and style 
                    bool skipFontDifferentiation;
                    ttd.GetBasicFontFaceInfo(ref basicFontFaceInfo, out skipFontDifferentiation); 
 
                    // Name and style adjustment.
                    if (!skipFontDifferentiation) 
                    {
                        FontDifferentiator.AdjustFamilyAndFaceInformation(
                            ref basicFontFaceInfo.nameTable,
                            ref basicFontFaceInfo.style, 
                            ref basicFontFaceInfo.weight,
                            ref basicFontFaceInfo.stretch 
                            ); 
                    }
 
                    LocalizedName[] familyNames = basicFontFaceInfo.nameTable.familyNames;
                    LocalizedName[] faceNames = basicFontFaceInfo.nameTable.faceNames;

                    if (familyNames == null) 
                    {
                        // If a font doesn't have preferred family names, 
                        // per OpenType spec we have to use legacy family names. 

                        familyNames = basicFontFaceInfo.nameTable.win32FamilyNames; 
                        if (familyNames == null)
                        {
                            // A valid font should contain at least one family name.
                            return; 
                        }
                    } 
 
                    if (faceNames == null)
                    { 
                        // If a font doesn't have preferred face names,
                        // per OpenType spec we have to use legacy face names.

                        faceNames = basicFontFaceInfo.nameTable.win32faceNames; 
                        if (faceNames == null)
                        { 
                            // A valid font should contain at least one face name. 
                            return;
                        } 
                    }

                    // See if other faces from this font family were already present.
 
                    // If there was a physical face from this family, PhysicalFamily object
                    // should have already been created, and we can use it instead of creating a new one. 
                    PhysicalFamily family = null; 

                    { 
                        // If there were any composite fonts using the same family name,
                        // remove such composite font families from the cache altogether,
                        // as we prefer physical to composite font families in case both are present.
                        List compositeFamiliesToRemove = null; 

                        foreach (LocalizedName familyName in familyNames) 
                        { 
                            BaseFamily baseFamily;
                            if (familyNameList.TryGetValue(familyName, out baseFamily)) 
                            {
                                if (baseFamily is PhysicalFamily)
                                {
                                    if (family == null) 
                                        family = (PhysicalFamily)baseFamily;
                                } 
                                else 
                                {
                                    Debug.Assert(baseFamily is CompositeFamily); 
                                    CompositeFamily compositeFamily = (CompositeFamily)baseFamily;
                                    if (compositeFamiliesToRemove == null)
                                        compositeFamiliesToRemove = new List();
                                    else 
                                    {
                                        // Make sure we count each composite family only once. 
                                        foreach (CompositeFamily olderFamily in compositeFamiliesToRemove) 
                                        {
                                            if (Object.ReferenceEquals(compositeFamily, olderFamily)) 
                                                continue;
                                        }
                                    }
                                    compositeFamiliesToRemove.Add(compositeFamily); 
                                }
                            } 
                        } 

                        // Remove composite families that had the same family name. 
                        if (compositeFamiliesToRemove != null)
                        {
                            foreach (CompositeFamily compositeFamily in compositeFamiliesToRemove)
                            { 
                                foreach (LocalizedName compositeFamilyName in compositeFamily.familyNames.Keys)
                                { 
                                    familyNameList.Remove(compositeFamilyName); 
                                }
                                bool removed = familyList.Remove(compositeFamily); 
                                Debug.Assert(removed);
                            }
                        }
                    } 

                    // this is the first time we encountered this family 
                    // create a new object to hold it 
                    if (family == null)
                    { 
                        family = new PhysicalFamily();
                        familyList.Add(family);
                    }
 
                    // add new family names (if any) to the list
                    foreach (LocalizedName familyName in familyNames) 
                    { 
                        familyNameList[familyName] = family;
                        SaveLocalizedString(familyName, frequentStrings); 
                        family.familyNames[familyName] = null;
                    }

                    // now, add a new face to the family 
                    PhysicalFace face = new PhysicalFace();
 
                    face.fontUri = fontSource.GetUriString(); 
                    face.faceIndex = basicFontFaceInfo.faceIndex;
 
                    // The original face is not simulated, we'll add simulated faces if necessary after analyzing all faces in this family.
                    face.styleSimulations = StyleSimulations.None;

                    face.style = basicFontFaceInfo.style; 
                    face.weight = basicFontFaceInfo.weight;
                    face.stretch = basicFontFaceInfo.stretch; 
 
                    face.version = basicFontFaceInfo.nameTable.version;
                    face.timestamp = fontSource.GetLastWriteTimeUtc(); 

                    face.symbol = basicFontFaceInfo.symbol;

                    face.designEmHeight = basicFontFaceInfo.designEmHeight; 
                    face.designCellAscent = basicFontFaceInfo.designCellAscent;
                    face.designCellDescent = basicFontFaceInfo.designCellDescent; 
                    face.designLineSpacing = basicFontFaceInfo.designLineSpacing; 

                    face.names = faceNames; 

                    family.typefaces.Add(face);
                }
            } 
            catch (SEHException e)
            { 
                throw Util.ConvertInPageException(fontSource, e); 
            }
            finally 
            {
                pinnedFontSource.Close();
            }
        } 

        ///  
        /// Critical - as this call GetFiles which can read files in Windows 
        ///            fonts directory under an elevation.
        /// Safe - as this doesn't expose this information.  This only works with 
        ///        font files in the windows fonts directory and that information
        ///        is considered safe to expose.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void BuildFamilyList(
            out List familyList, 
            out SortedDictionary familyNameList, 
            out SortedList frequentStrings)
        { 
            familyNameList = null;
            frequentStrings = null;
            familyList = null;
 
            foreach (FontSource fontSource in _fontFolder)
            { 
                // verify that we're dealing with a supported font extension 
                string extension = Util.GetUriExtension(fontSource.Uri);
                bool isComposite; 
                if (!Util.IsSupportedFontExtension(extension, out isComposite))
                    continue;

                // PERF: We create these data structures on demand because application specific font collection is often empty. 
                if (familyNameList == null)
                { 
                    familyNameList = new SortedDictionary(LocalizedName.NameComparer); 
                    frequentStrings = new SortedList(StringComparer.OrdinalIgnoreCase);
                    familyList = new List(); 
                }

                // Parse the file and add data from it to the family list.
                // Note that for both physical and composite fonts 
                // we eat certain exceptions in order to ignore malformed fonts.
                // Disable corresponding Presharp warning. 
#pragma warning disable 6502 

                try 
                {
                    if (isComposite)
                    {
                        AddCompositeFamilyToList(fontSource, familyList, familyNameList, frequentStrings); 
                    }
                    else 
                    { 
                        AddPhysicalFamilyToList(fontSource, familyList, familyNameList, frequentStrings);
                    } 
                }
                // Skip malformed fonts for both local and shared cases.
                catch (FileFormatException)
                { 
                }
                // Skip inaccessible fonts for local cache, re-throw for shared cache to make sure incomplete information 
                // is not shared to the clients. 
                catch (IOException)
                { 
                    if (_cacher.IsShared)
                        throw;
                }
                catch (UnauthorizedAccessException) 
                {
                    if (_cacher.IsShared) 
                        throw; 
                }
                catch (System.Net.WebException) 
                {
                    if (_cacher.IsShared)
                        throw;
                } 
#pragma warning restore 6502
            } 
        } 

        ///  
        ///  Critical:This function has unsafe code blocks ,it accesses element cacher and returns
        ///           the next character in cache
        /// 
        [SecurityCritical] 
        private unsafe char GetCachedChar(
            CachedName* cachedName, 
            int searchNameIndex) 
        {
            byte* name = _cacher[cachedName->offsetToName]; 
            int nameLength = *(int*)name;
            if (nameLength <= searchNameIndex * sizeof(char))
                return '\0';
            char c = *((char*)(name + sizeof(int)) + searchNameIndex); 
            return Char.ToUpperInvariant(c);
        } 
        ///  
        ///  Critical:This function has unsafe code blocks ,it accesses element cacher and returns
        ///           the next character in cache 
        /// 
        [SecurityCritical]
        private unsafe bool LookupNextCharacter(
            string searchName, 
            int initialSearchNameIndex,
            int searchNameIndex, 
            CachedName* cachedNames, 
            ref int startIndex,
            ref int endIndex 
            )
        {
            char s = Char.ToUpperInvariant(searchName[searchNameIndex]);
            for (; ; ) 
            {
                // we only have 1 or zero matches left 
                if (startIndex + 1 >= endIndex) 
                    return false;
 
                int median = (startIndex + endIndex) / 2;
                char d = GetCachedChar(cachedNames + median, searchNameIndex - initialSearchNameIndex);
                int res = s - d;
 
                if (res < 0)
                { 
                    endIndex = median; 
                }
                else if (res > 0) 
                {
                    startIndex = median + 1;
                }
                else 
                {
                    // we found a match, let's see if there are other matches 
                    int i; 

                    // move up to find a first non-match 
                    i = median;
                    while (--i >= startIndex)
                    {
                        char c = GetCachedChar(cachedNames + i, searchNameIndex - initialSearchNameIndex); 
                        Debug.Assert(c <= s);
                        if (c < s) 
                            break; 
                    }
                    startIndex = i + 1; 

                    // move down to find a first non-match
                    i = median;
                    while (++i < endIndex) 
                    {
                        char c = GetCachedChar(cachedNames + i, searchNameIndex - initialSearchNameIndex); 
                        Debug.Assert(c >= s); 
                        if (c > s)
                            break; 
                    }
                    endIndex = i;
                    return true;
                } 
            }
        } 
 
        /// 
        /// This function performs incremental binary search until we reach no more than one match. 
        /// 
        /// 
        /// The index of the current character in the search string.
        ///  
        /// 
        /// Offset to the data found. 
        ///  
        ///  Critical:This function has unsafe code blocks ,it accesses element cacher
        ///  also it takes a pointer that it passes on without validating 
        /// 
        [SecurityCritical]
        private unsafe int LookupLongestName(
            string searchName, 
            ref int searchNameIndex,
            CachedName* cachedNames, 
            int numberOfCachedNames 
            )
        { 
            Invariant.Assert(searchNameIndex >= 0);

            // startIndex is inclusive
            int startIndex = 0; 

            // endIndex is exclusive 
            int endIndex = numberOfCachedNames; 

            // remember the initial value of searchNameIndex 
            int initialSearchNameIndex = searchNameIndex;

            int longestPartialMatchIndex = searchNameIndex;
            int longestPartialMatchOffset = Util.nullOffset; 

            for (; ; ) 
            { 
                // we ran out of characters in the search string
                if (searchNameIndex == searchName.Length) 
                    break;

                if (!LookupNextCharacter(
                    searchName, 
                    initialSearchNameIndex,
                    searchNameIndex, 
                    cachedNames, 
                    ref startIndex,
                    ref endIndex 
                    ))
                    break;

                ++searchNameIndex; 

                // see if the first match is null terminated 
                // in this case we already have a partial match 
                char nextCharacter = GetCachedChar(cachedNames + startIndex, searchNameIndex - initialSearchNameIndex);
                if (nextCharacter == '\0') 
                {
                    longestPartialMatchIndex = searchNameIndex;
                    longestPartialMatchOffset = cachedNames[startIndex].data;
                } 
            }
 
            Debug.Assert(startIndex <= endIndex); 
            if (endIndex == startIndex)
            { 
                searchNameIndex = longestPartialMatchIndex;
                return longestPartialMatchOffset;
            }
 
            // only the item with startIndex can be exact match
            // to prove it, there are 2 cases here: 
            // a) searchNameIndex == searchName.Length 
            // in this case all items are either of the same length as search string
            // or longer. The item of the same length will be first in the list. 
            // b) searchNameIndex < searchName.Length
            // in this case no more than 1 match is left
            // Note that in both cases we must compare strings
            // starting with searchNameIndex 

            for (; ; ) 
            { 
                char d = GetCachedChar(cachedNames + startIndex, searchNameIndex - initialSearchNameIndex);
 
                // see if we reached the end of the input string
                if (searchNameIndex == searchName.Length)
                {
                    // here we have an exact match only if we reached the end of the cached string as well 
                    if (d == '\0')
                        return cachedNames[startIndex].data; 
 
                    // Otherwise, we have no match.
                    searchNameIndex = longestPartialMatchIndex; 
                    return longestPartialMatchOffset;
                }

                char s = Char.ToUpperInvariant(searchName[searchNameIndex]); 
                if (s != d)
                { 
                    // See if we reached the end of the cached string (partial match). 
                    if (d == '\0')
                        return cachedNames[startIndex].data; 

                    // Otherwise, we have no match.
                    searchNameIndex = longestPartialMatchIndex;
                    return longestPartialMatchOffset; 
                }
 
                ++searchNameIndex; 
            }
        } 

        /// 
        ///     Critical:This code calls into Alloc and also yields unverifiable code. It also acceses
        ///     unsafe code to operate on pointers. 
        ///     TreatAsSafe: Adding a physical family to cache is a safe operation.Also it uses primitives that
        ///     are bounds checked. 
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private unsafe int AddPhysicalFamilyToCache(PhysicalFamily physicalFamily, SortedList frequentStrings) 
        {
            int familyCacheOffset = _cacher.Alloc(
                checked(
                    sizeof(CachedPhysicalFamily) + 
                    physicalFamily.faceNames.Count * sizeof(CachedName) +
                    physicalFamily.typefaces.Count * sizeof(int) 
                ) 
            );
            CachedPhysicalFamily* cachedFamily = (CachedPhysicalFamily*)_cacher.Mapping.Probe(familyCacheOffset, sizeof(CachedPhysicalFamily)); 
            cachedFamily->familyType = FamilyType.Physical;
            CachedName* cachedFaceNames = (CachedName*)((byte*)cachedFamily + sizeof(CachedPhysicalFamily));
            int* facePointers = (int*)(cachedFaceNames + physicalFamily.faceNames.Count);
            cachedFamily->numberOfFaceNames = physicalFamily.faceNames.Count; 
            cachedFamily->numberOfTypefaces = 0;
 
            bool symbol = false; 

            // the following 4 variables set up the state for regular face matching 
            PhysicalFace bestFace = null;

            MatchingStyle regular = new MatchingStyle(FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
            MatchingStyle bestMatch = new MatchingStyle(); 

 
            // put all the faces to the cache 
            int j = 0;
            foreach (KeyValuePair pair in physicalFamily.faceNames) 
            {
                CachedName * cachedFaceName = cachedFaceNames + j;
                LocalizedName faceName = pair.Key;
                PhysicalFace face = pair.Value; 

                if (face.cacheOffset == Util.nullOffset) 
                { 
                    int fontUriSize = Util.StringSize(face.fontUri);
                    face.cacheOffset = _cacher.Alloc(checked(sizeof(CachedPhysicalFace) + fontUriSize)); 
                    CheckedPointer cachedFacePointer = _cacher.GetCheckedPointer(face.cacheOffset);
                    CachedPhysicalFace* cachedFace = (CachedPhysicalFace*)cachedFacePointer.Probe(0, sizeof(CachedPhysicalFace));

                    cachedFace->fontUriSize = fontUriSize; 

                    cachedFace->faceIndex = face.faceIndex; 
 
                    cachedFace->style = face.style;
                    cachedFace->weight = face.weight; 
                    cachedFace->stretch = face.stretch;
                    cachedFace->styleSimulations = face.styleSimulations;

                    Util.StringCopyToCheckedPointer(cachedFacePointer + sizeof(CachedPhysicalFace), face.fontUri); 

                    // if any of faces is non-Unicode, the whole family is treated as non-Unicode 
                    if (face.symbol) 
                        symbol = true;
 
                    // add the face to the face list
                    facePointers[cachedFamily->numberOfTypefaces] = face.cacheOffset;
                    ++cachedFamily->numberOfTypefaces;
 
                    // see if the face is a better match for regular face
                    MatchingStyle current = new MatchingStyle(face.style, face.weight, face.stretch); 
 
                    if (MatchingStyle.IsBetterMatch(
                                regular, 
                                bestMatch,
                                ref current
                                )
                        || bestFace == null) 
                    {
                        bestFace = face; 
                        bestMatch = current; 
                    }
                } 

                // now that we ensured the face is constructed, fill out CachedName
                cachedFaceName->offsetToIetfLanguageTag = GetOffsetToIetfLanguageTag(faceName.Language, frequentStrings);
                cachedFaceName->offsetToName = GetOffsetToString(faceName.Name, frequentStrings); 
                cachedFaceName->data = face.cacheOffset;
                ++j; 
            } 

            cachedFamily->symbol = symbol; 

            // obtain baseline and height from regular face
            Debug.Assert(bestFace != null);
 
            Debug.Assert(cachedFamily->numberOfTypefaces <= physicalFamily.typefaces.Count);
 
            double emsPerDesignUnit = 1.0 / bestFace.designEmHeight; 

            // The ascent is from the top of the cell. Per [....], we want baseline to be relative to the 
            // top of a logical line (represented by lineSpacing) in which the cell is vertically centered.
            // Thus we want half the external leading to be above the cell. The external leading is equal
            // to (lineSpacing - (ascent + descent)), giving us the following formula:
            // 
            // baseline = ascent + (lineSpacing - (ascent + descent)) * 0.5
            //          = ascent + lineSpacing * 0.5 - ascent * 0.5 - descent * 0.5 
            //          = ascent * 0.5 + lineSpacing * 0.5 - descent * 0.5 
            //          = (ascent + lineSpacing - descent) * 0.5
            // 
            cachedFamily->baseline =
                (bestFace.designCellAscent + bestFace.designLineSpacing - bestFace.designCellDescent)
                * 0.5 * emsPerDesignUnit;
 
            cachedFamily->lineSpacing = bestFace.designLineSpacing * emsPerDesignUnit;
 
            AddFamilyNames((CachedFamily*)cachedFamily, physicalFamily, frequentStrings); 

            return familyCacheOffset; 
        }


        ///  
        ///     Critical:This code calls into Alloc and also yields unverifiable code
        ///     TreatAsSafe: Adding a composite family to cache is a safe operation.Also it uses primitives that 
        ///     are bounds checked. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private unsafe int AddCompositeFamilyToCache(CompositeFamily compositeFamily, SortedList frequentStrings)
        {
            int familyCacheOffset;
            CachedCompositeFamily* cachedFamily = new CompositeFamilyCacher().Allocate( 
                compositeFamily.compositeFontInfo,
                this, 
                frequentStrings, 
                out familyCacheOffset
                ); 

            AddFamilyNames((CachedFamily*)cachedFamily, compositeFamily, frequentStrings);

            return familyCacheOffset; 
        }
 
        ///  
        /// Comparer for sorting XmlLanguages in decreasing order of specificity.
        ///  
        private class LanguageComparer : IComparer
        {
            internal static readonly LanguageComparer Comparer = new LanguageComparer();
 
            public int Compare(object x, object y)
            { 
                int dx = ((XmlLanguage) x).GetSpecificity(); 
                int dy = ((XmlLanguage) y).GetSpecificity();
 
                return
                    (dx > dy) ? -1 :    // dx is more specific -> sort before
                    (dx < dy) ?  1 :    // dx is less specific -> sort after
                    string.CompareOrdinal(x.ToString(), y.ToString());  // no need for case-insensitivity; only requirement is stability 
            }
 
        } 

        ///  
        /// Add a list of family names sorted by culture to a family.
        /// 
        /// Cached representation of a font family.
        /// Temporary in-memory representation of a font family. 
        /// String cache structure to optimize cache space.
        ///  
        ///     Critical:This code calls dereferences pointers without any validation. 
        /// 
        [SecurityCritical] 
        private unsafe void AddFamilyNames(CachedFamily* cachedFamily, BaseFamily family, SortedList frequentStrings)
        {
            cachedFamily->numberOfFamilyNames = family.familyNames.Count;
            Invariant.Assert(cachedFamily->numberOfFamilyNames >= 1); 

            cachedFamily->offsetToFamilyNames = _cacher.Alloc(2 * sizeof(int) * cachedFamily->numberOfFamilyNames); 
            int* familyNamePointers = (int*)_cacher[cachedFamily->offsetToFamilyNames]; 

            foreach (LocalizedName familyName in family.familyNames.Keys) 
            {
                familyNamePointers[0] = (int)frequentStrings[familyName.Language.IetfLanguageTag];
                familyNamePointers[1] = (int)frequentStrings[familyName.Name];
 
                Invariant.Assert(familyNamePointers[0] != Util.nullOffset && familyNamePointers[0] != 0);
                Invariant.Assert(familyNamePointers[1] != Util.nullOffset && familyNamePointers[1] != 0); 
                familyNamePointers += 2; 
            }
 
            Invariant.Assert(familyNamePointers - (int*)_cacher[cachedFamily->offsetToFamilyNames] == 2 * cachedFamily->numberOfFamilyNames);
        }

 
        /// 
        /// Fills in family list and family name list, constructing cache family object as needed. 
        ///  
        /// Family name list.
        /// Family list. 
        /// Number of elements in the family list.
        /// Mapping from family names to family objects.
        /// String cache structure to optimize cache space.
        ///  
        ///     Critical:This code calls into AddPhysicalFamilyToCache and also yields unverifiable code
        ///  
        [SecurityCritical] 
        private unsafe void AddFamiliesToCache(
            CachedName* cachedNames, 
            int* cachedFamilies,
            int familyCount,
            SortedDictionary familyNameList,
            SortedList frequentStrings) 
        {
            int i; 
 
            // Store frequently used strings only once, and use offsets to refer to them.
            for (i = 0; i < frequentStrings.Count; ++i) 
            {
                string name = frequentStrings.Keys[i];
                frequentStrings[name] = StoreString(name);
            } 

            int numberOfConstructedFamilies = 0; 
            i = 0; 
            foreach (KeyValuePair pair in familyNameList)
            { 
                CachedName * cachedFamilyName = cachedNames + i;
                LocalizedName familyName = pair.Key;
                BaseFamily family = pair.Value;
 
                try
                { 
                    if (family.cacheOffset == Util.nullOffset) 
                    {
                        // create family structure in case it doesn't exist 
                        PhysicalFamily physicalFamily = family as PhysicalFamily;
                        if (physicalFamily != null)
                        {
                            family.cacheOffset = AddPhysicalFamilyToCache(physicalFamily, frequentStrings); 
                        }
                        else 
                        { 
                            family.cacheOffset = AddCompositeFamilyToCache((CompositeFamily)family, frequentStrings);
                        } 

                        Invariant.Assert(numberOfConstructedFamilies < familyCount);
                        cachedFamilies[numberOfConstructedFamilies] = family.cacheOffset;
                        ++numberOfConstructedFamilies; 
                    }
 
                    // now that we ensured the family is constructed, fill out CachedName 
                    cachedFamilyName->offsetToIetfLanguageTag = GetOffsetToIetfLanguageTag(familyName.Language, frequentStrings);
                    cachedFamilyName->offsetToName = GetOffsetToString(familyName.Name, frequentStrings); 
                    cachedFamilyName->data = family.cacheOffset;
                }
                catch (OverflowException)
                { 
                    // Extreme numbers in the font 'name' table can cause arithmetic overflows in
                    // AddPhysicalFamilyToCache. 
                    // We want to ignore such fonts and continue creating entries for well formed fonts. 
                }
 
                ++i;
            }
        }
 
        /// 
        ///     Critical: This code calls into cacher. and results in unverifiable code 
        ///     TreatAsSafe: This code is safe to call since the cacher function is bounds checked., 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        private int StoreString(string name)
        {
            unsafe
            { 
                int newOffset = _cacher.Alloc(sizeof(int) + Util.StringSize(name));
                Util.StringAndLengthCopyToCheckedPointer(_cacher.GetCheckedPointer(newOffset), name); 
                return newOffset; 
            }
        } 

        private int GetOffsetToString(string name, SortedList frequentStrings)
        {
            int offset = (int)frequentStrings[name]; 
            Debug.Assert(offset != Util.nullOffset && offset != 0);
            return offset; 
        } 

        private int GetOffsetToIetfLanguageTag(XmlLanguage language, SortedList frequentStrings) 
        {
            return GetOffsetToString(language.IetfLanguageTag, frequentStrings);
        }
 
        private string GetStringFromOffset(int offsetToName)
        { 
            return Util.StringAndLengthCopyFromCheckedPointer(_cacher.GetCheckedPointer(offsetToName)); 
        }
 
        private XmlLanguage GetFamilyMapLanguageFromOffset(int offsetToIetfLanguageTag)
        {
            return XmlLanguage.GetLanguage(GetStringFromOffset(offsetToIetfLanguageTag));
        } 

        ///  
        ///    Critical: This function acceses _cacher[], it derefernces a pointer without any checking 
        /// 
        [SecurityCritical] 
        private unsafe bool InRange(CachedFamilyMap* cmap, int ch)
        {
            CachedCharacterRange* range = (CachedCharacterRange*)((byte*)cmap + cmap->offsetToRanges);
            for (int i = 0; i < cmap->numberOfRanges; i++) 
            {
                // clever code from Word meaning: "ch >= _first && ch <= _last", 
                // this is done with one test and branch. 
                if ((uint)(ch - range->first) <= range->delta)
                    return true; 
                ++range;
            }
            return false;
        } 

        ///  
        ///        Critical: This code calls into unsafe code blocks and exposes a pointer. 
        /// 
        [SecurityCritical] 
        private unsafe CachedFamiliesAndNames* GetCachedFamiliesAndNames()
        {
            return (CachedFamiliesAndNames*)_cacher[_data->offsetToCachedFamiliesAndNames];
        } 

        ///  
        ///        Critical: This code calls into unsafe code blocks and exposes a pointer. 
        /// 
        [SecurityCritical] 
        private unsafe void GetCachedFamilyNames(out CachedName* cachedFamilyNames, out int familyNameCount)
        {
            CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
            familyNameCount = cachedFamiliesAndNames->numberOfFamilyNames; 
            cachedFamilyNames = CachedFamiliesAndNames.GetCachedFamilyNames(cachedFamiliesAndNames);
        } 
 
        /// 
        ///        Critical: This code calls into unsafe code blocks and exposes a pointer. 
        /// 
        [SecurityCritical]
        private unsafe void GetCachedFamilies(out int* cachedFamilies, out int familyCount)
        { 
            CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
            familyCount = cachedFamiliesAndNames->numberOfFontFamilies; 
            cachedFamilies = CachedFamiliesAndNames.GetCachedFamilies(cachedFamiliesAndNames); 
        }
 
        #endregion Private Methods


        //----------------------------------------------------- 
        //
        //  Private Types 
        // 
        //------------------------------------------------------
 
        #region Private Types

        /// 
        /// Memory layout: Layout struct | folder name string. 
        /// 
        [StructLayout(LayoutKind.Explicit, Size = 20)] 
        internal struct Layout 
        {
            [FieldOffset(0)] 
            internal long timeStamp;

            [FieldOffset(8)]
            internal int offsetToCachedFamiliesAndNames; 

            ///  
            /// isDefaultSystemFontsFolder and folderUriSize should be the last members, 
            /// as they are stored along with the name string as a key for cache miss reports.
            ///  
            [FieldOffset(OffsetToKey)]
            internal bool isDefaultSystemFontsFolder;

            [FieldOffset(OffsetToFolderUriSize)] 
            internal int folderUriSize;
 
            internal const int OffsetToKey = 12; 
            internal const int OffsetToFolderUriSize = 16;
        } 

        /// 
        /// Memory layout: CachedFamiliesAndNames |
        /// unsorted array [numberOfFontFamilies] of font family pointers | 
        /// sorted array [numberOfFamilyNames] of CachedName family entries
        /// Unsorted array of font family pointers is used for font family enumeration. 
        /// Sorted array of font family names is used for font family lookup by name. 
        /// The structure should be aligned on a 4 byte bounary.
        /// This guarantees that arrays are also aligned on 4 byte boundaries. 
        /// 
        ///
        /// Critical - as this class contains sizes and offsets which are used to allocate
        ///            unamanged memory and perform pointer arithmetic; font cache corruption 
        ///            could result if any of the calculations are wrong.
        /// 
        ///            The entire class is declared critical because *all* of its members are 
        ///            critical, and because declaring the critical members individually would
        ///            not result in any more transparent code. 
        ///
        [SecurityCritical(SecurityCriticalScope.Everything)]
        [StructLayout(LayoutKind.Explicit, Size = 8)]
        private struct CachedFamiliesAndNames 
        {
            [FieldOffset(0)] 
            internal int numberOfFontFamilies; 
            [FieldOffset(4)]
            internal int numberOfFamilyNames; 

            // We cannot use sizeof to assign to a const variable.
            public const int OffsetToCachedFamilies = 8;
 
            public unsafe static int* GetCachedFamilies(CachedFamiliesAndNames* This)
            { 
                return (int*)((byte*)This + OffsetToCachedFamilies); 
            }
 
            public static int GetTotalSize(int familyCount, int familyNameCount)
            {
                unsafe
                { 
                    return GetOffsetToCachedFamilyNames(familyCount) + sizeof(CachedName) * familyNameCount;
                } 
            } 

            public static int GetOffsetToCachedFamilyNames(int familyCount) 
            {
                unsafe
                {
                    return OffsetToCachedFamilies + sizeof(int) * familyCount; 
                }
            } 
 
            public unsafe static CachedName* GetCachedFamilyNames(CachedFamiliesAndNames* This)
            { 
                return (CachedName*)((byte*)This + GetOffsetToCachedFamilyNames(This->numberOfFontFamilies));
            }
        }
 
        /// 
        /// The structure should be aligned on 4 byte boundary. 
        ///  
        [StructLayout(LayoutKind.Explicit, Size = 12)]
        private struct CachedName 
        {
            /// 
            /// Offset to the name string culture
            ///  
            [FieldOffset(0)]
            internal int offsetToIetfLanguageTag; 
 
            /// 
            /// Offset to the actual name string 
            /// 
            [FieldOffset(4)]
            internal int offsetToName;
 
            /// 
            /// Offset to PhysicalFace or Family 
            ///  
            [FieldOffset(8)]
            internal int data; 
        };

        internal enum FamilyType
        { 
            Composite = 0,
            Physical = 1 
        }; 

        ///  
        /// Base structure for CachedPhysicalFamily and CachedCompositeFamily.
        /// Since we cannot use inheritance here, we simply repeat the fields in derivees.
        /// 
        [StructLayout(LayoutKind.Explicit, Size = 32)] 
        internal struct CachedFamily
        { 
            ///  
            /// Composite or physical
            ///  
            [FieldOffset(0)]
            internal FamilyType familyType;

            ///  
            /// Number of typefaces in this family.
            ///  
            [FieldOffset(4)] 
            internal int numberOfTypefaces;
 
            [FieldOffset(8)]
            internal double baseline;

            [FieldOffset(16)] 
            internal double lineSpacing;
 
            ///  
            /// Number of localized names for this family.
            ///  
            [FieldOffset(24)]
            internal int numberOfFamilyNames;

            ///  
            /// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family,
            /// sorted by culture info string. 
            ///  
            [FieldOffset(28)]
            internal int offsetToFamilyNames; 
        };

        /// Allocates and initializes a CachedCompositeFamily structure and related structures. The
        /// memory layout is as follows. 
        ///
        /// 1.  CachedCompositeFamily struct 
        /// 
        /// 2.  at CachedCompositeFamily.OffsetToFamilyMaps:
        ///     array [numberOfFamilyMaps] of CachedFamilyMap in lookup order. 
        ///
        /// 3.  at CachedCompositeFamily.OffsetToFamilyMapRangeLists:
        ///     array [numberOfFamilyMapRangeLists] of CachedFamilyMapRangeList in decreasing
        ///     order of cultural specificity. 
        ///
        /// 4.  at CachedCompositeFamily.OffsetToFamilyTypeface: 
        ///     array[numberOfTypefaces] of CachedCompositeFace. 
        ///
        /// 5.  at CachedFamily.OffsetToTargetFamilyNameStrings 
        ///     one or more size-prefixed strings, each with the following layout:
        ///         int representing the size of the string in bytes
        ///         char[] containing characters of the string
        ///         optional padding for integer alignment 
        ///     Note: The first string is always the target of the first FontFamilyMap. Subsequent
        ///       strings may be in any order. If there are no FamilyMaps then a single string 
        ///       of length zero is added. 
        ///
        /// 6.  at _offsetToFamilyMapRanges: 
        ///     array[] of CachedCharacterRange.
        ///
        /// 7.  at _offsetToRangeListArrays:
        ///     array[] of ushort, referenced by CachedFamilyMapRangeList. 
        ///
        /// 8.  at _offsetToCachedDeviceFonts: 
        ///     zero or more of the following: 
        ///         CachedDeviceFont structure
        ///         at CachedDeviceFont.OffsetToLengthPrefixedName: length prefixed string 
        ///         at CachedDeviceFont.OffsetToCharacterMap: array[] of int
        ///
        /// 9.  at _offsetToCharacterMetricsArray:
        ///     array[] of CachedCharacterMetrics 
        ///
        /// 
        /// Critical - as this class contains sizes and offsets which are used to allocate 
        ///            unamanged memory and perform pointer arithmetic; font cache corruption
        ///            could result if any of the calculations are wrong. 
        ///
        ///            The entire class is declared critical because *all* of its members are
        ///            critical, and because declaring the critical members individually would
        ///            not result in any more transparent code. 
        ///
        [SecurityCritical(SecurityCriticalScope.Everything)] 
        private struct CompositeFamilyCacher 
        {
            private CompositeFontInfo _compositeFontInfo; 
            private CachedCompositeFamily _cachedFamily;

            // Family map information.
            // AnalyzeFamilyMaps initializes these fields. 
            private Hashtable _targetFamilyNamesTable;
            private int _sizeOfTargetFamilyNameStrings; 
            private int _sizeOfFamilyMapRanges; 
            private string _firstTargetFamilyName;
 
            // Family map language / range lists information.
            // AnalyzeFamilyMapLanguages initializes these fields.
            private XmlLanguage[] _familyMapLanguages;
            private ushort[][] _familyMapRangeLists; 
            private int _sizeOfRangeListArrays;
 
            // Device font information. 
            // AnalyzeDeviceFonts initializes these fields.
            private Hashtable _characterMetricsTable; 
            private int _sizeOfCachedDeviceFonts;
            private int _sizeOfCharacterMetrics;

            // Other offsets, based on the above sizes. 
            private int _offsetToFamilyMapRanges;
            private int _offsetToRangeListArrays; 
            private int _offsetToCachedDeviceFonts; 
            private int _offsetToCharacterMetricsArray;
            private int _allocationSize; 

            internal unsafe CachedCompositeFamily* Allocate(
                CompositeFontInfo compositeFontInfo,
                FamilyCollection collection, 
                SortedList frequentStrings,
                out int familyCacheOffset) 
            { 
                AnalyzeCompositeFontInfo(compositeFontInfo);
 
                // allocate memory for the CachedCompositeFamily and subsequent data
                familyCacheOffset = collection._cacher.Alloc(_allocationSize);

                // create a checked pointer for bounds-checked access using the allocation size 
                CheckedPointer cachedFamily = collection._cacher.Mapping.CheckedProbe(
                    familyCacheOffset, 
                    _allocationSize 
                    );
 
                // fill in basic information
                CachedCompositeFamily* familyPointer = (CachedCompositeFamily*)(cachedFamily.Probe(0, sizeof(CachedCompositeFamily)));
                *familyPointer = _cachedFamily;
 
                // fill in family maps
                WriteFamilyMaps(collection, cachedFamily, frequentStrings); 
                WriteFamilyTypefaces(cachedFamily); 

                return familyPointer; 
            }

            private void AnalyzeCompositeFontInfo(CompositeFontInfo compositeFontInfo)
            { 
                _compositeFontInfo = compositeFontInfo;
                _cachedFamily = new CachedCompositeFamily(); 
 
                // Partially initialize _cachedFamily.
                _cachedFamily.familyType = FamilyType.Composite; 
                _cachedFamily.baseline = compositeFontInfo.Baseline;
                _cachedFamily.lineSpacing = compositeFontInfo.LineSpacing;

                // Calculate other sizes and offsets, including some _cachedFamily fields. 
                AnalyzeFamilyMaps();
                AnalyzeFamilyTypefaces(); 
 
                // We've now initialized _cachedFamily to the point where we can rely on the values
                // returned by its OffsetToXXX properties. Calculate additional offsets based on the 
                // previously computed sizes.

                // array of CachedCharacterRange; 8-byte alignment required
                _offsetToFamilyMapRanges = Util.Align8( 
                    _cachedFamily.OffsetToTargetFamilyNameStrings + _sizeOfTargetFamilyNameStrings
                    ); 
 
                // array of ushort; ushort alignment is sufficient
                _offsetToRangeListArrays = _offsetToFamilyMapRanges + _sizeOfFamilyMapRanges; 

                // zero or more of ( CachedDeviceFont + array of char + array of int )
                _offsetToCachedDeviceFonts = Util.Align8(
                    _offsetToRangeListArrays + _sizeOfRangeListArrays 
                    );
 
                // array of CachedCharacterMetrics; 8-byte alignment is required 
                _offsetToCharacterMetricsArray = Util.Align8(
                    _offsetToCachedDeviceFonts + _sizeOfCachedDeviceFonts 
                    );

                _allocationSize = Util.Align8(
                    _offsetToCharacterMetricsArray + _sizeOfCharacterMetrics 
                    );
            } 
 
            private void AnalyzeFamilyMaps()
            { 
                unsafe
                {
                    FontFamilyMapCollection familyMaps = _compositeFontInfo.FamilyMaps;
                    if (familyMaps != null && familyMaps.Count != 0) 
                    {
                        // Remember the number of family maps. 
                        _cachedFamily.numberOfFamilyMaps = _compositeFontInfo.FamilyMaps.Count; 

                        // We're going to create a mapping of target family names to unique indexes 
                        // and also remember the first target family name.
                        _targetFamilyNamesTable = new Hashtable(_cachedFamily.numberOfFamilyMaps);
                        _firstTargetFamilyName = familyMaps[0].Target;
 
                        foreach (FontFamilyMap map in familyMaps)
                        { 
                            // Is this the first occurrence of this family name? 
                            if (!_targetFamilyNamesTable.ContainsKey(map.Target))
                            { 
                                // Associated each unique string with a unique index.
                                _targetFamilyNamesTable.Add(map.Target, _targetFamilyNamesTable.Count);

                                // Reserve room for length prefix + characters + any padding. 
                                _sizeOfTargetFamilyNameStrings += GetPrefixedStringSize(map.Target);
                            } 
 
                            _sizeOfFamilyMapRanges += map.Ranges.Length * sizeof(CachedCharacterRange);
                        } 

                        // Analyze the family map cultures and the corresponding range lists.
                        AnalyzeFamilyMapLanguages();
                    } 

                    // If there are no target family names we'll add a zero length string so that 
                    // CachedCompositeFamily.OffsetToTargetFamilyNameStrings always points to a 
                    // valid length-prefixed string.
                    if (_sizeOfTargetFamilyNameStrings == 0) 
                    {
                        // All we need is the length prefix with value zero.
                        _sizeOfTargetFamilyNameStrings = GetPrefixedStringSize(String.Empty);
                    } 
                }
            } 
 
            private static int GetPrefixedStringSize(string s)
            { 
                unsafe
                {
                    return Util.Align4(sizeof(int) + Util.StringSize(s));
                } 
            }
 
            private unsafe void WriteFamilyMaps(FamilyCollection collection, CheckedPointer cachedFamily, SortedList frequentStrings) 
            {
                CheckedPointer[] namePointers = null; 
                CheckedPointer nameStrings = cachedFamily + _cachedFamily.OffsetToTargetFamilyNameStrings;
                CheckedPointer nameStringsEnd = nameStrings;

                // fill in target family names 
                if (_targetFamilyNamesTable != null)
                { 
                    namePointers = new CheckedPointer[_targetFamilyNamesTable.Count]; 

                    // The first target family name must always come first. 
                    namePointers[0] = nameStringsEnd;
                    Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, _firstTargetFamilyName);
                    nameStringsEnd += GetPrefixedStringSize(_firstTargetFamilyName);
 
                    // The other target family names can be added in whatever order the
                    // hashtable enumerates them. 
                    foreach (DictionaryEntry entry in _targetFamilyNamesTable) 
                    {
                        // We've already added the first target family name so skip this 
                        // entry if nameIndex is zero.
                        int nameIndex = (int)entry.Value;
                        if (nameIndex != 0)
                        { 
                            namePointers[nameIndex] = nameStringsEnd;
                            string familyName = (string)entry.Key; 
                            Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, familyName); 
                            nameStringsEnd += GetPrefixedStringSize(familyName);
                        } 
                    }
                }
                else
                { 
                    // No family names. Store an empty string so that CachedCompositeFamily.OffsetToTargetFamilyNameStrings
                    // always is the offset of a valid length-prefixed string. 
                    Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, String.Empty); 
                    nameStringsEnd += GetPrefixedStringSize(String.Empty);
                } 

                // Make sure the calculated size agrees with the actual size.
                Invariant.Assert(nameStringsEnd.PointerEquals(nameStrings + _sizeOfTargetFamilyNameStrings));
 
                // fill in the CachedFamilyMap and CachedCharacterRange arrays
                if (_cachedFamily.numberOfFamilyMaps != 0) 
                { 
                    CheckedPointer cachedFamilyMaps = cachedFamily + _cachedFamily.OffsetToFamilyMaps;
                    CheckedPointer cachedRanges = cachedFamily + _offsetToFamilyMapRanges; 
                    CheckedPointer cachedRangesEnd = cachedRanges;

                    foreach (FontFamilyMap cmap in _compositeFontInfo.FamilyMaps)
                    { 
                        CachedFamilyMap* familyMap = (CachedFamilyMap*)cachedFamilyMaps.Probe(0, sizeof(CachedFamilyMap));
                        familyMap->scaleInEm = cmap.Scale; 
 
                        CheckedPointer namePointer = namePointers[(int)_targetFamilyNamesTable[cmap.Target]];
                        familyMap->targetFamilyNameOffset = cachedFamilyMaps.OffsetOf(namePointer); 

                        familyMap->numberOfRanges = cmap.Ranges.Length;
                        familyMap->offsetToRanges = cachedFamilyMaps.OffsetOf(cachedRangesEnd);
                        foreach (FontFamilyMap.Range range in cmap.Ranges) 
                        {
                            CachedCharacterRange* cachedRange = (CachedCharacterRange*)cachedRangesEnd.Probe(0, sizeof(CachedCharacterRange)); 
                            cachedRange->first = range.First; 
                            cachedRange->delta = range.Delta;
                            cachedRangesEnd += sizeof(CachedCharacterRange); 
                        }

                        cachedFamilyMaps += sizeof(CachedFamilyMap);
                    } 

                    // Make sure the calculated size agrees with the actual size. 
                    Invariant.Assert(cachedRangesEnd.PointerEquals(cachedRanges + _sizeOfFamilyMapRanges)); 

                    // Write the family map cultures and their associated range lists. 
                    WriteFamilyMapLanguages(collection, cachedFamily, frequentStrings);
                }
            }
 
            private void AnalyzeFamilyMapLanguages()
            { 
                unsafe 
                {
                    int languageCount = _compositeFontInfo.FamilyMapLanguages != null ? 
                        _compositeFontInfo.FamilyMapLanguages.Count :
                        0;

                    // We create one range list per culture, plus one for "any language", i.e., null. 
                    _cachedFamily.numberOfFamilyMapRangeLists = languageCount + 1;
 
                    // Create the array of cultures (including "any") and sort in decreasing order of specificity. 
                    _familyMapLanguages = new XmlLanguage[_cachedFamily.numberOfFamilyMapRangeLists];
                    if (languageCount > 0) 
                    {
                        _compositeFontInfo.FamilyMapLanguages.CopyTo(_familyMapLanguages, 0);
                        Array.Sort(_familyMapLanguages, 0, languageCount, LanguageComparer.Comparer);
                    } 

                    // Get the range list for each culture (including "any") and calculate their total size. 
                    _familyMapRangeLists = new ushort[_cachedFamily.numberOfFamilyMapRangeLists][]; 
                    for (int i = 0; i < _familyMapLanguages.Length; ++i)
                    { 
                        ushort[] ranges = _compositeFontInfo.GetFamilyMapsOfLanguage(_familyMapLanguages[i]);
                        _familyMapRangeLists[i] = ranges;
                        _sizeOfRangeListArrays += (ranges.Length - CompositeFontInfo.FirstFamilyMapRange) * sizeof(ushort);
                    } 
                }
            } 
 
            private unsafe void WriteFamilyMapLanguages(FamilyCollection collection, CheckedPointer cachedFamily, SortedList frequentStrings)
            { 
                // fill in the family map range lists
                CheckedPointer rangeLists = cachedFamily + _cachedFamily.OffsetToFamilyMapRangeLists;
                CheckedPointer rangeListArrays = cachedFamily + _offsetToRangeListArrays;
                CheckedPointer rangeListArraysEnd = rangeListArrays; 

                for (int i = 0; i < _familyMapLanguages.Length; ++i) 
                { 
                    XmlLanguage language = _familyMapLanguages[i];
                    ushort[] ranges = _familyMapRangeLists[i]; 

                    CachedFamilyMapRangeList* rangeList = (CachedFamilyMapRangeList*)rangeLists.Probe(0, sizeof(CachedFamilyMapRangeList));
                    rangeList->offsetToIetfLanguageTag = (language != null) ? collection.GetOffsetToIetfLanguageTag(language, frequentStrings) : 0;
                    rangeList->lengthOfRanges = ranges.Length - CompositeFontInfo.FirstFamilyMapRange; 
                    rangeList->offsetToRanges = rangeLists.OffsetOf(rangeListArraysEnd);
 
                    int arraySizeInBytes = Util.ArrayCopyToCheckedPointer( 
                        rangeListArraysEnd,
                        ranges, 
                        CompositeFontInfo.FirstFamilyMapRange,
                        rangeList->lengthOfRanges
                        );
 
                    rangeListArraysEnd += arraySizeInBytes;
                    rangeLists += sizeof(CachedFamilyMapRangeList); 
                } 

                // Make sure the calculated size agrees with the actual size. 
                Invariant.Assert(rangeListArrays.OffsetOf(rangeListArraysEnd) == _sizeOfRangeListArrays);
            }

            private void AnalyzeFamilyTypefaces() 
            {
                FamilyTypefaceCollection familyTypefaces = _compositeFontInfo.FamilyTypefaces; 
                if (familyTypefaces == null) 
                    return;
 
                _cachedFamily.numberOfTypefaces = familyTypefaces.Count;
                AnalyzeDeviceFonts();
            }
 
            private unsafe void WriteFamilyTypefaces(CheckedPointer cachedFamily)
            { 
                if (_cachedFamily.numberOfTypefaces == 0) 
                    return;
 
                // fill in the CachedCompositeFace array
                CachedCompositeFace* cachedFamilyTypeface = (CachedCompositeFace*)cachedFamily.Probe(
                    _cachedFamily.OffsetToFamilyTypeface,
                    _cachedFamily.numberOfTypefaces * sizeof(CachedCompositeFace) 
                    );
 
                foreach (FamilyTypeface familyTypeface in _compositeFontInfo.FamilyTypefaces) 
                {
                    cachedFamilyTypeface->capsHeight = familyTypeface.CapsHeight; 

                    cachedFamilyTypeface->fontStyle = familyTypeface.Style;
                    cachedFamilyTypeface->fontWeight = familyTypeface.Weight;
                    cachedFamilyTypeface->fontStretch = familyTypeface.Stretch; 

                    cachedFamilyTypeface->strikeThroughPosition = familyTypeface.StrikethroughPosition; 
                    cachedFamilyTypeface->strikeThroughThickness = familyTypeface.StrikethroughThickness; 
                    cachedFamilyTypeface->underlinePosition = familyTypeface.UnderlinePosition;
                    cachedFamilyTypeface->underlineThickness = familyTypeface.UnderlineThickness; 
                    cachedFamilyTypeface->xHeight = familyTypeface.XHeight;

                    ++cachedFamilyTypeface;
                } 

                // fill in device font information 
                WriteDeviceFonts(cachedFamily); 
            }
 
            private void AnalyzeDeviceFonts()
            {
                unsafe
                { 
                    for (int i = 0; i < _cachedFamily.numberOfTypefaces; ++i)
                    { 
                        // Does this FamilyTypeface have device font information? 
                        FamilyTypeface face = _compositeFontInfo.FamilyTypefaces[i];
                        if (face.DeviceFontName != null && face.DeviceFontCharacterMetrics.Count != 0) 
                        {
                            // We'll store only one copy of each unique CharacterMetrics so use a hash table
                            // to map character metrics (the key) to integers (indexes).
                            if (_characterMetricsTable == null) 
                            {
                                _characterMetricsTable = new Hashtable(_cachedFamily.numberOfTypefaces); 
                            } 

                            // Determine the number of "pages" of integers after the CachedDeviceFont structure. 
                            CharacterMetricsDictionary metricsDictionary = face.DeviceFontCharacterMetrics;
                            int pageCount = 0;
                            for (int j = 0; j < CharacterMetricsDictionary.PageCount; ++j)
                            { 
                                // Iterate over pages, where j is the page index
                                CharacterMetrics[] page = metricsDictionary.GetPage(j); 
                                if (page != null) 
                                {
                                    // The page exists so increment the page count 
                                    ++pageCount;

                                    // Add each unique CharacterMetrics object to the hash table
                                    for (int k = 0; k < page.Length; ++k) 
                                    {
                                        if (page[k] != null && !_characterMetricsTable.ContainsKey(page[k])) 
                                        { 
                                            _characterMetricsTable.Add(page[k], _characterMetricsTable.Count);
                                        } 
                                    }
                                }
                            }
 
                            // Add the total size of this device font including the CachedDeviceFont structure itself
                            // plus the string and character metrics array. 
                            _sizeOfCachedDeviceFonts += new CachedDeviceFont(face.DeviceFontName, pageCount).SizeOfCachedDeviceFont; 
                        }
                    } 

                    // Add room for the array of character metrics.
                    if (_characterMetricsTable != null)
                    { 
                        _sizeOfCharacterMetrics = _characterMetricsTable.Count * sizeof(CachedCharacterMetrics);
                    } 
                } 
            }
 
            private unsafe void WriteDeviceFonts(CheckedPointer cachedFamily)
            {
                if (_characterMetricsTable == null)
                    return; 

                // fill in the CachedCharacterMetrics array 
                CheckedPointer cachedCharacterMetricsArray = cachedFamily.CheckedProbe( 
                    _offsetToCharacterMetricsArray,
                    _characterMetricsTable.Count * sizeof(CachedCharacterMetrics) 
                    );
                foreach (DictionaryEntry entry in _characterMetricsTable)
                {
                    CharacterMetrics metrics = (CharacterMetrics)(entry.Key); 
                    CachedCharacterMetrics* cachedMetrics = (CachedCharacterMetrics*)cachedCharacterMetricsArray.Probe(
                        (int)(entry.Value) * sizeof(CachedCharacterMetrics), 
                        sizeof(CachedCharacterMetrics) 
                        );
 
                    cachedMetrics->blackBoxWidth = metrics.BlackBoxWidth;
                    cachedMetrics->blackBoxHeight = metrics.BlackBoxHeight;
                    cachedMetrics->baseline = metrics.Baseline;
                    cachedMetrics->leftSideBearing = metrics.LeftSideBearing; 
                    cachedMetrics->rightSideBearing = metrics.RightSideBearing;
                    cachedMetrics->topSideBearing = metrics.TopSideBearing; 
                    cachedMetrics->bottomSideBearing = metrics.BottomSideBearing; 
                }
 
                // iterate over the typefaces to fill in device fonts
                CheckedPointer cachedDeviceFonts = cachedFamily + _offsetToCachedDeviceFonts;
                CheckedPointer cachedDeviceFontsEnd = cachedDeviceFonts;
                CheckedPointer currentFamilyTypeface = cachedFamily + _cachedFamily.OffsetToFamilyTypeface; 

                foreach (FamilyTypeface familyTypeface in _compositeFontInfo.FamilyTypefaces) 
                { 
                    // Does this typeface have device font information?
                    string deviceFontName = familyTypeface.DeviceFontName; 
                    if (deviceFontName != null && familyTypeface.DeviceFontCharacterMetrics.Count != 0)
                    {
                        // Initialize the offset from the typeface to the corresponding device font.
                        CachedCompositeFace* typefacePtr = (CachedCompositeFace*)currentFamilyTypeface.Probe(0, sizeof(CachedCompositeFace)); 
                        typefacePtr->offsetToDeviceFont = currentFamilyTypeface.OffsetOf(cachedDeviceFontsEnd);
 
                        // Count the non-empty pages in the character metrics dictionary. 
                        CharacterMetricsDictionary metricsDictionary = familyTypeface.DeviceFontCharacterMetrics;
                        int pageCount = 0; 
                        for (int i = 0; i < CharacterMetricsDictionary.PageCount; ++i)
                        {
                            if (metricsDictionary.GetPage(i) != null)
                                ++pageCount; 
                        }
 
                        // Point to the device font structure and fill in the page count and name. 
                        CachedDeviceFont* deviceFontPtr = (CachedDeviceFont*)cachedDeviceFontsEnd.Probe(0, sizeof(CachedDeviceFont));
                        deviceFontPtr->numberOfPages = pageCount; 
                        Util.StringAndLengthCopyToCheckedPointer(
                            cachedDeviceFontsEnd + CachedDeviceFont.OffsetToLengthPrefixedName,
                            deviceFontName
                            ); 

                        // Create a checked pointer for accessing the character map; this ensures we won't 
                        // overwrite the memory we reserved for this device font. 
                        int characterMapOffset = deviceFontPtr->OffsetToCharacterMap;
                        int deviceFontSize = deviceFontPtr->SizeOfCachedDeviceFont; 
                        CheckedPointer characterMap = cachedDeviceFontsEnd.CheckedProbe(
                            characterMapOffset,
                            deviceFontSize - characterMapOffset
                            ); 

                        // Point to the page table. 
                        int* cachedPageTable = (int*)characterMap.Probe(0, CharacterMetricsDictionary.PageCount * sizeof(int)); 

                        // Integer index of current page from start of page table. 
                        int currentPageIndex = CharacterMetricsDictionary.PageCount;

                        // Iterate over the pages in the CharacterMetricsDictionary.
                        for (int i = 0; i < CharacterMetricsDictionary.PageCount; ++i) 
                        {
                            // Is there a page? 
                            CharacterMetrics[] page = metricsDictionary.GetPage(i); 
                            if (page != null)
                            { 
                                // Point to the page.
                                int* cachedPage = (int*)characterMap.Probe(
                                    currentPageIndex * sizeof(int),
                                    CharacterMetricsDictionary.PageSize * sizeof(int) 
                                    );
 
                                // Set the page table entry to the index of the beginning of the page. 
                                cachedPageTable[i] = currentPageIndex;
 
                                // Initialize the page contents.
                                for (int j = 0; j < CharacterMetricsDictionary.PageSize; ++j)
                                {
                                    if (page[j] != null) 
                                    {
                                        // Use the hash table to find the array index of the CachedCharacterMetrics object. 
                                        int metricsIndex = (int)_characterMetricsTable[page[j]]; 

                                        // Set the page entry to the offset of the CachedCharacterMetrics object. 
                                        cachedPage[j] = cachedDeviceFontsEnd.OffsetOf(
                                            cachedCharacterMetricsArray + (metricsIndex * sizeof(CachedCharacterMetrics))
                                            );
                                    } 
                                }
 
                                // Update the current page index. 
                                currentPageIndex += CharacterMetricsDictionary.PageSize;
                            } 
                        }

                        // Point to where the next CachedDeviceFont will go (if any).
                        cachedDeviceFontsEnd += deviceFontSize; 
                    }
 
                    currentFamilyTypeface += sizeof(CachedCompositeFace); 
                }
 
                // Make sure the total memory we filled in for all device fonts matches the calculated size.
                Invariant.Assert(cachedDeviceFonts.OffsetOf(cachedDeviceFontsEnd) == _sizeOfCachedDeviceFonts);
            }
        } 

        /// See comment for struct CompositeFamilyCacher for memory layout. 
        /// We never store this in a sequential array so we don't have to 8 byte align its size. 
        [StructLayout(LayoutKind.Explicit, Size = 40)]
        internal struct CachedCompositeFamily 
        {
            /// 
            /// Composite or physical
            ///  
            [FieldOffset(0)]
            internal FamilyType familyType; 
 
            /// 
            /// Number of typefaces in this family. 
            /// 
            [FieldOffset(4)]
            internal int numberOfTypefaces;
 
            [FieldOffset(8)]
            internal double baseline; 
 
            [FieldOffset(16)]
            internal double lineSpacing; 

            /// 
            /// Number of localized names for this family.
            ///  
            [FieldOffset(24)]
            internal int numberOfFamilyNames; 
 
            /// 
            /// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family, 
            /// sorted by culture info string.
            /// 
            [FieldOffset(28)]
            internal int offsetToFamilyNames; 

            ///  
            /// Number of family maps in the family maps list. 
            /// 
            [FieldOffset(32)] 
            internal int numberOfFamilyMaps;

            /// 
            /// Number of per-culture family map ranges lists. The range lists should be sorted in 
            /// order of decreasing cultural-specificity, with the default range list (for "any"
            /// culture) coming last. 
            ///  
            [FieldOffset(36)]
            internal int numberOfFamilyMapRangeLists; 

            // numberOfFamilyMaps structures of type CachedFamilyMap
            internal int OffsetToFamilyMaps
            { 
                get
                { 
                    unsafe 
                    {
                        return sizeof(CachedCompositeFamily); 
                    }
                }
            }
 
            // numberOfFamilyMapRangeLists structures of type CachedFamilyMapRangeList
            internal int OffsetToFamilyMapRangeLists 
            { 
                get
                { 
                    unsafe
                    {
                        return OffsetToFamilyMaps + numberOfFamilyMaps * sizeof(CachedFamilyMap);
                    } 
                }
            } 
 
            // [8 byte align barrier] numberOfTypefaces structures of type CachedCompositeFace
            internal int OffsetToFamilyTypeface 
            {
                get
                {
                    unsafe 
                    {
                        return Util.Align8( 
                            OffsetToFamilyMapRangeLists + 
                            numberOfFamilyMapRangeLists * sizeof(CachedFamilyMapRangeList)
                            ); 
                    }
                }
            }
 
            // variable-length array of char containing null-terminated family names
            internal int OffsetToTargetFamilyNameStrings 
            { 
                get
                { 
                    unsafe
                    {
                        return OffsetToFamilyTypeface + numberOfTypefaces * sizeof(CachedCompositeFace);
                    } 
                }
            } 
        } 

        ///  
        /// CachedFace is a base for CachedPhysicalFace and CachedCompositeFace
        /// We can't use inheritance here, so we much keep the three structures in sync.
        /// 
        [StructLayout(LayoutKind.Explicit, Size = 12)] 
        internal struct CachedFace
        { 
            [FieldOffset(0)] 
            internal FontStyle style;
 
            [FieldOffset(4)]
            internal FontWeight weight;

            [FieldOffset(8)] 
            internal FontStretch stretch;
        } 
 
        /// 
        /// Memory layout: PhysicalFace struct | font Uri string 
        /// 
        [StructLayout(LayoutKind.Explicit, Size = 24)]
        internal struct CachedPhysicalFace
        { 
            [FieldOffset(0)]
            internal FontStyle style; 
 
            [FieldOffset(4)]
            internal FontWeight weight; 

            [FieldOffset(8)]
            internal FontStretch stretch;
 
            // the Uri of the font face
            [FieldOffset(12)] 
            internal int fontUriSize; 

            [FieldOffset(16)] 
            internal int faceIndex;

            [FieldOffset(20)]
            internal StyleSimulations styleSimulations; 
        }
 
        // since we have doubles in this structure, 
        // always make its size aligned on 8 byte boundary
        [StructLayout(LayoutKind.Explicit, Size = 64)] 
        internal struct CachedCompositeFace
        {
            [FieldOffset(0)]
            internal FontStyle fontStyle; 
            [FieldOffset(4)]
            internal FontWeight fontWeight; 
            [FieldOffset(8)] 
            internal FontStretch fontStretch;
            [FieldOffset(12)] 
            internal int offsetToDeviceFont;

            [FieldOffset(16)]
            internal double underlinePosition; 
            [FieldOffset(24)]
            internal double underlineThickness; 
            [FieldOffset(32)] 
            internal double strikeThroughPosition;
            [FieldOffset(40)] 
            internal double strikeThroughThickness;
            [FieldOffset(48)]
            internal double capsHeight;
            [FieldOffset(56)] 
            internal double xHeight;
        }; 
 
        // Memory layout: CachedDeviceFont | string | array of int. The size of
        // the string and the integer array is variable, but we never store this structure in an 
        // array; its offset from the corresponding CachedCompositeFace is specified by the latter
        // structure's offsetToDeviceFont field. 8-byte alignment is required.
        [StructLayout(LayoutKind.Explicit, Size = 8)]
        internal struct CachedDeviceFont 
        {
            internal CachedDeviceFont(string name, int pageCount) 
            { 
                numberOfPages = pageCount;
                sizeOfDeviceFontName = Util.StringSize(name); 
            }

            // Number of pages of integers in the character map.
            [FieldOffset(0)] 
            internal int numberOfPages;
 
            // Size of the name in bytes; must be the last field in the structure 
            // as the device font name immediately follows
            [FieldOffset(4)] 
            internal int sizeOfDeviceFontName;

            // Offset of the sizeOfDeviceFontName field, which is the last field in
            // this structure and is immediately followed by character data 
            internal const int OffsetToLengthPrefixedName = 4;
 
            // Offset to an array of integers. The first CharacterMetricsDictionary.PageCount 
            // integers compose the "page table". It is followed by one or more "pages" each
            // CharacterMetricsDictionary.PageSize integers. 
            //
            // The high-order byte of a Unicode scalar value is an index into the page table,
            // and the integer at that index is either zero (if the character is not mapped)
            // or the index of the first element of the correct page for that range of characters. 
            // The low-order byte of the Unicode value is an index into the page, and the integer
            // at that index is either zero (if the character is not mapped) or the byte offset 
            // of a CachedCharacterMetrics structure relative to the CachedDeviceFont. 
            internal int OffsetToCharacterMap
            { 
                get
                {
                    unsafe
                    { 
                        return Util.Align4(
                            sizeof(CachedDeviceFont) + sizeOfDeviceFontName 
                            ); 
                    }
                } 
            }

            // Gets the total size of the cached device font, including the device font name
            // and character map, padded to an 8-byte boundary. The padding is so the the 
            // return value can be used as the offset to a subsequent device font.
            internal int SizeOfCachedDeviceFont 
            { 
                get
                { 
                    unsafe
                    {
                        // The character map consist of a "page table" of PageCount elements
                        // followed by a variable number of "pages" of PageSize elements. 
                        int characterMapLength =
                            CharacterMetricsDictionary.PageCount 
                            + (numberOfPages * CharacterMetricsDictionary.PageSize); 

                        return Util.Align8(OffsetToCharacterMap + (characterMapLength * sizeof(int))); 
                    }
                }
            }
        } 

        // since we have doubles in this structure, 
        // always make its size aligned on 8 byte boundary 
        [StructLayout(LayoutKind.Explicit, Size = 56)]
        internal struct CachedCharacterMetrics 
        {
            [FieldOffset(0)]
            internal double blackBoxWidth;
            [FieldOffset(8)] 
            internal double blackBoxHeight;
            [FieldOffset(16)] 
            internal double baseline; 
            [FieldOffset(24)]
            internal double leftSideBearing; 
            [FieldOffset(32)]
            internal double rightSideBearing;
            [FieldOffset(40)]
            internal double topSideBearing; 
            [FieldOffset(48)]
            internal double bottomSideBearing; 
        } 

        // since we have doubles in this structure, 
        // always make its size aligned on 8 byte boundary
        [StructLayout(LayoutKind.Explicit, Size = 24)]
        internal struct CachedFamilyMap
        { 
            [FieldOffset(0)]
            internal double scaleInEm; 
            [FieldOffset(8)] 
            internal int targetFamilyNameOffset;
            [FieldOffset(12)] 
            internal int numberOfRanges;
            [FieldOffset(16)]
            internal int offsetToRanges;
        }; 

        [StructLayout(LayoutKind.Explicit, Size = 8)] 
        internal struct CachedCharacterRange 
        {
            [FieldOffset(0)] 
            internal int first;
            [FieldOffset(4)]
            internal uint delta;
        }; 

        [StructLayout(LayoutKind.Explicit, Size = 12)] 
        internal struct CachedFamilyMapRangeList 
        {
            [FieldOffset(0)] 
            internal int offsetToIetfLanguageTag;

            [FieldOffset(4)]
            internal int lengthOfRanges; 

            [FieldOffset(8)] 
            internal int offsetToRanges; 
        };
 
        [StructLayout(LayoutKind.Explicit, Size = 40)]
        internal struct CachedPhysicalFamily
        {
            ///  
            /// Composite or physical
            ///  
            [FieldOffset(0)] 
            internal FamilyType familyType;
 
            /// 
            /// Number of typefaces in this family.
            /// 
            [FieldOffset(4)] 
            internal int numberOfTypefaces;
 
            [FieldOffset(8)] 
            internal double baseline;
 
            [FieldOffset(16)]
            internal double lineSpacing;

            ///  
            /// Number of localized names for this family.
            ///  
            [FieldOffset(24)] 
            internal int numberOfFamilyNames;
 
            /// 
            /// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family,
            /// sorted by culture info string.
            ///  
            [FieldOffset(28)]
            internal int offsetToFamilyNames; 
 
            [FieldOffset(32)]
            internal int numberOfFaceNames; 

            [FieldOffset(36)]
            internal bool symbol;
 
            // numberOfFaceNames face names of type CachedName follow
            // this sorted list is used for lookup by name 
 
            // numberOfTypefaces face pointers of type int follow
            // this list is used for face enumeration 
        };

        internal class BaseFamily
        { 
            /// 
            /// cacheOffset is used to reference the cached representation of the family 
            ///  
            internal int cacheOffset = Util.nullOffset;
 
            /// 
            /// A sorted list of LocalizedStrings containing all names for this family.
            /// Values don't matter.
            ///  
            internal SortedDictionary familyNames = new SortedDictionary(LocalizedName.LanguageComparer);
        } 
 
        internal class PhysicalFamily : BaseFamily
        { 
            // PhysicalFamily contains less members than CachedPhysicalFamily
            // because they can be computed only when we have the full list of families and faces

            internal SortedDictionary faceNames; 
            internal List typefaces = new List(1);
        } 
 
        private class CompositeFamily : BaseFamily
        { 
            internal CompositeFontInfo compositeFontInfo;
        }

        internal class PhysicalFace 
        {
            ///  
            /// cacheOffset is used to reference the cached representation of the face 
            /// 
            internal int cacheOffset = Util.nullOffset; 

            /// 
            /// Critical - fontUri can contain information about local file system.
            ///  
            [SecurityCritical]
            internal string fontUri; 
 
            internal int faceIndex;
            internal StyleSimulations styleSimulations; 

            internal FontStyle style;
            internal FontWeight weight;
            internal FontStretch stretch; 

            internal double version; 
            internal DateTime timestamp; 

            internal bool symbol; 

            internal ushort designEmHeight;
            internal ushort designCellAscent;
            internal ushort designCellDescent; 
            internal int designLineSpacing;
 
            internal LocalizedName[] names; 

            ///  
            /// Critical: accesses critical fontUri.
            /// TreatAsSafe: fontUri is copied into another critical member.
            /// 
            [SecurityCritical, SecurityTreatAsSafe] 
            internal PhysicalFace Clone()
            { 
                PhysicalFace clone = new PhysicalFace(); 
                clone.fontUri = this.fontUri;
                clone.faceIndex = this.faceIndex; 
                clone.styleSimulations = this.styleSimulations;

                clone.style = this.style;
                clone.weight = this.weight; 
                clone.stretch = this.stretch;
 
                clone.version = this.version; 
                clone.timestamp = this.timestamp;
 
                clone.symbol = this.symbol;

                clone.designEmHeight = this.designEmHeight;
                clone.designCellAscent = this.designCellAscent; 
                clone.designCellDescent = this.designCellDescent;
                clone.designLineSpacing = this.designLineSpacing; 
                clone.names = this.names; 
                return clone;
            } 
        }

        private unsafe struct LocalizedNameDictionary : IDictionary
        { 
            private FamilyCollection _familyCollection;
            private int _numberOfFamilyNames; 
            ///  
            ///     Critical: This holds reference to a pointer variable
            ///  
            [SecurityCritical]
            private int* _offsetToFamilyNames;
            /// 
            ///    Critical: This function acceses _cacher[] and dereferences a pointer 
            /// 
            [SecurityCritical] 
            internal LocalizedNameDictionary(FamilyCollection familyCollection, CachedFamily* cachedFamily) 
            {
                Invariant.Assert(cachedFamily!=null); 
                _numberOfFamilyNames = cachedFamily->numberOfFamilyNames;
                _familyCollection = familyCollection;
                _offsetToFamilyNames = (int*)_familyCollection._cacher[cachedFamily->offsetToFamilyNames];
                Invariant.Assert(_numberOfFamilyNames >= 1); 
                Invariant.Assert(*_offsetToFamilyNames != 0 && *_offsetToFamilyNames != Util.nullOffset);
            } 
 
            /// 
            /// Returns a name that uniquely identifies this family. 
            /// The name culture doesn't matter, as the name is supposed to be used only
            /// for FontFamily construction.
            /// 
            ///  
            /// 
            ///     Critical:This code acceses _offsetToFamilyName 
            ///     TreatAsSafe: This information is ok to expose and the pointer it accesses is critical 
            ///     and all creation is tracked
            ///  
            internal string OrdinaryName
            {
                [SecurityCritical,SecurityTreatAsSafe]
                get 
                {
                    return _familyCollection.GetStringFromOffset(*(_offsetToFamilyNames + 1)); 
                } 
            }
 
            #region IDictionary Members

            bool IDictionary.Remove(XmlLanguage key)
            { 
                throw new NotSupportedException();
            } 
 
            void IDictionary.Add(XmlLanguage key, string value)
            { 
                throw new NotSupportedException();
            }

            ICollection IDictionary.Keys 
            {
                get 
                { 
                    // Disable PreSharp warning "Property get methods should not throw exceptions."
                    // Presumably PreSharp is reporting this because the getter allocates memory, but 
                    // it is not unreasable for a getter that returns a collection to allocate a new
                    // object.
#pragma warning disable 6503
                    // OK, this is very slow, but semantically correct. 
                    // Keys are not used that often anyway.
                    XmlLanguage[] keys = new XmlLanguage[_numberOfFamilyNames]; 
                    int i = 0; 
                    foreach (KeyValuePair pair in this)
                    { 
                        keys[i++] = pair.Key;
                    }
                    return keys;
#pragma warning restore 6503 
                }
            } 
 
            string IDictionary.this[XmlLanguage key]
            { 
                get
                {
                    // the number of family name localizations is usually small
                    // if we find important families that behave otherwise 
                    // we can always change the linear algorithm below to binary search
                    // since the list of family names is already sorted by culture name 
 
                    foreach (KeyValuePair pair in this)
                    { 
                        if (pair.Key.Equals(key))
                            return pair.Value;
                    }
                    return null; 
                }
                set 
                { 
                    throw new NotSupportedException();
                } 
            }

            bool IDictionary.TryGetValue(XmlLanguage key, out string value)
            { 
                // the number of family name localizations is usually small
                // if we find important families that behave otherwise 
                // we can always change the linear algorithm below to binary search 
                // since the list of family names is already sorted by culture name
 
                foreach (KeyValuePair pair in this)
                {
                    if (pair.Key.Equals(key))
                    { 
                        value = pair.Value;
                        return true; 
                    } 
                }
                value = null; 
                return false;
            }

            ICollection IDictionary.Values 
            {
                get 
                { 
                    // Disable PreSharp warning "Property get methods should not throw exceptions."
                    // Presumably PreSharp is reporting this because the getter allocates memory, but 
                    // it is not unreasable for a getter that returns a collection to allocate a new
                    // object.
#pragma warning disable 6503
                    // OK, this is very slow, but semantically correct. 
                    // Values are not used that often anyway.
                    string[] values = new string[_numberOfFamilyNames]; 
                    int i = 0; 
                    foreach (KeyValuePair pair in this)
                    { 
                        values[i++] = pair.Value;
                    }
                    return values;
#pragma warning restore 6503 
                }
            } 
 
            bool IDictionary.ContainsKey(XmlLanguage key)
            { 
                // the number of family name localizations is usually small
                // if we find important families that behave otherwise
                // we can always change the linear algorithm below to binary search
                // since the list of family names is already sorted by culture name 

                foreach (KeyValuePair pair in this) 
                { 
                    if (pair.Key.Equals(key))
                        return true; 
                }
                return false;
            }
 
            #endregion
 
            #region ICollection> Members 

            bool ICollection>.Contains(KeyValuePair item) 
            {
                // the number of family name localizations is usually small
                // if we find important families that behave otherwise
                // we can always change the linear algorithm below to binary search 
                // since the list of family names is already sorted by culture name
                string value = item.Value; 
                foreach (KeyValuePair pair in this) 
                {
                    // We don't use KeyValuePair.Equals because it is inherited from ValueType.Equals 
                    // and involves reflection.
                    if (pair.Key.Equals(item.Key) && String.Compare(pair.Value, value, StringComparison.OrdinalIgnoreCase) == 0)
                        return true;
                } 
                return false;
            } 
 
            void ICollection>.Add(KeyValuePair item)
            { 
                throw new NotSupportedException();
            }

            bool ICollection>.Remove(KeyValuePair item) 
            {
                throw new NotSupportedException(); 
            } 

            bool ICollection>.IsReadOnly 
            {
                get
                {
                    return true; 
                }
            } 
 
            void ICollection>.Clear()
            { 
                throw new NotSupportedException();
            }

            int ICollection>.Count 
            {
                get 
                { 
                    return _numberOfFamilyNames;
                } 
            }

            void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
            { 
                int index = arrayIndex;
                foreach (KeyValuePair pair in this) 
                { 
                    array[index] = pair;
                    ++index; 
                }
            }

            #endregion 

            #region IEnumerable> Members 
 
            IEnumerator> IEnumerable>.GetEnumerator()
            { 
                return new Enumerator(this);
            }

            #endregion 

            #region IEnumerable Members 
 
            IEnumerator IEnumerable.GetEnumerator()
            { 
                return new Enumerator(this);
            }

            #endregion 

            private unsafe struct Enumerator : IEnumerator> 
            { 
                private int _currentFamilyName;
                private LocalizedNameDictionary _parent; 

                public Enumerator(LocalizedNameDictionary parent)
                {
                    _parent = parent; 
                    _currentFamilyName = -1;
                } 
 
                private void FailIfOutOfRange()
                { 
                    if (_currentFamilyName < 0)
                        throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));

                    if (_currentFamilyName >= _parent._numberOfFamilyNames) 
                        throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
                } 
                ///  
                ///     Critical:This code acceses _offsetToFamilyNames
                ///     TreatAsSafe: This information is ok to expose and the pointer it accesses is critical 
                ///     and all creation is tracked
                /// 
                [SecurityCritical,SecurityTreatAsSafe]
                private XmlLanguage GetCurrentLanguage() 
                {
                    string languageName = _parent._familyCollection.GetStringFromOffset(*(_parent._offsetToFamilyNames + 2 * _currentFamilyName)); 
                    return XmlLanguage.GetLanguage(languageName); 
                }
 

                /// 
                ///     Critical:This code acceses _offsetToFamilyNames
                ///     TreatAsSafe: This information is ok to expose and the pointer it accesses is critical 
                ///     and all creation is tracked
                ///  
                [SecurityCritical, SecurityTreatAsSafe] 
                private string GetCurrentName()
                { 
                    return _parent._familyCollection.GetStringFromOffset(*(_parent._offsetToFamilyNames + 2 * _currentFamilyName + 1));
                }

                #region IEnumerator> Members 

                public bool MoveNext() 
                { 
                    ++_currentFamilyName;
                    if (_currentFamilyName >= _parent._numberOfFamilyNames) 
                    {
                        // prevent cycling
                        _currentFamilyName = _parent._numberOfFamilyNames;
                        return false; 
                    }
                    return true; 
                } 

                ///  
                ///    Critical: This function acceses _cacher[]
                ///    TreatAsSafe: This information is ok to expose
                /// 
                KeyValuePair IEnumerator>.Current 
                {
                    [SecurityCritical,SecurityTreatAsSafe] 
                    get 
                    {
                        FailIfOutOfRange(); 
                        return new KeyValuePair(GetCurrentLanguage(), GetCurrentName());
                    }
                }
 
                object IEnumerator.Current
                { 
                    get 
                    {
                        return ((IEnumerator>)this).Current; 
                    }
                }

                public void Reset() 
                {
                    _currentFamilyName = -1; 
                } 

                #endregion 

                #region IDisposable Members

                public void Dispose() { } 

                #endregion 
            } 
        }
 
        #endregion Private Types

        //-----------------------------------------------------
        // 
        //  Private Fields
        // 
        //----------------------------------------------------- 

        #region Private Fields 

        private FontSourceCollection    _fontFolder;

        ///  
        /// Critical - this value is used to make security decisions (i.e., whether FontSourceCollection
        ///            does an assert) so it can only be set by critical code. 
        ///  
        [SecurityCritical]
        private bool _isWindowsFonts; 

        /// 
        /// Cached result of calling _fontFolder.GetUriString().
        /// This string is expensive to recompute and is used very frequently. 
        /// 
        ///  
        ///    Critical: This function stores the absolute font Uri, which is critical data. 
        /// 
        [SecurityCritical] 
        private string                  _fontFolderUriString;

        /// 
        ///     Critical: This holds reference to a pointer object 
        /// 
        [SecurityCritical] 
        private unsafe Layout* _data; 
        private ElementCacher _cacher;
 
        #endregion Private Fields
    }
}
 

// 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