ofs-dxf-ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/API.md +411 -0
  2. package/CHANGELOG.md +26 -0
  3. package/LICENSE +21 -0
  4. package/README.md +119 -0
  5. package/dist/core/dxf-builder.d.ts +348 -0
  6. package/dist/core/dxf-builder.js +549 -0
  7. package/dist/core/dxf-document.d.ts +220 -0
  8. package/dist/core/dxf-document.js +529 -0
  9. package/dist/core/dxf-entities.d.ts +327 -0
  10. package/dist/core/dxf-entities.js +22 -0
  11. package/dist/core/dxf-serializer.d.ts +48 -0
  12. package/dist/core/dxf-serializer.js +595 -0
  13. package/dist/core/hatch-patterns.d.ts +15 -0
  14. package/dist/core/hatch-patterns.js +214 -0
  15. package/dist/core/modelspace.d.ts +106 -0
  16. package/dist/core/modelspace.js +351 -0
  17. package/dist/geometry/math.utils.d.ts +8 -0
  18. package/dist/geometry/math.utils.js +43 -0
  19. package/dist/geometry/offset.utils.d.ts +32 -0
  20. package/dist/geometry/offset.utils.js +87 -0
  21. package/dist/geometry/polyline.utils.d.ts +10 -0
  22. package/dist/geometry/polyline.utils.js +63 -0
  23. package/dist/geometry/text.utils.d.ts +15 -0
  24. package/dist/geometry/text.utils.js +28 -0
  25. package/dist/index.d.ts +23 -0
  26. package/dist/index.js +29 -0
  27. package/dist/parser/auto-cad-color-index.d.ts +8 -0
  28. package/dist/parser/auto-cad-color-index.js +265 -0
  29. package/dist/parser/dim-style-codes.d.ts +5 -0
  30. package/dist/parser/dim-style-codes.js +32 -0
  31. package/dist/parser/dxf-parser.d.ts +16 -0
  32. package/dist/parser/dxf-parser.js +824 -0
  33. package/dist/parser/entities/3dface.d.ts +5 -0
  34. package/dist/parser/entities/3dface.js +76 -0
  35. package/dist/parser/entities/arc.d.ts +5 -0
  36. package/dist/parser/entities/arc.js +34 -0
  37. package/dist/parser/entities/attdef.d.ts +5 -0
  38. package/dist/parser/entities/attdef.js +86 -0
  39. package/dist/parser/entities/attribute.d.ts +5 -0
  40. package/dist/parser/entities/attribute.js +106 -0
  41. package/dist/parser/entities/circle.d.ts +5 -0
  42. package/dist/parser/entities/circle.js +40 -0
  43. package/dist/parser/entities/dimension.d.ts +5 -0
  44. package/dist/parser/entities/dimension.js +65 -0
  45. package/dist/parser/entities/ellipse.d.ts +5 -0
  46. package/dist/parser/entities/ellipse.js +42 -0
  47. package/dist/parser/entities/hatch.d.ts +5 -0
  48. package/dist/parser/entities/hatch.js +321 -0
  49. package/dist/parser/entities/insert.d.ts +5 -0
  50. package/dist/parser/entities/insert.js +52 -0
  51. package/dist/parser/entities/line.d.ts +5 -0
  52. package/dist/parser/entities/line.js +30 -0
  53. package/dist/parser/entities/lwpolyline.d.ts +5 -0
  54. package/dist/parser/entities/lwpolyline.js +101 -0
  55. package/dist/parser/entities/mtext.d.ts +5 -0
  56. package/dist/parser/entities/mtext.js +54 -0
  57. package/dist/parser/entities/point.d.ts +5 -0
  58. package/dist/parser/entities/point.js +30 -0
  59. package/dist/parser/entities/polyline.d.ts +5 -0
  60. package/dist/parser/entities/polyline.js +85 -0
  61. package/dist/parser/entities/solid.d.ts +5 -0
  62. package/dist/parser/entities/solid.js +35 -0
  63. package/dist/parser/entities/spline.d.ts +5 -0
  64. package/dist/parser/entities/spline.js +69 -0
  65. package/dist/parser/entities/text.d.ts +5 -0
  66. package/dist/parser/entities/text.js +47 -0
  67. package/dist/parser/entities/vertex.d.ts +5 -0
  68. package/dist/parser/entities/vertex.js +59 -0
  69. package/dist/parser/extended-data-parser.d.ts +18 -0
  70. package/dist/parser/extended-data-parser.js +100 -0
  71. package/dist/parser/index.d.ts +10 -0
  72. package/dist/parser/index.js +9 -0
  73. package/dist/parser/log.d.ts +14 -0
  74. package/dist/parser/log.js +39 -0
  75. package/dist/parser/parse-helpers.d.ts +23 -0
  76. package/dist/parser/parse-helpers.js +128 -0
  77. package/dist/parser/scanner.d.ts +29 -0
  78. package/dist/parser/scanner.js +143 -0
  79. package/dist/parser/types.d.ts +78 -0
  80. package/dist/parser/types.js +5 -0
  81. package/dist/symbols/slope-symbol.d.ts +21 -0
  82. package/dist/symbols/slope-symbol.js +81 -0
  83. package/dist/symbols/structures.d.ts +9 -0
  84. package/dist/symbols/structures.js +120 -0
  85. package/dist/template/blank-document.d.ts +13 -0
  86. package/dist/template/blank-document.js +13 -0
  87. package/dist/template/block-registry.d.ts +16 -0
  88. package/dist/template/block-registry.js +135 -0
  89. package/dist/template/index.d.ts +17 -0
  90. package/dist/template/index.js +17 -0
  91. package/dist/template/template-injector.d.ts +45 -0
  92. package/dist/template/template-injector.js +487 -0
  93. package/dist/template/template-reader.d.ts +28 -0
  94. package/dist/template/template-reader.js +80 -0
  95. package/dist/types/index.d.ts +24 -0
  96. package/dist/types/index.js +8 -0
  97. package/package.json +59 -0
@@ -0,0 +1,824 @@
1
+ /**
2
+ * DxfParser — TypeScript port of the `dxf-parser` package (see parser-raw/
3
+ * for the original JS source). Parses a DXF string into structured data:
4
+ * header, tables (layers, line types, styles, …), blocks and entities.
5
+ *
6
+ * Browser-first: only `parseSync(source: string)` is provided (the original's
7
+ * Node-stream `parseStream` was dropped).
8
+ */
9
+ import DxfArrayScanner from './scanner.js';
10
+ import AUTO_CAD_COLOR_INDEX from './auto-cad-color-index.js';
11
+ import dimStyleCodes from './dim-style-codes.js';
12
+ import log from './log.js';
13
+ import Face from './entities/3dface.js';
14
+ import Arc from './entities/arc.js';
15
+ import AttDef from './entities/attdef.js';
16
+ import Attribute from './entities/attribute.js';
17
+ import Circle from './entities/circle.js';
18
+ import Dimension from './entities/dimension.js';
19
+ import Ellipse from './entities/ellipse.js';
20
+ import Insert from './entities/insert.js';
21
+ import Line from './entities/line.js';
22
+ import LWPolyline from './entities/lwpolyline.js';
23
+ import MText from './entities/mtext.js';
24
+ import Point from './entities/point.js';
25
+ import Polyline from './entities/polyline.js';
26
+ import Solid from './entities/solid.js';
27
+ import Spline from './entities/spline.js';
28
+ import Text from './entities/text.js';
29
+ import Hatch from './entities/hatch.js';
30
+ function registerDefaultEntityHandlers(dxfParser) {
31
+ // Supported entities here (some entity code is still being refactored into this flow)
32
+ dxfParser.registerEntityHandler(Face);
33
+ dxfParser.registerEntityHandler(Arc);
34
+ dxfParser.registerEntityHandler(AttDef);
35
+ dxfParser.registerEntityHandler(Attribute);
36
+ dxfParser.registerEntityHandler(Circle);
37
+ dxfParser.registerEntityHandler(Dimension);
38
+ dxfParser.registerEntityHandler(Ellipse);
39
+ dxfParser.registerEntityHandler(Insert);
40
+ dxfParser.registerEntityHandler(Line);
41
+ dxfParser.registerEntityHandler(LWPolyline);
42
+ dxfParser.registerEntityHandler(MText);
43
+ dxfParser.registerEntityHandler(Point);
44
+ dxfParser.registerEntityHandler(Polyline);
45
+ dxfParser.registerEntityHandler(Solid);
46
+ dxfParser.registerEntityHandler(Spline);
47
+ dxfParser.registerEntityHandler(Text);
48
+ dxfParser.registerEntityHandler(Hatch);
49
+ }
50
+ export default class DxfParser {
51
+ _entityHandlers = {};
52
+ constructor() {
53
+ registerDefaultEntityHandlers(this);
54
+ }
55
+ registerEntityHandler(handlerType) {
56
+ this._entityHandlers[handlerType.ForEntityName] = new handlerType();
57
+ }
58
+ parseSync(source) {
59
+ if (typeof source === 'string') {
60
+ return this._parse(source);
61
+ }
62
+ else {
63
+ console.error('Cannot read DXF source of type `' + typeof source);
64
+ return null;
65
+ }
66
+ }
67
+ _parse(dxfString) {
68
+ const dxf = {};
69
+ let lastHandle = 0;
70
+ const dxfLinesArray = dxfString.split(/\r\n|\r|\n/g);
71
+ const scanner = new DxfArrayScanner(dxfLinesArray);
72
+ if (!scanner.hasNext()) {
73
+ throw Error('Empty file');
74
+ }
75
+ let curr;
76
+ const self = this;
77
+ const parseAll = () => {
78
+ curr = scanner.next();
79
+ while (!scanner.isEOF()) {
80
+ if (curr.code === 0 && curr.value === 'SECTION') {
81
+ curr = scanner.next();
82
+ // Be sure we are reading a section code
83
+ if (curr.code !== 2) {
84
+ console.error('Unexpected code %s after 0:SECTION', debugCode(curr));
85
+ curr = scanner.next();
86
+ continue;
87
+ }
88
+ if (curr.value === 'HEADER') {
89
+ log.debug('> HEADER');
90
+ dxf.header = parseHeader();
91
+ log.debug('<');
92
+ }
93
+ else if (curr.value === 'BLOCKS') {
94
+ log.debug('> BLOCKS');
95
+ dxf.blocks = parseBlocks();
96
+ log.debug('<');
97
+ }
98
+ else if (curr.value === 'ENTITIES') {
99
+ log.debug('> ENTITIES');
100
+ dxf.entities = parseEntities(false);
101
+ log.debug('<');
102
+ }
103
+ else if (curr.value === 'TABLES') {
104
+ log.debug('> TABLES');
105
+ dxf.tables = parseTables();
106
+ log.debug('<');
107
+ }
108
+ else if (curr.value === 'EOF') {
109
+ log.debug('EOF');
110
+ }
111
+ else {
112
+ log.warn(`Skipping section '${curr.value}'`);
113
+ }
114
+ }
115
+ else {
116
+ curr = scanner.next();
117
+ }
118
+ // If is a new section
119
+ }
120
+ };
121
+ const groupIs = (code, value) => {
122
+ return curr.code === code && curr.value === value;
123
+ };
124
+ /**
125
+ * @return header
126
+ */
127
+ const parseHeader = () => {
128
+ // interesting variables:
129
+ // $ACADVER, $VIEWDIR, $VIEWSIZE, $VIEWCTR, $TDCREATE, $TDUPDATE
130
+ // http://www.autodesk.com/techpubs/autocad/acadr14/dxf/header_section_al_u05_c.htm
131
+ // Also see VPORT table entries
132
+ let currVarName = null;
133
+ let currVarValue = null;
134
+ const header = {};
135
+ // loop through header variables
136
+ curr = scanner.next();
137
+ while (true) {
138
+ if (groupIs(0, 'ENDSEC')) {
139
+ if (currVarName != null)
140
+ header[currVarName] = currVarValue;
141
+ break;
142
+ }
143
+ else if (curr.code === 9) {
144
+ if (currVarName != null)
145
+ header[currVarName] = currVarValue;
146
+ currVarName = curr.value;
147
+ // Filter here for particular variables we are interested in
148
+ }
149
+ else {
150
+ if (curr.code === 10) {
151
+ currVarValue = { x: curr.value };
152
+ }
153
+ else if (curr.code === 20) {
154
+ currVarValue.y = curr.value;
155
+ }
156
+ else if (curr.code === 30) {
157
+ currVarValue.z = curr.value;
158
+ }
159
+ else {
160
+ currVarValue = curr.value;
161
+ }
162
+ }
163
+ curr = scanner.next();
164
+ }
165
+ curr = scanner.next(); // swallow up ENDSEC
166
+ return header;
167
+ };
168
+ const parseBlocks = () => {
169
+ const blocks = {};
170
+ curr = scanner.next();
171
+ while (curr.value !== 'EOF') {
172
+ if (groupIs(0, 'ENDSEC')) {
173
+ break;
174
+ }
175
+ if (groupIs(0, 'BLOCK')) {
176
+ log.debug('block {');
177
+ const block = parseBlock();
178
+ log.debug('}');
179
+ ensureHandle(block);
180
+ if (!block.name)
181
+ log.error('block with handle "' + block.handle + '" is missing a name.');
182
+ else
183
+ blocks[block.name] = block;
184
+ }
185
+ else {
186
+ logUnhandledGroup(curr);
187
+ curr = scanner.next();
188
+ }
189
+ }
190
+ return blocks;
191
+ };
192
+ const parseBlock = () => {
193
+ const block = {};
194
+ curr = scanner.next();
195
+ while (curr.value !== 'EOF') {
196
+ switch (curr.code) {
197
+ case 1:
198
+ block.xrefPath = curr.value;
199
+ curr = scanner.next();
200
+ break;
201
+ case 2:
202
+ block.name = curr.value;
203
+ curr = scanner.next();
204
+ break;
205
+ case 3:
206
+ block.name2 = curr.value;
207
+ curr = scanner.next();
208
+ break;
209
+ case 5:
210
+ block.handle = curr.value;
211
+ curr = scanner.next();
212
+ break;
213
+ case 8:
214
+ block.layer = curr.value;
215
+ curr = scanner.next();
216
+ break;
217
+ case 10:
218
+ block.position = parsePoint();
219
+ curr = scanner.next();
220
+ break;
221
+ case 67:
222
+ block.inPaperSpace = (curr.value && curr.value == 1) ? true : false;
223
+ curr = scanner.next();
224
+ break;
225
+ case 70:
226
+ if (curr.value != 0) {
227
+ //if(curr.value & BLOCK_ANONYMOUS_FLAG) console.log(' Anonymous block');
228
+ //if(curr.value & BLOCK_NON_CONSTANT_FLAG) console.log(' Non-constant attributes');
229
+ //if(curr.value & BLOCK_XREF_FLAG) console.log(' Is xref');
230
+ //if(curr.value & BLOCK_XREF_OVERLAY_FLAG) console.log(' Is xref overlay');
231
+ //if(curr.value & BLOCK_EXTERNALLY_DEPENDENT_FLAG) console.log(' Is externally dependent');
232
+ //if(curr.value & BLOCK_RESOLVED_OR_DEPENDENT_FLAG) console.log(' Is resolved xref or dependent of an xref');
233
+ //if(curr.value & BLOCK_REFERENCED_XREF) console.log(' This definition is a referenced xref');
234
+ block.type = curr.value;
235
+ }
236
+ curr = scanner.next();
237
+ break;
238
+ case 100:
239
+ // ignore class markers
240
+ curr = scanner.next();
241
+ break;
242
+ case 330:
243
+ block.ownerHandle = curr.value;
244
+ curr = scanner.next();
245
+ break;
246
+ case 0:
247
+ if (curr.value == 'ENDBLK')
248
+ break;
249
+ block.entities = parseEntities(true);
250
+ break;
251
+ default:
252
+ logUnhandledGroup(curr);
253
+ curr = scanner.next();
254
+ }
255
+ if (groupIs(0, 'ENDBLK')) {
256
+ curr = scanner.next();
257
+ break;
258
+ }
259
+ }
260
+ return block;
261
+ };
262
+ /**
263
+ * parseTables
264
+ * @return Object representing tables
265
+ */
266
+ const parseTables = () => {
267
+ const tables = {};
268
+ curr = scanner.next();
269
+ while (curr.value !== 'EOF') {
270
+ if (groupIs(0, 'ENDSEC'))
271
+ break;
272
+ if (groupIs(0, 'TABLE')) {
273
+ curr = scanner.next();
274
+ const tableDefinition = tableDefinitions[curr.value];
275
+ if (tableDefinition) {
276
+ log.debug(curr.value + ' Table {');
277
+ tables[tableDefinitions[curr.value].tableName] = parseTable();
278
+ log.debug('}');
279
+ }
280
+ else {
281
+ log.debug('Unhandled Table ' + curr.value);
282
+ }
283
+ }
284
+ else {
285
+ // else ignored
286
+ curr = scanner.next();
287
+ }
288
+ }
289
+ curr = scanner.next();
290
+ return tables;
291
+ };
292
+ const END_OF_TABLE_VALUE = 'ENDTAB';
293
+ const parseTable = () => {
294
+ const tableDefinition = tableDefinitions[curr.value];
295
+ const table = {};
296
+ let expectedCount = 0;
297
+ let actualCount;
298
+ curr = scanner.next();
299
+ while (!groupIs(0, END_OF_TABLE_VALUE)) {
300
+ switch (curr.code) {
301
+ case 5:
302
+ table.handle = curr.value;
303
+ curr = scanner.next();
304
+ break;
305
+ case 330:
306
+ table.ownerHandle = curr.value;
307
+ curr = scanner.next();
308
+ break;
309
+ case 100:
310
+ if (curr.value === 'AcDbSymbolTable') {
311
+ // ignore
312
+ curr = scanner.next();
313
+ }
314
+ else {
315
+ logUnhandledGroup(curr);
316
+ curr = scanner.next();
317
+ }
318
+ break;
319
+ case 70:
320
+ expectedCount = curr.value;
321
+ curr = scanner.next();
322
+ break;
323
+ case 0:
324
+ if (curr.value === tableDefinition.dxfSymbolName) {
325
+ table[tableDefinition.tableRecordsProperty] = tableDefinition.parseTableRecords();
326
+ }
327
+ else {
328
+ logUnhandledGroup(curr);
329
+ curr = scanner.next();
330
+ }
331
+ break;
332
+ default:
333
+ logUnhandledGroup(curr);
334
+ curr = scanner.next();
335
+ }
336
+ }
337
+ const tableRecords = table[tableDefinition.tableRecordsProperty];
338
+ if (tableRecords) {
339
+ if (tableRecords.constructor === Array) {
340
+ actualCount = tableRecords.length;
341
+ }
342
+ else if (typeof tableRecords === 'object') {
343
+ actualCount = Object.keys(tableRecords).length;
344
+ }
345
+ if (expectedCount !== actualCount) {
346
+ log.warn(`Parsed ${actualCount} ${tableDefinition.dxfSymbolName}'s but expected ${expectedCount}`);
347
+ }
348
+ }
349
+ else {
350
+ table[tableDefinition.tableRecordsProperty] = [];
351
+ }
352
+ curr = scanner.next();
353
+ return table;
354
+ };
355
+ const parseViewPortRecords = () => {
356
+ const viewPorts = []; // Multiple table entries may have the same name indicating a multiple viewport configuration
357
+ let viewPort = {};
358
+ log.debug('ViewPort {');
359
+ curr = scanner.next();
360
+ while (!groupIs(0, END_OF_TABLE_VALUE)) {
361
+ switch (curr.code) {
362
+ case 2: // layer name
363
+ viewPort.name = curr.value;
364
+ curr = scanner.next();
365
+ break;
366
+ case 10:
367
+ viewPort.lowerLeftCorner = parsePoint();
368
+ curr = scanner.next();
369
+ break;
370
+ case 11:
371
+ viewPort.upperRightCorner = parsePoint();
372
+ curr = scanner.next();
373
+ break;
374
+ case 12:
375
+ viewPort.center = parsePoint();
376
+ curr = scanner.next();
377
+ break;
378
+ case 13:
379
+ viewPort.snapBasePoint = parsePoint();
380
+ curr = scanner.next();
381
+ break;
382
+ case 14:
383
+ viewPort.snapSpacing = parsePoint();
384
+ curr = scanner.next();
385
+ break;
386
+ case 15:
387
+ viewPort.gridSpacing = parsePoint();
388
+ curr = scanner.next();
389
+ break;
390
+ case 16:
391
+ viewPort.viewDirectionFromTarget = parsePoint();
392
+ curr = scanner.next();
393
+ break;
394
+ case 17:
395
+ viewPort.viewTarget = parsePoint();
396
+ curr = scanner.next();
397
+ break;
398
+ case 42:
399
+ viewPort.lensLength = curr.value;
400
+ curr = scanner.next();
401
+ break;
402
+ case 43:
403
+ viewPort.frontClippingPlane = curr.value;
404
+ curr = scanner.next();
405
+ break;
406
+ case 44:
407
+ viewPort.backClippingPlane = curr.value;
408
+ curr = scanner.next();
409
+ break;
410
+ case 45:
411
+ viewPort.viewHeight = curr.value;
412
+ curr = scanner.next();
413
+ break;
414
+ case 50:
415
+ viewPort.snapRotationAngle = curr.value;
416
+ curr = scanner.next();
417
+ break;
418
+ case 51:
419
+ viewPort.viewTwistAngle = curr.value;
420
+ curr = scanner.next();
421
+ break;
422
+ case 79:
423
+ viewPort.orthographicType = curr.value;
424
+ curr = scanner.next();
425
+ break;
426
+ case 110:
427
+ viewPort.ucsOrigin = parsePoint();
428
+ curr = scanner.next();
429
+ break;
430
+ case 111:
431
+ viewPort.ucsXAxis = parsePoint();
432
+ curr = scanner.next();
433
+ break;
434
+ case 112:
435
+ viewPort.ucsYAxis = parsePoint();
436
+ curr = scanner.next();
437
+ break;
438
+ case 281:
439
+ // Note: the original parser also mapped 281 to
440
+ // defaultLightingType (0 = one distant light, 1 = two
441
+ // distant lights) in a duplicate, unreachable case.
442
+ viewPort.renderMode = curr.value;
443
+ curr = scanner.next();
444
+ break;
445
+ case 292:
446
+ viewPort.defaultLightingOn = curr.value;
447
+ curr = scanner.next();
448
+ break;
449
+ case 330:
450
+ viewPort.ownerHandle = curr.value;
451
+ curr = scanner.next();
452
+ break;
453
+ case 63: // These are all ambient color. Perhaps should be a gradient when multiple are set.
454
+ case 421:
455
+ case 431:
456
+ viewPort.ambientColor = curr.value;
457
+ curr = scanner.next();
458
+ break;
459
+ case 0:
460
+ // New ViewPort
461
+ if (curr.value === 'VPORT') {
462
+ log.debug('}');
463
+ viewPorts.push(viewPort);
464
+ log.debug('ViewPort {');
465
+ viewPort = {};
466
+ curr = scanner.next();
467
+ }
468
+ break;
469
+ default:
470
+ logUnhandledGroup(curr);
471
+ curr = scanner.next();
472
+ break;
473
+ }
474
+ }
475
+ // Note: do not call scanner.next() here,
476
+ // parseTable() needs the current group
477
+ log.debug('}');
478
+ viewPorts.push(viewPort);
479
+ return viewPorts;
480
+ };
481
+ const parseLineTypes = () => {
482
+ const ltypes = {};
483
+ let ltypeName;
484
+ let ltype = {};
485
+ let length = 0;
486
+ log.debug('LType {');
487
+ curr = scanner.next();
488
+ while (!groupIs(0, 'ENDTAB')) {
489
+ switch (curr.code) {
490
+ case 2:
491
+ ltype.name = curr.value;
492
+ ltypeName = curr.value;
493
+ curr = scanner.next();
494
+ break;
495
+ case 3:
496
+ ltype.description = curr.value;
497
+ curr = scanner.next();
498
+ break;
499
+ case 73: // Number of elements for this line type (dots, dashes, spaces);
500
+ length = curr.value;
501
+ if (length > 0)
502
+ ltype.pattern = [];
503
+ curr = scanner.next();
504
+ break;
505
+ case 40: // total pattern length
506
+ ltype.patternLength = curr.value;
507
+ curr = scanner.next();
508
+ break;
509
+ case 49:
510
+ ltype.pattern.push(curr.value);
511
+ curr = scanner.next();
512
+ break;
513
+ case 0:
514
+ log.debug('}');
515
+ if (length > 0 && length !== ltype.pattern.length) {
516
+ log.warn('lengths do not match on LTYPE pattern');
517
+ }
518
+ ltypes[ltypeName] = ltype;
519
+ ltype = {};
520
+ log.debug('LType {');
521
+ curr = scanner.next();
522
+ break;
523
+ default:
524
+ curr = scanner.next();
525
+ }
526
+ }
527
+ log.debug('}');
528
+ ltypes[ltypeName] = ltype;
529
+ return ltypes;
530
+ };
531
+ const parseLayers = () => {
532
+ const layers = {};
533
+ let layerName;
534
+ let layer = {};
535
+ log.debug('Layer {');
536
+ curr = scanner.next();
537
+ while (!groupIs(0, 'ENDTAB')) {
538
+ switch (curr.code) {
539
+ case 2: // layer name
540
+ layer.name = curr.value;
541
+ layerName = curr.value;
542
+ curr = scanner.next();
543
+ break;
544
+ case 62: // color, visibility
545
+ layer.visible = curr.value >= 0;
546
+ // TODO 0 and 256 are BYBLOCK and BYLAYER respectively. Need to handle these values for layers?.
547
+ layer.colorIndex = Math.abs(curr.value);
548
+ layer.color = getAcadColor(layer.colorIndex);
549
+ curr = scanner.next();
550
+ break;
551
+ case 70: // frozen layer
552
+ layer.frozen = ((curr.value & 1) !== 0 || (curr.value & 2) !== 0);
553
+ curr = scanner.next();
554
+ break;
555
+ case 420: // TrueColor Color
556
+ layer.color = curr.value;
557
+ curr = scanner.next();
558
+ break;
559
+ case 0:
560
+ // New Layer
561
+ if (curr.value === 'LAYER') {
562
+ log.debug('}');
563
+ layers[layerName] = layer;
564
+ log.debug('Layer {');
565
+ layer = {};
566
+ layerName = undefined;
567
+ curr = scanner.next();
568
+ }
569
+ break;
570
+ default:
571
+ logUnhandledGroup(curr);
572
+ curr = scanner.next();
573
+ break;
574
+ }
575
+ }
576
+ // Note: do not call scanner.next() here,
577
+ // parseLayerTable() needs the current group
578
+ log.debug('}');
579
+ layers[layerName] = layer;
580
+ return layers;
581
+ };
582
+ const parseDimStyles = () => {
583
+ const dimStyles = {};
584
+ let styleName;
585
+ let style = {};
586
+ log.debug('DimStyle {');
587
+ curr = scanner.next();
588
+ while (!groupIs(0, 'ENDTAB')) {
589
+ if (dimStyleCodes.has(curr.code)) {
590
+ style[dimStyleCodes.get(curr.code)] = curr.value;
591
+ curr = scanner.next();
592
+ }
593
+ else {
594
+ switch (curr.code) {
595
+ case 2: // style name
596
+ style.name = curr.value;
597
+ styleName = curr.value;
598
+ curr = scanner.next();
599
+ break;
600
+ case 0:
601
+ // New style
602
+ if (curr.value === 'DIMSTYLE') {
603
+ log.debug('}');
604
+ dimStyles[styleName] = style;
605
+ log.debug('DimStyle {');
606
+ style = {};
607
+ styleName = undefined;
608
+ curr = scanner.next();
609
+ }
610
+ break;
611
+ default:
612
+ logUnhandledGroup(curr);
613
+ curr = scanner.next();
614
+ break;
615
+ }
616
+ }
617
+ }
618
+ // Note: do not call scanner.next() here,
619
+ // parseLayerTable() needs the current group
620
+ log.debug('}');
621
+ dimStyles[styleName] = style;
622
+ return dimStyles;
623
+ };
624
+ const parseStyles = () => {
625
+ const styles = {};
626
+ let style = {};
627
+ let styleName;
628
+ log.debug('Style {');
629
+ curr = scanner.next();
630
+ while (!groupIs(0, END_OF_TABLE_VALUE)) {
631
+ switch (curr.code) {
632
+ case 100:
633
+ style.subClassMarker = curr.value;
634
+ curr = scanner.next();
635
+ break;
636
+ case 2:
637
+ style.styleName = curr.value;
638
+ styleName = curr.value;
639
+ curr = scanner.next();
640
+ break;
641
+ case 70:
642
+ style.standardFlag = curr.value;
643
+ curr = scanner.next();
644
+ break;
645
+ case 40:
646
+ style.fixedTextHeight = curr.value;
647
+ curr = scanner.next();
648
+ break;
649
+ case 41:
650
+ style.widthFactor = curr.value;
651
+ curr = scanner.next();
652
+ break;
653
+ case 50:
654
+ style.obliqueAngle = curr.value;
655
+ curr = scanner.next();
656
+ break;
657
+ case 71:
658
+ style.textGenerationFlag = curr.value;
659
+ curr = scanner.next();
660
+ break;
661
+ case 42:
662
+ style.lastHeight = curr.value;
663
+ curr = scanner.next();
664
+ break;
665
+ case 3:
666
+ style.font = curr.value;
667
+ curr = scanner.next();
668
+ break;
669
+ case 4:
670
+ style.bigFont = curr.value;
671
+ curr = scanner.next();
672
+ break;
673
+ case 1071:
674
+ style.extendedFont = curr.value;
675
+ curr = scanner.next();
676
+ break;
677
+ case 0:
678
+ if (curr.value === 'STYLE') {
679
+ log.debug('}');
680
+ styles[styleName] = style;
681
+ log.debug('Style {');
682
+ style = {};
683
+ styleName = undefined;
684
+ curr = scanner.next();
685
+ }
686
+ break;
687
+ default:
688
+ logUnhandledGroup(curr);
689
+ curr = scanner.next();
690
+ break;
691
+ }
692
+ }
693
+ log.debug('}');
694
+ styles[styleName] = style;
695
+ return styles;
696
+ };
697
+ const tableDefinitions = {
698
+ VPORT: {
699
+ tableRecordsProperty: 'viewPorts',
700
+ tableName: 'viewPort',
701
+ dxfSymbolName: 'VPORT',
702
+ parseTableRecords: parseViewPortRecords,
703
+ },
704
+ LTYPE: {
705
+ tableRecordsProperty: 'lineTypes',
706
+ tableName: 'lineType',
707
+ dxfSymbolName: 'LTYPE',
708
+ parseTableRecords: parseLineTypes,
709
+ },
710
+ LAYER: {
711
+ tableRecordsProperty: 'layers',
712
+ tableName: 'layer',
713
+ dxfSymbolName: 'LAYER',
714
+ parseTableRecords: parseLayers,
715
+ },
716
+ DIMSTYLE: {
717
+ tableRecordsProperty: 'dimStyles',
718
+ tableName: 'dimstyle',
719
+ dxfSymbolName: 'DIMSTYLE',
720
+ parseTableRecords: parseDimStyles,
721
+ },
722
+ STYLE: {
723
+ tableRecordsProperty: 'styles',
724
+ tableName: 'style',
725
+ dxfSymbolName: 'STYLE',
726
+ parseTableRecords: parseStyles,
727
+ },
728
+ };
729
+ /**
730
+ * Is called after the parser first reads the 0:ENTITIES group. The scanner
731
+ * should be on the start of the first entity already.
732
+ * @return the resulting entities
733
+ */
734
+ const parseEntities = (forBlock) => {
735
+ const entities = [];
736
+ const endingOnValue = forBlock ? 'ENDBLK' : 'ENDSEC';
737
+ if (!forBlock) {
738
+ curr = scanner.next();
739
+ }
740
+ while (true) {
741
+ if (curr.code === 0) {
742
+ if (curr.value === endingOnValue) {
743
+ break;
744
+ }
745
+ const handler = self._entityHandlers[curr.value];
746
+ if (handler != null) {
747
+ log.debug(curr.value + ' {');
748
+ const entity = handler.parseEntity(scanner, curr);
749
+ curr = scanner.lastReadGroup;
750
+ log.debug('}');
751
+ ensureHandle(entity);
752
+ entities.push(entity);
753
+ }
754
+ else {
755
+ log.warn('Unhandled entity ' + curr.value);
756
+ curr = scanner.next();
757
+ continue;
758
+ }
759
+ }
760
+ else {
761
+ // ignored lines from unsupported entity
762
+ curr = scanner.next();
763
+ }
764
+ }
765
+ if (endingOnValue == 'ENDSEC') {
766
+ curr = scanner.next(); // swallow up ENDSEC, but not ENDBLK
767
+ }
768
+ return entities;
769
+ };
770
+ /**
771
+ * Parses a 2D or 3D point, returning it as an object with x, y, and
772
+ * (sometimes) z property if it is 3D. It is assumed the current group
773
+ * is x of the point being read in, and scanner.next() will return the
774
+ * y. The parser will determine if there is a z point automatically.
775
+ * @return The 2D or 3D point as an object with x, y[, z]
776
+ */
777
+ const parsePoint = () => {
778
+ const point = {};
779
+ let code = curr.code;
780
+ point.x = curr.value;
781
+ code += 10;
782
+ curr = scanner.next();
783
+ if (curr.code != code)
784
+ throw new Error('Expected code for point value to be ' + code +
785
+ ' but got ' + curr.code + '.');
786
+ point.y = curr.value;
787
+ code += 10;
788
+ curr = scanner.next();
789
+ if (curr.code != code) {
790
+ scanner.rewind();
791
+ return point;
792
+ }
793
+ point.z = curr.value;
794
+ return point;
795
+ };
796
+ const ensureHandle = (entity) => {
797
+ if (!entity) {
798
+ throw new TypeError('entity cannot be undefined or null');
799
+ }
800
+ if (!entity.handle) {
801
+ entity.handle = lastHandle++;
802
+ }
803
+ };
804
+ parseAll();
805
+ return dxf;
806
+ }
807
+ }
808
+ function logUnhandledGroup(curr) {
809
+ log.debug('unhandled group ' + debugCode(curr));
810
+ }
811
+ function debugCode(curr) {
812
+ return curr.code + ':' + curr.value;
813
+ }
814
+ /**
815
+ * Returns the truecolor value of the given AutoCad color index value
816
+ * @return truecolor value as a number
817
+ */
818
+ function getAcadColor(index) {
819
+ return AUTO_CAD_COLOR_INDEX[index];
820
+ }
821
+ /* Notes */
822
+ // Code 6 of an entity indicates inheritance of properties (eg. color).
823
+ // BYBLOCK means inherits from block
824
+ // BYLAYER (default) mean inherits from layer