/// <summary> /// Get spans of index to the list of scaled shapeable typeface of the specified /// character string from the map table /// </summary> private bool GetCachedScaledTypefaceMap( CharacterBufferRange unicodeString, CultureInfo culture, CultureInfo digitCulture, ref SpanVector <int> cachedScaledTypefaceIndexSpans, int ichItem ) { IntMap map; if (!_intMaps.TryGetValue(culture, out map)) { return(false); } DigitMap digitMap = new DigitMap(digitCulture); int ich = 0; while (ich < unicodeString.Length) { // Get map entry for first character. int sizeofChar; int ch = digitMap[ Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, ich, unicodeString.Length - ich), out sizeofChar ) ]; ushort firstIndex = map[ch]; if (firstIndex == 0) { return(false); } // Advance past subsequent characters with the same mapping. int cchSpan = sizeofChar; for (; ich + cchSpan < unicodeString.Length; cchSpan += sizeofChar) { ch = digitMap[ Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, ich + cchSpan, unicodeString.Length - ich - cchSpan), out sizeofChar ) ]; if (map[ch] != firstIndex && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch)) { break; } } // map entry is stored in index+1, since 0 indicates uninitialized entry cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cchSpan, firstIndex - 1); ich += cchSpan; } return(true); }
/// <summary> /// Map characters by font family /// </summary> /// <remarks> /// Advance: /// number of characters not mapped to missing glyph /// /// NextValid: /// Offset to the nearest first character not mapped to missing glyph /// /// [Number of invalid characters following valid ones] = NextValid - Advance /// /// A B C D E F G H x x x x x F G H I J /// ---------------> /// Advance /// /// -------------------------> /// NextValid /// /// </remarks> private int MapByFontFamily( CharacterBufferRange unicodeString, CultureInfo culture, CultureInfo digitCulture, IFontFamily fontFamily, CanonicalFontFamilyReference canonicalFamilyReference, FontStyle canonicalStyle, FontWeight canonicalWeight, FontStretch canonicalStretch, ref PhysicalFontFamily firstValidFamily, ref int firstValidLength, IDeviceFont deviceFont, double scaleInEm, int recursionDepth, SpanVector scaledTypefaceSpans, int firstCharIndex, out int nextValid ) { // This is the *one* place where we check for the font mapping depths of the font linking // process. This protects the linking process against extremely long chain of linking or // circular dependencies in the composite fonts. if (recursionDepth >= MaxTypefaceMapDepths) { // Stop the recursion. In effect, this FontFamily does not map any of the input. // Higher-level code must map the input text to some other FontFamily, or to the // "null font" if there is no valid FontFamily. nextValid = 0; return(0); } // If a device font is not already specified higher up the stack, look for a device font // for this font family that matches the typeface style, weight, and stretch. if (deviceFont == null) { deviceFont = fontFamily.GetDeviceFont(_canonicalStyle, _canonicalWeight, _canonicalStretch); } DigitMap digitMap = new DigitMap(digitCulture); int advance = 0; int cchAdvance; int cchNextValid; int ich = 0; nextValid = 0; bool terminated = false; while (ich < unicodeString.Length && !terminated) { // Determine length of run with consistent mapping. Start by assuming we'll be able to // use the whole string, then reduce to the length that can be mapped consistently. int cchMap = unicodeString.Length - ich; // Determine whether the run is using a device font, and limit the run to the // first boundary between device/non-device font usage. bool useDeviceFont = false; if (deviceFont != null) { // Determine whether the first run uses a device font by inspecting the first character. // We do not support device fonts for codepoints >= U+10000 (aka surrogates), so we // don't need to call Classification.UnicodeScalar. useDeviceFont = deviceFont.ContainsCharacter(digitMap[unicodeString[ich]]); // Advance as long as 'useDeviceFont' remains unchanged. int i = ich + 1; while ((i < unicodeString.Length) && (useDeviceFont == deviceFont.ContainsCharacter(digitMap[unicodeString[i]]))) { i++; } cchMap = i - ich; } // Map as many characters to a family as we can up to the limit (cchMap) just determined. string targetFamilyName; double mapSizeInEm; bool isCompositeFontFamily = fontFamily.GetMapTargetFamilyNameAndScale( new CharacterBufferRange( unicodeString, ich, cchMap ), culture, digitCulture, scaleInEm, out cchMap, out targetFamilyName, out mapSizeInEm ); Debug.Assert(cchMap <= unicodeString.Length - ich); CharacterBufferRange mappedString = new CharacterBufferRange( unicodeString, ich, cchMap ); if (!isCompositeFontFamily) { // not a composite font family cchAdvance = MapByFontFaceFamily( mappedString, culture, digitCulture, fontFamily, canonicalStyle, canonicalWeight, canonicalStretch, ref firstValidFamily, ref firstValidLength, useDeviceFont ? deviceFont : null, false, // nullFont mapSizeInEm, scaledTypefaceSpans, firstCharIndex + ich, false, // ignoreMissing out cchNextValid ); } else if (!string.IsNullOrEmpty(targetFamilyName)) { // The base Uri used for resolving target family names is the Uri of the composite font. Uri baseUri = (canonicalFamilyReference != null) ? canonicalFamilyReference.LocationUri : null; // map to the target of the family map cchAdvance = MapByFontFamilyName( mappedString, culture, digitCulture, targetFamilyName, baseUri, ref firstValidFamily, ref firstValidLength, useDeviceFont ? deviceFont : null, mapSizeInEm, recursionDepth + 1, // increment the depth scaledTypefaceSpans, firstCharIndex + ich, out cchNextValid ); } else { // family map lookup returned no target family cchAdvance = 0; cchNextValid = cchMap; } int cchValid = cchMap; int cchInvalid = 0; cchValid = cchAdvance; cchInvalid = cchNextValid; if (cchValid < cchMap) { terminated = true; } advance += cchValid; nextValid = ich + cchInvalid; ich += cchValid; } return(advance); }
/// <summary> /// Cache index to the list of scaled shapeable typeface /// </summary> private void CacheScaledTypefaceMap( CharacterBufferRange unicodeString, CultureInfo culture, CultureInfo digitCulture, SpanVector scaledTypefaceSpans, ref SpanVector <int> cachedScaledTypefaceIndexSpans, int ichItem ) { IntMap map; if (!_intMaps.TryGetValue(culture, out map)) { map = new IntMap(); _intMaps.Add(culture, map); } DigitMap digitMap = new DigitMap(digitCulture); SpanRider typefaceSpanRider = new SpanRider(scaledTypefaceSpans); int ich = 0; while (ich < unicodeString.Length) { typefaceSpanRider.At(ich); int cch = Math.Min(unicodeString.Length - ich, typefaceSpanRider.Length); int index = IndexOfScaledTypeface((ScaledShapeTypeface)typefaceSpanRider.CurrentElement); Debug.Assert(index >= 0, "Invalid scaled shapeable typeface index spans"); cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cch, index); // we keep index + 1 in the map, so that we leave map entry zero // to indicate uninitialized entry. index++; int sizeofChar; for (int c = 0; c < cch; c += sizeofChar) { int ch = digitMap[ Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, ich + c, unicodeString.Length - ich - c), out sizeofChar ) ]; // only cache typeface map index for base characters if (!Classification.IsCombining(ch) && !Classification.IsJoiner(ch)) { // Dump values of local variables when the condition fails for better debuggability. // We use "if" to avoid the expensive string.Format() in normal case. if (map[ch] != 0 && map[ch] != index) { Invariant.Assert( false, string.Format( CultureInfo.InvariantCulture, "shapeable cache stores conflicting info, ch = {0}, map[ch] = {1}, index = {2}", ch, map[ch], index ) ); } map[ch] = (ushort)index; } } ich += cch; } }
/// <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); }
private unsafe FamilyCollection.CachedFamilyMap *GetCachedFamilyMap( CharacterBufferRange unicodeString, CultureInfo culture, CultureInfo digitCulture, out int cchAdvance ) { cchAdvance = 0; DigitMap digitMap = new DigitMap(digitCulture); int lengthOfRanges; ushort *ranges = _cachedFamily.FamilyCollection.GetFamilyMapRanges( _cachedFamily.CompositeFamily, culture, out lengthOfRanges ); Debug.Assert(ranges != null); int sizeofChar = 0; int ch = 0; cchAdvance = Classification.AdvanceWhile(unicodeString, ItemClass.JoinerClass); if (cchAdvance >= unicodeString.Length) { // It is rare that the run only contains joiner characters. // If it really happens, just map them to the initial family map. return(_cachedFamily.FamilyCollection.GetFamilyMapOfChar( _cachedFamily.CompositeFamily, ranges, lengthOfRanges, Classification.UnicodeScalar(unicodeString, out sizeofChar) )); } // // 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. // ch = Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance), out sizeofChar ); bool hasBaseChar = !Classification.IsCombining(ch); ch = digitMap[ch]; FamilyCollection.CachedFamilyMap *familyMap = _cachedFamily.FamilyCollection.GetFamilyMapOfChar(_cachedFamily.CompositeFamily, ranges, lengthOfRanges, ch); for (cchAdvance += sizeofChar; cchAdvance < unicodeString.Length; cchAdvance += sizeofChar) { ch = Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance), out sizeofChar ); if (Classification.IsJoiner(ch)) { continue; // continue to advance if current char is a joiner } if (!Classification.IsCombining(ch)) { hasBaseChar = true; } else if (hasBaseChar) { continue; // continue to advance for combining mark with base char } ch = digitMap[ch]; if (_cachedFamily.FamilyCollection.GetFamilyMapOfChar(_cachedFamily.CompositeFamily, ranges, lengthOfRanges, ch) != familyMap) { break; } } return(familyMap); }
private FontFamilyMap GetTargetFamilyMap( CharacterBufferRange unicodeString, CultureInfo culture, CultureInfo digitCulture, out int cchAdvance ) { DigitMap digitMap = new DigitMap(digitCulture); ushort[] familyMaps = _fontInfo.GetFamilyMapsOfLanguage(XmlLanguage.GetLanguage(culture.IetfLanguageTag)); int sizeofChar = 0; int ch = 0; // skip all the leading joinder characters. They need to be shaped with the // surrounding strong characters. cchAdvance = Classification.AdvanceWhile(unicodeString, ItemClass.JoinerClass); if (cchAdvance >= unicodeString.Length) { // It is rare that the run only contains joiner characters. // If it really happens, just map them to the initial family map. return(_fontInfo.GetFamilyMapOfChar( familyMaps, Classification.UnicodeScalar(unicodeString, out sizeofChar) )); } // // 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. // ch = Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance), out sizeofChar ); bool hasBaseChar = !Classification.IsCombining(ch); ch = digitMap[ch]; FontFamilyMap familyMap = _fontInfo.GetFamilyMapOfChar(familyMaps, ch); Invariant.Assert(familyMap != null); for (cchAdvance += sizeofChar; cchAdvance < unicodeString.Length; cchAdvance += sizeofChar) { ch = Classification.UnicodeScalar( new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance), out sizeofChar ); if (Classification.IsJoiner(ch)) { continue; // continue to advance if current char is a joiner } if (!Classification.IsCombining(ch)) { hasBaseChar = true; } else if (hasBaseChar) { continue; // continue to advance for combining mark with base char } ch = digitMap[ch]; if (_fontInfo.GetFamilyMapOfChar(familyMaps, ch) != familyMap) { break; } } return(familyMap); }
public Digit(int number, int segmentsCount, DigitMap digitMap) { DigitMap = digitMap; Number = number; SegmentsCount = segmentsCount; }