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