glyphs.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / glyphs.cs / 1305600 / glyphs.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: Glyphs.cs 
//
// Description: Glyphs element for fixed text rendering. 
// 
// Spec: http://avalon/text/DesignDocsAndSpecs/Glyphs%20element%20and%20GlyphRun%20object.htm
// 
// History:
//  06/02/2003 : mleonov - created.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization; 
using System.IO;
using System.Windows.Threading;

 
using System.Windows;
using System.Windows.Media; 
using System.Windows.Navigation; 
using System.Windows.Markup;
using System.ComponentModel; 
using System.Security;

using MS.Utility;
using MS.Internal.Navigation; 
using MS.Internal.Utility;
using MS.Internal; 
 
namespace System.Windows.Documents
{ 
    /// 
    /// Glyphs shape represents GlyphRun in markup
    /// 
    public sealed class Glyphs : FrameworkElement, IUriContext 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        /// 
        /// Constructor 
        ///  
        public Glyphs()
        { 
        }

        #endregion Constructors
 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //-----------------------------------------------------

        #region Public Methods
 

        ///  
        /// Creates a GlyphRun object from the properties on a Glyphs object. 
        /// 
        /// GlyphRun object that corresponds to the properties set on this Glyphs object. 
        public GlyphRun ToGlyphRun()
        {
            ComputeMeasurementGlyphRunAndOrigin();
            if (_measurementGlyphRun == null) 
                return null;
            Debug.Assert(_glyphRunProperties != null); 
 
            return _measurementGlyphRun;
        } 

        #endregion Public Methods

 
        #region IUriContext implementation
 
        ///  
        /// IUriContext interface is implemented by Glyphs element so that it
        /// can hold on to the base URI used by parser. 
        /// The base URI is needed to resolve FontUri property.
        /// 
        /// Base Uri
        Uri IUriContext.BaseUri 
        {
            get 
            { 
                return (Uri)GetValue(BaseUriHelper.BaseUriProperty);
            } 
            set
            {
                SetValue(BaseUriHelper.BaseUriProperty, value);
            } 
        }
        #endregion IUriContext implementation 
 
        #region Layout and rendering
 
        /// 
        /// ArrangeOverride sets the "shapeBounds" in for the shape.
        /// 
        protected override Size ArrangeOverride(Size finalSize) 
        {
            base.ArrangeOverride(finalSize); 
 
            Rect inkBoundingBox;
 
            if (_measurementGlyphRun != null)
                inkBoundingBox = _measurementGlyphRun.ComputeInkBoundingBox();
            else
                inkBoundingBox = Rect.Empty; 

            if (!inkBoundingBox.IsEmpty) 
            { 
                inkBoundingBox.X += _glyphRunOrigin.X;
                inkBoundingBox.Y += _glyphRunOrigin.Y; 
            }
            return finalSize;
        }
 
        /// 
        /// Renders GlyphRun into a drawing context 
        ///  
        /// Drawing context
        protected override void OnRender(DrawingContext context) 
        {
            if (_glyphRunProperties == null || _measurementGlyphRun == null)
                return;
 
            context.PushGuidelineY1(_glyphRunOrigin.Y);
            try 
            { 
                context.DrawGlyphRun(Fill, _measurementGlyphRun);
            } 
            finally
            {
                context.Pop();
            } 
        }
 
        ///  
        /// Measurement override for Glyphs
        ///  
        /// Input constraint
        /// 
        protected override Size MeasureOverride(Size constraint)
        { 
            ComputeMeasurementGlyphRunAndOrigin();
 
            if (_measurementGlyphRun == null) 
                return new Size();
 
            Rect designRect = _measurementGlyphRun.ComputeAlignmentBox();

            designRect.Offset(_glyphRunOrigin.X, _glyphRunOrigin.Y);
 
            return new Size(
                Math.Max(0, designRect.Right), 
                Math.Max(0, designRect.Bottom) 
            );
        } 

        #endregion Layout and rendering

        #region Parsing and GlyphRun creation 

        private void ComputeMeasurementGlyphRunAndOrigin() 
        { 
            if (_glyphRunProperties == null)
            { 
                _measurementGlyphRun = null;
                ParseGlyphRunProperties();

                if (_glyphRunProperties == null) 
                {
                    return; 
                } 
            }
            else if (_measurementGlyphRun != null) 
            {
                return;
            }
 
            bool leftToRight = ((BidiLevel & 1) == 0);
 
            bool haveOriginX = !DoubleUtil.IsNaN(OriginX); 
            bool haveOriginY = !DoubleUtil.IsNaN(OriginY);
 
            bool measurementGlyphRunOriginValid = false;

            Rect alignmentRect = new Rect();
            if (haveOriginX && haveOriginY && leftToRight) 
            {
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(new Point(OriginX,OriginY), Language); 
                measurementGlyphRunOriginValid = true; 
            }
            else 
            {
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(new Point(), Language);
                // compute alignment box for origins
                alignmentRect = _measurementGlyphRun.ComputeAlignmentBox(); 
            }
 
            if (haveOriginX) 
                _glyphRunOrigin.X = OriginX;
            else 
                _glyphRunOrigin.X = leftToRight ? 0 : alignmentRect.Width;

            if (haveOriginY)
                _glyphRunOrigin.Y = OriginY; 
            else
                _glyphRunOrigin.Y = -alignmentRect.Y; 
 
            if (!measurementGlyphRunOriginValid)
            { 
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(_glyphRunOrigin, Language);
            }
        }
 
        private void ParseCaretStops(LayoutDependentGlyphRunProperties glyphRunProperties)
        { 
            string caretStopsString = CaretStops; 
            if (String.IsNullOrEmpty(caretStopsString))
            { 
                glyphRunProperties.caretStops = null;
                return;
            }
 
            // Caret stop count should be equal to the number of UTF16 code points in the glyph run plus one.
            // Logic below is similar to GlyphRun.CodepointCount property. 
 
            int caretStopCount;
 
            if (!String.IsNullOrEmpty(glyphRunProperties.unicodeString))
                caretStopCount = glyphRunProperties.unicodeString.Length + 1;
            else
            { 
                if (glyphRunProperties.clusterMap != null && glyphRunProperties.clusterMap.Length != 0)
                    caretStopCount = glyphRunProperties.clusterMap.Length + 1; 
                else 
                {
                    Debug.Assert(glyphRunProperties.glyphIndices != null); 
                    caretStopCount = glyphRunProperties.glyphIndices.Length + 1;
                }
            }
 
            bool[] caretStops = new bool[caretStopCount];
 
            int i = 0; 
            foreach (char c in caretStopsString)
            { 
                if (Char.IsWhiteSpace(c))
                    continue;

                int nibble; 

                if ('0' <= c && c <= '9') 
                    nibble = c - '0'; 
                else if ('a' <= c && c <= 'f')
                    nibble = c - 'a' + 10; 
                else if ('A' <= c && c <= 'F')
                    nibble = c - 'A' + 10;
                else
                    throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsContainsHexDigits), "CaretStops"); 

                Debug.Assert(0 <= nibble && nibble <= 15); 
 
                if ((nibble & 8) != 0)
                { 
                    if (i >= caretStops.Length)
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops");
                    caretStops[i] = true;
                } 
                ++i;
                if ((nibble & 4) != 0) 
                { 
                    if (i >= caretStops.Length)
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops"); 
                    caretStops[i] = true;
                }
                ++i;
                if ((nibble & 2) != 0) 
                {
                    if (i >= caretStops.Length) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops"); 
                    caretStops[i] = true;
                } 
                ++i;
                if ((nibble & 1) != 0)
                {
                    if (i >= caretStops.Length) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops");
                    caretStops[i] = true; 
                } 
                ++i;
            } 

            // If the number of entries in the caret stop specification string is less than the number of code points,
            // set the remaining caret stop values to true.
            while (i < caretStops.Length) 
            {
                caretStops[i++] = true; 
            } 
            glyphRunProperties.caretStops = caretStops;
        } 

        ///
        /// Critical as it accesses the base Uri
        /// TreatAsSafe as it only uses this to load glyphtypefaces, and this information is not disclosed. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        private void ParseGlyphRunProperties() 
        {
            LayoutDependentGlyphRunProperties glyphRunProperties = null; 
            Uri uri = FontUri;

            if (uri != null)
            { 
                // Indices and UnicodeString cannot both be empty.
                if (String.IsNullOrEmpty(UnicodeString) && String.IsNullOrEmpty(Indices)) 
                    throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringAndIndicesCannotBothBeEmpty)); 

                glyphRunProperties = new LayoutDependentGlyphRunProperties(); 

                if (!uri.IsAbsoluteUri)
                {
                    uri = BindUriHelper.GetResolvedUri(BaseUriHelper.GetBaseUri(this), uri); 
                }
 
                glyphRunProperties.glyphTypeface = new GlyphTypeface(uri, StyleSimulations); 

                glyphRunProperties.unicodeString = UnicodeString; 
                glyphRunProperties.sideways = IsSideways;
                glyphRunProperties.deviceFontName = DeviceFontName;

                // parse the Indices property 
                List parsedGlyphs;
                int glyphCount = ParseGlyphsProperty( 
                    glyphRunProperties.glyphTypeface, 
                    glyphRunProperties.unicodeString,
                    glyphRunProperties.sideways, 
                    out parsedGlyphs,
                    out glyphRunProperties.clusterMap);

                Debug.Assert(parsedGlyphs.Count == glyphCount); 

                glyphRunProperties.glyphIndices = new ushort[glyphCount]; 
                glyphRunProperties.advanceWidths = new double[glyphCount]; 

                ParseCaretStops(glyphRunProperties); 

                // Delay creating glyphOffsets array because in many common cases it will contain only zeroed entries.
                glyphRunProperties.glyphOffsets = null;
 
                int i = 0;
 
                glyphRunProperties.fontRenderingSize = FontRenderingEmSize; 
                glyphRunProperties.bidiLevel = BidiLevel;
 
                double fromEmToMil = glyphRunProperties.fontRenderingSize / EmMultiplier;

                foreach (ParsedGlyphData parsedGlyphData in parsedGlyphs)
                { 
                    glyphRunProperties.glyphIndices[i] = parsedGlyphData.glyphIndex;
 
                    // convert advances and offsets from integers in em space to doubles coordinates in MIL space 
                    glyphRunProperties.advanceWidths[i] = parsedGlyphData.advanceWidth * fromEmToMil;
 
                    if (parsedGlyphData.offsetX != 0 || parsedGlyphData.offsetY != 0)
                    {
                        // Lazily create glyph offset array. Previous entries will be correctly set to zero
                        // by the default Point ctor. 
                        if (glyphRunProperties.glyphOffsets == null)
                            glyphRunProperties.glyphOffsets = new Point[glyphCount]; 
 
                        glyphRunProperties.glyphOffsets[i].X = parsedGlyphData.offsetX * fromEmToMil;
                        glyphRunProperties.glyphOffsets[i].Y = parsedGlyphData.offsetY * fromEmToMil; 
                    }

                    ++i;
                } 
            }
            _glyphRunProperties = glyphRunProperties; 
        } 

        private static bool IsEmpty(string s) 
        {
            foreach (char c in s)
            {
                if (!Char.IsWhiteSpace(c)) 
                    return false;
            } 
            return true; 
        }
 
        /// 
        /// Read GlyphIndex specification - glyph index value with an optional glyph cluster prefix.
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        /// true if glyph index is present, false if glyph index is not present. 
        private bool ReadGlyphIndex(
            string      valueSpec,
            ref bool    inCluster,
            ref int     glyphClusterSize, 
            ref int     characterClusterSize,
            ref ushort  glyphIndex) 
        { 
            // the format is ... [(CharacterClusterSize[:GlyphClusterSize])] GlyphIndex ...
            string glyphIndexString = valueSpec; 

            int firstBracket = valueSpec.IndexOf('(');
            if (firstBracket != -1)
            { 
                // Only spaces are allowed before the bracket
                for (int i=0; i= secondBracket - 1)
                        throw new ArgumentException(SR.Get(SRID.GlyphsClusterMisplacedSeparator)); 
                    string characterClusterSpec = valueSpec.Substring(firstBracket + 1, colon - (firstBracket + 1));
                    characterClusterSize = int.Parse(characterClusterSpec, CultureInfo.InvariantCulture); 
                    string glyphClusterSpec = valueSpec.Substring(colon + 1, secondBracket - (colon + 1)); 
                    glyphClusterSize = int.Parse(glyphClusterSpec, CultureInfo.InvariantCulture);
                } 
                inCluster = true;
                glyphIndexString = valueSpec.Substring(secondBracket + 1);
            }
            if (IsEmpty(glyphIndexString)) 
                return false;
 
            glyphIndex = ushort.Parse(glyphIndexString, CultureInfo.InvariantCulture); 
            return true;
        } 

        private static double GetAdvanceWidth(GlyphTypeface glyphTypeface, ushort glyphIndex, bool sideways)
        {
            double advance = sideways ? glyphTypeface.AdvanceHeights[glyphIndex] : glyphTypeface.AdvanceWidths[glyphIndex]; 
            return advance * EmMultiplier;
        } 
 
        private ushort GetGlyphFromCharacter(GlyphTypeface glyphTypeface, char character)
        { 
            ushort glyphIndex;
            // TryGetValue will return zero glyph index for missing code points,
            // which is the right thing to display per http://www.microsoft.com/typography/otspec/cmap.htm
            glyphTypeface.CharacterToGlyphMap.TryGetValue(character, out glyphIndex); 
            return glyphIndex;
        } 
 
        /// 
        /// Performs validation against cluster map size and throws a well defined exception. 
        /// 
        private static void SetClusterMapEntry(ushort[] clusterMap, int index, ushort value)
        {
            if (index < 0 || index >= clusterMap.Length) 
                throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort));
            clusterMap[index] = value; 
        } 

        private class ParsedGlyphData 
        {
            public ushort   glyphIndex;
            public double   advanceWidth;
            public double   offsetX; 
            public double   offsetY;
        }; 
 
        // ------------------------------------------------------------------------------
        // Parses a semicolon-delimited list of glyph specifiers, each of which consists 
        // of up to 4 comma-delimited values:
        //   - glyph index (ushort)
        //   - glyph advance (double)
        //   - glyph offset X (double) 
        //   - glyph offset Y (double)
        // A glyph entry can be have a cluster size prefix (int or pair of ints separated by a colon) 
        // Whitespace adjacent to a delimiter (comma or semicolon) is ignored. 
        // Returns the number of glyph specs parsed (number of semicolons plus 1).
        // 


        private int ParseGlyphsProperty(
            GlyphTypeface               fontFace, 
            string                      unicodeString,
            bool                        sideways, 
            out List   parsedGlyphs, 
            out ushort[]                clusterMap)
        { 
            string glyphsProp = Indices;

            // init for the whole parse, including the result arrays
            int parsedGlyphCount = 0; 
            int parsedCharacterCount = 0;
 
            int characterClusterSize = 1; 
            int glyphClusterSize = 1;
 
            bool inCluster = false;

            // make reasonable capacity guess on how many glyphs we can expect
            int estimatedNumberOfGlyphs; 

            if (!String.IsNullOrEmpty(unicodeString)) 
            { 
                clusterMap = new ushort[unicodeString.Length];
                estimatedNumberOfGlyphs = unicodeString.Length; 
            }
            else
            {
                clusterMap = null; 
                estimatedNumberOfGlyphs = 8;
            } 
 
            if (!String.IsNullOrEmpty(glyphsProp))
                estimatedNumberOfGlyphs = Math.Max(estimatedNumberOfGlyphs, glyphsProp.Length / 5); 

            parsedGlyphs = new List(estimatedNumberOfGlyphs);

            ParsedGlyphData parsedGlyphData = new ParsedGlyphData(); 

            #region Parse Glyphs string 
            if (!String.IsNullOrEmpty(glyphsProp)) 
            {
                // init per-glyph values for the first glyph/position 
                int valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)?
                int valueStartIndex = 0; // where (what index of Glyphs prop string) did this value start?

                // iterate and parse the characters of the Indices property 
                for (int i = 0; i <= glyphsProp.Length; i++)
                { 
                    // get next char or pseudo-terminator 
                    char c = i < glyphsProp.Length ? glyphsProp[i] : '\0';
 
                    // finished scanning the current per-glyph value?
                    if ((c == ',') || (c == ';') || (i == glyphsProp.Length))
                    {
                        int len = i - valueStartIndex; 

                        string valueSpec = glyphsProp.Substring(valueStartIndex, len); 
 
                        #region Interpret one comma-delimited value
 
                        switch (valueWithinGlyph)
                        {
                            case 0:
                                bool wasInCluster = inCluster; 
                                // interpret cluster size and glyph index spec
                                if (!ReadGlyphIndex( 
                                    valueSpec, 
                                    ref inCluster,
                                    ref glyphClusterSize, 
                                    ref characterClusterSize,
                                    ref parsedGlyphData.glyphIndex))
                                {
                                    if (String.IsNullOrEmpty(unicodeString)) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredIfNoUnicode));
 
                                    if (unicodeString.Length <= parsedCharacterCount) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort));
 
                                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]);
                                }

                                if (!wasInCluster && clusterMap != null) 
                                {
                                    // fill out cluster map at the start of each cluster 
                                    if (inCluster) 
                                    {
                                        for (int ch = parsedCharacterCount; ch < parsedCharacterCount + characterClusterSize; ++ch) 
                                        {
                                            SetClusterMapEntry(clusterMap, ch, (ushort)parsedGlyphCount);
                                        }
                                    } 
                                    else
                                    { 
                                        SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount); 
                                    }
                                } 
                                parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways);
                                break;

                            case 1: 
                                // interpret glyph advance spec
                                if (!IsEmpty(valueSpec)) 
                                { 
                                    parsedGlyphData.advanceWidth = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                    if (parsedGlyphData.advanceWidth < 0) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsAdvanceWidthCannotBeNegative));
                                }
                                break;
 
                            case 2:
                                // interpret glyph offset X 
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetX = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 

                            case 3:
                                // interpret glyph offset Y
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetY = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 
 
                            default:
                                // too many commas; can't interpret 
                                throw new ArgumentException(SR.Get(SRID.GlyphsTooManyCommas));
                        }
                        #endregion Interpret one comma-delimited value
 
                        // prepare to scan next value (if any)
                        valueWithinGlyph++; 
                        valueStartIndex = i + 1; 
                    }
 
                    // finished processing the current glyph?
                    if ((c == ';') || (i == glyphsProp.Length))
                    {
                        parsedGlyphs.Add(parsedGlyphData); 
                        parsedGlyphData = new ParsedGlyphData();
 
                        if (inCluster) 
                        {
                            --glyphClusterSize; 
                            // when we reach the end of a glyph cluster, increment character index
                            if (glyphClusterSize == 0)
                            {
                                parsedCharacterCount += characterClusterSize; 
                                inCluster = false;
                            } 
                        } 
                        else
                        { 
                            ++parsedCharacterCount;
                        }
                        parsedGlyphCount++;
 
                        // initalize new per-glyph values
                        valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)? 
                        valueStartIndex = i + 1; // where (what index of Glyphs prop string) did this value start? 
                    }
                } 
            }
            #endregion

            // fill the remaining glyphs with defaults, assuming 1:1 mapping 
            if (unicodeString != null)
            { 
                while (parsedCharacterCount < unicodeString.Length) 
                {
                    if (inCluster) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredWithinCluster));

                    if (unicodeString.Length <= parsedCharacterCount)
                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort)); 

                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]); 
                    parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways); 
                    parsedGlyphs.Add(parsedGlyphData);
                    parsedGlyphData = new ParsedGlyphData(); 
                    SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount);
                    ++parsedCharacterCount;
                    ++parsedGlyphCount;
                } 
            }
 
            // return number of glyphs actually specified 
            return parsedGlyphCount;
        } 
        #endregion Parsing and GlyphRun creation

        //------------------------------------------------------
        // 
        //  Public Properties
        // 
        //----------------------------------------------------- 

        #region Public Properties 

        private static void FillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Called when Fill is changed. 
            // If a SubPropertyInvalidatin is in progress this means a Freezable
            // has changed and we don't need to invalidate layout.  Otherwise 
            // we have to invalidate 

           ((UIElement)d).InvalidateVisual(); 
        }

        private static void GlyphRunPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            // Called when any property is changed that would require a new call to ParseGlyphRunProperties
 
            ((Glyphs)d)._glyphRunProperties = null; 
        }
 
        private static void OriginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Called when OriginX or OriginY is changed that would require recreation of the positioned GlyphRun
            // The _measurementGlyphRun will get updated as a result of layout 
            ((Glyphs)d)._measurementGlyphRun = null;
        } 
 
        /// 
        /// Fill property 
        /// 
        public static readonly DependencyProperty FillProperty
            = DependencyProperty.Register(
                "Fill", 
                typeof(Brush),
                typeof(Glyphs), 
                new FrameworkPropertyMetadata( 
                    (Brush)null,
                    FrameworkPropertyMetadataOptions.None, 
                    new PropertyChangedCallback(FillChanged),
                    null)
                );
 
        /// 
        /// Fill property 
        ///  
        public Brush Fill
        { 
            get
            {
                return (Brush)GetValue(FillProperty);
            } 
            set
            { 
                SetValue(FillProperty, value); 
            }
        } 

        /// 
        /// Indices property
        ///  
        public static readonly DependencyProperty IndicesProperty =
            DependencyProperty.Register( "Indices", typeof(string), typeof(Glyphs), 
                new FrameworkPropertyMetadata(string.Empty, 
                                              FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                              new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// Indices property accessor
        ///  
        public string Indices
        { 
            get 
            {
                return (string)GetValue(IndicesProperty); 
            }
            set
            {
                SetValue(IndicesProperty, value); 
            }
        } 
 
        /// 
        /// UnicodeString property 
        /// 
        public static readonly DependencyProperty UnicodeStringProperty =
            DependencyProperty.Register( "UnicodeString", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        ///  
        /// UnicodeString property accessor 
        /// 
        public string UnicodeString 
        {
            get
            {
                return (string)GetValue(UnicodeStringProperty);; 
            }
            set 
            { 
                SetValue(UnicodeStringProperty, value);
            } 
        }

        /// 
        /// CaretStops property. The property syntax is a string of hexadecimal digits that describe an array of Boolean values that 
        /// correspond to every code point in UnicodeString property.
        ///  
        public static readonly DependencyProperty CaretStopsProperty = 
            DependencyProperty.Register( "CaretStops", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// CaretStops property accessor
        ///  
        public string CaretStops
        { 
            get 
            {
                return (string)GetValue(CaretStopsProperty);; 
            }
            set
            {
                SetValue(CaretStopsProperty, value); 
            }
        } 
 
        /// 
        /// FontRenderingEmSize property 
        /// 
        public static readonly DependencyProperty FontRenderingEmSizeProperty =
            DependencyProperty.Register( "FontRenderingEmSize", typeof(double), typeof(Glyphs),
                new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        ///  
        /// FontRenderingEmSize property accessor 
        /// 
        [TypeConverter("System.Windows.FontSizeConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")] 
        public double FontRenderingEmSize
        {
            get
            { 
                return (double)GetValue(FontRenderingEmSizeProperty);
            } 
            set 
            {
                SetValue(FontRenderingEmSizeProperty, value); 
            }
        }

        ///  
        /// OriginX property
        ///  
        public static readonly DependencyProperty OriginXProperty = 
            DependencyProperty.Register( "OriginX", typeof(double), typeof(Glyphs),
                new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OriginPropertyChanged))); 

        /// 
        /// OriginX property accessor
        ///  
        [TypeConverter("System.Windows.LengthConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")]
        public double OriginX 
        { 
            get
            { 
                return (double)GetValue(OriginXProperty);
            }
            set
            { 
                SetValue(OriginXProperty, value);
            } 
        } 

        ///  
        /// OriginY property
        /// 
        public static readonly DependencyProperty OriginYProperty =
            DependencyProperty.Register( "OriginY", typeof(double), typeof(Glyphs), 
                new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OriginPropertyChanged)));
 
        ///  
        /// OriginY property accessor
        ///  
        [TypeConverter("System.Windows.LengthConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")]
        public double OriginY
        {
            get 
            {
                return (double)GetValue(OriginYProperty); 
            } 
            set
            { 
                SetValue(OriginYProperty, value);
            }
        }
 
        /// 
        /// FontUri property 
        ///  
        public static readonly DependencyProperty FontUriProperty =
            DependencyProperty.Register( "FontUri", typeof(Uri), typeof(Glyphs), 
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));

        /// 
        /// FontUri property accessor 
        /// 
        public Uri FontUri 
        { 
            get
            { 
                return (Uri)GetValue(FontUriProperty);
            }
            set
            { 
                SetValue(FontUriProperty, value);
            } 
        } 

        ///  
        /// StyleSimulations property
        /// 
        public static readonly DependencyProperty StyleSimulationsProperty =
            DependencyProperty.Register( "StyleSimulations", typeof(StyleSimulations), typeof(Glyphs), 
                new FrameworkPropertyMetadata(StyleSimulations.None, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));
 
        ///  
        /// StyleSimulations property accessor
        ///  
        public StyleSimulations StyleSimulations
        {
            get
            { 
                return (StyleSimulations)GetValue(StyleSimulationsProperty);
            } 
            set 
            {
                SetValue(StyleSimulationsProperty, value); 
            }
        }

        ///  
        /// Sideways property
        ///  
        public static readonly DependencyProperty IsSidewaysProperty = 
            DependencyProperty.Register( "IsSideways", typeof(bool), typeof(Glyphs),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// Specifies whether to rotate characters/glyphs 90 degrees anti-clockwise
        /// and use vertical baseline positioning metrics. 
        /// 
        /// true if the rotation should be applied, false otherwise. 
        public bool  IsSideways 
        {
            get 
            {
                return (bool)GetValue(IsSidewaysProperty);
            }
            set 
            {
                SetValue(IsSidewaysProperty, value); 
            } 
        }
 
        /// 
        /// BidiLevel property
        /// 
        public static readonly DependencyProperty BidiLevelProperty = 
            DependencyProperty.Register( "BidiLevel", typeof(int), typeof(Glyphs),
                new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 
 
        /// 
        /// Determines LTR/RTL reading order and bidi nesting. 
        /// 
        /// The value of bidirectional nesting level.
        public int BidiLevel
        { 
            get
            { 
                return (int)GetValue(BidiLevelProperty); 
            }
            set 
            {
                SetValue(BidiLevelProperty, value);
            }
        } 

        ///  
        /// DeviceFontName property 
        /// 
        public static readonly DependencyProperty DeviceFontNameProperty = 
            DependencyProperty.Register("DeviceFontName", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));

        ///  
        /// Identifies a specific device font for which the Glyphs element has been optimized. When a Glyphs element is
        /// being rendered on a device that has built-in support for this named font, then the Glyphs element should be rendered using a 
        /// possibly device specific mechanism for selecting that font, and by sending the Unicode codepoints rather than the 
        /// glyph indices. When rendering onto a device that does not include built-in support for the named font,
        /// this property should be ignored. 
        /// 
        public string DeviceFontName
        {
            get 
            {
                return (string) GetValue(DeviceFontNameProperty); 
            } 
            set
            { 
                SetValue(DeviceFontNameProperty, value);
            }
        }
 
        #endregion Public Properties
 
        //------------------------------------------------------ 
        //
        //  Internal Properties 
        //
        //-----------------------------------------------------

        #region Internal Properties 

        ///  
        /// This property was added for performance reasons.  It allows D2 code 
        /// to access the cached measurement glyph run instead of generating
        /// a new GlyphRun object by calling ToGlyphRun() 
        /// 
        internal GlyphRun MeasurementGlyphRun
        {
            get 
            {
                if (_glyphRunProperties == null || _measurementGlyphRun == null) 
                { 
                    ComputeMeasurementGlyphRunAndOrigin();
                } 
                return _measurementGlyphRun;
            }
        }
        #endregion Internal Properties 

        //----------------------------------------------------- 
        // 
        //  Private Classes
        // 
        //-----------------------------------------------------

        #region Private Classes
 
        /// 
        /// This class is temporarily needed because GlyphRun includes rendering information 
        /// that in future will be passed to DrawGlyphs separately. 
        /// 
        private class LayoutDependentGlyphRunProperties 
        {
            public double           fontRenderingSize;
            public ushort []        glyphIndices;
            public double []        advanceWidths; 
            public Point []         glyphOffsets;
            public ushort []        clusterMap; 
            public bool             sideways; 
            public int              bidiLevel;
            public GlyphTypeface    glyphTypeface; 
            public string           unicodeString;
            public IList      caretStops;
            public string           deviceFontName;
 
            public GlyphRun CreateGlyphRun(Point origin, XmlLanguage language)
            { 
                return new GlyphRun( 
                    glyphTypeface,               // GlyphTypeface
                    bidiLevel,                   // Bidi level 
                    sideways,                    // sideways flag
                    fontRenderingSize,           // rendering em size in MIL units
                    glyphIndices,                // glyph indices
                    origin,                      // origin of glyph-drawing space 
                    advanceWidths,               // glyph advances
                    glyphOffsets,                // glyph offsets 
                    unicodeString.ToCharArray(), // unicode characters 
                    deviceFontName,              // device font
                    clusterMap,                  // cluster map 
                    caretStops,                  // caret stops
                    language                     // language
                );
            } 
        }
 
        #endregion Private Classes 

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

        #region Private Fields 
 
        /// 
        /// Caches the result of parsing GlyphRun properties. 
        /// 
        private LayoutDependentGlyphRunProperties   _glyphRunProperties;

        ///  
        /// This GlyphRun instance is needed for measurement purposes only.
        ///  
        private GlyphRun                            _measurementGlyphRun; 

        private Point                               _glyphRunOrigin = new Point(); 
        private const double                        EmMultiplier = 100.0;

        #endregion Private Fields
    }; 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: Glyphs.cs 
//
// Description: Glyphs element for fixed text rendering. 
// 
// Spec: http://avalon/text/DesignDocsAndSpecs/Glyphs%20element%20and%20GlyphRun%20object.htm
// 
// History:
//  06/02/2003 : mleonov - created.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization; 
using System.IO;
using System.Windows.Threading;

 
using System.Windows;
using System.Windows.Media; 
using System.Windows.Navigation; 
using System.Windows.Markup;
using System.ComponentModel; 
using System.Security;

using MS.Utility;
using MS.Internal.Navigation; 
using MS.Internal.Utility;
using MS.Internal; 
 
namespace System.Windows.Documents
{ 
    /// 
    /// Glyphs shape represents GlyphRun in markup
    /// 
    public sealed class Glyphs : FrameworkElement, IUriContext 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        /// 
        /// Constructor 
        ///  
        public Glyphs()
        { 
        }

        #endregion Constructors
 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //-----------------------------------------------------

        #region Public Methods
 

        ///  
        /// Creates a GlyphRun object from the properties on a Glyphs object. 
        /// 
        /// GlyphRun object that corresponds to the properties set on this Glyphs object. 
        public GlyphRun ToGlyphRun()
        {
            ComputeMeasurementGlyphRunAndOrigin();
            if (_measurementGlyphRun == null) 
                return null;
            Debug.Assert(_glyphRunProperties != null); 
 
            return _measurementGlyphRun;
        } 

        #endregion Public Methods

 
        #region IUriContext implementation
 
        ///  
        /// IUriContext interface is implemented by Glyphs element so that it
        /// can hold on to the base URI used by parser. 
        /// The base URI is needed to resolve FontUri property.
        /// 
        /// Base Uri
        Uri IUriContext.BaseUri 
        {
            get 
            { 
                return (Uri)GetValue(BaseUriHelper.BaseUriProperty);
            } 
            set
            {
                SetValue(BaseUriHelper.BaseUriProperty, value);
            } 
        }
        #endregion IUriContext implementation 
 
        #region Layout and rendering
 
        /// 
        /// ArrangeOverride sets the "shapeBounds" in for the shape.
        /// 
        protected override Size ArrangeOverride(Size finalSize) 
        {
            base.ArrangeOverride(finalSize); 
 
            Rect inkBoundingBox;
 
            if (_measurementGlyphRun != null)
                inkBoundingBox = _measurementGlyphRun.ComputeInkBoundingBox();
            else
                inkBoundingBox = Rect.Empty; 

            if (!inkBoundingBox.IsEmpty) 
            { 
                inkBoundingBox.X += _glyphRunOrigin.X;
                inkBoundingBox.Y += _glyphRunOrigin.Y; 
            }
            return finalSize;
        }
 
        /// 
        /// Renders GlyphRun into a drawing context 
        ///  
        /// Drawing context
        protected override void OnRender(DrawingContext context) 
        {
            if (_glyphRunProperties == null || _measurementGlyphRun == null)
                return;
 
            context.PushGuidelineY1(_glyphRunOrigin.Y);
            try 
            { 
                context.DrawGlyphRun(Fill, _measurementGlyphRun);
            } 
            finally
            {
                context.Pop();
            } 
        }
 
        ///  
        /// Measurement override for Glyphs
        ///  
        /// Input constraint
        /// 
        protected override Size MeasureOverride(Size constraint)
        { 
            ComputeMeasurementGlyphRunAndOrigin();
 
            if (_measurementGlyphRun == null) 
                return new Size();
 
            Rect designRect = _measurementGlyphRun.ComputeAlignmentBox();

            designRect.Offset(_glyphRunOrigin.X, _glyphRunOrigin.Y);
 
            return new Size(
                Math.Max(0, designRect.Right), 
                Math.Max(0, designRect.Bottom) 
            );
        } 

        #endregion Layout and rendering

        #region Parsing and GlyphRun creation 

        private void ComputeMeasurementGlyphRunAndOrigin() 
        { 
            if (_glyphRunProperties == null)
            { 
                _measurementGlyphRun = null;
                ParseGlyphRunProperties();

                if (_glyphRunProperties == null) 
                {
                    return; 
                } 
            }
            else if (_measurementGlyphRun != null) 
            {
                return;
            }
 
            bool leftToRight = ((BidiLevel & 1) == 0);
 
            bool haveOriginX = !DoubleUtil.IsNaN(OriginX); 
            bool haveOriginY = !DoubleUtil.IsNaN(OriginY);
 
            bool measurementGlyphRunOriginValid = false;

            Rect alignmentRect = new Rect();
            if (haveOriginX && haveOriginY && leftToRight) 
            {
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(new Point(OriginX,OriginY), Language); 
                measurementGlyphRunOriginValid = true; 
            }
            else 
            {
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(new Point(), Language);
                // compute alignment box for origins
                alignmentRect = _measurementGlyphRun.ComputeAlignmentBox(); 
            }
 
            if (haveOriginX) 
                _glyphRunOrigin.X = OriginX;
            else 
                _glyphRunOrigin.X = leftToRight ? 0 : alignmentRect.Width;

            if (haveOriginY)
                _glyphRunOrigin.Y = OriginY; 
            else
                _glyphRunOrigin.Y = -alignmentRect.Y; 
 
            if (!measurementGlyphRunOriginValid)
            { 
                _measurementGlyphRun = _glyphRunProperties.CreateGlyphRun(_glyphRunOrigin, Language);
            }
        }
 
        private void ParseCaretStops(LayoutDependentGlyphRunProperties glyphRunProperties)
        { 
            string caretStopsString = CaretStops; 
            if (String.IsNullOrEmpty(caretStopsString))
            { 
                glyphRunProperties.caretStops = null;
                return;
            }
 
            // Caret stop count should be equal to the number of UTF16 code points in the glyph run plus one.
            // Logic below is similar to GlyphRun.CodepointCount property. 
 
            int caretStopCount;
 
            if (!String.IsNullOrEmpty(glyphRunProperties.unicodeString))
                caretStopCount = glyphRunProperties.unicodeString.Length + 1;
            else
            { 
                if (glyphRunProperties.clusterMap != null && glyphRunProperties.clusterMap.Length != 0)
                    caretStopCount = glyphRunProperties.clusterMap.Length + 1; 
                else 
                {
                    Debug.Assert(glyphRunProperties.glyphIndices != null); 
                    caretStopCount = glyphRunProperties.glyphIndices.Length + 1;
                }
            }
 
            bool[] caretStops = new bool[caretStopCount];
 
            int i = 0; 
            foreach (char c in caretStopsString)
            { 
                if (Char.IsWhiteSpace(c))
                    continue;

                int nibble; 

                if ('0' <= c && c <= '9') 
                    nibble = c - '0'; 
                else if ('a' <= c && c <= 'f')
                    nibble = c - 'a' + 10; 
                else if ('A' <= c && c <= 'F')
                    nibble = c - 'A' + 10;
                else
                    throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsContainsHexDigits), "CaretStops"); 

                Debug.Assert(0 <= nibble && nibble <= 15); 
 
                if ((nibble & 8) != 0)
                { 
                    if (i >= caretStops.Length)
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops");
                    caretStops[i] = true;
                } 
                ++i;
                if ((nibble & 4) != 0) 
                { 
                    if (i >= caretStops.Length)
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops"); 
                    caretStops[i] = true;
                }
                ++i;
                if ((nibble & 2) != 0) 
                {
                    if (i >= caretStops.Length) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops"); 
                    caretStops[i] = true;
                } 
                ++i;
                if ((nibble & 1) != 0)
                {
                    if (i >= caretStops.Length) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsCaretStopsLengthCorrespondsToUnicodeString), "CaretStops");
                    caretStops[i] = true; 
                } 
                ++i;
            } 

            // If the number of entries in the caret stop specification string is less than the number of code points,
            // set the remaining caret stop values to true.
            while (i < caretStops.Length) 
            {
                caretStops[i++] = true; 
            } 
            glyphRunProperties.caretStops = caretStops;
        } 

        ///
        /// Critical as it accesses the base Uri
        /// TreatAsSafe as it only uses this to load glyphtypefaces, and this information is not disclosed. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        private void ParseGlyphRunProperties() 
        {
            LayoutDependentGlyphRunProperties glyphRunProperties = null; 
            Uri uri = FontUri;

            if (uri != null)
            { 
                // Indices and UnicodeString cannot both be empty.
                if (String.IsNullOrEmpty(UnicodeString) && String.IsNullOrEmpty(Indices)) 
                    throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringAndIndicesCannotBothBeEmpty)); 

                glyphRunProperties = new LayoutDependentGlyphRunProperties(); 

                if (!uri.IsAbsoluteUri)
                {
                    uri = BindUriHelper.GetResolvedUri(BaseUriHelper.GetBaseUri(this), uri); 
                }
 
                glyphRunProperties.glyphTypeface = new GlyphTypeface(uri, StyleSimulations); 

                glyphRunProperties.unicodeString = UnicodeString; 
                glyphRunProperties.sideways = IsSideways;
                glyphRunProperties.deviceFontName = DeviceFontName;

                // parse the Indices property 
                List parsedGlyphs;
                int glyphCount = ParseGlyphsProperty( 
                    glyphRunProperties.glyphTypeface, 
                    glyphRunProperties.unicodeString,
                    glyphRunProperties.sideways, 
                    out parsedGlyphs,
                    out glyphRunProperties.clusterMap);

                Debug.Assert(parsedGlyphs.Count == glyphCount); 

                glyphRunProperties.glyphIndices = new ushort[glyphCount]; 
                glyphRunProperties.advanceWidths = new double[glyphCount]; 

                ParseCaretStops(glyphRunProperties); 

                // Delay creating glyphOffsets array because in many common cases it will contain only zeroed entries.
                glyphRunProperties.glyphOffsets = null;
 
                int i = 0;
 
                glyphRunProperties.fontRenderingSize = FontRenderingEmSize; 
                glyphRunProperties.bidiLevel = BidiLevel;
 
                double fromEmToMil = glyphRunProperties.fontRenderingSize / EmMultiplier;

                foreach (ParsedGlyphData parsedGlyphData in parsedGlyphs)
                { 
                    glyphRunProperties.glyphIndices[i] = parsedGlyphData.glyphIndex;
 
                    // convert advances and offsets from integers in em space to doubles coordinates in MIL space 
                    glyphRunProperties.advanceWidths[i] = parsedGlyphData.advanceWidth * fromEmToMil;
 
                    if (parsedGlyphData.offsetX != 0 || parsedGlyphData.offsetY != 0)
                    {
                        // Lazily create glyph offset array. Previous entries will be correctly set to zero
                        // by the default Point ctor. 
                        if (glyphRunProperties.glyphOffsets == null)
                            glyphRunProperties.glyphOffsets = new Point[glyphCount]; 
 
                        glyphRunProperties.glyphOffsets[i].X = parsedGlyphData.offsetX * fromEmToMil;
                        glyphRunProperties.glyphOffsets[i].Y = parsedGlyphData.offsetY * fromEmToMil; 
                    }

                    ++i;
                } 
            }
            _glyphRunProperties = glyphRunProperties; 
        } 

        private static bool IsEmpty(string s) 
        {
            foreach (char c in s)
            {
                if (!Char.IsWhiteSpace(c)) 
                    return false;
            } 
            return true; 
        }
 
        /// 
        /// Read GlyphIndex specification - glyph index value with an optional glyph cluster prefix.
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        /// true if glyph index is present, false if glyph index is not present. 
        private bool ReadGlyphIndex(
            string      valueSpec,
            ref bool    inCluster,
            ref int     glyphClusterSize, 
            ref int     characterClusterSize,
            ref ushort  glyphIndex) 
        { 
            // the format is ... [(CharacterClusterSize[:GlyphClusterSize])] GlyphIndex ...
            string glyphIndexString = valueSpec; 

            int firstBracket = valueSpec.IndexOf('(');
            if (firstBracket != -1)
            { 
                // Only spaces are allowed before the bracket
                for (int i=0; i= secondBracket - 1)
                        throw new ArgumentException(SR.Get(SRID.GlyphsClusterMisplacedSeparator)); 
                    string characterClusterSpec = valueSpec.Substring(firstBracket + 1, colon - (firstBracket + 1));
                    characterClusterSize = int.Parse(characterClusterSpec, CultureInfo.InvariantCulture); 
                    string glyphClusterSpec = valueSpec.Substring(colon + 1, secondBracket - (colon + 1)); 
                    glyphClusterSize = int.Parse(glyphClusterSpec, CultureInfo.InvariantCulture);
                } 
                inCluster = true;
                glyphIndexString = valueSpec.Substring(secondBracket + 1);
            }
            if (IsEmpty(glyphIndexString)) 
                return false;
 
            glyphIndex = ushort.Parse(glyphIndexString, CultureInfo.InvariantCulture); 
            return true;
        } 

        private static double GetAdvanceWidth(GlyphTypeface glyphTypeface, ushort glyphIndex, bool sideways)
        {
            double advance = sideways ? glyphTypeface.AdvanceHeights[glyphIndex] : glyphTypeface.AdvanceWidths[glyphIndex]; 
            return advance * EmMultiplier;
        } 
 
        private ushort GetGlyphFromCharacter(GlyphTypeface glyphTypeface, char character)
        { 
            ushort glyphIndex;
            // TryGetValue will return zero glyph index for missing code points,
            // which is the right thing to display per http://www.microsoft.com/typography/otspec/cmap.htm
            glyphTypeface.CharacterToGlyphMap.TryGetValue(character, out glyphIndex); 
            return glyphIndex;
        } 
 
        /// 
        /// Performs validation against cluster map size and throws a well defined exception. 
        /// 
        private static void SetClusterMapEntry(ushort[] clusterMap, int index, ushort value)
        {
            if (index < 0 || index >= clusterMap.Length) 
                throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort));
            clusterMap[index] = value; 
        } 

        private class ParsedGlyphData 
        {
            public ushort   glyphIndex;
            public double   advanceWidth;
            public double   offsetX; 
            public double   offsetY;
        }; 
 
        // ------------------------------------------------------------------------------
        // Parses a semicolon-delimited list of glyph specifiers, each of which consists 
        // of up to 4 comma-delimited values:
        //   - glyph index (ushort)
        //   - glyph advance (double)
        //   - glyph offset X (double) 
        //   - glyph offset Y (double)
        // A glyph entry can be have a cluster size prefix (int or pair of ints separated by a colon) 
        // Whitespace adjacent to a delimiter (comma or semicolon) is ignored. 
        // Returns the number of glyph specs parsed (number of semicolons plus 1).
        // 


        private int ParseGlyphsProperty(
            GlyphTypeface               fontFace, 
            string                      unicodeString,
            bool                        sideways, 
            out List   parsedGlyphs, 
            out ushort[]                clusterMap)
        { 
            string glyphsProp = Indices;

            // init for the whole parse, including the result arrays
            int parsedGlyphCount = 0; 
            int parsedCharacterCount = 0;
 
            int characterClusterSize = 1; 
            int glyphClusterSize = 1;
 
            bool inCluster = false;

            // make reasonable capacity guess on how many glyphs we can expect
            int estimatedNumberOfGlyphs; 

            if (!String.IsNullOrEmpty(unicodeString)) 
            { 
                clusterMap = new ushort[unicodeString.Length];
                estimatedNumberOfGlyphs = unicodeString.Length; 
            }
            else
            {
                clusterMap = null; 
                estimatedNumberOfGlyphs = 8;
            } 
 
            if (!String.IsNullOrEmpty(glyphsProp))
                estimatedNumberOfGlyphs = Math.Max(estimatedNumberOfGlyphs, glyphsProp.Length / 5); 

            parsedGlyphs = new List(estimatedNumberOfGlyphs);

            ParsedGlyphData parsedGlyphData = new ParsedGlyphData(); 

            #region Parse Glyphs string 
            if (!String.IsNullOrEmpty(glyphsProp)) 
            {
                // init per-glyph values for the first glyph/position 
                int valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)?
                int valueStartIndex = 0; // where (what index of Glyphs prop string) did this value start?

                // iterate and parse the characters of the Indices property 
                for (int i = 0; i <= glyphsProp.Length; i++)
                { 
                    // get next char or pseudo-terminator 
                    char c = i < glyphsProp.Length ? glyphsProp[i] : '\0';
 
                    // finished scanning the current per-glyph value?
                    if ((c == ',') || (c == ';') || (i == glyphsProp.Length))
                    {
                        int len = i - valueStartIndex; 

                        string valueSpec = glyphsProp.Substring(valueStartIndex, len); 
 
                        #region Interpret one comma-delimited value
 
                        switch (valueWithinGlyph)
                        {
                            case 0:
                                bool wasInCluster = inCluster; 
                                // interpret cluster size and glyph index spec
                                if (!ReadGlyphIndex( 
                                    valueSpec, 
                                    ref inCluster,
                                    ref glyphClusterSize, 
                                    ref characterClusterSize,
                                    ref parsedGlyphData.glyphIndex))
                                {
                                    if (String.IsNullOrEmpty(unicodeString)) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredIfNoUnicode));
 
                                    if (unicodeString.Length <= parsedCharacterCount) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort));
 
                                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]);
                                }

                                if (!wasInCluster && clusterMap != null) 
                                {
                                    // fill out cluster map at the start of each cluster 
                                    if (inCluster) 
                                    {
                                        for (int ch = parsedCharacterCount; ch < parsedCharacterCount + characterClusterSize; ++ch) 
                                        {
                                            SetClusterMapEntry(clusterMap, ch, (ushort)parsedGlyphCount);
                                        }
                                    } 
                                    else
                                    { 
                                        SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount); 
                                    }
                                } 
                                parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways);
                                break;

                            case 1: 
                                // interpret glyph advance spec
                                if (!IsEmpty(valueSpec)) 
                                { 
                                    parsedGlyphData.advanceWidth = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                    if (parsedGlyphData.advanceWidth < 0) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsAdvanceWidthCannotBeNegative));
                                }
                                break;
 
                            case 2:
                                // interpret glyph offset X 
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetX = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 

                            case 3:
                                // interpret glyph offset Y
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetY = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 
 
                            default:
                                // too many commas; can't interpret 
                                throw new ArgumentException(SR.Get(SRID.GlyphsTooManyCommas));
                        }
                        #endregion Interpret one comma-delimited value
 
                        // prepare to scan next value (if any)
                        valueWithinGlyph++; 
                        valueStartIndex = i + 1; 
                    }
 
                    // finished processing the current glyph?
                    if ((c == ';') || (i == glyphsProp.Length))
                    {
                        parsedGlyphs.Add(parsedGlyphData); 
                        parsedGlyphData = new ParsedGlyphData();
 
                        if (inCluster) 
                        {
                            --glyphClusterSize; 
                            // when we reach the end of a glyph cluster, increment character index
                            if (glyphClusterSize == 0)
                            {
                                parsedCharacterCount += characterClusterSize; 
                                inCluster = false;
                            } 
                        } 
                        else
                        { 
                            ++parsedCharacterCount;
                        }
                        parsedGlyphCount++;
 
                        // initalize new per-glyph values
                        valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)? 
                        valueStartIndex = i + 1; // where (what index of Glyphs prop string) did this value start? 
                    }
                } 
            }
            #endregion

            // fill the remaining glyphs with defaults, assuming 1:1 mapping 
            if (unicodeString != null)
            { 
                while (parsedCharacterCount < unicodeString.Length) 
                {
                    if (inCluster) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredWithinCluster));

                    if (unicodeString.Length <= parsedCharacterCount)
                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort)); 

                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]); 
                    parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways); 
                    parsedGlyphs.Add(parsedGlyphData);
                    parsedGlyphData = new ParsedGlyphData(); 
                    SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount);
                    ++parsedCharacterCount;
                    ++parsedGlyphCount;
                } 
            }
 
            // return number of glyphs actually specified 
            return parsedGlyphCount;
        } 
        #endregion Parsing and GlyphRun creation

        //------------------------------------------------------
        // 
        //  Public Properties
        // 
        //----------------------------------------------------- 

        #region Public Properties 

        private static void FillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Called when Fill is changed. 
            // If a SubPropertyInvalidatin is in progress this means a Freezable
            // has changed and we don't need to invalidate layout.  Otherwise 
            // we have to invalidate 

           ((UIElement)d).InvalidateVisual(); 
        }

        private static void GlyphRunPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            // Called when any property is changed that would require a new call to ParseGlyphRunProperties
 
            ((Glyphs)d)._glyphRunProperties = null; 
        }
 
        private static void OriginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Called when OriginX or OriginY is changed that would require recreation of the positioned GlyphRun
            // The _measurementGlyphRun will get updated as a result of layout 
            ((Glyphs)d)._measurementGlyphRun = null;
        } 
 
        /// 
        /// Fill property 
        /// 
        public static readonly DependencyProperty FillProperty
            = DependencyProperty.Register(
                "Fill", 
                typeof(Brush),
                typeof(Glyphs), 
                new FrameworkPropertyMetadata( 
                    (Brush)null,
                    FrameworkPropertyMetadataOptions.None, 
                    new PropertyChangedCallback(FillChanged),
                    null)
                );
 
        /// 
        /// Fill property 
        ///  
        public Brush Fill
        { 
            get
            {
                return (Brush)GetValue(FillProperty);
            } 
            set
            { 
                SetValue(FillProperty, value); 
            }
        } 

        /// 
        /// Indices property
        ///  
        public static readonly DependencyProperty IndicesProperty =
            DependencyProperty.Register( "Indices", typeof(string), typeof(Glyphs), 
                new FrameworkPropertyMetadata(string.Empty, 
                                              FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
                                              new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// Indices property accessor
        ///  
        public string Indices
        { 
            get 
            {
                return (string)GetValue(IndicesProperty); 
            }
            set
            {
                SetValue(IndicesProperty, value); 
            }
        } 
 
        /// 
        /// UnicodeString property 
        /// 
        public static readonly DependencyProperty UnicodeStringProperty =
            DependencyProperty.Register( "UnicodeString", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        ///  
        /// UnicodeString property accessor 
        /// 
        public string UnicodeString 
        {
            get
            {
                return (string)GetValue(UnicodeStringProperty);; 
            }
            set 
            { 
                SetValue(UnicodeStringProperty, value);
            } 
        }

        /// 
        /// CaretStops property. The property syntax is a string of hexadecimal digits that describe an array of Boolean values that 
        /// correspond to every code point in UnicodeString property.
        ///  
        public static readonly DependencyProperty CaretStopsProperty = 
            DependencyProperty.Register( "CaretStops", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// CaretStops property accessor
        ///  
        public string CaretStops
        { 
            get 
            {
                return (string)GetValue(CaretStopsProperty);; 
            }
            set
            {
                SetValue(CaretStopsProperty, value); 
            }
        } 
 
        /// 
        /// FontRenderingEmSize property 
        /// 
        public static readonly DependencyProperty FontRenderingEmSizeProperty =
            DependencyProperty.Register( "FontRenderingEmSize", typeof(double), typeof(Glyphs),
                new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        ///  
        /// FontRenderingEmSize property accessor 
        /// 
        [TypeConverter("System.Windows.FontSizeConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")] 
        public double FontRenderingEmSize
        {
            get
            { 
                return (double)GetValue(FontRenderingEmSizeProperty);
            } 
            set 
            {
                SetValue(FontRenderingEmSizeProperty, value); 
            }
        }

        ///  
        /// OriginX property
        ///  
        public static readonly DependencyProperty OriginXProperty = 
            DependencyProperty.Register( "OriginX", typeof(double), typeof(Glyphs),
                new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OriginPropertyChanged))); 

        /// 
        /// OriginX property accessor
        ///  
        [TypeConverter("System.Windows.LengthConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")]
        public double OriginX 
        { 
            get
            { 
                return (double)GetValue(OriginXProperty);
            }
            set
            { 
                SetValue(OriginXProperty, value);
            } 
        } 

        ///  
        /// OriginY property
        /// 
        public static readonly DependencyProperty OriginYProperty =
            DependencyProperty.Register( "OriginY", typeof(double), typeof(Glyphs), 
                new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OriginPropertyChanged)));
 
        ///  
        /// OriginY property accessor
        ///  
        [TypeConverter("System.Windows.LengthConverter, PresentationFramework, Version=" + Microsoft.Internal.BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + Microsoft.Internal.BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")]
        public double OriginY
        {
            get 
            {
                return (double)GetValue(OriginYProperty); 
            } 
            set
            { 
                SetValue(OriginYProperty, value);
            }
        }
 
        /// 
        /// FontUri property 
        ///  
        public static readonly DependencyProperty FontUriProperty =
            DependencyProperty.Register( "FontUri", typeof(Uri), typeof(Glyphs), 
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));

        /// 
        /// FontUri property accessor 
        /// 
        public Uri FontUri 
        { 
            get
            { 
                return (Uri)GetValue(FontUriProperty);
            }
            set
            { 
                SetValue(FontUriProperty, value);
            } 
        } 

        ///  
        /// StyleSimulations property
        /// 
        public static readonly DependencyProperty StyleSimulationsProperty =
            DependencyProperty.Register( "StyleSimulations", typeof(StyleSimulations), typeof(Glyphs), 
                new FrameworkPropertyMetadata(StyleSimulations.None, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));
 
        ///  
        /// StyleSimulations property accessor
        ///  
        public StyleSimulations StyleSimulations
        {
            get
            { 
                return (StyleSimulations)GetValue(StyleSimulationsProperty);
            } 
            set 
            {
                SetValue(StyleSimulationsProperty, value); 
            }
        }

        ///  
        /// Sideways property
        ///  
        public static readonly DependencyProperty IsSidewaysProperty = 
            DependencyProperty.Register( "IsSideways", typeof(bool), typeof(Glyphs),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 

        /// 
        /// Specifies whether to rotate characters/glyphs 90 degrees anti-clockwise
        /// and use vertical baseline positioning metrics. 
        /// 
        /// true if the rotation should be applied, false otherwise. 
        public bool  IsSideways 
        {
            get 
            {
                return (bool)GetValue(IsSidewaysProperty);
            }
            set 
            {
                SetValue(IsSidewaysProperty, value); 
            } 
        }
 
        /// 
        /// BidiLevel property
        /// 
        public static readonly DependencyProperty BidiLevelProperty = 
            DependencyProperty.Register( "BidiLevel", typeof(int), typeof(Glyphs),
                new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged))); 
 
        /// 
        /// Determines LTR/RTL reading order and bidi nesting. 
        /// 
        /// The value of bidirectional nesting level.
        public int BidiLevel
        { 
            get
            { 
                return (int)GetValue(BidiLevelProperty); 
            }
            set 
            {
                SetValue(BidiLevelProperty, value);
            }
        } 

        ///  
        /// DeviceFontName property 
        /// 
        public static readonly DependencyProperty DeviceFontNameProperty = 
            DependencyProperty.Register("DeviceFontName", typeof(string), typeof(Glyphs),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(GlyphRunPropertyChanged)));

        ///  
        /// Identifies a specific device font for which the Glyphs element has been optimized. When a Glyphs element is
        /// being rendered on a device that has built-in support for this named font, then the Glyphs element should be rendered using a 
        /// possibly device specific mechanism for selecting that font, and by sending the Unicode codepoints rather than the 
        /// glyph indices. When rendering onto a device that does not include built-in support for the named font,
        /// this property should be ignored. 
        /// 
        public string DeviceFontName
        {
            get 
            {
                return (string) GetValue(DeviceFontNameProperty); 
            } 
            set
            { 
                SetValue(DeviceFontNameProperty, value);
            }
        }
 
        #endregion Public Properties
 
        //------------------------------------------------------ 
        //
        //  Internal Properties 
        //
        //-----------------------------------------------------

        #region Internal Properties 

        ///  
        /// This property was added for performance reasons.  It allows D2 code 
        /// to access the cached measurement glyph run instead of generating
        /// a new GlyphRun object by calling ToGlyphRun() 
        /// 
        internal GlyphRun MeasurementGlyphRun
        {
            get 
            {
                if (_glyphRunProperties == null || _measurementGlyphRun == null) 
                { 
                    ComputeMeasurementGlyphRunAndOrigin();
                } 
                return _measurementGlyphRun;
            }
        }
        #endregion Internal Properties 

        //----------------------------------------------------- 
        // 
        //  Private Classes
        // 
        //-----------------------------------------------------

        #region Private Classes
 
        /// 
        /// This class is temporarily needed because GlyphRun includes rendering information 
        /// that in future will be passed to DrawGlyphs separately. 
        /// 
        private class LayoutDependentGlyphRunProperties 
        {
            public double           fontRenderingSize;
            public ushort []        glyphIndices;
            public double []        advanceWidths; 
            public Point []         glyphOffsets;
            public ushort []        clusterMap; 
            public bool             sideways; 
            public int              bidiLevel;
            public GlyphTypeface    glyphTypeface; 
            public string           unicodeString;
            public IList      caretStops;
            public string           deviceFontName;
 
            public GlyphRun CreateGlyphRun(Point origin, XmlLanguage language)
            { 
                return new GlyphRun( 
                    glyphTypeface,               // GlyphTypeface
                    bidiLevel,                   // Bidi level 
                    sideways,                    // sideways flag
                    fontRenderingSize,           // rendering em size in MIL units
                    glyphIndices,                // glyph indices
                    origin,                      // origin of glyph-drawing space 
                    advanceWidths,               // glyph advances
                    glyphOffsets,                // glyph offsets 
                    unicodeString.ToCharArray(), // unicode characters 
                    deviceFontName,              // device font
                    clusterMap,                  // cluster map 
                    caretStops,                  // caret stops
                    language                     // language
                );
            } 
        }
 
        #endregion Private Classes 

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

        #region Private Fields 
 
        /// 
        /// Caches the result of parsing GlyphRun properties. 
        /// 
        private LayoutDependentGlyphRunProperties   _glyphRunProperties;

        ///  
        /// This GlyphRun instance is needed for measurement purposes only.
        ///  
        private GlyphRun                            _measurementGlyphRun; 

        private Point                               _glyphRunOrigin = new Point(); 
        private const double                        EmMultiplier = 100.0;

        #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