Skip to content

Commit

Permalink
Merge pull request #124 from ironfede/pr/fix-121-trailing-chars
Browse files Browse the repository at this point in the history
Fix #121
There is more work to do on this subject but I think that this pr can be a starting point for future best implementations.
  • Loading branch information
ironfede authored Apr 17, 2024
2 parents c58985f + 485e865 commit 591eaea
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 52 deletions.
9 changes: 8 additions & 1 deletion sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace OpenMcdf.Extensions.OLEProperties
Expand Down Expand Up @@ -55,7 +56,13 @@ public void Write(BinaryWriter bw)

private string GetName()
{
return Encoding.GetEncoding(this.codePage).GetString(nameBytes);

var result = Encoding.GetEncoding(this.codePage).GetString(nameBytes);

result = result.Trim('\0');

return result;

}


Expand Down
24 changes: 22 additions & 2 deletions sources/OpenMcdf.Extensions/OLEProperties/OLEProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,30 @@ public VTPropertyType VTType
internal set;
}

object value;

public object Value
{
get;
set;
get
{
switch (VTType)
{
case VTPropertyType.VT_LPSTR:
case VTPropertyType.VT_LPWSTR:
if (value is string str && !String.IsNullOrEmpty(str))
return str.Trim('\0');
break;
default:
return this.value;

}

return this.value;
}
set
{
this.value = value;
}
}

public override bool Equals(object obj)
Expand Down
116 changes: 92 additions & 24 deletions sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,26 +412,86 @@ public override string ReadScalarValue(System.IO.BinaryReader br)
uint size = br.ReadUInt32();
data = br.ReadBytes((int)size);

return Encoding.GetEncoding(codePage).GetString(data);
var result = Encoding.GetEncoding(codePage).GetString(data);
//result = result.Trim(new char[] { '\0' });

//if (this.codePage == CodePages.CP_WINUNICODE)
//{
// result = result.Substring(0, result.Length - 2);
//}
//else
//{
// result = result.Substring(0, result.Length - 1);
//}

return result;
}

public override void WriteScalarValue(BinaryWriter bw, string pValue)
{
data = Encoding.GetEncoding(codePage).GetBytes(pValue);
uint dataLength = (uint)data.Length;
//bool addNullTerminator = true;

if (String.IsNullOrEmpty(pValue)) //|| String.IsNullOrEmpty(pValue.Trim(new char[] { '\0' })))
{
bw.Write((uint)0);
}
else if (this.codePage == CodePages.CP_WINUNICODE)
{

// The string data must be null terminated, so add a null byte if there isn't one already
bool addNullTerminator =
dataLength == 0 || data[dataLength - 1] != '\0';
data = Encoding.GetEncoding(codePage).GetBytes(pValue);

//if (data.Length >= 2 && data[data.Length - 2] == '\0' && data[data.Length - 1] == '\0')
// addNullTerminator = false;

uint dataLength = (uint)data.Length;

//if (addNullTerminator)
dataLength += 2; // null terminator \u+0000

// var mod = dataLength % 4; // pad to multiple of 4 bytes

bw.Write(dataLength); // datalength of string + null char (unicode)
bw.Write(data); // string


//if (addNullTerminator)
//{
bw.Write('\0'); // first byte of null unicode char
bw.Write('\0'); // second byte of null unicode char
//}

//for (int i = 0; i < (4 - mod); i++) // padding
// bw.Write('\0');
}
else
{
data = Encoding.GetEncoding(codePage).GetBytes(pValue);

//if (data.Length >= 1 && data[data.Length - 1] == '\0')
// addNullTerminator = false;

uint dataLength = (uint)data.Length;

//if (addNullTerminator)
dataLength += 1; // null terminator \u+0000

var mod = dataLength % 4; // pad to multiple of 4 bytes

bw.Write(dataLength); // datalength of string + null char (unicode)
bw.Write(data); // string


//if (addNullTerminator)
//{
bw.Write('\0'); // null terminator'\0'
//}

//for (int i = 0; i < (4 - mod); i++) // padding
// bw.Write('\0');
}

if (addNullTerminator)
dataLength += 1;

bw.Write(dataLength);
bw.Write(data);

if (addNullTerminator)
bw.Write((byte)0);
}
}

Expand All @@ -449,8 +509,11 @@ public VT_LPWSTR_Property(VTPropertyType vType, int codePage, bool isVariant) :
public override string ReadScalarValue(System.IO.BinaryReader br)
{
uint nChars = br.ReadUInt32();
data = br.ReadBytes((int)(nChars * 2)); //WChar
return Encoding.Unicode.GetString(data);
data = br.ReadBytes((int)((nChars * 2) - 2)); //WChar- null terminator
var result = Encoding.Unicode.GetString(data);
//result = result.Trim(new char[] { '\0' });

return result;
}

public override void WriteScalarValue(BinaryWriter bw, string pValue)
Expand All @@ -460,20 +523,25 @@ public override void WriteScalarValue(BinaryWriter bw, string pValue)
// The written data length field is the number of characters (not bytes) and must include a null terminator
// add a null terminator if there isn't one already
var byteLength = data.Length;
bool addNullTerminator =
byteLength == 0 || data[byteLength - 1] != '\0' || data[byteLength - 2] != '\0';

if (addNullTerminator)
byteLength += 2;
//bool addNullTerminator =
// byteLength == 0 || data[byteLength - 1] != '\0' || data[byteLength - 2] != '\0';

//if (addNullTerminator)
byteLength += 2;

bw.Write((uint)byteLength / 2);
bw.Write(data);

if (addNullTerminator)
{
bw.Write((byte)0);
bw.Write((byte)0);
}
//if (addNullTerminator)
//{
bw.Write((byte)0);
bw.Write((byte)0);
//}

//var mod = byteLength % 4; // pad to multiple of 4 bytes
//for (int i = 0; i < (4 - mod); i++) // padding
// bw.Write('\0');
}
}

Expand Down Expand Up @@ -645,7 +713,7 @@ public override void WriteScalarValue(BinaryWriter bw, object pValue)
}
}

#endregion
#endregion

}
}
2 changes: 1 addition & 1 deletion sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@
// http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html
// and Compilers should be deterministic: same inputs generate same outputs
// https://stackoverflow.com/questions/43019832/auto-versioning-in-visual-studio-2017-net-core/46985624#46985624
[assembly: AssemblyVersion("2.3.1.0")]
[assembly: AssemblyVersion("2.3.2.0")]
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,15 @@ public void Test_DOCUMENT_SUMMARY_INFO_MODIFY()

// The company property should exist but be empty
var companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY");
Assert.AreEqual("\0\0\0\0", companyProperty.Value);
Assert.AreEqual("", companyProperty.Value);

// As a sanity check, check that the value of a property that we don't change remains the same
var formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT");
Assert.AreEqual("A4 Paper (210x297 mm)\0\0\0", formatProperty.Value);
Assert.AreEqual("A4 Paper (210x297 mm)", formatProperty.Value);

// The manager property shouldn't exist, and we'll add it
Assert.IsFalse(co.Properties.Any(prop => prop.PropertyName == "PIDDSI_MANAGER"));

var managerProp = co.NewProperty(VTPropertyType.VT_LPSTR, 0x0000000E, "PIDDSI_MANAGER");
co.AddProperty(managerProp);

Expand All @@ -159,20 +160,21 @@ public void Test_DOCUMENT_SUMMARY_INFO_MODIFY()

co.Save(dsiStream);
cf.SaveAs(@"test_modify_summary.ppt");
cf.Close() ;
}

using (CompoundFile cf = new CompoundFile("test_modify_summary.ppt"))
{
var co = cf.RootStorage.GetStream("\u0005DocumentSummaryInformation").AsOLEPropertiesContainer();

var companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY");
Assert.AreEqual("My Company\0", companyProperty.Value);
Assert.AreEqual("My Company", companyProperty.Value);

var formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT");
Assert.AreEqual("A4 Paper (210x297 mm)\0\0\0", formatProperty.Value);
Assert.AreEqual("A4 Paper (210x297 mm)", formatProperty.Value);

var managerProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_MANAGER");
Assert.AreEqual("The Boss\0", managerProperty.Value);
Assert.AreEqual("The Boss", managerProperty.Value);
}
}

Expand Down Expand Up @@ -306,11 +308,11 @@ public void Test_SUMMARY_INFO_MODIFY_LPWSTRING()

var authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR");
Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType);
Assert.AreEqual("zkyiqpqoroxnbdwhnjfqroxlgylpbgcwuhjfifpkvycugvuecoputqgknnbs\0", authorProperty.Value);
Assert.AreEqual("zkyiqpqoroxnbdwhnjfqroxlgylpbgcwuhjfifpkvycugvuecoputqgknnbs", authorProperty.Value);

var keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS");
Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType);
Assert.AreEqual("abcdefghijk\0", keyWordsProperty.Value);
Assert.AreEqual("abcdefghijk", keyWordsProperty.Value);

authorProperty.Value = "ABC";
keyWordsProperty.Value = "";
Expand All @@ -325,11 +327,11 @@ public void Test_SUMMARY_INFO_MODIFY_LPWSTRING()

var authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR");
Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType);
Assert.AreEqual("ABC\0", authorProperty.Value);
Assert.AreEqual("ABC", authorProperty.Value);

var keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS");
Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType);
Assert.AreEqual("\0", keyWordsProperty.Value);
Assert.AreEqual("", keyWordsProperty.Value);
}
}

Expand Down Expand Up @@ -359,16 +361,16 @@ public void Test_Read_Unicode_User_Properties_Dictionary()
Assert.AreEqual((short)1200, propArray[0].Value);

// String properties
Assert.AreEqual("A\0", propArray[1].PropertyName);
Assert.AreEqual("\0", propArray[1].Value);
Assert.AreEqual("AB\0", propArray[2].PropertyName);
Assert.AreEqual("X\0", propArray[2].Value);
Assert.AreEqual("ABC\0", propArray[3].PropertyName);
Assert.AreEqual("XY\0", propArray[3].Value);
Assert.AreEqual("ABCD\0", propArray[4].PropertyName);
Assert.AreEqual("XYZ\0", propArray[4].Value);
Assert.AreEqual("ABCDE\0", propArray[5].PropertyName);
Assert.AreEqual("XYZ!\0", propArray[5].Value);
Assert.AreEqual("A", propArray[1].PropertyName);
Assert.AreEqual("", propArray[1].Value);
Assert.AreEqual("AB", propArray[2].PropertyName);
Assert.AreEqual("X", propArray[2].Value);
Assert.AreEqual("ABC", propArray[3].PropertyName);
Assert.AreEqual("XY", propArray[3].Value);
Assert.AreEqual("ABCD", propArray[4].PropertyName);
Assert.AreEqual("XYZ", propArray[4].Value);
Assert.AreEqual("ABCDE", propArray[5].PropertyName);
Assert.AreEqual("XYZ!", propArray[5].Value);
}
}

Expand Down Expand Up @@ -426,16 +428,16 @@ public void Test_DOCUMENT_SUMMARY_INFO_ADD_CUSTOM()
Assert.AreEqual((short)-535, propArray[0].Value);

// User properties
Assert.AreEqual("StringProperty\0", propArray[1].PropertyName);
Assert.AreEqual("Hello\0", propArray[1].Value);
Assert.AreEqual("StringProperty", propArray[1].PropertyName);
Assert.AreEqual("Hello", propArray[1].Value);
Assert.AreEqual(VTPropertyType.VT_LPSTR, propArray[1].VTType);
Assert.AreEqual("BooleanProperty\0", propArray[2].PropertyName);
Assert.AreEqual("BooleanProperty", propArray[2].PropertyName);
Assert.AreEqual(true, propArray[2].Value);
Assert.AreEqual(VTPropertyType.VT_BOOL, propArray[2].VTType);
Assert.AreEqual("IntegerProperty\0", propArray[3].PropertyName);
Assert.AreEqual("IntegerProperty", propArray[3].PropertyName);
Assert.AreEqual(3456, propArray[3].Value);
Assert.AreEqual(VTPropertyType.VT_I4, propArray[3].VTType);
Assert.AreEqual("DateProperty\0", propArray[4].PropertyName);
Assert.AreEqual("DateProperty", propArray[4].PropertyName);
Assert.AreEqual(testNow, propArray[4].Value);
Assert.AreEqual(VTPropertyType.VT_FILETIME, propArray[4].VTType);
}
Expand Down

0 comments on commit 591eaea

Please sign in to comment.