示例#1
0
文件: Typeface.cs 项目: yk2012985/wpf
        /// <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);
            }
        }