lineadraw 0.7.0 → 0.9.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.
@@ -2,21 +2,7 @@ import {
2
2
  __export
3
3
  } from "./chunk-PZ5AY32C.js";
4
4
 
5
- // ../core/src/export/dxf/exportDxf.ts
6
- import {
7
- DxfWriter,
8
- Units,
9
- LayerFlags,
10
- LWPolylineFlags,
11
- TextHorizontalAlignment,
12
- TextVerticalAlignment,
13
- HatchBoundaryPaths,
14
- HatchPolylineBoundary,
15
- HatchPredefinedPatterns,
16
- pattern
17
- } from "@tarikjabiri/dxf";
18
-
19
- // ../core/src/model/objects.ts
5
+ // ../core/src/model/hatchPatterns.ts
20
6
  import { z as z2 } from "zod";
21
7
 
22
8
  // ../core/src/model/common.ts
@@ -40,6 +26,716 @@ var ObjectBaseSchema = z.object({
40
26
  opacity: z.number().min(0).max(1).optional()
41
27
  });
42
28
 
29
+ // ../core/src/paint/sink.ts
30
+ var matIdentity = [1, 0, 0, 1, 0, 0];
31
+ var matMultiply = (m, n) => [
32
+ m[0] * n[0] + m[2] * n[1],
33
+ m[1] * n[0] + m[3] * n[1],
34
+ m[0] * n[2] + m[2] * n[3],
35
+ m[1] * n[2] + m[3] * n[3],
36
+ m[0] * n[4] + m[2] * n[5] + m[4],
37
+ m[1] * n[4] + m[3] * n[5] + m[5]
38
+ ];
39
+ var matApply = (m, p) => ({
40
+ x: m[0] * p.x + m[2] * p.y + m[4],
41
+ y: m[1] * p.x + m[3] * p.y + m[5]
42
+ });
43
+ var signedArcSweep = (startAngle, endAngle, ccw) => {
44
+ const TAU = Math.PI * 2;
45
+ let sweep = ccw ? ((endAngle - startAngle) % TAU + TAU) % TAU : -(((startAngle - endAngle) % TAU + TAU) % TAU);
46
+ if (sweep === 0) sweep = ccw ? TAU : -TAU;
47
+ return sweep;
48
+ };
49
+ var arcToBeziers = (center, r, startAngle, sweep) => {
50
+ const n = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)));
51
+ const step = sweep / n;
52
+ const k = 4 / 3 * Math.tan(step / 4);
53
+ const result = [];
54
+ for (let i = 0; i < n; i++) {
55
+ const a0 = startAngle + step * i;
56
+ const a1 = a0 + step;
57
+ const p0 = {
58
+ x: center.x + r * Math.cos(a0),
59
+ y: center.y + r * Math.sin(a0)
60
+ };
61
+ const p1 = {
62
+ x: center.x + r * Math.cos(a1),
63
+ y: center.y + r * Math.sin(a1)
64
+ };
65
+ result.push({
66
+ start: p0,
67
+ c1: { x: p0.x - r * k * Math.sin(a0), y: p0.y + r * k * Math.cos(a0) },
68
+ c2: { x: p1.x + r * k * Math.sin(a1), y: p1.y - r * k * Math.cos(a1) },
69
+ end: p1
70
+ });
71
+ }
72
+ return result;
73
+ };
74
+ var vExtent = (rings, v) => {
75
+ let min = Infinity;
76
+ let max = -Infinity;
77
+ for (const ring of rings) {
78
+ for (const p of ring) {
79
+ const dv = p.x * v.x + p.y * v.y;
80
+ if (dv < min) min = dv;
81
+ if (dv > max) max = dv;
82
+ }
83
+ }
84
+ return Number.isFinite(min) ? { min, max } : null;
85
+ };
86
+ var scanlineCrossings = (rings, u, v, off) => {
87
+ const crossings = [];
88
+ for (const ring of rings) {
89
+ for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
90
+ const a = ring[j];
91
+ const b = ring[i];
92
+ const va = a.x * v.x + a.y * v.y;
93
+ const vb = b.x * v.x + b.y * v.y;
94
+ if (va < off === vb < off) continue;
95
+ const t = (off - va) / (vb - va);
96
+ const px = a.x + (b.x - a.x) * t;
97
+ const py = a.y + (b.y - a.y) * t;
98
+ crossings.push(px * u.x + py * u.y);
99
+ }
100
+ }
101
+ crossings.sort((x, y) => x - y);
102
+ return crossings;
103
+ };
104
+ var computeHatchLines = (rings, angle2, spacing) => spacing <= 0 ? [] : computeHatchFamily(rings, {
105
+ angle: angle2,
106
+ origin: { x: 0, y: 0 },
107
+ deltaAlong: 0,
108
+ deltaPerp: spacing
109
+ });
110
+ var DOT_LENGTH_MM = 0.35;
111
+ var splitByDashes = (a, b, u, dashes, along0, out) => {
112
+ const len3 = (b.x - a.x) * u.x + (b.y - a.y) * u.y;
113
+ if (len3 <= 1e-9) return;
114
+ const cycle = dashes.reduce((s, d) => s + Math.abs(d), 0);
115
+ if (cycle <= 1e-9) {
116
+ out.push({ a, b });
117
+ return;
118
+ }
119
+ const pt = (d) => ({ x: a.x + u.x * d, y: a.y + u.y * d });
120
+ const base = a.x * u.x + a.y * u.y - along0;
121
+ let phase = (base % cycle + cycle) % cycle;
122
+ let idx = 0;
123
+ for (; idx < dashes.length; idx++) {
124
+ const w = Math.abs(dashes[idx]);
125
+ if (phase < w || w <= 1e-9) break;
126
+ phase -= w;
127
+ }
128
+ if (idx >= dashes.length) {
129
+ idx = 0;
130
+ phase = 0;
131
+ }
132
+ let remaining = Math.max(0, Math.abs(dashes[idx]) - phase);
133
+ let pos = 0;
134
+ let guard = 0;
135
+ while (pos < len3 - 1e-9 && guard++ < 1e5) {
136
+ const w = Math.abs(dashes[idx]);
137
+ const draw = dashes[idx] >= 0;
138
+ if (w <= 1e-9) {
139
+ if (draw) {
140
+ const s0 = Math.max(0, pos - DOT_LENGTH_MM / 2);
141
+ const s1 = Math.min(len3, pos + DOT_LENGTH_MM / 2);
142
+ if (s1 > s0) out.push({ a: pt(s0), b: pt(s1) });
143
+ }
144
+ idx = (idx + 1) % dashes.length;
145
+ remaining = Math.abs(dashes[idx]);
146
+ continue;
147
+ }
148
+ const step = Math.min(remaining, len3 - pos);
149
+ if (draw) out.push({ a: pt(pos), b: pt(pos + step) });
150
+ pos += step;
151
+ remaining -= step;
152
+ if (remaining <= 1e-9) {
153
+ idx = (idx + 1) % dashes.length;
154
+ remaining = Math.abs(dashes[idx]);
155
+ }
156
+ }
157
+ };
158
+ var computeHatchFamily = (rings, family) => {
159
+ const { deltaPerp } = family;
160
+ if (rings.length === 0 || Math.abs(deltaPerp) <= 1e-9) return [];
161
+ const u = { x: Math.cos(family.angle), y: Math.sin(family.angle) };
162
+ const v = { x: -u.y, y: u.x };
163
+ const ext = vExtent(rings, v);
164
+ if (!ext) return [];
165
+ const oV = family.origin.x * v.x + family.origin.y * v.y;
166
+ const oU = family.origin.x * u.x + family.origin.y * u.y;
167
+ const iA = (ext.min - oV) / deltaPerp;
168
+ const iB = (ext.max - oV) / deltaPerp;
169
+ const iLo = Math.ceil(Math.min(iA, iB));
170
+ const iHi = Math.floor(Math.max(iA, iB));
171
+ const dashes = family.dashesMm && family.dashesMm.length ? family.dashesMm : null;
172
+ const out = [];
173
+ for (let i = iLo; i <= iHi; i++) {
174
+ const off = oV + i * deltaPerp;
175
+ const along0 = oU + i * family.deltaAlong;
176
+ const crossings = scanlineCrossings(rings, u, v, off);
177
+ for (let k = 0; k + 1 < crossings.length; k += 2) {
178
+ const c0 = crossings[k];
179
+ const c1 = crossings[k + 1];
180
+ if (c1 - c0 < 1e-9) continue;
181
+ const a = { x: u.x * c0 + v.x * off, y: u.y * c0 + v.y * off };
182
+ const b = { x: u.x * c1 + v.x * off, y: u.y * c1 + v.y * off };
183
+ if (dashes) splitByDashes(a, b, u, dashes, along0, out);
184
+ else out.push({ a, b });
185
+ }
186
+ }
187
+ return out;
188
+ };
189
+ var ellipseArcToBeziers = (center, rx, ry, rotation, startAngle, sweep) => {
190
+ const cosR = Math.cos(rotation);
191
+ const sinR = Math.sin(rotation);
192
+ const map = (p) => ({
193
+ x: center.x + p.x * cosR - p.y * sinR,
194
+ y: center.y + p.x * sinR + p.y * cosR
195
+ });
196
+ return arcToBeziers({ x: 0, y: 0 }, 1, startAngle, sweep).map((b) => ({
197
+ start: map({ x: b.start.x * rx, y: b.start.y * ry }),
198
+ c1: map({ x: b.c1.x * rx, y: b.c1.y * ry }),
199
+ c2: map({ x: b.c2.x * rx, y: b.c2.y * ry }),
200
+ end: map({ x: b.end.x * rx, y: b.end.y * ry })
201
+ }));
202
+ };
203
+
204
+ // ../core/src/model/builtinPat.ts
205
+ var BUILTIN_PAT = `;; Linea built-in hatch pattern library.
206
+ ;;
207
+ ;; Format: .pat syntax - a \`*NAME,description\` header followed by one line
208
+ ;; family per row: \`angle, x-origin, y-origin, delta-x, delta-y [, dash...]\`
209
+ ;; (angle in degrees CCW; delta-x shifts successive lines along their own
210
+ ;; direction - dash phase - and delta-y is the perpendicular spacing;
211
+ ;; dashes: >0 pen-down, <0 gap, 0 dot). All lengths are millimeters (ISO
212
+ ;; scaling).
213
+ ;;
214
+ ;; The pattern geometry is functional interchange data, kept name- and
215
+ ;; value-compatible with the predefined DXF HATCH patterns other CAD
216
+ ;; applications resolve - ANSI/ISO/JIS/GOST section conventions plus common
217
+ ;; architectural textures. Descriptions and organization are Linea's
218
+ ;; own. There is no SOLID entry: solid fill is its own fill kind, not a
219
+ ;; line-family pattern.
220
+ ;;
221
+ ;; After editing, regenerate the embedded TS mirror:
222
+ ;; npm run gen:patterns -w @lineadraw/core
223
+ ;;
224
+
225
+ ;; Material and texture patterns.
226
+
227
+ *ANGLE,Steel angle framing
228
+ 0, 0, 0, 0, 6.985, 5.08, -1.905
229
+ 90, 0, 0, 0, 6.985, 5.08, -1.905
230
+ *ANSI31,Cast iron; general section (ANSI)
231
+ 45, 0, 0, 0, 3.175
232
+ *ANSI32,Steel (ANSI)
233
+ 45, 0, 0, 0, 9.525
234
+ 45, 4.49013, 0, 0, 9.525
235
+ *ANSI33,Bronze, brass, copper (ANSI)
236
+ 45, 0, 0, 0, 6.35
237
+ 45, 4.49013, 0, 0, 6.35, 3.175, -1.5875
238
+ *ANSI34,Plastic, rubber (ANSI)
239
+ 45, 0, 0, 0, 19.05
240
+ 45, 4.49013, 0, 0, 19.05
241
+ 45, 8.98026, 0, 0, 19.05
242
+ 45, 13.4704, 0, 0, 19.05
243
+ *ANSI35,Fire brick, refractory (ANSI)
244
+ 45, 0, 0, 0, 6.35
245
+ 45, 4.49013, 0, 0, 6.35, 7.9375, -1.5875, 0, -1.5875
246
+ *ANSI36,Marble, slate, glass (ANSI)
247
+ 45, 0, 0, 5.55625, 3.175, 7.9375, -1.5875, 0, -1.5875
248
+ *ANSI37,Lead, zinc, insulation (ANSI)
249
+ 45, 0, 0, 0, 3.175
250
+ 135, 0, 0, 0, 3.175
251
+ *ANSI38,Aluminum (ANSI)
252
+ 45, 0, 0, 0, 3.175
253
+ 135, 0, 0, 6.35, 3.175, 7.9375, -4.7625
254
+ *AR-B816,Block wall 8x16, running bond
255
+ 0, 0, 0, 0, 203.2
256
+ 90, 0, 0, 203.2, 203.2, 203.2, -203.2
257
+ *AR-B816C,Block wall 8x16 with mortar joints
258
+ 0, 0, 0, 203.2, 203.2, 396.875, -9.525
259
+ 0, -203.2, 9.525, 203.2, 203.2, 396.875, -9.525
260
+ 90, 0, 0, 203.2, 203.2, -212.725, 193.675
261
+ 90, -9.525, 0, 203.2, 203.2, -212.725, 193.675
262
+ *AR-B88,Block wall 8x8, running bond
263
+ 0, 0, 0, 0, 203.2
264
+ 90, 0, 0, 203.2, 101.6, 203.2, -203.2
265
+ *AR-BRELM,Brick wall, English bond, mortar joints
266
+ 0, 0, 0, 0, 135.484, 193.675, -9.525
267
+ 0, 0, 57.15, 0, 135.484, 193.675, -9.525
268
+ 0, 50.8, 67.7418, 0, 135.484, 92.075, -9.525
269
+ 0, 50.8, 124.892, 0, 135.484, 92.075, -9.525
270
+ 90, 0, 0, 0, 203.2, 57.15, -78.334
271
+ 90, -9.525, 0, 0, 203.2, 57.15, -78.334
272
+ 90, 50.8, 67.7418, 0, 101.6, 57.15, -78.334
273
+ 90, 41.275, 67.7418, 0, 101.6, 57.15, -78.334
274
+ *AR-BRSTD,Brick wall, running bond
275
+ 0, 0, 0, 0, 67.7418
276
+ 90, 0, 0, 67.7418, 101.6, 67.7418, -67.7418
277
+ *AR-CONC,Concrete, random aggregate
278
+ 50, 0, 0, 104.896, -149.807, 19.05, -209.55
279
+ 355, 0, 0, -51.76101082,187.25814969, 15.24, -167.64058417
280
+ 100.4514447,15.182007,-1.3282535,145.5569059,-176.270089,16.1900088,-178.0902446
281
+ 46.1842, 0, 50.8, 157.343, -224.71, 28.575, -314.325
282
+ 96.63555761,22.5899,47.2965,218.33577212,-264.40480444,24.28502314,-267.13560816
283
+ 351.18415117,0,50.8,196.67912063,280.88740361,22.85996707,-251.45973192
284
+ 21, 25.4, 38.1, 104.89565868, -149.80652586, 19.05, -209.55
285
+ 326, 25.4, 38.1, -51.7604, 187.258, 15.24, -167.64
286
+ 71.451445,38.0345326,29.5779001,145.5567546,-176.2700748,16.1900088,-178.0899376
287
+ 37.5, 0, 0, 53.9242, 65.2018, 0, -165.608, 0, -170.18, 0, -168.275
288
+ 7.5, 0, 0, 79.3242, 90.6018, 0, -97.028, 0, -161.798, 0, -64.135
289
+ -32.5, -56.642, 0, 117.434, 68.0212, 0, -63.5, 0, -198.12, 0, -262.89
290
+ -42.5, -82.042, 0, 92.0344, 118.821, 0, -82.55, 0, -131.572, 0, -186.69
291
+ *AR-HBONE,Brick paving, herringbone at 45 deg
292
+ 45, 0, 0, 101.6, 101.6, 304.8, -101.6
293
+ 135, 71.842, 71.842, 101.6, -101.6, 304.8, -101.6
294
+ *AR-PARQ1,Parquet flooring, basket weave
295
+ 90, 0, 0, 304.8, 304.8, 304.8, -304.8
296
+ 90, 50.8, 0, 304.8, 304.8, 304.8, -304.8
297
+ 90, 101.6, 0, 304.8, 304.8, 304.8, -304.8
298
+ 90, 152.4, 0, 304.8, 304.8, 304.8, -304.8
299
+ 90, 203.2, 0, 304.8, 304.8, 304.8, -304.8
300
+ 90, 254, 0, 304.8, 304.8, 304.8, -304.8
301
+ 90, 304.8, 0, 304.8, 304.8, 304.8, -304.8
302
+ 0, 0, 304.8, 304.8, -304.8, 304.8, -304.8
303
+ 0, 0, 355.6, 304.8, -304.8, 304.8, -304.8
304
+ 0, 0, 406.4, 304.8, -304.8, 304.8, -304.8
305
+ 0, 0, 457.2, 304.8, -304.8, 304.8, -304.8
306
+ 0, 0, 508, 304.8, -304.8, 304.8, -304.8
307
+ 0, 0, 558.8, 304.8, -304.8, 304.8, -304.8
308
+ 0, 0, 609.6, 304.8, -304.8, 304.8, -304.8
309
+ *AR-RROOF,Roof shingles
310
+ 0, 0, 0, 55.88, 25.4, 381, -50.8, 127, -25.4
311
+ 0, 33.782, 12.7, -25.4, 33.782, 76.2, -8.382, 152.4, -19.05
312
+ 0, 12.7, 21.59, 132.08, 17.018, 203.2, -35.56, 101.6, -25.4
313
+ *AR-RSHKE,Roof wood shakes
314
+ 0, 0, 0, 647.7, 304.8, 152.4, -127, 177.8, -76.2, 228.6, -101.6
315
+ 0, 152.4, 12.7, 647.7, 304.8, 127, -482.6, 101.6, -152.4
316
+ 0, 457.2, -19.05, 647.7, 304.8, 76.2, -787.4
317
+ 90, 0, 0, 304.8, 215.9, 292.1, -927.1
318
+ 90, 152.4, 0, 304.8, 215.9, 285.75, -933.45
319
+ 90, 279.4, 0, 304.8, 215.9, 266.7, -952.5
320
+ 90, 457.2, -19.05, 304.8, 215.9, 292.1, -927.1
321
+ 90, 533.4, -19.05, 304.8, 215.9, 292.1, -927.1
322
+ 90, 762, 0, 304.8, 215.9, 279.4, -939.8
323
+ *AR-SAND,Sand stipple
324
+ 37.5, 0, 0, 28.5242, 39.8018, 0, -38.608, 0, -43.18, 0, -41.275
325
+ 7.5, 0, 0, 53.9242, 65.2018, 0, -20.828, 0, -34.798, 0, -13.335
326
+ -32.5, -31.242, 0, 66.6344, 42.6212, 0, -12.7, 0, -45.72, 0, -59.69
327
+ -42.5, -31.242, 0, 41.2344, 68.0212, 0, -6.35, 0, -29.972, 0, -34.29
328
+ *BOX,Interlocking box weave
329
+ 90, 0, 0, 0, 25.4
330
+ 90, 6.35, 0, 0, 25.4
331
+ 0, 0, 0, 0, 25.4, -6.35, 6.35
332
+ 0, 0, 6.35, 0, 25.4, -6.35, 6.35
333
+ 0, 0, 12.7, 0, 25.4, 6.35, -6.35
334
+ 0, 0, 19.05, 0, 25.4, 6.35, -6.35
335
+ 90, 12.7, 0, 0, 25.4, 6.35, -6.35
336
+ 90, 19.05, 0, 0, 25.4, 6.35, -6.35
337
+ *BRASS,Brass
338
+ 0, 0, 0, 0, 6.35
339
+ 0, 0, 3.175, 0, 6.35, 3.175, -1.5875
340
+ *BRICK,Brick coursing
341
+ 0, 0, 0, 0, 6.35
342
+ 90, 0, 0, 0, 12.7, 6.35, -6.35
343
+ 90, 6.35, 0, 0, 12.7, -6.35, 6.35
344
+ *BRSTONE,Brick and stone mix
345
+ 0, 0, 0, 0, 8.382
346
+ 90, 22.86, 0, 8.382, 12.7, 8.382, -8.382
347
+ 90, 20.32, 0, 8.382, 12.7, 8.382, -8.382
348
+ 0, 22.86, 1.397, 12.7, 8.382, -22.86, 2.54
349
+ 0, 22.86, 2.794, 12.7, 8.382, -22.86, 2.54
350
+ 0, 22.86, 4.191, 12.7, 8.382, -22.86, 2.54
351
+ 0, 22.86, 5.588, 12.7, 8.382, -22.86, 2.54
352
+ 0, 22.86, 6.985, 12.7, 8.382, -22.86, 2.54
353
+ *CLAY,Clay
354
+ 0, 0, 0, 0, 4.7625
355
+ 0, 0, 0.79375, 0, 4.7625
356
+ 0, 0, 1.5875, 0, 4.7625
357
+ 0, 0, 3.175, 0, 4.7625, 4.7625, -3.175
358
+ *CORK,Cork
359
+ 0, 0, 0, 0, 3.175
360
+ 135, 1.5875, -1.5875, 0, 8.98026, 4.49013, -4.49013
361
+ 135, 2.38125, -1.5875, 0, 8.98026, 4.49013, -4.49013
362
+ 135, 3.175, -1.5875, 0, 8.98026, 4.49013, -4.49013
363
+ *CROSS,Cross tiling
364
+ 0, 0, 0, 6.35, 6.35, 3.175, -9.525
365
+ 90, 1.5875, -1.5875, 6.35, 6.35, 3.175, -9.525
366
+ *DASH,Staggered dashes
367
+ 0, 0, 0, 3.175, 3.175, 3.175, -3.175
368
+ *DOLMIT,Layered rock (dolomite)
369
+ 0, 0, 0, 0, 6.35
370
+ 45, 0, 0, 0, 17.9605,8.98025612106915355989072339,-17.9605122421383071197814467
371
+ *DOTS,Dot screen
372
+ 0, 0, 0, 0.79375, 1.5875, 0, -1.5875
373
+ *EARTH,Earth (section)
374
+ 0, 0, 0, 6.35, 6.35, 6.35, -6.35
375
+ 0, 0, 2.38125, 6.35, 6.35, 6.35, -6.35
376
+ 0, 0, 4.7625, 6.35, 6.35, 6.35, -6.35
377
+ 90, 0.79375, 5.55625, 6.35, 6.35, 6.35, -6.35
378
+ 90, 3.175, 5.55625, 6.35, 6.35, 6.35, -6.35
379
+ 90, 5.55625, 5.55625, 6.35, 6.35, 6.35, -6.35
380
+ *ESCHER,Isometric cube tiling
381
+ 60, 0, 0, -15.24, 26.3964542936, 27.94, -2.54
382
+ 180, 0, 0, -15.24, 26.3964542936, 27.94, -2.54
383
+ 300, 0, 0, 15.24, 26.3964542936, 27.94, -2.54
384
+ 60, 2.54, 0, -15.24, 26.3964542936, 5.08, -25.4
385
+ 300, 2.54, 0, 15.24, 26.3964542936, 5.08, -25.4
386
+ 60, -1.27, 2.199704516, -15.24, 26.3964542936, 5.08, -25.4
387
+ 180, -1.27, 2.199704516, -15.24, 26.3964542936, 5.08, -25.4
388
+ 300, -1.27, -2.199704516, 15.24, 26.3964542936, 5.08, -25.4
389
+ 180, -1.27, -2.199704516, -15.24, 26.3964542936, 5.08, -25.4
390
+ 60, -10.16, 0, -15.24, 26.3964542936, 5.08, -25.4
391
+ 300, -10.16, 0, 15.24, 26.3964542936, 5.08, -25.4
392
+ 60, 5.08, -8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
393
+ 180, 5.08, -8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
394
+ 300, 5.08, 8.7988180894, 15.24, 26.3964542936, 5.08, -25.4
395
+ 180, 5.08, 8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
396
+ 0, 5.08, 4.3994090574, -15.24, 26.3964542936, 17.78, -12.7
397
+ 0, 5.08, -4.3994090574, -15.24, 26.3964542936, 17.78, -12.7
398
+ 120, 1.27, 6.5991135734, 15.24, 26.3964542936, 17.78, -12.7
399
+ 120, -6.35, 2.199704516, 15.24, 26.3964542936, 17.78, -12.7
400
+ 240, -6.35, -2.199704516, 15.24, 26.3964542936, 17.78, -12.7
401
+ 240, 1.27, -6.5991135734, 15.24, 26.3964542936, 17.78, -12.7
402
+ *FLEX,Flexible material
403
+ 0, 0, 0, 0, 6.35, 6.35, -6.35
404
+ 45,6.35,0,4.490128053,4.490128053,1.5875,-5.8052561314,1.5875,-8.9802561314
405
+ *GOST_GLASS,Glass (GOST)
406
+ 45, 0, 0, 6, -6, 5, -7
407
+ 45, 2.121320, 0, 6, -6, 2, -10
408
+ 45, 0, 2.121320, 6, -6, 2, -10
409
+ *GOST_GROUND,Ground (GOST)
410
+ 45, 0, 0, 10, -10, 20
411
+ 45, 3, 0, 10, -10, 20
412
+ 45, 6, 0, 10, -10, 20
413
+ *GOST_WOOD,Wood (GOST)
414
+ 90, 0, 0, 0, -6, 10, -2
415
+ 90, 2, -2, 0, -6, 6, -1.5, 3, -1.5
416
+ 90, 4, -5, 0, -6, 10, -2
417
+ *GRASS,Grass
418
+ 90, 0, 0, 17.96051224, 17.96051224, 4.7625, -31.15852448
419
+ 45, 0, 0, 0, 25.4, 4.7625, -20.6375
420
+ 135, 0, 0, 0, 25.4, 4.7625, -20.6375
421
+ *GRATE,Grating
422
+ 0, 0, 0, 0, 0.79375
423
+ 90, 0, 0, 0, 3.175
424
+ *GRAVEL,Gravel, random cobbles
425
+ 228.0127875,18.288,25.4,305.85067529778,1.88796713245,3.4172144,-338.30483639565
426
+ 184.969741,16.002,22.86,-305.8545235377,1.10019612724,5.8640472,-580.54048893524
427
+ 132.5104471,10.16,22.352,-377.59492241548,1.56030959675,4.1348152,-409.347227941
428
+ 267.273689,.254,16.002,-508.63316875916,1.20815479432,5.3400452,-528.66437425738
429
+ 292.83365418,0.0,10.668,-330.19770134945,1.23208097566,5.236337,-518.39807745344
430
+ 357.273689,2.032,5.842,-508.63316875916,1.20815479432,5.3400452,-528.66437425738
431
+ 37.69424047,7.366,5.588,-416.58997273292,.91357450169,7.0619366,-699.13115314247
432
+ 72.25532837,12.954,9.906,586.40373773403,.96766293399,6.6671952,-660.05256601905
433
+ 121.42956562,14.986,16.256,387.71230339293,1.2040754753,5.35813,-530.45545698712
434
+ 175.2363583,12.192,20.828,-280.5442400419,2.10935518695,6.1171328,-299.7393695
435
+ 222.3974378,6.096,21.336,413.48123885686,.81554484621,7.9107792,-783.16772512177
436
+ 138.81407483,25.4,15.748,234.164238558,2.38943100688,2.7000454,-267.30565824344
437
+ 171.4692344,23.368,17.526,-334.082478726,1.25594916784,5.1368198,-508.5463899704
438
+ 225.00000,18.288,18.288,17.96051224214,17.96051224214,3.5920934,-32.32893108428
439
+ 203.19859051,16.51,21.336,-136.74251918,3.33518339548,1.9344132,-191.50622368894
440
+ 291.80140949,14.732,20.574,-80.18324702488,4.71666158921,2.7356562,-134.0475299
441
+ 30.96375653,15.748,18.034,91.47734531502,4.35606406258,4.4431966,-143.6629815291
442
+ 161.56505118,19.558,20.32,-56.2252967978,8.03218525675,3.2128714,-77.10898116828
443
+ 16.389540334,0.0,20.574,265.17991128726,1.43340492604,4.50088,-445.58826672539
444
+ 70.34617594,4.318,21.844,-297.29446803469,1.70858889651,3.7759894,-373.822156782
445
+ 293.19859051,19.558,25.4,-136.7425191801,3.33518339548,3.868801,-189.57183588894
446
+ 343.61045967,21.082,21.844,-265.17991128725,1.433404926,4.50088,-445.5882667254
447
+ 339.44395478,0.0,4.826,-136.75087638398,2.97284513779,4.340352,-212.67734313106
448
+ 294.7751406,4.064,3.302,-306.90424056705,1.77401295215,3.6367212,-360.0359338072
449
+ 66.80140949,19.812,0.0,136.74251918012,3.33518339452,3.868801,-189.57183588894
450
+ 17.35402464,21.336,3.556,-345.47402804977,1.51523696536,4.2578274,-421.523759802
451
+ 69.44395478,7.366,0.0,-136.75087638396,2.97284513874,2.170176,-214.84751913106
452
+ 101.309932474,18.288,0.0,104.60834648271,4.98134983255,1.295146,-128.21994964526
453
+ 165.963756532,18.034,1.27,-80.085263387,6.16040487582,5.236337,-99.49054589069
454
+ 186.00900596,12.954,2.54,-255.26337856879,1.32949676118,4.85267,-480.41364863337
455
+ 303.69006753,15.748,15.748,-56.35753993648,7.0446924921,3.6632388,-87.9177635968
456
+ 353.15722659,17.78,12.7,434.77679606606,1.0087628707,6.3955676,-633.16009065031
457
+ 60.9453959,24.13,11.938,-204.76648550216,2.46706609031,2.6150824,-258.8939231811
458
+ 90.0000000,25.4,14.224,25.4,25.4,1.524,-23.876
459
+ 120.25643716,12.446,3.302,-204.77318477297,1.8283320086,3.5286696,-349.339407732
460
+ 48.01278750,10.668,6.35,305.85067529778,1.88796713138,6.8344288,-334.88762199565
461
+ 0.000000000,15.24,11.43,25.4,25.4,6.604,-18.796
462
+ 325.3048465,21.844,11.43,310.04235091354,-1.6064370526,4.0160956,-397.5931672414
463
+ 254.0546041,25.146,9.144,104.6687497289,3.48895832444,3.6982908,-181.21650038772
464
+ 207.64597536,24.13,5.588,545.36007557253,1.07143433066,6.021451,-596.12464422938
465
+ 175.42607874,18.796,2.794,331.1739336186,1.01276432357,6.3702946,-630.6584645624
466
+ *HEX,Hexagonal mesh
467
+ 0, 0, 0, 0, 5.4992613154, 3.175, -6.35
468
+ 120, 0, 0, 0, 5.4992613154, 3.175, -6.35
469
+ 60, 3.175, 0, 0, 5.4992613154, 3.175, -6.35
470
+ *HONEY,Honeycomb
471
+ 0, 0, 0, 4.7625, 2.749630645, 3.175, -6.35
472
+ 120, 0, 0, 4.7625, 2.749630645, 3.175, -6.35
473
+ 60, 0, 0, 4.7625, 2.749630645, -6.35, 3.175
474
+ *HOUND,Houndstooth
475
+ 0, 0, 0, 6.35, 1.5875, 25.4, -12.7
476
+ 90, 0, 0, -6.35, 1.5875, 25.4, -12.7
477
+ *INSUL,Insulation batt
478
+ 0, 0, 0, 0, 9.525
479
+ 0, 0, 3.175, 0, 9.525, 3.175, -3.175
480
+ 0, 0, 6.35, 0, 9.525, 3.175, -3.175
481
+ *LINE,Horizontal lines
482
+ 0, 0, 0, 0, 3.175
483
+ *MUDST,Mud / sand (geological)
484
+ 0, 0, 0, 12.7, 6.35, 6.35, -6.35, 0, -6.35, 0, -6.35
485
+ *NET,Square grid
486
+ 0, 0, 0, 0, 3.175
487
+ 90, 0, 0, 0, 3.175
488
+ *NET3,Triangular grid
489
+ 0, 0, 0, 0, 3.175
490
+ 60, 0, 0, 0, 3.175
491
+ 120, 0, 0, 0, 3.175
492
+ *PLAST,Plastic
493
+ 0, 0, 0, 0, 6.35
494
+ 0, 0, 0.79375, 0, 6.35
495
+ 0, 0, 1.5875, 0, 6.35
496
+ *PLASTI,Plastic, dense
497
+ 0, 0, 0, 0, 6.35
498
+ 0, 0, 0.79375, 0, 6.35
499
+ 0, 0, 1.5875, 0, 6.35
500
+ 0, 0, 3.96875, 0, 6.35
501
+ *SACNCR,Concrete, dotted diagonal
502
+ 45, 0, 0, 0, 2.38125
503
+ 45, 1.6838, 0, 0, 2.38125, 0, -2.38125
504
+ *SQUARE,Dashed square lattice
505
+ 0, 0, 0, 0, 3.175, 3.175, -3.175
506
+ 90, 0, 0, 0, 3.175, 3.175, -3.175
507
+ *STARS,Six-pointed star lattice
508
+ 0, 0, 0, 0, 5.4992613154, 3.175, -3.175
509
+ 60, 0, 0, 0, 5.4992613154, 3.175, -3.175
510
+ 120, 1.5875, 2.7496306704, 0, 5.4992613154, 3.175, -3.175
511
+ *STEEL,Steel, paired lines
512
+ 45, 0, 0, 0, 3.175
513
+ 45, 0, 1.5875, 0, 3.175
514
+ *SWAMP,Swamp / marsh
515
+ 0, 0, 0, 12.7, 21.9970452362, 3.175, -22.225
516
+ 90, 1.5875, 0, 21.9970452362, 12.7, 1.5875, -42.4065904724
517
+ 90, 1.984375, 0, 21.9970452362, 12.7, 1.27, -42.7240904724
518
+ 90, 1.190625, 0, 21.9970452362, 12.7, 1.27, -42.7240904724
519
+ 60, 2.38125, 0, 12.7, 21.9970452362, 1.016, -24.384
520
+ 120, 0.79375, 0, 12.7, 21.9970452362, 1.016, -24.384
521
+ *TRANS,Heat-transfer surface
522
+ 0, 0, 0, 0, 6.35
523
+ 0, 0, 3.175, 0, 6.35, 3.175, -3.175
524
+ *TRIANG,Triangle tiling
525
+ 60, 0, 0, 4.7625, 8.2488919604, 4.7625, -4.7625
526
+ 120, 0, 0, 4.7625, 8.2488919604, 4.7625, -4.7625
527
+ 0, -2.38125, 4.1244459802, 4.7625, 8.2488919604, 4.7625, -4.7625
528
+ *ZIGZAG,Stair-step zigzag
529
+ 0, 0, 0, 3.175, 3.175, 3.175, -3.175
530
+ 90, 3.175, 0, 3.175, 3.175, 3.175, -3.175
531
+
532
+ ;; Hatch companions to the ISO 128 line types (1 mm pen base).
533
+
534
+ *ACAD_ISO02W100,ISO dashed
535
+ 0, 0,0, 0,5, 12,-3
536
+ *ACAD_ISO03W100,ISO dashed, wide gaps
537
+ 0, 0,0, 0,5, 12,-18
538
+ *ACAD_ISO04W100,ISO long-dash dot
539
+ 0, 0,0, 0,5, 24,-3,.5,-3
540
+ *ACAD_ISO05W100,ISO long-dash double-dot
541
+ 0, 0,0, 0,5, 24,-3,.5,-3,.5,-3
542
+ *ACAD_ISO06W100,ISO long-dash triple-dot
543
+ 0, 0,0, 0,5, 24,-3,.5,-3,.5,-6.5
544
+ 0, 0,0, 0,5, -34,.5,-3
545
+ *ACAD_ISO07W100,ISO dotted
546
+ 0, 0,0, 0,5, .5,-3
547
+ *ACAD_ISO08W100,ISO long-dash short-dash
548
+ 0, 0,0, 0,5, 24,-3,6,-3
549
+ *ACAD_ISO09W100,ISO long-dash double short-dash
550
+ 0, 0,0, 0,5, 24,-3,6,-3,6,-3
551
+ *ACAD_ISO10W100,ISO dash dot
552
+ 0, 0,0, 0,5, 12,-3,.5,-3
553
+ *ACAD_ISO11W100,ISO double-dash dot
554
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-3
555
+ *ACAD_ISO12W100,ISO dash double-dot
556
+ 0, 0,0, 0,5, 12,-3,.5,-3,.5,-3
557
+ *ACAD_ISO13W100,ISO double-dash double-dot
558
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-6.5
559
+ 0, 0,0, 0,5, -33.5,.5,-3
560
+ *ACAD_ISO14W100,ISO dash triple-dot
561
+ 0, 0,0, 0,5, 12,-3,.5,-3,.5,-6.5
562
+ 0, 0,0, 0,5, -22,.5,-3
563
+ *ACAD_ISO15W100,ISO double-dash triple-dot
564
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-10
565
+ 0, 0,0, 0,5, -33.5,.5,-3,.5,-3
566
+
567
+ ;; JIS A 0150 section patterns.
568
+
569
+ *JIS_LC_20,Lightweight concrete, 20 mm (JIS)
570
+ 45,0,0,0,20
571
+ 45,.4,0,0,20
572
+ *JIS_LC_20A,Lightweight concrete, 20 mm wide (JIS)
573
+ 45,0,0,0,20
574
+ 45,1,0,0,20
575
+ *JIS_LC_8,Lightweight concrete, 8 mm (JIS)
576
+ 45,0,0,0,7.8
577
+ 45,.4,0,0,7.8
578
+ *JIS_LC_8A,Lightweight concrete, 8 mm wide (JIS)
579
+ 45,0,0,0,7.8
580
+ 45,1,0,0,7.8
581
+ *JIS_RC_10,Reinforced concrete, 10 mm (JIS)
582
+ 45,0,0,0,10
583
+ 45,.725,0,0,10
584
+ 45,1.45,0,0,10
585
+ *JIS_RC_15,Reinforced concrete, 15 mm (JIS)
586
+ 45,0,0,0,15
587
+ 45,.725,0,0,15
588
+ 45,1.45,0,0,15
589
+ *JIS_RC_18,Reinforced concrete, 18 mm (JIS)
590
+ 45,0,0,0,18
591
+ 45,1,0,0,18
592
+ 45,2,0,0,18
593
+ *JIS_RC_30,Reinforced concrete, 30 mm (JIS)
594
+ 45,0,0,0,30
595
+ 45,1,0,0,30
596
+ 45,2,0,0,30
597
+ *JIS_STN_1E,Stone, 1 mm (JIS)
598
+ 45, 0, 0, 0, 1
599
+ 45, .705, 0, 0,1, 1, -.5
600
+ *JIS_STN_2.5,Stone, 2.5 mm (JIS)
601
+ 45, 0, 0, 0, 2.5
602
+ 45, 1.765, 0, 0,2.5, 1.2, -.5
603
+ *JIS_WOOD,Wood (JIS)
604
+ 45,0,0,0,.70710678
605
+ `;
606
+
607
+ // ../core/src/model/hatchPatterns.ts
608
+ var parsePatternDefinitions = (text, unitScale = 25.4) => {
609
+ const DEG2RAD = Math.PI / 180;
610
+ const patterns = [];
611
+ let current = null;
612
+ for (const raw of text.split(/\r?\n/)) {
613
+ const line = raw.trim();
614
+ if (!line || line.startsWith(";")) continue;
615
+ if (line.startsWith("*")) {
616
+ const [name, ...desc] = line.slice(1).split(",");
617
+ current = { name: name.trim(), lines: [] };
618
+ const d = desc.join(",").trim();
619
+ if (d) current.description = d;
620
+ patterns.push(current);
621
+ continue;
622
+ }
623
+ if (!current) continue;
624
+ const n = line.split(",").map((s) => Number(s.trim()));
625
+ if (n.length < 5 || n.some((v) => Number.isNaN(v))) continue;
626
+ const [angle2, x, y, dx, dy, ...dashes] = n;
627
+ current.lines.push({
628
+ angle: angle2 * DEG2RAD,
629
+ origin: { x: x * unitScale, y: y * unitScale },
630
+ deltaX: dx * unitScale,
631
+ deltaY: dy * unitScale,
632
+ dashesMm: dashes.length ? dashes.map((v) => v * unitScale) : void 0
633
+ });
634
+ }
635
+ return patterns.filter((p) => p.lines.length > 0);
636
+ };
637
+ var BUILT_IN_PATTERNS = parsePatternDefinitions(
638
+ BUILTIN_PAT,
639
+ 1
640
+ ).filter((p) => p.name.toUpperCase() !== "SOLID");
641
+ var normalizeName = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
642
+ var BUILT_IN_BY_NAME = new Map(
643
+ BUILT_IN_PATTERNS.map((p) => [normalizeName(p.name), p])
644
+ );
645
+ var HATCH_PATTERN_NAMES = BUILT_IN_PATTERNS.map(
646
+ (p) => p.name
647
+ );
648
+ var getHatchPattern = (name) => BUILT_IN_BY_NAME.get(normalizeName(name));
649
+ var MAX_HATCH_SEGMENTS = 1e5;
650
+ var estimatePatternSegments = (rings, pattern2, scale3) => {
651
+ if (scale3 <= 0) return 0;
652
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
653
+ for (const ring of rings)
654
+ for (const p of ring) {
655
+ if (p.x < minX) minX = p.x;
656
+ if (p.x > maxX) maxX = p.x;
657
+ if (p.y < minY) minY = p.y;
658
+ if (p.y > maxY) maxY = p.y;
659
+ }
660
+ if (minX > maxX) return 0;
661
+ const diag = Math.hypot(maxX - minX, maxY - minY);
662
+ let count = 0;
663
+ for (const fam of pattern2.lines) {
664
+ const perp2 = Math.abs(fam.deltaY) * scale3;
665
+ if (perp2 <= 1e-9) continue;
666
+ const lines = diag / perp2 + 1;
667
+ const period = (fam.dashesMm?.reduce((a, b) => a + Math.abs(b), 0) ?? 0) * scale3;
668
+ const perLine = period > 1e-9 ? diag / period + 1 : 1;
669
+ count += lines * perLine;
670
+ }
671
+ return count;
672
+ };
673
+ var computePatternGeometry = (rings, pattern2, angle2, scale3, origin = { x: 0, y: 0 }) => {
674
+ const out = [];
675
+ if (scale3 <= 0) return out;
676
+ const c = Math.cos(angle2);
677
+ const s = Math.sin(angle2);
678
+ for (const fam of pattern2.lines) {
679
+ if (Math.abs(fam.deltaY) * scale3 <= 1e-9) continue;
680
+ const ox = fam.origin.x * scale3;
681
+ const oy = fam.origin.y * scale3;
682
+ const segs = computeHatchFamily(rings, {
683
+ angle: angle2 + fam.angle,
684
+ origin: { x: ox * c - oy * s + origin.x, y: ox * s + oy * c + origin.y },
685
+ deltaAlong: fam.deltaX * scale3,
686
+ deltaPerp: fam.deltaY * scale3,
687
+ dashesMm: fam.dashesMm?.map((d) => d * scale3)
688
+ });
689
+ for (const seg of segs) out.push(seg);
690
+ }
691
+ return out;
692
+ };
693
+ var PatternLineSchema = z2.object({
694
+ /** Radians CCW, in the pattern's own frame. */
695
+ angle: z2.number(),
696
+ /** Base point, mm. */
697
+ origin: Vec2Schema,
698
+ /** Shift ALONG the line between successive lines (dash phase), mm. */
699
+ deltaX: z2.number(),
700
+ /** Perpendicular spacing between successive lines, mm. */
701
+ deltaY: z2.number().positive(),
702
+ /** Dash cycle along the line: >0 dash, <0 gap, 0 dot; absent = solid. */
703
+ dashesMm: z2.array(z2.number()).optional()
704
+ });
705
+ var HatchPatternSchema = z2.object({
706
+ /** Stored on fills as `fill.name`; case-insensitive, unique per document. */
707
+ name: z2.string().min(1),
708
+ description: z2.string().optional(),
709
+ lines: z2.array(PatternLineSchema).min(1)
710
+ });
711
+ var hatchPatternKey = (name) => normalizeName(name);
712
+ var resolveHatchPattern = (name, hatchPatterns) => {
713
+ if (!name) return void 0;
714
+ if (hatchPatterns && hatchPatterns.length > 0) {
715
+ const key = normalizeName(name);
716
+ const own = hatchPatterns.find((p) => normalizeName(p.name) === key);
717
+ if (own) return own;
718
+ }
719
+ return getHatchPattern(name);
720
+ };
721
+
722
+ // ../core/src/export/dxf/exportDxf.ts
723
+ import {
724
+ DxfWriter,
725
+ Units,
726
+ LayerFlags,
727
+ LWPolylineFlags,
728
+ TextHorizontalAlignment,
729
+ TextVerticalAlignment,
730
+ HatchBoundaryPaths,
731
+ HatchPolylineBoundary,
732
+ HatchPredefinedPatterns,
733
+ pattern
734
+ } from "@tarikjabiri/dxf";
735
+
736
+ // ../core/src/model/objects.ts
737
+ import { z as z3 } from "zod";
738
+
43
739
  // ../core/src/model/fonts.ts
44
740
  var DEFAULT_FONT = "arial";
45
741
  var FONTS = {
@@ -172,102 +868,185 @@ var fontTypeface = (def) => def.cssStack.split(",")[0].replace(/["']/g, "").trim
172
868
  var FONT_OPTIONS = Object.values(
173
869
  FONTS
174
870
  ).filter((f) => f.id !== "osifont-italic").map((f) => ({ value: f.id, label: f.label }));
871
+ var SHX_STEMS = /* @__PURE__ */ new Set([
872
+ "txt",
873
+ "monotxt",
874
+ "simplex",
875
+ "complex",
876
+ "romans",
877
+ "romand",
878
+ "romanc",
879
+ "romant",
880
+ "italic",
881
+ "italicc",
882
+ "italict",
883
+ "scripts",
884
+ "scriptc",
885
+ "gothice",
886
+ "gothicg",
887
+ "gothici",
888
+ "greeks",
889
+ "greekc",
890
+ "isocp",
891
+ "isocp2",
892
+ "isocp3",
893
+ "isoct",
894
+ "isoct2",
895
+ "isoct3",
896
+ "iso",
897
+ "amgdt",
898
+ "gdt",
899
+ "symap",
900
+ "syastro",
901
+ "symath",
902
+ "symeteo",
903
+ "symusic",
904
+ "bigfont",
905
+ "ltypeshp",
906
+ "swiss",
907
+ "swissb",
908
+ "swissi",
909
+ "swissbi",
910
+ "sansserif",
911
+ "sanss",
912
+ "sansserifb",
913
+ "sanssb",
914
+ "arc",
915
+ "ic-romans",
916
+ "ic-simplex",
917
+ "txtalt"
918
+ ]);
919
+ var fontFromDxfStyle = (fontFile, typeface) => {
920
+ const file = (fontFile ?? "").trim();
921
+ const stem = file.replace(/\.[a-z0-9]+$/i, "").toLowerCase();
922
+ const family = (typeface ?? "").trim();
923
+ const familyLower = family.toLowerCase();
924
+ for (const def of Object.values(FONTS)) {
925
+ if (file && def.dxfFontFile.toLowerCase() === file.toLowerCase() || familyLower && fontTypeface(def).toLowerCase() === familyLower)
926
+ return def.id === "osifont-italic" ? { font: "osifont", italic: true } : { font: def.id };
927
+ }
928
+ const arialFace = /^arial(bd|bi|i)$/.exec(stem);
929
+ if (arialFace)
930
+ return {
931
+ font: "arial",
932
+ ...arialFace[1].includes("b") ? { bold: true } : {},
933
+ ...arialFace[1].includes("i") ? { italic: true } : {}
934
+ };
935
+ const key = `${familyLower} ${stem}`;
936
+ if (/narrow|condensed|arialn/.test(key)) return { font: "roboto-condensed" };
937
+ if (/mono|courier|\bcour\b|consola|lucon|menlo/.test(key))
938
+ return { font: "jetbrains-mono" };
939
+ if (/times|georgia|cambria|garamond|serif|\bbook/.test(key))
940
+ return { font: "source-serif" };
941
+ if (/comic/.test(key)) return { font: "comic-neue" };
942
+ if (/\.shx$/i.test(file) || !file && !family || SHX_STEMS.has(stem))
943
+ return {
944
+ font: "osifont",
945
+ .../italic/.test(stem) ? { italic: true } : {}
946
+ };
947
+ return { font: family || stem };
948
+ };
175
949
 
176
950
  // ../core/src/model/objects.ts
177
951
  var PointSchema = ObjectBaseSchema.extend({
178
- type: z2.literal("point"),
952
+ type: z3.literal("point"),
179
953
  p: Vec2Schema
180
954
  });
181
955
  var LineSchema = ObjectBaseSchema.extend({
182
- type: z2.literal("line"),
956
+ type: z3.literal("line"),
183
957
  a: Vec2Schema,
184
958
  b: Vec2Schema
185
959
  });
186
- var PolylineVertexSchema = z2.preprocess(
960
+ var PolylineVertexSchema = z3.preprocess(
187
961
  (v) => Array.isArray(v) && v.length >= 2 ? { x: v[0], y: v[1], bulge: v[2] ?? 0 } : v,
188
- z2.object({ x: z2.number(), y: z2.number(), bulge: z2.number().default(0) })
962
+ z3.object({ x: z3.number(), y: z3.number(), bulge: z3.number().default(0) })
189
963
  );
190
964
  var PolylineSchema = ObjectBaseSchema.extend({
191
- type: z2.literal("polyline"),
192
- points: z2.array(PolylineVertexSchema).min(2),
193
- closed: z2.boolean().default(false)
965
+ type: z3.literal("polyline"),
966
+ points: z3.array(PolylineVertexSchema).min(2),
967
+ closed: z3.boolean().default(false)
194
968
  });
195
969
  var CircleSchema = ObjectBaseSchema.extend({
196
- type: z2.literal("circle"),
970
+ type: z3.literal("circle"),
197
971
  center: Vec2Schema,
198
- radius: z2.number().positive()
972
+ radius: z3.number().positive()
199
973
  });
200
974
  var ArcSchema = ObjectBaseSchema.extend({
201
- type: z2.literal("arc"),
975
+ type: z3.literal("arc"),
202
976
  center: Vec2Schema,
203
- radius: z2.number().positive(),
204
- startAngle: z2.number(),
205
- endAngle: z2.number()
977
+ radius: z3.number().positive(),
978
+ startAngle: z3.number(),
979
+ endAngle: z3.number()
206
980
  });
207
981
  var EllipseSchema = ObjectBaseSchema.extend({
208
- type: z2.literal("ellipse"),
982
+ type: z3.literal("ellipse"),
209
983
  center: Vec2Schema,
210
984
  majorAxis: Vec2Schema,
211
- ratio: z2.number().gt(0).max(1),
212
- startAngle: z2.number().default(0),
213
- endAngle: z2.number().default(Math.PI * 2)
985
+ ratio: z3.number().gt(0).max(1),
986
+ startAngle: z3.number().default(0),
987
+ endAngle: z3.number().default(Math.PI * 2)
214
988
  });
215
- var HatchLoopSchema = z2.object({
216
- points: z2.array(PolylineVertexSchema).min(3)
989
+ var HatchLoopSchema = z3.object({
990
+ points: z3.array(PolylineVertexSchema).min(3)
217
991
  });
218
- var HatchFillSchema = z2.discriminatedUnion("kind", [
219
- z2.object({ kind: z2.literal("solid") }),
220
- z2.object({
221
- kind: z2.literal("pattern"),
222
- name: z2.string(),
223
- angle: z2.number(),
224
- scale: z2.number().positive()
992
+ var HatchFillSchema = z3.discriminatedUnion("kind", [
993
+ z3.object({ kind: z3.literal("solid") }),
994
+ z3.object({
995
+ kind: z3.literal("pattern"),
996
+ name: z3.string(),
997
+ angle: z3.number(),
998
+ scale: z3.number().positive(),
999
+ /** Where the pattern frame is anchored, world mm (absent = the drawing
1000
+ * origin, like a hatch drawn here). Set by import for hatches that came
1001
+ * out of blocks — AutoCAD anchors a block's hatch to the block, so the
1002
+ * pattern phase follows the insertion — and carried by the transforms. */
1003
+ origin: Vec2Schema.optional()
225
1004
  }),
226
- z2.object({
227
- kind: z2.literal("lines"),
228
- angle: z2.number(),
229
- spacing: z2.number().positive()
1005
+ z3.object({
1006
+ kind: z3.literal("lines"),
1007
+ angle: z3.number(),
1008
+ spacing: z3.number().positive()
230
1009
  }),
231
- z2.object({
232
- kind: z2.literal("gradient"),
1010
+ z3.object({
1011
+ kind: z3.literal("gradient"),
233
1012
  /** linear = color1→color2 along `angle`; radial = color1 at the
234
1013
  * boundary's center fading to color2 at its edge. */
235
- shape: z2.enum(["linear", "radial"]).default("linear"),
236
- color1: z2.string().default("#808080"),
237
- color2: z2.string().default("#2d2d2d"),
1014
+ shape: z3.enum(["linear", "radial"]).default("linear"),
1015
+ color1: z3.string().default("#808080"),
1016
+ color2: z3.string().default("#2d2d2d"),
238
1017
  /** Direction, radians CCW: 0 = color1 on the left (linear only). */
239
- angle: z2.number().default(0)
1018
+ angle: z3.number().default(0)
240
1019
  })
241
1020
  ]);
242
1021
  var HatchSchema = ObjectBaseSchema.extend({
243
- type: z2.literal("hatch"),
244
- loops: z2.array(HatchLoopSchema).min(1),
1022
+ type: z3.literal("hatch"),
1023
+ loops: z3.array(HatchLoopSchema).min(1),
245
1024
  fill: HatchFillSchema.default({
246
1025
  kind: "lines",
247
1026
  angle: Math.PI / 4,
248
1027
  spacing: 5
249
1028
  }),
250
1029
  /** Pattern spacing multiplier (same idea as DimStyle.scale). */
251
- scale: z2.number().positive().default(1)
1030
+ scale: z3.number().positive().default(1)
252
1031
  });
253
- var ArrowTypeSchema = z2.enum(["filled", "open", "tick", "dot", "none"]);
254
- var TextStyleSchema = z2.object({
255
- id: z2.string().default(() => crypto.randomUUID()),
256
- name: z2.string().default("Standard"),
1032
+ var ArrowTypeSchema = z3.enum(["filled", "open", "tick", "dot", "none"]);
1033
+ var TextStyleSchema = z3.object({
1034
+ id: z3.string().default(() => crypto.randomUUID()),
1035
+ name: z3.string().default("Standard"),
257
1036
  /** Font id (model/fonts.ts). */
258
- font: z2.string().default("arial"),
1037
+ font: z3.string().default("arial"),
259
1038
  /** AutoCAD-style width factor: horizontal glyph scale. */
260
- widthFactor: z2.number().positive().default(1),
1039
+ widthFactor: z3.number().positive().default(1),
261
1040
  /** Bold / italic face (bundled outlines where the font has them, else
262
1041
  * synthesized — model/fonts.ts `fontFace`). Absent = regular. */
263
- bold: z2.boolean().optional(),
264
- italic: z2.boolean().optional(),
1042
+ bold: z3.boolean().optional(),
1043
+ italic: z3.boolean().optional(),
265
1044
  /** Cap height in mm (world), before the object's `scale`. */
266
- textHeight: z2.number().positive().default(2.5),
1045
+ textHeight: z3.number().positive().default(2.5),
267
1046
  /** Leader arrowhead style. */
268
1047
  arrowType: ArrowTypeSchema.default("filled"),
269
1048
  /** Leader arrowhead size (mm), before the object's `scale`. */
270
- arrowSize: z2.number().positive().default(2.5)
1049
+ arrowSize: z3.number().positive().default(2.5)
271
1050
  });
272
1051
  var DEFAULT_TEXT_STYLE = {
273
1052
  id: "standard",
@@ -278,14 +1057,14 @@ var DEFAULT_TEXT_STYLE = {
278
1057
  arrowType: "filled",
279
1058
  arrowSize: 2.5
280
1059
  };
281
- var TextStyleOverrideSchema = z2.object({
282
- font: z2.string().optional(),
283
- widthFactor: z2.number().positive().optional(),
284
- bold: z2.boolean().optional(),
285
- italic: z2.boolean().optional(),
286
- textHeight: z2.number().positive().optional(),
1060
+ var TextStyleOverrideSchema = z3.object({
1061
+ font: z3.string().optional(),
1062
+ widthFactor: z3.number().positive().optional(),
1063
+ bold: z3.boolean().optional(),
1064
+ italic: z3.boolean().optional(),
1065
+ textHeight: z3.number().positive().optional(),
287
1066
  arrowType: ArrowTypeSchema.optional(),
288
- arrowSize: z2.number().positive().optional()
1067
+ arrowSize: z3.number().positive().optional()
289
1068
  });
290
1069
  var textStyleFor = (obj, styles) => styles.find((s) => s.id === obj.styleId) ?? styles[0] ?? DEFAULT_TEXT_STYLE;
291
1070
  var resolveTextStyle = (obj, styles) => ({ ...textStyleFor(obj, styles), ...obj.styleOverride });
@@ -301,62 +1080,79 @@ var effectiveTextFont = (obj, styles) => {
301
1080
  widthFactor: o?.widthFactor ?? base.widthFactor
302
1081
  };
303
1082
  };
1083
+ var TextSpanSchema = z3.object({
1084
+ start: z3.number().int().min(0),
1085
+ end: z3.number().int().min(0),
1086
+ /** Font id (model/fonts.ts). */
1087
+ font: z3.string().optional(),
1088
+ bold: z3.boolean().optional(),
1089
+ italic: z3.boolean().optional(),
1090
+ underline: z3.boolean().optional(),
1091
+ /** Literal colour; absent = the text's own (possibly ByLayer) colour. */
1092
+ color: ColorSchema.optional(),
1093
+ /** × the text's cap height. */
1094
+ height: z3.number().positive().optional(),
1095
+ script: z3.enum(["sub", "super"]).optional()
1096
+ });
304
1097
  var TextSchema = ObjectBaseSchema.extend({
305
- type: z2.literal("text"),
1098
+ type: z3.literal("text"),
306
1099
  position: Vec2Schema,
307
- content: z2.string(),
1100
+ content: z3.string(),
1101
+ /** Character formatting over `content`; absent = all plain. Replacing
1102
+ * `content` without new spans drops them (offsets would be stale). */
1103
+ spans: z3.array(TextSpanSchema).optional(),
308
1104
  /** Arrow polylines pointing at this text (arrowhead at [0]). */
309
- leaderLines: z2.array(z2.array(Vec2Schema).min(2)).default([]),
1105
+ leaderLines: z3.array(z3.array(Vec2Schema).min(2)).default([]),
310
1106
  /**
311
1107
  * Max text box width in mm (before `scale`) — the word-wrap boundary and the
312
1108
  * frame width. Undefined = no wrapping (lines break only on explicit
313
1109
  * newlines). Maps to the DXF MTEXT reference rectangle width.
314
1110
  */
315
- width: z2.number().positive().optional(),
316
- hAlign: z2.enum(["left", "center", "right"]).default("center"),
317
- vAlign: z2.enum(["bottom", "center", "top"]).default("center"),
1111
+ width: z3.number().positive().optional(),
1112
+ hAlign: z3.enum(["left", "center", "right"]).default("center"),
1113
+ vAlign: z3.enum(["bottom", "center", "top"]).default("center"),
318
1114
  /** Border drawn around the text: a rectangle, a circle, a slot (rounded
319
1115
  * ends), a line under the text or under its first line. */
320
- frame: z2.enum(["none", "rectangle", "circle", "slot", "bottom", "firstLine"]).optional(),
1116
+ frame: z3.enum(["none", "rectangle", "circle", "slot", "bottom", "firstLine"]).optional(),
321
1117
  /** The document text style this text follows (absent/dangling = the
322
1118
  * document's first style). */
323
- styleId: z2.string().optional(),
1119
+ styleId: z3.string().optional(),
324
1120
  /** Style keys the user set on THIS text (font, height, arrows, …);
325
1121
  * absent = inherit the referenced style. */
326
1122
  styleOverride: TextStyleOverrideSchema.optional(),
327
- rotation: z2.number().default(0),
1123
+ rotation: z3.number().default(0),
328
1124
  /** Size multiplier for height + arrow size (same idea as
329
1125
  * Dimension.scale). */
330
- scale: z2.number().positive().default(1)
1126
+ scale: z3.number().positive().default(1)
331
1127
  });
332
- var DimensionKindSchema = z2.enum([
1128
+ var DimensionKindSchema = z3.enum([
333
1129
  "linear",
334
1130
  "angular",
335
1131
  "radial",
336
1132
  "diameter"
337
1133
  ]);
338
- var DimStyleSchema = z2.object({
339
- id: z2.string().default(() => crypto.randomUUID()),
340
- name: z2.string().default("Standard"),
1134
+ var DimStyleSchema = z3.object({
1135
+ id: z3.string().default(() => crypto.randomUUID()),
1136
+ name: z3.string().default("Standard"),
341
1137
  /** Font id for dimension text (model/fonts.ts). */
342
- font: z2.string().default("arial"),
1138
+ font: z3.string().default("arial"),
343
1139
  /** Horizontal glyph scale for dimension text. */
344
- widthFactor: z2.number().positive().default(1),
1140
+ widthFactor: z3.number().positive().default(1),
345
1141
  /** Bold / italic dimension text (same faces as TextStyle). */
346
- bold: z2.boolean().optional(),
347
- italic: z2.boolean().optional(),
348
- textHeight: z2.number().positive().default(2.5),
1142
+ bold: z3.boolean().optional(),
1143
+ italic: z3.boolean().optional(),
1144
+ textHeight: z3.number().positive().default(2.5),
349
1145
  /** Dimension line → text center distance (mm, × scale; DXF DIMGAP-ish). */
350
- textOffset: z2.number().default(1.8),
1146
+ textOffset: z3.number().default(1.8),
351
1147
  arrowType: ArrowTypeSchema.default("tick"),
352
- arrowSize: z2.number().positive().default(2.5),
1148
+ arrowSize: z3.number().positive().default(2.5),
353
1149
  /** "long" runs each extension line from the measured point to the
354
1150
  * dimension line; "short" draws only a 2×overshoot tick across it. */
355
- extensionType: z2.enum(["long", "short"]).default("long"),
356
- extensionOffset: z2.number().default(1),
357
- extensionOvershoot: z2.number().default(1.25),
1151
+ extensionType: z3.enum(["long", "short"]).default("long"),
1152
+ extensionOffset: z3.number().default(1),
1153
+ extensionOvershoot: z3.number().default(1.25),
358
1154
  /** Decimal places for the measurement text. */
359
- precision: z2.number().int().min(0).max(6).default(1)
1155
+ precision: z3.number().int().min(0).max(6).default(1)
360
1156
  });
361
1157
  var DEFAULT_DIM_STYLE = {
362
1158
  id: "standard",
@@ -372,19 +1168,19 @@ var DEFAULT_DIM_STYLE = {
372
1168
  extensionOvershoot: 1,
373
1169
  precision: 1
374
1170
  };
375
- var DimStyleOverrideSchema = z2.object({
376
- font: z2.string().optional(),
377
- widthFactor: z2.number().positive().optional(),
378
- bold: z2.boolean().optional(),
379
- italic: z2.boolean().optional(),
380
- textHeight: z2.number().positive().optional(),
381
- textOffset: z2.number().optional(),
1171
+ var DimStyleOverrideSchema = z3.object({
1172
+ font: z3.string().optional(),
1173
+ widthFactor: z3.number().positive().optional(),
1174
+ bold: z3.boolean().optional(),
1175
+ italic: z3.boolean().optional(),
1176
+ textHeight: z3.number().positive().optional(),
1177
+ textOffset: z3.number().optional(),
382
1178
  arrowType: ArrowTypeSchema.optional(),
383
- arrowSize: z2.number().positive().optional(),
384
- extensionType: z2.enum(["long", "short"]).optional(),
385
- extensionOffset: z2.number().optional(),
386
- extensionOvershoot: z2.number().optional(),
387
- precision: z2.number().int().min(0).max(6).optional()
1179
+ arrowSize: z3.number().positive().optional(),
1180
+ extensionType: z3.enum(["long", "short"]).optional(),
1181
+ extensionOffset: z3.number().optional(),
1182
+ extensionOvershoot: z3.number().optional(),
1183
+ precision: z3.number().int().min(0).max(6).optional()
388
1184
  });
389
1185
  var unknownStyleOverrideKeys = (input) => {
390
1186
  const so = input.styleOverride;
@@ -403,48 +1199,114 @@ var assertKnownStyleOverrideKeys = (input, where) => {
403
1199
  };
404
1200
  var dimStyleFor = (obj, styles) => styles.find((s) => s.id === obj.styleId) ?? styles[0] ?? DEFAULT_DIM_STYLE;
405
1201
  var dimFontFace = (style) => fontFace(style.font, style.bold, style.italic);
406
- var DimensionSchema = ObjectBaseSchema.extend({
407
- type: z2.literal("dimension"),
1202
+ var DIM_VALUE_TOKEN = "{value}";
1203
+ var DimLabelSchema = z3.object({
1204
+ /** mm along the dimension line from the segment's origin (linear: the
1205
+ * foot of the segment's first point; angular: where ray a meets the arc,
1206
+ * as arc length; radial/diameter: the center). Undefined = automatic
1207
+ * placement: centered when the text fits, else outside. */
1208
+ along: z3.number().optional(),
1209
+ /** mm perpendicular to the dimension line, positive on the side the
1210
+ * dimension line is on (linear/angular) or to the left of `along`
1211
+ * (radial/diameter). Undefined or 0 = on the line; otherwise the text
1212
+ * floats, with a leader when it no longer overlaps its segment. */
1213
+ across: z3.number().optional(),
1214
+ /** Text; `{value}` is replaced by the formatted measurement. Undefined =
1215
+ * the kind's default template ("{value}", "{value}°", "R{value}",
1216
+ * "Ø{value}"). */
1217
+ content: z3.string().optional(),
1218
+ /** Character formatting over `content` (the TEMPLATE: a span covering
1219
+ * `{value}` formats the measurement). Needs `content` set. */
1220
+ spans: z3.array(TextSpanSchema).optional()
1221
+ });
1222
+ var DimensionShapeSchema = ObjectBaseSchema.extend({
1223
+ type: z3.literal("dimension"),
408
1224
  kind: DimensionKindSchema.default("linear"),
409
1225
  /**
410
- * Measured points. Linear dims with MORE than two points render as a
411
- * CHAIN: one measurement per consecutive pair, sharing the dimension
412
- * line (text placement is derived — see `offset`, not a text anchor).
1226
+ * Measured points. linear: 2+ (more than two render as a CHAIN — one
1227
+ * measurement per consecutive pair sharing the dimension line);
1228
+ * angular: [vertex, a, b]; radial: [center, curve point]; diameter:
1229
+ * [p1, p2] opposite on the curve.
1230
+ */
1231
+ points: z3.array(Vec2Schema).min(2),
1232
+ /**
1233
+ * linear only: the measuring direction, radians CCW from +X (DXF's
1234
+ * "rotated" dimension, code 50). Undefined = aligned to
1235
+ * points[0] → points[last] (DXF "aligned"), so dragging a point turns the
1236
+ * dimension; set, the direction is locked and a point drag only changes
1237
+ * the measurement.
413
1238
  */
414
- points: z2.array(Vec2Schema).min(1),
1239
+ rotation: z3.number().optional(),
415
1240
  /**
416
- * Dimension line placement, anchored at the kind's base point (points[0];
417
- * diameter: the points' midpoint): anchor + offset lies on the dimension
418
- * line (angular: on the arc — its length is the arc radius; radial /
419
- * diameter: at the text). The DIRECTION is set at creation and preserved
420
- * afterwards — the offset grip only rescales the length.
1241
+ * linear: signed distance of the dimension line from points[0] along the
1242
+ * LEFT perpendicular of the measuring direction. angular: the arc radius,
1243
+ * positive for the arc running CCW from a to b, negative for the
1244
+ * complementary arc. radial/diameter: absent — the line's direction comes
1245
+ * from the points and the text position is labels[0].along.
421
1246
  */
422
- offset: Vec2Schema,
423
- textOverride: z2.string().optional(),
1247
+ offset: z3.number().optional(),
1248
+ /** One label per measured segment (linear chain: points.length − 1,
1249
+ * every other kind: 1). Missing or short = automatic. */
1250
+ labels: z3.array(DimLabelSchema).default([]),
424
1251
  /** The document dimension style this dimension follows (absent/dangling =
425
1252
  * the document's first style). */
426
- styleId: z2.string().optional(),
1253
+ styleId: z3.string().optional(),
427
1254
  styleOverride: DimStyleOverrideSchema.optional(),
428
1255
  /** Annotation size multiplier (text/arrows/offsets — same idea as
429
1256
  * Text.scale; rides as DIMSCALE in DXF). */
430
- scale: z2.number().positive().default(1)
1257
+ scale: z3.number().positive().default(1)
431
1258
  });
1259
+ var dimensionRefinement = (d, ctx) => {
1260
+ const need = d.kind === "angular" ? 3 : 2;
1261
+ if (d.points.length < need)
1262
+ ctx.addIssue({
1263
+ code: "custom",
1264
+ path: ["points"],
1265
+ message: `a ${d.kind} dimension needs at least ${need} points`
1266
+ });
1267
+ if (d.kind !== "linear" && d.kind !== "angular") {
1268
+ if (d.offset !== void 0)
1269
+ ctx.addIssue({
1270
+ code: "custom",
1271
+ path: ["offset"],
1272
+ message: `offset does not apply to a ${d.kind} dimension (the text position is labels[0].along)`
1273
+ });
1274
+ } else if (d.offset === void 0)
1275
+ ctx.addIssue({
1276
+ code: "custom",
1277
+ path: ["offset"],
1278
+ message: `a ${d.kind} dimension needs offset`
1279
+ });
1280
+ if (d.kind !== "linear" && d.rotation !== void 0)
1281
+ ctx.addIssue({
1282
+ code: "custom",
1283
+ path: ["rotation"],
1284
+ message: "rotation applies to linear dimensions only"
1285
+ });
1286
+ };
1287
+ var DimensionSchema = DimensionShapeSchema.superRefine(dimensionRefinement);
1288
+ var DIM_DEFAULT_CONTENT = {
1289
+ linear: DIM_VALUE_TOKEN,
1290
+ angular: `${DIM_VALUE_TOKEN}\xB0`,
1291
+ radial: `R${DIM_VALUE_TOKEN}`,
1292
+ diameter: `\xD8${DIM_VALUE_TOKEN}`
1293
+ };
432
1294
  var BlockSchema = ObjectBaseSchema.extend({
433
- type: z2.literal("block"),
434
- definitionId: z2.string(),
1295
+ type: z3.literal("block"),
1296
+ definitionId: z3.string(),
435
1297
  /** Applied LAST, about the pivot (the first point input). */
436
- rotation: z2.number().default(0),
437
- scale: z2.number().positive().default(1),
1298
+ rotation: z3.number().default(0),
1299
+ scale: z3.number().positive().default(1),
438
1300
  /** Per-instance parameter values (merged over the definition defaults). */
439
- params: z2.record(
440
- z2.string(),
441
- z2.union([
442
- z2.number(),
443
- z2.string(),
444
- z2.boolean(),
1301
+ params: z3.record(
1302
+ z3.string(),
1303
+ z3.union([
1304
+ z3.number(),
1305
+ z3.string(),
1306
+ z3.boolean(),
445
1307
  // numberList / stringList parameters.
446
- z2.array(z2.number()),
447
- z2.array(z2.string())
1308
+ z3.array(z3.number()),
1309
+ z3.array(z3.string())
448
1310
  ])
449
1311
  ).default({}),
450
1312
  /**
@@ -456,63 +1318,73 @@ var BlockSchema = ObjectBaseSchema.extend({
456
1318
  * Transforms map the points and the script recomputes, which is what
457
1319
  * makes mirror correct for script-derived geometry.
458
1320
  */
459
- inputs: z2.array(z2.union([Vec2Schema, z2.string()])).default([])
1321
+ inputs: z3.array(z3.union([Vec2Schema, z3.string()])).default([])
460
1322
  });
461
1323
  var CommentSchema = ObjectBaseSchema.extend({
462
- type: z2.literal("comment"),
1324
+ type: z3.literal("comment"),
463
1325
  /** Tail tip of the bubble (world/sheet mm). */
464
1326
  position: Vec2Schema,
465
- text: z2.string().default(""),
466
- tags: z2.array(z2.string()).default([])
1327
+ text: z3.string().default(""),
1328
+ tags: z3.array(z3.string()).default([])
467
1329
  });
468
- var TableCellSchema = z2.object({
469
- /** Plain text; "\n" = line break. Long lines wrap at the cell width. */
470
- text: z2.string().default(""),
1330
+ var cellInput = (v) => {
1331
+ if (typeof v === "string") return { content: v };
1332
+ if (v && typeof v === "object" && "text" in v && !("content" in v)) {
1333
+ const { text, ...rest } = v;
1334
+ return { ...rest, content: text };
1335
+ }
1336
+ return v;
1337
+ };
1338
+ var TableCellSchema = z3.object({
1339
+ /** Plain text; "\n" = line break. Long lines wrap at the cell width.
1340
+ * (Named `text` before document v22 — still read, see `cellInput`.) */
1341
+ content: z3.string().default(""),
1342
+ /** Character formatting over `content` (core/table.ts `cellWithContent`
1343
+ * keeps it consistent when the content changes). */
1344
+ spans: z3.array(TextSpanSchema).optional(),
471
1345
  /** Merge, set on the ANCHOR (top-left) cell. Covered cells stay in the
472
1346
  * grid with their content ignored — AutoCAD's model. */
473
- span: z2.object({
474
- cols: z2.number().int().min(1),
475
- rows: z2.number().int().min(1)
1347
+ span: z3.object({
1348
+ cols: z3.number().int().min(1),
1349
+ rows: z3.number().int().min(1)
476
1350
  }).optional(),
477
1351
  /** Where the text sits in the cell; absent = left / center. */
478
- hAlign: z2.enum(["left", "center", "right"]).optional(),
479
- vAlign: z2.enum(["top", "center", "bottom"]).optional(),
480
- styleId: z2.string().optional(),
1352
+ hAlign: z3.enum(["left", "center", "right"]).optional(),
1353
+ vAlign: z3.enum(["top", "center", "bottom"]).optional(),
1354
+ styleId: z3.string().optional(),
481
1355
  styleOverride: TextStyleOverrideSchema.optional(),
482
1356
  /** Text color; absent = the table's. */
483
1357
  color: ColorSchema.optional()
484
1358
  });
485
- var TableSchema = ObjectBaseSchema.extend({
486
- type: z2.literal("table"),
1359
+ var TableShapeSchema = ObjectBaseSchema.extend({
1360
+ type: z3.literal("table"),
487
1361
  position: Vec2Schema,
488
- rotation: z2.number().default(0),
489
- scale: z2.number().positive().default(1),
1362
+ rotation: z3.number().default(0),
1363
+ scale: z3.number().positive().default(1),
490
1364
  /** Column widths (mm). */
491
- columns: z2.array(z2.number().positive()).min(1),
1365
+ columns: z3.array(z3.number().positive()).min(1),
492
1366
  /** Per-row settings. Input may give a row COUNT (all auto-height). */
493
- rows: z2.preprocess(
1367
+ rows: z3.preprocess(
494
1368
  (v) => typeof v === "number" ? Array.from({ length: v }, () => ({})) : v,
495
- z2.array(z2.object({ minHeight: z2.number().positive().optional() })).min(1)
1369
+ z3.array(z3.object({ minHeight: z3.number().positive().optional() })).min(1)
496
1370
  ),
497
1371
  /** Row-major: `cells[row][column]`, exactly rows × columns. Input may
498
1372
  * give a cell as a plain string (= `{ text }`). */
499
- cells: z2.array(
500
- z2.array(
501
- z2.preprocess(
502
- (v) => typeof v === "string" ? { text: v } : v,
503
- TableCellSchema
504
- )
1373
+ cells: z3.array(
1374
+ z3.array(
1375
+ z3.preprocess(cellInput, TableCellSchema)
505
1376
  )
506
1377
  ),
507
1378
  /** Rows above the heavier header rule. */
508
- headerRows: z2.number().int().min(0).default(0),
1379
+ headerRows: z3.number().int().min(0).default(0),
509
1380
  /** Text inset from the cell edges (mm). */
510
- cellMargin: z2.number().min(0).default(1),
511
- borders: z2.enum(["all", "outer", "rows", "none"]).default("all"),
1381
+ cellMargin: z3.number().min(0).default(1),
1382
+ borders: z3.enum(["all", "outer", "rows", "none"]).default("all"),
512
1383
  /** Document text style for every cell (cells may override). */
513
- styleId: z2.string().optional(),
1384
+ styleId: z3.string().optional(),
514
1385
  styleOverride: TextStyleOverrideSchema.optional()
515
- }).superRefine((t, ctx) => {
1386
+ });
1387
+ var tableRefinement = (t, ctx) => {
516
1388
  const issue = (message) => ctx.addIssue({ code: "custom", message });
517
1389
  const nr = t.rows.length;
518
1390
  const nc = t.columns.length;
@@ -536,40 +1408,28 @@ var TableSchema = ObjectBaseSchema.extend({
536
1408
  }
537
1409
  })
538
1410
  );
539
- });
1411
+ };
1412
+ var TableSchema = TableShapeSchema.superRefine(tableRefinement);
540
1413
  var blockPivot = (b) => {
541
1414
  for (const v of b.inputs) if (typeof v !== "string") return v;
542
1415
  return { x: 0, y: 0 };
543
1416
  };
544
1417
  var ViewportSchema = ObjectBaseSchema.extend({
545
- type: z2.literal("viewport"),
1418
+ type: z3.literal("viewport"),
546
1419
  /** Placement on the sheet, mm, origin bottom-left of sheet. */
547
- rect: z2.object({
548
- x: z2.number(),
549
- y: z2.number(),
550
- w: z2.number().positive(),
551
- h: z2.number().positive()
1420
+ rect: z3.object({
1421
+ x: z3.number(),
1422
+ y: z3.number(),
1423
+ w: z3.number().positive(),
1424
+ h: z3.number().positive()
552
1425
  }),
553
1426
  /** World point shown at the center of the rect. */
554
1427
  modelCenter: Vec2Schema.default({ x: 0, y: 0 }),
555
1428
  /** Drawing-scale denominator (model mm per paper mm); 1:50 => 50. */
556
- scale: z2.number().positive().default(1),
1429
+ scale: z3.number().positive().default(1),
557
1430
  /** Pinned: no pan/zoom-through, no move/resize grips. */
558
- locked: z2.boolean().default(false),
559
- hiddenLayerIds: z2.array(z2.string()).default([])
560
- });
561
- var ImageSchema = ObjectBaseSchema.extend({
562
- type: z2.literal("image"),
563
- /** Key into `document.assets`. */
564
- assetId: z2.string(),
565
- /** Bottom-left corner (world/sheet mm) — rotation pivots here. */
566
- origin: Vec2Schema,
567
- width: z2.number().positive(),
568
- height: z2.number().positive(),
569
- /** Radians CCW about `origin`. */
570
- rotation: z2.number().default(0)
571
- // (opacity moved to ObjectBaseSchema — every object paints with it now;
572
- // documents that stored it per-image parse unchanged.)
1431
+ locked: z3.boolean().default(false),
1432
+ hiddenLayerIds: z3.array(z3.string()).default([])
573
1433
  });
574
1434
  var REFERENCE_FORMATS = [
575
1435
  "linea",
@@ -579,54 +1439,55 @@ var REFERENCE_FORMATS = [
579
1439
  "png",
580
1440
  "jpg"
581
1441
  ];
582
- var ReferenceSourceSchema = z2.discriminatedUnion("kind", [
583
- z2.object({
584
- kind: z2.literal("path"),
585
- path: z2.string().min(1),
586
- cacheKey: z2.string().min(1).optional()
1442
+ var ReferenceSourceSchema = z3.discriminatedUnion("kind", [
1443
+ z3.object({
1444
+ kind: z3.literal("path"),
1445
+ path: z3.string().min(1),
1446
+ cacheKey: z3.string().min(1).optional()
587
1447
  }),
588
- z2.object({ kind: z2.literal("url"), url: z2.string().url() })
1448
+ z3.object({ kind: z3.literal("url"), url: z3.string().url() }),
1449
+ z3.object({ kind: z3.literal("asset"), assetId: z3.string().min(1) })
589
1450
  ]);
590
1451
  var ReferenceSchema = ObjectBaseSchema.extend({
591
- type: z2.literal("reference"),
592
- format: z2.enum(REFERENCE_FORMATS),
593
- name: z2.string().min(1),
1452
+ type: z3.literal("reference"),
1453
+ format: z3.enum(REFERENCE_FORMATS),
1454
+ name: z3.string().min(1),
594
1455
  source: ReferenceSourceSchema,
595
1456
  position: Vec2Schema,
596
1457
  /** Radians CCW about `position`. */
597
- rotation: z2.number().default(0),
598
- scale: z2.number().positive().default(1),
599
- mirrored: z2.boolean().optional(),
1458
+ rotation: z3.number().default(0),
1459
+ scale: z3.number().positive().default(1),
1460
+ mirrored: z3.boolean().optional(),
600
1461
  /** PDF: the 1-based page drawn, and how (vector lines + text / raster). */
601
- pdfPage: z2.number().int().positive().optional(),
602
- pdfMode: z2.enum(["vector", "raster"]).optional(),
1462
+ pdfPage: z3.number().int().positive().optional(),
1463
+ pdfMode: z3.enum(["vector", "raster"]).optional(),
603
1464
  /** The FILE's layers not drawn, by name (stable across reloads). */
604
- hiddenLayers: z2.array(z2.string()).default([]),
1465
+ hiddenLayers: z3.array(z3.string()).default([]),
605
1466
  /** Switched off in the References panel: not drawn, picked or snapped. */
606
- hidden: z2.boolean().optional(),
1467
+ hidden: z3.boolean().optional(),
607
1468
  /** Locked in the References panel: drawn and snapped, but never hovered
608
1469
  * or picked on the canvas — select it from the panel instead. */
609
- locked: z2.boolean().optional(),
1470
+ locked: z3.boolean().optional(),
610
1471
  /** Crop rectangle in FILE coordinates: only content inside is drawn and
611
1472
  * snapped (AutoCAD's XCLIP, rectangular). */
612
- clip: z2.object({
613
- minX: z2.number(),
614
- minY: z2.number(),
615
- maxX: z2.number(),
616
- maxY: z2.number()
1473
+ clip: z3.object({
1474
+ minX: z3.number(),
1475
+ minY: z3.number(),
1476
+ maxX: z3.number(),
1477
+ maxY: z3.number()
617
1478
  }).optional(),
618
1479
  /**
619
1480
  * The content's bounds in file coordinates, recorded on each successful
620
1481
  * load: a missing file still has a frame to draw, pick and snap to.
621
1482
  */
622
- extent: z2.object({
623
- minX: z2.number(),
624
- minY: z2.number(),
625
- maxX: z2.number(),
626
- maxY: z2.number()
1483
+ extent: z3.object({
1484
+ minX: z3.number(),
1485
+ minY: z3.number(),
1486
+ maxX: z3.number(),
1487
+ maxY: z3.number()
627
1488
  }).optional()
628
1489
  });
629
- var ModelObjectSchema = z2.discriminatedUnion("type", [
1490
+ var ModelObjectSchema = z3.discriminatedUnion("type", [
630
1491
  PointSchema,
631
1492
  LineSchema,
632
1493
  PolylineSchema,
@@ -637,13 +1498,15 @@ var ModelObjectSchema = z2.discriminatedUnion("type", [
637
1498
  TextSchema,
638
1499
  DimensionSchema,
639
1500
  BlockSchema,
640
- ImageSchema,
641
1501
  ViewportSchema,
642
1502
  CommentSchema,
643
1503
  TableSchema,
644
1504
  ReferenceSchema
645
1505
  ]);
646
1506
 
1507
+ // ../core/src/model/lineTypes.ts
1508
+ import { z as z4 } from "zod";
1509
+
647
1510
  // ../core/src/model/builtinLin.ts
648
1511
  var BUILTIN_LIN = `;; Linea built-in line type library.
649
1512
  ;;
@@ -799,7 +1662,7 @@ var CURATED = [
799
1662
  dxfName: "DIVIDE"
800
1663
  }
801
1664
  ];
802
- var normalizeName = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
1665
+ var normalizeName2 = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
803
1666
  var asciiPreview = (dashesMm) => {
804
1667
  const WIDTH = 15;
805
1668
  if (!dashesMm.length) return "_".repeat(WIDTH * 0.8);
@@ -820,9 +1683,9 @@ var withPreview = (p) => {
820
1683
  return { ...p, label: `${p.label} ${asciiPreview(p.dashesMm)}` };
821
1684
  };
822
1685
  var BUILT_IN = CURATED.map(withPreview);
823
- var BY_NAME = new Map(BUILT_IN.map((p) => [normalizeName(p.name), p]));
1686
+ var BY_NAME = new Map(BUILT_IN.map((p) => [normalizeName2(p.name), p]));
824
1687
  for (const p of parseLinDefinitions(BUILTIN_LIN)) {
825
- const key = normalizeName(p.name);
1688
+ const key = normalizeName2(p.name);
826
1689
  if (BY_NAME.has(key)) continue;
827
1690
  const pattern2 = withPreview({
828
1691
  name: p.name,
@@ -840,15 +1703,40 @@ var scaleVariantBase = (name) => {
840
1703
  };
841
1704
  for (const p of BUILT_IN) {
842
1705
  const base = scaleVariantBase(p.name);
843
- const target = base ? BY_NAME.get(normalizeName(base)) : void 0;
1706
+ const target = base ? BY_NAME.get(normalizeName2(base)) : void 0;
844
1707
  if (target && target !== p) p.hidden = true;
845
1708
  }
846
1709
  var LINE_TYPE_NAMES = BUILT_IN.filter((p) => !p.hidden).map(
847
1710
  (p) => p.name
848
1711
  );
849
1712
  var LINE_TYPE_DEFS = BUILT_IN;
850
- var getLineType = (name) => BY_NAME.get(normalizeName(name));
1713
+ var getLineType = (name) => BY_NAME.get(normalizeName2(name));
851
1714
  var renderDashes = (pattern2) => pattern2.dashesMm.length ? pattern2.dashesMm.map((v) => v === 0 ? 0.1 : Math.abs(v)) : void 0;
1715
+ var LineTypeSchema = z4.object({
1716
+ /** Stored on objects/layers as `lineType`; case-insensitive, unique per
1717
+ * document. */
1718
+ name: z4.string().min(1),
1719
+ description: z4.string().optional(),
1720
+ /** Signed dash cycle, mm per 1 mm of line width: >0 dash, <0 gap, 0 dot.
1721
+ * Empty = continuous. */
1722
+ dashesMm: z4.array(z4.number())
1723
+ });
1724
+ var lineTypeKey = (name) => normalizeName2(name);
1725
+ var lineTypeAsPattern = (t) => ({
1726
+ name: t.name,
1727
+ label: t.description ?? t.name,
1728
+ dashesMm: t.dashesMm,
1729
+ dxfName: t.name.toUpperCase()
1730
+ });
1731
+ var resolveLineType = (name, lineTypes) => {
1732
+ if (!name) return void 0;
1733
+ if (lineTypes && lineTypes.length > 0) {
1734
+ const key = normalizeName2(name);
1735
+ const own = lineTypes.find((t) => normalizeName2(t.name) === key);
1736
+ if (own) return lineTypeAsPattern(own);
1737
+ }
1738
+ return getLineType(name);
1739
+ };
852
1740
 
853
1741
  // ../core/src/geometry/vec2.ts
854
1742
  var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
@@ -939,105 +1827,105 @@ var readListParam = (type, value) => {
939
1827
  };
940
1828
 
941
1829
  // ../core/src/model/blockDefinition.ts
942
- import { z as z3 } from "zod";
1830
+ import { z as z5 } from "zod";
943
1831
  var paramBase = {
944
- name: z3.string().regex(/^[a-zA-Z_]\w*$/),
945
- label: z3.string().optional(),
1832
+ name: z5.string().regex(/^[a-zA-Z_]\w*$/),
1833
+ label: z5.string().optional(),
946
1834
  /** What the parameter means: the label's tooltip in the panel. */
947
- hint: z3.string().optional()
1835
+ hint: z5.string().optional()
948
1836
  };
949
1837
  var HEX_COLOR = /^#[0-9a-f]{6}$/i;
950
- var EnumOptionSchema = z3.preprocess(
1838
+ var EnumOptionSchema = z5.preprocess(
951
1839
  (raw) => typeof raw === "string" ? { value: raw } : raw,
952
- z3.object({
953
- value: z3.string(),
954
- label: z3.string().optional(),
1840
+ z5.object({
1841
+ value: z5.string(),
1842
+ label: z5.string().optional(),
955
1843
  /** A `#rrggbb` color square before the option's label. */
956
- swatch: z3.string().regex(HEX_COLOR).optional()
1844
+ swatch: z5.string().regex(HEX_COLOR).optional()
957
1845
  })
958
1846
  );
959
- var NumberParam = z3.object({
1847
+ var NumberParam = z5.object({
960
1848
  ...paramBase,
961
- type: z3.literal("number"),
962
- default: z3.number(),
963
- min: z3.number().optional(),
964
- max: z3.number().optional(),
1849
+ type: z5.literal("number"),
1850
+ default: z5.number(),
1851
+ min: z5.number().optional(),
1852
+ max: z5.number().optional(),
965
1853
  /** Whole numbers only. */
966
- integer: z3.boolean().optional(),
1854
+ integer: z5.boolean().optional(),
967
1855
  /** ArrowUp / ArrowDown increment in the panel (default 1). */
968
- step: z3.number().positive().optional(),
1856
+ step: z5.number().positive().optional(),
969
1857
  /** Shown inside the field before / after the value ("1:", "mm"). */
970
- prefix: z3.string().optional(),
971
- suffix: z3.string().optional(),
1858
+ prefix: z5.string().optional(),
1859
+ suffix: z5.string().optional(),
972
1860
  /** Standard values offered in the field's dropdown; typing stays free. */
973
- presets: z3.array(z3.number()).optional()
1861
+ presets: z5.array(z5.number()).optional()
974
1862
  });
975
- var StringParam = z3.object({
1863
+ var StringParam = z5.object({
976
1864
  ...paramBase,
977
- type: z3.literal("string"),
978
- default: z3.string(),
979
- placeholder: z3.string().optional(),
1865
+ type: z5.literal("string"),
1866
+ default: z5.string(),
1867
+ placeholder: z5.string().optional(),
980
1868
  /** A textarea; Shift+Enter starts a new line. */
981
- multiline: z3.boolean().optional(),
982
- presets: z3.array(z3.string()).optional()
1869
+ multiline: z5.boolean().optional(),
1870
+ presets: z5.array(z5.string()).optional()
983
1871
  });
984
- var BooleanParam = z3.object({
1872
+ var BooleanParam = z5.object({
985
1873
  ...paramBase,
986
- type: z3.literal("boolean"),
987
- default: z3.boolean(),
1874
+ type: z5.literal("boolean"),
1875
+ default: z5.boolean(),
988
1876
  /** "toggle": a toggle button showing the label (compact, e.g. in a row
989
1877
  * beside a picker) instead of a switch. */
990
- display: z3.enum(["switch", "toggle"]).optional()
1878
+ display: z5.enum(["switch", "toggle"]).optional()
991
1879
  });
992
- var NumberListParam = z3.object({
1880
+ var NumberListParam = z5.object({
993
1881
  ...paramBase,
994
- type: z3.literal("numberList"),
995
- default: z3.array(z3.number()),
1882
+ type: z5.literal("numberList"),
1883
+ default: z5.array(z5.number()),
996
1884
  /** Shown while the list is empty. */
997
- placeholder: z3.string().optional(),
998
- min: z3.number().optional(),
999
- max: z3.number().optional(),
1000
- presets: z3.array(z3.array(z3.number())).optional()
1885
+ placeholder: z5.string().optional(),
1886
+ min: z5.number().optional(),
1887
+ max: z5.number().optional(),
1888
+ presets: z5.array(z5.array(z5.number())).optional()
1001
1889
  });
1002
- var StringListParam = z3.object({
1890
+ var StringListParam = z5.object({
1003
1891
  ...paramBase,
1004
- type: z3.literal("stringList"),
1005
- default: z3.array(z3.string()),
1892
+ type: z5.literal("stringList"),
1893
+ default: z5.array(z5.string()),
1006
1894
  /** Shown while the list is empty. */
1007
- placeholder: z3.string().optional(),
1008
- presets: z3.array(z3.array(z3.string())).optional()
1895
+ placeholder: z5.string().optional(),
1896
+ presets: z5.array(z5.array(z5.string())).optional()
1009
1897
  });
1010
- var ColorParam = z3.object({
1898
+ var ColorParam = z5.object({
1011
1899
  ...paramBase,
1012
- type: z3.literal("color"),
1013
- default: z3.string().regex(HEX_COLOR),
1014
- presets: z3.array(z3.string().regex(HEX_COLOR)).optional()
1900
+ type: z5.literal("color"),
1901
+ default: z5.string().regex(HEX_COLOR),
1902
+ presets: z5.array(z5.string().regex(HEX_COLOR)).optional()
1015
1903
  });
1016
- var EnumParam = z3.object({
1904
+ var EnumParam = z5.object({
1017
1905
  ...paramBase,
1018
- type: z3.literal("enum"),
1019
- default: z3.string(),
1020
- options: z3.array(EnumOptionSchema).min(1),
1906
+ type: z5.literal("enum"),
1907
+ default: z5.string(),
1908
+ options: z5.array(EnumOptionSchema).min(1),
1021
1909
  /** "buttons": a segmented toggle group of the options (short choices);
1022
1910
  * the panel falls back to the dropdown when they don't fit. */
1023
- display: z3.enum(["dropdown", "buttons"]).optional()
1911
+ display: z5.enum(["dropdown", "buttons"]).optional()
1024
1912
  });
1025
- var resourceParam = (type, fallback) => z3.object({
1913
+ var resourceParam = (type, fallback) => z5.object({
1026
1914
  ...paramBase,
1027
- type: z3.literal(type),
1028
- default: z3.string().default(fallback)
1915
+ type: z5.literal(type),
1916
+ default: z5.string().default(fallback)
1029
1917
  });
1030
1918
  var TextStyleParam = resourceParam("textStyle", "");
1031
1919
  var DimStyleParam = resourceParam("dimStyle", "");
1032
1920
  var FontParam = resourceParam("font", "arial");
1033
1921
  var LineTypeParam = resourceParam("lineType", "continuous");
1034
1922
  var HatchPatternParam = resourceParam("hatchPattern", "ANSI31");
1035
- var LabelParam = z3.object({ ...paramBase, type: z3.literal("label") });
1923
+ var LabelParam = z5.object({ ...paramBase, type: z5.literal("label") });
1036
1924
  var enumDefaultIsAnOption = (p) => p.type !== "enum" || p.options.some(
1037
1925
  (o) => o.value === p.default
1038
1926
  );
1039
1927
  var ENUM_DEFAULT = { message: "enum default must be one of its options" };
1040
- var RowItemSchema = z3.discriminatedUnion("type", [
1928
+ var RowItemSchema = z5.discriminatedUnion("type", [
1041
1929
  NumberParam,
1042
1930
  StringParam,
1043
1931
  BooleanParam,
@@ -1049,12 +1937,12 @@ var RowItemSchema = z3.discriminatedUnion("type", [
1049
1937
  LineTypeParam,
1050
1938
  HatchPatternParam
1051
1939
  ]).refine(enumDefaultIsAnOption, ENUM_DEFAULT);
1052
- var RowParam = z3.object({
1940
+ var RowParam = z5.object({
1053
1941
  ...paramBase,
1054
- type: z3.literal("row"),
1055
- items: z3.array(RowItemSchema).min(2).max(3)
1942
+ type: z5.literal("row"),
1943
+ items: z5.array(RowItemSchema).min(2).max(3)
1056
1944
  });
1057
- var BlockParamSchema = z3.discriminatedUnion("type", [
1945
+ var ParamDefSchema = z5.discriminatedUnion("type", [
1058
1946
  NumberParam,
1059
1947
  StringParam,
1060
1948
  BooleanParam,
@@ -1075,7 +1963,7 @@ var valueParams = (defs) => defs.flatMap(
1075
1963
  );
1076
1964
 
1077
1965
  // ../core/src/extensions/evalBlock.ts
1078
- import { z as z5 } from "zod";
1966
+ import { z as z7 } from "zod";
1079
1967
 
1080
1968
  // ../core/src/geometry/line.ts
1081
1969
  var projectParam = (p, a, b) => {
@@ -1108,16 +1996,6 @@ var remapBlockInputRefs = (obj, idMap) => {
1108
1996
  };
1109
1997
  var mapInputPoints = (obj, fn) => obj.inputs.map((value) => typeof value === "string" ? value : fn(value));
1110
1998
  var pointsCarryPose = (obj) => obj.inputs.some((v) => typeof v === "string") || obj.inputs.filter((v) => typeof v !== "string").length >= 2;
1111
- var imageCenter = (obj) => add(obj.origin, rot({ x: obj.width / 2, y: obj.height / 2 }, obj.rotation));
1112
- var imageCorners = (obj) => {
1113
- const c = (x, y) => add(obj.origin, rot({ x, y }, obj.rotation));
1114
- return [
1115
- obj.origin,
1116
- c(obj.width, 0),
1117
- c(obj.width, obj.height),
1118
- c(0, obj.height)
1119
- ];
1120
- };
1121
1999
  var mapObjectPoints = (obj, fn) => {
1122
2000
  switch (obj.type) {
1123
2001
  case "line":
@@ -1133,6 +2011,10 @@ var mapObjectPoints = (obj, fn) => {
1133
2011
  case "hatch":
1134
2012
  return {
1135
2013
  ...obj,
2014
+ // The pattern frame moves with its region, as in AutoCAD — an
2015
+ // unanchored fill's frame is the drawing origin, which moves along
2016
+ // too (else the pattern would swim inside a moved hatch).
2017
+ fill: obj.fill.kind === "pattern" ? { ...obj.fill, origin: fn(obj.fill.origin ?? { x: 0, y: 0 }) } : obj.fill,
1136
2018
  loops: obj.loops.map((loop) => ({
1137
2019
  points: loop.points.map((v) => ({ ...v, ...fn(v) }))
1138
2020
  }))
@@ -1149,8 +2031,6 @@ var mapObjectPoints = (obj, fn) => {
1149
2031
  return { ...obj, inputs: mapInputPoints(obj, fn) };
1150
2032
  case "viewport":
1151
2033
  return obj;
1152
- case "image":
1153
- return { ...obj, origin: fn(obj.origin) };
1154
2034
  case "comment":
1155
2035
  return { ...obj, position: fn(obj.position) };
1156
2036
  case "table":
@@ -1178,6 +2058,7 @@ var moveViewportCenter = (obj, map) => {
1178
2058
  const nc = map(c);
1179
2059
  return translateObject(obj, sub(nc, c));
1180
2060
  };
2061
+ var turnFill = (fill, f) => fill.kind === "pattern" || fill.kind === "lines" ? { ...fill, angle: f(fill.angle) } : fill;
1181
2062
  var rotateObject = (obj, center, ang) => {
1182
2063
  const r = (p) => rotAround(p, center, ang);
1183
2064
  const mapped = mapObjectPoints(obj, r);
@@ -1194,19 +2075,19 @@ var rotateObject = (obj, center, ang) => {
1194
2075
  case "table":
1195
2076
  return { ...mapped, rotation: mapped.rotation + ang };
1196
2077
  case "dimension":
1197
- return { ...mapped, offset: rot(mapped.offset, ang) };
2078
+ return mapped.rotation === void 0 ? mapped : { ...mapped, rotation: mapped.rotation + ang };
1198
2079
  case "block":
1199
2080
  return pointsCarryPose(mapped) ? mapped : { ...mapped, rotation: mapped.rotation + ang };
1200
2081
  case "viewport":
1201
2082
  return moveViewportCenter(mapped, r);
1202
- case "image":
1203
2083
  case "reference":
1204
2084
  return { ...mapped, rotation: normAngle(mapped.rotation + ang) };
2085
+ case "hatch":
2086
+ return { ...mapped, fill: turnFill(mapped.fill, (a) => a + ang) };
1205
2087
  case "line":
1206
2088
  case "point":
1207
2089
  case "polyline":
1208
2090
  case "circle":
1209
- case "hatch":
1210
2091
  case "comment":
1211
2092
  return mapped;
1212
2093
  default:
@@ -1231,7 +2112,16 @@ var scaleObjectUniform = (obj, s) => {
1231
2112
  case "table":
1232
2113
  return { ...mapped, scale: mapped.scale * s };
1233
2114
  case "dimension":
1234
- return { ...mapped, offset: m(mapped.offset), scale: mapped.scale * s };
2115
+ return {
2116
+ ...mapped,
2117
+ ...mapped.offset === void 0 ? {} : { offset: mapped.offset * s },
2118
+ labels: mapped.labels.map((l) => ({
2119
+ ...l,
2120
+ ...l.along === void 0 ? {} : { along: l.along * s },
2121
+ ...l.across === void 0 ? {} : { across: l.across * s }
2122
+ })),
2123
+ scale: mapped.scale * s
2124
+ };
1235
2125
  case "block":
1236
2126
  return pointsCarryPose(mapped) ? mapped : { ...mapped, scale: mapped.scale * s };
1237
2127
  case "viewport":
@@ -1247,8 +2137,6 @@ var scaleObjectUniform = (obj, s) => {
1247
2137
  // mm per paper mm) shrinks as the frame grows.
1248
2138
  scale: mapped.scale / s
1249
2139
  };
1250
- case "image":
1251
- return { ...mapped, width: mapped.width * s, height: mapped.height * s };
1252
2140
  case "reference":
1253
2141
  return { ...mapped, scale: mapped.scale * s };
1254
2142
  case "point":
@@ -1284,12 +2172,6 @@ var mirrorObject = (obj, a, b) => {
1284
2172
  mirrored: !obj.mirrored
1285
2173
  };
1286
2174
  }
1287
- if (obj.type === "image") {
1288
- const c = imageCenter(obj);
1289
- const rotation = mirrorAngle(obj.rotation);
1290
- const moved = { ...obj, rotation };
1291
- return translateObject(moved, sub(m(c), imageCenter(moved)));
1292
- }
1293
2175
  if (obj.type === "table") {
1294
2176
  const width = obj.columns.reduce((a2, w) => a2 + w, 0) * obj.scale;
1295
2177
  const topRight = add(obj.position, scale(fromAngle(obj.rotation), width));
@@ -1317,6 +2199,7 @@ var mirrorObject = (obj, a, b) => {
1317
2199
  case "hatch":
1318
2200
  return {
1319
2201
  ...mapped,
2202
+ fill: turnFill(mapped.fill, mirrorAngle),
1320
2203
  loops: mapped.loops.map((loop) => ({
1321
2204
  points: loop.points.map((v) => ({ ...v, bulge: -v.bulge }))
1322
2205
  }))
@@ -1331,8 +2214,17 @@ var mirrorObject = (obj, a, b) => {
1331
2214
  hAlign: readable ? mapped.hAlign : swap[mapped.hAlign]
1332
2215
  };
1333
2216
  }
1334
- case "dimension":
1335
- return { ...mapped, offset: sub(m(add(a, mapped.offset)), a) };
2217
+ case "dimension": {
2218
+ const handed = mapped.kind === "radial" || mapped.kind === "diameter";
2219
+ return {
2220
+ ...mapped,
2221
+ ...mapped.rotation === void 0 ? {} : { rotation: mirrorAngle(mapped.rotation) },
2222
+ ...mapped.offset === void 0 ? {} : { offset: -mapped.offset },
2223
+ labels: handed ? mapped.labels.map(
2224
+ (l) => l.across === void 0 ? l : { ...l, across: -l.across }
2225
+ ) : mapped.labels
2226
+ };
2227
+ }
1336
2228
  case "block":
1337
2229
  return pointsCarryPose(mapped) ? mapped : { ...mapped, rotation: mirrorAngle(mapped.rotation) };
1338
2230
  case "viewport":
@@ -1345,7 +2237,6 @@ var mirrorObject = (obj, a, b) => {
1345
2237
  // Mirrored above, before the point map.
1346
2238
  case "ellipse":
1347
2239
  case "reference":
1348
- case "image":
1349
2240
  case "table":
1350
2241
  return mapped;
1351
2242
  default:
@@ -1354,7 +2245,7 @@ var mirrorObject = (obj, a, b) => {
1354
2245
  };
1355
2246
 
1356
2247
  // ../core/src/extensions/blockDtos.ts
1357
- import { z as z4 } from "zod";
2248
+ import { z as z6 } from "zod";
1358
2249
 
1359
2250
  // ../core/src/extensions/jsLiteral.ts
1360
2251
  var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
@@ -1385,8 +2276,51 @@ ${indent}}`;
1385
2276
  return "undefined";
1386
2277
  };
1387
2278
 
2279
+ // ../core/src/model/assets.ts
2280
+ var hashAsset = (data) => {
2281
+ let h = 5381;
2282
+ for (let i = 0; i < data.length; i++)
2283
+ h = (h << 5) + h + data.charCodeAt(i) | 0;
2284
+ return `${(h >>> 0).toString(16)}-${data.length.toString(16)}`;
2285
+ };
2286
+ var dataUrlMime = (dataUrl) => /^data:([^;,]*)/.exec(dataUrl)?.[1] ?? "";
2287
+ var dataUrlBytes = (dataUrl) => {
2288
+ const comma = dataUrl.indexOf(",");
2289
+ const payload = dataUrl.slice(comma + 1);
2290
+ if (!/;base64$/.test(dataUrl.slice(0, comma)))
2291
+ return new TextEncoder().encode(decodeURIComponent(payload));
2292
+ const bin = atob(payload);
2293
+ const out = new Uint8Array(bin.length);
2294
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
2295
+ return out;
2296
+ };
2297
+ var bytesToDataUrl = (bytes, mime) => {
2298
+ let bin = "";
2299
+ for (let i = 0; i < bytes.length; i += 32768)
2300
+ bin += String.fromCharCode(...bytes.subarray(i, i + 32768));
2301
+ return `data:${mime};base64,${btoa(bin)}`;
2302
+ };
2303
+ var imagePixelSize = (b) => {
2304
+ const u32 = (i) => (b[i] << 24 | b[i + 1] << 16 | b[i + 2] << 8 | b[i + 3]) >>> 0;
2305
+ const u16 = (i) => b[i] << 8 | b[i + 1];
2306
+ if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71)
2307
+ return { width: u32(16), height: u32(20) };
2308
+ if (b[0] !== 255 || b[1] !== 216) return null;
2309
+ for (let i = 2; i + 9 < b.length; ) {
2310
+ if (b[i] !== 255) return null;
2311
+ const marker = b[i + 1];
2312
+ const len3 = u16(i + 2);
2313
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204)
2314
+ return { width: u16(i + 7), height: u16(i + 5) };
2315
+ i += 2 + len3;
2316
+ }
2317
+ return null;
2318
+ };
2319
+ var PX_TO_MM = 25.4 / 96;
2320
+ var embeddedAssetId = (o) => o.type === "reference" && o.source?.kind === "asset" ? o.source.assetId : void 0;
2321
+
1388
2322
  // ../core/src/model/migrations.ts
1389
- var CURRENT_DOCUMENT_VERSION = 20;
2323
+ var CURRENT_DOCUMENT_VERSION = 23;
1390
2324
  var leaderRawToText = (raw) => {
1391
2325
  const points = Array.isArray(raw.points) ? raw.points : [];
1392
2326
  const extraLines = Array.isArray(raw.extraLines) ? raw.extraLines : [];
@@ -1888,6 +2822,134 @@ var migrateV18toV19 = (doc) => {
1888
2822
  };
1889
2823
  };
1890
2824
  var migrateV19toV20 = (doc) => ({ ...swapTextAlignments(doc), version: 20 });
2825
+ var dimensionPointCount = (kind) => kind === "angular" ? 3 : 2;
2826
+ var wrapAngle = (a) => {
2827
+ const twoPi = 2 * Math.PI;
2828
+ const r = a % twoPi;
2829
+ return r < 0 ? r + twoPi : r;
2830
+ };
2831
+ var dimensionRawToLabels = (raw) => {
2832
+ if (raw.type !== "dimension") return raw;
2833
+ const legacyOffset = Array.isArray(raw.offset) ? { x: Number(raw.offset[0]), y: Number(raw.offset[1]) } : raw.offset !== null && typeof raw.offset === "object" ? raw.offset : null;
2834
+ if (legacyOffset === null && !("textOverride" in raw)) return raw;
2835
+ const kind = typeof raw.kind === "string" ? raw.kind : "linear";
2836
+ const points = Array.isArray(raw.points) ? raw.points : [];
2837
+ const tooFew = points.length < dimensionPointCount(kind);
2838
+ const out = { ...raw };
2839
+ delete out.textOverride;
2840
+ const labels = Array.isArray(raw.labels) ? raw.labels.map((l) => ({ ...l })) : [];
2841
+ const label = (i) => labels[i] ??= {};
2842
+ if (legacyOffset) {
2843
+ const v = legacyOffset;
2844
+ const vLen = Math.hypot(v.x, v.y);
2845
+ if (kind === "linear") {
2846
+ delete out.rotation;
2847
+ if (vLen < 1e-9 || tooFew) out.offset = 0;
2848
+ else {
2849
+ let ux = -v.y / vLen;
2850
+ let uy = v.x / vLen;
2851
+ if (ux < 0 || ux === 0 && uy < 0) {
2852
+ ux = -ux;
2853
+ uy = -uy;
2854
+ }
2855
+ out.rotation = Math.atan2(uy, ux);
2856
+ out.offset = v.x * -uy + v.y * ux;
2857
+ }
2858
+ } else if (kind === "angular") {
2859
+ if (tooFew) out.offset = vLen;
2860
+ else {
2861
+ const [vertex, a, b] = points;
2862
+ const angA = Math.atan2(a.y - vertex.y, a.x - vertex.x);
2863
+ const angB = Math.atan2(b.y - vertex.y, b.x - vertex.x);
2864
+ const angL = Math.atan2(v.y, v.x);
2865
+ const inAB = wrapAngle(angL - angA) <= wrapAngle(angB - angA);
2866
+ out.offset = inAB ? vLen : -vLen;
2867
+ }
2868
+ } else {
2869
+ delete out.offset;
2870
+ const [p0, p1] = points;
2871
+ const d = tooFew ? 0 : Math.hypot(p1.x - p0.x, p1.y - p0.y);
2872
+ if (d > 1e-9) {
2873
+ const ux = (p1.x - p0.x) / d;
2874
+ const uy = (p1.y - p0.y) / d;
2875
+ label(0).along = v.x * ux + v.y * uy;
2876
+ }
2877
+ }
2878
+ }
2879
+ if (typeof raw.textOverride === "string") {
2880
+ const template = {
2881
+ linear: "{value}",
2882
+ angular: "{value}\xB0",
2883
+ radial: "R{value}",
2884
+ diameter: "\xD8{value}"
2885
+ };
2886
+ const content = raw.textOverride.replaceAll(
2887
+ "<>",
2888
+ template[kind] ?? "{value}"
2889
+ );
2890
+ const segments = kind === "linear" ? Math.max(1, points.length - 1) : 1;
2891
+ for (let i = 0; i < segments; i++) label(i).content = content;
2892
+ }
2893
+ if (labels.length > 0) out.labels = labels;
2894
+ return out;
2895
+ };
2896
+ var migrateV20toV21 = (doc) => {
2897
+ const degenerate = (raw) => raw.type === "dimension" && (Array.isArray(raw.points) ? raw.points.length : 0) < dimensionPointCount(typeof raw.kind === "string" ? raw.kind : "linear");
2898
+ const mapObjects = (list) => Array.isArray(list) ? list.filter(
2899
+ (raw) => raw === null || typeof raw !== "object" || !degenerate(raw)
2900
+ ).map(
2901
+ (raw) => raw !== null && typeof raw === "object" ? dimensionRawToLabels(raw) : raw
2902
+ ) : list;
2903
+ const layouts = Array.isArray(doc.layouts) ? doc.layouts.map((raw) => {
2904
+ if (raw === null || typeof raw !== "object") return raw;
2905
+ const layout = raw;
2906
+ return { ...layout, objects: mapObjects(layout.objects) };
2907
+ }) : doc.layouts;
2908
+ return { ...doc, objects: mapObjects(doc.objects), layouts, version: 21 };
2909
+ };
2910
+ var migrateV21toV22 = (doc) => ({ ...doc, version: 22 });
2911
+ var imageRawToReference = (raw, assets) => {
2912
+ const rest = { ...raw };
2913
+ delete rest.assetId;
2914
+ delete rest.origin;
2915
+ delete rest.width;
2916
+ delete rest.height;
2917
+ const assetId = typeof raw.assetId === "string" ? raw.assetId : "";
2918
+ const data = assets[assetId];
2919
+ const width = typeof raw.width === "number" && raw.width > 0 ? raw.width : 1;
2920
+ const height = typeof raw.height === "number" && raw.height > 0 ? raw.height : 1;
2921
+ let px;
2922
+ try {
2923
+ px = typeof data === "string" ? imagePixelSize(dataUrlBytes(data)) : null;
2924
+ } catch {
2925
+ px = null;
2926
+ }
2927
+ const w = px ? px.width * PX_TO_MM : width;
2928
+ const h = px ? px.height * PX_TO_MM : height;
2929
+ return {
2930
+ ...rest,
2931
+ type: "reference",
2932
+ format: typeof data === "string" && dataUrlMime(data) === "image/jpeg" ? "jpg" : "png",
2933
+ name: "Image",
2934
+ source: { kind: "asset", assetId: assetId || "missing" },
2935
+ position: raw.origin ?? { x: 0, y: 0 },
2936
+ rotation: typeof raw.rotation === "number" ? raw.rotation : 0,
2937
+ scale: width / w,
2938
+ extent: { minX: 0, minY: 0, maxX: w, maxY: h }
2939
+ };
2940
+ };
2941
+ var migrateV22toV23 = (doc) => {
2942
+ const assets = doc.assets !== null && typeof doc.assets === "object" ? doc.assets : {};
2943
+ const mapObjects = (list) => Array.isArray(list) ? list.map(
2944
+ (raw) => raw !== null && typeof raw === "object" && raw.type === "image" ? imageRawToReference(raw, assets) : raw
2945
+ ) : list;
2946
+ const layouts = Array.isArray(doc.layouts) ? doc.layouts.map((raw) => {
2947
+ if (raw === null || typeof raw !== "object") return raw;
2948
+ const layout = raw;
2949
+ return { ...layout, objects: mapObjects(layout.objects) };
2950
+ }) : doc.layouts;
2951
+ return { ...doc, objects: mapObjects(doc.objects), layouts, version: 23 };
2952
+ };
1891
2953
  var MIGRATION_STEPS = [
1892
2954
  migrateV1toV2,
1893
2955
  migrateV2toV3,
@@ -1907,7 +2969,10 @@ var MIGRATION_STEPS = [
1907
2969
  migrateV16toV17,
1908
2970
  migrateV17toV18,
1909
2971
  migrateV18toV19,
1910
- migrateV19toV20
2972
+ migrateV19toV20,
2973
+ migrateV20toV21,
2974
+ migrateV21toV22,
2975
+ migrateV22toV23
1911
2976
  ];
1912
2977
  function migrateDocument(raw) {
1913
2978
  if (raw === null || typeof raw !== "object") return raw;
@@ -1926,7 +2991,11 @@ function migrateDocument(raw) {
1926
2991
 
1927
2992
  // ../core/src/extensions/blockDtos.ts
1928
2993
  var CHILD_OMIT = { id: true, layerId: true };
1929
- var BlockChildSchema = z4.preprocess(
2994
+ var CHILD_LAYER = {
2995
+ layerId: z6.string().optional(),
2996
+ layer: z6.string().optional()
2997
+ };
2998
+ var BlockChildSchema = z6.preprocess(
1930
2999
  // Back-compat: scripts written before document v6 may still emit "leader"
1931
3000
  // children (→ text with leaderLines), pre-v9 scripts may emit dimensions
1932
3001
  // in the linePoint/rotation shape (→ offset), pre-v17 scripts may put
@@ -1939,27 +3008,30 @@ var BlockChildSchema = z4.preprocess(
1939
3008
  let obj = raw;
1940
3009
  if (obj.type === "leader") obj = leaderRawToText(obj);
1941
3010
  if (obj.type === "dimension")
1942
- return dimRawScaleToField(dimensionRawToOffset(obj));
3011
+ return dimensionRawToLabels(
3012
+ dimRawScaleToField(dimensionRawToOffset(obj))
3013
+ );
1943
3014
  if (obj.type === "text") return textRawStyleToOverride(obj);
1944
3015
  return obj;
1945
3016
  },
1946
- z4.discriminatedUnion("type", [
1947
- LineSchema.omit(CHILD_OMIT),
1948
- PointSchema.omit(CHILD_OMIT),
1949
- PolylineSchema.omit(CHILD_OMIT),
1950
- CircleSchema.omit(CHILD_OMIT),
1951
- ArcSchema.omit(CHILD_OMIT),
1952
- EllipseSchema.omit(CHILD_OMIT),
1953
- HatchSchema.omit(CHILD_OMIT),
1954
- TextSchema.omit(CHILD_OMIT),
1955
- DimensionSchema.omit(CHILD_OMIT),
1956
- BlockSchema.omit(CHILD_OMIT).refine(
1957
- (b) => b.inputs.every((v) => typeof v !== "string"),
1958
- { message: "nested block instances may not carry object references" }
1959
- )
3017
+ z6.discriminatedUnion("type", [
3018
+ LineSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3019
+ PointSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3020
+ PolylineSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3021
+ CircleSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3022
+ ArcSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3023
+ EllipseSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3024
+ HatchSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3025
+ TextSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3026
+ TableShapeSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).superRefine(tableRefinement),
3027
+ DimensionShapeSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).superRefine(dimensionRefinement),
3028
+ ReferenceSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
3029
+ BlockSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).refine((b) => b.inputs.every((v) => typeof v !== "string"), {
3030
+ message: "nested block instances may not carry object references"
3031
+ })
1960
3032
  ])
1961
3033
  );
1962
- var BlockScriptResultSchema = z4.array(BlockChildSchema);
3034
+ var BlockScriptResultSchema = z6.array(BlockChildSchema);
1963
3035
 
1964
3036
  // ../core/src/extensions/scriptHelpers.ts
1965
3037
  var scriptHelpers_exports = {};
@@ -1967,7 +3039,6 @@ __export(scriptHelpers_exports, {
1967
3039
  add: () => add2,
1968
3040
  dist: () => dist2,
1969
3041
  dot: () => dot2,
1970
- helpers: () => helpers,
1971
3042
  len: () => len2,
1972
3043
  norm: () => norm2,
1973
3044
  polar: () => polar,
@@ -2011,22 +3082,9 @@ var rotate = (p, angle2) => {
2011
3082
  const sin = Math.sin(angle2);
2012
3083
  return { x: p.x * cos - p.y * sin, y: p.x * sin + p.y * cos };
2013
3084
  };
2014
- var helpers = Object.freeze({
2015
- range,
2016
- polar,
2017
- toDeg,
2018
- toRad,
2019
- len: len2,
2020
- dist: dist2,
2021
- add: add2,
2022
- sub: sub2,
2023
- scale: scale2,
2024
- norm: norm2,
2025
- dot: dot2,
2026
- rotate
2027
- });
2028
3085
 
2029
3086
  // ../core/src/extensions/scriptRuntime.ts
3087
+ var helpers = Object.freeze({ ...scriptHelpers_exports });
2030
3088
  var onTranspilerReady = null;
2031
3089
  var transpiler = null;
2032
3090
  var transpilerLoading = null;
@@ -2146,13 +3204,13 @@ var toBlockModule = (mod) => {
2146
3204
  );
2147
3205
  return { ...mod, draw, place };
2148
3206
  };
2149
- var ParamsExportSchema = z5.array(BlockParamSchema);
2150
- var OptionalBlockMetaSchema = z5.object({
2151
- description: z5.string().optional(),
2152
- version: z5.string().optional(),
2153
- authors: z5.array(z5.string()).optional(),
2154
- tags: z5.array(z5.string()).optional(),
2155
- dependencies: z5.array(z5.string()).optional()
3207
+ var ParamsExportSchema = z7.array(ParamDefSchema);
3208
+ var OptionalBlockMetaSchema = z7.object({
3209
+ description: z7.string().optional(),
3210
+ version: z7.string().optional(),
3211
+ authors: z7.array(z7.string()).optional(),
3212
+ tags: z7.array(z7.string()).optional(),
3213
+ dependencies: z7.array(z7.string()).optional()
2156
3214
  });
2157
3215
  var parseParamsExport = (paramsExport) => {
2158
3216
  if (typeof paramsExport === "function") {
@@ -2288,7 +3346,7 @@ var stylesSig = (s) => {
2288
3346
  };
2289
3347
  return `${sig(s.textStyles)}.${sig(s.dimStyles)}`;
2290
3348
  };
2291
- var ParamVisibilityResultSchema = z5.record(z5.string(), z5.boolean());
3349
+ var ParamVisibilityResultSchema = z7.record(z7.string(), z7.boolean());
2292
3350
  var paramsKey = (params) => JSON.stringify(Object.entries(params).sort(([a], [b]) => a.localeCompare(b)));
2293
3351
  var r6 = (v) => Math.round(v * 1e6) / 1e6;
2294
3352
  var sigOf = (v) => typeof v === "string" ? `#${v}` : `${r6(v.x)},${r6(v.y)}`;
@@ -2314,7 +3372,7 @@ var resolveInputs = (inputs, pivot, resolve) => {
2314
3372
  return obj ? translateObject(obj, neg) : null;
2315
3373
  });
2316
3374
  };
2317
- var BlockInputsSchema = z5.array(z5.union([Vec2Schema, z5.string()]));
3375
+ var BlockInputsSchema = z7.array(z7.union([Vec2Schema, z7.string()]));
2318
3376
  var acquireBlockInputs = async (script, pickers) => {
2319
3377
  const { mod, meta } = compileBlock(script);
2320
3378
  if (!mod.place) return [await pickers.pickPoint("Insertion point")];
@@ -2410,15 +3468,17 @@ var testBlockDefinition = async (script) => {
2410
3468
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
2411
3469
  }
2412
3470
  };
2413
- var childToObject = (child, instance, index) => {
3471
+ var childToObject = (child, instance, index, layers) => {
3472
+ const { layer, ...rest } = child;
3473
+ const named = layer === void 0 ? void 0 : (layers?.find((l) => l.id === layer) ?? layers?.find((l) => l.name === layer))?.id;
2414
3474
  return {
2415
3475
  color: instance.color,
2416
3476
  lineType: instance.lineType,
2417
3477
  lineTypeScale: instance.lineTypeScale,
2418
3478
  lineWidth: instance.lineWidth,
2419
- ...child,
3479
+ ...rest,
2420
3480
  id: `${instance.id}:${index}`,
2421
- layerId: instance.layerId
3481
+ layerId: named ?? child.layerId ?? instance.layerId
2422
3482
  };
2423
3483
  };
2424
3484
  var evaluating = /* @__PURE__ */ new Set();
@@ -2486,7 +3546,7 @@ var getEvaluatedWorldChildren = (instance, scripts, resolveObject, styles = DEFA
2486
3546
  evalErrors.delete(instance.id);
2487
3547
  const pivot = blockPivot(instance);
2488
3548
  return children.map((child, i) => {
2489
- let obj = childToObject(child, instance, i);
3549
+ let obj = childToObject(child, instance, i, styles.layers);
2490
3550
  if (instance.scale !== 1) obj = scaleObjectUniform(obj, instance.scale);
2491
3551
  if (instance.rotation !== 0)
2492
3552
  obj = rotateObject(obj, { x: 0, y: 0 }, instance.rotation);
@@ -2516,246 +3576,29 @@ var indexOf = (doc) => {
2516
3576
  var resolveDocumentObject = (doc, id) => indexOf(doc).get(id);
2517
3577
  var documentObjectResolver = (doc) => (id) => resolveDocumentObject(doc, id);
2518
3578
 
2519
- // ../core/src/paint/dimensionGeometry.ts
2520
- var RAD2DEG = 180 / Math.PI;
2521
- var formatDimValue = (value, precision) => {
2522
- const fixed = value.toFixed(precision);
2523
- return fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
3579
+ // ../core/src/paint/textMetrics.ts
3580
+ var LINE_HEIGHT_FACTOR = 1.66;
3581
+ var TAB_SIZE_FACTOR = 4;
3582
+ var boundMeasurer = (measure, font, widthFactor) => font === void 0 && widthFactor === void 0 ? measure : {
3583
+ measureTextWidth: (text, heightMm) => measure.measureTextWidth(text, heightMm, font, widthFactor)
2524
3584
  };
2525
- var resolveDimStyle = (obj, styles) => ({
2526
- ...dimStyleFor(obj, styles),
2527
- ...obj.styleOverride,
2528
- scale: obj.scale
2529
- });
2530
- var readableRotation = (u) => {
2531
- const raw = normAngle(angle(u));
2532
- const flipped = raw > Math.PI / 2 + 1e-9 && raw <= 3 * Math.PI / 2 + 1e-9;
2533
- return flipped ? raw - Math.PI : raw;
3585
+ var lineSegments = (line, heightMm, measure) => {
3586
+ if (!line.includes(" ")) return [{ text: line, x: 0 }];
3587
+ const tab = heightMm * TAB_SIZE_FACTOR;
3588
+ const out = [];
3589
+ let x = 0;
3590
+ const parts = line.split(" ");
3591
+ for (let i = 0; i < parts.length; i++) {
3592
+ if (i > 0) x = (Math.floor(x / tab + 1e-9) + 1) * tab;
3593
+ out.push({ text: parts[i], x });
3594
+ x += measure.measureTextWidth(parts[i], heightMm);
3595
+ }
3596
+ return out;
2534
3597
  };
2535
- var textFor = (content, at, along, style) => {
2536
- const rotation = readableRotation(along);
2537
- const textUp = perp(fromAngle(rotation));
2538
- return {
2539
- content,
2540
- anchor: add(at, scale(textUp, style.textOffset * style.scale)),
2541
- rotation,
2542
- height: style.textHeight * style.scale,
2543
- font: dimFontFace(style),
2544
- widthFactor: style.widthFactor
2545
- };
2546
- };
2547
- var linearGeometry = (obj, style) => {
2548
- if (obj.points.length < 2) return null;
2549
- const s = style.scale;
2550
- const points = obj.points;
2551
- const first = points[0];
2552
- const last = points[points.length - 1];
2553
- const upLen = len(obj.offset);
2554
- const u = upLen > 1e-9 ? perp(scale(obj.offset, 1 / upLen)) : dist(first, last) > 1e-9 ? norm(sub(last, first)) : { x: 1, y: 0 };
2555
- const v = perp(u);
2556
- const h = dot(obj.offset, v);
2557
- const feet = points.map((p) => add(p, scale(v, h - dot(sub(p, first), v))));
2558
- const extensionLines = [];
2559
- points.forEach((p, i) => {
2560
- const f = feet[i];
2561
- const gap = dist(p, f);
2562
- if (gap < 1e-9) return;
2563
- const w = norm(sub(f, p));
2564
- extensionLines.push({
2565
- a: style.extensionType === "short" ? sub(f, scale(w, style.extensionOvershoot * s)) : add(p, scale(w, Math.min(style.extensionOffset * s, gap))),
2566
- b: add(f, scale(w, style.extensionOvershoot * s))
2567
- });
2568
- });
2569
- const dimLines = [];
2570
- const arrows = [];
2571
- const texts = [];
2572
- for (let i = 0; i + 1 < feet.length; i++) {
2573
- const a = feet[i];
2574
- const b = feet[i + 1];
2575
- const value = dist(a, b);
2576
- if (value < 1e-9) continue;
2577
- dimLines.push({ a, b });
2578
- arrows.push(
2579
- { tip: a, toward: b, size: style.arrowSize * s },
2580
- { tip: b, toward: a, size: style.arrowSize * s }
2581
- );
2582
- texts.push(
2583
- textFor(
2584
- obj.textOverride ?? formatDimValue(value, style.precision),
2585
- mid(a, b),
2586
- u,
2587
- style
2588
- )
2589
- );
2590
- }
2591
- if (dimLines.length === 0) return null;
2592
- return {
2593
- extensionLines,
2594
- dimLines,
2595
- dimArc: null,
2596
- arrows,
2597
- arrowType: style.arrowType,
2598
- texts,
2599
- value: dist(feet[0], feet[feet.length - 1])
2600
- };
2601
- };
2602
- var angularGeometry = (obj, style) => {
2603
- if (obj.points.length < 3) return null;
2604
- const s = style.scale;
2605
- const [vertex, a, b] = obj.points;
2606
- const radius = len(obj.offset);
2607
- const da = dist(vertex, a);
2608
- const db = dist(vertex, b);
2609
- if (radius < 1e-9 || da < 1e-9 || db < 1e-9) return null;
2610
- const angA = angle(sub(a, vertex));
2611
- const angB = angle(sub(b, vertex));
2612
- const angL = angle(obj.offset);
2613
- const sweepAB = normAngle(angB - angA);
2614
- const inAB = normAngle(angL - angA) <= sweepAB;
2615
- const start = inAB ? angA : angB;
2616
- const sweep = inAB ? sweepAB : normAngle(angA - angB);
2617
- if (sweep < 1e-9) return null;
2618
- const at = (t) => add(vertex, scale(fromAngle(start + t), radius));
2619
- const extensionLines = [];
2620
- for (const [p, d] of [
2621
- [a, da],
2622
- [b, db]
2623
- ]) {
2624
- const w = norm(sub(p, vertex));
2625
- const inner = style.extensionType === "short" ? radius - style.extensionOvershoot * s : Math.min(d + style.extensionOffset * s, radius);
2626
- const outer = radius + style.extensionOvershoot * s;
2627
- if (outer - inner > 1e-9)
2628
- extensionLines.push({
2629
- a: add(vertex, scale(w, inner)),
2630
- b: add(vertex, scale(w, outer))
2631
- });
2632
- }
2633
- const tangentStep = Math.min(sweep / 3, style.arrowSize * s * 2 / radius);
2634
- const valueDeg = sweep * RAD2DEG;
2635
- const midPoint = at(sweep / 2);
2636
- const midTangent = perp(norm(sub(midPoint, vertex)));
2637
- return {
2638
- extensionLines,
2639
- dimLines: [],
2640
- dimArc: {
2641
- center: vertex,
2642
- radius,
2643
- startAngle: start,
2644
- endAngle: start + sweep
2645
- },
2646
- arrows: [
2647
- { tip: at(0), toward: at(tangentStep), size: style.arrowSize * s },
2648
- {
2649
- tip: at(sweep),
2650
- toward: at(sweep - tangentStep),
2651
- size: style.arrowSize * s
2652
- }
2653
- ],
2654
- arrowType: style.arrowType,
2655
- texts: [
2656
- textFor(
2657
- obj.textOverride ?? `${formatDimValue(valueDeg, style.precision)}\xB0`,
2658
- midPoint,
2659
- midTangent,
2660
- style
2661
- )
2662
- ],
2663
- value: sweep
2664
- };
2665
- };
2666
- var radialGeometry = (obj, style) => {
2667
- if (obj.points.length < 2) return null;
2668
- const s = style.scale;
2669
- const [center, p] = obj.points;
2670
- const r = dist(center, p);
2671
- if (r < 1e-9) return null;
2672
- const u = norm(sub(p, center));
2673
- const t = Math.max(dot(obj.offset, u), r * 0.2);
2674
- const textAt = add(center, scale(u, t));
2675
- return {
2676
- extensionLines: [],
2677
- // The line runs center → text position (through the curve point).
2678
- dimLines: [{ a: center, b: t > r ? textAt : p }],
2679
- dimArc: null,
2680
- arrows: [{ tip: p, toward: center, size: style.arrowSize * s }],
2681
- arrowType: style.arrowType,
2682
- texts: [
2683
- textFor(
2684
- obj.textOverride ?? `R${formatDimValue(r, style.precision)}`,
2685
- textAt,
2686
- u,
2687
- style
2688
- )
2689
- ],
2690
- value: r
2691
- };
2692
- };
2693
- var diameterGeometry = (obj, style) => {
2694
- if (obj.points.length < 2) return null;
2695
- const s = style.scale;
2696
- const [p1, p2] = obj.points;
2697
- const d = dist(p1, p2);
2698
- if (d < 1e-9) return null;
2699
- const u = norm(sub(p2, p1));
2700
- const t = dot(sub(add(mid(p1, p2), obj.offset), p1), u);
2701
- const textAt = add(p1, scale(u, Math.min(Math.max(t, d * 0.15), d * 0.85)));
2702
- return {
2703
- extensionLines: [],
2704
- dimLines: [{ a: p1, b: p2 }],
2705
- dimArc: null,
2706
- arrows: [
2707
- { tip: p1, toward: p2, size: style.arrowSize * s },
2708
- { tip: p2, toward: p1, size: style.arrowSize * s }
2709
- ],
2710
- arrowType: style.arrowType,
2711
- texts: [
2712
- textFor(
2713
- obj.textOverride ?? `\xD8${formatDimValue(d, style.precision)}`,
2714
- textAt,
2715
- u,
2716
- style
2717
- )
2718
- ],
2719
- value: d
2720
- };
2721
- };
2722
- var dimensionGeometry = (obj, styles) => {
2723
- const style = resolveDimStyle(obj, styles);
2724
- switch (obj.kind) {
2725
- case "angular":
2726
- return angularGeometry(obj, style);
2727
- case "radial":
2728
- return radialGeometry(obj, style);
2729
- case "diameter":
2730
- return diameterGeometry(obj, style);
2731
- case "linear":
2732
- return linearGeometry(obj, style);
2733
- }
2734
- };
2735
-
2736
- // ../core/src/paint/textMetrics.ts
2737
- var LINE_HEIGHT_FACTOR = 1.66;
2738
- var TAB_SIZE_FACTOR = 4;
2739
- var boundMeasurer = (measure, font, widthFactor) => font === void 0 && widthFactor === void 0 ? measure : {
2740
- measureTextWidth: (text, heightMm) => measure.measureTextWidth(text, heightMm, font, widthFactor)
2741
- };
2742
- var lineSegments = (line, heightMm, measure) => {
2743
- if (!line.includes(" ")) return [{ text: line, x: 0 }];
2744
- const tab = heightMm * TAB_SIZE_FACTOR;
2745
- const out = [];
2746
- let x = 0;
2747
- const parts = line.split(" ");
2748
- for (let i = 0; i < parts.length; i++) {
2749
- if (i > 0) x = (Math.floor(x / tab + 1e-9) + 1) * tab;
2750
- out.push({ text: parts[i], x });
2751
- x += measure.measureTextWidth(parts[i], heightMm);
2752
- }
2753
- return out;
2754
- };
2755
- var lineWidthWithTabs = (line, heightMm, measure) => {
2756
- const segs = lineSegments(line, heightMm, measure);
2757
- const last = segs[segs.length - 1];
2758
- return last.x + measure.measureTextWidth(last.text, heightMm);
3598
+ var lineWidthWithTabs = (line, heightMm, measure) => {
3599
+ const segs = lineSegments(line, heightMm, measure);
3600
+ const last = segs[segs.length - 1];
3601
+ return last.x + measure.measureTextWidth(last.text, heightMm);
2759
3602
  };
2760
3603
  var wrapWords = (line, heightMm, measure, maxWidthMm) => {
2761
3604
  const words = line.split(" ");
@@ -2802,6 +3645,203 @@ var textBlock = (content, heightMm, measure, maxWidthMm) => {
2802
3645
  };
2803
3646
  };
2804
3647
 
3648
+ // ../core/src/paint/richText.ts
3649
+ var ATTR_KEYS = [
3650
+ "font",
3651
+ "bold",
3652
+ "italic",
3653
+ "underline",
3654
+ "color",
3655
+ "height",
3656
+ "script"
3657
+ ];
3658
+ var SCRIPT_SCALE = 0.6;
3659
+ var SUPER_RISE = 0.45;
3660
+ var SUB_DROP = 0.2;
3661
+ var attrsOf = (s) => {
3662
+ const out = {};
3663
+ for (const k of ATTR_KEYS) if (s[k] !== void 0) out[k] = s[k];
3664
+ return out;
3665
+ };
3666
+ var sameAttrs = (a, b) => ATTR_KEYS.every((k) => a[k] === b[k]);
3667
+ var isEmpty = (a) => ATTR_KEYS.every((k) => a[k] === void 0);
3668
+ var snap = (i, content) => {
3669
+ const c = Math.max(0, Math.min(content.length, Math.round(i)));
3670
+ const code = content.charCodeAt(c);
3671
+ return c > 0 && code >= 56320 && code <= 57343 ? c - 1 : c;
3672
+ };
3673
+ var toIntervals = (spans, content, extra = []) => {
3674
+ const cuts = /* @__PURE__ */ new Set([0, content.length]);
3675
+ for (const s of spans ?? []) {
3676
+ cuts.add(snap(s.start, content));
3677
+ cuts.add(snap(s.end, content));
3678
+ }
3679
+ for (const c of extra) cuts.add(snap(c, content));
3680
+ const sorted = [...cuts].sort((a, b) => a - b);
3681
+ const out = [];
3682
+ for (let i = 0; i + 1 < sorted.length; i++) {
3683
+ const start = sorted[i];
3684
+ const end = sorted[i + 1];
3685
+ let attrs = {};
3686
+ for (const s of spans ?? [])
3687
+ if (snap(s.start, content) <= start && snap(s.end, content) >= end)
3688
+ attrs = { ...attrs, ...attrsOf(s) };
3689
+ out.push({ start, end, attrs });
3690
+ }
3691
+ return out;
3692
+ };
3693
+ var fromIntervals = (intervals) => {
3694
+ const out = [];
3695
+ for (const iv of intervals) {
3696
+ if (iv.end <= iv.start || isEmpty(iv.attrs)) continue;
3697
+ const last = out[out.length - 1];
3698
+ if (last && last.end === iv.start && sameAttrs(attrsOf(last), iv.attrs))
3699
+ last.end = iv.end;
3700
+ else out.push({ start: iv.start, end: iv.end, ...iv.attrs });
3701
+ }
3702
+ return out;
3703
+ };
3704
+ var normalizeSpans = (spans, content) => fromIntervals(toIntervals(spans, content));
3705
+ var sliceSpans = (spans, start, end, to = 0) => (spans ?? []).filter((s) => s.end > start && s.start < end).map((s) => ({
3706
+ ...s,
3707
+ start: Math.max(s.start, start) - start + to,
3708
+ end: Math.min(s.end, end) - start + to
3709
+ }));
3710
+ var richTextBlock = (content, spans, base, measure, tabMeasure, maxWidthMm) => {
3711
+ const h = base.heightMm;
3712
+ const baseFace = parseFontFace(base.face);
3713
+ const runs = toIntervals(spans, content).map((iv) => {
3714
+ const a = iv.attrs;
3715
+ const cap = h * (a.height ?? 1);
3716
+ return {
3717
+ ...iv,
3718
+ face: fontFace(
3719
+ a.font ?? baseFace.font,
3720
+ a.bold ?? baseFace.bold,
3721
+ a.italic ?? baseFace.italic
3722
+ ),
3723
+ cap,
3724
+ glyph: a.script ? cap * SCRIPT_SCALE : cap,
3725
+ dy: a.script === "super" ? cap * SUPER_RISE : a.script === "sub" ? -cap * SUB_DROP : 0
3726
+ };
3727
+ });
3728
+ const runsIn = (a, b) => runs.filter((r) => r.end > a && r.start < b);
3729
+ const widthOf = (a, b, m) => runsIn(a, b).reduce(
3730
+ (w, r) => w + m.measureTextWidth(
3731
+ content.slice(Math.max(a, r.start), Math.min(b, r.end)),
3732
+ r.glyph,
3733
+ r.face,
3734
+ base.widthFactor
3735
+ ),
3736
+ 0
3737
+ );
3738
+ const tab = h * TAB_SIZE_FACTOR;
3739
+ const nextStop = (x) => (Math.floor(x / tab + 1e-9) + 1) * tab;
3740
+ const segments = (a, b) => {
3741
+ const out = [];
3742
+ let x = 0;
3743
+ let s = a;
3744
+ for (let i = a; i <= b; i++) {
3745
+ if (i < b && content[i] !== " ") continue;
3746
+ out.push({ start: s, end: i, x });
3747
+ x = nextStop(x + widthOf(s, i, tabMeasure));
3748
+ s = i + 1;
3749
+ }
3750
+ return out;
3751
+ };
3752
+ const wrapWords2 = (a, b, max) => {
3753
+ const out = [];
3754
+ let lineStart = a;
3755
+ let cur = -1;
3756
+ for (let i = a; i <= b; i++) {
3757
+ if (i < b && content[i] !== " ") continue;
3758
+ if (cur >= 0 && widthOf(lineStart, i, measure) > max) {
3759
+ out.push([lineStart, cur]);
3760
+ lineStart = cur + 1;
3761
+ }
3762
+ cur = i;
3763
+ }
3764
+ out.push([lineStart, b]);
3765
+ return out;
3766
+ };
3767
+ const raw = [];
3768
+ let p0 = 0;
3769
+ for (const para of content.split("\n")) {
3770
+ const p1 = p0 + para.length;
3771
+ const tabbed = para.includes(" ");
3772
+ const wrap = maxWidthMm !== void 0 && maxWidthMm > 0;
3773
+ if (!wrap) raw.push({ start: p0, end: p1, tabbed });
3774
+ else if (!tabbed)
3775
+ for (const [s, e] of wrapWords2(p0, p1, maxWidthMm))
3776
+ raw.push({ start: s, end: e, tabbed });
3777
+ else {
3778
+ const segs = segments(p0, p1);
3779
+ const last = segs[segs.length - 1];
3780
+ const width = last.x + widthOf(last.start, last.end, measure);
3781
+ if (width <= maxWidthMm || maxWidthMm - last.x <= h)
3782
+ raw.push({ start: p0, end: p1, tabbed });
3783
+ else
3784
+ wrapWords2(last.start, p1, maxWidthMm - last.x).forEach(
3785
+ ([s, e], i) => raw.push(
3786
+ i === 0 ? { start: p0, end: e, tabbed } : { start: s, end: e, hang: last.x, tabbed }
3787
+ )
3788
+ );
3789
+ }
3790
+ p0 = p1 + 1;
3791
+ }
3792
+ const lines = [];
3793
+ let baseline = 0;
3794
+ let prevCap = 0;
3795
+ for (const r of raw) {
3796
+ const segs = r.hang !== void 0 ? [{ start: r.start, end: r.end, x: r.hang }] : segments(r.start, r.end);
3797
+ const pieces = [];
3798
+ for (const seg of segs) {
3799
+ let x = seg.x;
3800
+ for (const run of runsIn(seg.start, seg.end)) {
3801
+ const text = content.slice(
3802
+ Math.max(seg.start, run.start),
3803
+ Math.min(seg.end, run.end)
3804
+ );
3805
+ const width = measure.measureTextWidth(
3806
+ text,
3807
+ run.glyph,
3808
+ run.face,
3809
+ base.widthFactor
3810
+ );
3811
+ pieces.push({
3812
+ text,
3813
+ x,
3814
+ width,
3815
+ dy: run.dy,
3816
+ heightMm: run.glyph,
3817
+ face: run.face,
3818
+ ...run.attrs.color ? { color: run.attrs.color } : {},
3819
+ ...run.attrs.underline ? { underline: true } : {}
3820
+ });
3821
+ x += width;
3822
+ }
3823
+ }
3824
+ const at = runsIn(r.start, Math.max(r.end, r.start + 1));
3825
+ const cap = Math.max(...at.map((run) => run.cap), at.length ? 0 : h);
3826
+ baseline += lines.length === 0 ? cap : LINE_HEIGHT_FACTOR * (prevCap + cap) / 2;
3827
+ prevCap = cap;
3828
+ const last = pieces[pieces.length - 1];
3829
+ lines.push({
3830
+ start: r.start,
3831
+ end: r.end,
3832
+ pieces,
3833
+ width: last ? last.x + last.width : 0,
3834
+ fromBlockEdge: r.tabbed,
3835
+ baseline
3836
+ });
3837
+ }
3838
+ return {
3839
+ lines,
3840
+ width: maxWidthMm !== void 0 && maxWidthMm > 0 ? maxWidthMm : Math.max(...lines.map((l) => l.width), 0),
3841
+ height: baseline
3842
+ };
3843
+ };
3844
+
2805
3845
  // ../core/src/paint/advanceTables.ts
2806
3846
  var EXTRACTED_RANGES = [
2807
3847
  [32, 126],
@@ -6284,6 +7324,473 @@ var approximateTextMeasurer = {
6284
7324
  }
6285
7325
  };
6286
7326
 
7327
+ // ../core/src/paint/dimensionGeometry.ts
7328
+ var RAD2DEG = 180 / Math.PI;
7329
+ var formatDimValue = (value, precision) => {
7330
+ const fixed = value.toFixed(precision);
7331
+ return fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
7332
+ };
7333
+ var dimLabelContent = (kind, label, formatted) => {
7334
+ const parts = (label?.content ?? DIM_DEFAULT_CONTENT[kind]).split(
7335
+ DIM_VALUE_TOKEN
7336
+ );
7337
+ const content = parts.join(formatted);
7338
+ if (!label?.spans?.length || label.content === void 0) return { content };
7339
+ const T = DIM_VALUE_TOKEN.length;
7340
+ const map = (p, end) => {
7341
+ let t = 0;
7342
+ let c = 0;
7343
+ for (let i = 0; i < parts.length; i++) {
7344
+ if (p <= t + parts[i].length) return c + (p - t);
7345
+ t += parts[i].length;
7346
+ c += parts[i].length;
7347
+ if (i === parts.length - 1) break;
7348
+ if (p < t + T) return end ? c + formatted.length : c;
7349
+ t += T;
7350
+ c += formatted.length;
7351
+ }
7352
+ return content.length;
7353
+ };
7354
+ const spans = normalizeSpans(
7355
+ label.spans.map((s) => ({
7356
+ ...s,
7357
+ start: map(s.start, false),
7358
+ end: map(s.end, true)
7359
+ })),
7360
+ content
7361
+ );
7362
+ return spans.length ? { content, spans } : { content };
7363
+ };
7364
+ var labelWidth = (label, h, font, widthFactor) => label.spans?.length ? richTextBlock(
7365
+ label.content,
7366
+ label.spans,
7367
+ { face: font, heightMm: h, widthFactor },
7368
+ approximateTextMeasurer,
7369
+ approximateTextMeasurer
7370
+ ).width : approximateTextMeasurer.measureTextWidth(
7371
+ label.content,
7372
+ h,
7373
+ font,
7374
+ widthFactor
7375
+ );
7376
+ var resolveDimStyle = (obj, styles) => ({
7377
+ ...dimStyleFor(obj, styles),
7378
+ ...obj.styleOverride,
7379
+ scale: obj.scale
7380
+ });
7381
+ var readableRotation = (u) => {
7382
+ const raw = normAngle(angle(u));
7383
+ const flipped = raw > Math.PI / 2 + 1e-9 && raw <= 3 * Math.PI / 2 + 1e-9;
7384
+ return flipped ? raw - Math.PI : raw;
7385
+ };
7386
+ var straightFrame = (origin, u, n, s0, s1) => ({
7387
+ s0,
7388
+ s1,
7389
+ toWorld: (along, across) => add(origin, add(scale(u, along), scale(n, across))),
7390
+ fromWorld: (p) => {
7391
+ const d = sub(p, origin);
7392
+ return { along: dot(d, u), across: dot(d, n) };
7393
+ },
7394
+ tangent: () => u
7395
+ });
7396
+ var linearAxis = (obj) => {
7397
+ const first = obj.points[0];
7398
+ const last = obj.points[obj.points.length - 1];
7399
+ const u = obj.rotation !== void 0 ? fromAngle(obj.rotation) : dist(first, last) > 1e-9 ? norm(sub(last, first)) : { x: 1, y: 0 };
7400
+ return { u, n: perp(u) };
7401
+ };
7402
+ var linearFeet = (obj) => {
7403
+ const { u, n } = linearAxis(obj);
7404
+ const first = obj.points[0];
7405
+ const h = obj.offset ?? 0;
7406
+ const feet = obj.points.map(
7407
+ (p) => add(p, scale(n, h - dot(sub(p, first), n)))
7408
+ );
7409
+ return { feet, u, n };
7410
+ };
7411
+ var dimensionLabelFrames = (obj) => {
7412
+ switch (obj.kind) {
7413
+ case "linear": {
7414
+ if (obj.points.length < 2) return [];
7415
+ const { feet, u, n } = linearFeet(obj);
7416
+ const across = (obj.offset ?? 0) >= 0 ? n : scale(n, -1);
7417
+ const frames = [];
7418
+ for (let i = 0; i + 1 < feet.length; i++) {
7419
+ const L = dot(sub(feet[i + 1], feet[i]), u);
7420
+ if (Math.abs(L) < 1e-9) continue;
7421
+ const dir = L > 0 ? u : scale(u, -1);
7422
+ frames.push(straightFrame(feet[i], dir, across, 0, Math.abs(L)));
7423
+ }
7424
+ return frames;
7425
+ }
7426
+ case "angular": {
7427
+ const polar2 = angularFrame(obj);
7428
+ return polar2 ? [polar2.frame] : [];
7429
+ }
7430
+ case "radial": {
7431
+ if (obj.points.length < 2) return [];
7432
+ const [center, p] = obj.points;
7433
+ const r = dist(center, p);
7434
+ if (r < 1e-9) return [];
7435
+ const u = norm(sub(p, center));
7436
+ return [straightFrame(center, u, perp(u), 0, r)];
7437
+ }
7438
+ case "diameter": {
7439
+ if (obj.points.length < 2) return [];
7440
+ const [p1, p2] = obj.points;
7441
+ const d = dist(p1, p2);
7442
+ if (d < 1e-9) return [];
7443
+ const u = norm(sub(p2, p1));
7444
+ const center = add(p1, scale(u, d / 2));
7445
+ return [straightFrame(center, u, perp(u), -d / 2, d / 2)];
7446
+ }
7447
+ }
7448
+ };
7449
+ var angularFrame = (obj) => {
7450
+ if (obj.points.length < 3) return null;
7451
+ const [vertex, a, b] = obj.points;
7452
+ const signed = obj.offset ?? 0;
7453
+ const radius = Math.abs(signed);
7454
+ if (radius < 1e-9 || dist(vertex, a) < 1e-9 || dist(vertex, b) < 1e-9)
7455
+ return null;
7456
+ const angA = angle(sub(a, vertex));
7457
+ const angB = angle(sub(b, vertex));
7458
+ const start = signed >= 0 ? angA : angB;
7459
+ const sweep = signed >= 0 ? normAngle(angB - angA) : normAngle(angA - angB);
7460
+ if (sweep < 1e-9) return null;
7461
+ const frame = {
7462
+ s0: 0,
7463
+ s1: radius * sweep,
7464
+ toWorld: (along, across) => add(vertex, scale(fromAngle(start + along / radius), radius + across)),
7465
+ fromWorld: (p) => {
7466
+ const d = sub(p, vertex);
7467
+ let w = normAngle(angle(d) - start);
7468
+ if (w > Math.PI) w -= 2 * Math.PI;
7469
+ return { along: w * radius, across: len(d) - radius };
7470
+ },
7471
+ tangent: (along) => perp(fromAngle(start + along / radius))
7472
+ };
7473
+ return { vertex, radius, start, sweep, frame };
7474
+ };
7475
+ var overlaps = (a, b) => a[0] < b[1] - 1e-9 && b[0] < a[1] - 1e-9;
7476
+ var placeLabel = (frame, spec, index, label, style, arrowCount, mode, stack) => {
7477
+ const s = style.scale;
7478
+ const h = style.textHeight * s;
7479
+ const gap = style.textOffset * s;
7480
+ const A = style.arrowSize * s;
7481
+ const font = dimFontFace(style);
7482
+ const width = labelWidth(label, h, font, style.widthFactor);
7483
+ const L = frame.s1 - frame.s0;
7484
+ const arrowsInside = L >= arrowCount * A - 1e-9;
7485
+ let along = spec?.along;
7486
+ let across = spec?.across ?? 0;
7487
+ let row = 0;
7488
+ if (along === void 0) {
7489
+ const fits = L >= width + 2 * gap + arrowCount * A - 1e-9;
7490
+ const push = (arrowsInside ? 0 : A) + gap + width / 2;
7491
+ if (fits) along = (frame.s0 + frame.s1) / 2;
7492
+ else if (mode === "end") along = frame.s1 + push;
7493
+ else if (mode === "start") along = frame.s0 - push;
7494
+ else {
7495
+ along = (frame.s0 + frame.s1) / 2;
7496
+ row = 1;
7497
+ const interval2 = [along - width / 2, along + width / 2];
7498
+ if (stack && stack.row >= 1 && overlaps(interval2, stack.interval))
7499
+ row = stack.row + 1;
7500
+ across = row * (h + gap);
7501
+ }
7502
+ }
7503
+ const interval = [along - width / 2, along + width / 2];
7504
+ let cover = null;
7505
+ if (across === 0 && (interval[1] > frame.s1 || interval[0] < frame.s0))
7506
+ cover = [
7507
+ Math.min(frame.s0, interval[0] - gap),
7508
+ Math.max(frame.s1, interval[1] + gap)
7509
+ ];
7510
+ const leaders = [];
7511
+ if (spec?.across !== void 0 && spec.across !== 0) {
7512
+ const landingA = frame.toWorld(interval[0], across);
7513
+ const landingB = frame.toWorld(interval[1], across);
7514
+ const from = frame.toWorld((frame.s0 + frame.s1) / 2, 0);
7515
+ const to = dist(from, landingA) <= dist(from, landingB) ? landingA : landingB;
7516
+ leaders.push({ a: from, b: to }, { a: landingA, b: landingB });
7517
+ }
7518
+ const tangent = frame.tangent(along);
7519
+ const rotation = readableRotation(tangent);
7520
+ const textUp = perp(fromAngle(rotation));
7521
+ const text = {
7522
+ content: label.content,
7523
+ ...label.spans ? { spans: label.spans } : {},
7524
+ anchor: add(frame.toWorld(along, across), scale(textUp, gap)),
7525
+ rotation,
7526
+ height: h,
7527
+ width,
7528
+ font,
7529
+ widthFactor: style.widthFactor,
7530
+ segment: index,
7531
+ along,
7532
+ across
7533
+ };
7534
+ return { text, cover, leaders, row };
7535
+ };
7536
+ var linearGeometry = (obj, style) => {
7537
+ if (obj.points.length < 2) return null;
7538
+ const s = style.scale;
7539
+ const A = style.arrowSize * s;
7540
+ const { feet, u } = linearFeet(obj);
7541
+ const frames = dimensionLabelFrames(obj);
7542
+ if (frames.length === 0) return null;
7543
+ const extensionLines = [];
7544
+ obj.points.forEach((p, i) => {
7545
+ const f = feet[i];
7546
+ const gap = dist(p, f);
7547
+ if (gap < 1e-9) return;
7548
+ const w = norm(sub(f, p));
7549
+ extensionLines.push({
7550
+ a: style.extensionType === "short" ? sub(f, scale(w, style.extensionOvershoot * s)) : add(p, scale(w, Math.min(style.extensionOffset * s, gap))),
7551
+ b: add(f, scale(w, style.extensionOvershoot * s))
7552
+ });
7553
+ });
7554
+ const dimLines = [];
7555
+ const arrows = [];
7556
+ const labels = [];
7557
+ const leaders = [];
7558
+ let stack = null;
7559
+ frames.forEach((frame, i) => {
7560
+ const L = frame.s1 - frame.s0;
7561
+ const value = L;
7562
+ const mode = frames.length === 1 || i === frames.length - 1 ? "end" : i === 0 ? "start" : "raise";
7563
+ const placed = placeLabel(
7564
+ frame,
7565
+ obj.labels[i],
7566
+ i,
7567
+ dimLabelContent(
7568
+ obj.kind,
7569
+ obj.labels[i],
7570
+ formatDimValue(value, style.precision)
7571
+ ),
7572
+ style,
7573
+ 2,
7574
+ mode,
7575
+ stack
7576
+ );
7577
+ stack = placed.row > 0 ? {
7578
+ row: placed.row,
7579
+ interval: [
7580
+ placed.text.along - placed.text.width / 2,
7581
+ placed.text.along + placed.text.width / 2
7582
+ ]
7583
+ } : null;
7584
+ labels.push(placed.text);
7585
+ leaders.push(...placed.leaders);
7586
+ const a = frame.toWorld(frame.s0, 0);
7587
+ const b = frame.toWorld(frame.s1, 0);
7588
+ const arrowsInside = L >= 2 * A - 1e-9;
7589
+ let from = frame.s0;
7590
+ let to = frame.s1;
7591
+ if (!arrowsInside) {
7592
+ from -= 2 * A;
7593
+ to += 2 * A;
7594
+ }
7595
+ if (placed.cover) {
7596
+ from = Math.min(from, placed.cover[0]);
7597
+ to = Math.max(to, placed.cover[1]);
7598
+ }
7599
+ dimLines.push({ a: frame.toWorld(from, 0), b: frame.toWorld(to, 0) });
7600
+ arrows.push(
7601
+ arrowsInside ? { tip: a, toward: b, size: A } : { tip: a, toward: frame.toWorld(from, 0), size: A },
7602
+ arrowsInside ? { tip: b, toward: a, size: A } : { tip: b, toward: frame.toWorld(to, 0), size: A }
7603
+ );
7604
+ });
7605
+ return {
7606
+ extensionLines,
7607
+ dimLines,
7608
+ dimArc: null,
7609
+ leaders,
7610
+ arrows,
7611
+ arrowType: style.arrowType,
7612
+ labels,
7613
+ value: Math.abs(dot(sub(feet[feet.length - 1], feet[0]), u))
7614
+ };
7615
+ };
7616
+ var angularGeometry = (obj, style) => {
7617
+ const polar2 = angularFrame(obj);
7618
+ if (!polar2) return null;
7619
+ const { vertex, radius, start, sweep, frame } = polar2;
7620
+ const s = style.scale;
7621
+ const A = style.arrowSize * s;
7622
+ const [, a, b] = obj.points;
7623
+ const extensionLines = [];
7624
+ for (const p of [a, b]) {
7625
+ const d = dist(vertex, p);
7626
+ const w = norm(sub(p, vertex));
7627
+ const inner = style.extensionType === "short" ? radius - style.extensionOvershoot * s : Math.min(d + style.extensionOffset * s, radius);
7628
+ const outer = radius + style.extensionOvershoot * s;
7629
+ if (outer - inner > 1e-9)
7630
+ extensionLines.push({
7631
+ a: add(vertex, scale(w, inner)),
7632
+ b: add(vertex, scale(w, outer))
7633
+ });
7634
+ }
7635
+ const placed = placeLabel(
7636
+ frame,
7637
+ obj.labels[0],
7638
+ 0,
7639
+ dimLabelContent(
7640
+ obj.kind,
7641
+ obj.labels[0],
7642
+ formatDimValue(sweep * RAD2DEG, style.precision)
7643
+ ),
7644
+ style,
7645
+ 2,
7646
+ "end",
7647
+ null
7648
+ );
7649
+ const L = frame.s1;
7650
+ const arrowsInside = L >= 2 * A - 1e-9;
7651
+ let from = 0;
7652
+ let to = L;
7653
+ if (!arrowsInside) {
7654
+ from -= 2 * A;
7655
+ to += 2 * A;
7656
+ }
7657
+ if (placed.cover) {
7658
+ from = Math.min(from, placed.cover[0]);
7659
+ to = Math.max(to, placed.cover[1]);
7660
+ }
7661
+ const step = Math.min(sweep / 3, A * 2 / radius) * radius;
7662
+ const at = (along) => frame.toWorld(along, 0);
7663
+ return {
7664
+ extensionLines,
7665
+ dimLines: [],
7666
+ dimArc: {
7667
+ center: vertex,
7668
+ radius,
7669
+ startAngle: start + from / radius,
7670
+ endAngle: start + to / radius
7671
+ },
7672
+ leaders: placed.leaders,
7673
+ arrows: [
7674
+ { tip: at(0), toward: at(arrowsInside ? step : -step), size: A },
7675
+ { tip: at(L), toward: at(arrowsInside ? L - step : L + step), size: A }
7676
+ ],
7677
+ arrowType: style.arrowType,
7678
+ labels: [placed.text],
7679
+ value: sweep
7680
+ };
7681
+ };
7682
+ var radialGeometry = (obj, style) => {
7683
+ const [frame] = dimensionLabelFrames(obj);
7684
+ if (!frame) return null;
7685
+ const s = style.scale;
7686
+ const A = style.arrowSize * s;
7687
+ const [center, p] = obj.points;
7688
+ const r = frame.s1;
7689
+ const spec = obj.labels[0];
7690
+ const content = dimLabelContent(
7691
+ obj.kind,
7692
+ spec,
7693
+ formatDimValue(r, style.precision)
7694
+ );
7695
+ const width = labelWidth(
7696
+ content,
7697
+ style.textHeight * s,
7698
+ dimFontFace(style),
7699
+ style.widthFactor
7700
+ );
7701
+ const gap = style.textOffset * s;
7702
+ const autoInside = spec?.along === void 0 && r >= width + 2 * gap + A - 1e-9 ? { ...spec, along: r - A - gap - width / 2 } : spec;
7703
+ const placed = placeLabel(
7704
+ frame,
7705
+ autoInside,
7706
+ 0,
7707
+ content,
7708
+ style,
7709
+ 1,
7710
+ "end",
7711
+ null
7712
+ );
7713
+ const outside = placed.text.along > r + 1e-9;
7714
+ let to = r;
7715
+ if (placed.cover) to = Math.max(to, placed.cover[1]);
7716
+ return {
7717
+ extensionLines: [],
7718
+ dimLines: [{ a: center, b: frame.toWorld(to, 0) }],
7719
+ dimArc: null,
7720
+ leaders: placed.leaders,
7721
+ // Head at the curve: pointing outward with the text inside, inward
7722
+ // (body on the outside) with the text outside.
7723
+ arrows: [
7724
+ { tip: p, toward: outside ? frame.toWorld(r + A, 0) : center, size: A }
7725
+ ],
7726
+ arrowType: style.arrowType,
7727
+ labels: [placed.text],
7728
+ value: r
7729
+ };
7730
+ };
7731
+ var diameterGeometry = (obj, style) => {
7732
+ const [frame] = dimensionLabelFrames(obj);
7733
+ if (!frame) return null;
7734
+ const s = style.scale;
7735
+ const A = style.arrowSize * s;
7736
+ const [p1, p2] = obj.points;
7737
+ const d = frame.s1 - frame.s0;
7738
+ const placed = placeLabel(
7739
+ frame,
7740
+ obj.labels[0],
7741
+ 0,
7742
+ dimLabelContent(
7743
+ obj.kind,
7744
+ obj.labels[0],
7745
+ formatDimValue(d, style.precision)
7746
+ ),
7747
+ style,
7748
+ 2,
7749
+ "end",
7750
+ null
7751
+ );
7752
+ const arrowsInside = d >= 2 * A - 1e-9;
7753
+ let from = frame.s0;
7754
+ let to = frame.s1;
7755
+ if (!arrowsInside) {
7756
+ from -= 2 * A;
7757
+ to += 2 * A;
7758
+ }
7759
+ if (placed.cover) {
7760
+ from = Math.min(from, placed.cover[0]);
7761
+ to = Math.max(to, placed.cover[1]);
7762
+ }
7763
+ return {
7764
+ extensionLines: [],
7765
+ dimLines: [{ a: frame.toWorld(from, 0), b: frame.toWorld(to, 0) }],
7766
+ dimArc: null,
7767
+ leaders: placed.leaders,
7768
+ arrows: arrowsInside ? [
7769
+ { tip: p1, toward: p2, size: A },
7770
+ { tip: p2, toward: p1, size: A }
7771
+ ] : [
7772
+ { tip: p1, toward: frame.toWorld(from, 0), size: A },
7773
+ { tip: p2, toward: frame.toWorld(to, 0), size: A }
7774
+ ],
7775
+ arrowType: style.arrowType,
7776
+ labels: [placed.text],
7777
+ value: d
7778
+ };
7779
+ };
7780
+ var dimensionGeometry = (obj, styles) => {
7781
+ const style = resolveDimStyle(obj, styles);
7782
+ switch (obj.kind) {
7783
+ case "linear":
7784
+ return linearGeometry(obj, style);
7785
+ case "angular":
7786
+ return angularGeometry(obj, style);
7787
+ case "radial":
7788
+ return radialGeometry(obj, style);
7789
+ case "diameter":
7790
+ return diameterGeometry(obj, style);
7791
+ }
7792
+ };
7793
+
6287
7794
  // ../core/src/paint/textGeometry.ts
6288
7795
  var textHeight = (obj, textStyles) => (obj.styleOverride?.textHeight ?? textStyleFor(obj, textStyles).textHeight) * obj.scale;
6289
7796
  var textArrowSize = (obj, textStyles) => (obj.styleOverride?.arrowSize ?? textStyleFor(obj, textStyles).arrowSize) * obj.scale;
@@ -6292,11 +7799,24 @@ var textWidth = (obj) => obj.width === void 0 ? void 0 : obj.width * obj.scale;
6292
7799
  var FRAME_MARGIN_FACTOR = 0.35;
6293
7800
  var textBoxLocal = (obj, measure, textStyles) => {
6294
7801
  const { font, widthFactor } = effectiveTextFont(obj, textStyles);
6295
- const m = boundMeasurer(measure, font, widthFactor);
6296
- const block = textBlock(
7802
+ const block = obj.spans?.length ? (
7803
+ // Formatted: per-run fonts, so the measurer stays unbound.
7804
+ richTextBlock(
7805
+ obj.content,
7806
+ obj.spans,
7807
+ {
7808
+ face: font,
7809
+ heightMm: textHeight(obj, textStyles),
7810
+ widthFactor
7811
+ },
7812
+ measure,
7813
+ approximateTextMeasurer,
7814
+ textWidth(obj)
7815
+ )
7816
+ ) : textBlock(
6297
7817
  obj.content,
6298
7818
  textHeight(obj, textStyles),
6299
- m,
7819
+ boundMeasurer(measure, font, widthFactor),
6300
7820
  textWidth(obj)
6301
7821
  );
6302
7822
  const u = fromAngle(obj.rotation);
@@ -6397,7 +7917,7 @@ var cellText = (t, row, col) => {
6397
7917
  id: `${t.id}:${row}:${col}`,
6398
7918
  layerId: t.layerId,
6399
7919
  position: { x: 0, y: 0 },
6400
- content: cell.text,
7920
+ content: cell.content,
6401
7921
  leaderLines: [],
6402
7922
  hAlign: "center",
6403
7923
  vAlign: "center",
@@ -6441,6 +7961,18 @@ var cellAnchor = (t, colX, rowY, a) => {
6441
7961
  vAlign: v
6442
7962
  };
6443
7963
  };
7964
+ var tableCellFrame = (t, textStyles, row, col) => {
7965
+ const { colX, rowY, anchors } = tableLayout(t, textStyles);
7966
+ const a = anchors.find(
7967
+ (a2) => row >= a2.row && row < a2.row + a2.rows && col >= a2.col && col < a2.col + a2.cols
7968
+ ) ?? { row, col, rows: 1, cols: 1 };
7969
+ return {
7970
+ row: a.row,
7971
+ col: a.col,
7972
+ text: { ...cellText(t, a.row, a.col), ...cellAnchor(t, colX, rowY, a) },
7973
+ innerWidth: colX[a.col + a.cols] - colX[a.col] - 2 * t.cellMargin * t.scale
7974
+ };
7975
+ };
6444
7976
  var cache = /* @__PURE__ */ new WeakMap();
6445
7977
  var tableLayout = (t, textStyles) => {
6446
7978
  const hit = cache.get(t);
@@ -6462,9 +7994,32 @@ var tableLayout = (t, textStyles) => {
6462
7994
  const h = textHeight(text, textStyles);
6463
7995
  const { font, widthFactor } = effectiveTextFont(text, textStyles);
6464
7996
  const inner2 = colX[c + cols] - colX[c] - 2 * margin;
7997
+ if (cell.spans?.length) {
7998
+ const rich = richTextBlock(
7999
+ cell.content,
8000
+ cell.spans,
8001
+ { face: font, heightMm: h, widthFactor },
8002
+ approximateTextMeasurer,
8003
+ approximateTextMeasurer,
8004
+ inner2 > 0 ? inner2 : void 0
8005
+ );
8006
+ let content = "";
8007
+ const spans = [];
8008
+ rich.lines.forEach((l, k) => {
8009
+ if (k > 0) content += "\n";
8010
+ spans.push(...sliceSpans(cell.spans, l.start, l.end, content.length));
8011
+ content += cell.content.slice(l.start, l.end);
8012
+ });
8013
+ wrapped.push({
8014
+ text: { ...text, spans },
8015
+ lines: [content],
8016
+ height: rich.height + 2 * margin
8017
+ });
8018
+ return;
8019
+ }
6465
8020
  const measure = boundMeasurer(approximateTextMeasurer, font, widthFactor);
6466
8021
  const block = textBlock(
6467
- cell.text,
8022
+ cell.content,
6468
8023
  h,
6469
8024
  measure,
6470
8025
  inner2 > 0 ? inner2 : void 0
@@ -6474,7 +8029,7 @@ var tableLayout = (t, textStyles) => {
6474
8029
  text,
6475
8030
  lines,
6476
8031
  // An empty cell still reserves one line of its text height.
6477
- height: (cell.text ? h + (lines.length - 1) * block.lineHeight : h) + 2 * margin
8032
+ height: (cell.content ? h + (lines.length - 1) * block.lineHeight : h) + 2 * margin
6478
8033
  });
6479
8034
  })
6480
8035
  );
@@ -6553,7 +8108,7 @@ var tableLayout = (t, textStyles) => {
6553
8108
  }));
6554
8109
  anchors.forEach((a, i) => {
6555
8110
  const { text, lines } = wrapped[i];
6556
- if (!t.cells[a.row][a.col].text) return;
8111
+ if (!t.cells[a.row][a.col].content) return;
6557
8112
  children.push({
6558
8113
  ...text,
6559
8114
  ...cellAnchor(t, colX, rowY, a),
@@ -6869,6 +8424,303 @@ var hexToTrueColor = (hex) => {
6869
8424
  return rgb ? rgb[0] << 16 | rgb[1] << 8 | rgb[2] : null;
6870
8425
  };
6871
8426
 
8427
+ // ../core/src/export/dxf/codecs.ts
8428
+ var LINE_WEIGHTS = [
8429
+ 0,
8430
+ 5,
8431
+ 9,
8432
+ 13,
8433
+ 15,
8434
+ 18,
8435
+ 20,
8436
+ 25,
8437
+ 30,
8438
+ 35,
8439
+ 40,
8440
+ 50,
8441
+ 53,
8442
+ 60,
8443
+ 70,
8444
+ 80,
8445
+ 90,
8446
+ 100,
8447
+ 106,
8448
+ 120,
8449
+ 140,
8450
+ 158,
8451
+ 200,
8452
+ 211
8453
+ ];
8454
+ var lineWeightToMm = (lineWeight) => lineWeight === void 0 || lineWeight < 0 ? void 0 : Math.max(lineWeight, 5) / 100;
8455
+ var mmToLineWeight = (mm) => LINE_WEIGHTS.reduce(
8456
+ (best, w) => Math.abs(w - mm * 100) < Math.abs(best - mm * 100) ? w : best
8457
+ );
8458
+ var encodeMtext = (s) => s.replace(/\r\n/g, "\n").replace(/\r/g, "").replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/\^/g, "^ ").replace(/\n/g, "\\P").replace(/\t/g, "^I");
8459
+ var decodeTextEscapes = (text) => text.replace(/%%([dpcuo%])/gi, (_, ch) => {
8460
+ switch (ch.toLowerCase()) {
8461
+ case "d":
8462
+ return "\xB0";
8463
+ case "p":
8464
+ return "\xB1";
8465
+ case "c":
8466
+ return "\xD8";
8467
+ case "u":
8468
+ case "o":
8469
+ return "";
8470
+ default:
8471
+ return "%";
8472
+ }
8473
+ });
8474
+ var SUPERSCRIPT = {
8475
+ "0": "\u2070",
8476
+ "1": "\xB9",
8477
+ "2": "\xB2",
8478
+ "3": "\xB3",
8479
+ "4": "\u2074",
8480
+ "5": "\u2075",
8481
+ "6": "\u2076",
8482
+ "7": "\u2077",
8483
+ "8": "\u2078",
8484
+ "9": "\u2079",
8485
+ "+": "\u207A",
8486
+ "-": "\u207B",
8487
+ "=": "\u207C",
8488
+ "(": "\u207D",
8489
+ ")": "\u207E",
8490
+ n: "\u207F",
8491
+ i: "\u2071"
8492
+ };
8493
+ var SUBSCRIPT = {
8494
+ "0": "\u2080",
8495
+ "1": "\u2081",
8496
+ "2": "\u2082",
8497
+ "3": "\u2083",
8498
+ "4": "\u2084",
8499
+ "5": "\u2085",
8500
+ "6": "\u2086",
8501
+ "7": "\u2087",
8502
+ "8": "\u2088",
8503
+ "9": "\u2089",
8504
+ "+": "\u208A",
8505
+ "-": "\u208B",
8506
+ "=": "\u208C",
8507
+ "(": "\u208D",
8508
+ ")": "\u208E"
8509
+ };
8510
+ var toScript = (s, map) => [...s].every((c) => map[c]) ? [...s].map((c) => map[c]).join("") : s;
8511
+ var stackText = (body) => {
8512
+ const m = /^(.*?)([\^#/])(.*)$/s.exec(body);
8513
+ if (!m) return body;
8514
+ const top = m[1].trim();
8515
+ const bottom = m[3].trim();
8516
+ if (top && !bottom) return toScript(top, SUPERSCRIPT);
8517
+ if (!top && bottom) return toScript(bottom, SUBSCRIPT);
8518
+ return `${top}/${bottom}`;
8519
+ };
8520
+ var SYMBOL_FONT = {
8521
+ a: "\u03B1",
8522
+ b: "\u03B2",
8523
+ c: "\u03C7",
8524
+ d: "\u03B4",
8525
+ e: "\u03B5",
8526
+ f: "\u03C6",
8527
+ g: "\u03B3",
8528
+ h: "\u03B7",
8529
+ i: "\u03B9",
8530
+ k: "\u03BA",
8531
+ l: "\u03BB",
8532
+ m: "\u03BC",
8533
+ n: "\u03BD",
8534
+ p: "\u03C0",
8535
+ q: "\u03B8",
8536
+ r: "\u03C1",
8537
+ s: "\u03C3",
8538
+ t: "\u03C4",
8539
+ u: "\u03C5",
8540
+ w: "\u03C9",
8541
+ x: "\u03BE",
8542
+ y: "\u03C8",
8543
+ z: "\u03B6",
8544
+ D: "\u0394",
8545
+ F: "\u03A6",
8546
+ G: "\u0393",
8547
+ L: "\u039B",
8548
+ P: "\u03A0",
8549
+ Q: "\u0398",
8550
+ S: "\u03A3",
8551
+ W: "\u03A9",
8552
+ X: "\u039E",
8553
+ Y: "\u03A8",
8554
+ "\xA3": "\u2264",
8555
+ "\xB3": "\u2265",
8556
+ "\xB9": "\u2260",
8557
+ "\xBB": "\u2248",
8558
+ "\xA5": "\u221E",
8559
+ "\xB4": "\xD7",
8560
+ "\xB8": "\xF7",
8561
+ \u00D6: "\u221A",
8562
+ "\xAE": "\u2192",
8563
+ "\xAC": "\u2190",
8564
+ \u00C6: "\u2205",
8565
+ \u00CE: "\u2208",
8566
+ "\xB6": "\u2202"
8567
+ };
8568
+ var isSymbolFont = (fontCode) => /^symbol(\.ttf)?$/i.test(fontCode.split("|")[0].trim());
8569
+ var scanMtext = (text, opts) => {
8570
+ const chunks = [];
8571
+ let st = { symbol: false };
8572
+ const stack = [];
8573
+ const attrs = (extra) => opts ? {
8574
+ ...st.font !== void 0 ? { font: st.font } : {},
8575
+ ...st.bold !== void 0 ? { bold: st.bold } : {},
8576
+ ...st.italic !== void 0 ? { italic: st.italic } : {},
8577
+ ...st.height !== void 0 ? { height: st.height } : {},
8578
+ ...st.color !== void 0 ? { color: st.color } : {},
8579
+ ...st.underline ? { underline: true } : {},
8580
+ ...extra
8581
+ } : {};
8582
+ const emit = (t, extra) => {
8583
+ if (!t) return;
8584
+ const a = attrs(extra);
8585
+ const last = chunks[chunks.length - 1];
8586
+ if (!extra && last && JSON.stringify(last.attrs) === JSON.stringify(a))
8587
+ last.text += t;
8588
+ else chunks.push({ text: t, attrs: a });
8589
+ };
8590
+ const codeBody = (i2) => {
8591
+ const end = text.indexOf(";", i2);
8592
+ return end === -1 ? ["", i2] : [text.slice(i2, end), end + 1];
8593
+ };
8594
+ let i = 0;
8595
+ while (i < text.length) {
8596
+ const ch = text[i];
8597
+ if (ch === "{") {
8598
+ stack.push(st);
8599
+ st = { ...st };
8600
+ i++;
8601
+ } else if (ch === "}") {
8602
+ st = stack.pop() ?? st;
8603
+ i++;
8604
+ } else if (ch !== "\\") {
8605
+ emit(st.symbol ? SYMBOL_FONT[ch] ?? ch : ch);
8606
+ i++;
8607
+ } else {
8608
+ const next = text[i + 1] ?? "";
8609
+ if (next === "\\" || next === "{" || next === "}") {
8610
+ emit(next);
8611
+ i += 2;
8612
+ } else if (next === "P") {
8613
+ emit("\n");
8614
+ i += 2;
8615
+ } else if (next === "~") {
8616
+ emit(" ");
8617
+ i += 2;
8618
+ } else if (next === "L" || next === "l") {
8619
+ st = { ...st, underline: next === "L" };
8620
+ i += 2;
8621
+ } else if ("OoKk".includes(next)) {
8622
+ i += 2;
8623
+ } else if (next === "S") {
8624
+ const [body, at] = codeBody(i + 2);
8625
+ const m = /^(.*?)([\^#/])(.*)$/s.exec(body);
8626
+ const top = m?.[1].trim() ?? "";
8627
+ const bottom = m?.[3].trim() ?? "";
8628
+ if (opts && m && m[2] === "^" && top && !bottom)
8629
+ emit(top, { script: "super" });
8630
+ else if (opts && m && m[2] === "^" && !top && bottom)
8631
+ emit(bottom, { script: "sub" });
8632
+ else emit(stackText(body));
8633
+ i = at === i + 2 ? i + 2 : at;
8634
+ } else if ((next === "U" || next === "u") && /^\+[0-9a-fA-F]{4}/.test(text.slice(i + 2, i + 7))) {
8635
+ emit(String.fromCodePoint(parseInt(text.slice(i + 3, i + 7), 16)));
8636
+ i += 7;
8637
+ } else if (next === "f" || next === "F") {
8638
+ const [body, at] = codeBody(i + 2);
8639
+ st = { ...st, symbol: isSymbolFont(body) };
8640
+ if (opts && !st.symbol && body) {
8641
+ const [name, ...flags] = body.split("|");
8642
+ const file = name.includes(".");
8643
+ const face = fontFromDxfStyle(
8644
+ file ? name : void 0,
8645
+ file ? void 0 : name
8646
+ );
8647
+ const flag = (k) => flags.find((f) => f[0] === k)?.slice(1);
8648
+ st.font = face.font;
8649
+ st.bold = flag("b") !== void 0 ? flag("b") === "1" : face.bold;
8650
+ st.italic = flag("i") !== void 0 ? flag("i") === "1" : face.italic;
8651
+ }
8652
+ i = at === i + 2 ? i + 2 : at;
8653
+ } else if (opts && next === "H") {
8654
+ const [body, at] = codeBody(i + 2);
8655
+ const v = Number.parseFloat(body);
8656
+ if (Number.isFinite(v) && v > 0)
8657
+ st = {
8658
+ ...st,
8659
+ height: /x$/i.test(body) ? (st.height ?? 1) * v : v / (opts.heightMm > 0 ? opts.heightMm : v)
8660
+ };
8661
+ i = at === i + 2 ? i + 2 : at;
8662
+ } else if (opts && next === "C") {
8663
+ const [body, at] = codeBody(i + 2);
8664
+ st = { ...st, color: aciToHex(Number.parseInt(body, 10)) };
8665
+ i = at === i + 2 ? i + 2 : at;
8666
+ } else {
8667
+ const end = text.indexOf(";", i + 1);
8668
+ i = end === -1 ? i + 2 : end + 1;
8669
+ }
8670
+ }
8671
+ }
8672
+ return chunks;
8673
+ };
8674
+ var stripMtextCodes = (text) => scanMtext(text).map((c) => c.text).join("");
8675
+ var mtextWidthFactor = (text) => {
8676
+ const m = /\\W([0-9.]+);/.exec(text);
8677
+ const v = m ? Number(m[1]) : NaN;
8678
+ return Number.isFinite(v) && v > 0 && v !== 1 ? v : void 0;
8679
+ };
8680
+ var mtextFontFlags = (text) => {
8681
+ const m = /\\f[^|;]*((?:\|[a-z]\d+)*)/.exec(text);
8682
+ if (!m) return {};
8683
+ return {
8684
+ .../\|b1/.test(m[1]) ? { bold: true } : {},
8685
+ .../\|i1/.test(m[1]) ? { italic: true } : {}
8686
+ };
8687
+ };
8688
+ var decodeControlCodes = (text) => text.replace(
8689
+ /\^M\^J|\^[IJM ]|\r\n?/g,
8690
+ (m) => m === "^I" ? " " : m === "^ " ? "^" : "\n"
8691
+ );
8692
+ var cleanText = (raw, mtext) => decodeControlCodes(decodeTextEscapes(mtext ? stripMtextCodes(raw) : raw));
8693
+ var cleanRichText = (raw, heightMm) => {
8694
+ let content = "";
8695
+ const spans = [];
8696
+ for (const c of scanMtext(raw, { heightMm })) {
8697
+ const text = decodeControlCodes(decodeTextEscapes(c.text));
8698
+ spans.push({
8699
+ start: content.length,
8700
+ end: content.length + text.length,
8701
+ ...c.attrs
8702
+ });
8703
+ content += text;
8704
+ }
8705
+ return { content, spans: normalizeSpans(spans, content) };
8706
+ };
8707
+ var encodeStack = (s) => encodeMtext(s).replace(/[#/;]/g, (c) => `\\${c}`);
8708
+ var encodeRichMtext = (content, spans, face) => {
8709
+ const base = parseFontFace(face);
8710
+ let out = "";
8711
+ let at = 0;
8712
+ for (const s of normalizeSpans(spans, content)) {
8713
+ out += encodeMtext(content.slice(at, s.start));
8714
+ const fontCode = s.font !== void 0 || s.bold !== void 0 || s.italic !== void 0 ? `\\f${fontTypeface(resolveFont(s.font ?? base.font))}|b${s.bold ?? base.bold ? 1 : 0}|i${s.italic ?? base.italic ? 1 : 0};` : "";
8715
+ const codes = fontCode + (s.height !== void 0 ? `\\H${s.height}x;` : "") + (s.color ? `\\C${nearestAci(s.color)};` : "") + (s.underline ? "\\L" : "");
8716
+ const body = content.slice(s.start, s.end);
8717
+ const text = s.script && !/[\n\t]/.test(body) ? s.script === "super" ? `\\S${encodeStack(body)}^;` : `\\S^${encodeStack(body)};` : encodeMtext(body);
8718
+ out += codes || s.script ? `{${codes}${text}}` : text;
8719
+ at = s.end;
8720
+ }
8721
+ return out + encodeMtext(content.slice(at));
8722
+ };
8723
+
6872
8724
  // ../core/src/export/dxf/dxfExtras.ts
6873
8725
  import { Point, DxfObject } from "@tarikjabiri/dxf";
6874
8726
  var RAW_COLOR_BY_BLOCK = -1056964608;
@@ -7030,7 +8882,7 @@ var MultiLeaderEntity = class extends Point {
7030
8882
  dx.push(45, 1);
7031
8883
  dx.push(170, 1);
7032
8884
  dx.push(90, RAW_COLOR_BY_BLOCK);
7033
- dx.push(171, 1);
8885
+ dx.push(171, a.attachment);
7034
8886
  dx.push(172, 1);
7035
8887
  dx.push(91, RAW_COLOR_NONE);
7036
8888
  dx.push(141, 1.5);
@@ -7167,6 +9019,130 @@ var MLeaderStyleObject = class extends DxfObject {
7167
9019
  dx.push(273, 9);
7168
9020
  }
7169
9021
  };
9022
+ var AcadTableEntity = class extends Point {
9023
+ args;
9024
+ constructor(args) {
9025
+ super(args.position.x, args.position.y, 0);
9026
+ this.args = args;
9027
+ this.type = "ACAD_TABLE";
9028
+ this.subclassMarker = "AcDbBlockReference";
9029
+ }
9030
+ dxfyChild(dx) {
9031
+ const a = this.args;
9032
+ dx.push(2, a.blockName);
9033
+ dx.push(10, a.position.x);
9034
+ dx.push(20, a.position.y);
9035
+ dx.push(30, 0);
9036
+ dx.push(100, "AcDbTable");
9037
+ dx.push(280, 0);
9038
+ dx.push(342, a.tableStyleHandle);
9039
+ dx.push(343, a.blockRecordHandle);
9040
+ dx.push(11, a.direction.x);
9041
+ dx.push(21, a.direction.y);
9042
+ dx.push(31, 0);
9043
+ dx.push(90, 22);
9044
+ dx.push(91, a.rowHeights.length);
9045
+ dx.push(92, a.colWidths.length);
9046
+ dx.push(93, 2);
9047
+ dx.push(94, 0);
9048
+ dx.push(95, 0);
9049
+ dx.push(96, 0);
9050
+ dx.push(281, 1);
9051
+ for (const h of a.rowHeights) dx.push(141, h);
9052
+ for (const w of a.colWidths) dx.push(142, w);
9053
+ for (const c of a.cells) {
9054
+ dx.push(171, 1);
9055
+ dx.push(172, 0);
9056
+ dx.push(173, c.merged ? 1 : 0);
9057
+ dx.push(174, 0);
9058
+ dx.push(175, c.cols);
9059
+ dx.push(176, c.rows);
9060
+ dx.push(91, 1);
9061
+ dx.push(178, 0);
9062
+ dx.push(145, 0);
9063
+ dx.push(170, c.alignment);
9064
+ dx.push(92, 0);
9065
+ dx.push(301, "CELL_VALUE");
9066
+ if (c.text === "") {
9067
+ dx.push(93, 3);
9068
+ dx.push(90, 0);
9069
+ } else {
9070
+ dx.push(93, 6);
9071
+ dx.push(90, 4);
9072
+ dx.push(1, c.text);
9073
+ }
9074
+ dx.push(94, 0);
9075
+ dx.push(300, "");
9076
+ dx.push(302, c.text);
9077
+ dx.push(304, "ACVALUE_END");
9078
+ }
9079
+ }
9080
+ };
9081
+ var TableStyleObject = class extends DxfObject {
9082
+ args;
9083
+ constructor(args) {
9084
+ super("TABLESTYLE");
9085
+ this.args = args;
9086
+ }
9087
+ dxfy(dx) {
9088
+ const a = this.args;
9089
+ dx.push(0, "TABLESTYLE");
9090
+ dx.push(5, this.handle);
9091
+ dx.push(102, "{ACAD_REACTORS");
9092
+ dx.push(330, a.dictionaryHandle);
9093
+ dx.push(102, "}");
9094
+ dx.push(330, a.dictionaryHandle);
9095
+ dx.push(100, "AcDbTableStyle");
9096
+ dx.push(280, 0);
9097
+ dx.push(3, a.name);
9098
+ dx.push(70, 0);
9099
+ dx.push(71, 0);
9100
+ dx.push(40, a.margin);
9101
+ dx.push(41, a.margin);
9102
+ dx.push(280, 0);
9103
+ dx.push(281, 0);
9104
+ for (const [height, alignment] of [
9105
+ [a.textHeight, 2],
9106
+ [a.textHeight, 5],
9107
+ [a.textHeight, 5]
9108
+ ]) {
9109
+ dx.push(7, a.textStyleName);
9110
+ dx.push(140, height);
9111
+ dx.push(170, alignment);
9112
+ dx.push(62, 0);
9113
+ dx.push(63, 7);
9114
+ dx.push(283, 0);
9115
+ dx.push(90, 4);
9116
+ dx.push(91, 0);
9117
+ dx.push(1, "");
9118
+ for (const [lw, vis, color] of [
9119
+ [274, 284, 64],
9120
+ [275, 285, 65],
9121
+ [276, 286, 66],
9122
+ [277, 287, 67],
9123
+ [278, 288, 68],
9124
+ [279, 289, 69]
9125
+ ]) {
9126
+ dx.push(lw, -2);
9127
+ dx.push(vis, 1);
9128
+ dx.push(color, 0);
9129
+ }
9130
+ }
9131
+ }
9132
+ };
9133
+ var DXF_CLASSES = [
9134
+ ["ACAD_TABLE", "AcDbTable", "ObjectDBX Classes", 1025, 1],
9135
+ ["TABLESTYLE", "AcDbTableStyle", "ObjectDBX Classes", 4095, 0],
9136
+ ["MULTILEADER", "AcDbMLeader", "ACDB_MLEADER_CLASS", 1025, 1],
9137
+ ["MLEADERSTYLE", "AcDbMLeaderStyle", "ACDB_MLEADERSTYLE_CLASS", 4095, 0]
9138
+ ];
9139
+ var classesSection = (content) => DXF_CLASSES.filter(([name]) => content.includes(`
9140
+ 0
9141
+ ${name}
9142
+ `)).map(
9143
+ ([name, cpp, app, proxy, isEntity]) => [0, "CLASS", 1, name, 2, cpp, 3, app, 90, proxy, 91, 0, 280, 0, 281, isEntity].map((v) => `${v}
9144
+ `).join("")
9145
+ ).join("");
7170
9146
 
7171
9147
  // ../core/src/export/dxf/hatchPattern.ts
7172
9148
  var ANSI31_BASE_SPACING = 3.175;
@@ -7187,17 +9163,35 @@ var mtextAttachment = (hAlign, vAlign) => {
7187
9163
  const col = hAlign === "left" ? 1 : hAlign === "center" ? 2 : 3;
7188
9164
  return row + col;
7189
9165
  };
7190
- var escapeMtext = (s) => s.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/\n/g, "\\P").replace(/\t/g, "^I");
7191
- var dxfLineTypeName = (lineType) => {
9166
+ var dxfLineTypeName = (lineType, lineTypes) => {
7192
9167
  if (!lineType) return void 0;
7193
- const p = getLineType(lineType);
9168
+ const p = resolveLineType(lineType, lineTypes);
7194
9169
  if (!p || p.dashesMm.length === 0) return void 0;
7195
9170
  return p.dxfName ?? p.name.toUpperCase();
7196
9171
  };
7197
- var DIM_STYLE_NAME = "LINEA";
9172
+ var setLineWeight = (target, weight) => {
9173
+ const emit = target.dxfy.bind(target);
9174
+ target.dxfy = (dx) => {
9175
+ const start = dx.lines.length;
9176
+ emit(dx);
9177
+ const lines = dx.lines;
9178
+ for (let i = start; i < lines.length; i += 2)
9179
+ if (lines[i] === 370) {
9180
+ lines[i + 1] = weight;
9181
+ return;
9182
+ }
9183
+ let markers = 0;
9184
+ for (let i = start; i < lines.length; i += 2)
9185
+ if (lines[i] === 100 && ++markers === 2) {
9186
+ lines.splice(i, 0, 370, weight);
9187
+ return;
9188
+ }
9189
+ lines.push(370, weight);
9190
+ };
9191
+ };
7198
9192
  var optionsFor = (obj, ctx) => {
7199
9193
  const layer = ctx.doc.layers.find((l) => l.id === obj.layerId);
7200
- const dashed = (getLineType(obj.lineType ?? layer?.lineType ?? "")?.dashesMm.length ?? 0) > 0;
9194
+ const dashed = (resolveLineType(obj.lineType ?? layer?.lineType ?? "", ctx.doc.lineTypes)?.dashesMm.length ?? 0) > 0;
7201
9195
  const lineTypeScale = (obj.lineTypeScale ?? 1) * (obj.lineWidth ?? layer?.lineWidth ?? 0.25);
7202
9196
  const trueColor = obj.color ? hexToTrueColor(obj.color) : null;
7203
9197
  return {
@@ -7206,7 +9200,7 @@ var optionsFor = (obj, ctx) => {
7206
9200
  // color (420) which wins wherever it is supported.
7207
9201
  ...obj.color ? { colorNumber: nearestAci(obj.color) } : {},
7208
9202
  ...trueColor !== null ? { trueColor: String(trueColor) } : {},
7209
- ...dxfLineTypeName(obj.lineType) ? { lineType: dxfLineTypeName(obj.lineType) } : {},
9203
+ ...dxfLineTypeName(obj.lineType, ctx.doc.lineTypes) ? { lineType: dxfLineTypeName(obj.lineType, ctx.doc.lineTypes) } : {},
7210
9204
  ...dashed && lineTypeScale !== 1 ? { lineTypeScale } : {}
7211
9205
  };
7212
9206
  };
@@ -7251,28 +9245,87 @@ var addArrowhead = (space, tip, toward, size, type, opts) => {
7251
9245
  break;
7252
9246
  }
7253
9247
  };
7254
- var textStyleFor2 = (ctx, font) => {
9248
+ var uniqueTableName = (taken, base) => {
9249
+ let name = base;
9250
+ for (let i = 2; taken.has(name.toUpperCase()); i++) name = `${base}_${i}`;
9251
+ taken.add(name.toUpperCase());
9252
+ return name;
9253
+ };
9254
+ var writableFace = (font) => {
7255
9255
  const def = resolveFont(font);
7256
- const { bold, italic } = parseFontFace(font);
7257
- const suffix = (bold ? "B" : "") + (italic ? "I" : "");
7258
- const key = fontFace(def.id, bold, italic);
7259
- let rec = ctx.textStyles.get(key);
7260
- if (!rec) {
7261
- const style = ctx.writer.document.tables.styleTable.addStyle(
7262
- `LINEA_${def.id.toUpperCase()}${suffix ? `_${suffix}` : ""}`
7263
- );
7264
- style.fontFileName = def.dxfFontFile;
7265
- rec = { name: style.name, handle: style.handle };
7266
- ctx.textStyles.set(key, rec);
7267
- }
9256
+ const { font: bare, bold, italic } = parseFontFace(font);
9257
+ const foreign = bare !== void 0 && bare !== "" && bare !== def.id;
9258
+ const fontId = foreign ? bare : def.id;
9259
+ return {
9260
+ face: fontFace(fontId, bold, italic),
9261
+ fontFile: foreign ? `${fontId}.ttf` : def.dxfFontFile,
9262
+ fontId,
9263
+ foreign
9264
+ };
9265
+ };
9266
+ var addStyleRecord = (ctx, key, name, font, widthFactor = 1, fixedHeight = 0) => {
9267
+ const { face, fontFile } = writableFace(font);
9268
+ const table = ctx.writer.document.tables.styleTable;
9269
+ const predefined = name.toUpperCase() === "STANDARD" && !ctx.styleNames.has("STANDARD") ? table.records.find((r) => r.name.toUpperCase() === "STANDARD") : void 0;
9270
+ const style = predefined ?? table.addStyle(uniqueTableName(ctx.styleNames, name));
9271
+ if (predefined) ctx.styleNames.add("STANDARD");
9272
+ style.fontFileName = fontFile;
9273
+ style.widthFactor = widthFactor;
9274
+ style.fixedTextHeight = fixedHeight;
9275
+ const rec = { name: style.name, handle: style.handle, face };
9276
+ ctx.textStyles.set(key, rec);
7268
9277
  return rec;
7269
9278
  };
9279
+ var createDocTextStyles = (ctx) => {
9280
+ for (const s of ctx.doc.textStyles)
9281
+ addStyleRecord(
9282
+ ctx,
9283
+ `doc:${s.id}`,
9284
+ sanitizeLayerName(s.name),
9285
+ fontFace(s.font, s.bold, s.italic),
9286
+ s.widthFactor,
9287
+ s.textHeight
9288
+ );
9289
+ };
9290
+ var dimTextStyleFor = (ctx, face, widthFactor) => {
9291
+ const want = writableFace(face).face;
9292
+ const own = ctx.doc.textStyles.find(
9293
+ (s) => writableFace(fontFace(s.font, s.bold, s.italic)).face === want && Math.abs(s.widthFactor - widthFactor) < 1e-9
9294
+ );
9295
+ const ownRec = own && ctx.textStyles.get(`doc:${own.id}`);
9296
+ if (ownRec) return ownRec;
9297
+ if (Math.abs(widthFactor - 1) < 1e-9) return textStyleFor2(ctx, face);
9298
+ const key = `${want}|w${widthFactor}`;
9299
+ return ctx.textStyles.get(key) ?? addStyleRecord(
9300
+ ctx,
9301
+ key,
9302
+ `${textStyleFor2(ctx, face).name}_W${Math.round(widthFactor * 100)}`,
9303
+ face,
9304
+ widthFactor
9305
+ );
9306
+ };
9307
+ var textStyleFor2 = (ctx, font) => {
9308
+ const { face, fontId, foreign } = writableFace(font);
9309
+ const { bold, italic } = parseFontFace(face);
9310
+ const suffix = (bold ? "B" : "") + (italic ? "I" : "");
9311
+ return ctx.textStyles.get(face) ?? addStyleRecord(
9312
+ ctx,
9313
+ face,
9314
+ `LINEA_${(foreign ? fontId.replace(/[^A-Za-z0-9_-]/g, "_") : fontId).toUpperCase()}${suffix ? `_${suffix}` : ""}`,
9315
+ face
9316
+ );
9317
+ };
9318
+ var textRecordFor = (ctx, obj, face) => {
9319
+ const base = textStyleFor(obj, ctx.doc.textStyles);
9320
+ const own = ctx.textStyles.get(`doc:${base.id}`);
9321
+ return own && writableFace(fontFace(base.font, base.bold, base.italic)).face === writableFace(face).face ? own : textStyleFor2(ctx, face);
9322
+ };
7270
9323
  var addTextLines = (space, content, position, height, rotation, hAlign, vAlign, opts, measure, mtextWidth, fontOpts = {}) => {
7271
- const { styleName, font, widthFactor = 1 } = fontOpts;
9324
+ const { styleName, font, widthFactor = 1, spans } = fontOpts;
7272
9325
  const m = boundMeasurer(measure, font, widthFactor);
7273
9326
  const block = textBlock(content, height, m, mtextWidth);
7274
- if (block.lines.length > 1 || mtextWidth !== void 0 && mtextWidth > 0 || content.includes(" ")) {
7275
- const value = (widthFactor !== 1 ? `\\W${widthFactor};` : "") + escapeMtext(content);
9327
+ if (block.lines.length > 1 || mtextWidth !== void 0 && mtextWidth > 0 || content.includes(" ") || !!spans?.length) {
9328
+ const value = (widthFactor !== 1 ? `\\W${widthFactor};` : "") + (spans?.length ? encodeRichMtext(content, spans, font) : encodeMtext(content));
7276
9329
  const mtext = space.addMText(v3(position), height, value, {
7277
9330
  rotation: rotation * RAD2DEG2,
7278
9331
  attachmentPoint: mtextAttachment(hAlign, vAlign),
@@ -7283,7 +9336,8 @@ var addTextLines = (space, content, position, height, rotation, hAlign, vAlign,
7283
9336
  return;
7284
9337
  }
7285
9338
  const down = scale(perp(fromAngle(rotation)), -1);
7286
- block.lines.forEach((line, i) => {
9339
+ block.lines.forEach((raw, i) => {
9340
+ const line = raw.replace(/\r/g, "");
7287
9341
  if (!line) return;
7288
9342
  const anchor = add(position, scale(down, i * block.lineHeight));
7289
9343
  const text = space.addText(v3(anchor), height, line, {
@@ -7298,11 +9352,94 @@ var addTextLines = (space, content, position, height, rotation, hAlign, vAlign,
7298
9352
  if (styleName) text.textStyle = styleName;
7299
9353
  });
7300
9354
  };
9355
+ var documentPattern = (ctx, name) => ctx.doc.hatchPatterns.find(
9356
+ (p) => hatchPatternKey(p.name) === hatchPatternKey(name)
9357
+ );
9358
+ var injectHatchPatternLines = (content, ctx) => {
9359
+ if (!content.includes("\n0\nHATCH\n")) return content;
9360
+ const out = [];
9361
+ let pos = 0;
9362
+ for (; ; ) {
9363
+ const start = content.indexOf("\n0\nHATCH\n", pos);
9364
+ if (start === -1) break;
9365
+ const rest = content.slice(start + 8);
9366
+ const m = /\n0\n(?=[A-Z][A-Z0-9_]*\n)/.exec(rest);
9367
+ const end = m ? start + 8 + m.index : content.length;
9368
+ let record = content.slice(start, end);
9369
+ const handle = /\n5\n([0-9A-Fa-f]+)\n/.exec(record)?.[1];
9370
+ const bodyAt = record.indexOf("\nAcDbHatch\n");
9371
+ const body = bodyAt === -1 ? record : record.slice(bodyAt);
9372
+ const name = /\n2\n([^\n]*)\n/.exec(body)?.[1];
9373
+ const anchor = handle ? ctx.hatchOrigins.get(handle) : void 0;
9374
+ const own = name ? documentPattern(ctx, name) : void 0;
9375
+ const pat = own ?? (name ? resolveHatchPattern(name, []) : void 0);
9376
+ const m77 = pat ? /\n77\n[^\n]*\n/.exec(body) : null;
9377
+ if (pat && m77 && bodyAt !== -1) {
9378
+ const angle2 = Number(/\n52\n([^\n]*)\n/.exec(body)?.[1] ?? 0) * Math.PI / 180;
9379
+ const scale3 = Number(/\n41\n([^\n]*)\n/.exec(body)?.[1] ?? 1) || 1;
9380
+ const cos = Math.cos(angle2);
9381
+ const sin = Math.sin(angle2);
9382
+ const bake = (x, y) => [
9383
+ (cos * x - sin * y) * scale3,
9384
+ (sin * x + cos * y) * scale3
9385
+ ];
9386
+ const lines = pat.lines.map((l) => {
9387
+ const [bx, by] = bake(l.origin.x, l.origin.y);
9388
+ const ox = bx + (anchor?.x ?? 0);
9389
+ const oy = by + (anchor?.y ?? 0);
9390
+ const bakedAngle = l.angle + angle2;
9391
+ const dx = (Math.cos(bakedAngle) * l.deltaX - Math.sin(bakedAngle) * l.deltaY) * scale3;
9392
+ const dy = (Math.sin(bakedAngle) * l.deltaX + Math.cos(bakedAngle) * l.deltaY) * scale3;
9393
+ const dashes = l.dashesMm ?? [];
9394
+ return `53
9395
+ ${(l.angle * 180 / Math.PI + angle2 * 180 / Math.PI).toFixed(6)}
9396
+ 43
9397
+ ${ox.toFixed(6)}
9398
+ 44
9399
+ ${oy.toFixed(6)}
9400
+ 45
9401
+ ${dx.toFixed(6)}
9402
+ 46
9403
+ ${dy.toFixed(6)}
9404
+ 79
9405
+ ${dashes.length}
9406
+ ` + dashes.map((d) => `49
9407
+ ${(d * scale3).toFixed(6)}
9408
+ `).join("");
9409
+ }).join("");
9410
+ const at = bodyAt + m77.index + m77[0].length;
9411
+ const tail = record.slice(at);
9412
+ const writerBlock = tail.startsWith("78\n") ? (() => {
9413
+ const stop = /\n(?:47|98)\n/.exec(tail);
9414
+ return stop ? stop.index + 1 : tail.length;
9415
+ })() : 0;
9416
+ record = record.slice(0, at) + `78
9417
+ ${pat.lines.length}
9418
+ ` + lines + tail.slice(writerBlock);
9419
+ if (own) record = record.replace(/\n76\n1\n/, "\n76\n2\n");
9420
+ else if (name && name !== pat.name.toUpperCase()) {
9421
+ const nameAt = record.indexOf(`
9422
+ 2
9423
+ ${name}
9424
+ `, bodyAt);
9425
+ if (nameAt !== -1)
9426
+ record = record.slice(0, nameAt) + `
9427
+ 2
9428
+ ${pat.name.toUpperCase()}
9429
+ ` + record.slice(nameAt + name.length + 4);
9430
+ }
9431
+ }
9432
+ out.push(content.slice(pos, start), record);
9433
+ pos = end;
9434
+ }
9435
+ out.push(content.slice(pos));
9436
+ return out.join("");
9437
+ };
7301
9438
  var dxfPatternName = (name) => {
7302
9439
  const key = name.toUpperCase().replace(/-/g, "_").replace(/\./g, "_");
7303
9440
  return HatchPredefinedPatterns[key] ?? HatchPredefinedPatterns.ANSI31;
7304
9441
  };
7305
- var writeHatch = (space, obj, opts) => {
9442
+ var writeHatch = (space, obj, opts, ctx) => {
7306
9443
  if (obj.type !== "hatch") return;
7307
9444
  if (obj.fill.kind === "gradient") {
7308
9445
  opts = {
@@ -7326,9 +9463,11 @@ var writeHatch = (space, obj, opts) => {
7326
9463
  obj.fill.kind === "solid" || obj.fill.kind === "gradient" ? pattern({ name: HatchPredefinedPatterns.SOLID }) : obj.fill.kind === "pattern" ? (
7327
9464
  // The curated library names are the standard predefined patterns, so
7328
9465
  // this is a direct mapping; fill.angle/scale ARE the DXF rotation
7329
- // (code 52) and scale (code 41).
9466
+ // (code 52) and scale (code 41). A DOCUMENT pattern goes out under
9467
+ // its own name; its definition lines are spliced in afterwards
9468
+ // (injectHatchPatternLines — the writer has no custom patterns).
7330
9469
  pattern({
7331
- name: dxfPatternName(obj.fill.name),
9470
+ name: documentPattern(ctx, obj.fill.name) ? obj.fill.name.toUpperCase() : dxfPatternName(obj.fill.name),
7332
9471
  angle: obj.fill.angle * RAD2DEG2,
7333
9472
  scale: obj.fill.scale * obj.scale
7334
9473
  })
@@ -7339,10 +9478,12 @@ var writeHatch = (space, obj, opts) => {
7339
9478
  scale: obj.fill.spacing * obj.scale / ANSI31_BASE_SPACING
7340
9479
  })
7341
9480
  );
7342
- space.addHatch(boundary, fill, opts);
9481
+ const written = space.addHatch(boundary, fill, opts);
9482
+ if (obj.fill.kind === "pattern" && obj.fill.origin)
9483
+ ctx.hatchOrigins.set(written.handle, obj.fill.origin);
7343
9484
  };
7344
9485
  var writeDimensionGeometry = (target, geom, opts, ctx) => {
7345
- for (const seg of [...geom.extensionLines, ...geom.dimLines])
9486
+ for (const seg of [...geom.extensionLines, ...geom.dimLines, ...geom.leaders])
7346
9487
  target.addLine(v3(seg.a), v3(seg.b), opts);
7347
9488
  if (geom.dimArc)
7348
9489
  target.addArc(
@@ -7361,7 +9502,7 @@ var writeDimensionGeometry = (target, geom, opts, ctx) => {
7361
9502
  geom.arrowType,
7362
9503
  opts
7363
9504
  );
7364
- for (const text of geom.texts)
9505
+ for (const text of geom.labels)
7365
9506
  addTextLines(
7366
9507
  target,
7367
9508
  text.content,
@@ -7376,13 +9517,43 @@ var writeDimensionGeometry = (target, geom, opts, ctx) => {
7376
9517
  {
7377
9518
  styleName: textStyleFor2(ctx, text.font).name,
7378
9519
  font: text.font,
7379
- widthFactor: text.widthFactor
9520
+ widthFactor: text.widthFactor,
9521
+ spans: text.spans
7380
9522
  }
7381
9523
  );
7382
9524
  };
7383
- var dimStyleFor2 = (ctx, style) => {
9525
+ var addDimStyleRecord = (ctx, name, style) => {
9526
+ const table = ctx.writer.document.tables.dimStyleTable;
9527
+ const predefined = name.toUpperCase() === "STANDARD" && !ctx.dimStyleNames.has("STANDARD") ? table.records.find((r) => r.name.toUpperCase() === "STANDARD") : void 0;
9528
+ const rec = predefined ?? ctx.writer.addDimStyle(uniqueTableName(ctx.dimStyleNames, name));
9529
+ if (predefined) ctx.dimStyleNames.add("STANDARD");
7384
9530
  const scale3 = style.scale ?? 1;
9531
+ rec.DIMASZ = style.arrowSize;
9532
+ rec.DIMEXO = style.extensionOffset;
9533
+ rec.DIMEXE = style.extensionOvershoot;
9534
+ rec.DIMTXT = style.textHeight;
9535
+ rec.DIMGAP = style.textOffset;
9536
+ rec.DIMDEC = style.precision;
9537
+ if (scale3 !== 1) rec.DIMSCALE = scale3;
9538
+ if (style.arrowType !== "filled") rec.DIMBLK = DIMBLK_NAMES[style.arrowType];
9539
+ rec.DIMTXSTY = dimTextStyleFor(
9540
+ ctx,
9541
+ dimFontFace(style),
9542
+ style.widthFactor
9543
+ ).handle;
9544
+ return rec.name;
9545
+ };
9546
+ var createDocDimStyles = (ctx) => {
9547
+ for (const s of ctx.doc.dimStyles)
9548
+ ctx.dimStyleRecords.set(
9549
+ s.id,
9550
+ addDimStyleRecord(ctx, sanitizeLayerName(s.name), s)
9551
+ );
9552
+ };
9553
+ var dimOverrideStyleFor = (ctx, base, style) => {
9554
+ const baseName = ctx.dimStyleRecords.get(base.id) ?? ctx.dimStyleRecords.values().next().value ?? "Standard";
7385
9555
  const key = JSON.stringify([
9556
+ baseName,
7386
9557
  style.arrowSize,
7387
9558
  style.arrowType,
7388
9559
  style.extensionOffset,
@@ -7391,21 +9562,13 @@ var dimStyleFor2 = (ctx, style) => {
7391
9562
  style.textOffset,
7392
9563
  style.precision,
7393
9564
  dimFontFace(style),
7394
- scale3
9565
+ style.scale ?? 1
7395
9566
  ]);
7396
- let name = ctx.dimStyles.get(key);
7397
- if (name) return name;
7398
- name = ctx.dimStyles.size === 0 ? DIM_STYLE_NAME : `${DIM_STYLE_NAME}_${ctx.dimStyles.size}`;
7399
- const rec = ctx.writer.addDimStyle(name);
7400
- rec.DIMASZ = style.arrowSize;
7401
- rec.DIMEXO = style.extensionOffset;
7402
- rec.DIMEXE = style.extensionOvershoot;
7403
- rec.DIMTXT = style.textHeight;
7404
- rec.DIMGAP = style.textOffset;
7405
- rec.DIMDEC = style.precision;
7406
- if (scale3 !== 1) rec.DIMSCALE = scale3;
7407
- if (style.arrowType !== "filled") rec.DIMBLK = DIMBLK_NAMES[style.arrowType];
7408
- rec.DIMTXSTY = textStyleFor2(ctx, dimFontFace(style)).handle;
9567
+ const cached = ctx.dimStyles.get(key);
9568
+ if (cached) return cached;
9569
+ const n = (ctx.dimOverrideCounts.get(baseName) ?? 0) + 1;
9570
+ ctx.dimOverrideCounts.set(baseName, n);
9571
+ const name = addDimStyleRecord(ctx, `${baseName}_${n}`, style);
7409
9572
  ctx.dimStyles.set(key, name);
7410
9573
  return name;
7411
9574
  };
@@ -7431,26 +9594,38 @@ var writeDimension = (space, obj, opts, ctx) => {
7431
9594
  const blockName = `LINEA_DIM_${ctx.dimBlockCount++}`;
7432
9595
  const block = ctx.writer.addBlock(blockName);
7433
9596
  writeDimensionGeometry(block, geom, opts, ctx);
7434
- const styleName = obj.styleId || obj.styleOverride || obj.scale !== 1 ? dimStyleFor2(ctx, {
7435
- ...dimStyleFor(obj, ctx.doc.dimStyles),
9597
+ const baseStyle = dimStyleFor(obj, ctx.doc.dimStyles);
9598
+ const hasOverride = obj.styleOverride !== void 0 && Object.values(obj.styleOverride).some((v) => v !== void 0);
9599
+ const styleName = hasOverride || obj.scale !== 1 ? dimOverrideStyleFor(ctx, baseStyle, {
9600
+ ...baseStyle,
7436
9601
  ...obj.styleOverride,
7437
9602
  scale: obj.scale
7438
- }) : DIM_STYLE_NAME;
9603
+ }) : ctx.dimStyleRecords.get(baseStyle.id) ?? dimOverrideStyleFor(ctx, baseStyle, baseStyle);
9604
+ const content = obj.labels[0]?.content;
9605
+ const precision = {
9606
+ ...dimStyleFor(obj, ctx.doc.dimStyles),
9607
+ ...obj.styleOverride
9608
+ }.precision;
9609
+ const text = content === void 0 ? void 0 : content.split(DIM_DEFAULT_CONTENT[obj.kind]).join("<>").split(DIM_VALUE_TOKEN).join(
9610
+ obj.kind === "radial" || obj.kind === "diameter" ? formatDimValue(geom.value, precision) : "<>"
9611
+ );
7439
9612
  const dimOpts = {
7440
9613
  blockName,
7441
- text: geom.texts[0]?.content,
9614
+ ...text === void 0 ? {} : { text },
7442
9615
  styleName,
7443
9616
  // Text middle point (group 11) — without it a regenerating CAD places
7444
9617
  // the value at the style default instead of where the user saw it.
7445
- ...geom.texts[0] ? { middlePoint: v3(geom.texts[0].anchor) } : {},
9618
+ ...geom.labels[0] ? { middlePoint: v3(geom.labels[0].anchor) } : {},
7446
9619
  ...opts
7447
9620
  };
7448
9621
  switch (obj.kind) {
7449
9622
  case "angular": {
7450
9623
  const [vertex, a, b] = obj.points;
9624
+ const [frame] = dimensionLabelFrames(obj);
7451
9625
  space.addAngularPointsDim(v3(vertex), v3(a), v3(b), {
7452
9626
  ...dimOpts,
7453
- definitionPoint: v3(add(vertex, obj.offset))
9627
+ // A point on the arc (its middle): the radius and which arc.
9628
+ definitionPoint: v3(frame.toWorld((frame.s0 + frame.s1) / 2, 0))
7454
9629
  });
7455
9630
  break;
7456
9631
  }
@@ -7466,8 +9641,7 @@ var writeDimension = (space, obj, opts, ctx) => {
7466
9641
  }
7467
9642
  case "linear": {
7468
9643
  const [p1, p2] = obj.points;
7469
- const upLen = Math.hypot(obj.offset.x, obj.offset.y);
7470
- const u = upLen > 1e-9 ? perp(scale(obj.offset, 1 / upLen)) : norm(sub(p2, p1));
9644
+ const { u } = linearAxis(obj);
7471
9645
  space.addLinearDim(v3(p1), v3(p2), {
7472
9646
  ...dimOpts,
7473
9647
  definitionPoint: v3(geom.dimLines[0].b),
@@ -7478,7 +9652,90 @@ var writeDimension = (space, obj, opts, ctx) => {
7478
9652
  }
7479
9653
  return true;
7480
9654
  };
9655
+ var tableCellMtext = (text, ctx) => {
9656
+ if (text.content.trim() === "") return "";
9657
+ const eff = resolveTextStyle(text, ctx.doc.textStyles);
9658
+ const face = fontFace(eff.font, eff.bold, eff.italic);
9659
+ const { font, bold, italic } = parseFontFace(face);
9660
+ const height = eff.textHeight * text.scale;
9661
+ const body = text.spans?.length ? encodeRichMtext(text.content, text.spans, face) : encodeMtext(text.content);
9662
+ return `{\\f${fontTypeface(resolveFont(font))}|b${bold ? 1 : 0}|i${italic ? 1 : 0}|c0|p34;\\H${+height.toFixed(6)};` + (eff.widthFactor !== 1 ? `\\W${eff.widthFactor};` : "") + `${body}}`;
9663
+ };
9664
+ var attachmentOf = (hAlign, vAlign) => (vAlign === "top" ? 0 : vAlign === "center" ? 3 : 6) + (hAlign === "left" ? 1 : hAlign === "center" ? 2 : 3);
9665
+ var writeNativeTable = (space, obj, ctx) => {
9666
+ const layout = tableLayout(obj, ctx.doc.textStyles);
9667
+ const name = `*T${++ctx.tableBlockCount}`;
9668
+ const block = ctx.writer.document.blocks.addBlock(
9669
+ name,
9670
+ ctx.writer.document.objects,
9671
+ false
9672
+ );
9673
+ block.flags = 1;
9674
+ const before = ctx.exported;
9675
+ const origin = { x: 0, y: 0 };
9676
+ for (const child of layout.children)
9677
+ writeObject(
9678
+ block,
9679
+ rotateObject(
9680
+ translateObject(child, { x: -obj.position.x, y: -obj.position.y }),
9681
+ origin,
9682
+ -obj.rotation
9683
+ ),
9684
+ ctx
9685
+ );
9686
+ ctx.exported = before;
9687
+ const blockRecordHandle = ctx.writer.document.tables.blockRecordTable.records.find(
9688
+ (r) => r.name === name
9689
+ )?.handle ?? "0";
9690
+ const rows = obj.rows.length;
9691
+ const cols = obj.columns.length;
9692
+ const anchorAt = new Map(
9693
+ layout.anchors.map((a) => [`${a.row}:${a.col}`, a])
9694
+ );
9695
+ const cells = [];
9696
+ for (let r = 0; r < rows; r++)
9697
+ for (let c = 0; c < cols; c++) {
9698
+ const a = anchorAt.get(`${r}:${c}`);
9699
+ if (!a) {
9700
+ cells.push({ text: "", cols: 1, rows: 1, merged: true, alignment: 1 });
9701
+ continue;
9702
+ }
9703
+ const { text } = tableCellFrame(obj, ctx.doc.textStyles, r, c);
9704
+ cells.push({
9705
+ text: tableCellMtext(text, ctx),
9706
+ cols: a.cols,
9707
+ rows: a.rows,
9708
+ merged: false,
9709
+ alignment: attachmentOf(text.hAlign, text.vAlign)
9710
+ });
9711
+ }
9712
+ const diffs = (xs) => xs.slice(1).map((x, i) => x - xs[i]);
9713
+ const entity = new AcadTableEntity({
9714
+ blockName: name,
9715
+ blockRecordHandle,
9716
+ tableStyleHandle: ctx.tableStyleHandle,
9717
+ position: obj.position,
9718
+ direction: { x: Math.cos(obj.rotation), y: Math.sin(obj.rotation) },
9719
+ rowHeights: diffs(layout.rowY),
9720
+ colWidths: diffs(layout.colX),
9721
+ cells
9722
+ });
9723
+ entity.layerName = ctx.layerNames.get(obj.layerId) ?? "0";
9724
+ if (obj.color) {
9725
+ entity.colorNumber = nearestAci(obj.color);
9726
+ const tc = hexToTrueColor(obj.color);
9727
+ if (tc !== null) entity.trueColor = String(tc);
9728
+ }
9729
+ space.addEntity(entity);
9730
+ };
7481
9731
  var writeObject = (space, obj, ctx) => {
9732
+ const before = space.entities.length;
9733
+ writeObjectEntities(space, obj, ctx);
9734
+ if (obj.lineWidth !== void 0)
9735
+ for (const entity of space.entities.slice(before))
9736
+ setLineWeight(entity, mmToLineWeight(obj.lineWidth));
9737
+ };
9738
+ var writeObjectEntities = (space, obj, ctx) => {
7482
9739
  const opts = optionsFor(obj, ctx);
7483
9740
  switch (obj.type) {
7484
9741
  case "line":
@@ -7523,12 +9780,27 @@ var writeObject = (space, obj, ctx) => {
7523
9780
  );
7524
9781
  break;
7525
9782
  case "hatch":
7526
- writeHatch(space, obj, opts);
9783
+ writeHatch(space, obj, opts, ctx);
7527
9784
  break;
7528
9785
  case "text": {
7529
9786
  const eff = resolveTextStyle(obj, ctx.doc.textStyles);
7530
9787
  const { arrowSize, arrowType } = eff;
7531
9788
  const face = fontFace(eff.font, eff.bold, eff.italic);
9789
+ for (const part of textFrameGeometry(
9790
+ obj,
9791
+ ctx.measure,
9792
+ ctx.doc.textStyles
9793
+ ))
9794
+ space.addLWPolyline(
9795
+ part.points.map((v) => ({
9796
+ point: { x: v.x, y: v.y },
9797
+ bulge: v.bulge
9798
+ })),
9799
+ {
9800
+ flags: part.closed ? LWPolylineFlags.Closed : LWPolylineFlags.None,
9801
+ ...opts
9802
+ }
9803
+ );
7532
9804
  if (obj.leaderLines.length > 0 && ctx.explodeLeaders) {
7533
9805
  for (const line of obj.leaderLines) {
7534
9806
  space.addLWPolyline(
@@ -7562,13 +9834,15 @@ var writeObject = (space, obj, ctx) => {
7562
9834
  junction,
7563
9835
  doglegDirection: { x: toText.x / gap, y: toText.y / gap },
7564
9836
  doglegLength: gap,
7565
- content: escapeMtext(obj.content),
9837
+ content: obj.spans?.length ? encodeRichMtext(obj.content, obj.spans, face) : encodeMtext(obj.content),
7566
9838
  textHeight: eff.textHeight,
7567
9839
  arrowSize,
7568
9840
  overallScale: obj.scale,
7569
9841
  textLocation: obj.position,
9842
+ // Model alignment → MTEXT-style attachment point (1–9).
9843
+ attachment: 1 + 3 * ["top", "center", "bottom"].indexOf(obj.vAlign) + ["left", "center", "right"].indexOf(obj.hAlign),
7570
9844
  styleHandle: ctx.mleaderStyleHandle,
7571
- textStyleHandle: textStyleFor2(ctx, face).handle,
9845
+ textStyleHandle: textRecordFor(ctx, obj, face).handle,
7572
9846
  lineTypeHandle: ctx.lineTypeHandle
7573
9847
  });
7574
9848
  entity.layerName = ctx.layerNames.get(obj.layerId) ?? "0";
@@ -7592,9 +9866,10 @@ var writeObject = (space, obj, ctx) => {
7592
9866
  ctx.measure,
7593
9867
  obj.width === void 0 ? void 0 : obj.width * obj.scale,
7594
9868
  {
7595
- styleName: textStyleFor2(ctx, face).name,
9869
+ styleName: textRecordFor(ctx, obj, face).name,
7596
9870
  font: face,
7597
- widthFactor: eff.widthFactor
9871
+ widthFactor: eff.widthFactor,
9872
+ spans: obj.spans
7598
9873
  }
7599
9874
  );
7600
9875
  break;
@@ -7606,6 +9881,10 @@ var writeObject = (space, obj, ctx) => {
7606
9881
  }
7607
9882
  break;
7608
9883
  case "block": {
9884
+ if (ctx.blockChain.includes(obj.definitionId)) {
9885
+ skip(ctx, "block");
9886
+ return;
9887
+ }
7609
9888
  const children = getEvaluatedWorldChildren(
7610
9889
  obj,
7611
9890
  ctx.doc.blockDefinitions,
@@ -7617,14 +9896,23 @@ var writeObject = (space, obj, ctx) => {
7617
9896
  return;
7618
9897
  }
7619
9898
  const before = ctx.exported;
7620
- for (const child of children) {
7621
- if (child.type === "block" || child.type === "viewport") continue;
7622
- writeObject(space, child, ctx);
9899
+ ctx.blockChain.push(obj.definitionId);
9900
+ try {
9901
+ for (const child of children) {
9902
+ if (child.type === "viewport") continue;
9903
+ writeObject(space, child, ctx);
9904
+ }
9905
+ } finally {
9906
+ ctx.blockChain.pop();
7623
9907
  }
7624
9908
  ctx.exported = before;
7625
9909
  break;
7626
9910
  }
7627
9911
  case "table": {
9912
+ if (!ctx.explodeTables) {
9913
+ writeNativeTable(space, obj, ctx);
9914
+ break;
9915
+ }
7628
9916
  const before = ctx.exported;
7629
9917
  for (const child of tableLayout(obj, ctx.doc.textStyles).children)
7630
9918
  writeObject(space, child, ctx);
@@ -7634,9 +9922,6 @@ var writeObject = (space, obj, ctx) => {
7634
9922
  case "viewport":
7635
9923
  skip(ctx, "viewport");
7636
9924
  return;
7637
- case "image":
7638
- skip(ctx, "image");
7639
- return;
7640
9925
  case "reference": {
7641
9926
  if (obj.hidden || obj.format !== "dxf" && obj.format !== "dwg" || obj.source.kind !== "path") {
7642
9927
  skip(ctx, "reference");
@@ -7690,10 +9975,22 @@ var writeViewport = (space, vp, id, ctx) => {
7690
9975
  );
7691
9976
  ctx.exported++;
7692
9977
  };
9978
+ var DWG_DXF_OPTIONS = {
9979
+ explodeLeaders: true,
9980
+ explodeTables: true
9981
+ };
7693
9982
  var buildDxf = (doc, measure, options = {}) => {
7694
9983
  const writer = new DxfWriter();
7695
9984
  writer.setUnits(Units.Millimeters);
7696
9985
  const emittedLtypes = /* @__PURE__ */ new Set();
9986
+ for (const t of doc.lineTypes) {
9987
+ if (t.dashesMm.length === 0) continue;
9988
+ const dxfName = t.name.toUpperCase();
9989
+ if (emittedLtypes.has(dxfName)) continue;
9990
+ emittedLtypes.add(dxfName);
9991
+ const descriptive = (t.description ?? t.name).replace(/[^\x20-\x7e]/g, "").slice(0, 47);
9992
+ writer.addLType(dxfName, descriptive, t.dashesMm);
9993
+ }
7697
9994
  for (const p of LINE_TYPE_DEFS) {
7698
9995
  if (p.dashesMm.length === 0) continue;
7699
9996
  const dxfName = p.dxfName ?? p.name.toUpperCase();
@@ -7715,18 +10012,22 @@ var buildDxf = (doc, measure, options = {}) => {
7715
10012
  layerNames.set(layer.id, name);
7716
10013
  if (name === "0") {
7717
10014
  const zero = writer.layer("0");
7718
- if (zero) layerRecords.set(layer.id, zero);
10015
+ if (zero) {
10016
+ setLineWeight(zero, mmToLineWeight(layer.lineWidth));
10017
+ layerRecords.set(layer.id, zero);
10018
+ }
7719
10019
  continue;
7720
10020
  }
7721
10021
  const flags = (layer.visible ? 0 : LayerFlags.Frozen) | (layer.locked ? LayerFlags.Locked : 0);
7722
10022
  const record = writer.addLayer(
7723
10023
  name,
7724
10024
  nearestAci(layer.color),
7725
- dxfLineTypeName(layer.lineType) ?? "Continuous",
10025
+ dxfLineTypeName(layer.lineType, doc.lineTypes) ?? "Continuous",
7726
10026
  flags || void 0
7727
10027
  );
7728
10028
  const tc = hexToTrueColor(layer.color);
7729
10029
  if (tc !== null) record.trueColor = tc;
10030
+ setLineWeight(record, mmToLineWeight(layer.lineWidth));
7730
10031
  layerRecords.set(layer.id, record);
7731
10032
  }
7732
10033
  const textStyleHandle = writer.document.tables.styleTable.records.find((r) => r.name === "Standard")?.handle ?? "0";
@@ -7747,6 +10048,22 @@ var buildDxf = (doc, measure, options = {}) => {
7747
10048
  );
7748
10049
  mleaderStyle.ownerObjecthandle = mleaderDict.handle;
7749
10050
  mleaderDict.addEntryObject("Standard", mleaderStyle.handle);
10051
+ const tableStyleDict = writer.document.objects.addDictionary();
10052
+ writer.document.objects.addEntryToRoot(
10053
+ "ACAD_TABLESTYLE",
10054
+ tableStyleDict.handle
10055
+ );
10056
+ const tableStyle = writer.document.objects.addObject(
10057
+ new TableStyleObject({
10058
+ name: "Standard",
10059
+ textStyleName: "Standard",
10060
+ textHeight: doc.textStyles[0]?.textHeight ?? 2.5,
10061
+ margin: 1.5,
10062
+ dictionaryHandle: tableStyleDict.handle
10063
+ })
10064
+ );
10065
+ tableStyle.ownerObjecthandle = tableStyleDict.handle;
10066
+ tableStyleDict.addEntryObject("Standard", tableStyle.handle);
7750
10067
  const ctx = {
7751
10068
  doc,
7752
10069
  measure,
@@ -7757,14 +10074,24 @@ var buildDxf = (doc, measure, options = {}) => {
7757
10074
  exported: 0,
7758
10075
  dimBlockCount: 0,
7759
10076
  xrefBlocks: /* @__PURE__ */ new Map(),
10077
+ blockChain: [],
7760
10078
  explodeLeaders: options.explodeLeaders ?? false,
10079
+ explodeTables: options.explodeTables ?? false,
10080
+ tableStyleHandle: tableStyle.handle,
10081
+ tableBlockCount: 0,
7761
10082
  mleaderStyleHandle: mleaderStyle.handle,
7762
10083
  textStyleHandle,
7763
10084
  lineTypeHandle,
7764
10085
  textStyles: /* @__PURE__ */ new Map(),
7765
- dimStyles: /* @__PURE__ */ new Map()
10086
+ styleNames: /* @__PURE__ */ new Set(),
10087
+ dimStyleRecords: /* @__PURE__ */ new Map(),
10088
+ dimStyleNames: /* @__PURE__ */ new Set(),
10089
+ dimStyles: /* @__PURE__ */ new Map(),
10090
+ dimOverrideCounts: /* @__PURE__ */ new Map(),
10091
+ hatchOrigins: /* @__PURE__ */ new Map()
7766
10092
  };
7767
- dimStyleFor2(ctx, doc.dimStyles[0]);
10093
+ createDocTextStyles(ctx);
10094
+ createDocDimStyles(ctx);
7768
10095
  for (const obj of doc.objects)
7769
10096
  writeObject(writer.document.blocks.modelSpace, obj, ctx);
7770
10097
  const layoutRecords = [];
@@ -7836,16 +10163,35 @@ var buildDxf = (doc, measure, options = {}) => {
7836
10163
  );
7837
10164
  if (record) record.layoutObject = layoutObj.handle;
7838
10165
  }
10166
+ const content = injectHatchPatternLines(
10167
+ injectStyleTypefaces(writer.stringify(), ctx.textStyles),
10168
+ ctx
10169
+ );
7839
10170
  return {
7840
- content: injectStyleTypefaces(writer.stringify(), ctx.textStyles),
10171
+ // The writer's CLASSES section is always empty: declare the non-fixed
10172
+ // types this file uses (AutoCAD expects them declared).
10173
+ content: content.replace(
10174
+ "\n0\nSECTION\n2\nCLASSES\n0\nENDSEC\n",
10175
+ `
10176
+ 0
10177
+ SECTION
10178
+ 2
10179
+ CLASSES
10180
+ ${classesSection(content)}0
10181
+ ENDSEC
10182
+ `
10183
+ ),
7841
10184
  exported: ctx.exported,
7842
10185
  skipped: ctx.skipped
7843
10186
  };
7844
10187
  };
7845
10188
  var injectStyleTypefaces = (content, textStyles) => {
7846
10189
  let out = content;
7847
- for (const [fontId, rec] of textStyles) {
7848
- const typeface = fontTypeface(resolveFont(fontId));
10190
+ for (const rec of textStyles.values()) {
10191
+ const fontId = rec.face;
10192
+ const { font: bare } = parseFontFace(fontId);
10193
+ const def = resolveFont(fontId);
10194
+ const typeface = bare !== void 0 && bare !== def.id ? bare : fontTypeface(def);
7849
10195
  const { bold, italic } = parseFontFace(fontId);
7850
10196
  const flags = (italic ? 16777216 : 0) | (bold ? 33554432 : 0);
7851
10197
  const marker = `
@@ -7875,6 +10221,7 @@ export {
7875
10221
  add,
7876
10222
  sub,
7877
10223
  scale,
10224
+ dot,
7878
10225
  len,
7879
10226
  dist,
7880
10227
  norm,
@@ -7885,8 +10232,6 @@ export {
7885
10232
  normAngle,
7886
10233
  closestPointOnSegment,
7887
10234
  remapBlockInputRefs,
7888
- imageCenter,
7889
- imageCorners,
7890
10235
  translateObject,
7891
10236
  rotateObject,
7892
10237
  scaleObjectUniform,
@@ -7894,10 +10239,12 @@ export {
7894
10239
  Vec2Schema,
7895
10240
  ColorSchema,
7896
10241
  FONTS,
10242
+ fontFace,
7897
10243
  parseFontFace,
7898
10244
  REAL_FACES,
7899
10245
  realFace,
7900
10246
  resolveFont,
10247
+ fontFromDxfStyle,
7901
10248
  TextStyleSchema,
7902
10249
  DEFAULT_TEXT_STYLE,
7903
10250
  textStyleFor,
@@ -7916,6 +10263,8 @@ export {
7916
10263
  approximateTextMeasurer,
7917
10264
  lineSegments,
7918
10265
  textBlock,
10266
+ normalizeSpans,
10267
+ richTextBlock,
7919
10268
  textHeight,
7920
10269
  textArrowSize,
7921
10270
  textArrowType,
@@ -7923,10 +10272,34 @@ export {
7923
10272
  textBoxCorners,
7924
10273
  textFrameGeometry,
7925
10274
  tableLayout,
10275
+ hashAsset,
10276
+ dataUrlBytes,
10277
+ bytesToDataUrl,
10278
+ imagePixelSize,
10279
+ PX_TO_MM,
10280
+ embeddedAssetId,
10281
+ matIdentity,
10282
+ matMultiply,
10283
+ matApply,
10284
+ signedArcSweep,
10285
+ arcToBeziers,
10286
+ computeHatchLines,
10287
+ ellipseArcToBeziers,
10288
+ getHatchPattern,
10289
+ MAX_HATCH_SEGMENTS,
10290
+ estimatePatternSegments,
10291
+ computePatternGeometry,
10292
+ HatchPatternSchema,
10293
+ hatchPatternKey,
10294
+ resolveHatchPattern,
7926
10295
  getLineType,
7927
10296
  renderDashes,
10297
+ LineTypeSchema,
10298
+ lineTypeKey,
10299
+ resolveLineType,
7928
10300
  CURRENT_DOCUMENT_VERSION,
7929
10301
  dimensionRawToOffset,
10302
+ dimensionRawToLabels,
7930
10303
  migrateDocument,
7931
10304
  HEX_COLOR,
7932
10305
  valueParams,
@@ -7935,6 +10308,7 @@ export {
7935
10308
  ensureScriptTranspiler,
7936
10309
  executeScriptModule,
7937
10310
  scriptTranspilerReady,
10311
+ BlockChildSchema,
7938
10312
  expandWithBlockDependents,
7939
10313
  invalidateBlockCache,
7940
10314
  getBlockError,
@@ -7949,9 +10323,18 @@ export {
7949
10323
  testBlockDefinition,
7950
10324
  getEvaluatedWorldChildren,
7951
10325
  ANSI31_BASE_SPACING,
10326
+ linearAxis,
7952
10327
  dimensionGeometry,
10328
+ hexToRgb,
7953
10329
  rgbToHex,
7954
10330
  aciToHex,
10331
+ lineWeightToMm,
10332
+ decodeTextEscapes,
10333
+ mtextWidthFactor,
10334
+ mtextFontFlags,
10335
+ cleanText,
10336
+ cleanRichText,
10337
+ DWG_DXF_OPTIONS,
7955
10338
  buildDxf
7956
10339
  };
7957
- //# sourceMappingURL=chunk-NCNZ7A6M.js.map
10340
+ //# sourceMappingURL=chunk-BQCSFVAF.js.map