diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 6d31f7ee34..a131ff96a7 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2807,7 +2807,7 @@ private static IFragment GenerateAudioWsContent(string wsId, private static void GenerateRunWithPossibleLink(GeneratorSettings settings, string writingSystem, IFragmentWriter writer, string style, string text, Guid linkDestination, bool rightToLeft, ConfigurableDictionaryNode config, string externalLink = null) { - settings.ContentGenerator.StartRun(writer, writingSystem); + settings.ContentGenerator.StartRun(writer, config, settings.PropertyTable, writingSystem); var wsRtl = settings.Cache.WritingSystemFactory.get_Engine(writingSystem).RightToLeftScript; if (rightToLeft != wsRtl) { @@ -3031,7 +3031,7 @@ private static void GenerateTableRow(ITsString rowUSFM, IFragmentWriter writer, private static void GenerateError(string text, IFragmentWriter writer, GeneratorSettings settings) { var writingSystem = settings.Cache.WritingSystemFactory.GetStrFromWs(settings.Cache.WritingSystemFactory.UserWs); - settings.ContentGenerator.StartRun(writer, writingSystem); + settings.ContentGenerator.StartRun(writer, null, settings.PropertyTable, writingSystem); settings.ContentGenerator.SetRunStyle(writer, null, settings.PropertyTable, writingSystem, null, true); if (text.Contains(TxtLineSplit)) { diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 186294e118..53e556eeeb 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -32,7 +32,7 @@ IFragment GenerateGroupingNode(object field, string className, ConfigurableDicti void EndMultiRunString(IFragmentWriter writer); void StartBiDiWrapper(IFragmentWriter writer, bool rightToLeft); void EndBiDiWrapper(IFragmentWriter writer); - void StartRun(IFragmentWriter writer, string writingSystem); + void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem); void EndRun(IFragmentWriter writer); void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propertyTable, string writingSystem, string runStyle, bool error); void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 3821c6bfd9..0a85fee66f 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -161,7 +161,7 @@ public void EndBiDiWrapper(IFragmentWriter writer) { } - public void StartRun(IFragmentWriter writer, string writingSystem) + public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem) { var jsonWriter = (JsonFragmentWriter)writer; jsonWriter.StartObject(); diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 2e5367f0bf..f38ec6a70a 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -35,7 +35,6 @@ namespace SIL.FieldWorks.XWorks public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator { private LcmCache Cache { get; } - private static Styles _styleSheet { get; set; } = new Styles(); private static Dictionary _styleDictionary = new Dictionary(); private ReadOnlyPropertyTable _propertyTable; internal const int maxImageHeightInches = 1; @@ -127,21 +126,22 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor { // Initialize word doc's styles xml stylePart = AddStylesPartToPackage(fragment.DocFrag); + Styles styleSheet = new Styles(); - // Add generated styles into the stylesheet from the dictionary - foreach (var style in _styleDictionary.Values) + lock(_styleDictionary) { - _styleSheet.AppendChild(style.CloneNode(true)); + // Add generated styles into the stylesheet from the dictionary + foreach (var style in _styleDictionary.Values) + { + styleSheet.AppendChild(style.CloneNode(true)); + } + + // clear the dictionary + _styleDictionary = new Dictionary(); } // Clone styles from the stylesheet into the word doc's styles xml - stylePart.Styles = ((Styles)_styleSheet.CloneNode(true)); - - // clear the dictionary - _styleDictionary = new Dictionary(); - - // clear the styleSheet - _styleSheet = new WP.Styles(); + stylePart.Styles = ((Styles)styleSheet.CloneNode(true)); } fragment.DocFrag.Dispose(); @@ -214,7 +214,7 @@ public DocFragment(MemoryStream str) /// public DocFragment(string str) : this() { - // Only create paragraph, run, and text objects if the string is nonempty + // Only create run, and text objects if the string is nonempty if (!string.IsNullOrEmpty(str)) { WP.Run run = DocBody.AppendChild(new WP.Run()); @@ -248,115 +248,28 @@ internal static DocFragment GenerateLetterHeaderDocFragment(string str, string s return docFrag; } - public static void LinkStyleOrInheritParentStyle(IFragment content, ConfigurableDictionaryNode config) + public static string GetWsStyleName(LcmCache cache, string styleName, ConfigurableDictionaryNode config, string writingSystem) { - DocFragment frag = ((DocFragment)content); - - // Don't add style for tables. - if (frag.DocBody.Elements().FirstOrDefault() != null) + // If the config does not contain writing system options, then just return the style name.(An example is custom fields.) + if (!(config.DictionaryNodeOptions is DictionaryNodeWritingSystemOptions)) { - return; + return styleName; } - if (!string.IsNullOrEmpty(config.Style)) - { - frag.AddStyleLink(config.Style, config, config.StyleType); - } - else if (!string.IsNullOrEmpty(config.Parent?.Style)) + var wsStr = writingSystem; + var possiblyMagic = WritingSystemServices.GetMagicWsIdFromName(writingSystem); + // If it is magic, then get the associated ws. + if (possiblyMagic != 0) { - frag.AddStyleLink(config.Parent.Style, config.Parent, config.Parent.StyleType); + // Get a list of the writing systems for the magic name, and use the first one. + wsStr = WritingSystemServices.GetWritingSystemList(cache, possiblyMagic, false).First().Id; } - } - public void AddStyleLink(string styleName, ConfigurableDictionaryNode config, ConfigurableDictionaryNode.StyleTypes styleType) - { - styleName = GetWsStyleName(styleName, config); - if (string.IsNullOrEmpty(styleName)) - return; - - if (styleType == ConfigurableDictionaryNode.StyleTypes.Paragraph) - { - if (string.IsNullOrEmpty(ParagraphStyle)) - { - ParagraphStyle = styleName; - } - } - else - LinkCharStyle(styleName); - } - - /// - /// Appends the given styleName as a style ID for the last paragraph in the doc, or creates a new paragraph with the given styleID if no paragraph exists. - /// - /// - internal void LinkParaStyle(string styleName) - { - if (string.IsNullOrEmpty(styleName)) - return; - - WP.Paragraph par = GetLastParagraph(); - if (par.ParagraphProperties != null) - { - // if a style is already linked to the paragraph, return without adding another link - if (par.ParagraphProperties.Descendants().Any()) - return; - - par.ParagraphProperties.PrependChild(new ParagraphStyleId() { Val = styleName }); - } - else - { - WP.ParagraphProperties paragraphProps = new WP.ParagraphProperties(new ParagraphStyleId() { Val = styleName }); - par.PrependChild(paragraphProps); - } - } - - private void LinkCharStyle(string styleName) - { - WP.Run run = GetLastRun(); - if (run.RunProperties != null) - { - // if a style is already linked to the run, replace the stylename and return without adding another link - if (run.RunProperties.Descendants().Any()) - { - run.RunProperties.Descendants().Last().Val = styleName; - return; - } - - run.RunProperties.Append(new RunStyle() { Val = styleName }); - } - else - { - WP.RunProperties runProps = - new WP.RunProperties(new RunStyle() { Val = styleName }); - // Prepend runproperties so it appears before any text elements contained in the run - run.PrependChild(runProps); - } - } - - public static string GetWsStyleName(string styleName, ConfigurableDictionaryNode config) - { - if (config.DictionaryNodeOptions is DictionaryNodeWritingSystemOptions) - { - foreach (var opt in ((DictionaryNodeWritingSystemOptions)config.DictionaryNodeOptions).Options) - { - if (opt.IsEnabled) - { - // If it's magic then don't return a language tag specific style. - var possiblyMagic = WritingSystemServices.GetMagicWsIdFromName(opt.Id); - if (possiblyMagic != 0) - { - return styleName; - } - // else, the DictionaryNodeOption Id specifies a particular writing system - // if there is no base style, return just the ws style - if (styleName == null) - return WordStylesGenerator.GetWsString(opt.Id); - // if there is a base style, return the ws-specific version of that style - return styleName + WordStylesGenerator.GetWsString(opt.Id); - } - } - } - return styleName; + // If there is no base style, return just the ws style. + if (styleName == null) + return WordStylesGenerator.GetWsString(wsStr); + // If there is a base style, return the ws-specific version of that style. + return styleName + WordStylesGenerator.GetWsString(wsStr); } /// @@ -704,17 +617,57 @@ public void Insert(IFragment frag) /// /// Add a new run to the WordFragment DocBody. /// - public void CreateRun(string writingSystem) + public void AddRun(LcmCache cache, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem) { + var run = new WP.Run(); + WordFragment.DocBody.AppendChild(run); + if (writingSystem == null) - WordFragment.DocBody.AppendChild(new WP.Run()); + { + return; + } + + string styleName = null; + if (config == null || string.IsNullOrEmpty(config.Style)) + { + styleName = WordStylesGenerator.GetWsString(writingSystem); + } else { - var wsString = WordStylesGenerator.GetWsString(writingSystem); - var run = new WP.Run(); - run.Append(new RunProperties()); - run.RunProperties.Append(new RunStyle() { Val = "span"+wsString }); - WordFragment.DocBody.AppendChild(run); + styleName = DocFragment.GetWsStyleName(cache, config.Style, config, writingSystem); + } + run.Append(new RunProperties(new RunStyle() { Val = styleName })); + + // If the style is not in the dictionary, then add it. + lock (_styleDictionary) + { + if (!_styleDictionary.ContainsKey(styleName)) + { + if (config != null && !string.IsNullOrEmpty(config.Style)) + { + var wsId = cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(writingSystem); + Style style = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(config.Style, wsId, propTable); + if (style == null || style.Type != StyleValues.Character) + { + // If we hit this assert, then we might end up referencing a style that + // does not get created. + Debug.Assert(false); + return; + } + + var wsString = WordStylesGenerator.GetWsString(writingSystem); + style.Append(new BasedOn() { Val = wsString }); + style.StyleId = styleName; + style.StyleName = new StyleName() { Val = style.StyleId }; + _styleDictionary[styleName] = style; + } + else + { + // If we hit this assert, then we might need to create a style for just the ws. + // We are expecting the ws style to be added to the _styleDictionary in GetDefaultWordStyles(). + Debug.Assert(false); + } + } } } } @@ -737,17 +690,21 @@ public IFragment GenerateAudioLinkContent(string classname, string srcAttribute, } public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className) { - return WriteProcessedElementContent(elementContent, config, className); + return WriteProcessedElementContent(elementContent, config); } public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className) { - return WriteProcessedElementContent(elementContent, config, className); + return WriteProcessedElementContent(elementContent, config); } - private IFragment WriteProcessedElementContent(IFragment elementContent, ConfigurableDictionaryNode config, string className) + private IFragment WriteProcessedElementContent(IFragment elementContent, ConfigurableDictionaryNode config) { - // Use the style name and type of the config node or its parent to link a style to the elementContent fragment where the processed contents are written. - DocFragment.LinkStyleOrInheritParentStyle(elementContent, config); + // Check if the character style for the last run should be modified. + if (string.IsNullOrEmpty(config.Style) && !string.IsNullOrEmpty(config.Parent.Style) && + (config.Parent.StyleType != ConfigurableDictionaryNode.StyleTypes.Paragraph)) + { + AddRunStyle(elementContent, config.Parent.Style, false); + } bool eachOnANewLine = config != null && config.DictionaryNodeOptions is IParaOption && @@ -885,15 +842,14 @@ public IFragment AddSenseData(IFragment senseNumberSpan, Guid ownerGuid, Configu return senseData; } + public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config, IFragment content, bool first) { - if (!string.IsNullOrEmpty(config.Style)) + // Add the style to all the runs in the content fragment. + if (!string.IsNullOrEmpty(config.Style) && + (config.StyleType != ConfigurableDictionaryNode.StyleTypes.Paragraph)) { - if (isBlock && (config.StyleType == ConfigurableDictionaryNode.StyleTypes.Paragraph)) - ((DocFragment)content).AddStyleLink(config.Style, config, ConfigurableDictionaryNode.StyleTypes.Paragraph); - - else if (!isBlock) - ((DocFragment)content).AddStyleLink(config.Style, config,ConfigurableDictionaryNode.StyleTypes.Character); + AddRunStyle(content, config.Style, true); } var collData = CreateFragment(); @@ -974,9 +930,9 @@ public void EndBiDiWrapper(IFragmentWriter writer) /// /// /// - public void StartRun(IFragmentWriter writer, string writingSystem) + public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem) { - ((WordFragmentWriter)writer).CreateRun(writingSystem); + ((WordFragmentWriter)writer).AddRun(Cache, config, propTable, writingSystem); } public void EndRun(IFragmentWriter writer) { @@ -986,7 +942,7 @@ public void EndRun(IFragmentWriter writer) } /// - /// Set the style for a specific run. + /// Overrides the style for a specific run. /// This is needed to set the specific style for any field that allows the /// default style to be overridden (Table Cell, Custom Field, Note...). /// @@ -994,32 +950,15 @@ public void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode confi { if (!string.IsNullOrEmpty(runStyle)) { - // Add the style link. - ((WordFragmentWriter)writer).WordFragment.AddStyleLink(runStyle, config, ConfigurableDictionaryNode.StyleTypes.Character); - - // Only add the style to the styleSheet if not already there. - if (!_styleSheet.ChildElements.Any(p => ((Style)p).StyleId == runStyle)) - { - int ws = Cache.WritingSystemFactory.GetWsFromStr(writingSystem); - var wpStyle = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(runStyle, ws, _propertyTable); - _styleSheet.Append(wpStyle); - } + AddRunStyle(((WordFragmentWriter)writer).WordFragment, runStyle, false); } } public void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination) { - if (config != null && !string.IsNullOrEmpty(config.Style)) - { - ((WordFragmentWriter)writer).WordFragment.AddStyleLink(config.Style, config, ConfigurableDictionaryNode.StyleTypes.Character); - } return; } public void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, string externalDestination) { - if (config != null && !string.IsNullOrEmpty(config.Style)) - { - ((WordFragmentWriter)writer).WordFragment.AddStyleLink(config.Style, config, ConfigurableDictionaryNode.StyleTypes.Character); - } return; } public void EndLink(IFragmentWriter writer) @@ -1284,16 +1223,17 @@ public void AddEntryData(IFragmentWriter writer, List()) { - _styleSheet.Append(style.CloneNode(true)); + lock(_styleDictionary) + { + _styleDictionary[style.StyleId] = (WP.Style)style.CloneNode(true); + } } } @@ -1476,6 +1424,37 @@ public void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyProperty // TODO: Generate style for audiows after we add audio to export //WordStylesGenerator.GenerateWordStyleForAudioWs(_styleSheet, cache); } + + /// + /// Gets the style from the dictionary (if it is in the dictionary). If not in the + /// dictionary then create the Word style from the LCM Style Sheet and add it to the dictionary. + /// + /// Returns null if it fails to find or create the character style. + private Style GetOrCreateCharacterStyle(string styleName) + { + Style retStyle = null; + lock (_styleDictionary) + { + if (_styleDictionary.TryGetValue(styleName, out retStyle)) + { + if (retStyle.Type != StyleValues.Character) + { + return null; + } + } + else + { + retStyle = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(styleName, 0, _propertyTable); + if (retStyle == null || retStyle.Type != StyleValues.Character) + { + return null; + } + _styleDictionary[styleName] = retStyle; + } + } + return retStyle; + } + public string AddStyles(ConfigurableDictionaryNode node) { return AddStyles(node, false); @@ -1494,23 +1473,24 @@ public string AddStyles(ConfigurableDictionaryNode node, bool addEntryContinuati // Generate all styles that are needed by this class and add them to the dictionary with their stylename as the key. var className = $".{CssGenerator.GetClassAttributeForConfig(node)}"; - lock (_styleDictionary) + Styles styleContent = null; + if (addEntryContinuationStyle) { - Styles styleContent = null; - if (addEntryContinuationStyle) - { - styleContent = WordStylesGenerator.CheckRangeOfStylesForEmpties(WordStylesGenerator.GenerateContinuationWordStyles(node, _propertyTable)); - } - else - { - styleContent = WordStylesGenerator.CheckRangeOfStylesForEmpties(WordStylesGenerator.GenerateWordStylesFromConfigurationNode(node, className, _propertyTable)); - } - if (styleContent == null) - return className; - if (!styleContent.Any()) - return className; + styleContent = WordStylesGenerator.CheckRangeOfStylesForEmpties(WordStylesGenerator.GenerateContinuationWordStyles(node, _propertyTable)); + } + else + { + styleContent = WordStylesGenerator.CheckRangeOfStylesForEmpties(WordStylesGenerator.GenerateWordStylesFromConfigurationNode(node, className, _propertyTable)); + } + + if (styleContent == null) + return className; + if (!styleContent.Any()) + return className; - foreach (Style style in styleContent.Descendants