• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DomCR / ACadSharp / 14885819796

07 May 2025 02:23PM UTC coverage: 75.518%. First build
14885819796

Pull #659

github

web-flow
Merge 5cf33a810 into 9eb0eaeae
Pull Request #659: issue 627

5742 of 8357 branches covered (68.71%)

Branch coverage included in aggregate %.

22 of 25 new or added lines in 5 files covered. (88.0%)

22892 of 29560 relevant lines covered (77.44%)

83096.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.58
/src/ACadSharp/IO/DXF/DxfReader.cs
1
using ACadSharp.Classes;
2
using ACadSharp.Entities;
3
using ACadSharp.Exceptions;
4
using ACadSharp.Header;
5
using ACadSharp.IO.DXF;
6
using CSUtilities.IO;
7
using CSUtilities.Text;
8
using System;
9
using System.Collections.Generic;
10
using System.Diagnostics;
11
using System.IO;
12
using System.Linq;
13
using System.Text;
14

15
namespace ACadSharp.IO
16
{
17
        /// <summary>
18
        /// Class for reading a DXF file into a <see cref="CadDocument"></see>.
19
        /// </summary>
20
        public class DxfReader : CadReaderBase<DxfReaderConfiguration>
21
        {
22
                private ACadVersion _version;
23
                private DxfDocumentBuilder _builder;
24
                private IDxfStreamReader _reader;
25

26
                /// <summary>
27
                /// Initializes a new instance of the <see cref="DxfReader"/> class
28
                /// </summary>
29
                /// <param name="filename">The file to open.</param>
30
                /// <param name="notification">Notification handler, sends any message or notification about the reading process.</param>
31
                public DxfReader(string filename, NotificationEventHandler notification = null) : base(filename, notification) { }
882✔
32

33
                /// <summary>
34
                /// Initializes a new instance of the <see cref="DxfReader"/> class
35
                /// </summary>
36
                /// <param name="stream">The stream to read from.</param>
37
                /// <param name="notification">Notification handler, sends any message or notification about the reading process.</param>
38
                public DxfReader(Stream stream, NotificationEventHandler notification = null) : base(stream, notification) { }
126✔
39

40
                /// <summary>
41
                /// Check if the file format is in binary.
42
                /// </summary>
43
                /// <returns></returns>
44
                public bool IsBinary()
45
                {
14✔
46
                        return IsBinary(this._fileStream.Stream);
14✔
47
                }
14✔
48

49
                /// <summary>
50
                /// Check if the file format is in binary.
51
                /// </summary>
52
                /// <param name="filename">Path to the dxf file.</param>
53
                /// <returns></returns>
54
                public static bool IsBinary(string filename)
55
                {
14✔
56
                        Stream stream = File.OpenRead(filename);
14✔
57
                        bool result = IsBinary(stream);
14✔
58

59
                        stream.Close();
14✔
60

61
                        return result;
14✔
62
                }
14✔
63

64
                /// <summary>
65
                /// Check if the file format is in binary.
66
                /// </summary>
67
                /// <param name="stream"></param>
68
                /// <param name="resetPos"></param>
69
                /// <returns></returns>
70
                public static bool IsBinary(Stream stream, bool resetPos = false)
71
                {
350✔
72
                        StreamIO sio = new StreamIO(stream);
350✔
73
                        sio.Position = 0;
350✔
74
                        string sn = sio.ReadString(DxfBinaryReader.Sentinel.Length);
350✔
75

76
                        bool isBinary = sn == DxfBinaryReader.Sentinel;
350✔
77

78
                        if (resetPos)
350!
79
                        {
×
80
                                stream.Position = 0;
×
81
                        }
×
82

83
                        return isBinary;
350✔
84
                }
350✔
85

86
                public static CadDocument Read(string filename, DxfReaderConfiguration configuration, NotificationEventHandler notification = null)
87
                {
14✔
88
                        CadDocument doc = null;
14✔
89

90
                        using (DxfReader reader = new DxfReader(filename, notification))
14✔
91
                        {
14✔
92
                                reader.Configuration = configuration;
14✔
93
                                doc = reader.Read();
14✔
94
                        }
14✔
95

96
                        return doc;
14✔
97
                }
14✔
98

99
                /// <summary>
100
                /// Read a dxf document in a stream
101
                /// </summary>
102
                /// <param name="stream"></param>
103
                /// <param name="notification">Notification handler, sends any message or notification about the reading process.</param>
104
                /// <returns></returns>
105
                public static CadDocument Read(Stream stream, NotificationEventHandler notification = null)
106
                {
42✔
107
                        CadDocument doc = null;
42✔
108

109
                        using (DxfReader reader = new DxfReader(stream, notification))
42✔
110
                        {
42✔
111
                                doc = reader.Read();
42✔
112
                        }
42✔
113

114
                        return doc;
42✔
115
                }
42✔
116

117
                /// <summary>
118
                /// Read a dxf document from a file
119
                /// </summary>
120
                /// <param name="filename"></param>
121
                /// <param name="notification">Notification handler, sends any message or notification about the reading process.</param>
122
                /// <returns></returns>
123
                public static CadDocument Read(string filename, NotificationEventHandler notification = null)
124
                {
42✔
125
                        return Read(File.OpenRead(filename), notification);
42✔
126
                }
42✔
127

128
                /// <inheritdoc/>
129
                public override CadDocument Read()
130
                {
266✔
131
                        this._document = new CadDocument(false);
266✔
132
                        this._document.SummaryInfo = new CadSummaryInfo();
266✔
133

134
                        this._reader = this._reader ?? this.getReader();
266✔
135

136
                        this._builder = new DxfDocumentBuilder(this._version, this._document, this.Configuration);
266✔
137
                        this._builder.OnNotification += this.onNotificationEvent;
266✔
138

139
                        while (this._reader.ValueAsString != DxfFileToken.EndOfFile)
8,182✔
140
                        {
7,916✔
141
                                if (this._reader.ValueAsString != DxfFileToken.BeginSection)
7,916✔
142
                                {
6,319✔
143
                                        this._reader.ReadNext();
6,319✔
144
                                        continue;
6,319✔
145
                                }
146
                                else
147
                                {
1,597✔
148
                                        this._reader.ReadNext();
1,597✔
149
                                }
1,597✔
150

151
                                switch (this._reader.ValueAsString)
1,597✔
152
                                {
153
                                        case DxfFileToken.HeaderSection:
154
                                                this._document.Header = this.ReadHeader();
266✔
155
                                                this._document.Header.Document = this._document;
266✔
156
                                                this._builder.InitialHandSeed = this._document.Header.HandleSeed;
266✔
157
                                                break;
266✔
158
                                        case DxfFileToken.ClassesSection:
159
                                                this._document.Classes = this.readClasses();
230✔
160
                                                break;
230✔
161
                                        case DxfFileToken.TablesSection:
162
                                                this.readTables();
266✔
163
                                                break;
266✔
164
                                        case DxfFileToken.BlocksSection:
165
                                                this.readBlocks();
266✔
166
                                                break;
266✔
167
                                        case DxfFileToken.EntitiesSection:
168
                                                this.readEntities();
266✔
169
                                                break;
266✔
170
                                        case DxfFileToken.ObjectsSection:
171
                                                this.readObjects();
230✔
172
                                                break;
230✔
173
                                        default:
174
                                                this.triggerNotification(($"Section not implemented {this._reader.ValueAsString}"), NotificationType.NotImplemented);
73✔
175
                                                break;
73✔
176
                                }
177

178
                                this._reader.ReadNext();
1,597✔
179
                        }
1,597✔
180

181
                        if (this._document.Header == null)
266!
182
                        {
×
183
                                this._document.Header = new CadHeader(this._document);
×
184
                        }
×
185

186
                        this._builder.BuildDocument();
266✔
187

188
                        return this._document;
266✔
189
                }
266✔
190

191
                /// <inheritdoc/>
192
                public override CadHeader ReadHeader()
193
                {
294✔
194
                        this._reader = this.goToSection(DxfFileToken.HeaderSection);
294✔
195

196
                        CadHeader header = new CadHeader();
294✔
197

198
                        Dictionary<string, CadSystemVariable> headerMap = CadHeader.GetHeaderMap();
294✔
199

200
                        this._reader.ReadNext();
294✔
201

202
                        //Loop until the section ends
203
                        while (this._reader.ValueAsString != DxfFileToken.EndSection)
66,374✔
204
                        {
66,080✔
205
                                //Get the current header variable
206
                                string currVar = this._reader.ValueAsString;
66,080✔
207

208
                                if (this._reader.ValueAsString == null || !headerMap.TryGetValue(currVar, out CadSystemVariable data))
66,080!
209
                                {
6,056✔
210
#if TEST
211
                                        this.triggerNotification($"Header variable not implemented {currVar}", NotificationType.NotImplemented);
212
#endif
213
                                        this._reader.ReadNext();
6,056✔
214
                                        continue;
6,056✔
215
                                }
216

217
                                object[] parameters = new object[data.DxfCodes.Length];
60,024✔
218
                                for (int i = 0; i < data.DxfCodes.Length; i++)
267,392✔
219
                                {
73,672✔
220
                                        this._reader.ReadNext();
73,672✔
221

222
                                        if (this._reader.DxfCode == DxfCode.CLShapeText)
73,672!
223
                                        {
×
224
                                                //Irregular dxf files may not follow the header type
225
                                                int c = data.DxfCodes[i];
×
226
                                                GroupCodeValueType g = GroupCodeValue.TransformValue(c);
×
227
                                                switch (g)
×
228
                                                {
229
                                                        case GroupCodeValueType.Bool:
230
                                                                parameters[i] = false;
×
231
                                                                break;
×
232
                                                        case GroupCodeValueType.Byte:
233
                                                        case GroupCodeValueType.Int16:
234
                                                        case GroupCodeValueType.Int32:
235
                                                        case GroupCodeValueType.Int64:
236
                                                        case GroupCodeValueType.Double:
237
                                                        case GroupCodeValueType.Point3D:
238
                                                                parameters[i] = 0;
×
239
                                                                break;
×
240
                                                        case GroupCodeValueType.None:
241
                                                        case GroupCodeValueType.String:
242
                                                        default:
243
                                                                parameters[i] = default;
×
244
                                                                break;
×
245
                                                }
246

247
                                                break;
×
248
                                        }
249

250
                                        parameters[i] = this._reader.Value;
73,672✔
251
                                }
73,672✔
252

253
                                try
254
                                {
60,024✔
255
                                        //Set the header value by name
256
                                        header.SetValue(currVar, parameters);
60,024✔
257
                                }
60,024✔
258
                                catch (Exception ex)
×
259
                                {
×
260
                                        this.triggerNotification($"Invalid value for header variable {currVar} | {parameters.FirstOrDefault()}", NotificationType.Warning, ex);
×
261
                                }
×
262

263
                                if (this._reader.DxfCode != DxfCode.CLShapeText)
60,024✔
264
                                {
60,024✔
265
                                        this._reader.ReadNext();
60,024✔
266
                                }
60,024✔
267
                        }
60,024✔
268

269
                        return header;
294✔
270
                }
294✔
271

272
                /// <summary>
273
                /// Read only the tables section in the dxf document
274
                /// </summary>
275
                /// <remarks>
276
                /// The <see cref="CadDocument"/> will not contain any entity, only the tables and it's records
277
                /// </remarks>
278
                /// <returns></returns>
279
                public CadDocument ReadTables()
280
                {
14✔
281
                        this._reader = this._reader ?? this.getReader();
14✔
282

283
                        this._builder = new DxfDocumentBuilder(this._version, this._document, this.Configuration);
14✔
284
                        this._builder.OnNotification += this.onNotificationEvent;
14✔
285

286
                        this.readTables();
14✔
287

288
                        this._document.Header = new CadHeader(this._document);
14✔
289

290
                        this._builder.RegisterTables();
14✔
291

292
                        this._builder.BuildTables();
14✔
293

294
                        return this._document;
14✔
295
                }
14✔
296

297
                /// <summary>
298
                /// Read only the entities section in the dxf document
299
                /// </summary>
300
                /// <remarks>
301
                /// The entities will be completely independent from each other and linetypes and layers will only have it's name set, all the other properties will be set as default
302
                /// </remarks>
303
                /// <returns></returns>
304
                public List<Entity> ReadEntities()
305
                {
14✔
306
                        this._reader = this._reader ?? this.getReader();
14✔
307

308
                        this._builder = new DxfDocumentBuilder(this._version, this._document, this.Configuration);
14✔
309
                        this._builder.OnNotification += this.onNotificationEvent;
14✔
310

311
                        this.readEntities();
14✔
312

313
                        return this._builder.BuildEntities();
14✔
314
                }
14✔
315

316
                /// <inheritdoc/>
317
                public override void Dispose()
318
                {
336✔
319
                        base.Dispose();
336✔
320

321
                        if (this.Configuration.ClearChache)
336✔
322
                        {
336✔
323
                                DxfMap.ClearCache();
336✔
324
                        }
336✔
325
                }
336✔
326

327
                #region DxfClasses
328

329
                /// <summary>
330
                /// Read the CLASSES section of the DXF file.
331
                /// </summary>
332
                /// <returns></returns>
333
                private DxfClassCollection readClasses()
334
                {
230✔
335
                        //Get the needed handler
336
                        this._reader = this.goToSection(DxfFileToken.ClassesSection);
230✔
337

338
                        DxfClassCollection classes = new DxfClassCollection();
230✔
339

340
                        //Advance to the first value in the section
341
                        this._reader.ReadNext();
230✔
342
                        //Loop until the section ends
343
                        while (this._reader.ValueAsString != DxfFileToken.EndSection)
9,566✔
344
                        {
9,336✔
345
                                if (this._reader.ValueAsString == DxfFileToken.ClassEntry)
9,336!
346
                                {
9,336✔
347
                                        var dxfClass = this.readClass();
9,336✔
348

349
                                        if(dxfClass.ClassNumber < 500)
9,336✔
350
                                        {
9,336✔
351
                                                dxfClass.ClassNumber = (short)(500 + classes.Count);
9,336✔
352
                                        }
9,336✔
353

354
                                        classes.AddOrUpdate(dxfClass);
9,336✔
355
                                }
9,336✔
356
                                else
357
                                        this._reader.ReadNext();
×
358
                        }
9,336✔
359

360
                        return classes;
230✔
361
                }
230✔
362

363
                private DxfClass readClass()
364
                {
9,336✔
365
                        DxfClass curr = new DxfClass();
9,336✔
366

367
                        Debug.Assert(this._reader.ValueAsString == DxfFileToken.ClassEntry);
9,336✔
368

369
                        this._reader.ReadNext();
9,336✔
370
                        //Loop until the next class or the end of the section
371
                        while (this._reader.DxfCode != DxfCode.Start)
73,140✔
372
                        {
63,804✔
373
                                switch (this._reader.Code)
63,804✔
374
                                {
375
                                        //Class DXF record name; always unique
376
                                        case 1:
377
                                                curr.DxfName = this._reader.ValueAsString;
9,336✔
378
                                                break;
9,336✔
379
                                        //C++ class name. Used to bind with software that defines object class behavior; always unique
380
                                        case 2:
381
                                                curr.CppClassName = this._reader.ValueAsString;
9,336✔
382
                                                break;
9,336✔
383
                                        //Application name. Posted in Alert box when a class definition listed in this section is not currently loaded
384
                                        case 3:
385
                                                curr.ApplicationName = this._reader.ValueAsString;
9,336✔
386
                                                break;
9,336✔
387
                                        //Proxy capabilities flag.
388
                                        case 90:
389
                                                curr.ProxyFlags = (ProxyFlags)this._reader.ValueAsUShort;
9,336✔
390
                                                break;
9,336✔
391
                                        //Instance count for a custom class
392
                                        case 91:
393
                                                curr.InstanceCount = this._reader.ValueAsInt;
7,788✔
394
                                                break;
7,788✔
395
                                        //Was-a-proxy flag. Set to 1 if class was not loaded when this DXF file was created, and 0 otherwise
396
                                        case 280:
397
                                                curr.WasZombie = this._reader.ValueAsBool;
9,336✔
398
                                                break;
9,336✔
399
                                        //Is - an - entity flag.
400
                                        case 281:
401
                                                curr.IsAnEntity = this._reader.ValueAsBool;
9,336✔
402
                                                break;
9,336✔
403
                                        default:
404
                                                break;
×
405
                                }
406

407
                                this._reader.ReadNext();
63,804✔
408
                        }
63,804✔
409

410
                        return curr;
9,336✔
411
                }
9,336✔
412

413
                #endregion
414

415
                #region Tables
416

417
                private void readTables()
418
                {
280✔
419
                        //Get the needed handler
420
                        this._reader = this.goToSection(DxfFileToken.TablesSection);
280✔
421

422
                        DxfTablesSectionReader reader = new DxfTablesSectionReader(this._reader, this._builder);
280✔
423

424
                        reader.Read();
280✔
425
                }
280✔
426

427
                #endregion
428

429
                /// <summary>
430
                /// Read the BLOCKS section of the DXF file.
431
                /// </summary>
432
                private void readBlocks()
433
                {
266✔
434
                        //Get the needed handler
435
                        this._reader = this.goToSection(DxfFileToken.BlocksSection);
266✔
436

437
                        DxfBlockSectionReader reader = new DxfBlockSectionReader(this._reader, this._builder);
266✔
438

439
                        reader.Read();
266✔
440
                }
266✔
441

442
                /// <summary>
443
                /// Read the ENTITIES section of the DXF file.
444
                /// </summary>
445
                private void readEntities()
446
                {
280✔
447
                        //Get the needed handler
448
                        this._reader = this.goToSection(DxfFileToken.EntitiesSection);
280✔
449

450
                        DxfEntitiesSectionReader reader = new DxfEntitiesSectionReader(this._reader, this._builder);
280✔
451

452
                        reader.Read();
280✔
453
                }
280✔
454

455
                /// <summary>
456
                /// Read the OBJECTS section of the DXF file.
457
                /// </summary>
458
                private void readObjects()
459
                {
230✔
460
                        //Get the needed handler
461
                        this._reader = this.goToSection(DxfFileToken.ObjectsSection);
230✔
462

463
                        DxfObjectsSectionReader reader = new DxfObjectsSectionReader(this._reader, this._builder);
230✔
464

465
                        reader.Read();
230✔
466
                }
230✔
467

468
                /// <summary>
469
                /// Read the THUMBNAILIMAGE section of the DXF file.
470
                /// </summary>
471
                private void readThumbnailImage()
472
                {
×
473
                        throw new NotImplementedException();
×
474
                }
475

476
                private IDxfStreamReader getReader()
477
                {
322✔
478
                        IDxfStreamReader tmpReader = null;
322✔
479
                        this._version = ACadVersion.Unknown;
322✔
480

481
                        bool isBinary = IsBinary(this._fileStream.Stream, false);
322✔
482
                        bool isAC1009Format = false;
322✔
483

484
                        if (isBinary && this._fileStream.Stream.ReadByte() != -1)
322✔
485
                        {
160✔
486
                                int flag = this._fileStream.ReadByte();
160✔
487
                                if (flag != -1 && flag != 0)
160!
488
                                {
22✔
489
                                        isAC1009Format = true;
22✔
490
                                }
22✔
491
                        }
160✔
492

493
                        tmpReader = this.createReader(isBinary, isAC1009Format);
322✔
494

495
                        if (!tmpReader.Find(DxfFileToken.HeaderSection))
322!
496
                        {
×
NEW
497
                                this.triggerNotification($"Header section not found, using a generic reader.", NotificationType.Warning);
×
498

499
                                this._version = ACadVersion.Unknown;
×
500
                                tmpReader.Start();
×
501
                                return tmpReader;
×
502
                        }
503

504
                        while (tmpReader.ValueAsString != DxfFileToken.EndSection)
13,832✔
505
                        {
13,788✔
506
                                if (tmpReader.ValueAsString == "$ACADVER")
13,788✔
507
                                {
310✔
508
                                        tmpReader.ReadNext();
310✔
509
                                        this._version = CadUtils.GetVersionFromName(tmpReader.ValueAsString);
310✔
510
                                        if (this._version >= ACadVersion.AC1021)
310✔
511
                                        {
178✔
512
                                                this._encoding = Encoding.UTF8;
178✔
513
                                                break;
178✔
514
                                        }
515

516
                                        if (this._version < ACadVersion.AC1002)
132!
517
                                        {
×
518
                                                if (this._version == ACadVersion.Unknown)
×
519
                                                {
×
520
                                                        throw new CadNotSupportedException();
×
521
                                                }
522
                                                else
523
                                                {
×
524
                                                        throw new CadNotSupportedException(this._version);
×
525
                                                }
526
                                        }
527
                                }
132✔
528
                                else if (tmpReader.ValueAsString == "$DWGCODEPAGE")
13,478✔
529
                                {
100✔
530
                                        tmpReader.ReadNext();
100✔
531

532
                                        string encoding = tmpReader.ValueAsString;
100✔
533

534
                                        CodePage code = CadUtils.GetCodePage(encoding.ToLower());
100✔
535
                                        this._encoding = this.getListedEncoding((int)code);
100✔
536
                                        break;
100✔
537
                                }
538

539
                                tmpReader.ReadNext();
13,510✔
540
                        }
13,510✔
541

542
                        if(this._version == ACadVersion.Unknown)
322✔
543
                        {
12✔
544
                                this.triggerNotification($"Dxf version not found, using a generic reader.", NotificationType.Warning);
12✔
545
                        }
12✔
546

547
                        return this.createReader(isBinary, isAC1009Format);
322✔
548
                }
322✔
549

550
                private IDxfStreamReader goToSection(string sectionName)
551
                {
1,580✔
552
                        //Get the needed handler
553
                        this._reader = this._reader ?? this.getReader();
1,580✔
554

555
                        if (this._reader.ValueAsString == sectionName)
1,580✔
556
                                return this._reader;
1,524✔
557

558
                        //Go to the start of header section
559
                        this._reader.Find(sectionName);
56✔
560

561
                        return this._reader;
56✔
562
                }
1,580✔
563

564
                private IDxfStreamReader createReader(bool isBinary, bool isAC1009Format)
565
                {
644✔
566
                        Encoding encoding = this._encoding;
644✔
567
                        if (encoding == null)
644!
568
                        {
×
569
                                encoding = Encoding.ASCII;
×
570
                        }
×
571

572
                        if (isBinary)
644✔
573
                        {
320✔
574
                                if (isAC1009Format)
320✔
575
                                {
44✔
576
                                        return new DxfBinaryReaderAC1009(this._fileStream.Stream, encoding);
44✔
577
                                }
578
                                else
579
                                {
276✔
580
                                        return new DxfBinaryReader(this._fileStream.Stream, encoding);
276✔
581
                                }
582
                        }
583
                        else
584
                        {
324✔
585
                                return new DxfTextReader(this._fileStream.Stream, encoding);
324✔
586
                        }
587
                }
644✔
588
        }
589
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc