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