/// <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);
            }
        }
Beispiel #2
0
        private void ComputeTypographyAvailabilities()
        {
            int glyphBitsLength = (GlyphCount + 31) >> 5;

            uint[] glyphBits = BufferCache.GetUInts(glyphBitsLength);
            Array.Clear(glyphBits, 0, glyphBitsLength);

            ushort minGlyphId = 65535;
            ushort maxGlyphId = 0;

            WritingSystem[]          complexScripts;
            TypographyAvailabilities typography = TypographyAvailabilities.None;

            GsubGposTables GsubGpos = new GsubGposTables(this);

            // preparing the glyph bits. When the bit is set, it means the corresponding
            // glyph needs to be checked against.
            for (int i = 0; i < fastTextRanges.Length; i++)
            {
                uint[]   codepoints   = fastTextRanges[i].GetFullRange();
                ushort[] glyphIndices = BufferCache.GetUShorts(codepoints.Length);

                unsafe
                {
                    fixed(uint *pCodepoints = &codepoints[0])
                    {
                        fixed(ushort *pGlyphIndices = &glyphIndices[0])
                        {
                            CharacterMap.TryGetValues(pCodepoints, checked ((uint)codepoints.Length), pGlyphIndices);
                        }
                    }
                }

                for (int j = 0; j < codepoints.Length; j++)
                {
                    ushort glyphId = glyphIndices[j];
                    if (glyphId != 0)
                    {
                        glyphBits[glyphId >> 5] |= (uint)(1 << (glyphId % 32));

                        if (glyphId > maxGlyphId)
                        {
                            maxGlyphId = glyphId;
                        }
                        if (glyphId < minGlyphId)
                        {
                            minGlyphId = glyphId;
                        }
                    }
                }

                BufferCache.ReleaseUShorts(glyphIndices);
            }

            //
            // Step 1: call OpenType layout engine to test presence of
            // 'locl' feature. Based on the returned writing systems, set
            // FastTextMajorLanguageLocalizedFormAvailable bit and
            // FastTextExtraLanguageLocalizedFormAvailable bit
            //
            OpenTypeLayoutResult result;

            unsafe
            {
                result = OpenTypeLayout.GetComplexLanguageList(
                    GsubGpos,
                    LoclFeature,
                    glyphBits,
                    minGlyphId,
                    maxGlyphId,
                    out complexScripts
                    );
            }

            if (result != OpenTypeLayoutResult.Success)
            {
                // The check failed. We abort and don't keep partial results that are not reliable
                _typographyAvailabilities = TypographyAvailabilities.None;
                return;
            }
            else if (complexScripts != null)
            {
                // This is the bits for localized form we would want to set
                // if both bits for localized form were set, we can end the loop earlier
                TypographyAvailabilities loclBitsTest =
                    TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable
                    | TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;

                for (int i = 0; i < complexScripts.Length && typography != loclBitsTest; i++)
                {
                    if (MajorLanguages.Contains((ScriptTags)complexScripts[i].scriptTag, (LanguageTags)complexScripts[i].langSysTag))
                    {
                        typography |= TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable;
                    }
                    else
                    {
                        typography |= TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;
                    }
                }
            }

            //
            // step 2: continue to find out whether there is common features availabe
            // in the font for the unicode ranges and set the FastTextTypographyAvailable bit
            //
            unsafe
            {
                result = OpenTypeLayout.GetComplexLanguageList(
                    GsubGpos,
                    RequiredTypographyFeatures,
                    glyphBits,
                    minGlyphId,
                    maxGlyphId,
                    out complexScripts
                    );
            }

            if (result != OpenTypeLayoutResult.Success)
            {
                // The check failed. We abort and don't keep partial results that are not reliable
                _typographyAvailabilities = TypographyAvailabilities.None;
                return;
            }
            else if (complexScripts != null)
            {
                typography |= TypographyAvailabilities.FastTextTypographyAvailable;
            }

            //
            // Step 3: call OpenType layout engine to find out if there is any feature present for
            // ideographs. Because there are many ideographs to check for, an alternative is to
            // check for all scripts with the required features in the font by setting all
            // glyph bits to 1, then see whether CJKIdeograph is in the returned list.
            //
            for (int i = 0; i < glyphBitsLength; i++)
            {
                glyphBits[i] = 0xFFFFFFFF;
            }

            unsafe
            {
                result = OpenTypeLayout.GetComplexLanguageList(
                    GsubGpos,
                    RequiredFeatures,
                    glyphBits,
                    minGlyphId,
                    maxGlyphId,
                    out complexScripts
                    );
            }

            if (result != OpenTypeLayoutResult.Success)
            {
                // The check failed. We abort and don't keep partial results that are not reliable
                _typographyAvailabilities = TypographyAvailabilities.None;
                return;
            }
            else if (complexScripts != null)
            {
                for (int i = 0; i < complexScripts.Length; i++)
                {
                    if (complexScripts[i].scriptTag == (uint)ScriptTags.CJKIdeographic)
                    {
                        typography |= TypographyAvailabilities.IdeoTypographyAvailable;
                    }
                    else
                    {
                        typography |= TypographyAvailabilities.Available;
                    }
                }
            }

            if (typography != TypographyAvailabilities.None)
            {
                // if any of the bits were set, set TypographyAvailabilities.Avaialble bit
                // as well to indicate some lookup is available.
                typography |= TypographyAvailabilities.Available;
            }

            _typographyAvailabilities = typography;

            // Note: we don't worry about calling ReleaseUInts in case of early out for a failure
            // above.  Releasing glyphBits is a performance optimization that is not necessary
            // for correctness, and not interesting in the rare failure case.
            BufferCache.ReleaseUInts(glyphBits);
        }