private IcuRulesCollationDefinition ReadCollationRulesForCustomIcu(XElement collationElem, WritingSystemDefinition ws, string collationType) { var icd = new IcuRulesCollationDefinition(collationType) {WritingSystemFactory = _writingSystemFactory, OwningWritingSystemDefinition = ws}; icd.Imports.AddRange(collationElem.NonAltElements("import").Select(ie => new IcuCollationImport((string) ie.Attribute("source"), (string) ie.Attribute("type")))); icd.IcuRules = LdmlCollationParser.GetIcuRulesFromCollationNode(collationElem); return icd; }
/// <summary> /// Update element based on the writing system model. At the end, write the contents to LDML /// </summary> /// <param name="writer"></param> /// <param name="element"></param> /// <param name="ws"></param> private void WriteLdml(XmlWriter writer, XElement element, WritingSystemDefinition ws) { Debug.Assert(element != null); Debug.Assert(ws != null); XElement identityElem = element.GetOrCreateElement("identity"); WriteIdentityElement(identityElem, ws); RemoveIfEmpty(ref identityElem); XElement charactersElem = element.GetOrCreateElement("characters"); WriteCharactersElement(charactersElem, ws); RemoveIfEmpty(ref charactersElem); XElement delimitersElem = element.GetOrCreateElement("delimiters"); WriteDelimitersElement(delimitersElem, ws); RemoveIfEmpty(ref delimitersElem); XElement layoutElem = element.GetOrCreateElement("layout"); WriteLayoutElement(layoutElem, ws); RemoveIfEmpty(ref layoutElem); XElement numbersElem = element.GetOrCreateElement("numbers"); WriteNumbersElement(numbersElem, ws); RemoveIfEmpty(ref numbersElem); XElement collationsElem = element.GetOrCreateElement("collations"); WriteCollationsElement(collationsElem, ws); RemoveIfEmpty(ref collationsElem); // Can have multiple specials. Find the one with SIL namespace and external-resources. // Also handle case where we create special because writingsystem has entries to write XElement specialElem = element.NonAltElements("special").FirstOrDefault( e => !string.IsNullOrEmpty((string) e.Attribute(XNamespace.Xmlns+"sil")) && e.NonAltElement(Sil + "external-resources") != null); if (specialElem == null && (ws.Fonts.Count > 0 || ws.KnownKeyboards.Count > 0 || ws.SpellCheckDictionaries.Count > 0)) { // Create special element specialElem = GetOrCreateSpecialElement(element); } if (specialElem != null) { WriteTopLevelSpecialElements(specialElem, ws); RemoveIfEmpty(ref specialElem); } element.WriteTo(writer); }
// Numbering system gets added to the character set definition private void ReadNumbersElement(XElement numbersElem, WritingSystemDefinition ws) { XElement defaultNumberingSystemElem = numbersElem.NonAltElement("defaultNumberingSystem"); if (defaultNumberingSystemElem != null) { var id = (string) defaultNumberingSystemElem; XElement numberingSystemsElem = numbersElem.NonAltElements("numberingSystem") .FirstOrDefault(e => id == (string) e.Attribute("id") && (string) e.Attribute("type") == "numeric"); if (numberingSystemsElem != null) { var csd = new CharacterSetDefinition("numeric"); // Only handle numeric types var digits = (string) numberingSystemsElem.Attribute("digits"); foreach (char charItem in digits) csd.Characters.Add(charItem.ToString(CultureInfo.InvariantCulture)); ws.CharacterSets.Add(csd); } } }
private void ReadCollationsElement(XElement collationsElem, WritingSystemDefinition ws) { XElement defaultCollationElem = collationsElem.Element("defaultCollation"); ws.DefaultCollationType = (string) defaultCollationElem; foreach (XElement collationElem in collationsElem.NonAltElements("collation")) ReadCollationElement(collationElem, ws); }
private void ReadKeyboardElement(XElement externalResourcesElem, WritingSystemDefinition ws) { foreach (XElement kbdElem in externalResourcesElem.NonAltElements(Sil + "kbd")) { var id = (string) kbdElem.Attribute("id"); if (!string.IsNullOrEmpty(id)) { KeyboardFormat format = KeyboardToKeyboardFormat[(string) kbdElem.Attribute("type") ?? string.Empty]; IKeyboardDefinition keyboard = Keyboard.Controller.CreateKeyboard(id, format, kbdElem.NonAltElements(Sil + "url").Select(u => (string) u)); ws.KnownKeyboards.Add(keyboard); } } }
private void ReadCharactersElement(XElement charactersElem, WritingSystemDefinition ws) { foreach (XElement exemplarCharactersElem in charactersElem.NonAltElements("exemplarCharacters")) ReadExemplarCharactersElem(exemplarCharactersElem, ws); XElement specialElem = charactersElem.NonAltElement("special"); if (specialElem != null) { foreach (XElement exemplarCharactersElem in specialElem.NonAltElements(Sil + "exemplarCharacters")) { // Sil:exemplarCharacters are required to have a type if (!string.IsNullOrEmpty((string) exemplarCharactersElem.Attribute("type"))) ReadExemplarCharactersElem(exemplarCharactersElem, ws); } } }
private void ReadFontElement(XElement externalResourcesElem, WritingSystemDefinition ws) { foreach (XElement fontElem in externalResourcesElem.NonAltElements(Sil + "font")) { var fontName = (string) fontElem.Attribute("name"); if (!string.IsNullOrEmpty(fontName)) { var fd = new FontDefinition(fontName); // Types (space separate list) var roles = (string) fontElem.Attribute("types"); if (!String.IsNullOrEmpty(roles)) { IEnumerable<string> roleList = roles.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); foreach (string roleEntry in roleList) { fd.Roles |= RoleToFontRoles[roleEntry]; } } else { fd.Roles = FontRoles.Default; } // Relative Size fd.RelativeSize = (float?) fontElem.Attribute("size") ?? 1.0f; // Minversion fd.MinVersion = (string) fontElem.Attribute("minversion"); // Features (space separated list of key=value pairs) fd.Features = (string) fontElem.Attribute("features"); // Language fd.Language = (string) fontElem.Attribute("lang"); // OpenType language fd.OpenTypeLanguage = (string) fontElem.Attribute("otlang"); // Font Engine (space separated list) supercedes legacy isGraphite flag // If attribute is missing it is assumed to be "gr ot" var engines = (string) fontElem.Attribute("engines") ?? "gr ot"; IEnumerable<string> engineList = engines.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); foreach (string engineEntry in engineList) fd.Engines |= (EngineToFontEngines[engineEntry]); // Subset fd.Subset = (string) fontElem.Attribute("subset"); // URL elements foreach (XElement urlElem in fontElem.NonAltElements(Sil + "url")) fd.Urls.Add(urlElem.Value); ws.Fonts.Add(fd); } } }
private void WriteNumbersElement(XElement numbersElem, WritingSystemDefinition ws) { Debug.Assert(numbersElem != null); Debug.Assert(ws != null); // Save off defaultNumberingSystem if it exists. string defaultNumberingSystem = "standard"; XElement defaultNumberingSystemElem = numbersElem.NonAltElement("defaultNumberingSystem"); if (defaultNumberingSystemElem != null && !string.IsNullOrEmpty((string) defaultNumberingSystemElem)) defaultNumberingSystem = (string) defaultNumberingSystemElem; // Remove defaultNumberingSystem and numberingSystems elements of type "numeric" to repopulate later numbersElem.NonAltElements("defaultNumberingSystem").Remove(); numbersElem.NonAltElements("numberingSystem").Where(e => (string) e.Attribute("id") == defaultNumberingSystem && (string) e.Attribute("type") == "numeric" && e.Attribute("alt") == null).Remove(); CharacterSetDefinition csd; if (ws.CharacterSets.TryGet("numeric", out csd)) { // Create defaultNumberingSystem element and add as the first child if (defaultNumberingSystemElem == null) { defaultNumberingSystemElem = new XElement("defaultNumberingSystem", defaultNumberingSystem); numbersElem.AddFirst(defaultNumberingSystemElem); } // Populate numbering system element var numberingSystemsElem = new XElement("numberingSystem"); numberingSystemsElem.SetAttributeValue("id", defaultNumberingSystem); numberingSystemsElem.SetAttributeValue("type", csd.Type); string digits = string.Join("", csd.Characters); numberingSystemsElem.SetAttributeValue("digits", digits); numbersElem.Add(numberingSystemsElem); } }
private void WriteKeyboardElement(XElement externalResourcesElem, WritingSystemDefinition ws) { Debug.Assert(externalResourcesElem != null); Debug.Assert(ws != null); // Remove sil:kbd elements to repopulate later externalResourcesElem.NonAltElements(Sil + "kbd").Remove(); // Don't include unknown system keyboard definitions foreach (IKeyboardDefinition keyboard in ws.KnownKeyboards.Where(kbd=>kbd.Format != KeyboardFormat.Unknown)) { var kbdElem = new XElement(Sil + "kbd"); // id required kbdElem.SetAttributeValue("id", keyboard.Id); if (!string.IsNullOrEmpty(keyboard.Id)) { kbdElem.SetAttributeValue("type", KeyboardFormatToKeyboard[keyboard.Format]); foreach (var url in keyboard.Urls) { var urlElem = new XElement(Sil + "url", url); kbdElem.Add(urlElem); } } externalResourcesElem.Add(kbdElem); } }
/// <summary> /// Read the LDML file at the root element and populate the writing system. /// </summary> /// <param name="element">Root element</param> /// <param name="ws">Writing System to populate</param> /// <remarks> /// For elements that have * annotation in the LDML spec, we use the extensions NonAltElement /// and NonAltElements to ignore the elements that have the alt attribute. /// </remarks> private void ReadLdml(XElement element, WritingSystemDefinition ws) { Debug.Assert(element != null); Debug.Assert(ws != null); if (element.Name != "ldml") throw new ApplicationException("Unable to load writing system definition: Missing <ldml> tag."); ResetWritingSystemDefinition(ws); XElement identityElem = element.Element("identity"); if (identityElem != null) ReadIdentityElement(identityElem, ws); // Check for the proper LDML version after reading identity element so that we have the proper language tag if an error occurs. foreach (XElement specialElem in element.NonAltElements("special")) CheckVersion(specialElem, ws); XElement charactersElem = element.Element("characters"); if (charactersElem != null) ReadCharactersElement(charactersElem, ws); XElement delimitersElem = element.Element("delimiters"); if (delimitersElem != null) ReadDelimitersElement(delimitersElem, ws); XElement layoutElem = element.Element("layout"); if (layoutElem != null) ReadLayoutElement(layoutElem, ws); XElement numbersElem = element.Element("numbers"); if (numbersElem != null) ReadNumbersElement(numbersElem, ws); XElement collationsElem = element.Element("collations"); if (collationsElem != null) ReadCollationsElement(collationsElem, ws); foreach (XElement specialElem in element.NonAltElements("special")) ReadTopLevelSpecialElement(specialElem, ws); // Validate collations after all of them have been read in (for self-referencing imports) foreach (CollationDefinition cd in ws.Collations) { string message; cd.Validate(out message); } ws.Id = string.Empty; ws.AcceptChanges(); }
private void WriteSpellcheckElement(XElement externalResourcesElem, WritingSystemDefinition ws) { Debug.Assert(externalResourcesElem != null); Debug.Assert(ws != null); // Remove sil:spellcheck elements to repopulate later externalResourcesElem.NonAltElements(Sil + "spellcheck").Remove(); foreach (SpellCheckDictionaryDefinition scd in ws.SpellCheckDictionaries) { var scElem = new XElement(Sil + "spellcheck"); scElem.SetAttributeValue("type", SpellCheckDictionaryFormatsToSpellCheck[scd.Format]); // URL elements foreach (var url in scd.Urls) { var urlElem = new XElement(Sil + "url", url); scElem.Add(urlElem); } externalResourcesElem.Add(scElem); } }
private void WriteFontElement(XElement externalResourcesElem, WritingSystemDefinition ws) { Debug.Assert(externalResourcesElem != null); Debug.Assert(ws != null); // Remove sil:font elements to repopulate later externalResourcesElem.NonAltElements(Sil + "font").Remove(); foreach (FontDefinition font in ws.Fonts) { var fontElem = new XElement(Sil + "font"); fontElem.SetAttributeValue("name", font.Name); // Generate space-separated list of font roles if (font.Roles != FontRoles.Default) { var fontRoleList = new List<string>(); foreach (FontRoles fontRole in Enum.GetValues(typeof(FontRoles))) { if ((font.Roles & fontRole) != 0) fontRoleList.Add(FontRolesToRole[fontRole]); } fontElem.SetAttributeValue("types", string.Join(" ", fontRoleList)); } if (font.RelativeSize != 1.0f) fontElem.SetAttributeValue("size", font.RelativeSize); fontElem.SetOptionalAttributeValue("minversion", font.MinVersion); fontElem.SetOptionalAttributeValue("features", font.Features); fontElem.SetOptionalAttributeValue("lang", font.Language); fontElem.SetOptionalAttributeValue("otlang", font.OpenTypeLanguage); fontElem.SetOptionalAttributeValue("subset", font.Subset); // Generate space-separated list of font engines if (font.Engines != (FontEngines.Graphite | FontEngines.OpenType)) { var fontEngineList = new List<string>(); foreach (FontEngines fontEngine in Enum.GetValues(typeof (FontEngines))) { if ((font.Engines & fontEngine) != 0) fontEngineList.Add(FontEnginesToEngine[fontEngine]); } fontElem.SetAttributeValue("engines", fontEngineList.Count == 0 ? null : string.Join(" ", fontEngineList)); } foreach (var url in font.Urls) fontElem.Add(new XElement(Sil + "url", url)); externalResourcesElem.Add(fontElem); } }
private void WriteCollationsElement(XElement collationsElem, WritingSystemDefinition ws) { // Preserve exisiting collations since we don't process them all // Remove only the collations we can repopulate from the writing system collationsElem.NonAltElements("collation").Where(ce => ce.NonAltElements("special").Elements().All(se => se.Name != (Sil + "reordered"))).Remove(); // if there will be no collation elements, don't write out defaultCollation element if (!collationsElem.Elements("collation").Any() && ws.Collations.All(c => c is SystemCollationDefinition)) return; XElement defaultCollationElem = collationsElem.GetOrCreateElement("defaultCollation"); defaultCollationElem.SetValue(ws.DefaultCollationType); foreach (CollationDefinition collation in ws.Collations) WriteCollationElement(collationsElem, collation); }
private void WriteIdentityElement(XElement identityElem, WritingSystemDefinition ws) { Debug.Assert(identityElem != null); Debug.Assert(ws != null); // Remove non-special elements to repopulate later // Preserve special because we don't recreate all its contents identityElem.NonAltElements().Where(e => e.Name != "special").Remove(); // Version is required. If VersionNumber is blank, the empty attribute is still written XElement versionElem = identityElem.GetOrCreateElement("version"); versionElem.SetAttributeValue("number", ws.VersionNumber); if (!string.IsNullOrEmpty(ws.VersionDescription)) versionElem.SetValue(ws.VersionDescription); // Write generation date with UTC so no more ambiguity on timezone identityElem.SetAttributeValue("generation", "date", ws.DateModified.ToISO8601TimeFormatWithUTCString()); WriteLanguageTagElements(identityElem, ws.LanguageTag); // Create special element if data needs to be written int index = IetfLanguageTag.GetIndexOfFirstNonCommonPrivateUseVariant(ws.Variants); string variantName = string.Empty; if (index != -1) variantName = ws.Variants[index].Name; if (!string.IsNullOrEmpty(ws.WindowsLcid) || !string.IsNullOrEmpty(ws.DefaultRegion) || !string.IsNullOrEmpty(variantName)) { XElement specialElem = GetOrCreateSpecialElement(identityElem); XElement silIdentityElem = specialElem.GetOrCreateElement(Sil + "identity"); // uid and revid attributes are left intact silIdentityElem.SetOptionalAttributeValue("windowsLCID", ws.WindowsLcid); silIdentityElem.SetOptionalAttributeValue("defaultRegion", ws.DefaultRegion); silIdentityElem.SetOptionalAttributeValue("variantName", variantName); // Move special to the end of the identity block (preserving order) specialElem.Remove(); identityElem.Add(specialElem); } }
private void ReadSpellcheckElement(XElement externalResourcesElem, WritingSystemDefinition ws) { foreach (XElement scElem in externalResourcesElem.NonAltElements(Sil + "spellcheck")) { var type = (string) scElem.Attribute("type"); if (!string.IsNullOrEmpty(type)) { var scd = new SpellCheckDictionaryDefinition(SpellCheckToSpecllCheckDictionaryFormats[type]); // URL elements foreach (XElement urlElem in scElem.NonAltElements(Sil + "url")) scd.Urls.Add(urlElem.Value); ws.SpellCheckDictionaries.Add(scd); } } }
private void WriteCharactersElement(XElement charactersElem, WritingSystemDefinition ws) { Debug.Assert(charactersElem != null); Debug.Assert(ws != null); // Remove exemplarCharacters and special sil:exemplarCharacters elements to repopulate later charactersElem.NonAltElements("exemplarCharacters").Remove(); XElement specialElem = charactersElem.NonAltElement("special"); if (specialElem != null) { specialElem.NonAltElements(Sil + "exemplarCharacters").Remove(); RemoveIfEmpty(ref specialElem); } foreach (CharacterSetDefinition csd in ws.CharacterSets) { XElement exemplarCharactersElem; switch (csd.Type) { // These character sets go to the normal LDML exemplarCharacters space // http://unicode.org/reports/tr35/tr35-general.html#Exemplars case "main": case "auxiliary": case "index": case "punctuation": exemplarCharactersElem = new XElement("exemplarCharacters", UnicodeSet.ToPattern(csd.Characters)); // Assume main set doesn't have an attribute type if (csd.Type != "main") exemplarCharactersElem.SetAttributeValue("type", csd.Type); charactersElem.Add(exemplarCharactersElem); break; // Numeric characters will be written in the numbers element case "numeric": break; // All others go to special Sil:exemplarCharacters default: string unicodeSet = csd.Type == "footnotes" ? string.Format("[{0}]", string.Join(" ", csd.Characters.Select(c => c.Length > 1 ? string.Format("{{{0}}}", c) : c))) : UnicodeSet.ToPattern(csd.Characters); exemplarCharactersElem = new XElement(Sil + "exemplarCharacters", unicodeSet); exemplarCharactersElem.SetAttributeValue("type", csd.Type); specialElem = GetOrCreateSpecialElement(charactersElem); specialElem.Add(exemplarCharactersElem); break; } } }
private void WriteDelimitersElement(XElement delimitersElem, WritingSystemDefinition ws) { Debug.Assert(delimitersElem != null); Debug.Assert(ws != null); // Remove non-special elements to repopulate later delimitersElem.NonAltElements().Where(e => e.Name != "special").Remove(); // Level 1 normal => quotationStart and quotationEnd QuotationMark qm1 = ws.QuotationMarks.FirstOrDefault(q => q.Level == 1 && q.Type == QuotationMarkingSystemType.Normal); if (qm1 != null) { var quotationStartElem = new XElement("quotationStart", qm1.Open); var quotationEndElem = new XElement("quotationEnd", qm1.Close); delimitersElem.Add(quotationStartElem); delimitersElem.Add(quotationEndElem); } // Level 2 normal => alternateQuotationStart and alternateQuotationEnd QuotationMark qm2 = ws.QuotationMarks.FirstOrDefault(q => q.Level == 2 && q.Type == QuotationMarkingSystemType.Normal); if (qm2 != null) { var alternateQuotationStartElem = new XElement("alternateQuotationStart", qm2.Open); var alternateQuotationEndElem = new XElement("alternateQuotationEnd", qm2.Close); delimitersElem.Add(alternateQuotationStartElem); delimitersElem.Add(alternateQuotationEndElem); } // Remove special Sil:matched-pairs elements to repopulate later XElement specialElem = delimitersElem.NonAltElement("special"); XElement matchedPairsElem; if (specialElem != null) { matchedPairsElem = specialElem.NonAltElement(Sil + "matched-pairs"); if (matchedPairsElem != null) { matchedPairsElem.NonAltElements(Sil + "matched-pair").Remove(); RemoveIfEmpty(ref matchedPairsElem); } RemoveIfEmpty(ref specialElem); } foreach (var mp in ws.MatchedPairs) { var matchedPairElem = new XElement(Sil + "matched-pair"); // open and close are required matchedPairElem.SetAttributeValue("open", mp.Open); matchedPairElem.SetAttributeValue("close", mp.Close); matchedPairElem.SetAttributeValue("paraClose", mp.ParagraphClose); // optional, default to false? specialElem = GetOrCreateSpecialElement(delimitersElem); matchedPairsElem = specialElem.GetOrCreateElement(Sil + "matched-pairs"); matchedPairsElem.Add(matchedPairElem); } // Remove special Sil:punctuation-patterns elements to repopulate later XElement punctuationPatternsElem; if (specialElem != null) { punctuationPatternsElem = specialElem.NonAltElement(Sil + "punctuation-patterns"); if (punctuationPatternsElem != null) { punctuationPatternsElem.NonAltElements(Sil + "punctuation-pattern").Remove(); RemoveIfEmpty(ref punctuationPatternsElem); } RemoveIfEmpty(ref specialElem); } foreach (var pp in ws.PunctuationPatterns) { var punctuationPatternElem = new XElement(Sil + "punctuation-pattern"); // text is required punctuationPatternElem.SetAttributeValue("pattern", pp.Pattern); punctuationPatternElem.SetAttributeValue("context", PunctuationPatternContextToContext[pp.Context]); specialElem = GetOrCreateSpecialElement(delimitersElem); punctuationPatternsElem = specialElem.GetOrCreateElement(Sil + "punctuation-patterns"); punctuationPatternsElem.Add(punctuationPatternElem); } // Remove sil:quotation elements where type is blank or narrative. Also remove quotation continue elements. // These will be repopulated later XElement quotationmarksElem = null; if (specialElem != null) { quotationmarksElem = specialElem.NonAltElement(Sil + "quotation-marks"); if (quotationmarksElem != null) { quotationmarksElem.NonAltElements(Sil + "quotation").Where(e => string.IsNullOrEmpty((string) e.Attribute("type"))).Remove(); quotationmarksElem.NonAltElements(Sil + "quotation").Where(e => (string) e.Attribute("type") == "narrative").Remove(); quotationmarksElem.NonAltElements(Sil + "quotationContinue").Remove(); quotationmarksElem.NonAltElements(Sil + "alternateQuotationContinue").Remove(); RemoveIfEmpty(ref quotationmarksElem); } RemoveIfEmpty(ref specialElem); } if (qm1 != null) { var level1ContinuerElem = new XElement(Sil + "quotationContinue", qm1.Continue); specialElem = GetOrCreateSpecialElement(delimitersElem); quotationmarksElem = specialElem.GetOrCreateElement(Sil + "quotation-marks"); quotationmarksElem.Add(level1ContinuerElem); } if (qm2 != null && !string.IsNullOrEmpty(qm2.Continue)) { var level2ContinuerElem = new XElement(Sil + "alternateQuotationContinue", qm2.Continue); specialElem = GetOrCreateSpecialElement(delimitersElem); quotationmarksElem = specialElem.GetOrCreateElement(Sil + "quotation-marks"); quotationmarksElem.Add(level2ContinuerElem); } foreach (QuotationMark qm in ws.QuotationMarks) { // Level 1 and 2 normal have already been written if (!((qm.Level == 1 || qm.Level == 2) && qm.Type == QuotationMarkingSystemType.Normal)) { var quotationElem = new XElement(Sil + "quotation"); // open and level required quotationElem.SetAttributeValue("open", qm.Open); quotationElem.SetOptionalAttributeValue("close", qm.Close); quotationElem.SetOptionalAttributeValue("continue", qm.Continue); quotationElem.SetAttributeValue("level", qm.Level); // normal quotation mark can have no attribute defined. Narrative --> "narrative" quotationElem.SetOptionalAttributeValue("type", QuotationMarkingSystemTypesToQuotation[qm.Type]); specialElem = GetOrCreateSpecialElement(delimitersElem); quotationmarksElem = specialElem.GetOrCreateElement(Sil + "quotation-marks"); quotationmarksElem.Add(quotationElem); } } if ((ws.QuotationParagraphContinueType != QuotationParagraphContinueType.None) && (quotationmarksElem != null)) { quotationmarksElem.SetAttributeValue("paraContinueType", QuotationParagraphContinueTypesToQuotation[ws.QuotationParagraphContinueType]); } }