Skip to content
Open
29 changes: 29 additions & 0 deletions src/ACadSharp.Tests/IO/DXF/DxfWriterSingleObjectTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using ACadSharp.Entities;
using ACadSharp.IO;
using ACadSharp.Objects;
using CSMath;
using System;
using System.IO;
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -54,6 +56,33 @@ public void WriteCasesAC1032(SingleCaseGenerator data)
this.writeDxfFile(data, ACadVersion.AC1032);
}

[Fact]
public void WriteFieldDateValue()
{
DateTime date = new DateTime(2026, 9, 30, 10, 15, 30, 250);

SingleCaseGenerator data = new SingleCaseGenerator();
data.FieldCadValues();

MText text = Assert.Single(data.Document.Entities.OfType<MText>());
Field field = text.XDictionary.GetEntry<CadDictionary>("ACAD_FIELD").GetEntry<Field>("TEXT");
CadValue dateValue = new CadValue();
dateValue.SetValue(date, CadValueType.Date);
field.Values.Add("date", dateValue);

using MemoryStream output = new MemoryStream();
DxfWriter.Write(output, data.Document);

using MemoryStream input = new MemoryStream(output.ToArray());
CadDocument result = DxfReader.Read(input);
Field writtenField = result.GetCadObject<Field>(field.Handle);

Assert.NotNull(writtenField);
Assert.True(writtenField.Values.TryGetValue("date", out CadValue value));
Assert.Equal(CadValueType.Date, value.ValueType);
Assert.Equal(date, value.Value);
}

[Theory]
[InlineData(-90, 270, -90, 270)]
[InlineData(180, 540, -180, 180)]
Expand Down
63 changes: 63 additions & 0 deletions src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ static WriterSingleObjectTests()
Data.Add(new(nameof(SingleCaseGenerator.SingleMeshWithTextureCoordinates)));
Data.Add(new(nameof(SingleCaseGenerator.SingleMaterial)));
Data.Add(new(nameof(SingleCaseGenerator.LeaderWithArrowHead)));
Data.Add(new(nameof(SingleCaseGenerator.FieldCadValues)));
}

public WriterSingleObjectTests(ITestOutputHelper output) : base(output)
Expand Down Expand Up @@ -1699,6 +1700,68 @@ public void EntityTransparency()
};
}

public void FieldCadValues()
{
MText text = new MText();
text.Value = "cad values";
this.Document.Entities.Add(text);

Field field = new Field();
field.EvaluatorId = "_text";
field.FieldCode = "cad values";
field.FormatString = string.Empty;
field.Value.SetValue("cad values", CadValueType.String);

CadValue unknownEmpty = new CadValue();
unknownEmpty.IsEmpty = true;
field.Values.Add("unknown_empty", unknownEmpty);

//Unknown value that is not empty, as read from a pre R2007 dwg
CadValue unknown = new CadValue();
unknown.SetValue(0);
field.Values.Add("unknown", unknown);

CadValue longValue = new CadValue();
longValue.SetValue(42, CadValueType.Long);
field.Values.Add("long", longValue);

CadValue doubleValue = new CadValue();
doubleValue.SetValue(0.5, CadValueType.Double);
field.Values.Add("double", doubleValue);

CadValue stringValue = new CadValue();
stringValue.SetValue("text value", CadValueType.String);
field.Values.Add("string", stringValue);

CadValue pointValue = new CadValue();
pointValue.SetValue(new XYZ(1, 2, 3), CadValueType.Point3D);
field.Values.Add("point", pointValue);

CadDictionary dict = text.CreateExtendedDictionary();
CadDictionary fields = new CadDictionary("ACAD_FIELD");
dict.Add(fields);
fields.Add("TEXT", field);

this.AssertRoundtrip = (doc) =>
{
if (this.Format == CadFileFormat.DWG)
{
return;
}

Field result = doc.GetCadObject<Field>(field.Handle);

Assert.NotNull(result);
foreach (var item in field.Values)
{
Assert.True(result.Values.TryGetValue(item.Key, out CadValue value), $"Value {item.Key} not found");
Assert.Equal(item.Value.ValueType, value.ValueType);
Assert.Equal(item.Value.IsEmpty, value.IsEmpty);
Assert.Equal(item.Value.Value, value.Value);
}
};
}

public void GenerateExampleDxf()
{
this.Document.Header.UnitMode = (short)ACadSharp.Types.Units.UnitsType.Millimeters;
Expand Down
44 changes: 43 additions & 1 deletion src/ACadSharp/CadUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using CSUtilities.Text;
using CSUtilities.Converters;
using CSUtilities.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

Expand Down Expand Up @@ -364,4 +366,44 @@ public static void DateToJulian(DateTime date, out int jdate, out int milisecond
jdate = date.Day + (int)System.Math.Floor((153.0 * (double)(month + 12 * day - 3) + 2.0) / 5.0) + 365 * year + (int)System.Math.Floor((double)year / 4.0) - (int)System.Math.Floor((double)year / 100.0) + (int)System.Math.Floor((double)year / 400.0) - 32045;
miliseconds = date.Millisecond + date.Second * 1000 + date.Minute * 60000 + date.Hour * 3600000;
}

/// <summary>
/// Converts a date into the 16 bytes of a SYSTEMTIME structure: year, month, day of the week, day, hour, minute, second and millisecond, each one as a little endian short.
/// </summary>
/// <param name="date">The date to convert.</param>
/// <returns>The 16 bytes that represent the date.</returns>
public static byte[] ToSystemTime(DateTime date)
{
byte[] array = new byte[16];
var stream = new MemoryStream(array);

stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Year), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Month), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.DayOfWeek), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Day), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Hour), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Minute), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Second), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Millisecond), 0, 2);

return array;
}

/// <summary>
/// Converts the 16 bytes of a SYSTEMTIME structure into a date. The day of the week is ignored.
/// </summary>
/// <param name="array">The 16 bytes that represent the date.</param>
/// <returns>The date represented by the bytes.</returns>
public static DateTime FromSystemTime(byte[] array)
{
int year = LittleEndianConverter.Instance.ToInt16(array, 0);
int month = LittleEndianConverter.Instance.ToInt16(array, 2);
int day = LittleEndianConverter.Instance.ToInt16(array, 6);
int hour = LittleEndianConverter.Instance.ToInt16(array, 8);
int minute = LittleEndianConverter.Instance.ToInt16(array, 10);
int second = LittleEndianConverter.Instance.ToInt16(array, 12);
int millisecond = LittleEndianConverter.Instance.ToInt16(array, 14);

return new DateTime(year, month, day, hour, minute, second, millisecond);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,17 +192,7 @@ private void readCustomTableData(CustomDataEntry entry)
switch (size)
{
case 16:
{
int year = LittleEndianConverter.Instance.ToInt16(array, 0);
int month = LittleEndianConverter.Instance.ToInt16(array, 2);
int day = LittleEndianConverter.Instance.ToInt16(array, 6);
int hour = LittleEndianConverter.Instance.ToInt16(array, 8);
int minute = LittleEndianConverter.Instance.ToInt16(array, 10);
int second = LittleEndianConverter.Instance.ToInt16(array, 12);
int millisecond = LittleEndianConverter.Instance.ToInt16(array, 14);

return new System.DateTime(year, month, day, hour, minute, second, millisecond);
}
return CadUtils.FromSystemTime(array);
case 14:
{
int year = LittleEndianConverter.Instance.ToInt16(array, 0);
Expand Down
12 changes: 1 addition & 11 deletions src/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Objects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -829,17 +829,7 @@ private void writeDateCadValue(System.DateTime? date)
byte[] array;
if (this.R2007Plus)
{
array = new byte[16];
var stream = new MemoryStream(array);

stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Year), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Month), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.DayOfWeek), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Day), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Hour), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Minute), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Second), 0, 2);
stream.Write(LittleEndianConverter.Instance.GetBytes((short)date.Value.Millisecond), 0, 2);
array = CadUtils.ToSystemTime(date.Value);
}
else
{
Expand Down
37 changes: 36 additions & 1 deletion src/ACadSharp/IO/DXF/DxfStreamReader/DxfSectionReaderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,18 @@ private bool readTableEntity(CadEntityTemplate template, DxfMap map, string subc
tmp.CurrentCell.Rotation = this._reader.ValueAsDouble;
return true;
case 170:
//Has data flag
// Per-cell alignment (set when the cell flag at code 91 has the
// CellAlignment override bit). Capture it on the cell-level
// StyleOverride so callers reading the table back via DXF can
// resolve alignment exactly like they do through DWG.
if (tmp.CurrentCell != null)
{
tmp.CurrentCell.StyleOverride.HasData = true;
tmp.CurrentCell.StyleOverride.CellAlignment =
(ACadSharp.Objects.TableStyle.CellAlignmentType)this._reader.ValueAsShort;
tmp.CurrentCell.StyleOverride.PropertyOverrideFlags |=
ACadSharp.Objects.TableStyle.CellStylePropertyFlags.Alignment;
}
return true;
case 171:
tmp.CreateCell((TableEntity.CellType)this._reader.ValueAsInt);
Expand All @@ -572,6 +583,18 @@ private bool readTableEntity(CadEntityTemplate template, DxfMap map, string subc
case 179:
//Unknown value
return true;
case 70:
// Table-level flow direction (0 = top to bottom, 1 = bottom to top).
// Stored on the table-level CellStyleOverride so callers can read it
// through the same API used for round-tripping the rest of the
// override flags.
if (this._reader.ValueAsShort == 1)
{
table.CellStyleOverride.HasData = true;
table.CellStyleOverride.TableCellStylePropertyFlags |=
ACadSharp.Objects.TableStyle.CellStylePropertyFlags.FlowDirectionBottomToTop;
}
return true;
case 301:
content = new TableEntity.CellContent();
var contentTemplate = new CadTableCellContentTemplate(content);
Expand Down Expand Up @@ -634,12 +657,24 @@ protected CadValueTemplate readCadValue(CadValue value)
case 91:
value.SetValue(this._reader.ValueAsInt);
break;
case 92:
//Size of the date data in 310
break;
case 93:
value.Flags = this._reader.ValueAsInt;
break;
case 140:
value.SetValue(this._reader.ValueAsDouble);
break;
case 310 when value.ValueType == CadValueType.Date:
{
byte[] array = this._reader.ValueAsBinaryChunk;
if (array.Length == 16)
{
value.SetValue(CadUtils.FromSystemTime(array));
}
}
break;
case 330:
template.ValueHandle = this._reader.ValueAsHandle;
break;
Expand Down
Loading
Loading