diff --git a/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs b/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs index 251c81b..d033ce6 100644 --- a/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs +++ b/Excel.TemplateEngine.Tests/ObjectPrintingTests/ExcelParsingTests.cs @@ -8,6 +8,7 @@ using SkbKontur.Excel.TemplateEngine.Exceptions; using SkbKontur.Excel.TemplateEngine.FileGenerating; +using SkbKontur.Excel.TemplateEngine.FileGenerating.Primitives; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.ExcelDocumentPrimitives.Implementations; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.LazyParse; using SkbKontur.Excel.TemplateEngine.ObjectPrinting.NavigationPrimitives.Implementations; @@ -291,6 +292,51 @@ public void TestImportAfterCreate(string extension) model.Type.Should().Be("Значение 2"); } + [TestCase("TemplateVersion", "1.9.text.$$%")] + [TestCase("another custom property", "another value")] + [TestCase("non-existent key", null)] + [TestCase(null, null)] + [TestCase("", null)] + public void TestGetCustomProperty(string key, string value) + { + var template = File.ReadAllBytes(GetFilePath("customProperties.xlsx")); + using (var templateModel = ExcelDocumentFactory.CreateFromTemplate(template, logger)) + { + CheckCustomProperty(templateModel, key, value); + var printedDocument = templateModel.CloseAndGetDocumentBytes(); + using (var printedDocumentModel = ExcelDocumentFactory.CreateFromTemplate(printedDocument, logger)) + { + CheckCustomProperty(printedDocumentModel, key, value); + } + } + } + + [TestCase("customProperties.xlsx")] + [TestCase("empty.xlsx", Description = "template file without custom properties")] + public void TestSetCustomProperty(string fileName) + { + var addedKey = "NewKey"; + var addedValue = "CertainValue"; + var templateBytes = File.ReadAllBytes(GetFilePath(fileName)); + using (var template = ExcelDocumentFactory.CreateFromTemplate(templateBytes, logger)) + { + CheckCustomProperty(template, addedKey, null); + template.SetCustomProperty(addedKey, addedValue); + CheckCustomProperty(template, addedKey, addedValue); + var printedDocumentBytes = template.CloseAndGetDocumentBytes(); + using (var printedDocument = ExcelDocumentFactory.CreateFromTemplate(printedDocumentBytes, logger)) + { + CheckCustomProperty(printedDocument, addedKey, addedValue); + } + } + } + + private void CheckCustomProperty(IExcelDocument documentModel, string key, string value) + { + documentModel.TryGetCustomProperty(key, out var printedVersion).Should().Be(value != null); + printedVersion.Should().Be(value); + } + private (TModel model, Dictionary mappingForErrors) Parse(string templateFileName, string targetFileName) where TModel : new() { diff --git a/Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/customProperties.xlsx b/Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/customProperties.xlsx new file mode 100644 index 0000000..7cf1f6c Binary files /dev/null and b/Excel.TemplateEngine.Tests/ObjectPrintingTests/Files/customProperties.xlsx differ diff --git a/Excel.TemplateEngine/FileGenerating/Helpers/CustomFilePropertiesPartHelper.cs b/Excel.TemplateEngine/FileGenerating/Helpers/CustomFilePropertiesPartHelper.cs new file mode 100644 index 0000000..e61b0c8 --- /dev/null +++ b/Excel.TemplateEngine/FileGenerating/Helpers/CustomFilePropertiesPartHelper.cs @@ -0,0 +1,18 @@ +using System.Linq; + +using DocumentFormat.OpenXml.CustomProperties; +using DocumentFormat.OpenXml.Packaging; + +using JetBrains.Annotations; + +namespace SkbKontur.Excel.TemplateEngine.FileGenerating.Helpers; + +public static class CustomFilePropertiesPartHelper +{ + [CanBeNull] + public static CustomDocumentProperty GetProperty(this CustomFilePropertiesPart customFilePropertiesPart, string key) + { + return customFilePropertiesPart.Properties?.OfType() + .FirstOrDefault(i => i?.Name?.Value == key); + } +} \ No newline at end of file diff --git a/Excel.TemplateEngine/FileGenerating/Primitives/IExcelDocument.cs b/Excel.TemplateEngine/FileGenerating/Primitives/IExcelDocument.cs index ecd7514..b06e11c 100644 --- a/Excel.TemplateEngine/FileGenerating/Primitives/IExcelDocument.cs +++ b/Excel.TemplateEngine/FileGenerating/Primitives/IExcelDocument.cs @@ -34,5 +34,10 @@ public interface IExcelDocument : IDisposable void AddDescription([NotNull] string text); void CopyVbaInfoFrom([NotNull] IExcelDocument excelDocument); + + [ContractAnnotation("=> true, value:notnull; => false, value:null")] + bool TryGetCustomProperty([NotNull] string key, out string value); + + void SetCustomProperty([NotNull] string key, string value); } } \ No newline at end of file diff --git a/Excel.TemplateEngine/FileGenerating/Primitives/Implementations/ExcelDocument.cs b/Excel.TemplateEngine/FileGenerating/Primitives/Implementations/ExcelDocument.cs index a008bd1..3906f15 100644 --- a/Excel.TemplateEngine/FileGenerating/Primitives/Implementations/ExcelDocument.cs +++ b/Excel.TemplateEngine/FileGenerating/Primitives/Implementations/ExcelDocument.cs @@ -5,9 +5,11 @@ using System.Text; using System.Xml; +using DocumentFormat.OpenXml.CustomProperties; using DocumentFormat.OpenXml.Drawing; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.VariantTypes; using JetBrains.Annotations; @@ -168,6 +170,39 @@ public void RenameWorksheet(int index, [NotNull] string name) spreadsheetDocument.WorkbookPart.Workbook.Sheets.Elements().ElementAt(index).Name = name; } + public bool TryGetCustomProperty(string key, out string value) + { + ThrowIfSpreadsheetDisposed(); + var property = spreadsheetDocument.CustomFilePropertiesPart?.GetProperty(key); + value = property?.InnerText; + return value != null; + } + + public void SetCustomProperty(string key, string value) + { + // https://learn.microsoft.com/en-us/office/open-xml/how-to-set-a-custom-property-in-a-word-processing-document + const string customPropertyFormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"; + var customProps = spreadsheetDocument.CustomFilePropertiesPart + ?? spreadsheetDocument.AddCustomFilePropertiesPart(); + // ReSharper disable once ConstantNullCoalescingCondition + customProps.Properties ??= new Properties(); + + var existentProperty = customProps.GetProperty(key); + existentProperty?.Remove(); + customProps.Properties.AppendChild(new CustomDocumentProperty + { + VTLPWSTR = new VTLPWSTR(value), + FormatId = customPropertyFormatId, + Name = key + }); + var pid = 2; + foreach (var item in customProps.Properties.OfType()) + { + item.PropertyId = pid++; + } + customProps.Properties.Save(); + } + [NotNull] public IExcelWorksheet AddWorksheet([NotNull] string worksheetName) { diff --git a/version.json b/version.json index 1ebd4c3..abe9271 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.2", + "version": "1.3", "assemblyVersion": { "precision": "build" },