From 55b8ca72b0cfd68e37f2209a8cb6c14109f26518 Mon Sep 17 00:00:00 2001 From: danielshih Date: Sat, 23 Mar 2024 17:23:03 +0800 Subject: [PATCH 1/5] implement-async method. --- src/MiniWord/Extensions/OpenXmlExtension.cs | 28 ++++---- src/MiniWord/MiniWord.Implment.cs | 73 +++++++++++++-------- src/MiniWord/MiniWord.cs | 25 +++++++ src/MiniWord/Utility/FileAyncExtension.cs | 14 ++++ src/MiniWord/Utility/Helpers.cs | 1 + 5 files changed, 101 insertions(+), 40 deletions(-) create mode 100644 src/MiniWord/Utility/FileAyncExtension.cs diff --git a/src/MiniWord/Extensions/OpenXmlExtension.cs b/src/MiniWord/Extensions/OpenXmlExtension.cs index 6e247d7..acd6e61 100644 --- a/src/MiniWord/Extensions/OpenXmlExtension.cs +++ b/src/MiniWord/Extensions/OpenXmlExtension.cs @@ -12,10 +12,10 @@ namespace MiniSoftware.Extensions internal static class OpenXmlExtension { /// - /// ߼õַ + /// �߼��������õ���������������ַ��� /// - /// - /// Item1ıItem2飻Item3ı + /// ���� + /// Item1�������ı���Item2���飻Item3�����ı� internal static List, List>> GetContinuousString(this Paragraph paragraph) { List, List>> tuples = new List, List>>(); @@ -26,13 +26,13 @@ internal static List, List>> GetContinuousString(t var runs = new List(); var texts = new List(); - //䣺Ӽ + //���䣺�����Ӽ� foreach (var pChildElement in paragraph.ChildElements) { - // + //�� if (pChildElement is Run run) { - //ı + //�ı��� if (run.IsText()) { var text = run.GetFirstChild(); @@ -50,10 +50,10 @@ internal static List, List>> GetContinuousString(t texts = new List(); } } - //ʽǩ... + //��ʽ����ǩ... else { - // + //���������� if (pChildElement is BookmarkStart || pChildElement is BookmarkEnd) { @@ -81,16 +81,16 @@ internal static List, List>> GetContinuousString(t } /// - /// ַַ + /// �����ַ����������ַ������� /// - /// ַ - /// ַ + /// �����ַ����� + /// �������ַ��� internal static void TrimStringToInContinuousString(this IEnumerable texts, string text) { /* - //Ϊ[A][BC][DE][FG][H] - //滻[AB][E][GH] - //ŻΪ[AB][C][DE][FGH][] + //�����Ϊ��[A][BC][DE][FG][H] + //�����滻��[AB][E][GH] + //�Ż���Ϊ��[AB][C][DE][FGH][] */ var allTxtx = string.Concat(texts.SelectMany(o => o.Text)); diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 3234eba..11ada1e 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -15,6 +15,8 @@ namespace MiniSoftware using A = DocumentFormat.OpenXml.Drawing; using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing; using PIC = DocumentFormat.OpenXml.Drawing.Pictures; + using System.Threading.Tasks; + using System.Threading; public static partial class MiniWord { @@ -25,22 +27,39 @@ private static void SaveAsByTemplateImpl(Stream stream, byte[] template, Diction using (var ms = new MemoryStream()) { ms.Write(template, 0, template.Length); - ms.Position = 0; - using (var docx = WordprocessingDocument.Open(ms, true)) - { - var hc = docx.MainDocumentPart.HeaderParts.Count(); - var fc = docx.MainDocumentPart.FooterParts.Count(); - for (int i = 0; i < hc; i++) - docx.MainDocumentPart.HeaderParts.ElementAt(i).Header.Generate(docx, value); - for (int i = 0; i < fc; i++) - docx.MainDocumentPart.FooterParts.ElementAt(i).Footer.Generate(docx, value); - docx.MainDocumentPart.Document.Body.Generate(docx, value); - docx.Save(); - } - bytes = ms.ToArray(); + bytes = WriteToByte(data, ms); } stream.Write(bytes, 0, bytes.Length); } + + private static async Task SaveAsByTemplateImplAsync(Stream stream, byte[] template, Dictionary data,CancellationToken token) + { + byte[] bytes = null; + using (var ms = new MemoryStream()) + { + await ms.WriteAsync(template, 0, template.Length, token); + bytes = WriteToByte(data, ms); + } + await stream.WriteAsync(bytes, 0, bytes.Length,token); + } + + private static byte[] WriteToByte(Dictionary value, MemoryStream ms) + { + ms.Position = 0; + using (var docx = WordprocessingDocument.Open(ms, true)) + { + var hc = docx.MainDocumentPart.HeaderParts.Count(); + var fc = docx.MainDocumentPart.FooterParts.Count(); + for (int i = 0; i < hc; i++) + docx.MainDocumentPart.HeaderParts.ElementAt(i).Header.Generate(docx, value); + for (int i = 0; i < fc; i++) + docx.MainDocumentPart.FooterParts.ElementAt(i).Footer.Generate(docx, value); + docx.MainDocumentPart.Document.Body.Generate(docx, value); + docx.Save(); + } + return ms.ToArray(); + } + private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary tags) { // avoid {{tag}} like {{ @@ -53,14 +72,14 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum { foreach (var table in tables) { - var trs = table.Descendants().ToArray(); // remember toarray or system will loop OOM; + var trs = table.Descendants().ToArray(); // remember toarray otherwise system will loop OOM; foreach (var tr in trs) { var innerText = tr.InnerText.Replace("{{foreach", "").Replace("endforeach}}", "") .Replace("{{if(", "").Replace(")if", "").Replace("endif}}", ""); - var matchs = (Regex.Matches(innerText, "(?<={{).*?\\..*?(?=}})") - .Cast().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value)).ToArray(); + var matchs = Regex.Matches(innerText, "(?<={{).*?\\..*?(?=}})") + .Cast().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value).ToArray(); if (matchs.Length > 0) { var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray(); @@ -76,15 +95,8 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum foreach (Dictionary es in list) { - var dic = new Dictionary(); //TODO: optimize - + var dic = es.ToDictionary(key => $"{listKey}.{key}", item=>item.Value); var newTr = tr.CloneNode(true); - foreach (var e in es) - { - var dicKey = $"{listKey}.{e.Key}"; - dic.Add(dicKey, e.Value); - } - ReplaceStatements(newTr, tags: dic); ReplaceText(newTr, docx, tags: dic); @@ -356,8 +368,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey => { - var innerTag = "{{" + tag.Key + "." + dictKey + "}}"; - return t.Text.Contains(innerTag); + return t.Text.Contains($@"{{{{{tag.Key}.{dictKey}}}}}"); }))) { isMatch = true; @@ -700,5 +711,15 @@ private static byte[] GetBytes(string path) return ms.ToArray(); } } + + private static async Task GetByteAsync(string path) + { + using (var st = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true)) + using (var ms = new MemoryStream()) + { + await st.CopyToAsync(ms); + return ms.ToArray(); + } + } } } \ No newline at end of file diff --git a/src/MiniWord/MiniWord.cs b/src/MiniWord/MiniWord.cs index c5e7beb..675e8e1 100644 --- a/src/MiniWord/MiniWord.cs +++ b/src/MiniWord/MiniWord.cs @@ -2,11 +2,14 @@ namespace MiniSoftware { using DocumentFormat.OpenXml.Office2013.Excel; using MiniSoftware.Extensions; + using MiniSoftware.Utility; using System.Collections.Generic; using System.ComponentModel; using System.Dynamic; using System.IO; using System.Linq.Expressions; + using System.Threading; + using System.Threading.Tasks; public static partial class MiniWord { @@ -31,5 +34,27 @@ public static void SaveAsByTemplate(this Stream stream, byte[] templateBytes, ob { SaveAsByTemplateImpl(stream, templateBytes, value.ToDictionary()); } + + public static async Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value,CancellationToken token = default(CancellationToken)) + { + await SaveAsByTemplateImplAsync(stream, templateBytes, value.ToDictionary(),token).ConfigureAwait(false); + } + + public static async Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value,CancellationToken token = default(CancellationToken)) + { + await SaveAsByTemplateImplAsync(stream, await GetByteAsync(templatePath), value.ToDictionary(),token).ConfigureAwait(false); + } + + public static async Task SaveAsByTemplateAsync(string path, string templatePath, object value,CancellationToken token = default(CancellationToken)) + { + using (var stream = FileHelper.CreateAsync(path)) + await SaveAsByTemplateImplAsync(await stream, await GetByteAsync(templatePath), value.ToDictionary(),token); + } + + public static async Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value,CancellationToken token = default(CancellationToken)) + { + using (var stream = FileHelper.CreateAsync(path)) + await SaveAsByTemplateImplAsync(await stream, templateBytes, value.ToDictionary(),token); + } } } \ No newline at end of file diff --git a/src/MiniWord/Utility/FileAyncExtension.cs b/src/MiniWord/Utility/FileAyncExtension.cs new file mode 100644 index 0000000..8bd4081 --- /dev/null +++ b/src/MiniWord/Utility/FileAyncExtension.cs @@ -0,0 +1,14 @@ +namespace MiniSoftware.Utility +{ + using System.IO; + using System.Threading.Tasks; + internal static class FileHelper{ + internal static async Task CreateAsync(string path) + { + using (var stream = File.Create(path)) + { + return await Task.FromResult(stream); + } + } + } +} \ No newline at end of file diff --git a/src/MiniWord/Utility/Helpers.cs b/src/MiniWord/Utility/Helpers.cs index 5e8f3a9..e919602 100644 --- a/src/MiniWord/Utility/Helpers.cs +++ b/src/MiniWord/Utility/Helpers.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Dynamic; using System.IO; + using System.Threading.Tasks; internal static partial class Helpers { From b494b9189ced0d597d67b327209d79058409665f Mon Sep 17 00:00:00 2001 From: danielshih Date: Sat, 23 Mar 2024 17:53:55 +0800 Subject: [PATCH 2/5] add unit test. --- src/MiniWord/MiniWord.Implment.cs | 2 +- src/MiniWord/MiniWord.cs | 9 +- src/MiniWord/Utility/FileAyncExtension.cs | 14 - tests/MiniWordTests/IssueTestsAsync.cs | 876 ++++++++++++++++++++++ tests/MiniWordTests/MiniWordTestAsync.cs | 272 +++++++ tests/MiniWordTests/MiniWordTests.csproj | 2 +- 6 files changed, 1155 insertions(+), 20 deletions(-) delete mode 100644 src/MiniWord/Utility/FileAyncExtension.cs create mode 100644 tests/MiniWordTests/IssueTestsAsync.cs create mode 100644 tests/MiniWordTests/MiniWordTestAsync.cs diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 11ada1e..ffe2a6f 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -95,7 +95,7 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum foreach (Dictionary es in list) { - var dic = es.ToDictionary(key => $"{listKey}.{key}", item=>item.Value); + var dic = es.ToDictionary(e => $"{listKey}.{e.Key}", e => e.Value); var newTr = tr.CloneNode(true); ReplaceStatements(newTr, tags: dic); diff --git a/src/MiniWord/MiniWord.cs b/src/MiniWord/MiniWord.cs index 675e8e1..3ca2706 100644 --- a/src/MiniWord/MiniWord.cs +++ b/src/MiniWord/MiniWord.cs @@ -3,6 +3,7 @@ namespace MiniSoftware using DocumentFormat.OpenXml.Office2013.Excel; using MiniSoftware.Extensions; using MiniSoftware.Utility; + using System; using System.Collections.Generic; using System.ComponentModel; using System.Dynamic; @@ -47,14 +48,14 @@ public static void SaveAsByTemplate(this Stream stream, byte[] templateBytes, ob public static async Task SaveAsByTemplateAsync(string path, string templatePath, object value,CancellationToken token = default(CancellationToken)) { - using (var stream = FileHelper.CreateAsync(path)) - await SaveAsByTemplateImplAsync(await stream, await GetByteAsync(templatePath), value.ToDictionary(),token); + using (var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true)) + await SaveAsByTemplateImplAsync(stream, await GetByteAsync(templatePath), value.ToDictionary(),token); } public static async Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value,CancellationToken token = default(CancellationToken)) { - using (var stream = FileHelper.CreateAsync(path)) - await SaveAsByTemplateImplAsync(await stream, templateBytes, value.ToDictionary(),token); + using (var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true)) + await SaveAsByTemplateImplAsync(stream, templateBytes, value.ToDictionary(),token); } } } \ No newline at end of file diff --git a/src/MiniWord/Utility/FileAyncExtension.cs b/src/MiniWord/Utility/FileAyncExtension.cs deleted file mode 100644 index 8bd4081..0000000 --- a/src/MiniWord/Utility/FileAyncExtension.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MiniSoftware.Utility -{ - using System.IO; - using System.Threading.Tasks; - internal static class FileHelper{ - internal static async Task CreateAsync(string path) - { - using (var stream = File.Create(path)) - { - return await Task.FromResult(stream); - } - } - } -} \ No newline at end of file diff --git a/tests/MiniWordTests/IssueTestsAsync.cs b/tests/MiniWordTests/IssueTestsAsync.cs new file mode 100644 index 0000000..1667da6 --- /dev/null +++ b/tests/MiniWordTests/IssueTestsAsync.cs @@ -0,0 +1,876 @@ +using MiniSoftware; +using System; +using System.Collections.Generic; +using Xunit; +using System.Dynamic; +using DocumentFormat.OpenXml.Wordprocessing; +using System.Threading.Tasks; + +namespace MiniWordTests +{ + public class IssueTestsAsync + { + [Fact] + public async Task TestIssue69() + { + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue47.docx"); + var value = new Dictionary() + { + { "line",new List> + { + new Dictionary + { + {"num", "1"}, + {"spc", "Specification1"}, + {"desc", "Description1"}, + {"qty", "10"}, + {"up", "20.00"}, + {"tax_rate", "0.20"}, + {"tax_amount", "4.00"} + }, + new Dictionary + { + {"num", "2"}, + {"spc", "Specification2"}, + {"desc", "Description2"}, + {"qty", "5"}, + {"up", "30.00"}, + {"tax_rate", "0.15"}, + {"tax_amount", "2.25"} + } + } + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + // TODO: waiting solution + //var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + //Assert.Contains(@"MiniWord", xml); + } + } + + [Fact] + public async Task TaskSplitTag() + { + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestDemo04.docx"); + var value = new Dictionary + { + ["title"] = "Hello MiniWord", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"MiniWord", xml); + } + } + + /// + /// [Text color multiple tags format error · Issue #37 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/37) + /// + [Fact] + public async Task TestIssue37() + { + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue37.docx"); + var value = new Dictionary + { + ["Content"] = "Test", + ["Content2"] = "Test2", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Test", xml); + Assert.Contains(@"Test2", xml); + } + + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue37.docx"); + var value = new Dictionary + { + ["Content"] = new MiniWordHyperLink() + { + Url = "https://google.com", + Text = "Test1!!" + }, + ["Content2"] = new MiniWordHyperLink() + { + Url = "https://google.com", + Text = "Test2!!" + }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Test", xml); + Assert.Contains(@"Test2", xml); + } + } + + [Fact] + public async Task TestDemo04() + { + var outputPath = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestDemo04.docx"); + var value = new Dictionary() { ["title"] = "Hello MiniWord" }; + await MiniWord.SaveAsByTemplateAsync(outputPath, templatePath, value); + } + + [Fact] + public async Task TestDemo04_new() + { + var outputPath = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestDemo04.docx"); + var value = new { title = "Hello MiniWord" }; + await MiniWord.SaveAsByTemplateAsync(outputPath, templatePath, value); + } + + + [Fact] + public async Task TestIssue18() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue18.docx"); + + { + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = new List> { + new Dictionary{{"name","Jack"},{ "department", "HR" } }, + new Dictionary {{ "name", "Loan"},{ "department", "IT" } } + }, + ["employees"] = new List> { + new Dictionary{{ "name", "Wade" },{ "department", "HR" } }, + new Dictionary {{ "name", "Felix" },{ "department", "HR" } }, + new Dictionary{{ "name", "Eric" },{ "department", "IT" } }, + new Dictionary {{ "name", "Keaton" },{ "department", "IT" } } + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Keaton", xml); + Assert.Contains(@"Eric", xml); + } + + //Strong type + { + var value = new + { + title = "FooCompany", + managers = new[] + { + new {name="Jack",department="HR" }, + new {name="Loan",department="IT" }, + }, + employees = new[] + { + new {name="Jack",department="HR" }, + new {name="Loan",department="HR" }, + new {name="Eric",department="IT" }, + new {name="Keaton",department="IT" }, + }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Keaton", xml); + Assert.Contains(@"Eric", xml); + } + + //Strong type + { + Foo value = new Foo() + { + title = "FooCompany", + managers = new List() + { + new User (){ name="Jack",department="HR"}, + new User (){ name="Loan",department="IT"}, + }, + employees = new List() + { + new User (){ name="Jack",department="HR"}, + new User (){ name="Loan",department="HR"}, + new User (){ name="Eric",department="IT"}, + new User (){ name="Keaton",department="IT"}, + }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Keaton", xml); + Assert.Contains(@"Eric", xml); + } + } + + /// + /// [Split template string like `{{Tag}}` problem · Issue #17 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/17) + /// + [Fact] + public async Task TestIssue17() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue17.docx"); + var value = new Dictionary() + { + ["Content"] = "Test", + ["Content2"] = "Test2", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Test", xml); + Assert.Contains(@"Test2", xml); + } + + /// + /// [Split template string like `{{Tag}}` problem · Issue #17 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/17) + /// + [Fact] + public async Task TestIssue17_new() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue17.docx"); + var value = new + { + Content = "Test", + Content2 = "Test2", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Test", xml); + Assert.Contains(@"Test2", xml); + } + + + /// + /// [Support table generate · Issue #13 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/13) + /// + [Fact] + public async Task TestIssue13() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestExpenseDemo.docx"); + var value = new Dictionary() + { + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate",DateTime.Parse("2022-09-08 08:30:00")}, + { "eDate",DateTime.Parse("2022-09-08 15:00:00")}, + { "How","Discussion requirement part1"}, + { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting02.png"), Width = 160, Height = 90 }}, + }, + new Dictionary + { + { "sDate",DateTime.Parse("2022-09-09 08:30:00")}, + { "eDate",DateTime.Parse("2022-09-09 17:00:00")}, + { "How","Discussion requirement part2 and development"}, + { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting01.png"), Width = 160, Height = 90 }}, + }, + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Discussion requirement part2 and development", xml); + Assert.Contains(@"Discussion requirement part1", xml); + } + + /// + /// [Support table generate · Issue #13 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/13) + /// + [Fact] + public async Task TestIssue13_new() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestExpenseDemo.docx"); + var value = new + { + TripHs = new List> + { + new Dictionary + { + { "sDate",DateTime.Parse("2022-09-08 08:30:00")}, + { "eDate",DateTime.Parse("2022-09-08 15:00:00")}, + { "How","Discussion requirement part1"}, + { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting02.png"), Width = 160, Height = 90 }}, + }, + new Dictionary + { + { "sDate",DateTime.Parse("2022-09-09 08:30:00")}, + { "eDate",DateTime.Parse("2022-09-09 17:00:00")}, + { "How","Discussion requirement part2 and development"}, + { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting01.png"), Width = 160, Height = 90 }}, + }, + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Discussion requirement part2 and development", xml); + Assert.Contains(@"Discussion requirement part1", xml); + } + + [Fact] + public async Task TestDemo01_Tag_Text() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestExpenseDemo.docx"); + var value = new Dictionary() + { + ["Name"] = "Jack", + ["Department"] = "IT Department", + ["Purpose"] = "Shanghai site needs a new system to control HR system.", + ["StartDate"] = DateTime.Parse("2022-09-07 08:30:00"), + ["EndDate"] = DateTime.Parse("2022-09-15 15:30:00"), + ["Approved"] = true, + ["Total_Amount"] = 123456, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + + + [Fact] + public async Task TestDemo01_Tag_Text_new() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestExpenseDemo.docx"); + var value = new + { + Name = "Jack", + Department = "IT Department", + Purpose = "Shanghai site needs a new system to control HR system.", + StartDate = DateTime.Parse("2022-09-07 08:30:00"), + EndDate = DateTime.Parse("2022-09-15 15:30:00"), + Approved = true, + Total_Amount = 123456, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + + /// + /// [System.InvalidOperationException: 'The parent of this element is null.' · Issue #12 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/12) + /// + [Fact] + public async Task TestIssue12() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new Dictionary() + { + ["Company_Name"] = "MiniSofteware\n", + ["Name"] = "Jack", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP\n", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"MiniSofteware", xml); + Assert.Contains(@"", xml); + } + + /// + /// [System.InvalidOperationException: 'The parent of this element is null.' · Issue #12 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/12) + /// + [Fact] + public async Task TestIssue12_dynamic() + { + + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + dynamic value = new ExpandoObject(); + value.Company_Name = "MiniSofteware\n"; + value.Name = "Jack"; + value.CreateDate = new DateTime(2021, 01, 01); + value.VIP = true; + value.Points = 123; + value.APP = "Demo APP\n"; + + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"MiniSofteware", xml); + Assert.Contains(@"", xml); + } + + /// + /// [System.InvalidOperationException: 'The parent of this element is null.' · Issue #12 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/12) + /// + [Fact] + public async Task TestIssue12_new() + { + + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + object value = new + { + Company_Name = "MiniSofteware\n", + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123, + APP = "Demo APP\n", + }; + + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"MiniSofteware", xml); + Assert.Contains(@"", xml); + } + + [Fact] + public async Task TestIssueDemo03() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestDemo02.docx"); + var value = new Dictionary() + { + ["FullName"] = "Julian Anderson", + ["Title"] = "IT Manager", + ["Phone"] = "+86 1234567890", + ["Mail"] = "shps95100@gmail.com", + ["Education"] = "Michigan State University | From Aug 2013 to May 2015", + ["Major"] = "Computer Science", + ["Favorites"] = "Music、Programing、Design", + ["Skills"] = new[] { "- Photoshop", "- InDesign", "- MS Office", "- HTML 5", "- CSS 3" }, + ["Address"] = "1234, White Home, Road-12/ABC Street-13, New York, USA, 12345", + ["AboutMe"] = "Hi, I’m Julian Anderson dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled the industry's standard dummy.", + ["Birthday"] = "1993-09-26", + ["Experiences"] = @"# SENIOR UI/UX DEVELOPER & DESIGNER +◼ The Matrix Media Limited | From May 2013 to May 2015 +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took. + +◼ JUNIOR UI/UX DEVELOPER & DESIGNER +Linux OS Interface Limited | From Jan 2010 to Feb 2013 +Lorem Ipsum has been the industry's standard dummy text +ever since the 1500s, when an unknown printer took. + +◼ TEAM LEADER & CORE GRAPHIC DESIGNER +Apple OS Interface Limited | From Jan 2008 to Feb 2010 +Lorem Ipsum has been the industry's standard dummy text +ever since the 1500s, when an unknown printer took. + +◼ JUNIOR UI/UX DEVELOPER & DESIGNER +Apple OS Interface Limited | From Jan 2008 to Feb 2010 +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took. + +◼ JUNIOR UI/UX DEVELOPER & DESIGNER +Apple OS Interface Limited | From Jan 2008 to Feb 2010 +Lorem Ipsum has been the industry's standard dummy text +ever since the 1500s, when an unknown printer took. +", + ["Image"] = new MiniWordPicture() { Path = PathHelper.GetFile("demo01.png"), Width = 160, Height = 90 }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + } + + [Fact] + public async Task TestIssueDemo03_dynamic() + { + + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestDemo02.docx"); + dynamic value = new ExpandoObject(); + value.FullName = "Julian Anderson"; + value.Title = "IT Manager"; + value.Phone = "+86 1234567890"; + value.Mail = "shps95100@gmail.com"; + value.Education = "Michigan State University | From Aug 2013 to May 2015"; + value.Major = "Computer Science"; + value.Favorites = "Music、Programing、Design"; + value.Skills = new[] { "- Photoshop", "- InDesign", "- MS Office", "- HTML 5", "- CSS 3" }; + value.Address = "1234, White Home, Road-12/ABC Street-13, New York, USA, 12345"; + value.AboutMe = "Hi, I’m Julian Anderson dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled the industry's standard dummy."; + value.Birthday = "1993-09-26"; + value.Experiences = @"# SENIOR UI/UX DEVELOPER & DESIGNER + ◼ The Matrix Media Limited | From May 2013 to May 2015 + Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took. + + ◼ JUNIOR UI/UX DEVELOPER & DESIGNER + Linux OS Interface Limited | From Jan 2010 to Feb 2013 + Lorem Ipsum has been the industry's standard dummy text + ever since the 1500s, when an unknown printer took. + + ◼ TEAM LEADER & CORE GRAPHIC DESIGNER + Apple OS Interface Limited | From Jan 2008 to Feb 2010 + Lorem Ipsum has been the industry's standard dummy text + ever since the 1500s, when an unknown printer took. + + ◼ JUNIOR UI/UX DEVELOPER & DESIGNER + Apple OS Interface Limited | From Jan 2008 to Feb 2010 + Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took. + + ◼ JUNIOR UI/UX DEVELOPER & DESIGNER + Apple OS Interface Limited | From Jan 2008 to Feb 2010 + Lorem Ipsum has been the industry's standard dummy text + ever since the 1500s, when an unknown printer took. + "; + value.Image = new MiniWordPicture() { Path = PathHelper.GetFile("demo01.png"), Width = 160, Height = 90 }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + } + + /// + /// [support array list string to generate multiple row · Issue #11 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/11) + /// + [Fact] + public async Task TestIssue11() + { + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue11.docx"); + var value = new Dictionary() + { + ["managers"] = new[] { "Jack", "Alan" }, + ["employees"] = new[] { "Mike", "Henry" }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains("Jack", xml); + } + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue11.docx"); + var value = new Dictionary() + { + ["managers"] = new List { "Jack", "Alan" }, + ["employees"] = new List { "Mike", "Henry" }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains("Jack", xml); + } + } + + /// + /// [support array list string to generate multiple row · Issue #11 · mini-software/MiniWord] + /// (https://github.com/mini-software/MiniWord/issues/11) + /// + [Fact] + public async Task TestIssue11_new() + { + //{ + // var path = PathHelper.GetTempFilePath(); + // var templatePath = PathHelper.GetFile("TestIssue11.docx"); + // var value = new + // { + // managers = new[] { "Jack", "Alan" }, + // employees = new[] { "Mike", "Henry" }, + // }; + // await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + // var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + // Assert.Contains("Jack", xml); + //} + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue11.docx"); + var value = new + { + managers = new List { "Jack", "Alan" }, + employees = new List { "Mike", "Henry" }, + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains("Jack", xml); + } + } + + + /// + /// [Support image · Issue #3 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/3) + /// + [Fact] + public async Task TestIssue3() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicImage.docx"); + var value = new Dictionary() + { + ["Logo"] = new MiniWordPicture() { Path = PathHelper.GetFile("DemoLogo.png"), Width = 180, Height = 180 } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains("", xml); + } + + /// + /// [Fuzzy Regex replace similar key · Issue #5 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/5) + /// + [Fact] + public async Task TestIssue5() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new Dictionary() + { + ["Name"] = new MiniWordHyperLink() + { + Url = "https://google.com", + Text = "測試連結!!" + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //Console.WriteLine(path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.DoesNotContain("Jack Demo APP Account Data", xml); + Assert.Contains("MiniSofteware Demo APP Account Data", xml); + Assert.Contains("MiniSofteware Demo APP Account Data", xml); + Assert.Contains("() + { + ["Name"] = new List(){ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //Console.WriteLine(path); + var docXml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.DoesNotContain("Jack Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains("() + { + ["Name"] = new MiniWordHyperLink[]{ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + + var docXml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.DoesNotContain("Jack Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains("() + { + ["Name"] = new[]{ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + + var docXml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.DoesNotContain("Jack Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains("MiniSofteware Demo APP Account Data", docXml); + Assert.Contains(" + /// [Fuzzy Regex replace similar key · Issue #5 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/5) + /// + [Fact] + public async Task TestIssue5_new() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new + { + Name = new MiniWordHyperLink() + { + Url = "https://google.com", + Text = "測試連結!!" + }, + Company_Name = "MiniSofteware", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123, + APP = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //Console.WriteLine(path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.DoesNotContain("Jack Demo APP Account Data", xml); + Assert.Contains("MiniSofteware Demo APP Account Data", xml); + Assert.Contains(" + /// [Paragraph replace by tag · Issue #4 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/4) + /// + [Fact] + public async Task TestIssue4() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new Dictionary() + { + ["Company_Name"] = "MiniSofteware", + ["Name"] = "Jack", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + + /// + /// [Paragraph replace by tag · Issue #4 · mini-software/MiniWord](https://github.com/mini-software/MiniWord/issues/4) + /// + [Fact] + public async Task TestIssue4_new() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new + { + Company_Name = "MiniSofteware", + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123, + APP = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + [Fact] + public async Task TestColor() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new + { + Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB" }, + Name = new MiniWordColorText { Text = "Jack", HighlightColor = "#eb70AB" }, + CreateDate = new MiniWordColorText + { + Text = new DateTime(2021, 01, 01).ToString() + , + HighlightColor = "#eb70AB", + FontColor = "#ffffff" + }, + VIP = true, + Points = 123, + APP = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + + [Fact] + public async Task TestMultipleColorWordByWord() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestBasicFill.docx"); + var value = new + { + Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", }, + Name = new[] { + new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" }, + new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" } + }, + CreateDate = new MiniWordColorText + { + Text = new DateTime(2021, 01, 01).ToString(), + HighlightColor = "#eb70AB", + FontColor = "#ffffff", + }, + VIP = true, + Points = 123, + APP = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + } + + + #region Model:TestIssue18.docx + public class Foo + { + public string title { get; set; } + public List managers { get; set; } + public List employees { get; set; } + } + public class User + { + public string name { get; set; } + public string department { get; set; } + } + #endregion + } +} diff --git a/tests/MiniWordTests/MiniWordTestAsync.cs b/tests/MiniWordTests/MiniWordTestAsync.cs new file mode 100644 index 0000000..d2dfa9a --- /dev/null +++ b/tests/MiniWordTests/MiniWordTestAsync.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DocumentFormat.OpenXml.Office2010.ExcelAc; +using MiniSoftware; +using Xunit; + +namespace MiniWordTests +{ + public class MiniWordTestAsync + { + [Fact] + public async void TestForeachLoopInTablesAsync() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestForeachInTablesDemo.docx"); + var value = new Dictionary() + { + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-08 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-08 15:00:00") }, + { "How", "Discussion requirement part1" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + }, + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-09 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-09 17:00:00") }, + { "How", "Discussion requirement part2 and development" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + } + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Discussion requirement part2 and development", xml); + Assert.Contains(@"Discussion requirement part1", xml); + Assert.Contains( + "Air way to the Airplane | Parking way to the Car / Hotel way to the Room, Food way to the Plate", xml); + } + + [Fact] + public async void MiniWordIfStatement_FirstIfAsync() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIfStatement.docx"); + var value = new Dictionary() + { + ["Name"] = new List(){ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //Console.WriteLine(path); + var docXml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains("First if chosen: MiniSofteware", docXml); + Assert.DoesNotContain("Second if chosen: MaxiSoftware", docXml); + Assert.Contains("Points are greater than 100", docXml); + Assert.Contains("CreateDate is not less than 2021", docXml); + Assert.DoesNotContain("CreateDate is not greater than 2021", docXml); + } + + [Fact] + public async void TestForeachLoopInTablesWithIfStatementAsync() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestForeachInTablesWithIfStatementDemo.docx"); + var value = new Dictionary() + { + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-08 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-08 15:00:00") }, + { "How", "Discussion requirement part1" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + }, + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-09 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-09 17:00:00") }, + { "How", "Discussion requirement part2 and development" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + } + } + }; + await MiniWord.SaveAsByTemplateAsync(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Discussion requirement part2 and development", xml); + Assert.Contains(@"Discussion requirement part1", xml); + Assert.Contains("Air way to the Airplane | Hotel way to the Room", xml); + } + } +} \ No newline at end of file diff --git a/tests/MiniWordTests/MiniWordTests.csproj b/tests/MiniWordTests/MiniWordTests.csproj index 953e880..6b0cb5b 100644 --- a/tests/MiniWordTests/MiniWordTests.csproj +++ b/tests/MiniWordTests/MiniWordTests.csproj @@ -1,7 +1,7 @@ - net5.0; + net6.0; false From 0d17306e754f2b2e7887a0d0483b1eb93f091fb2 Mon Sep 17 00:00:00 2001 From: danielshih Date: Sat, 23 Mar 2024 17:58:03 +0800 Subject: [PATCH 3/5] fix garbled characters --- src/MiniWord/Extensions/OpenXmlExtension.cs | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/MiniWord/Extensions/OpenXmlExtension.cs b/src/MiniWord/Extensions/OpenXmlExtension.cs index acd6e61..513cde5 100644 --- a/src/MiniWord/Extensions/OpenXmlExtension.cs +++ b/src/MiniWord/Extensions/OpenXmlExtension.cs @@ -1,4 +1,4 @@ -namespace MiniSoftware.Extensions +namespace MiniSoftware.Extensions { using DocumentFormat.OpenXml.Wordprocessing; using System; @@ -12,10 +12,10 @@ namespace MiniSoftware.Extensions internal static class OpenXmlExtension { /// - /// �߼��������õ���������������ַ��� + /// 高级搜索:得到段落里面的连续字符串 /// - /// ���� - /// Item1�������ı���Item2���飻Item3�����ı� + /// 段落 + /// Item1:连续文本;Item2:块;Item3:块文本 internal static List, List>> GetContinuousString(this Paragraph paragraph) { List, List>> tuples = new List, List>>(); @@ -26,13 +26,13 @@ internal static List, List>> GetContinuousString(t var runs = new List(); var texts = new List(); - //���䣺�����Ӽ� + //段落:所有子级 foreach (var pChildElement in paragraph.ChildElements) { - //�� + //块 if (pChildElement is Run run) { - //�ı��� + //文本块 if (run.IsText()) { var text = run.GetFirstChild(); @@ -50,10 +50,10 @@ internal static List, List>> GetContinuousString(t texts = new List(); } } - //��ʽ����ǩ... + //公式,书签... else { - //���������� + //跳过的类型 if (pChildElement is BookmarkStart || pChildElement is BookmarkEnd) { @@ -81,16 +81,16 @@ internal static List, List>> GetContinuousString(t } /// - /// �����ַ����������ַ������� + /// 整理字符串到连续字符串块中 /// - /// �����ַ����� - /// �������ַ��� + /// 连续字符串块 + /// 待整理字符串 internal static void TrimStringToInContinuousString(this IEnumerable texts, string text) { /* - //�����Ϊ��[A][BC][DE][FG][H] - //�����滻��[AB][E][GH] - //�Ż���Ϊ��[AB][C][DE][FGH][] + //假如块为:[A][BC][DE][FG][H] + //假如替换:[AB][E][GH] + //优化块为:[AB][C][DE][FGH][] */ var allTxtx = string.Concat(texts.SelectMany(o => o.Text)); From 3c2f5c1abb079dac3c63a52e1709bac0cf5c2c9b Mon Sep 17 00:00:00 2001 From: danielshih Date: Sat, 23 Mar 2024 17:59:21 +0800 Subject: [PATCH 4/5] revert version to net5.0 --- tests/MiniWordTests/MiniWordTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/MiniWordTests/MiniWordTests.csproj b/tests/MiniWordTests/MiniWordTests.csproj index 6b0cb5b..953e880 100644 --- a/tests/MiniWordTests/MiniWordTests.csproj +++ b/tests/MiniWordTests/MiniWordTests.csproj @@ -1,7 +1,7 @@ - net6.0; + net5.0; false From 649a49c68681a9b3d0b6f0dac1635644abea0840 Mon Sep 17 00:00:00 2001 From: danielshih Date: Sat, 23 Mar 2024 18:00:39 +0800 Subject: [PATCH 5/5] upgrade net6.0 which is LTS version. --- .github/workflows/dotnet.yml | 2 +- tests/MiniWordTests/MiniWordTests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 659168f..31607fa 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/tests/MiniWordTests/MiniWordTests.csproj b/tests/MiniWordTests/MiniWordTests.csproj index 953e880..6b0cb5b 100644 --- a/tests/MiniWordTests/MiniWordTests.csproj +++ b/tests/MiniWordTests/MiniWordTests.csproj @@ -1,7 +1,7 @@ - net5.0; + net6.0; false