/// <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); } }