CompositeFontInfo.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / MS / Internal / FontFace / CompositeFontInfo.cs / 1305600 / CompositeFontInfo.cs

                            //+------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2003
// 
//  File:      CompositeFontInfo.cs
// 
//  Contents:  Composite font info parsed from composite font file 
//
//  Created:   7-30-2003 Worachai Chaoweeraprasit (wchao) 
//
//-----------------------------------------------------------------------

using System; 
using System.Globalization;
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Markup;
using System.Windows.Media;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace MS.Internal.FontFace 
{
    ///  
    /// Composite font info
    /// 
    internal sealed class CompositeFontInfo
    { 
        private LanguageSpecificStringDictionary    _familyNames;
        private double                              _baseline; 
        private double                              _lineSpacing; 
        private FamilyTypefaceCollection            _familyTypefaces;
        private FontFamilyMapCollection             _familyMaps; 
        private ushort[]                            _defaultFamilyMapRanges;
        private Dictionary   _familyMapRangesByLanguage;

 
        private const int InitialCultureCount = 1;  // at least a familyMap for one locale available
        private const int InitialTargetFamilyCount = 1; 
 

        ///  
        /// Construct a composite font
        /// 
        internal CompositeFontInfo()
        { 
            _familyNames = new LanguageSpecificStringDictionary(new Dictionary(InitialCultureCount));
            _familyMaps = new FontFamilyMapCollection(this); 
            _defaultFamilyMapRanges = EmptyFamilyMapRanges; 
        }
 
        /// 
        /// Called by FontFamilyMapCollection when a FontFamilyMap is being added.
        /// 
        internal void PrepareToAddFamilyMap(FontFamilyMap familyMap) 
        {
            // Validate parameters. 
            if (familyMap == null) 
                throw new ArgumentNullException("familyMap");
 
            if (string.IsNullOrEmpty(familyMap.Target))
                throw new ArgumentException(SR.Get(SRID.FamilyMap_TargetNotSet));

            // If it's culture-specific make sure it's in the hash table. 
            if (familyMap.Language != null)
            { 
                if (_familyMapRangesByLanguage == null) 
                {
                    _familyMapRangesByLanguage = new Dictionary(InitialCultureCount); 
                    _familyMapRangesByLanguage.Add(familyMap.Language, EmptyFamilyMapRanges);
                }
                else if (!_familyMapRangesByLanguage.ContainsKey(familyMap.Language))
                { 
                    _familyMapRangesByLanguage.Add(familyMap.Language, EmptyFamilyMapRanges);
                } 
            } 
        }
 
        #region family map ranges (skip lists)

        /// 
        /// FontFamilyMap ranges (aka. skip lists) are an optimization to speed up family map lookup. 
        ///
        /// OBSERVABLE BEHAVIOR 
        /// 
        ///     The observable behavior of family map lookup should be as if we traverse the
        ///     list of family maps sequentially and return the first one that matches both 
        ///     the text culture and the code point.
        ///
        ///     The language matches if the family map language is null, or is equal to the text
        ///     language, or if the family map language's "range" includes the text culture. 
        ///     This logic is implemented by FontFamilyMap.MatchCulture() and
        ///     FontFamilyMap.MatchLanguage, which call XmlLanguage.RangeIncludes(). 
        /// 
        ///
        /// SKIP LISTS 
        ///
        ///     Skip lists allow us to avoid doing the culture comparisons described above on
        ///     every character lookup. Instead, we generate a skip list once the first time we
        ///     use a particular culture, and then use the skip list to determine which family 
        ///     maps to look at and which to skip.
        /// 
        ///     A skip list is an array of ushort. The first array member represents the size of 
        ///     the family map list and is used to determine whether the skip list is invalid
        ///     (see Invalidating Skip Lists). The remainder of the skip list (beginning at 
        ///     at index FirstFamilyMapRange) consists of pairs of ushort values. Each pair
        ///     denotes a range of family maps in the family maps list; the first member of the
        ///     pair is the index of the first element in the range, and the second is the index
        ///     one past the last element in the range. Collectively, these ranges include all of 
        ///     the family maps that should be included in the lookup for a culture, i.e., the
        ///     culture associated with the skip list. 
        /// 
        ///     Following is an example of a family map list and the corresponding skip lists:
        /// 
        ///     0       1       2       3       4       5       6       7       8
        ///     +-------+-------+-------+-------+-------+-------+-------+-------+
        ///     |   ja  |   ja  |   ko  |   ko  | zh-CHT| zh-CHS|  any  |  any  |
        ///     +-------+-------+-------+-------+-------+-------+-------+-------+ 
        ///
        ///     "ja"     -> (0,2) (6,8) 
        ///     "ko"     -> (2,4) (6,8) 
        ///     "zh-CHT" -> (4,5) (6,8)
        ///     "zh-CHS" -> (5,8) 
        ///     default  -> (6,8)
        ///
        /// INVALIDATING SKIP LISTS
        /// 
        ///     A skip list becomes invalid whenever the family map list changes. To avoid
        ///     recreating skip lists every time a family map is added, skip lists are created 
        ///     lazily. Skip lists are added to the _familyMapRangesByLanguage hash table as 
        ///     family maps are added, but each skip list is initialized by EmptyFamilyMapRanges.
        /// 
        ///     After a skip list has been created, a may be rendered invalid by subsequent
        ///     changes to the family map list. We have two mechanisms to detect this.
        ///
        ///       (1)  Each skip list includes (as its first member) the size of the family 
        ///            map when the skip list was created. Additions to or insertions into
        ///            the list can therefore be detected because the sizes no longer match. 
        /// 
        ///       (2)  For all other changes (removing items changing items), the FamilyMaps
        ///            list calls InvalidateFamilyMapRanges(), which setes all skip lists to 
        ///            EmptyFamilyMapRanges.
        ///
        /// 
 
        private static readonly ushort[] EmptyFamilyMapRanges = new ushort[] { 0 };
        private const int InitialFamilyMapRangesCapacity = 7; // count + 3 ranges 
        internal const int FirstFamilyMapRange = 1; 

        ///  
        /// Called by FontFamilyMapCollection when a change occurs that renders all
        /// family map ranges potentially invalid.
        /// 
        internal void InvalidateFamilyMapRanges() 
        {
            _defaultFamilyMapRanges = EmptyFamilyMapRanges; 
 
            if (_familyMapRangesByLanguage != null)
            { 
                Dictionary table = new Dictionary(_familyMapRangesByLanguage.Count);
                foreach (XmlLanguage language in _familyMapRangesByLanguage.Keys)
                {
                    table.Add(language, EmptyFamilyMapRanges); 
                }
                _familyMapRangesByLanguage = table; 
            } 
        }
 
        /// 
        /// Returns information about which family maps apply to the specified culture.
        /// The return value is used by GetFamilyMapOfChar.
        ///  
        internal ushort[] GetFamilyMapsOfLanguage(XmlLanguage language)
        { 
            ushort[] ranges = null; 

            // Look for a family map range for the specified language or one of its matching languages 
            if (_familyMapRangesByLanguage != null && language != null)
            {
                foreach (XmlLanguage matchingLanguage in language.MatchingLanguages)
                { 
                    // break out of loop to handle default list of ranges
                    if (matchingLanguage.IetfLanguageTag.Length == 0) 
                        break; 

                    if (_familyMapRangesByLanguage.TryGetValue(matchingLanguage, out ranges)) 
                    {
                        // Recreate the list of ranges if we've added more family maps.
                        if (!IsFamilyMapRangesValid(ranges))
                        { 
                            ranges = CreateFamilyMapRanges(matchingLanguage);
                            _familyMapRangesByLanguage[matchingLanguage] = ranges; 
                        } 
                        return ranges;
                    } 
                }
            }

            // Use the default list of ranges (containing only family maps that match 
            // any culture); recreate it if we've added more family maps.
            if (!IsFamilyMapRangesValid(_defaultFamilyMapRanges)) 
            { 
                _defaultFamilyMapRanges = CreateFamilyMapRanges(null);
            } 

            return _defaultFamilyMapRanges;
        }
 
        /// 
        /// Gets the first FontFamilyMap that matches the specified Unicode scalar value. 
        ///  
        /// Return value of GetFamilyMapsOfCulture.
        /// Character to map. 
        /// FontFamilyMap or null.
        internal FontFamilyMap GetFamilyMapOfChar(ushort[] familyMapRanges, int ch)
        {
            Debug.Assert(IsFamilyMapRangesValid(familyMapRanges)); 

            // Iterate over the ushort pairs in the skip list. 
            for (int i = FirstFamilyMapRange; i < familyMapRanges.Length; i += 2) 
            {
                // Each pair specifies a range in the family map list. 
                int begin = familyMapRanges[i];
                int end = familyMapRanges[i + 1];
                Debug.Assert(begin < end && end <= _familyMaps.Count);
 
                // Iterate over the family maps in the specified range.
                for (int j = begin; j < end; ++j) 
                { 
                    FontFamilyMap familyMap = _familyMaps[j];
                    Invariant.Assert(familyMap != null); 
                    if (familyMap.InRange(ch))
                        return familyMap;
                }
            } 

            return FontFamilyMap.Default; 
        } 

        private bool IsFamilyMapRangesValid(ushort[] familyMapRanges) 
        {
            return familyMapRanges[0] == _familyMaps.Count;
        }
 
        private ushort[] CreateFamilyMapRanges(XmlLanguage language)
        { 
            // We could use an ArrayList, but a ushort[] is not much more code 
            // and requires many fewer boxed objects.
            ushort[] ranges = new ushort[InitialFamilyMapRangesCapacity]; 
            ranges[0] = (ushort)_familyMaps.Count;
            int count = 1;

            Debug.Assert(count == FirstFamilyMapRange); 

            for (int i = 0; i < _familyMaps.Count; ++i) 
            { 
                if (FontFamilyMap.MatchLanguage(_familyMaps[i].Language, language))
                { 
                    // grow ranges if necessary.
                    if (count + 2 > ranges.Length)
                    {
                        ushort[] temp = new ushort[ranges.Length * 2 - FirstFamilyMapRange]; 
                        ranges.CopyTo(temp, 0);
                        ranges = temp; 
                    } 

                    // beginning of range 
                    ranges[count++] = (ushort)i;

                    ++i;
                    while (i < _familyMaps.Count && FontFamilyMap.MatchLanguage(_familyMaps[i].Language, language)) 
                    {
                        ++i; 
                    } 

                    // end of range, i.e., last index + 1 
                    ranges[count++] = (ushort)i;
                }
            }
 
            // reallocate ranges to the exact size required
            if (count < ranges.Length) 
            { 
                ushort[] temp = new ushort[count];
                Array.Copy(ranges, temp, count); 
                ranges = temp;
            }

            return ranges; 
        }
 
        #endregion 

        ///  
        /// List of typefaces; can be null.
        /// 
        internal FamilyTypefaceCollection FamilyTypefaces
        { 
            get { return _familyTypefaces; }
        } 
 
        /// 
        /// Gets the list of family typefaces, creating it if necessary. 
        /// 
        internal FamilyTypefaceCollection GetFamilyTypefaceList()
        {
            if (_familyTypefaces == null) 
                _familyTypefaces = new FamilyTypefaceCollection();
 
            return _familyTypefaces; 
        }
 
        /// 
        /// Distance from character cell top to English baseline relative to em size.
        /// 
        internal double Baseline 
        {
            get { return _baseline; } 
            set 
            {
                CompositeFontParser.VerifyNonNegativeMultiplierOfEm("Baseline", ref value); 
                _baseline = value;
            }
        }
 

        ///  
        /// Additional line spacing after Height relative to em size 
        /// 
        internal double LineSpacing 
        {
            get { return _lineSpacing; }
            set
            { 
                CompositeFontParser.VerifyPositiveMultiplierOfEm("LineSpacing", ref value);
                _lineSpacing = value; 
            } 
        }
 

        /// 
        /// Dictionary of names by culture.
        ///  
        internal LanguageSpecificStringDictionary FamilyNames
        { 
            get { return _familyNames; } 
        }
 

        /// 
        /// List of family maps.
        ///  
        internal FontFamilyMapCollection FamilyMaps
        { 
            get { return _familyMaps; } 
        }
 

        /// 
        /// Collection of cultures associated with family maps; can be null
        /// if all family maps are culture-independent. 
        /// 
        internal ICollection FamilyMapLanguages 
        { 
            get
            { 
                if (_familyMapRangesByLanguage != null)
                    return _familyMapRangesByLanguage.Keys;
                else
                    return null; 
            }
        } 
    } 
}

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