Skip to content
Open
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
Binary file removed reference/ACadFileExploration.xlsx
Binary file not shown.
3 changes: 3 additions & 0 deletions samples/line.shp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*1,42,SINGLE-LINE
4,125,4,80,3,125,3,80,3,128,002,9,(0,0),001,9,(127,127),(1,1),(0,0),002,9,(-127,-127),(-1,-1),(0,0),001,4,128,4,80,4,125
3,80,3,125,0
Binary file added samples/line.shx
Binary file not shown.
Binary file added samples/ltypeshp.shx
Binary file not shown.
16 changes: 16 additions & 0 deletions src/ACadSharp.Tests/IO/ShapeFileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using ACadSharp.IO;
using System.IO;
using Xunit;

namespace ACadSharp.Tests.IO
{
public class ShapeFileTests
{
[Fact]
public void OpenTest()
{
ShapeFile.Open(Path.Combine(TestVariables.SamplesFolder, ShapeFile.DefaultShapeFile));
ShapeFile.Open(Path.Combine(TestVariables.SamplesFolder, "line.shx"));
}
}
}
10 changes: 1 addition & 9 deletions src/ACadSharp/Entities/Shape.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,7 @@ public TextStyle ShapeStyle
{
throw new ArgumentNullException(nameof(value));
}

if (this.Document != null)
{
this._style = CadObject.updateCollection(value, this.Document.TextStyles);
}
else
{
this._style = value;
}
this._style = CadObject.updateCollection(value, this.Document?.TextStyles);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4470,7 +4470,7 @@ private CadTemplate readLayer()
private CadTemplate readTextStyle()
{
TextStyle style = new TextStyle();
CadTableEntryTemplate<TextStyle> template = new CadTableEntryTemplate<TextStyle>(style);
CadTextStyleTemplate template = new CadTextStyleTemplate(style);

this.readCommonNonEntityData(template);

Expand Down
34 changes: 34 additions & 0 deletions src/ACadSharp/IO/ShapeFile.Geometry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using CSMath;
using System.Collections.Generic;

namespace ACadSharp.IO
{
public partial class ShapeFile
{
public class Geometry
{
public List<List<XY>> Lines { get; } = new();

public string Name { get; }

public Geometry(string name)
{
this.Name = name;
}

public static Geometry Create(string name, byte[] data)
{
int index = 0;

ShapeBuilder builder = new ShapeBuilder();

while (index < data.Length)
{
index += builder.ProcessValue(index, data);
}

return builder.Build(name);
}
}
}
}
225 changes: 225 additions & 0 deletions src/ACadSharp/IO/ShapeFile.ShapeBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
using CSMath;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ACadSharp.IO
{
public partial class ShapeFile
{
internal class ShapeBuilder
{
public List<XY> CurrentLine
{
get
{
if (this.Polylines.Count == 0)
{
this.Polylines.Add(new List<XY>());
}

return this.Polylines.Last();
}
}

public XY LastPt { get; set; }

public bool NotStore { get; set; }

public List<List<XY>> Polylines { get; } = new();

internal bool DrawModeOn
{
get
{
return this._drawModeOn;
}
set
{
this._drawModeOn = value;
this._isLine = false;
}
}

private static readonly XY[] _vectors =
{
new XY(1.0, 0.0),
new XY(1.0, 0.5),
new XY(1.0, 1.0),
new XY(0.5, 1.0),
new XY(0.0, 1.0),
new XY(-0.5, 1.0),
new XY(-1.0, 1.0),
new XY(-1.0, 0.5),
new XY(-1.0, 0.0),
new XY(-1.0, -0.5),
new XY(-1.0, -1.0),
new XY(-0.5, -1.0),
new XY(0.0, -1.0),
new XY(0.5, -1.0),
new XY(1.0, -1.0),
new XY(1.0, -0.5)
};

private readonly Stack<XY> _locationStack = new();

private double _current = 1.0d;

private bool _drawModeOn = true;

private bool _isLine = false;

public ShapeBuilder()
{
}

public Geometry Build(string name)
{
Geometry geometry = new Geometry(name);

geometry.Lines.AddRange(this.Polylines);

return geometry;
}

public bool HasToStore()
{
bool result = !this.NotStore;
this.NotStore = false;

return result;
}

public int ProcessValue(int index, byte[] data)
{
byte code = data[index];
switch (code)
{
case 0x0:
// End of shape definition
return 1;
case 0x1:
// Activate Draw mode(pen down)
if (this.HasToStore())
{
this.DrawModeOn = true;
}
return 1;
case 0x2:
//Deactivate Draw mode (pen up)
if (this.HasToStore())
{
this.DrawModeOn = false;
}
return 1;
case 0x3:
if (this.HasToStore())
{
//Divide vector lengths by next byte
double div = (sbyte)data[index + 1];
this._current /= div;
}
return 2;
case 0x4:
if (this.HasToStore())
{
//Multiply vector lengths by next byte
double mult = (sbyte)data[index + 1];
this._current *= mult;
}
return 2;
case 0x5:
if (this.HasToStore())
{
//Push current location onto stack
this._locationStack.Push(this.LastPt);
}
return 1;
case 0x6:
if (this.HasToStore())
{
//Pop current location from stack
if (this._locationStack.Count > 0)
{
this.LastPt = this._locationStack.Pop();
}

this._isLine = false;
}
return 1;
case 0x7:
//Draw subshape number given by next byte
throw new NotImplementedException();
case 0x8:
if (this.HasToStore())
{
//X-Y displacement given by next two bytes
var x = (sbyte)data[index + 1];
var y = (sbyte)data[index + 2];

this.drawLine(x, y);
}
return 3;
case 0x9:
//Multiple X-Y displacements, terminated (0,0)
bool flag = this.HasToStore();
int i;
for (i = index + 1; data[i] != 0 || data[i + 1] != 0; i += 2)
{
if (flag)
{
this.drawLine((sbyte)data[i], (sbyte)data[i + 1]);
}
}
return i - index + 2;
case 0xA:
//Octant arc defined by next two bytes

return 0;
case 0xB:
//Fractional arc defined by next five bytes
case 0xC:
//Arc defined by X-Y displacement and bulge
case 0xD:
//Multiple bulge-specified arcs
case 0xE:
//Process next command only if vertical text
throw new NotImplementedException();
default:
byte b = data[index];
double value = (b >> 4) & 0xF;
XY pt = _vectors[b & 0xF];
if (this.HasToStore())
{
this.drawLine(value * pt.X, value * pt.Y);
}
return 1;
}
}

private void createNewLine()
{
if (!this._isLine)
{
this._isLine = true;
this.Polylines.Add(new List<XY>());
}
}

private void drawLine(double x, double y)
{
var pt = new XY(
this.LastPt.X + this._current * x,
this.LastPt.Y + this._current * y);

if (this._drawModeOn)
{
this.createNewLine();
this.CurrentLine.Add(pt);
}

this.LastPt = pt;
}
}
}
}
71 changes: 71 additions & 0 deletions src/ACadSharp/IO/ShapeFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace ACadSharp.IO
{
public partial class ShapeFile
{
public const string DefaultShapeFile = "ltypeshp.shx";

private const string _sentinelV1 = "AutoCAD-86 shapes 1.0";

public Dictionary<ushort, Geometry> Geometries { get; } = new();

public static void Open(string file)
{
if (string.IsNullOrEmpty(file))
{
throw new ArgumentNullException(nameof(file));
}

using (BinaryReader reader = new BinaryReader(File.OpenRead(file)))
{
byte[] sentinel = reader.ReadBytes(21);
StringBuilder sb = new StringBuilder(21);
sb.Append(sentinel.Select(b => (char)b).ToArray());

if (!sb.ToString().Equals(_sentinelV1, StringComparison.InvariantCulture))
{
throw new ArgumentException("Not a valid Shape binary file .SHX.", nameof(file));
}

reader.ReadBytes(3);

ushort firstShape = reader.ReadUInt16();
ushort lastShape = reader.ReadUInt16();
ushort nEntries = reader.ReadUInt16();

var shapes = new List<(ushort, ushort)>(nEntries);
for (int i = 0; i < nEntries; i++)
{
var index = reader.ReadUInt16();
var size = reader.ReadUInt16();
shapes.Add((index, size));
}

for (int i = 0; i < nEntries; i++)
{
string name = nullTerminatedString(reader, Encoding.ASCII);
byte[] shape = reader.ReadBytes(shapes[i].Item2 - (name.Length + 1));

Geometry.Create(name, shape);
}
}
}

private static string nullTerminatedString(BinaryReader reader, Encoding encoding)
{
byte c = reader.ReadByte();
List<byte> bytes = new List<byte>();
while (c != 0) // strings always end with a 0 byte (char NULL)
{
bytes.Add(c);
c = reader.ReadByte();
}
return encoding.GetString(bytes.ToArray(), 0, bytes.Count);
}
}
}
Loading
Loading