diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index e2bd0109c2..dc32532404 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -26,6 +26,7 @@ using XmlDrawing = DocumentFormat.OpenXml.Drawing; using DrawingWP = DocumentFormat.OpenXml.Drawing.Wordprocessing; using Pictures = DocumentFormat.OpenXml.Drawing.Pictures; +using System.Text.RegularExpressions; namespace SIL.FieldWorks.XWorks { @@ -1754,6 +1755,25 @@ private WP.Run CreateRun(string runText, string styleDisplayName) /// The BeforeAfterBetween run. private WP.Run CreateBeforeAfterBetweenRun(string text) { + if(text.Contains("\\A")) + { + var run = new WP.Run() + { + RunProperties = new WP.RunProperties(new RunStyle() { Val = WordStylesGenerator.BeforeAfterBetweenDisplayName }) + }; + // If the before after between text has line break characters return a composite run including theline breaks + // Use Regex.Matches to capture both the content and the delimiters + var matches = Regex.Matches(text, @"(\\A|\\0A)|[^\\]*(?:(?=\\A|\\0A)|$)"); + foreach (Match match in matches) + { + if (match.Groups[1].Success) + run.Append(new WP.Break() { Type = BreakValues.TextWrapping }); + else + run.Append(new WP.Text(match.Value)); + } + return run; + } + return CreateRun(text, WordStylesGenerator.BeforeAfterBetweenDisplayName); } diff --git a/Src/xWorks/xWorksTests/LcmWordGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmWordGeneratorTests.cs new file mode 100644 index 0000000000..165e6e538a --- /dev/null +++ b/Src/xWorks/xWorksTests/LcmWordGeneratorTests.cs @@ -0,0 +1,217 @@ +// Copyright (c) 2020-2023 SIL International +// This software is licensed under the LGPL, version 2.1 or later +// (http://www.gnu.org/licenses/lgpl-2.1.html) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms.VisualStyles; +using System.Xml; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using NUnit.Framework; +using SIL.FieldWorks.Common.Framework; +using SIL.FieldWorks.Common.FwUtils; +using SIL.FieldWorks.Common.Widgets; +using SIL.LCModel; +using SIL.LCModel.Application; +using SIL.LCModel.DomainImpl; +using SIL.LCModel.DomainServices; +using SIL.TestUtilities; +using XCore; +using static SIL.FieldWorks.XWorks.LcmWordGenerator; +// ReSharper disable StringLiteralTypo + +namespace SIL.FieldWorks.XWorks +{ + [TestFixture] + class LcmWordGeneratorTests : MemoryOnlyBackendProviderRestoredForEachTestTestBase, IDisposable + { + private FwXApp m_application; + private FwXWindow m_window; + private PropertyTable m_propertyTable; + private Mediator m_mediator; + private RecordClerk m_Clerk; + + private const string DictionaryNormal = "Dictionary-Normal"; + + private ConfiguredLcmGenerator.GeneratorSettings DefaultSettings; + + private static XmlNamespaceManager WordNamespaceManager; + + static LcmWordGeneratorTests() + { + var openXmlSchema = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + WordNamespaceManager = new XmlNamespaceManager(new NameTable()); + WordNamespaceManager.AddNamespace("w", openXmlSchema); + WordNamespaceManager.AddNamespace("r", openXmlSchema); + WordNamespaceManager.AddNamespace("wp", openXmlSchema); + } + + [OneTimeSetUp] + public override void FixtureSetup() + { + base.FixtureSetup(); + FwRegistrySettings.Init(); + m_application = new MockFwXApp(new MockFwManager { Cache = Cache }, null, null); + var m_configFilePath = Path.Combine(FwDirectoryFinder.CodeDirectory, m_application.DefaultConfigurationPathname); + m_window = new MockFwXWindow(m_application, m_configFilePath); + ((MockFwXWindow)m_window).Init(Cache); // initializes Mediator values + m_propertyTable = m_window.PropTable; + m_mediator = m_window.Mediator; + m_window.LoadUI(m_configFilePath); // actually loads UI here; needed for non-null stylesheet + var wordGenerator = new LcmWordGenerator(Cache); + wordGenerator.Init(new ReadOnlyPropertyTable(m_propertyTable)); + DefaultSettings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, + new ReadOnlyPropertyTable(m_propertyTable), true, false, null) + { ContentGenerator = wordGenerator, StylesGenerator = wordGenerator }; + + var styles = FontHeightAdjuster.StyleSheetFromPropertyTable(m_propertyTable).Styles; + if (!styles.Contains(DictionaryNormal)) + styles.Add(new BaseStyleInfo { Name = DictionaryNormal }); + if (!styles.Contains("Dictionary-Headword")) + styles.Add(new BaseStyleInfo { Name = "Dictionary-Headword", IsParagraphStyle = false}); + + m_Clerk = CreateClerk(); + m_propertyTable.SetProperty("ActiveClerk", m_Clerk, false); + + m_propertyTable.SetProperty("currentContentControl", "lexiconDictionary", false); + Cache.ProjectId.Path = Path.Combine(FwDirectoryFinder.SourceDirectory, "xWorks/xWorksTests/TestData/"); + } + + private RecordClerk CreateClerk() + { + const string entryClerk = @" + + + + + + + + + + + + + + + "; + var doc = new XmlDocument(); + doc.LoadXml(entryClerk); + XmlNode clerkNode = doc.SelectSingleNode("//tools/tool[@label='Dictionary']//parameters[@area='lexicon']"); + RecordClerk clerk = RecordClerkFactory.CreateClerk(m_mediator, m_propertyTable, clerkNode, false); + clerk.SortName = "Headword"; + return clerk; + } + #region disposal + protected virtual void Dispose(bool disposing) + { + System.Diagnostics.Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + ". ****** "); + if (disposing) + { + m_Clerk?.Dispose(); + m_application?.Dispose(); + m_window?.Dispose(); + m_mediator?.Dispose(); + m_propertyTable?.Dispose(); + } + } + + ~LcmWordGeneratorTests() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SupressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + #endregion disposal + + [OneTimeTearDown] + public override void FixtureTeardown() + { + base.FixtureTeardown(); + Dispose(); + } + + + [Test] + public void GenerateWordDocForEntry_OneSenseWithGlossGeneratesCorrectResult() + { + var wsOpts = ConfiguredXHTMLGeneratorTests.GetWsOptionsForLanguages(new[] { "en" }); + var glossNode = new ConfigurableDictionaryNode + { + FieldDescription = "Gloss", + DictionaryNodeOptions = wsOpts, + Style = "Dictionary-Headword" + }; + var sensesNode = new ConfigurableDictionaryNode + { + FieldDescription = "Senses", + DictionaryNodeOptions = ConfiguredXHTMLGeneratorTests.GetSenseNodeOptions(), + Children = new List { glossNode }, + Style = DictionaryNormal + }; + var mainEntryNode = new ConfigurableDictionaryNode + { + Children = new List { sensesNode }, + FieldDescription = "LexEntry", + Style = DictionaryNormal + }; + CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var entry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); + + //SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0) as DocFragment; + Console.WriteLine(result); + AssertThatXmlIn.String(result.mainDocPart.RootElement.OuterXml).HasSpecifiedNumberOfMatchesForXpath( + "/w:document/w:body/w:p/w:r/w:t[text()='gloss']", + 1, + WordNamespaceManager); + } + + [Test] + public void GenerateWordDocForEntry_LineBreaksInBeforeContentWork() + { + var wsOpts = ConfiguredXHTMLGeneratorTests.GetWsOptionsForLanguages(new[] { "en" }); + var glossNode = new ConfigurableDictionaryNode + { + FieldDescription = "Gloss", + DictionaryNodeOptions = wsOpts, + Style = "Dictionary-Headword", + Before = "\\Abefore\\0A" + }; + var sensesNode = new ConfigurableDictionaryNode + { + FieldDescription = "Senses", + DictionaryNodeOptions = ConfiguredXHTMLGeneratorTests.GetSenseNodeOptions(), + Children = new List { glossNode }, + Style = DictionaryNormal + }; + var mainEntryNode = new ConfigurableDictionaryNode + { + Children = new List { sensesNode }, + FieldDescription = "LexEntry", + Style = DictionaryNormal + }; + CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var entry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); + //SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0) as DocFragment; + Console.WriteLine(result); + AssertThatXmlIn.String(result?.mainDocPart.RootElement?.OuterXml).HasSpecifiedNumberOfMatchesForXpath( + "/w:document/w:body/w:p/w:r/w:br[@w:type='textWrapping']", + 2, + WordNamespaceManager); + } + } +} diff --git a/Src/xWorks/xWorksTests/xWorksTests.csproj b/Src/xWorks/xWorksTests/xWorksTests.csproj index 6ad730e7c1..04c4b0183a 100644 --- a/Src/xWorks/xWorksTests/xWorksTests.csproj +++ b/Src/xWorks/xWorksTests/xWorksTests.csproj @@ -130,6 +130,10 @@ AnyCPU + + False + ..\..\..\Output\Debug\DocumentFormat.OpenXml.dll + False @@ -308,6 +312,7 @@ +