diff --git a/sources/OpenMcdf.Extensions/OLEProperties/OLEPropertiesContainer.cs b/sources/OpenMcdf.Extensions/OLEProperties/OLEPropertiesContainer.cs index 0dc62fbc..4c240890 100644 --- a/sources/OpenMcdf.Extensions/OLEProperties/OLEPropertiesContainer.cs +++ b/sources/OpenMcdf.Extensions/OLEProperties/OLEPropertiesContainer.cs @@ -194,6 +194,41 @@ public void RemoveProperty(uint propertyIdentifier) properties.Remove(toRemove); } + /// + /// Create a new UserDefinedProperties container within this container. + /// + /// + /// Only containers of type DocumentSummaryInfo can contain user defined properties. + /// + /// The code page to use for the user defined properties. + /// The UserDefinedProperties container. + /// If this container is a type that doesn't suppose user defined properties. + public OLEPropertiesContainer CreateUserDefinedProperties(int codePage) + { + // Only the DocumentSummaryInfo stream can contain a UserDefinedProperties + if (this.ContainerType != ContainerType.DocumentSummaryInfo) + { + throw new CFInvalidOperation($"Only a DocumentSummaryInfo can contain user defined properties. Current container type is {this.ContainerType}"); + } + + // Create the container, and add the codepage to the initial set of properties + UserDefinedProperties = new OLEPropertiesContainer(codePage, ContainerType.UserDefinedProperties) + { + PropertyNames = new Dictionary() + }; + + var op = new OLEProperty(UserDefinedProperties) + { + VTType = VTPropertyType.VT_I2, + PropertyIdentifier = 1, + Value = (short)codePage + }; + + UserDefinedProperties.properties.Add(op); + this.HasUserDefinedProperties = true; + + return UserDefinedProperties; + } public void Save(CFStream cfStream) { diff --git a/sources/OpenMcdf/CompoundFile.cs b/sources/OpenMcdf/CompoundFile.cs index df2d429d..0266c690 100644 --- a/sources/OpenMcdf/CompoundFile.cs +++ b/sources/OpenMcdf/CompoundFile.cs @@ -27,7 +27,7 @@ public int Compare(CFItem x, CFItem y) } /// - /// Configuration parameters for the compund files. + /// Configuration parameters for the compound files. /// They can be OR-combined to configure /// Compound file behaviour. /// All flags are NOT set by Default. @@ -112,7 +112,7 @@ public enum CFSUpdateMode /// Standard Microsoft© Compound File implementation. /// It is also known as OLE/COM structured storage /// and contains a hierarchy of storage and stream objects providing - /// efficent storage of multiple kinds of documents in a single file. + /// efficient storage of multiple kinds of documents in a single file. /// Version 3 and 4 of specifications are supported. /// public class CompoundFile : IDisposable @@ -1146,7 +1146,7 @@ private void AllocateDIFATSectorChain(List FATsectorChain) nCurrentSectors++; //... so, adding a FAT sector may induce DIFAT sectors to increase by one - // and consequently this may induce ANOTHER FAT sector (TO-THINK: May this condition occure ?) + // and consequently this may induce ANOTHER FAT sector (TO-THINK: May this condition occur ?) if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT < (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ? header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT : @@ -1569,7 +1569,7 @@ internal List GetSectorChain(int secID, SectorType chainType) return GetMiniSectorChain(secID); default: - throw new CFException("Unsupproted chain type"); + throw new CFException("Unsupported chain type"); } } @@ -2668,7 +2668,7 @@ void IDisposable.Dispose() /// /// When called from user code, release all resources, otherwise, in the case runtime called it, - /// only unmanagd resources are released. + /// only unmanaged resources are released. /// /// If true, method has been called from User code, if false it's been called from .net runtime protected virtual void Dispose(bool disposing) @@ -2760,7 +2760,7 @@ private IList FindDirectoryEntries(String entryName) /// /// Get a list of all entries with a given name contained in the document. /// - /// Name of entries to retrive + /// Name of entries to retrieve /// A list of name-matching entries /// This function is aimed to speed up entity lookup in /// flat-structure files (only one or little more known entries) diff --git a/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs b/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs index 30858259..953535d7 100644 --- a/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs +++ b/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs @@ -486,5 +486,58 @@ public void Test_CLSID_PROPERTY() } } + + // The test file 'report.xls' contains a DocumentSummaryInfo section, but no user defined properties. + // This tests adding a new user defined properties section to the existing DocumentSummaryInfo. + [TestMethod] + public void Test_ADD_USER_DEFINED_PROPERTIES_SECTION() + { + if (File.Exists("test_add_user_defined_properties.xls")) + File.Delete("test_add_user_defined_properties.xls"); + + using (CompoundFile cf = new CompoundFile("report.xls")) + { + var dsiStream = cf.RootStorage.GetStream("\u0005DocumentSummaryInformation"); + var co = dsiStream.AsOLEPropertiesContainer(); + + Assert.IsFalse(co.HasUserDefinedProperties); + Assert.IsNull(co.UserDefinedProperties); + + var newUserDefinedProperties = co.CreateUserDefinedProperties(65001); // 65001 - UTF-8 + + newUserDefinedProperties.PropertyNames[2] = "MyCustomProperty"; + + var newProperty = co.NewProperty(VTPropertyType.VT_LPSTR, 2); + newProperty.Value = "Testing"; + newUserDefinedProperties.AddProperty(newProperty); + + co.Save(dsiStream); + cf.SaveAs("test_add_user_defined_properties.xls"); + } + + using (CompoundFile cf = new CompoundFile("test_add_user_defined_properties.xls")) + { + var co = cf.RootStorage.GetStream("\u0005DocumentSummaryInformation").AsOLEPropertiesContainer(); + + // User defined properties should be present now + Assert.IsTrue(co.HasUserDefinedProperties); + Assert.IsNotNull(co.UserDefinedProperties); + Assert.AreEqual(65001, co.UserDefinedProperties.Context.CodePage); + + // And the expected properties should the there + var propArray = co.UserDefinedProperties.Properties.ToArray(); + Assert.AreEqual(propArray.Length, 2); + + // CodePage prop + Assert.AreEqual(1u, propArray[0].PropertyIdentifier); + Assert.AreEqual("0x00000001", propArray[0].PropertyName); + Assert.AreEqual((short)-535, propArray[0].Value); + + // User properties + Assert.AreEqual("MyCustomProperty", propArray[1].PropertyName); + Assert.AreEqual("Testing", propArray[1].Value); + Assert.AreEqual(VTPropertyType.VT_LPSTR, propArray[1].VTType); + } + } } }