Example #1
0
        /// <summary>
        /// Map character supported by the typeface
        /// </summary>
        /// <remarks>
        /// Combining mark is considered part of the character that may be supported
        /// thru precomposed form or OpenType glyph substitution table.
        /// </remarks>
        private int MapCharacters(
            MS.Internal.Text.TextInterface.Font font,
            CharacterBufferRange unicodeString,
            CultureInfo digitCulture,
            ref int nextValid
            )
        {
            DigitMap digitMap = new DigitMap(digitCulture);

            int sizeofChar = 0;
            int advance;

            // skip all the leading joiner characters. They need to be shaped with the
            // surrounding strong characters.
            advance = Classification.AdvanceWhile(unicodeString, ItemClass.JoinerClass);
            if (advance >= unicodeString.Length)
            {
                // It is rare that the run only contains joiner characters.
                // If it really happens, just return.
                return(advance);
            }

            //
            // If the run starts with combining marks, we will not be able to find base characters for them
            // within the run. These combining marks will be mapped to their best fonts as normal characters.
            //
            bool hasBaseChar = false;

            // Determine how many characters we can advance, i.e., find the first invalid character.
            for (; advance < unicodeString.Length; advance += sizeofChar)
            {
                // Get the character and apply digit substitution, if any.
                int originalChar = Classification.UnicodeScalar(
                    new CharacterBufferRange(unicodeString, advance, unicodeString.Length - advance),
                    out sizeofChar
                    );

                if (Classification.IsJoiner(originalChar))
                {
                    continue;
                }

                if (!Classification.IsCombining(originalChar))
                {
                    hasBaseChar = true;
                }
                else if (hasBaseChar)
                {
                    // continue to advance for combining mark with base char
                    continue;
                }

                int ch = digitMap[originalChar];

                if (font.HasCharacter(checked ((uint)ch)))
                {
                    continue;
                }

                // If ch is a substituted character, can we substitute a different character instead?
                if (ch != originalChar)
                {
                    ch = DigitMap.GetFallbackCharacter(ch);
                    if (ch != 0 && font.HasCharacter(checked ((uint)ch)))
                    {
                        continue;
                    }
                }

                // If we fall through to here it's invalid.
                break;
            }

            // UnicodeScalar won't return a sizeofChar that exceeds the string length.
            Debug.Assert(advance <= unicodeString.Length);

            // Find the next valid character.
            if (advance < unicodeString.Length)
            {
                // UnicodeScalar won't return a sizeofChar that exceeds the string length.
                Debug.Assert(advance + sizeofChar <= unicodeString.Length);

                for (nextValid = advance + sizeofChar; nextValid < unicodeString.Length; nextValid += sizeofChar)
                {
                    // Get the character.
                    int originalChar = Classification.UnicodeScalar(
                        new CharacterBufferRange(unicodeString, nextValid, unicodeString.Length - nextValid),
                        out sizeofChar
                        );

                    // Apply digit substitution, if any.
                    int ch = digitMap[originalChar];

                    //
                    // Combining mark should always be shaped by the same font as the base char.
                    // If the physical font is invalid for the base char, it should also be invalid for the
                    // following combining mark so that both characters will go onto the same fallback font.
                    // - When the fallback font is physical font, the font will be valid for both characters
                    //   if and only if it is valid for the base char. Otherwise, it will be invalid for both.
                    // - When the fallback font is composite font, it maps the combining mark to the same font
                    //   as the base char such that they will eventually be resolved to the same physical font.
                    //   That means FamilyMap for the combining mark is not used when it follows a base char.
                    //
                    // The same goes for joiner. Note that "hasBaseChar" here indicates if there is an invalid base
                    // char in front.
                    if (Classification.IsJoiner(ch) ||
                        (hasBaseChar && Classification.IsCombining(ch))
                        )
                    {
                        continue;
                    }

                    // If we have a glyph it's valid.
                    if (font.HasCharacter(checked ((uint)ch)))
                    {
                        break;
                    }

                    // If ch is a substituted character, can we substitute a different character instead?
                    if (ch != originalChar)
                    {
                        ch = DigitMap.GetFallbackCharacter(ch);
                        if (ch != 0 && font.HasCharacter(checked ((uint)ch)))
                        {
                            break;
                        }
                    }
                }
            }

            return(advance);
        }
Example #2
0
        internal GlyphTypeface MapGlyphTypeface(
            FontStyle style,
            FontWeight weight,
            FontStretch stretch,
            CharacterBufferRange charString,
            CultureInfo digitCulture,
            ref int advance,
            ref int nextValid
            )
        {
            int smallestInvalid = charString.Length;

            // Add all the cached font faces to a priority queue.
            MatchingStyle targetStyle = new MatchingStyle(style, weight, stretch);

            PriorityQueue <MatchingFace> queue = new PriorityQueue <MatchingFace>(
                checked ((int)_family.Count),
                new MatchingFaceComparer(targetStyle));

            foreach (Text.TextInterface.Font face in _family)
            {
                queue.Push(new MatchingFace(face));
            }

            // Remember the best style match.
            MS.Internal.Text.TextInterface.Font bestStyleTypeface = null;

            // Iterate in priority order.
            for (; queue.Count != 0; queue.Pop())
            {
                int invalid = 0;
                MS.Internal.Text.TextInterface.Font font = queue.Top.FontFace;
                int valid = MapCharacters(font, charString, digitCulture, ref invalid);
                if (valid > 0)
                {
                    if (smallestInvalid > 0 && smallestInvalid < valid)
                    {
                        // advance only to smallestInvalid because there's a better match after that
                        advance   = smallestInvalid;
                        nextValid = 0;
                    }
                    else
                    {
                        advance   = valid;
                        nextValid = invalid;
                    }

                    return(new GlyphTypeface(font));
                }
                else
                {
                    if (invalid < smallestInvalid)
                    {
                        // keep track of the current shortest length of invalid characters,
                        smallestInvalid = invalid;
                    }

                    if (bestStyleTypeface == null)
                    {
                        bestStyleTypeface = font;
                    }
                }
            }

            // no face can map the specified character string,
            // fall back to the closest style match
            advance   = 0;
            nextValid = smallestInvalid;
            Debug.Assert(bestStyleTypeface != null);
            return(new GlyphTypeface(bestStyleTypeface));
        }