TextShapeableCharacters.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / MS / Internal / TextFormatting / TextShapeableCharacters.cs / 3 / TextShapeableCharacters.cs

                            //------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation
// 
//  File:      TextShapeableCharacters.cs
// 
//  Contents:  Implementation of text shapeable symbols for characters 
//
//  Spec:      [....]/sites/Avalon/Specs/Text%20Formatting%20API.doc 
//
//  Created:   1-2-2004 [....] ([....])
//  History:  1-19-2004 garyyyang: Change the class to internal
// 
//-----------------------------------------------------------------------
 
 
using System;
using System.Diagnostics; 
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Security; 
using System.Windows;
using System.Windows.Markup;    // for XmlLanguage 
using System.Windows.Media; 
using MS.Internal;
using MS.Internal.FontCache; 
using MS.Internal.TextFormatting;
using MS.Internal.Shaping;

namespace System.Windows.Media.TextFormatting 
{
    ///  
    /// A specialized TextShapeableSymbols implemented by TextFormatter to represent 
    /// a collection of glyphs from a physical typeface.
    ///  
    internal sealed class TextShapeableCharacters : TextShapeableSymbols
    {
        private CharacterBufferRange    _characterBufferRange;
        private TextRunProperties       _properties; 
        private double                  _emSize;    // after-scaled
 
        private Item                    _textItem; 
        private ShapeTypeface           _shapeTypeface;
        private bool                    _nullShape; 

        #region Constructors

        ///  
        /// Construct a shapeable characters object
        ///  
        ///  
        /// The shapeTypeface parameter can be null if and only if CheckFastPathNominalGlyphs
        /// has previously returned true. 
        /// 
        internal TextShapeableCharacters(
            CharacterBufferRange    characterRange,
            TextRunProperties       properties, 
            double                  emSize,
            Item                    textItem, 
            ShapeTypeface           shapeTypeface, 
            bool                    nullShape
            ) 
        {
            _characterBufferRange = characterRange;
            _properties = properties;
            _emSize = emSize; 
            _textItem = textItem;
            _shapeTypeface = shapeTypeface; 
            _nullShape = nullShape; 
        }
 
        #endregion

        #region TextRun implementation
 
        /// 
        /// Character reference 
        ///  
        public sealed override CharacterBufferReference CharacterBufferReference
        { 
            get
            {
                return _characterBufferRange.CharacterBufferReference;
            } 
        }
 
 
        /// 
        /// Character length of the run 
        /// 
        public sealed override int Length
        {
            get 
            {
                return _characterBufferRange.Length; 
            } 
        }
 

        /// 
        /// A set of properties shared by every characters in the run
        ///  
        public sealed override TextRunProperties Properties
        { 
            get 
            {
                return _properties; 
            }
        }

        #endregion 

 
        #region TextShapeableSymbols implementation 

        ///  
        /// Map specified character string into glyphs
        /// 
        internal sealed override ushort[] GetGlyphs(
            CheckedCharPointer                        characterString, 
            int                                       length,
            ShapingOptions                            shapeOptions, 
            FeatureSet                                featureSet, 
            CheckedUShortPointer                      clusterMap,
            CheckedCharacterShapingPropertiesPointer  characterProperties, 
            out GlyphShapingProperties[]              glyphProperties,
            out int                                   glyphCount
            )
        { 
            Invariant.Assert(_shapeTypeface != null);
            // Device fonts are only used through the LS non-glyphed code path. Only when a DigitCulture is set 
            // will a potential device font be ignored and come through shaping. 
            Invariant.Assert(_shapeTypeface.DeviceFont == null  || _textItem.DigitCulture != null);
 
            ushort[] glyphs;
            if (_nullShape && _textItem.Script != ScriptID.Control)
            {
                glyphs = GetNullGlyphs( 
                    length,
                    characterString, 
                    characterProperties, 
                    clusterMap,
                    out glyphProperties, 
                    out glyphCount
                    );
            }
            else 
            {
                glyphs = _shapeTypeface.GetGlyphs( 
                    characterString, 
                    length,
                    CultureMapper.GetSpecificCulture(_properties.CultureInfo), 
                    _textItem,
                    shapeOptions,
                    featureSet,
                    characterProperties, 
                    clusterMap,
                    out glyphProperties, 
                    out glyphCount 
                    );
 
                if (    glyphs == null
                    ||  glyphs.Length == 0
                    ||  glyphCount == 0)
                { 
                    glyphs = GetNullGlyphs(
                        length, 
                        characterString, 
                        characterProperties,
                        clusterMap, 
                        out glyphProperties,
                        out glyphCount
                        );
                } 
            }
 
            return glyphs; 
        }
 

        /// 
        /// Shape specified character string into an array of missing glyphs
        ///  
        /// 
        /// Critical - the method probes into checked pointer 
        /// Safe     - The method fills in null glyphs, null glyphs are ok to expose 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private unsafe ushort[] GetNullGlyphs(
            int                                         length,
            CheckedCharPointer                          characterString,
            CheckedCharacterShapingPropertiesPointer    characterProperties, 
            CheckedUShortPointer                        clusterMap,
            out GlyphShapingProperties[]                glyphProperties, 
            out int                                     glyphCount 
            )
        { 
            glyphCount          = 0;
            ushort[] glyphs     = new ushort[length];
            glyphProperties     = new GlyphShapingProperties[length];
 
            // Probe into the checked pointer
            ushort * pClusterMap = clusterMap.Probe(0, length); 
            CharacterShapingProperties* pCharacterProperties = characterProperties.Probe(0, length); 
            char * charBuffer    = characterString.Probe(0, length);
 
            int wordCount;
            for(int i = 0; i < length; i = i + wordCount)
            {
                wordCount = 1; 

                pClusterMap[i] = (ushort) glyphCount; 
                pCharacterProperties[i].CanGlyphAlone = true; 
                pCharacterProperties[i].EngineReserved = 0; // Engine reserved. Single glyph cluster
 
                glyphs[glyphCount] = 0;
                glyphProperties[glyphCount] = new GlyphShapingProperties(
                    (ushort)(GlyphFlags.ClusterStart | GlyphFlags.Unresolved),  // glyph flags
                    (ushort)((1 << 8) | 0) // Engine reserved. Single glyph cluster 
                    );
 
                glyphCount ++; 

                if (i < length -1 && ((charBuffer[i] & 0xFC00) == 0xD800) && ((charBuffer[i + 1] & 0xFC00) == 0xDC00)) 
                {
                    // Both higher and lower surrogate should point to the same glyph.
                    wordCount = 2;
                    pClusterMap[i + 1] = pClusterMap[i]; 
                    pCharacterProperties[i + 1].CanGlyphAlone = true;
                } 
            } 

            return glyphs; 
        }


        ///  
        /// Get placement of glyphs
        ///  
        internal sealed override void GetGlyphPlacements( 
            CheckedUShortPointer                     glyphIndices,
            CheckedGlyphShapingPropertiesPointer     glyphProperties, 
            int                                      glyphCount,
            CheckedUShortPointer                     clusterMap,
            CheckedCharacterShapingPropertiesPointer characterProperties,
            int                                      length, 
            ShapingOptions                           shapeOptions,
            FeatureSet                               featureSet, 
            double                                   scalingFactor, 
            CheckedIntPointer                        glyphAdvances,
            CheckedGlyphOffsetPointer                glyphOffsets 
            )
        {
            Invariant.Assert(_shapeTypeface != null);
            // Device fonts are only used through the LS non-glyphed code path. Only when a DigitCulture is set 
            // will a potential device font be ignored and come through shaping.
            Invariant.Assert(_shapeTypeface.DeviceFont == null  || _textItem.DigitCulture != null); 
 
            _shapeTypeface.GetGlyphPlacements(
                _characterBufferRange, 
                glyphIndices,
                glyphProperties,
                glyphCount,
                _textItem, 
                CultureMapper.GetSpecificCulture(_properties.CultureInfo),
                clusterMap, 
                characterProperties, 
                length,
                shapeOptions, 
                featureSet,
                _emSize * scalingFactor,
                glyphAdvances,
                glyphOffsets 
                );
        } 
 

        ///  
        /// Compute a shaped glyph run object from specified glyph-based info
        /// 
        internal sealed override GlyphRun ComputeShapedGlyphRun(
            Point                    origin, 
            char[]                   characterString,
            ushort[]                 clusterMap, 
            ushort[]                 glyphIndices, 
            GlyphShapingProperties[] glyphProperties,
            IList            glyphAdvances, 
            IList             glyphOffsets,
            bool                     rightToLeft,
            bool                     sideways
            ) 
        {
            Invariant.Assert(_shapeTypeface != null); 
            Invariant.Assert(glyphIndices   != null); 
            // Device fonts are only used through the LS non-glyphed code path. Only when a DigitCulture is set
            // will a potential device font be ignored and come through shaping. 
            Invariant.Assert(_shapeTypeface.DeviceFont == null  || _textItem.DigitCulture != null);

            bool[] caretStops = null;
 
            if (    clusterMap != null
                &&  (HasExtendedCharacter || NeedsCaretInfo) 
                ) 
            {
                caretStops = new bool[clusterMap.Length + 1]; 

                // caret stops at cluster boundaries, the first and the last entries are always set
                caretStops[0] = true;
                caretStops[clusterMap.Length] = true; 

                ushort lastGlyph = clusterMap[0]; 
 
                for (int i = 1; i < clusterMap.Length; i++)
                { 
                    ushort glyph = clusterMap[i];

                    if (glyph != lastGlyph)
                    { 
                        caretStops[i] = true;
                        lastGlyph = glyph; 
                    } 
                }
            } 

            return GlyphRun.TryCreate(
                _shapeTypeface.GlyphTypeface,
                (rightToLeft ? 1 : 0), 
                sideways,
                _emSize, 
                glyphIndices, 
                origin,
                glyphAdvances, 
                glyphOffsets,
                characterString,
                null,
                clusterMap, 
                caretStops,
                XmlLanguage.GetLanguage(CultureMapper.GetSpecificCulture(_properties.CultureInfo).IetfLanguageTag) 
                ); 
        }
 
        private GlyphTypeface GetGlyphTypeface(out bool nullFont)
        {
            GlyphTypeface glyphTypeface;
 
            if (_shapeTypeface == null)
            { 
                // We're in the optimized path where the GlyphTypeface depends only 
                // on the Typeface, not on the particular input characters.
                Typeface typeface = _properties.Typeface; 

                // Get the GlyphTypeface.
                glyphTypeface = typeface.TryGetGlyphTypeface();
 
                // If Typeface does not specify *any* valid font family, then we use
                // the GlyphTypeface for Arial but only to display missing glyphs. 
                nullFont = typeface.NullFont; 
            }
            else 
            {
                // Font linking has mapped the input to a specific GlyphTypeface.
                glyphTypeface = _shapeTypeface.GlyphTypeface;
 
                // If font linking could not find *any* physical font family, then we
                // use the GlyphTypeface for Arial but only to display missing glyphs. 
                nullFont = _nullShape; 
            }
 
            Invariant.Assert(glyphTypeface != null);
            return glyphTypeface;
        }
 
        /// 
        /// Compute unshaped glyph run object from the specified character-based info 
        ///  
        internal sealed override GlyphRun ComputeUnshapedGlyphRun(
            Point         origin, 
            char[]        characterString,
            IList characterAdvances
            )
        { 
            bool nullFont;
            GlyphTypeface glyphTypeface = GetGlyphTypeface(out nullFont); 
            Invariant.Assert(glyphTypeface != null); 

            return glyphTypeface.ComputeUnshapedGlyphRun( 
                origin,
                new CharacterBufferRange(
                    characterString,
                    0,  // offsetToFirstChar 
                    characterString.Length
                    ), 
                characterAdvances, 
                _emSize,
                _properties.FontHintingEmSize, 
                nullFont,
                CultureMapper.GetSpecificCulture(_properties.CultureInfo),
                (_shapeTypeface == null  ||  _shapeTypeface.DeviceFont == null) ? null : _shapeTypeface.DeviceFont.Name
            ); 
        }
 
 
        /// 
        /// Draw glyph run to the drawing surface 
        /// 
        internal sealed override void Draw(
            DrawingContext      drawingContext,
            Brush               foregroundBrush, 
            GlyphRun            glyphRun
            ) 
        { 
            if (drawingContext == null)
                throw new ArgumentNullException("drawingContext"); 

            glyphRun.EmitBackground(drawingContext, _properties.BackgroundBrush);

            drawingContext.DrawGlyphRun( 
                foregroundBrush != null ? foregroundBrush : _properties.ForegroundBrush,
                glyphRun 
                ); 
        }
 

        /// 
        /// Get advance widths of unshaped characters
        ///  
        /// 
        ///    Critical: This code calls into unsafe code blocks 
        ///  
        [SecurityCritical]
        internal sealed override unsafe void GetAdvanceWidthsUnshaped( 
            char*         characterString,
            int           characterLength,
            double        scalingFactor,
            int*          advanceWidthsUnshaped 
            )
        { 
            if (!IsShapingRequired) 
            {
                if (    (_shapeTypeface            != null) 
                    &&  (_shapeTypeface.DeviceFont != null))
                {
                    // Use device font to compute advance widths
                    _shapeTypeface.DeviceFont.GetAdvanceWidths( 
                        characterString,
                        characterLength, 
                        _emSize * scalingFactor, 
                        advanceWidthsUnshaped
                    ); 
                }
                else
                {
                    bool nullFont; 
                    GlyphTypeface glyphTypeface = GetGlyphTypeface(out nullFont);
                    Invariant.Assert(glyphTypeface != null); 
 
                    glyphTypeface.GetAdvanceWidthsUnshaped(
                        characterString, 
                        characterLength,
                        _emSize * scalingFactor,
                        advanceWidthsUnshaped,
                        nullFont 
                        );
                } 
            } 
            else
            { 
                GlyphTypeface glyphTypeface = _shapeTypeface.GlyphTypeface;

                Invariant.Assert(glyphTypeface != null);
                Invariant.Assert(characterLength > 0); 

                IDictionary cmap = glyphTypeface.CharacterToGlyphMap; 
 
                // compensate for approximated usual reduction of shaped widths for complex script
                scalingFactor *= ApproximateNominalToIdealWidthRatio * _emSize; 

                for (int i = 0; i < characterLength; i++)
                {
                    ushort glyphIndex; 
                    cmap.TryGetValue(characterString[i], out glyphIndex);
                    advanceWidthsUnshaped[i] = (int)Math.Round(scalingFactor * glyphTypeface.GetAdvanceWidth(glyphIndex)); 
                } 
            }
        } 


        /// 
        /// Get the ratio of typical ideal glyph width over nominal character width. It is used to 
        /// compensate the usual reduction of shaped widths.
        ///  
        internal sealed override double ApproximateNominalToIdealWidthRatio 
        {
            get 
            {
                double nominalToIdealWidthRatio = 1.0;

                //  Apply a scaling ratio if: 
                //  1) the script is not Latin (hence more likely to formed into ligature)
                //  2) or the run contains combining mark or extended characters 
                if (    _textItem.Script != ScriptID.Latin 
                    ||  (_textItem.Flags & (ItemFlags.HasCombiningMark | ItemFlags.HasExtendedCharacter)) != 0
                    ) 
                {
                    nominalToIdealWidthRatio = IsScriptIndic(_textItem.Script) ? 0.35 : 0.75;
                }
 
                return nominalToIdealWidthRatio;
            } 
        } 

        ///  
        /// Returns true if specified script is Indic.
        /// 
        private bool IsScriptIndic(ScriptID script)
        { 
            return script == ScriptID.Bengali
                || script == ScriptID.Devanagari 
                || script == ScriptID.Gurmukhi 
                || script == ScriptID.Gujarati
                || script == ScriptID.Kannada 
                || script == ScriptID.Malayalam
                || script == ScriptID.Oriya
                || script == ScriptID.Tamil
                || script == ScriptID.Telugu; 
        }
 
        ///  
        /// Return value indicates whether two runs can shape together
        ///  
        internal sealed override bool CanShapeTogether(
            TextShapeableSymbols   shapeable
            )
        { 
            TextShapeableCharacters charShape = shapeable as TextShapeableCharacters;
 
            if (charShape == null) 
                return false;
 
            return
                    _shapeTypeface.Equals(charShape._shapeTypeface)
                &&  _textItem.Script == charShape._textItem.Script
                // Extended characters need to be shaped by surrogate shaper. They cannot be shaped together with non-exteneded characters. 
                &&  (_textItem.Flags & ItemFlags.HasExtendedCharacter) == (charShape._textItem.Flags & ItemFlags.HasExtendedCharacter)
                &&  _emSize == charShape._emSize 
                &&  ( 
                    _properties.CultureInfo == null ?
                        charShape._properties.CultureInfo == null 
                      : _properties.CultureInfo.Equals(charShape._properties.CultureInfo)
                    )
                &&  _nullShape == charShape._nullShape;
        } 

 
        ///  
        /// Indicate whether run cannot be treated as simple characters because shaping is required.
        /// 
        /// The following cases use simple rendering without shaping:
        ///   o  No _shapeTypeface. This happens in very simple rendering cases.
        ///   o  Non-Unicode (i.e. symbol) fonts.
        ///   o  When using a device font. 
        ///
        /// Note that the presence of a device font in _shapeTypeface.DeviceFont implies use of 
        /// a device font in all cases except where digit substitution applies. This special 
        /// case occurs because the cached result per codepoint of TypefaceMap must include the device font
        /// for non-western digits in order to support device font rendering of the non-Western 
        /// digit Unicode codepoints. The device font is not used however when the non-Western digits
        /// are displayed as a result of digit substitution from backing store Western digits.
        /// 
        internal sealed override bool IsShapingRequired 
        {
            get 
            { 
                return
                        (_shapeTypeface != null)                 // Can't use shaping without a shape typeface 
                    &&  (    (_shapeTypeface.DeviceFont == null) // Can't use shaping when rendering with a device font
                         ||  (_textItem.DigitCulture != null))   //   -- unless substituting digits
                    &&  (!IsSymbol);                             // Can't use shaping for symbol (non-Unicode) fonts
            } 
        }
 
 
        /// 
        /// A Boolean value indicates whether additional info is required for caret positioning 
        /// 
        internal sealed override bool NeedsCaretInfo
        {
            get 
            {
                return  (_textItem.Flags & ItemFlags.HasCombiningMark) != 0 
                    ||  (Script.Flags[(int)_textItem.Script] & ScriptFlags.NeedsCaretInfo) != 0; 
            }
        } 


        /// 
        /// A Boolean value indicates whether run has extended character 
        /// 
        internal sealed override bool HasExtendedCharacter 
        { 
            get
            { 
                return (_textItem.Flags & ItemFlags.HasExtendedCharacter) != 0;
            }
        }
 

        ///  
        /// Run height 
        /// 
        internal sealed override double Height 
        {
            get
            {
                return _properties.Typeface.LineSpacing * _properties.FontRenderingEmSize; 
            }
        } 
 

        ///  
        /// Distance from top to baseline
        /// 
        internal sealed override double Baseline
        { 
            get
            { 
                return _properties.Typeface.Baseline * _properties.FontRenderingEmSize; 
            }
        } 


        /// 
        /// Distance from baseline to underline position relative to TextRunProperties.FontRenderingEmSize 
        /// 
        internal sealed override double UnderlinePosition 
        { 
            get
            { 
                return _properties.Typeface.UnderlinePosition;
            }
        }
 

        ///  
        /// Underline thickness relative to TextRunProperties.FontRenderingEmSize 
        /// 
        internal sealed override double UnderlineThickness 
        {
            get
            {
                return _properties.Typeface.UnderlineThickness; 
            }
        } 
 

        ///  
        /// Distance from baseline to strike-through position relative to TextRunProperties.FontRenderingEmSize
        /// 
        internal sealed override double StrikethroughPosition
        { 
            get
            { 
                return _properties.Typeface.StrikethroughPosition; 
            }
        } 


        /// 
        /// strike-through thickness relative to TextRunProperties.FontRenderingEmSize 
        /// 
        internal sealed override double StrikethroughThickness 
        { 
            get
            { 
                return _properties.Typeface.StrikethroughThickness;
            }
        }
 
        #endregion
 
 
        /// 
        /// Whether all characters in this run are non-Unicode character (symbol) 
        /// 
        internal bool IsSymbol
        {
            get 
            {
                if (_shapeTypeface != null) 
                    return _shapeTypeface.GlyphTypeface.Symbol; 

                return _properties.Typeface.Symbol; 
            }
        }

        ///  
        /// Returns maximum possible cluster size for the run.  Normally, this
        /// is 8 characters, but Indic scripts require this to be 15. 
        ///  
        internal const ushort DefaultMaxClusterSize = 8;
        private  const ushort IndicMaxClusterSize = 15; 
        internal sealed override ushort MaxClusterSize
        {
            get
            { 
                switch (_textItem.Script)
                { 
                    case ScriptID.Bengali: 
                    case ScriptID.Devanagari:
                    case ScriptID.Gurmukhi: 
                    case ScriptID.Gujarati:
                    case ScriptID.Kannada:
                    case ScriptID.Malayalam:
                    case ScriptID.Oriya: 
                    case ScriptID.Tamil:
                    case ScriptID.Telugu: 
                        return IndicMaxClusterSize; 
                }
 
                return DefaultMaxClusterSize;
            }
        }
    } 
}
 

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