@linkiez/dxf-renew 5.3.1 → 7.1.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 (133) hide show
  1. package/{docs/CODE_PATTERNS.md → .github/instructions/code-patterns.instructions.md} +4 -1
  2. package/.github/instructions/exdxf.instruction.md +161 -0
  3. package/.github/instructions/tdd.instructions.md +271 -0
  4. package/.github/workflows/release.yml +4 -5
  5. package/.releaserc.json +1 -1
  6. package/.yarn/install-state.gz +0 -0
  7. package/CHANGELOG.md +59 -0
  8. package/CONTRIBUTING.md +16 -14
  9. package/PLAN.md +34 -84
  10. package/README.md +43 -8
  11. package/dist/dxf.js +1388 -376
  12. package/docs/DIMENSION_SUMMARY.md +11 -5
  13. package/docs/DXF_VERSION_SUPPORT.md +45 -0
  14. package/docs/ENTITY_SVG_ROADMAP.md +96 -0
  15. package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
  16. package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
  17. package/docs/README.md +22 -0
  18. package/docs/SVG_RENDERING_INTEGRATION_TESTS.md +119 -0
  19. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +1 -1
  20. package/lib/Helper.cjs +2 -2
  21. package/lib/Helper.cjs.map +2 -2
  22. package/lib/Helper.js +2 -2
  23. package/lib/Helper.js.map +2 -2
  24. package/lib/denormalise.cjs +131 -91
  25. package/lib/denormalise.cjs.map +2 -2
  26. package/lib/denormalise.js +131 -91
  27. package/lib/denormalise.js.map +2 -2
  28. package/lib/dimensionToSVG.cjs +318 -53
  29. package/lib/dimensionToSVG.cjs.map +3 -3
  30. package/lib/dimensionToSVG.js +316 -52
  31. package/lib/dimensionToSVG.js.map +2 -2
  32. package/lib/handlers/entities.cjs +90 -26
  33. package/lib/handlers/entities.cjs.map +3 -3
  34. package/lib/handlers/entities.js +90 -26
  35. package/lib/handlers/entities.js.map +3 -3
  36. package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
  37. package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
  38. package/lib/handlers/entity/dgnUnderlay.js +71 -0
  39. package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
  40. package/lib/handlers/entity/dimension.cjs +24 -0
  41. package/lib/handlers/entity/dimension.cjs.map +2 -2
  42. package/lib/handlers/entity/dimension.js +24 -0
  43. package/lib/handlers/entity/dimension.js.map +2 -2
  44. package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
  45. package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
  46. package/lib/handlers/entity/dwfUnderlay.js +71 -0
  47. package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
  48. package/lib/handlers/entity/image.cjs +123 -0
  49. package/lib/handlers/entity/image.cjs.map +7 -0
  50. package/lib/handlers/entity/image.js +88 -0
  51. package/lib/handlers/entity/image.js.map +7 -0
  52. package/lib/handlers/entity/leader.cjs +148 -0
  53. package/lib/handlers/entity/leader.cjs.map +7 -0
  54. package/lib/handlers/entity/leader.js +113 -0
  55. package/lib/handlers/entity/leader.js.map +7 -0
  56. package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
  57. package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
  58. package/lib/handlers/entity/pdfUnderlay.js +71 -0
  59. package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
  60. package/lib/handlers/entity/tolerance.cjs +90 -0
  61. package/lib/handlers/entity/tolerance.cjs.map +7 -0
  62. package/lib/handlers/entity/tolerance.js +55 -0
  63. package/lib/handlers/entity/tolerance.js.map +7 -0
  64. package/lib/handlers/objects.cjs +257 -136
  65. package/lib/handlers/objects.cjs.map +2 -2
  66. package/lib/handlers/objects.js +257 -136
  67. package/lib/handlers/objects.js.map +2 -2
  68. package/lib/toSVG.cjs +71 -8
  69. package/lib/toSVG.cjs.map +3 -3
  70. package/lib/toSVG.js +72 -9
  71. package/lib/toSVG.js.map +2 -2
  72. package/lib/types/dimension-entity.cjs.map +1 -1
  73. package/lib/types/entity.cjs.map +1 -1
  74. package/lib/types/image-entity.cjs +17 -0
  75. package/lib/types/image-entity.cjs.map +7 -0
  76. package/lib/types/image-entity.js +1 -0
  77. package/lib/types/image-entity.js.map +7 -0
  78. package/lib/types/index.cjs +8 -0
  79. package/lib/types/index.cjs.map +2 -2
  80. package/lib/types/index.js +4 -0
  81. package/lib/types/index.js.map +2 -2
  82. package/lib/types/leader-entity.cjs +17 -0
  83. package/lib/types/leader-entity.cjs.map +7 -0
  84. package/lib/types/leader-entity.js +1 -0
  85. package/lib/types/leader-entity.js.map +7 -0
  86. package/lib/types/options.cjs.map +1 -1
  87. package/lib/types/tables.cjs.map +1 -1
  88. package/lib/types/tolerance-entity.cjs +17 -0
  89. package/lib/types/tolerance-entity.cjs.map +7 -0
  90. package/lib/types/tolerance-entity.js +1 -0
  91. package/lib/types/tolerance-entity.js.map +7 -0
  92. package/lib/types/underlay-entity.cjs +17 -0
  93. package/lib/types/underlay-entity.cjs.map +7 -0
  94. package/lib/types/underlay-entity.js +1 -0
  95. package/lib/types/underlay-entity.js.map +7 -0
  96. package/lib/util/escapeXmlText.cjs +27 -0
  97. package/lib/util/escapeXmlText.cjs.map +7 -0
  98. package/lib/util/escapeXmlText.js +7 -0
  99. package/lib/util/escapeXmlText.js.map +7 -0
  100. package/package.json +13 -4
  101. package/playwright.config.cjs +20 -0
  102. package/src/Helper.ts +3 -3
  103. package/src/denormalise.ts +182 -116
  104. package/src/dimensionToSVG.ts +466 -54
  105. package/src/handlers/entities.ts +109 -34
  106. package/src/handlers/entity/dgnUnderlay.ts +94 -0
  107. package/src/handlers/entity/dimension.ts +27 -1
  108. package/src/handlers/entity/dwfUnderlay.ts +94 -0
  109. package/src/handlers/entity/image.ts +118 -0
  110. package/src/handlers/entity/leader.ts +153 -0
  111. package/src/handlers/entity/pdfUnderlay.ts +94 -0
  112. package/src/handlers/entity/tolerance.ts +75 -0
  113. package/src/handlers/objects.ts +323 -139
  114. package/src/toSVG.ts +98 -7
  115. package/src/types/dimension-entity.ts +11 -0
  116. package/src/types/entity.ts +10 -0
  117. package/src/types/image-entity.ts +35 -0
  118. package/src/types/index.ts +4 -0
  119. package/src/types/leader-entity.ts +40 -0
  120. package/src/types/options.ts +41 -0
  121. package/src/types/tables.ts +84 -0
  122. package/src/types/tolerance-entity.ts +20 -0
  123. package/src/types/underlay-entity.ts +35 -0
  124. package/src/util/escapeXmlText.ts +10 -0
  125. package/tools/browser_test_server.cjs +87 -0
  126. package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
  127. package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
  128. package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
  129. package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
  130. package/tools/ezdxf_validate_fixtures.py +165 -0
  131. package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
  132. package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
  133. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
@@ -0,0 +1,94 @@
1
+ import type { DXFTuple } from '../../types'
2
+
3
+ import common from './common'
4
+
5
+ export const TYPE = 'PDFUNDERLAY'
6
+
7
+ interface UnderlayEntity {
8
+ type: typeof TYPE
9
+
10
+ insertionPoint: { x: number; y: number; z: number }
11
+ scale: { x: number; y: number; z: number }
12
+ normal: { x: number; y: number; z: number }
13
+
14
+ underlayDefinitionHandle?: string
15
+ rotation?: number
16
+ flags?: number
17
+ contrast?: number
18
+ fade?: number
19
+
20
+ [key: string]: unknown
21
+ }
22
+
23
+ export const process = (tuples: DXFTuple[]): UnderlayEntity => {
24
+ return tuples.reduce(
25
+ (entity, tuple) => {
26
+ const type = tuple[0]
27
+ const value = tuple[1]
28
+
29
+ switch (type) {
30
+ case 340:
31
+ entity.underlayDefinitionHandle = String(value)
32
+ break
33
+
34
+ case 10:
35
+ entity.insertionPoint.x = value as number
36
+ break
37
+ case 20:
38
+ entity.insertionPoint.y = value as number
39
+ break
40
+ case 30:
41
+ entity.insertionPoint.z = value as number
42
+ break
43
+
44
+ case 41:
45
+ entity.scale.x = value as number
46
+ break
47
+ case 42:
48
+ entity.scale.y = value as number
49
+ break
50
+ case 43:
51
+ entity.scale.z = value as number
52
+ break
53
+
54
+ case 50:
55
+ entity.rotation = value as number
56
+ break
57
+
58
+ case 210:
59
+ entity.normal.x = value as number
60
+ break
61
+ case 220:
62
+ entity.normal.y = value as number
63
+ break
64
+ case 230:
65
+ entity.normal.z = value as number
66
+ break
67
+
68
+ case 280:
69
+ entity.flags = value as number
70
+ break
71
+ case 281:
72
+ entity.contrast = value as number
73
+ break
74
+ case 282:
75
+ entity.fade = value as number
76
+ break
77
+
78
+ default:
79
+ Object.assign(entity, common(type, value))
80
+ break
81
+ }
82
+
83
+ return entity
84
+ },
85
+ {
86
+ type: TYPE,
87
+ insertionPoint: { x: 0, y: 0, z: 0 },
88
+ scale: { x: 1, y: 1, z: 1 },
89
+ normal: { x: 0, y: 0, z: 1 },
90
+ } as UnderlayEntity,
91
+ )
92
+ }
93
+
94
+ export default { TYPE, process }
@@ -0,0 +1,75 @@
1
+ import type { DXFTuple } from '../../types'
2
+
3
+ import common from './common'
4
+
5
+ export const TYPE = 'TOLERANCE'
6
+
7
+ interface ToleranceEntity {
8
+ type: typeof TYPE
9
+
10
+ insertionPoint: { x: number; y: number; z: number }
11
+ text?: string
12
+ dimensionStyleName?: string
13
+ xAxisDirection?: { x: number; y: number; z: number }
14
+
15
+ [key: string]: unknown
16
+ }
17
+
18
+ function ensureVector3(
19
+ entity: ToleranceEntity,
20
+ key: 'insertionPoint' | 'xAxisDirection',
21
+ ): { x: number; y: number; z: number } {
22
+ entity[key] ??= { x: 0, y: 0, z: 0 }
23
+ return entity[key] as { x: number; y: number; z: number }
24
+ }
25
+
26
+ export const process = (tuples: DXFTuple[]): ToleranceEntity => {
27
+ return tuples.reduce(
28
+ (entity, tuple) => {
29
+ const code = tuple[0]
30
+ const value = tuple[1]
31
+
32
+ switch (code) {
33
+ case 3:
34
+ entity.dimensionStyleName = String(value)
35
+ break
36
+
37
+ case 10:
38
+ ensureVector3(entity, 'insertionPoint').x = value as number
39
+ break
40
+ case 20:
41
+ ensureVector3(entity, 'insertionPoint').y = value as number
42
+ break
43
+ case 30:
44
+ ensureVector3(entity, 'insertionPoint').z = value as number
45
+ break
46
+
47
+ case 1:
48
+ entity.text = String(value)
49
+ break
50
+
51
+ case 11:
52
+ ensureVector3(entity, 'xAxisDirection').x = value as number
53
+ break
54
+ case 21:
55
+ ensureVector3(entity, 'xAxisDirection').y = value as number
56
+ break
57
+ case 31:
58
+ ensureVector3(entity, 'xAxisDirection').z = value as number
59
+ break
60
+
61
+ default:
62
+ Object.assign(entity, common(code, value))
63
+ break
64
+ }
65
+
66
+ return entity
67
+ },
68
+ {
69
+ type: TYPE,
70
+ insertionPoint: { x: 0, y: 0, z: 0 },
71
+ } as ToleranceEntity,
72
+ )
73
+ }
74
+
75
+ export default { TYPE, process }
@@ -1,152 +1,336 @@
1
- import type { DXFTuple, LayoutInternal, ParsedObjects } from '../types'
1
+ import type {
2
+ DictionaryObject,
3
+ DXFTuple,
4
+ ImageDefObject,
5
+ ImageDefReactorObject,
6
+ LayoutInternal,
7
+ ParsedObjects,
8
+ UnderlayDefinitionObject,
9
+ XRecordObject,
10
+ } from '../types'
2
11
 
3
- export default function parseObjects(tuples: DXFTuple[]): ParsedObjects {
4
- let state: 'IDLE' | 'layout' | 'AcDbLayout' | undefined
5
- const objects: ParsedObjects = {
6
- layouts: [],
7
- }
8
- let layout: LayoutInternal | undefined
12
+ type ObjectGroup = DXFTuple[]
13
+
14
+ function groupObjectsByZero(tuples: DXFTuple[]): ObjectGroup[] {
15
+ const groups: ObjectGroup[] = []
16
+ let current: ObjectGroup | undefined
9
17
 
10
18
  for (const tuple of tuples) {
19
+ const code = tuple[0]
20
+ if (code === 0) {
21
+ if (current && current.length > 0) groups.push(current)
22
+ current = [tuple]
23
+ continue
24
+ }
25
+ if (!current) continue
26
+ current.push(tuple)
27
+ }
28
+
29
+ if (current && current.length > 0) groups.push(current)
30
+ return groups
31
+ }
32
+
33
+ const LAYOUT_FLOAT_FIELDS: Record<number, keyof LayoutInternal> = {
34
+ 10: 'minLimitX',
35
+ 20: 'minLimitY',
36
+ 11: 'maxLimitX',
37
+ 21: 'maxLimitY',
38
+ 12: 'x',
39
+ 22: 'y',
40
+ 32: 'z',
41
+ 14: 'minX',
42
+ 24: 'minY',
43
+ 34: 'minZ',
44
+ 15: 'maxX',
45
+ 25: 'maxY',
46
+ 35: 'maxZ',
47
+ 146: 'elevation',
48
+ 13: 'ucsX',
49
+ 23: 'ucsY',
50
+ 33: 'ucsZ',
51
+ 16: 'ucsXaxisX',
52
+ 26: 'ucsXaxisY',
53
+ 36: 'ucsXaxisZ',
54
+ 17: 'ucsYaxisX',
55
+ 27: 'ucsYaxisY',
56
+ 37: 'ucsYaxisZ',
57
+ }
58
+
59
+ const LAYOUT_DIRECT_FIELDS: Record<number, keyof LayoutInternal> = {
60
+ 1: 'name',
61
+ 5: 'handle',
62
+ 71: 'tabOrder',
63
+ 330: 'tableRecord',
64
+ 331: 'lastActiveViewport',
65
+ 333: 'shadePlot',
66
+ }
67
+
68
+ const LAYOUT_UCS_TYPE: Record<number, LayoutInternal['ucsType']> = {
69
+ 0: 'NOT ORTHOGRAPHIC',
70
+ 1: 'TOP',
71
+ 2: 'BOTTOM',
72
+ 3: 'FRONT',
73
+ 4: 'BACK',
74
+ 5: 'LEFT',
75
+ 6: 'RIGHT',
76
+ }
77
+
78
+ type LayoutParseState = {
79
+ state: 'IDLE' | 'layout' | 'AcDbLayout'
80
+ layout?: LayoutInternal
81
+ }
82
+
83
+ function consumeLayoutStartTuple(current: LayoutParseState, tuple: DXFTuple): LayoutParseState {
84
+ if (tuple[1] === 'LAYOUT') return { state: 'layout', layout: {} }
85
+ return { state: 'IDLE', layout: current.layout }
86
+ }
87
+
88
+ function consumeLayoutSubclassTuple(current: LayoutParseState, tuple: DXFTuple): LayoutParseState {
89
+ if (tuple[0] === 100 && tuple[1] === 'AcDbLayout') return { state: 'AcDbLayout', layout: current.layout }
90
+ return current
91
+ }
92
+
93
+ function applyLayoutFieldTuple(layout: LayoutInternal, tuple: DXFTuple): void {
94
+ const type = tuple[0]
95
+ const value = tuple[1]
96
+
97
+ const floatKey = LAYOUT_FLOAT_FIELDS[type]
98
+ if (floatKey) {
99
+ ;(layout as any)[floatKey] = Number.parseFloat(String(value))
100
+ return
101
+ }
102
+
103
+ const directKey = LAYOUT_DIRECT_FIELDS[type]
104
+ if (directKey) {
105
+ ;(layout as any)[directKey] = value
106
+ return
107
+ }
108
+
109
+ if (type === 70) {
110
+ layout.flag = value === 1 ? 'PSLTSCALE' : 'LIMCHECK'
111
+ return
112
+ }
113
+
114
+ if (type === 76) {
115
+ const ucsType = LAYOUT_UCS_TYPE[Number(value)]
116
+ if (ucsType) layout.ucsType = ucsType
117
+ }
118
+ }
119
+
120
+ function consumeLayoutTuple(current: LayoutParseState, tuple: DXFTuple): LayoutParseState {
121
+ if (tuple[0] === 0) return consumeLayoutStartTuple(current, tuple)
122
+
123
+ if (current.state === 'layout') return consumeLayoutSubclassTuple(current, tuple)
124
+
125
+ if (current.state === 'AcDbLayout' && current.layout) applyLayoutFieldTuple(current.layout, tuple)
126
+ return current
127
+ }
128
+
129
+ function parseLayoutObject(group: ObjectGroup): LayoutInternal | undefined {
130
+ let current: LayoutParseState = { state: 'IDLE' }
131
+ for (const tuple of group) current = consumeLayoutTuple(current, tuple)
132
+ return current.layout
133
+ }
134
+
135
+ function parseDictionaryObject(group: ObjectGroup): DictionaryObject | undefined {
136
+ if (group[0]?.[1] !== 'DICTIONARY') return undefined
137
+
138
+ const dict: DictionaryObject = {
139
+ type: 'DICTIONARY',
140
+ entries: {},
141
+ }
142
+
143
+ let pendingKey: string | undefined
144
+ for (const tuple of group.slice(1)) {
11
145
  const type = tuple[0]
12
146
  const value = tuple[1]
13
- if (type === 0) {
14
- state = 'IDLE'
147
+
148
+ if (type === 5) {
149
+ dict.handle = value
150
+ continue
15
151
  }
16
- if (value === 'LAYOUT') {
17
- state = 'layout'
18
- layout = {}
19
- objects.layouts.push(layout)
152
+ if (type === 330) {
153
+ dict.ownerHandle = value
154
+ continue
20
155
  }
21
- if (state === 'layout' && type !== 0) {
22
- // wait until AcDbLayout shows up
23
- switch (type) {
24
- case 100:
25
- if (value === 'AcDbLayout') state = 'AcDbLayout'
26
- break
27
- }
156
+ if (type === 3) {
157
+ pendingKey = String(value)
158
+ continue
28
159
  }
29
- if (state === 'AcDbLayout' && type !== 0 && layout) {
30
- // save layout attributes
31
- switch (type) {
32
- case 1:
33
- layout.name = value
34
- break
35
- case 5:
36
- layout.handle = value
37
- break
38
- case 10:
39
- layout.minLimitX = Number.parseFloat(String(value))
40
- break
41
- case 20:
42
- layout.minLimitY = Number.parseFloat(String(value))
43
- break
44
- case 11:
45
- layout.maxLimitX = Number.parseFloat(String(value))
46
- break
47
- case 21:
48
- layout.maxLimitY = Number.parseFloat(String(value))
49
- break
50
- case 12:
51
- layout.x = Number.parseFloat(String(value))
52
- break
53
- case 22:
54
- layout.y = Number.parseFloat(String(value))
55
- break
56
- case 32:
57
- layout.z = Number.parseFloat(String(value))
58
- break
59
- case 14:
60
- layout.minX = Number.parseFloat(String(value))
61
- break
62
- case 24:
63
- layout.minY = Number.parseFloat(String(value))
64
- break
65
- case 34:
66
- layout.minZ = Number.parseFloat(String(value))
67
- break
68
- case 15:
69
- layout.maxX = Number.parseFloat(String(value))
70
- break
71
- case 25:
72
- layout.maxY = Number.parseFloat(String(value))
73
- break
74
- case 35:
75
- layout.maxZ = Number.parseFloat(String(value))
76
- break
77
- case 70:
78
- layout.flag = value === 1 ? 'PSLTSCALE' : 'LIMCHECK'
79
- break
80
- case 71:
81
- layout.tabOrder = value
82
- break
83
- case 146:
84
- layout.elevation = Number.parseFloat(String(value))
85
- break
86
- case 13:
87
- layout.ucsX = Number.parseFloat(String(value))
88
- break
89
- case 23:
90
- layout.ucsY = Number.parseFloat(String(value))
91
- break
92
- case 33:
93
- layout.ucsZ = Number.parseFloat(String(value))
94
- break
95
- case 16:
96
- layout.ucsXaxisX = Number.parseFloat(String(value))
97
- break
98
- case 26:
99
- layout.ucsXaxisY = Number.parseFloat(String(value))
100
- break
101
- case 36:
102
- layout.ucsXaxisZ = Number.parseFloat(String(value))
103
- break
104
- case 17:
105
- layout.ucsYaxisX = Number.parseFloat(String(value))
106
- break
107
- case 27:
108
- layout.ucsYaxisY = Number.parseFloat(String(value))
109
- break
110
- case 37:
111
- layout.ucsYaxisZ = Number.parseFloat(String(value))
112
- break
113
- case 76:
114
- switch (value) {
115
- case 0:
116
- layout.ucsType = 'NOT ORTHOGRAPHIC'
117
- break
118
- case 1:
119
- layout.ucsType = 'TOP'
120
- break
121
- case 2:
122
- layout.ucsType = 'BOTTOM'
123
- break
124
- case 3:
125
- layout.ucsType = 'FRONT'
126
- break
127
- case 4:
128
- layout.ucsType = 'BACK'
129
- break
130
- case 5:
131
- layout.ucsType = 'LEFT'
132
- break
133
- case 6:
134
- layout.ucsType = 'RIGHT'
135
- break
136
- }
137
- break
138
- case 330:
139
- layout.tableRecord = value
140
- break
141
- case 331:
142
- layout.lastActiveViewport = value
143
- break
144
- case 333:
145
- layout.shadePlot = value
146
- break
147
- }
160
+ if ((type === 350 || type === 360) && pendingKey) {
161
+ dict.entries[pendingKey] = String(value)
162
+ pendingKey = undefined
148
163
  }
149
164
  }
150
165
 
166
+ return dict
167
+ }
168
+
169
+ function parseXRecordObject(group: ObjectGroup): XRecordObject | undefined {
170
+ if (group[0]?.[1] !== 'XRECORD') return undefined
171
+
172
+ const tuples = group.slice(1)
173
+
174
+ const xRecord: XRecordObject = {
175
+ type: 'XRECORD',
176
+ tuples,
177
+ }
178
+
179
+ for (const tuple of tuples) {
180
+ const type = tuple[0]
181
+ const value = tuple[1]
182
+
183
+ if (type === 5) xRecord.handle = value
184
+ if (type === 330) xRecord.ownerHandle = value
185
+ }
186
+
187
+ return xRecord
188
+ }
189
+
190
+ function parseImageDefObject(group: ObjectGroup): ImageDefObject | undefined {
191
+ if (group[0]?.[1] !== 'IMAGEDEF') return undefined
192
+
193
+ const tuples = group.slice(1)
194
+
195
+ const imageDef: ImageDefObject = {
196
+ type: 'IMAGEDEF',
197
+ tuples,
198
+ }
199
+
200
+ for (const tuple of tuples) {
201
+ const type = tuple[0]
202
+ const value = tuple[1]
203
+
204
+ if (type === 5) imageDef.handle = value
205
+ if (type === 330 && imageDef.ownerHandle === undefined) imageDef.ownerHandle = value
206
+ if (type === 1) imageDef.fileName = String(value)
207
+ if (type === 10) imageDef.pixelSizeX = Number(value)
208
+ if (type === 20) imageDef.pixelSizeY = Number(value)
209
+ }
210
+
211
+ return imageDef
212
+ }
213
+
214
+ function parseImageDefReactorObject(group: ObjectGroup): ImageDefReactorObject | undefined {
215
+ if (group[0]?.[1] !== 'IMAGEDEF_REACTOR') return undefined
216
+
217
+ const tuples = group.slice(1)
218
+
219
+ const reactor: ImageDefReactorObject = {
220
+ type: 'IMAGEDEF_REACTOR',
221
+ tuples,
222
+ }
223
+
224
+ for (const tuple of tuples) {
225
+ const type = tuple[0]
226
+ const value = tuple[1]
227
+
228
+ if (type === 5) reactor.handle = value
229
+ if (type === 330) reactor.imageHandle = value
230
+ }
231
+
232
+ return reactor
233
+ }
234
+
235
+ const UNDERLAY_DEFINITION_OBJECT_TYPES = new Set([
236
+ 'UNDERLAYDEFINITION',
237
+ 'PDFDEFINITION',
238
+ 'DWFDEFINITION',
239
+ 'DGNDEFINITION',
240
+ ])
241
+
242
+ function parseUnderlayDefinitionObject(group: ObjectGroup): UnderlayDefinitionObject | undefined {
243
+ const objectType = group[0]?.[1]
244
+ if (typeof objectType !== 'string') return undefined
245
+ if (!UNDERLAY_DEFINITION_OBJECT_TYPES.has(objectType)) return undefined
246
+
247
+ const tuples = group.slice(1)
248
+
249
+ const def: UnderlayDefinitionObject = {
250
+ type: objectType as UnderlayDefinitionObject['type'],
251
+ tuples,
252
+ }
253
+
254
+ for (const tuple of tuples) {
255
+ const type = tuple[0]
256
+ const value = tuple[1]
257
+
258
+ if (type === 5) def.handle = value
259
+ if (type === 330 && def.ownerHandle === undefined) def.ownerHandle = value
260
+ if (type === 1) def.fileName = String(value)
261
+ if (type === 2) def.underlayName = String(value)
262
+ }
263
+
264
+ return def
265
+ }
266
+
267
+ type ObjectGroupHandler = (objects: ParsedObjects, group: ObjectGroup) => void
268
+
269
+ const OBJECT_GROUP_HANDLERS: Record<string, ObjectGroupHandler> = {
270
+ LAYOUT: (objects, group) => {
271
+ const layout = parseLayoutObject(group)
272
+ if (layout) objects.layouts.push(layout)
273
+ },
274
+ DICTIONARY: (objects, group) => {
275
+ const dict = parseDictionaryObject(group)
276
+ const handle = dict?.handle ? String(dict.handle) : undefined
277
+ if (dict && handle) objects.dictionaries![handle] = dict
278
+ },
279
+ XRECORD: (objects, group) => {
280
+ const xRecord = parseXRecordObject(group)
281
+ const handle = xRecord?.handle ? String(xRecord.handle) : undefined
282
+ if (xRecord && handle) objects.xRecords![handle] = xRecord
283
+ },
284
+ IMAGEDEF: (objects, group) => {
285
+ const imageDef = parseImageDefObject(group)
286
+ const handle = imageDef?.handle ? String(imageDef.handle) : undefined
287
+ if (imageDef && handle) objects.imageDefs![handle] = imageDef
288
+ },
289
+ IMAGEDEF_REACTOR: (objects, group) => {
290
+ const reactor = parseImageDefReactorObject(group)
291
+ const handle = reactor?.handle ? String(reactor.handle) : undefined
292
+ if (reactor && handle) objects.imageDefReactors![handle] = reactor
293
+ },
294
+ UNDERLAYDEFINITION: (objects, group) => {
295
+ const def = parseUnderlayDefinitionObject(group)
296
+ const handle = def?.handle ? String(def.handle) : undefined
297
+ if (def && handle) objects.underlayDefinitions![handle] = def
298
+ },
299
+ PDFDEFINITION: (objects, group) => {
300
+ const def = parseUnderlayDefinitionObject(group)
301
+ const handle = def?.handle ? String(def.handle) : undefined
302
+ if (def && handle) objects.underlayDefinitions![handle] = def
303
+ },
304
+ DWFDEFINITION: (objects, group) => {
305
+ const def = parseUnderlayDefinitionObject(group)
306
+ const handle = def?.handle ? String(def.handle) : undefined
307
+ if (def && handle) objects.underlayDefinitions![handle] = def
308
+ },
309
+ DGNDEFINITION: (objects, group) => {
310
+ const def = parseUnderlayDefinitionObject(group)
311
+ const handle = def?.handle ? String(def.handle) : undefined
312
+ if (def && handle) objects.underlayDefinitions![handle] = def
313
+ },
314
+ }
315
+
316
+ export default function parseObjects(tuples: DXFTuple[]): ParsedObjects {
317
+ const objects: ParsedObjects = {
318
+ layouts: [],
319
+ dictionaries: {},
320
+ xRecords: {},
321
+ imageDefs: {},
322
+ imageDefReactors: {},
323
+ underlayDefinitions: {},
324
+ }
325
+
326
+ const groups = groupObjectsByZero(tuples)
327
+ for (const group of groups) {
328
+ const objectType = group[0]?.[1]
329
+ if (typeof objectType !== 'string') continue
330
+
331
+ const handler = OBJECT_GROUP_HANDLERS[objectType]
332
+ if (handler) handler(objects, group)
333
+ }
334
+
151
335
  return objects
152
336
  }