Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / MS / Internal / TextFormatting / NumberSubstitution.cs / 1 / NumberSubstitution.cs
using System; using System.Globalization; using System.Runtime.InteropServices; using System.Windows.Media; using System.Windows.Media.TextFormatting; using MS.Internal.FontCache; using MS.Internal.FontFace; using MS.Internal.Shaping; using MS.Win32; namespace MS.Internal.TextFormatting { ////// DigitState contains the high-level logic used to convert the number culture implied by /// text run properties to the low-level digit culture used for shaping. /// internal class DigitState { ////// DigitCulture gets a CultureInfo with the actual symbols and digits used for digit /// substitution. If no substitution is required, this property is null. /// internal CultureInfo DigitCulture { get { return _digitCulture; } } ////// RequiresNumberSubstitution is true if digit substitution is required (DigitCulture != null) /// and false if digit substitution is not required (DigitCulture == null). /// internal bool RequiresNumberSubstitution { get { return _digitCulture != null; } } ////// Contextual is true if contextual digit substitution is required. If so, DigitCulture specifies /// the digits to use in Arabic contexts. In non-Arabic contexts, null should be used as the /// digit culture. /// internal bool Contextual { get { return _contextual; } } ////// SetTextRunProperties initializes the DigitCulture and Contextual properties to reflect the /// specified text run properties. /// internal void SetTextRunProperties(TextRunProperties properties) { // Determine the number culture and substitution method. NumberSubstitutionMethod method; CultureInfo numberCulture = GetNumberCulture(properties, out method); // The digit culture is a function of the number culture and the substitution method. Only // determine the digit culture if either of these two parameters change. if (!object.ReferenceEquals(numberCulture, _lastNumberCulture) || method != _lastMethod) { _lastNumberCulture = numberCulture; _lastMethod = method; _digitCulture = GetDigitCulture(numberCulture, method, out _contextual); } } private CultureInfo GetNumberCulture(TextRunProperties properties, out NumberSubstitutionMethod method) { NumberSubstitution sub = properties.NumberSubstitution; if (sub == null) { method = NumberSubstitutionMethod.AsCulture; return CultureMapper.GetSpecificCulture(properties.CultureInfo); } method = sub.Substitution; switch (sub.CultureSource) { case NumberCultureSource.Text: return CultureMapper.GetSpecificCulture(properties.CultureInfo); case NumberCultureSource.User: return CultureInfo.CurrentCulture; case NumberCultureSource.Override: return sub.CultureOverride; } return null; } private CultureInfo GetDigitCulture(CultureInfo numberCulture, NumberSubstitutionMethod method, out bool contextual) { contextual = false; if (numberCulture == null) { return null; } if (method == NumberSubstitutionMethod.AsCulture) { switch (numberCulture.NumberFormat.DigitSubstitution) { case DigitShapes.Context: method = NumberSubstitutionMethod.Context; break; case DigitShapes.NativeNational: method = NumberSubstitutionMethod.NativeNational; break; default: return null; } } CultureInfo digitCulture; switch (method) { case NumberSubstitutionMethod.Context: if (IsArabic(numberCulture) || Is----(numberCulture)) { contextual = true; digitCulture = GetTraditionalCulture(numberCulture); } else { digitCulture = null; } break; case NumberSubstitutionMethod.NativeNational: if (!HasLatinDigits(numberCulture)) { digitCulture = numberCulture; } else { digitCulture = null; } break; case NumberSubstitutionMethod.Traditional: digitCulture = GetTraditionalCulture(numberCulture); break; default: method = NumberSubstitutionMethod.European; digitCulture = null; break; } return digitCulture; } private static bool HasLatinDigits(CultureInfo culture) { string[] digits = culture.NumberFormat.NativeDigits; for (int i = 0; i < 10; ++i) { string d = digits[i]; if (d.Length != 1 || d[0] != (char)('0' + i)) return false; } return true; } private static bool IsArabic(CultureInfo culture) { return (culture.LCID & 0xFF) == 0x01; } private static bool Is----(CultureInfo culture) { return (culture.LCID & 0xFF) == 0x29; } #region Traditional Cultures ////// Returns the digit culture to use for traditional number substitution given the /// specified number culture. /// private CultureInfo GetTraditionalCulture(CultureInfo numberCulture) { int lcid = numberCulture.LCID; // Do we already have a traditional culture for this LCID? if (_lastTraditionalCulture != null && _lastTraditionalCulture.LCID == lcid) { return _lastTraditionalCulture; } // Branch using the primary language ID (the low-order word of the LCID). If a language // maps to more than one script, we then branch on the entire LCID. The mapping of cultures // (LCIDs) to scripts is based on the following spreadsheet: // http://winworld/teams/giftweb/lme/typography/Font%20Technology%20Infrastructure/OpenType%20and%20OTLS/lcid%20to%20OT%20ScriptLang.xls. // // If the LCID maps to a script for which we have traditional digits, we return the // the corresponding property. For example, the Marathi and Sanscrit languages map to // the Devangari script so we return the TraditionalDevangari property. The script names // are the English names in ISO-15924 (http://www.unicode.org/iso15924/iso15924-codes.html). // The "Arabic" script is a special case as it has two sets of digits, and therefore two // properties: TraditionalArabic and TraditionalEasternArabic. // // See also the Uniscribe number substitution spec: // http://winworld/teams/giftweb/lme/typography/Font%20Technology%20Infrastructure/Uniscribe%20and%20Shaping%20Engines/Digit%20Substitution.doc. // CultureInfo digitCulture = null; switch (lcid & 0xFF) { case 0x01: // Arabic digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0660, // Unicode value of Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x1e: // Thai digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0e50, // Unicode value of Thai digit zero false); // European percent, decimal, and group symbols break; case 0x20: // Urdu case 0x29: // Persian digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x39: // Hindi digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x45: // Bengali digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x46: // Punjabi // This language maps to more than one script; branch on the lcid. if (lcid == 0x0446) // Punjabi (India) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0a66, // Unicode value of Gurmukhi digit zero false); // European percent, decimal, and group symbols else if (lcid == 0x0846) // Punjabi (Pakistan) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x47: // Gujarati digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ae6, // Unicode value of Gujarati digit zero false); // European percent, decimal, and group symbols break; case 0x48: // Oriya digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0b66, // Unicode value of Oriya digit zero false); // European percent, decimal, and group symbols break; case 0x49: // Tamil digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0be6, // Unicode value of Tamil digit zero false); // European percent, decimal, and group symbols break; case 0x4a: // Teluga digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0c66, // Unicode value of Teluga digit zero false); // European percent, decimal, and group symbols break; case 0x4b: // Kannada digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ce6, // Unicode value of Kannada digit zero false); // European percent, decimal, and group symbols break; case 0x4c: // Malayalam digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0d66, // Unicode value of Malayalam digit zero false); // European percent, decimal, and group symbols break; case 0x4d: // Assamese digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x4e: // Marathi case 0x4f: // Sanskrit digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x50: // Mongolian // This language maps to more than one script; branch on the lcid. if (lcid == 0x0850) // Mongolian (PRC) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x1810, // Unicode value of Mongolian digit zero false); // European percent, decimal, and group symbols break; case 0x51: // Tibetan digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0f20, // Unicode value of Tibetan digit zero false); // European percent, decimal, and group symbols break; case 0x53: // Khmer digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x17e0, // Unicode value of Khmer digit zero false); // European percent, decimal, and group symbols break; case 0x54: // Lao digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ed0, // Unicode value of Lao digit zero false); // European percent, decimal, and group symbols break; case 0x55: // Burmese digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x1040, // Unicode value of Myanmar (Burmese) digit zero false); // European percent, decimal, and group symbols break; case 0x57: // Konkani digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x58: // Manipuri - India digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x59: // Sindhi // This language maps to more than one script; branch on the lcid. if (lcid == 0x0459) // Sindhi (India) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols else if (lcid == 0x0859) // Sindhi (Pakistan) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x5f: // Tamazight // This language maps to more than one script; branch on the lcid. if (lcid == 0x045f) // Tamazight (Berber/Arabic) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0660, // Unicode value of Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x60: // ----i // This language maps to more than one script; branch on the lcid. if (lcid == 0x0460) // ----i (Arabic); ks-PK digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols else if (lcid == 0x0860) // ----i; ks-IN digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x61: // Nepali digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x63: // Pashto digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x8c: // Dari digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; } if (digitCulture == null) { // No hard-coded mapping for this LCID. Use the given culture if it has non-Latin digits, // otherwise return null. Don't cache the number culture because we didn't create it and // its digits may depend on other things than the LCID (e.g., it may be a custom culture). if (!HasLatinDigits(numberCulture)) { digitCulture = numberCulture; } } else { // We have a mapping for this LCID. Cache the digit culture in case we're called with // the same LCID again. _lastTraditionalCulture = digitCulture; } return digitCulture; } // Create a modifiable culture with the same properties as the specified number culture, // but with digits '0' through '9' starting at the specified unicode value. private CultureInfo CreateTraditionalCulture(CultureInfo numberCulture, int firstDigit, bool arabic) { // Create the digit culture by cloning the given number culture. According to MSDN, // "CultureInfo.Clone is a shallow copy with exceptions. The objects returned by // the NumberFormat and the DateTimeFormat properties are also cloned, so that the // CultureInfo clone can modify the properties of NumberFormat and DateTimeFormat // without affecting the original CultureInfo." CultureInfo digitCulture = (CultureInfo)numberCulture.Clone(); // Create the array of digits. string[] digits = new string[10]; if (firstDigit < 0x10000) { for (int i = 0; i < 10; ++i) { digits[i] = new string((char)(firstDigit + i), 1); } } else { for (int i = 0; i < 10; ++i) { int n = firstDigit + i - 0x10000; digits[i] = new string( new char[] { (char)((n >> 10) | 0xD800), // high surrogate (char)((n & 0x03FF) | 0xDC00) // low surrogate } ); } } // Set the digits. digitCulture.NumberFormat.NativeDigits = digits; if (arabic) { digitCulture.NumberFormat.PercentSymbol = "\u066a"; digitCulture.NumberFormat.NumberDecimalSeparator = "\u066b"; digitCulture.NumberFormat.NumberGroupSeparator = "\u066c"; } else { digitCulture.NumberFormat.PercentSymbol = "%"; digitCulture.NumberFormat.NumberDecimalSeparator = "."; digitCulture.NumberFormat.NumberGroupSeparator = ","; } return digitCulture; } private CultureInfo _lastTraditionalCulture; #endregion private NumberSubstitutionMethod _lastMethod; private CultureInfo _lastNumberCulture; private CultureInfo _digitCulture; private bool _contextual; } ////// DigitMap maps unicode code points (from the backing store) to unicode code /// points (to be rendered) based on a specified digit culture. /// internal struct DigitMap { private NumberFormatInfo _format; private string[] _digits; internal DigitMap(CultureInfo digitCulture) { if (digitCulture != null) { _format = digitCulture.NumberFormat; _digits = _format.NativeDigits; } else { _format = null; _digits = null; } } internal int this[int ch] { get { if (_format != null && IsDigitOrSymbol(ch)) { uint n = (uint)ch - '0'; if (n < 10) { ch = StringToScalar(_digits[n], ch); } else if (ch == '%') { ch = StringToScalar(_format.PercentSymbol, ch); } else if (ch == ',') { ch = StringToScalar(_format.NumberGroupSeparator, ch); } else { ch = StringToScalar(_format.NumberDecimalSeparator, ch); } } return ch; } } ////// In some cases, our first choice for a substituted code point is not present /// in many older fonts. To avoid displaying missing glyphs in such cases, this /// function returns the alternate character to fall back to if the specified /// substituted character does not exist in the font. The return value is zero /// if there is no fallback character. /// internal static int GetFallbackCharacter(int ch) { switch (ch) { case 0x066B: return (int)','; // Arabic decimal point -> Western comma case 0x066C: return 0x060C; // Arabic thousands separator -> Arabic comma case 0x0BE6: return (int)'0'; // Tamil zero -> Western zero } return 0; // no fallback character } private static int StringToScalar(string s, int defaultValue) { if (s.Length == 1) { return (int)s[0]; } else if (s.Length == 2 && DefaultShape.DefaultSurrogateShaper.IsHighSurrogate((int)s[0]) && DefaultShape.DefaultSurrogateShaper.IsLowSurrogate((int)s[1])) { return DefaultShape.DefaultSurrogateShaper.MakeUnicodeScalar((int)s[0], (int)s[1]); } else { return defaultValue; } } private static bool IsDigitOrSymbol(int ch) { // The code points we're interested in are in the range 0x25 - 0x39. const int first = 0x25; // percent const int last = 0x39; // '9' // Make sure we're in range. This is necessary because (mask >> N) // where N is some large number does not yield zero, but rather is // equivalent to (mask >> (N % 32)). if ((uint)(ch - first) <= (uint)(last - first)) { // Let mask be an array of bits indexed by code point, with // first as the base for indexing. const uint mask = (1U << ('%' - first)) | // U+0025 (1U << (',' - first)) | // U+002C (1U << ('.' - first)) | // U+002E (1U << ('0' - first)) | // U+0030 (1U << ('1' - first)) | // U+0031 (1U << ('2' - first)) | // U+0032 (1U << ('3' - first)) | // U+0033 (1U << ('4' - first)) | // U+0034 (1U << ('5' - first)) | // U+0035 (1U << ('6' - first)) | // U+0036 (1U << ('7' - first)) | // U+0037 (1U << ('8' - first)) | // U+0038 (1U << ('9' - first)); // U+0039 // Return the bit that correponds to the given code point. return ((mask >> (ch - first)) & 1) != 0; } else { // The code point is out of our given range. return false; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Globalization; using System.Runtime.InteropServices; using System.Windows.Media; using System.Windows.Media.TextFormatting; using MS.Internal.FontCache; using MS.Internal.FontFace; using MS.Internal.Shaping; using MS.Win32; namespace MS.Internal.TextFormatting { ////// DigitState contains the high-level logic used to convert the number culture implied by /// text run properties to the low-level digit culture used for shaping. /// internal class DigitState { ////// DigitCulture gets a CultureInfo with the actual symbols and digits used for digit /// substitution. If no substitution is required, this property is null. /// internal CultureInfo DigitCulture { get { return _digitCulture; } } ////// RequiresNumberSubstitution is true if digit substitution is required (DigitCulture != null) /// and false if digit substitution is not required (DigitCulture == null). /// internal bool RequiresNumberSubstitution { get { return _digitCulture != null; } } ////// Contextual is true if contextual digit substitution is required. If so, DigitCulture specifies /// the digits to use in Arabic contexts. In non-Arabic contexts, null should be used as the /// digit culture. /// internal bool Contextual { get { return _contextual; } } ////// SetTextRunProperties initializes the DigitCulture and Contextual properties to reflect the /// specified text run properties. /// internal void SetTextRunProperties(TextRunProperties properties) { // Determine the number culture and substitution method. NumberSubstitutionMethod method; CultureInfo numberCulture = GetNumberCulture(properties, out method); // The digit culture is a function of the number culture and the substitution method. Only // determine the digit culture if either of these two parameters change. if (!object.ReferenceEquals(numberCulture, _lastNumberCulture) || method != _lastMethod) { _lastNumberCulture = numberCulture; _lastMethod = method; _digitCulture = GetDigitCulture(numberCulture, method, out _contextual); } } private CultureInfo GetNumberCulture(TextRunProperties properties, out NumberSubstitutionMethod method) { NumberSubstitution sub = properties.NumberSubstitution; if (sub == null) { method = NumberSubstitutionMethod.AsCulture; return CultureMapper.GetSpecificCulture(properties.CultureInfo); } method = sub.Substitution; switch (sub.CultureSource) { case NumberCultureSource.Text: return CultureMapper.GetSpecificCulture(properties.CultureInfo); case NumberCultureSource.User: return CultureInfo.CurrentCulture; case NumberCultureSource.Override: return sub.CultureOverride; } return null; } private CultureInfo GetDigitCulture(CultureInfo numberCulture, NumberSubstitutionMethod method, out bool contextual) { contextual = false; if (numberCulture == null) { return null; } if (method == NumberSubstitutionMethod.AsCulture) { switch (numberCulture.NumberFormat.DigitSubstitution) { case DigitShapes.Context: method = NumberSubstitutionMethod.Context; break; case DigitShapes.NativeNational: method = NumberSubstitutionMethod.NativeNational; break; default: return null; } } CultureInfo digitCulture; switch (method) { case NumberSubstitutionMethod.Context: if (IsArabic(numberCulture) || Is----(numberCulture)) { contextual = true; digitCulture = GetTraditionalCulture(numberCulture); } else { digitCulture = null; } break; case NumberSubstitutionMethod.NativeNational: if (!HasLatinDigits(numberCulture)) { digitCulture = numberCulture; } else { digitCulture = null; } break; case NumberSubstitutionMethod.Traditional: digitCulture = GetTraditionalCulture(numberCulture); break; default: method = NumberSubstitutionMethod.European; digitCulture = null; break; } return digitCulture; } private static bool HasLatinDigits(CultureInfo culture) { string[] digits = culture.NumberFormat.NativeDigits; for (int i = 0; i < 10; ++i) { string d = digits[i]; if (d.Length != 1 || d[0] != (char)('0' + i)) return false; } return true; } private static bool IsArabic(CultureInfo culture) { return (culture.LCID & 0xFF) == 0x01; } private static bool Is----(CultureInfo culture) { return (culture.LCID & 0xFF) == 0x29; } #region Traditional Cultures ////// Returns the digit culture to use for traditional number substitution given the /// specified number culture. /// private CultureInfo GetTraditionalCulture(CultureInfo numberCulture) { int lcid = numberCulture.LCID; // Do we already have a traditional culture for this LCID? if (_lastTraditionalCulture != null && _lastTraditionalCulture.LCID == lcid) { return _lastTraditionalCulture; } // Branch using the primary language ID (the low-order word of the LCID). If a language // maps to more than one script, we then branch on the entire LCID. The mapping of cultures // (LCIDs) to scripts is based on the following spreadsheet: // http://winworld/teams/giftweb/lme/typography/Font%20Technology%20Infrastructure/OpenType%20and%20OTLS/lcid%20to%20OT%20ScriptLang.xls. // // If the LCID maps to a script for which we have traditional digits, we return the // the corresponding property. For example, the Marathi and Sanscrit languages map to // the Devangari script so we return the TraditionalDevangari property. The script names // are the English names in ISO-15924 (http://www.unicode.org/iso15924/iso15924-codes.html). // The "Arabic" script is a special case as it has two sets of digits, and therefore two // properties: TraditionalArabic and TraditionalEasternArabic. // // See also the Uniscribe number substitution spec: // http://winworld/teams/giftweb/lme/typography/Font%20Technology%20Infrastructure/Uniscribe%20and%20Shaping%20Engines/Digit%20Substitution.doc. // CultureInfo digitCulture = null; switch (lcid & 0xFF) { case 0x01: // Arabic digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0660, // Unicode value of Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x1e: // Thai digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0e50, // Unicode value of Thai digit zero false); // European percent, decimal, and group symbols break; case 0x20: // Urdu case 0x29: // Persian digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x39: // Hindi digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x45: // Bengali digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x46: // Punjabi // This language maps to more than one script; branch on the lcid. if (lcid == 0x0446) // Punjabi (India) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0a66, // Unicode value of Gurmukhi digit zero false); // European percent, decimal, and group symbols else if (lcid == 0x0846) // Punjabi (Pakistan) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x47: // Gujarati digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ae6, // Unicode value of Gujarati digit zero false); // European percent, decimal, and group symbols break; case 0x48: // Oriya digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0b66, // Unicode value of Oriya digit zero false); // European percent, decimal, and group symbols break; case 0x49: // Tamil digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0be6, // Unicode value of Tamil digit zero false); // European percent, decimal, and group symbols break; case 0x4a: // Teluga digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0c66, // Unicode value of Teluga digit zero false); // European percent, decimal, and group symbols break; case 0x4b: // Kannada digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ce6, // Unicode value of Kannada digit zero false); // European percent, decimal, and group symbols break; case 0x4c: // Malayalam digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0d66, // Unicode value of Malayalam digit zero false); // European percent, decimal, and group symbols break; case 0x4d: // Assamese digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x4e: // Marathi case 0x4f: // Sanskrit digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x50: // Mongolian // This language maps to more than one script; branch on the lcid. if (lcid == 0x0850) // Mongolian (PRC) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x1810, // Unicode value of Mongolian digit zero false); // European percent, decimal, and group symbols break; case 0x51: // Tibetan digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0f20, // Unicode value of Tibetan digit zero false); // European percent, decimal, and group symbols break; case 0x53: // Khmer digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x17e0, // Unicode value of Khmer digit zero false); // European percent, decimal, and group symbols break; case 0x54: // Lao digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0ed0, // Unicode value of Lao digit zero false); // European percent, decimal, and group symbols break; case 0x55: // Burmese digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x1040, // Unicode value of Myanmar (Burmese) digit zero false); // European percent, decimal, and group symbols break; case 0x57: // Konkani digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x58: // Manipuri - India digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x09e6, // Unicode value of Bengali digit zero false); // European percent, decimal, and group symbols break; case 0x59: // Sindhi // This language maps to more than one script; branch on the lcid. if (lcid == 0x0459) // Sindhi (India) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols else if (lcid == 0x0859) // Sindhi (Pakistan) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x5f: // Tamazight // This language maps to more than one script; branch on the lcid. if (lcid == 0x045f) // Tamazight (Berber/Arabic) digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0660, // Unicode value of Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x60: // ----i // This language maps to more than one script; branch on the lcid. if (lcid == 0x0460) // ----i (Arabic); ks-PK digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols else if (lcid == 0x0860) // ----i; ks-IN digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x61: // Nepali digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x0966, // Unicode value of Devanagari digit zero false); // European percent, decimal, and group symbols break; case 0x63: // Pashto digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; case 0x8c: // Dari digitCulture = CreateTraditionalCulture( numberCulture, // culture to clone 0x06F0, // Unicode value of Eastern Arabic digit zero true); // Arabic percent, decimal, and group symbols break; } if (digitCulture == null) { // No hard-coded mapping for this LCID. Use the given culture if it has non-Latin digits, // otherwise return null. Don't cache the number culture because we didn't create it and // its digits may depend on other things than the LCID (e.g., it may be a custom culture). if (!HasLatinDigits(numberCulture)) { digitCulture = numberCulture; } } else { // We have a mapping for this LCID. Cache the digit culture in case we're called with // the same LCID again. _lastTraditionalCulture = digitCulture; } return digitCulture; } // Create a modifiable culture with the same properties as the specified number culture, // but with digits '0' through '9' starting at the specified unicode value. private CultureInfo CreateTraditionalCulture(CultureInfo numberCulture, int firstDigit, bool arabic) { // Create the digit culture by cloning the given number culture. According to MSDN, // "CultureInfo.Clone is a shallow copy with exceptions. The objects returned by // the NumberFormat and the DateTimeFormat properties are also cloned, so that the // CultureInfo clone can modify the properties of NumberFormat and DateTimeFormat // without affecting the original CultureInfo." CultureInfo digitCulture = (CultureInfo)numberCulture.Clone(); // Create the array of digits. string[] digits = new string[10]; if (firstDigit < 0x10000) { for (int i = 0; i < 10; ++i) { digits[i] = new string((char)(firstDigit + i), 1); } } else { for (int i = 0; i < 10; ++i) { int n = firstDigit + i - 0x10000; digits[i] = new string( new char[] { (char)((n >> 10) | 0xD800), // high surrogate (char)((n & 0x03FF) | 0xDC00) // low surrogate } ); } } // Set the digits. digitCulture.NumberFormat.NativeDigits = digits; if (arabic) { digitCulture.NumberFormat.PercentSymbol = "\u066a"; digitCulture.NumberFormat.NumberDecimalSeparator = "\u066b"; digitCulture.NumberFormat.NumberGroupSeparator = "\u066c"; } else { digitCulture.NumberFormat.PercentSymbol = "%"; digitCulture.NumberFormat.NumberDecimalSeparator = "."; digitCulture.NumberFormat.NumberGroupSeparator = ","; } return digitCulture; } private CultureInfo _lastTraditionalCulture; #endregion private NumberSubstitutionMethod _lastMethod; private CultureInfo _lastNumberCulture; private CultureInfo _digitCulture; private bool _contextual; } ////// DigitMap maps unicode code points (from the backing store) to unicode code /// points (to be rendered) based on a specified digit culture. /// internal struct DigitMap { private NumberFormatInfo _format; private string[] _digits; internal DigitMap(CultureInfo digitCulture) { if (digitCulture != null) { _format = digitCulture.NumberFormat; _digits = _format.NativeDigits; } else { _format = null; _digits = null; } } internal int this[int ch] { get { if (_format != null && IsDigitOrSymbol(ch)) { uint n = (uint)ch - '0'; if (n < 10) { ch = StringToScalar(_digits[n], ch); } else if (ch == '%') { ch = StringToScalar(_format.PercentSymbol, ch); } else if (ch == ',') { ch = StringToScalar(_format.NumberGroupSeparator, ch); } else { ch = StringToScalar(_format.NumberDecimalSeparator, ch); } } return ch; } } ////// In some cases, our first choice for a substituted code point is not present /// in many older fonts. To avoid displaying missing glyphs in such cases, this /// function returns the alternate character to fall back to if the specified /// substituted character does not exist in the font. The return value is zero /// if there is no fallback character. /// internal static int GetFallbackCharacter(int ch) { switch (ch) { case 0x066B: return (int)','; // Arabic decimal point -> Western comma case 0x066C: return 0x060C; // Arabic thousands separator -> Arabic comma case 0x0BE6: return (int)'0'; // Tamil zero -> Western zero } return 0; // no fallback character } private static int StringToScalar(string s, int defaultValue) { if (s.Length == 1) { return (int)s[0]; } else if (s.Length == 2 && DefaultShape.DefaultSurrogateShaper.IsHighSurrogate((int)s[0]) && DefaultShape.DefaultSurrogateShaper.IsLowSurrogate((int)s[1])) { return DefaultShape.DefaultSurrogateShaper.MakeUnicodeScalar((int)s[0], (int)s[1]); } else { return defaultValue; } } private static bool IsDigitOrSymbol(int ch) { // The code points we're interested in are in the range 0x25 - 0x39. const int first = 0x25; // percent const int last = 0x39; // '9' // Make sure we're in range. This is necessary because (mask >> N) // where N is some large number does not yield zero, but rather is // equivalent to (mask >> (N % 32)). if ((uint)(ch - first) <= (uint)(last - first)) { // Let mask be an array of bits indexed by code point, with // first as the base for indexing. const uint mask = (1U << ('%' - first)) | // U+0025 (1U << (',' - first)) | // U+002C (1U << ('.' - first)) | // U+002E (1U << ('0' - first)) | // U+0030 (1U << ('1' - first)) | // U+0031 (1U << ('2' - first)) | // U+0032 (1U << ('3' - first)) | // U+0033 (1U << ('4' - first)) | // U+0034 (1U << ('5' - first)) | // U+0035 (1U << ('6' - first)) | // U+0036 (1U << ('7' - first)) | // U+0037 (1U << ('8' - first)) | // U+0038 (1U << ('9' - first)); // U+0039 // Return the bit that correponds to the given code point. return ((mask >> (ch - first)) & 1) != 0; } else { // The code point is out of our given range. return false; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlSchemaSequence.cs
- BufferedStream2.cs
- TemplateNameScope.cs
- Cursors.cs
- Int64AnimationBase.cs
- mediaeventshelper.cs
- OracleConnectionString.cs
- StackBuilderSink.cs
- ScaleTransform3D.cs
- ProtocolElementCollection.cs
- ObjectDataSourceStatusEventArgs.cs
- NameValuePermission.cs
- UserControlAutomationPeer.cs
- elementinformation.cs
- TextOnlyOutput.cs
- _AutoWebProxyScriptWrapper.cs
- ExtensionSimplifierMarkupObject.cs
- TextSpanModifier.cs
- TTSVoice.cs
- ValidationPropertyAttribute.cs
- BinHexEncoder.cs
- Wildcard.cs
- SessionState.cs
- DataGrid.cs
- SqlCacheDependencySection.cs
- CreateInstanceBinder.cs
- ToolboxItemAttribute.cs
- BuildResult.cs
- NoResizeSelectionBorderGlyph.cs
- StringHelper.cs
- GradientBrush.cs
- WebExceptionStatus.cs
- WebConfigurationFileMap.cs
- DependencyObjectProvider.cs
- ToolboxItem.cs
- CaseKeyBox.ViewModel.cs
- DataReceivedEventArgs.cs
- VisualStyleRenderer.cs
- InvalidCommandTreeException.cs
- RSAPKCS1KeyExchangeDeformatter.cs
- HandleRef.cs
- CommandLibraryHelper.cs
- BrowserDefinition.cs
- VBCodeProvider.cs
- BulletedListEventArgs.cs
- TreeIterator.cs
- AppSecurityManager.cs
- TextEffectResolver.cs
- FileDialogPermission.cs
- SafeNativeMethodsCLR.cs
- Visual3D.cs
- lengthconverter.cs
- HtmlTableCellCollection.cs
- StorageMappingFragment.cs
- PtsHelper.cs
- WhereQueryOperator.cs
- FlowchartDesignerCommands.cs
- ArrayHelper.cs
- SqlTriggerContext.cs
- HMACMD5.cs
- OdbcConnectionOpen.cs
- FileLogRecordHeader.cs
- ToolStripComboBox.cs
- filewebrequest.cs
- DataRowChangeEvent.cs
- FixedSchema.cs
- Rect3D.cs
- IdentityVerifier.cs
- PlanCompilerUtil.cs
- DependencyPropertyDescriptor.cs
- itemelement.cs
- HyperlinkAutomationPeer.cs
- BasicKeyConstraint.cs
- Suspend.cs
- XamlInterfaces.cs
- CharacterHit.cs
- TabletDevice.cs
- ObjectQueryState.cs
- RuntimeConfigurationRecord.cs
- Group.cs
- WebRequestModulesSection.cs
- WindowsListViewGroup.cs
- IgnoreSectionHandler.cs
- PropertyValidationContext.cs
- DataTableNewRowEvent.cs
- XsdBuilder.cs
- EntityDataSourceColumn.cs
- XPathConvert.cs
- VScrollBar.cs
- XmlElement.cs
- versioninfo.cs
- WebPartTracker.cs
- ConditionedDesigner.cs
- ImageCodecInfoPrivate.cs
- StatusStrip.cs
- TextEndOfLine.cs
- DispatcherHooks.cs
- Tuple.cs
- DynamicPropertyReader.cs
- XamlWriter.cs