/// <summary> /// Lookup characters nominal glyphs and width /// </summary> /// <param name="charBufferRange">character buffer range</param> /// <param name="emSize">height of Em</param> /// <param name="toIdeal"> scaling factor from real to ideal unit </param> /// <param name="nominalWidths">glyph nominal advances in ideal units</param> /// <param name="idealWidth">total width in ideal units</param> /// <returns>true for success</returns> /// <remarks>This function is only used in fast path, and can only be called /// if CheckFastPathNominalGlyphs has previously returned true.</remarks> internal void GetCharacterNominalWidthsAndIdealWidth( CharacterBufferRange charBufferRange, double emSize, float pixelsPerDip, double toIdeal, TextFormattingMode textFormattingMode, bool isSideways, out int[] nominalWidths, out int idealWidth ) { // This function should only be called if CheckFastPathNominalGlyphs has // returned true so we can assume the ITypefaceMetrics is a GlyphTypeface. GlyphTypeface glyphTypeface = TryGetGlyphTypeface(); Invariant.Assert(glyphTypeface != null); MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(charBufferRange.Length); glyphTypeface.GetGlyphMetricsOptimized(charBufferRange, emSize, pixelsPerDip, textFormattingMode, isSideways, glyphMetrics); nominalWidths = new int[charBufferRange.Length]; idealWidth = 0; if (TextFormattingMode.Display == textFormattingMode) { double designToEm = emSize / glyphTypeface.DesignEmHeight; for (int i = 0; i < charBufferRange.Length; i++) { nominalWidths[i] = (int)Math.Round(TextFormatterImp.RoundDipForDisplayMode(glyphMetrics[i].AdvanceWidth * designToEm, pixelsPerDip) * toIdeal); idealWidth += nominalWidths[i]; } } else { double designToEm = emSize * toIdeal / glyphTypeface.DesignEmHeight; for (int i = 0; i < charBufferRange.Length; i++) { nominalWidths[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm); idealWidth += nominalWidths[i]; } } BufferCache.ReleaseGlyphMetrics(glyphMetrics); }
/// <summary> /// Scan through specified character string checking for valid character /// nominal glyph. /// </summary> /// <param name="charBufferRange">character buffer range</param> /// <param name="emSize">height of Em</param> /// <param name="scalingFactor">This is the factor by which we will scale up /// the metrics. Typically this value to used to convert metrics from the real /// space to the ideal space</param> /// <param name="widthMax">maximum width allowed</param> /// <param name="keepAWord">do not stop arbitrarily in the middle of a word</param> /// <param name="numberSubstitution">digits require complex shaping</param> /// <param name="cultureInfo">CultureInfo of the text</param> /// <param name="textFormattingMode">The TextFormattingMode used (Ideal vs. Display)</param> /// <param name="isSideways">Indicates whether to rotate glyphs.</param> /// <param name="breakOnTabs">Determines whether to stop checking at a tab and /// break the run there</param> /// <param name="stringLengthFit">number of character fit in given width</param> /// <returns>whether the specified string can be optimized by nominal glyph lookup</returns> internal bool CheckFastPathNominalGlyphs( CharacterBufferRange charBufferRange, double emSize, double scalingFactor, double widthMax, bool keepAWord, bool numberSubstitution, CultureInfo cultureInfo, TextFormattingMode textFormattingMode, bool isSideways, bool breakOnTabs, out int stringLengthFit ) { stringLengthFit = 0; if (CachedTypeface.NullFont) { return(false); } GlyphTypeface glyphTypeface = TryGetGlyphTypeface(); if (glyphTypeface == null) { return(false); } double totalWidth = 0; int i = 0; ushort blankGlyph = glyphTypeface.BlankGlyphIndex; ushort glyph = blankGlyph; ushort charFlagsMask = numberSubstitution ? (ushort)(CharacterAttributeFlags.CharacterComplex | CharacterAttributeFlags.CharacterDigit) : (ushort)CharacterAttributeFlags.CharacterComplex; ushort charFlags = 0; ushort charFastTextCheck = (ushort)(CharacterAttributeFlags.CharacterFastText | CharacterAttributeFlags.CharacterIdeo); bool symbolTypeface = glyphTypeface.Symbol; if (symbolTypeface) { // we don't care what code points are present if it's a non-Unicode font such as Symbol or Wingdings; // the code points don't have any standardized meanings, and we always want to bypass shaping charFlagsMask = 0; } bool ignoreWidths = widthMax == double.MaxValue; ushort[] glyphIndices = BufferCache.GetUShorts(charBufferRange.Length); MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = ignoreWidths ? null : BufferCache.GetGlyphMetrics(charBufferRange.Length); glyphTypeface.GetGlyphMetricsOptimized(charBufferRange, emSize, glyphIndices, glyphMetrics, textFormattingMode, isSideways ); double designToEm = emSize / glyphTypeface.DesignEmHeight; // // This block will advance until one of: // 1. The end of the charBufferRange is reached // 2. The charFlags have some of the charFlagsMask values // 3. The glyph is BlankGlyph (space) // 4. Glyph index is 0 (unless symbol font) // // At this point totalWidth includes all of the widths including the stop character (which fits above) // i indexes the next character (not included in the width) // if (keepAWord) { do { char ch = charBufferRange[i++]; if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn || (breakOnTabs && ch == TextStore.CharTab)) { --i; break; } else { int charClass = (int)Classification.GetUnicodeClassUTF16(ch); charFlags = Classification.CharAttributeOf(charClass).Flags; charFastTextCheck &= charFlags; glyph = glyphIndices[i - 1]; if (!ignoreWidths) { totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor; } } } while( i < charBufferRange.Length && ((charFlags & charFlagsMask) == 0) && (glyph != 0 || symbolTypeface) && glyph != blankGlyph ); // i is now at a character immediately following a leading blank } // // This block will advance until one of: // 1. The end of the charBufferRange is reached // 2. The charFlags have some of the charFlagsMask values // 3. Glyph index is 0 (unless symbol font) // 4. totalWidth > widthMax // while ( i < charBufferRange.Length && (ignoreWidths || totalWidth <= widthMax) && ((charFlags & charFlagsMask) == 0) && (glyph != 0 || symbolTypeface) ) { char ch = charBufferRange[i++]; if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn || (breakOnTabs && ch == TextStore.CharTab)) { --i; break; } else { int charClass = (int)Classification.GetUnicodeClassUTF16(ch); charFlags = Classification.CharAttributeOf(charClass).Flags; charFastTextCheck &= charFlags; glyph = glyphIndices[i - 1]; if (!ignoreWidths) { totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor; } } } BufferCache.ReleaseUShorts(glyphIndices); glyphIndices = null; BufferCache.ReleaseGlyphMetrics(glyphMetrics); glyphMetrics = null; if (symbolTypeface) { // always optimize for non-Unicode font as we don't support shaping or typographic features; // we also don't fall back from non-Unicode fonts so we don't care if there are missing glyphs stringLengthFit = i; return(true); } if (glyph == 0) { // character is not supported by the font return(false); } if ((charFlags & charFlagsMask) != 0) { // complex character encountered, exclude it Debug.Assert(i > 0); if (--i <= 0) { // first char is complex, fail the call return(false); } } stringLengthFit = i; TypographyAvailabilities typography = glyphTypeface.FontFaceLayoutInfo.TypographyAvailabilities; if ((charFastTextCheck & (byte)CharacterAttributeFlags.CharacterFastText) != 0) { // all input code points are Fast Text if ((typography & (TypographyAvailabilities.FastTextTypographyAvailable | TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable ) ) != 0 ) { // Considered too risky to optimize. It is either because the font // has required features or the font has 'locl' lookup for major languages. return(false); } else if ((typography & TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable) != 0) { // The font has 'locl' lookup for FastText code points for non major languages. // Check whether the input is major langauge. If it is, we are still good to optimize. return(MajorLanguages.Contains(cultureInfo)); } else { // No FastText flags are present, safe to optimize return(true); } } else if ((charFastTextCheck & (byte)CharacterAttributeFlags.CharacterIdeo) != 0) { // The input are all ideographs, check the IdeoTypographyAvailable bit. It is safe if // the bit is not set. return((typography & TypographyAvailabilities.IdeoTypographyAvailable) == 0); } else { // for all the rest of the cases, just check whether there is any required typography // present at all. If none exists, it is optimizable. We might under-optimize here but // it will be non-major languages. return((typography & TypographyAvailabilities.Available) == 0); } }
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, (float)_properties.PixelsPerDip, scalingFactor, advanceWidthsUnshaped, nullFont, _textFormattingMode, _isSideways ); } } else { GlyphTypeface glyphTypeface = _shapeTypeface.GlyphTypeface; Invariant.Assert(glyphTypeface != null); Invariant.Assert(characterLength > 0); CharacterBufferRange newBuffer = new CharacterBufferRange(characterString, characterLength); MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(characterLength); glyphTypeface.GetGlyphMetricsOptimized(newBuffer, _emSize, (float)_properties.PixelsPerDip, _textFormattingMode, _isSideways, glyphMetrics ); if (_textFormattingMode == TextFormattingMode.Display && TextFormatterContext.IsSpecialCharacter(*characterString)) { // (DDVSO 92892) If the run starts with a special character (in // the LineServices sense), we apply display-mode rounding now, // as we won't get another chance. This assumes that the characters // in a run are either all special or all non-special; that assumption // is valid in the current LS implementation. double designToEm = _emSize / glyphTypeface.DesignEmHeight; double pixelsPerDip = _properties.PixelsPerDip; for (int i = 0; i < characterLength; i++) { advanceWidthsUnshaped[i] = (int)Math.Round(TextFormatterImp.RoundDipForDisplayMode(glyphMetrics[i].AdvanceWidth * designToEm, pixelsPerDip) * scalingFactor); } } else { // For the normal case, rounding is applied later on when LS // invokes the callback GetGlyphPositions, so that adjustments // due to justification and shaping are taken into account. double designToEm = _emSize * scalingFactor / glyphTypeface.DesignEmHeight; for (int i = 0; i < characterLength; i++) { advanceWidthsUnshaped[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm); } } BufferCache.ReleaseGlyphMetrics(glyphMetrics); } }
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, _textFormattingMode, _isSideways ); } } else { GlyphTypeface glyphTypeface = _shapeTypeface.GlyphTypeface; Invariant.Assert(glyphTypeface != null); Invariant.Assert(characterLength > 0); CharacterBufferRange newBuffer = new CharacterBufferRange(characterString, characterLength); MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(characterLength); glyphTypeface.GetGlyphMetricsOptimized(newBuffer, _emSize, _textFormattingMode, _isSideways, glyphMetrics ); double designToEm = _emSize * scalingFactor / glyphTypeface.DesignEmHeight; for (int i = 0; i < characterLength; i++) { advanceWidthsUnshaped[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm); } BufferCache.ReleaseGlyphMetrics(glyphMetrics); } }