Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / TextFormatting / TextShapeableCharacters.cs / 1 / TextShapeableCharacters.cs
//------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation // // File: TextShapeableCharacters.cs // // Contents: Implementation of text shapeable symbols for characters // // Spec: http://team/sites/Avalon/Specs/Text%20Formatting%20API.doc // // Created: 1-2-2004 Worachai Chaoweeraprasit (wchao) // 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, IListglyphAdvances, 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, IListcharacterAdvances ) { 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); IDictionarycmap = 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. //------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation // // File: TextShapeableCharacters.cs // // Contents: Implementation of text shapeable symbols for characters // // Spec: http://team/sites/Avalon/Specs/Text%20Formatting%20API.doc // // Created: 1-2-2004 Worachai Chaoweeraprasit (wchao) // 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, IListglyphAdvances, 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, IListcharacterAdvances ) { 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); IDictionarycmap = 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AuthStoreRoleProvider.cs
- CngKeyBlobFormat.cs
- SystemFonts.cs
- PointHitTestResult.cs
- DataColumnChangeEvent.cs
- MostlySingletonList.cs
- CacheVirtualItemsEvent.cs
- InvalidateEvent.cs
- BrowsableAttribute.cs
- IndentedWriter.cs
- NameValuePermission.cs
- RenderTargetBitmap.cs
- Attributes.cs
- ToolStripItemCollection.cs
- VirtualPathProvider.cs
- BulletedList.cs
- RootDesignerSerializerAttribute.cs
- ImageField.cs
- URLEditor.cs
- SiteMembershipCondition.cs
- RangeValidator.cs
- EdmConstants.cs
- ConsoleEntryPoint.cs
- Ref.cs
- FormsAuthentication.cs
- ParentUndoUnit.cs
- QilTargetType.cs
- ToolStripMenuItem.cs
- CharacterBufferReference.cs
- PolyBezierSegment.cs
- XmlToDatasetMap.cs
- Boolean.cs
- SByteConverter.cs
- Repeater.cs
- ClientEventManager.cs
- SignedInfo.cs
- ApplicationProxyInternal.cs
- RuntimeCompatibilityAttribute.cs
- ConfigXmlElement.cs
- HostedHttpContext.cs
- StorageFunctionMapping.cs
- SqlTypesSchemaImporter.cs
- IBuiltInEvidence.cs
- ParserExtension.cs
- FontFamily.cs
- PanelStyle.cs
- GlobalAllocSafeHandle.cs
- Section.cs
- ComponentResourceKey.cs
- Win32Exception.cs
- Propagator.ExtentPlaceholderCreator.cs
- PathGeometry.cs
- SqlNamer.cs
- NameValueConfigurationElement.cs
- ServiceProviders.cs
- SubstitutionList.cs
- ActivityCodeDomSerializer.cs
- ContentOperations.cs
- ItemsPresenter.cs
- InkCollectionBehavior.cs
- SubstitutionDesigner.cs
- EditorPart.cs
- IIS7UserPrincipal.cs
- UnsignedPublishLicense.cs
- ErrorsHelper.cs
- ControlBuilder.cs
- DynamicDocumentPaginator.cs
- securitycriticaldata.cs
- AuthenticatingEventArgs.cs
- XsdBuilder.cs
- WindowsAltTab.cs
- ProxySimple.cs
- UserPersonalizationStateInfo.cs
- HeaderedContentControl.cs
- TextClipboardData.cs
- ControlDesignerState.cs
- Label.cs
- Attribute.cs
- ConnectionPoint.cs
- HtmlTextArea.cs
- TextRunCache.cs
- WindowsScroll.cs
- DataViewSettingCollection.cs
- InputBinding.cs
- GridSplitter.cs
- EventWaitHandleSecurity.cs
- GlobalizationSection.cs
- Point3DCollectionValueSerializer.cs
- ListBoxItem.cs
- HitTestFilterBehavior.cs
- SelectionItemProviderWrapper.cs
- RectConverter.cs
- ExtensionElement.cs
- DesignerAttribute.cs
- RecognizerStateChangedEventArgs.cs
- BinaryKeyIdentifierClause.cs
- BooleanProjectedSlot.cs
- GridViewRowCollection.cs
- ObjectCloneHelper.cs
- BadImageFormatException.cs