Skip to content

Commit

Permalink
Merge pull request #517 from DomCR/issue-503_dwg-xdata
Browse files Browse the repository at this point in the history
Issue 503 dwg xdata
  • Loading branch information
DomCR authored Dec 30, 2024
2 parents 14190ff + fdccb37 commit f0cbe70
Show file tree
Hide file tree
Showing 27 changed files with 510 additions and 77 deletions.
26 changes: 26 additions & 0 deletions src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using ACadSharp.Entities;
using ACadSharp.Objects;
using ACadSharp.Tables;
using ACadSharp.XData;
using CSMath;
using CSUtilities.Extensions;
using System;
Expand Down Expand Up @@ -468,6 +469,30 @@ public void AddBlockWithAttributes()
this.Document.Entities.Add(insert);
}

public void XData()
{
AppId app = new AppId("my_app");
Layer layer = new Layer("my_layer");
this.Document.AppIds.Add(app);
this.Document.Layers.Add(layer);

Line line = new Line(XYZ.Zero, new XYZ(100, 100, 0));

List<ExtendedDataRecord> records = new();
records.Add(new ExtendedDataControlString(false));
records.Add(new ExtendedDataInteger16(5));
records.Add(new ExtendedDataInteger32(33));
records.Add(new ExtendedDataString("my extended data string"));
records.Add(new ExtendedDataHandle(5));
records.Add(new ExtendedDataLayer(layer.Handle));
records.Add(new ExtendedDataBinaryChunk(new byte[] { 1, 2, 3, 4 }));
records.Add(new ExtendedDataControlString(true));

line.ExtendedData.Add(app, records);

this.Document.Entities.Add(line);
}

public void Deserialize(IXunitSerializationInfo info)
{
this.Name = info.GetValue<string>(nameof(this.Name));
Expand Down Expand Up @@ -525,6 +550,7 @@ static WriterSingleObjectTests()
Data.Add(new(nameof(SingleCaseGenerator.AddCustomScale)));
Data.Add(new(nameof(SingleCaseGenerator.AddCustomBookColor)));
Data.Add(new(nameof(SingleCaseGenerator.Dimensions)));
Data.Add(new(nameof(SingleCaseGenerator.XData)));
}

protected string getPath(string name, string ext, ACadVersion version)
Expand Down
8 changes: 6 additions & 2 deletions src/ACadSharp/CadObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using ACadSharp.Objects.Collections;
using ACadSharp.Tables;
using ACadSharp.Tables.Collections;
using ACadSharp.XData;
using System;
using System.Collections.Generic;

Expand Down Expand Up @@ -71,7 +72,7 @@ internal set
/// <summary>
/// Extended data attached to this object
/// </summary>
public ExtendedDataDictionary ExtendedData { get; } = new ExtendedDataDictionary();
public ExtendedDataDictionary ExtendedData { get; }

/// <summary>
/// Document where this element belongs
Expand All @@ -87,7 +88,10 @@ public CadDocument Document
/// <summary>
/// Default constructor.
/// </summary>
public CadObject() { }
public CadObject()
{
this.ExtendedData = new ExtendedDataDictionary(this);
}

/// <summary>
/// Creates the extended dictionary if null.
Expand Down
76 changes: 49 additions & 27 deletions src/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
using static ACadSharp.Objects.MultiLeaderAnnotContext;
using CSUtilities.Converters;
using CSUtilities.Extensions;
using static ACadSharp.Entities.TableEntity;
using static ACadSharp.Entities.TableEntity.BreakData;
using static ACadSharp.Objects.MultiLeaderAnnotContext;
using System.Globalization;
using ACadSharp.XData;

namespace ACadSharp.IO.DWG
{
Expand Down Expand Up @@ -545,17 +543,17 @@ private void readExtendedData(CadTemplate template)
long endPos = this._objectReader.Position + size;

//template.ExtendedData
ExtendedData edata = this.readExtendedDataRecords(endPos);
List<ExtendedDataRecord> edata = this.readExtendedDataRecords(endPos);

template.EDataTemplate.Add(appHandle, edata);

size = this._objectReader.ReadBitShort();
}
}

private ExtendedData readExtendedDataRecords(long endPos)
private List<ExtendedDataRecord> readExtendedDataRecords(long endPos)
{
ExtendedData data = new ExtendedData();
List<ExtendedDataRecord> records = new List<ExtendedDataRecord>();

while (this._objectReader.Position < endPos)
{
Expand All @@ -567,18 +565,18 @@ private ExtendedData readExtendedDataRecords(long endPos)
switch (dxfCode)
{
//0 (1000) String.
case DxfCode.ExtendedDataAsciiString:
//R13-R2004: 1st byte of value is the length N; this is followed by a 2-byte short indicating the codepage, followed by N single-byte characters.
//R2007 +: 2 - byte length N, followed by N Unicode characters(2 bytes each).
case DxfCode.ExtendedDataAsciiString:
case DxfCode.ExtendedDataRegAppName:
//1 (1001) This one seems to be invalid; can't even use as a string inside braces.
//This would be a registered application that this data relates to, but we've already had that above,
//so it would be redundant or irrelevant here.
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadTextUnicode());
record = new ExtendedDataString(this._objectReader.ReadTextUnicode());
break;
case DxfCode.ExtendedDataControlString:
//2 (1002) A '{' or '}'; 1 byte; ASCII 0 means '{', ASCII 1 means '}'
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadByte());
record = new ExtendedDataControlString(this._objectReader.ReadByte() == 1);
break;
case DxfCode.ExtendedDataLayerName:
//3 (1003) A layer table reference. The value is the handle of the layer;
Expand All @@ -587,11 +585,11 @@ private ExtendedData readExtendedDataRecords(long endPos)
//Even layer 0 is referred to by handle here.
byte[] arr = this._objectReader.ReadBytes(8);
ulong handle = BigEndianConverter.Instance.ToUInt64(arr);
record = new ExtendedDataRecord(dxfCode, handle);
record = new ExtendedDataLayer(handle);
break;
case DxfCode.ExtendedDataBinaryChunk:
//4 (1004) Binary chunk. The first byte of the value is a char giving the length; the bytes follow.
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadBytes(this._objectReader.ReadByte()));
record = new ExtendedDataBinaryChunk(this._objectReader.ReadBytes(this._objectReader.ReadByte()));
break;
case DxfCode.ExtendedDataHandle:
//5 (1005) An entity handle reference.
Expand All @@ -600,22 +598,42 @@ private ExtendedData readExtendedDataRecords(long endPos)
//(There's no length specifier this time.)
arr = this._objectReader.ReadBytes(8);
handle = BigEndianConverter.Instance.ToUInt64(arr);
record = new ExtendedDataRecord(dxfCode, handle);
record = new ExtendedDataHandle(handle);
break;
//10 - 13 (1010 - 1013)
case DxfCode.ExtendedDataXCoordinate:
//Points; 24 bytes(XYZ)-- 3 doubles
record = new ExtendedDataCoordinate(
new XYZ(
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble()
)
);
break;
case DxfCode.ExtendedDataWorldXCoordinate:
case DxfCode.ExtendedDataWorldYCoordinate:
case DxfCode.ExtendedDataWorldZCoordinate:
//Points; 24 bytes(XYZ)-- 3 doubles
record = new ExtendedDataWorldCoordinate(
new XYZ(
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble()
)
);
break;
case DxfCode.ExtendedDataWorldXDisp:
case DxfCode.ExtendedDataWorldYDisp:
case DxfCode.ExtendedDataWorldZDisp:
//Points; 24 bytes(XYZ)-- 3 doubles
record = new ExtendedDataDisplacement(
new XYZ(
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble()
)
);
break;
case DxfCode.ExtendedDataWorldXDir:
case DxfCode.ExtendedDataWorldYDir:
case DxfCode.ExtendedDataWorldZDir:
//Points; 24 bytes(XYZ)-- 3 doubles
record = new ExtendedDataRecord(
dxfCode,
record = new ExtendedDataDirection(
new XYZ(
this._objectReader.ReadDouble(),
this._objectReader.ReadDouble(),
Expand All @@ -624,30 +642,34 @@ private ExtendedData readExtendedDataRecords(long endPos)
);
break;
//40 - 42 (1040 - 1042)
//Reals; 8 bytes(double)
case DxfCode.ExtendedDataReal:
record = new ExtendedDataReal(this._objectReader.ReadDouble());
break;
case DxfCode.ExtendedDataDist:
record = new ExtendedDataDistance(this._objectReader.ReadDouble());
break;
case DxfCode.ExtendedDataScale:
//Reals; 8 bytes(double)
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadDouble());
record = new ExtendedDataScale(this._objectReader.ReadDouble());
break;
//70(1070) A short int; 2 bytes
case DxfCode.ExtendedDataInteger16:
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadShort());
record = new ExtendedDataInteger16(this._objectReader.ReadShort());
break;
//71(1071) A long int; 4 bytes
case DxfCode.ExtendedDataInteger32:
record = new ExtendedDataRecord(dxfCode, this._objectReader.ReadRawLong());
record = new ExtendedDataInteger32((int)this._objectReader.ReadRawLong());
break;
default:
this._objectReader.ReadBytes((int)(endPos - this._objectReader.Position));
this._builder.Notify($"Unknown code for extended data: {dxfCode}", NotificationType.Warning);
return data;
return records;
}

data.Data.Add(record);
records.Add(record);
}

return data;
return records;
}

// Add the reactors to the template.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public void WriteShiftValue()

public void WriteBytes(byte[] bytes, int offset, int length)
{
throw new NotImplementedException();
this.Main.WriteBytes(bytes, offset, length);
}

public void WriteEnColor(Color color, Transparency transparency, bool isBookColor)
Expand Down
92 changes: 92 additions & 0 deletions src/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Common.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using ACadSharp.Classes;
using ACadSharp.Entities;
using ACadSharp.Tables;
using ACadSharp.XData;
using CSUtilities.Converters;
using System.IO;
using System.Text;

namespace ACadSharp.IO.DWG
{
Expand Down Expand Up @@ -330,9 +332,99 @@ private void writeEntityMode(Entity entity)
private void writeExtendedData(ExtendedDataDictionary data)
{
//EED size BS size of extended entity data, if any
foreach (var item in data.Entries)
{
writeExtendedDataEntry(item.Key, item.Value);
}

this._writer.WriteBitShort(0);
}

private void writeExtendedDataEntry(AppId app, ExtendedData entry)
{
using (MemoryStream mstream = new MemoryStream())
{
foreach (ExtendedDataRecord record in entry.Records)
{
//Each data item has a 1-byte code (DXF group code minus 1000) followed by the value.
mstream.WriteByte((byte)(record.Code - 1000));

switch (record)
{
case ExtendedDataBinaryChunk binaryChunk:
mstream.WriteByte((byte)binaryChunk.Value.Length);
mstream.Write(binaryChunk.Value, 0, binaryChunk.Value.Length);
break;
case ExtendedDataControlString control:
mstream.WriteByte((byte)(control.Value == '}' ? 1 : 0));
break;
case ExtendedDataInteger16 s16:
mstream.Write(LittleEndianConverter.Instance.GetBytes(s16.Value), 0, 2);
break;
case ExtendedDataInteger32 s32:
mstream.Write(LittleEndianConverter.Instance.GetBytes(s32.Value), 0, 4);
break;
case ExtendedDataScale scale:
mstream.Write(LittleEndianConverter.Instance.GetBytes(scale.Value), 0, 8);
break;
case ExtendedDataDistance dist:
mstream.Write(LittleEndianConverter.Instance.GetBytes(dist.Value), 0, 8);
break;
case ExtendedDataDirection dir:
mstream.Write(LittleEndianConverter.Instance.GetBytes(dir.Value.X), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(dir.Value.Y), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(dir.Value.Z), 0, 8);
break;
case ExtendedDataCoordinate coord:
mstream.Write(LittleEndianConverter.Instance.GetBytes(coord.Value.X), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(coord.Value.Y), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(coord.Value.Z), 0, 8);
break;
case ExtendedDataWorldCoordinate wcoord:
mstream.Write(LittleEndianConverter.Instance.GetBytes(wcoord.Value.X), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(wcoord.Value.Y), 0, 8);
mstream.Write(LittleEndianConverter.Instance.GetBytes(wcoord.Value.Z), 0, 8);
break;
case IExtendedDataHandleReference handle:
ulong h = handle.Value;
if (handle.ResolveReference(this._document) == null)
{
h = 0;
}
mstream.Write(BigEndianConverter.Instance.GetBytes(h), 0, 8);
break;
case ExtendedDataString str:
//same as ReadTextUnicode()
if (this.R2007Plus)
{
mstream.Write(LittleEndianConverter.Instance.GetBytes((ushort)str.Value.Length + 1), 0, 2);
byte[] bytes = Encoding.Unicode.GetBytes(str.Value);

mstream.Write(bytes, 0, bytes.Length);
mstream.WriteByte(0);
mstream.WriteByte(0);
}
else
{
byte[] bytes = this._writer.Encoding.GetBytes(string.IsNullOrEmpty(str.Value) ? string.Empty : str.Value);
mstream.Write(LittleEndianConverter.Instance.GetBytes((ushort)str.Value.Length + 1), 0, 2);
mstream.Write(bytes, 0, bytes.Length);
mstream.WriteByte(0);
}
break;
default:
throw new System.NotSupportedException($"ExtendedDataRecord of type {record.GetType().FullName} not supported.");
}
}

this._writer.WriteBitShort((short)mstream.Length);

this._writer.Main.HandleReference(DwgReferenceType.HardPointer, app.Handle);

this._writer.WriteBytes(mstream.GetBuffer(), 0, (int)mstream.Length);
}
}

private void writeReactorsAndDictionaryHandle(CadObject cadObject)
{
//TODO: Write reactors
Expand Down
Loading

0 comments on commit f0cbe70

Please sign in to comment.