Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework the handling of FMTID0 in OlePropertiesContainer #220

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions OpenMcdf.Ole.Tests/OlePropertiesExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,15 @@ public void TestRetainDictionaryPropertyInAppSpecificStreams()
[4] = "Project Name"
};

Guid expectedFmtid0 = Guid.Parse("f0d6d0b1-a0d8-11ce-8aa2-08003601e988");

using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
{
using CfbStream testStream = cf.OpenStream("Issue134");
OlePropertiesContainer co = new(testStream);

Assert.AreEqual(ContainerType.AppSpecific, co.ContainerType);
Assert.AreEqual(expectedFmtid0, co.FMTID0);
CollectionAssert.AreEqual(expectedPropertyNames, co.PropertyNames);

// Write test file
Expand All @@ -527,6 +531,8 @@ public void TestRetainDictionaryPropertyInAppSpecificStreams()
using CfbStream testStream = cf.OpenStream("Issue134");
OlePropertiesContainer co = new(testStream);

Assert.AreEqual(ContainerType.AppSpecific, co.ContainerType);
Assert.AreEqual(expectedFmtid0, co.FMTID0);
CollectionAssert.AreEqual(expectedPropertyNames, co.PropertyNames);
}
}
Expand Down
59 changes: 48 additions & 11 deletions OpenMcdf.Ole/OlePropertiesContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,26 @@ public class OlePropertiesContainer

public OlePropertiesContainer? UserDefinedProperties { get; private set; }

/// <summary>
/// Gets the type of the container.
/// </summary>
public ContainerType ContainerType { get; }
private Guid? FmtID0 { get; }

/// <summary>
/// Gets the FMTID of the properties container.
/// </summary>
public Guid FMTID0 { get; }

public PropertyContext Context { get; }

private readonly List<OleProperty> properties = new();
internal Stream? cfStream;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thought when looking at this - is there actually a need to keep the stream as a member?


/// <summary>
/// Create a new instance of <see cref="OlePropertiesContainer"/> with the specified code page and container type.
/// </summary>
/// <param name="codePage">The code page to use for the new container.</param>
/// <param name="containerType">The type of the new container.</param>
public OlePropertiesContainer(int codePage, ContainerType containerType)
{
Context = new PropertyContext
Expand All @@ -36,6 +48,7 @@ public OlePropertiesContainer(int codePage, ContainerType containerType)
};

ContainerType = containerType;
FMTID0 = FmtIdFromContainerType(containerType);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a previous suggestion to add a constructor that allows users to specify the fmtid to use rather than inferring it such that values the library doesn't know about can be used, but I don't need tht ability myself so I never spent any time on it.
Could be done later if useful though

}

public OlePropertiesContainer(CfbStream cfStream)
Expand All @@ -47,13 +60,8 @@ public OlePropertiesContainer(CfbStream cfStream)
using BinaryReader reader = new(cfStream, Encoding.Unicode, true);
pStream.Read(reader);

if (pStream.FMTID0 == FormatIdentifiers.SummaryInformation)
ContainerType = ContainerType.SummaryInfo;
else if (pStream.FMTID0 == FormatIdentifiers.DocSummaryInformation)
ContainerType = ContainerType.DocumentSummaryInfo;
else
ContainerType = ContainerType.AppSpecific;
FmtID0 = pStream.FMTID0;
FMTID0 = pStream.FMTID0;
ContainerType = ContainerTypeFromFmtId(pStream.FMTID0);

PropertyNames = (Dictionary<uint, string>?)pStream.PropertySet0!.Properties
.FirstOrDefault(p => p.PropertyType == PropertyType.DictionaryProperty)?.Value;
Expand Down Expand Up @@ -217,8 +225,6 @@ public void Save(Stream cfStream)
{
using BinaryWriter bw = new(cfStream);

Guid fmtId0 = FmtID0 ?? (ContainerType == ContainerType.SummaryInfo ? FormatIdentifiers.SummaryInformation : FormatIdentifiers.DocSummaryInformation);

PropertySetStream ps = new()
{
ByteOrder = 0xFFFE,
Expand All @@ -228,7 +234,7 @@ public void Save(Stream cfStream)

NumPropertySets = 1,

FMTID0 = fmtId0,
FMTID0 = this.FMTID0,
Offset0 = 0,

FMTID1 = Guid.Empty,
Expand Down Expand Up @@ -284,4 +290,35 @@ public void Save(Stream cfStream)

ps.Write(bw);
}

// Determine the type of the container from the FMTID0 property.
private static ContainerType ContainerTypeFromFmtId(Guid fmtId0)
{
if (fmtId0 == FormatIdentifiers.SummaryInformation)
return ContainerType.SummaryInfo;
else if (fmtId0 == FormatIdentifiers.DocSummaryInformation)
return ContainerType.DocumentSummaryInfo;
else if (fmtId0 == FormatIdentifiers.GlobalInfo)
return ContainerType.GlobalInfo;
else if (fmtId0 == FormatIdentifiers.ImageInfo)
return ContainerType.ImageInfo;
else if (fmtId0 == FormatIdentifiers.ImageContents)
return ContainerType.ImageContents;

return ContainerType.AppSpecific;
}

// Determine the FMTID property from the container type.
// Note: Uses FMTID_DocSummaryInformation by default to match the previous behavior.
private static Guid FmtIdFromContainerType(ContainerType containerType)
{
return containerType switch
{
ContainerType.SummaryInfo => FormatIdentifiers.SummaryInformation,
ContainerType.GlobalInfo => FormatIdentifiers.GlobalInfo,
ContainerType.ImageContents => FormatIdentifiers.ImageContents,
ContainerType.ImageInfo => FormatIdentifiers.ImageInfo,
_ => FormatIdentifiers.DocSummaryInformation,
};
}
}