@linkiez/dxf-renew 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/instructions/code-patterns.instructions.md +1 -1
- package/.github/instructions/exdxf.instruction.md +161 -0
- package/.github/instructions/tdd.instructions.md +271 -0
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +23 -0
- package/CONTRIBUTING.md +16 -14
- package/PLAN.md +34 -84
- package/README.md +43 -8
- package/dist/dxf.js +1388 -376
- package/docs/DIMENSION_SUMMARY.md +11 -5
- package/docs/DXF_VERSION_SUPPORT.md +45 -0
- package/docs/ENTITY_SVG_ROADMAP.md +96 -0
- package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
- package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
- package/docs/README.md +22 -0
- package/docs/SVG_RENDERING_INTEGRATION_TESTS.md +119 -0
- package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +1 -1
- package/lib/Helper.cjs +2 -2
- package/lib/Helper.cjs.map +2 -2
- package/lib/Helper.js +2 -2
- package/lib/Helper.js.map +2 -2
- package/lib/denormalise.cjs +131 -91
- package/lib/denormalise.cjs.map +2 -2
- package/lib/denormalise.js +131 -91
- package/lib/denormalise.js.map +2 -2
- package/lib/dimensionToSVG.cjs +318 -53
- package/lib/dimensionToSVG.cjs.map +3 -3
- package/lib/dimensionToSVG.js +316 -52
- package/lib/dimensionToSVG.js.map +2 -2
- package/lib/handlers/entities.cjs +90 -26
- package/lib/handlers/entities.cjs.map +3 -3
- package/lib/handlers/entities.js +90 -26
- package/lib/handlers/entities.js.map +3 -3
- package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
- package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/dgnUnderlay.js +71 -0
- package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
- package/lib/handlers/entity/dimension.cjs +24 -0
- package/lib/handlers/entity/dimension.cjs.map +2 -2
- package/lib/handlers/entity/dimension.js +24 -0
- package/lib/handlers/entity/dimension.js.map +2 -2
- package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
- package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/dwfUnderlay.js +71 -0
- package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
- package/lib/handlers/entity/image.cjs +123 -0
- package/lib/handlers/entity/image.cjs.map +7 -0
- package/lib/handlers/entity/image.js +88 -0
- package/lib/handlers/entity/image.js.map +7 -0
- package/lib/handlers/entity/leader.cjs +148 -0
- package/lib/handlers/entity/leader.cjs.map +7 -0
- package/lib/handlers/entity/leader.js +113 -0
- package/lib/handlers/entity/leader.js.map +7 -0
- package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
- package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/pdfUnderlay.js +71 -0
- package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
- package/lib/handlers/entity/tolerance.cjs +90 -0
- package/lib/handlers/entity/tolerance.cjs.map +7 -0
- package/lib/handlers/entity/tolerance.js +55 -0
- package/lib/handlers/entity/tolerance.js.map +7 -0
- package/lib/handlers/objects.cjs +257 -136
- package/lib/handlers/objects.cjs.map +2 -2
- package/lib/handlers/objects.js +257 -136
- package/lib/handlers/objects.js.map +2 -2
- package/lib/toSVG.cjs +71 -8
- package/lib/toSVG.cjs.map +3 -3
- package/lib/toSVG.js +72 -9
- package/lib/toSVG.js.map +2 -2
- package/lib/types/dimension-entity.cjs.map +1 -1
- package/lib/types/entity.cjs.map +1 -1
- package/lib/types/image-entity.cjs +17 -0
- package/lib/types/image-entity.cjs.map +7 -0
- package/lib/types/image-entity.js +1 -0
- package/lib/types/image-entity.js.map +7 -0
- package/lib/types/index.cjs +8 -0
- package/lib/types/index.cjs.map +2 -2
- package/lib/types/index.js +4 -0
- package/lib/types/index.js.map +2 -2
- package/lib/types/leader-entity.cjs +17 -0
- package/lib/types/leader-entity.cjs.map +7 -0
- package/lib/types/leader-entity.js +1 -0
- package/lib/types/leader-entity.js.map +7 -0
- package/lib/types/options.cjs.map +1 -1
- package/lib/types/tables.cjs.map +1 -1
- package/lib/types/tolerance-entity.cjs +17 -0
- package/lib/types/tolerance-entity.cjs.map +7 -0
- package/lib/types/tolerance-entity.js +1 -0
- package/lib/types/tolerance-entity.js.map +7 -0
- package/lib/types/underlay-entity.cjs +17 -0
- package/lib/types/underlay-entity.cjs.map +7 -0
- package/lib/types/underlay-entity.js +1 -0
- package/lib/types/underlay-entity.js.map +7 -0
- package/lib/util/escapeXmlText.cjs +27 -0
- package/lib/util/escapeXmlText.cjs.map +7 -0
- package/lib/util/escapeXmlText.js +7 -0
- package/lib/util/escapeXmlText.js.map +7 -0
- package/package.json +6 -1
- package/playwright.config.cjs +20 -0
- package/src/Helper.ts +3 -3
- package/src/denormalise.ts +182 -116
- package/src/dimensionToSVG.ts +466 -54
- package/src/handlers/entities.ts +109 -34
- package/src/handlers/entity/dgnUnderlay.ts +94 -0
- package/src/handlers/entity/dimension.ts +27 -1
- package/src/handlers/entity/dwfUnderlay.ts +94 -0
- package/src/handlers/entity/image.ts +118 -0
- package/src/handlers/entity/leader.ts +153 -0
- package/src/handlers/entity/pdfUnderlay.ts +94 -0
- package/src/handlers/entity/tolerance.ts +75 -0
- package/src/handlers/objects.ts +323 -139
- package/src/toSVG.ts +98 -7
- package/src/types/dimension-entity.ts +11 -0
- package/src/types/entity.ts +10 -0
- package/src/types/image-entity.ts +35 -0
- package/src/types/index.ts +4 -0
- package/src/types/leader-entity.ts +40 -0
- package/src/types/options.ts +41 -0
- package/src/types/tables.ts +84 -0
- package/src/types/tolerance-entity.ts +20 -0
- package/src/types/underlay-entity.ts +35 -0
- package/src/util/escapeXmlText.ts +10 -0
- package/tools/browser_test_server.cjs +87 -0
- package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
- package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
- package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
- package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
- package/tools/ezdxf_validate_fixtures.py +165 -0
- package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
- package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
- package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
package/src/toSVG.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Box2 } from 'vecks'
|
|
2
2
|
|
|
3
3
|
import denormalise from './denormalise'
|
|
4
|
-
import dimensionToSVG from './dimensionToSVG'
|
|
4
|
+
import dimensionToSVG, { getDimensionGeometryBBox } from './dimensionToSVG'
|
|
5
5
|
import entityToPolyline from './entityToPolyline'
|
|
6
6
|
import getRGBForEntity from './getRGBForEntity'
|
|
7
|
+
import escapeXmlText from './util/escapeXmlText'
|
|
7
8
|
import logger from './util/logger'
|
|
8
9
|
import rgbToColorAttribute from './util/rgbToColorAttribute'
|
|
9
10
|
import rotate from './util/rotate'
|
|
@@ -16,10 +17,13 @@ import type {
|
|
|
16
17
|
DimensionEntity,
|
|
17
18
|
EllipseEntity,
|
|
18
19
|
Entity,
|
|
20
|
+
LeaderEntity,
|
|
19
21
|
MTextEntity,
|
|
20
22
|
ParsedDXF,
|
|
21
23
|
SplineEntity,
|
|
22
24
|
TextEntity,
|
|
25
|
+
ToleranceEntity,
|
|
26
|
+
ToSVGOptions,
|
|
23
27
|
} from './types'
|
|
24
28
|
import type { BoundsAndElement } from './types/svg'
|
|
25
29
|
|
|
@@ -90,6 +94,26 @@ const lwpolyline = (entity: Entity): BoundsAndElement => {
|
|
|
90
94
|
)
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
const leader = (entity: LeaderEntity): BoundsAndElement | null => {
|
|
98
|
+
if (!entity.vertices || entity.vertices.length < 2) return null
|
|
99
|
+
|
|
100
|
+
const bbox0 = entity.vertices.reduce(
|
|
101
|
+
(acc, p) => acc.expandByPoint({ x: p.x, y: p.y }),
|
|
102
|
+
new Box2(),
|
|
103
|
+
)
|
|
104
|
+
const d = entity.vertices.reduce((acc, p, i) => {
|
|
105
|
+
acc += i === 0 ? 'M' : 'L'
|
|
106
|
+
acc += p.x + ',' + p.y
|
|
107
|
+
return acc
|
|
108
|
+
}, '')
|
|
109
|
+
|
|
110
|
+
return transformBoundingBoxAndElement(
|
|
111
|
+
bbox0,
|
|
112
|
+
`<path d="${d}" />`,
|
|
113
|
+
entity.transforms ?? [],
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
93
117
|
|
|
94
118
|
/**
|
|
95
119
|
* Create a <circle /> element for the CIRCLE entity.
|
|
@@ -311,7 +335,7 @@ const text = (entity: TextEntity): BoundsAndElement => {
|
|
|
311
335
|
.expandByPoint({ x: x + textWidth, y: y + height })
|
|
312
336
|
|
|
313
337
|
const rotationDegrees = (rotation * 180) / Math.PI
|
|
314
|
-
const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`
|
|
338
|
+
const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${escapeXmlText(content)}</text>`
|
|
315
339
|
|
|
316
340
|
const { bbox, element } = addFlipXIfApplicable(entity, {
|
|
317
341
|
bbox: bbox0,
|
|
@@ -340,8 +364,36 @@ const mtext = (entity: MTextEntity): BoundsAndElement => {
|
|
|
340
364
|
? Math.atan2(entity.xAxisY, entity.xAxisX)
|
|
341
365
|
: 0
|
|
342
366
|
const rotationDegrees = (rotation * 180) / Math.PI
|
|
367
|
+
const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${escapeXmlText(content)}</text>`
|
|
368
|
+
|
|
369
|
+
const { bbox, element } = addFlipXIfApplicable(entity, {
|
|
370
|
+
bbox: bbox0,
|
|
371
|
+
element: element0,
|
|
372
|
+
})
|
|
373
|
+
return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Minimal <text /> fallback for TOLERANCE entities.
|
|
378
|
+
* DXF uses special control codes; we preserve the raw string.
|
|
379
|
+
*/
|
|
380
|
+
const tolerance = (entity: ToleranceEntity): BoundsAndElement => {
|
|
381
|
+
const x = entity.insertionPoint?.x ?? 0
|
|
382
|
+
const y = entity.insertionPoint?.y ?? 0
|
|
383
|
+
const height = 1
|
|
384
|
+
const content = entity.text ?? ''
|
|
385
|
+
|
|
386
|
+
const rotation = entity.xAxisDirection
|
|
387
|
+
? Math.atan2(entity.xAxisDirection.y, entity.xAxisDirection.x)
|
|
388
|
+
: 0
|
|
389
|
+
const rotationDegrees = (rotation * 180) / Math.PI
|
|
390
|
+
|
|
391
|
+
const textWidth = content.length * height * 0.6
|
|
392
|
+
const bbox0 = new Box2()
|
|
393
|
+
.expandByPoint({ x, y })
|
|
394
|
+
.expandByPoint({ x: x + textWidth, y: y + height })
|
|
343
395
|
|
|
344
|
-
const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`
|
|
396
|
+
const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${escapeXmlText(content)}</text>`
|
|
345
397
|
|
|
346
398
|
const { bbox, element } = addFlipXIfApplicable(entity, {
|
|
347
399
|
bbox: bbox0,
|
|
@@ -356,8 +408,10 @@ const mtext = (entity: MTextEntity): BoundsAndElement => {
|
|
|
356
408
|
const dimension = (
|
|
357
409
|
entity: DimensionEntity,
|
|
358
410
|
dimStyle?: any,
|
|
411
|
+
options?: ToSVGOptions,
|
|
412
|
+
viewport?: { width: number; height: number },
|
|
359
413
|
): BoundsAndElement => {
|
|
360
|
-
const result = dimensionToSVG(entity, dimStyle)
|
|
414
|
+
const result = dimensionToSVG(entity, dimStyle, options, viewport)
|
|
361
415
|
return transformBoundingBoxAndElement(
|
|
362
416
|
result.bbox,
|
|
363
417
|
result.element,
|
|
@@ -410,6 +464,8 @@ const bezier = (entity: SplineEntity): BoundsAndElement => {
|
|
|
410
464
|
const entityToBoundsAndElement = (
|
|
411
465
|
entity: Entity,
|
|
412
466
|
dimStyles?: { [name: string]: any },
|
|
467
|
+
options?: ToSVGOptions,
|
|
468
|
+
viewport?: { width: number; height: number },
|
|
413
469
|
): BoundsAndElement | null => {
|
|
414
470
|
switch (entity.type) {
|
|
415
471
|
case 'CIRCLE':
|
|
@@ -430,7 +486,7 @@ const entityToBoundsAndElement = (
|
|
|
430
486
|
const dimStyle = styleName && dimStyles
|
|
431
487
|
? dimStyles[styleName]
|
|
432
488
|
: undefined
|
|
433
|
-
return dimension(dimEntity, dimStyle)
|
|
489
|
+
return dimension(dimEntity, dimStyle, options, viewport)
|
|
434
490
|
}
|
|
435
491
|
case 'SPLINE': {
|
|
436
492
|
const splineEntity = entity as SplineEntity
|
|
@@ -454,22 +510,57 @@ const entityToBoundsAndElement = (
|
|
|
454
510
|
case 'LWPOLYLINE': {
|
|
455
511
|
return lwpolyline(entity)
|
|
456
512
|
}
|
|
513
|
+
case 'LEADER': {
|
|
514
|
+
return leader(entity as LeaderEntity)
|
|
515
|
+
}
|
|
516
|
+
case 'TOLERANCE': {
|
|
517
|
+
return tolerance(entity as ToleranceEntity)
|
|
518
|
+
}
|
|
457
519
|
default:
|
|
458
520
|
logger.warn('entity type not supported in SVG rendering:', entity.type)
|
|
459
521
|
return null
|
|
460
522
|
}
|
|
461
523
|
}
|
|
462
524
|
|
|
463
|
-
export default function toSVG(parsed: ParsedDXF): string {
|
|
525
|
+
export default function toSVG(parsed: ParsedDXF, options: ToSVGOptions = {}): string {
|
|
464
526
|
const entities = denormalise(parsed)
|
|
465
527
|
const dimStyles = parsed.tables.dimStyles
|
|
528
|
+
|
|
529
|
+
const geometryBBox = entities.reduce((acc: Box2, entity: Entity): Box2 => {
|
|
530
|
+
if (entity.type === 'DIMENSION') {
|
|
531
|
+
const bbox = getDimensionGeometryBBox(entity as DimensionEntity)
|
|
532
|
+
if (bbox.valid) {
|
|
533
|
+
acc.expandByPoint(bbox.min)
|
|
534
|
+
acc.expandByPoint(bbox.max)
|
|
535
|
+
}
|
|
536
|
+
return acc
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options)
|
|
540
|
+
if (boundsAndElement?.bbox.valid) {
|
|
541
|
+
acc.expandByPoint(boundsAndElement.bbox.min)
|
|
542
|
+
acc.expandByPoint(boundsAndElement.bbox.max)
|
|
543
|
+
}
|
|
544
|
+
return acc
|
|
545
|
+
}, new Box2())
|
|
546
|
+
|
|
547
|
+
const viewport = geometryBBox.valid
|
|
548
|
+
? {
|
|
549
|
+
width: geometryBBox.max.x - geometryBBox.min.x,
|
|
550
|
+
height: geometryBBox.max.y - geometryBBox.min.y,
|
|
551
|
+
}
|
|
552
|
+
: {
|
|
553
|
+
width: 0,
|
|
554
|
+
height: 0,
|
|
555
|
+
}
|
|
556
|
+
|
|
466
557
|
const { bbox, elements } = entities.reduce(
|
|
467
558
|
(
|
|
468
559
|
acc: { bbox: Box2; elements: string[] },
|
|
469
560
|
entity: Entity,
|
|
470
561
|
): { bbox: Box2; elements: string[] } => {
|
|
471
562
|
const rgb = getRGBForEntity(parsed.tables.layers, entity)
|
|
472
|
-
const boundsAndElement = entityToBoundsAndElement(entity, dimStyles)
|
|
563
|
+
const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options, viewport)
|
|
473
564
|
// Ignore entities that don't produce SVG elements or have unsupported types
|
|
474
565
|
if (boundsAndElement) {
|
|
475
566
|
const { bbox, element } = boundsAndElement
|
|
@@ -7,6 +7,17 @@ export interface DimensionEntity extends BaseEntity {
|
|
|
7
7
|
type: 'DIMENSION'
|
|
8
8
|
block?: string
|
|
9
9
|
start: Point3D
|
|
10
|
+
/**
|
|
11
|
+
* Angular vertex for DIMENSION type 5 (Angular 3-point).
|
|
12
|
+
* This is parsed from group codes 15/25/35.
|
|
13
|
+
*/
|
|
14
|
+
angleVertex?: Point3D
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Angular arc location point for DIMENSION angular types.
|
|
18
|
+
* This is parsed from group codes 16/26/36 (OCS in the DXF reference).
|
|
19
|
+
*/
|
|
20
|
+
arcPoint?: Point3D
|
|
10
21
|
measureStart: Point3D
|
|
11
22
|
measureEnd: Point3D
|
|
12
23
|
textMidpoint: Point3D
|
package/src/types/entity.ts
CHANGED
|
@@ -6,7 +6,9 @@ import type { CircleEntity } from './circle-entity'
|
|
|
6
6
|
import type { DimensionEntity } from './dimension-entity'
|
|
7
7
|
import type { EllipseEntity } from './ellipse-entity'
|
|
8
8
|
import type { HatchEntity } from './hatch-entity'
|
|
9
|
+
import type { ImageEntity } from './image-entity'
|
|
9
10
|
import type { InsertEntity } from './insert-entity'
|
|
11
|
+
import type { LeaderEntity } from './leader-entity'
|
|
10
12
|
import type { LineEntity } from './line-entity'
|
|
11
13
|
import type { MTextEntity } from './mtext-entity'
|
|
12
14
|
import type { PointEntity } from './point-entity'
|
|
@@ -14,6 +16,8 @@ import type { PolylineEntity } from './polyline-entity'
|
|
|
14
16
|
import type { SolidEntity } from './solid-entity'
|
|
15
17
|
import type { SplineEntity } from './spline-entity'
|
|
16
18
|
import type { TextEntity } from './text-entity'
|
|
19
|
+
import type { ToleranceEntity } from './tolerance-entity'
|
|
20
|
+
import type { DgnUnderlayEntity, DwfUnderlayEntity, PdfUnderlayEntity } from './underlay-entity'
|
|
17
21
|
|
|
18
22
|
export type Entity =
|
|
19
23
|
| LineEntity
|
|
@@ -28,5 +32,11 @@ export type Entity =
|
|
|
28
32
|
| DimensionEntity
|
|
29
33
|
| SolidEntity
|
|
30
34
|
| InsertEntity
|
|
35
|
+
| ImageEntity
|
|
36
|
+
| LeaderEntity
|
|
37
|
+
| ToleranceEntity
|
|
38
|
+
| DwfUnderlayEntity
|
|
39
|
+
| DgnUnderlayEntity
|
|
40
|
+
| PdfUnderlayEntity
|
|
31
41
|
| HatchEntity
|
|
32
42
|
| BaseEntity
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// IMAGE entity type
|
|
2
|
+
|
|
3
|
+
import type { BaseEntity } from './base-entity'
|
|
4
|
+
import type { Point3D } from './common'
|
|
5
|
+
|
|
6
|
+
export interface ImageEntity extends BaseEntity {
|
|
7
|
+
type: 'IMAGE'
|
|
8
|
+
|
|
9
|
+
insertionPoint: Point3D
|
|
10
|
+
uVector: Point3D
|
|
11
|
+
vVector: Point3D
|
|
12
|
+
|
|
13
|
+
/** Image size in pixels. */
|
|
14
|
+
pixelSizeX: number
|
|
15
|
+
pixelSizeY: number
|
|
16
|
+
|
|
17
|
+
/** Hard reference to IMAGEDEF object. */
|
|
18
|
+
imageDefHandle?: string
|
|
19
|
+
|
|
20
|
+
/** Hard reference to IMAGEDEF_REACTOR object. */
|
|
21
|
+
imageDefReactorHandle?: string
|
|
22
|
+
|
|
23
|
+
/** Image display properties bitmask. */
|
|
24
|
+
displayProperties?: number
|
|
25
|
+
|
|
26
|
+
/** Clipping state: 0 = Off; 1 = On. */
|
|
27
|
+
clippingState?: number
|
|
28
|
+
|
|
29
|
+
brightness?: number
|
|
30
|
+
contrast?: number
|
|
31
|
+
fade?: number
|
|
32
|
+
|
|
33
|
+
/** DXF class version. */
|
|
34
|
+
classVersion?: number
|
|
35
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -12,7 +12,9 @@ export * from './circle-entity'
|
|
|
12
12
|
export * from './dimension-entity'
|
|
13
13
|
export * from './ellipse-entity'
|
|
14
14
|
export * from './hatch-entity'
|
|
15
|
+
export * from './image-entity'
|
|
15
16
|
export * from './insert-entity'
|
|
17
|
+
export * from './leader-entity'
|
|
16
18
|
export * from './line-entity'
|
|
17
19
|
export * from './mtext-entity'
|
|
18
20
|
export * from './ole2frame-entity'
|
|
@@ -21,6 +23,8 @@ export * from './polyline-entity'
|
|
|
21
23
|
export * from './solid-entity'
|
|
22
24
|
export * from './spline-entity'
|
|
23
25
|
export * from './text-entity'
|
|
26
|
+
export * from './tolerance-entity'
|
|
27
|
+
export * from './underlay-entity'
|
|
24
28
|
export * from './viewport-entity'
|
|
25
29
|
|
|
26
30
|
// Union type
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// LEADER entity type
|
|
2
|
+
|
|
3
|
+
import type { BaseEntity } from './base-entity'
|
|
4
|
+
import type { Point3D } from './common'
|
|
5
|
+
|
|
6
|
+
export interface LeaderEntity extends BaseEntity {
|
|
7
|
+
type: 'LEADER'
|
|
8
|
+
|
|
9
|
+
/** Leader vertices, in drawing coordinates. */
|
|
10
|
+
vertices: Point3D[]
|
|
11
|
+
|
|
12
|
+
/** Dimension style name. */
|
|
13
|
+
dimensionStyleName?: string
|
|
14
|
+
|
|
15
|
+
/** Arrowhead flag: 0 = Disabled; 1 = Enabled. */
|
|
16
|
+
arrowheadFlag?: number
|
|
17
|
+
|
|
18
|
+
/** Leader path type: 0 = Straight line segments; 1 = Spline. */
|
|
19
|
+
pathType?: number
|
|
20
|
+
|
|
21
|
+
/** Leader creation flag: 0 = text; 1 = tolerance; 2 = insert; 3 = none. */
|
|
22
|
+
creationFlag?: number
|
|
23
|
+
|
|
24
|
+
hooklineDirectionFlag?: number
|
|
25
|
+
hooklineFlag?: number
|
|
26
|
+
|
|
27
|
+
textHeight?: number
|
|
28
|
+
textWidth?: number
|
|
29
|
+
|
|
30
|
+
/** Color to use if leader's DIMCLR D = BYBLOCK. */
|
|
31
|
+
color?: number
|
|
32
|
+
|
|
33
|
+
/** Hard reference to associated annotation entity. */
|
|
34
|
+
annotationHandle?: string
|
|
35
|
+
|
|
36
|
+
normal?: Point3D
|
|
37
|
+
horizontalDirection?: Point3D
|
|
38
|
+
blockOffset?: Point3D
|
|
39
|
+
annotationOffset?: Point3D
|
|
40
|
+
}
|
package/src/types/options.ts
CHANGED
|
@@ -4,9 +4,50 @@ export interface ToPolylinesOptions {
|
|
|
4
4
|
interpolationsPerSplineSegment?: number
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Viewport percentage controls for DIMENSION autoScale.
|
|
9
|
+
*
|
|
10
|
+
* Each value is a percentage (0..100) of the viewport minimum dimension.
|
|
11
|
+
*/
|
|
12
|
+
export interface DimensionAutoScaleViewportPercentages {
|
|
13
|
+
/** Arrowhead marker size (markerWidth/markerHeight) */
|
|
14
|
+
arrowSize?: number
|
|
15
|
+
/** Text height (`font-size`) */
|
|
16
|
+
textHeight?: number
|
|
17
|
+
/** Extension line offset from the measured points */
|
|
18
|
+
extLineOffset?: number
|
|
19
|
+
/** Extension line overshoot beyond the dimension line */
|
|
20
|
+
extLineExtension?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
export interface ToSVGOptions {
|
|
8
24
|
width?: number
|
|
9
25
|
height?: number
|
|
26
|
+
dimension?: {
|
|
27
|
+
/**
|
|
28
|
+
* Automatically scale DIMENSION arrow size, extension endpoints, and
|
|
29
|
+
* text height estimates based on the SVG viewport size.
|
|
30
|
+
*/
|
|
31
|
+
autoScale?: boolean
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Viewport reference size used by DIMENSION autoScale.
|
|
35
|
+
* Scale factor is: min(viewBoxWidth, viewBoxHeight) / autoScaleViewportReference.
|
|
36
|
+
* Default: 40.
|
|
37
|
+
*/
|
|
38
|
+
autoScaleViewportReference?: number
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Per-element viewport-percentage overrides for DIMENSION autoScale.
|
|
42
|
+
*
|
|
43
|
+
* When provided (and `autoScale` is enabled), these values set the final
|
|
44
|
+
* sizes directly as a percentage of the viewport minimum dimension:
|
|
45
|
+
* `size = min(viewBoxWidth, viewBoxHeight) * (percent / 100)`.
|
|
46
|
+
*
|
|
47
|
+
* Percent values are expected in the `0..100` range.
|
|
48
|
+
*/
|
|
49
|
+
autoScaleViewportPercentages?: DimensionAutoScaleViewportPercentages
|
|
50
|
+
}
|
|
10
51
|
}
|
|
11
52
|
|
|
12
53
|
export interface Config {
|
package/src/types/tables.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// DXF Tables parsing types
|
|
2
2
|
|
|
3
3
|
import type { Point2D, Point3D } from './common'
|
|
4
|
+
import type { DXFTuple } from './dxf'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Partial point for parsing (all coordinates optional)
|
|
@@ -408,7 +409,90 @@ export interface DimStyleInternal {
|
|
|
408
409
|
/**
|
|
409
410
|
* DXF Objects section result
|
|
410
411
|
*/
|
|
412
|
+
export interface DictionaryObject {
|
|
413
|
+
type: 'DICTIONARY'
|
|
414
|
+
handle?: string | number
|
|
415
|
+
ownerHandle?: string | number
|
|
416
|
+
entries: Record<string, string>
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface XRecordObject {
|
|
420
|
+
type: 'XRECORD'
|
|
421
|
+
handle?: string | number
|
|
422
|
+
ownerHandle?: string | number
|
|
423
|
+
|
|
424
|
+
/** Raw tuples for downstream consumers (excluding the initial 0/XRECORD tuple). */
|
|
425
|
+
tuples: DXFTuple[]
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export interface ImageDefObject {
|
|
429
|
+
type: 'IMAGEDEF'
|
|
430
|
+
handle?: string | number
|
|
431
|
+
|
|
432
|
+
/** Soft-pointer ID/handle to the ACAD_IMAGE_dict dictionary (when present). */
|
|
433
|
+
ownerHandle?: string | number
|
|
434
|
+
|
|
435
|
+
/** File name of the referenced image. */
|
|
436
|
+
fileName?: string
|
|
437
|
+
|
|
438
|
+
/** Image size in pixels (when available). */
|
|
439
|
+
pixelSizeX?: number
|
|
440
|
+
pixelSizeY?: number
|
|
441
|
+
|
|
442
|
+
/** Raw tuples for downstream consumers (excluding the initial 0/IMAGEDEF tuple). */
|
|
443
|
+
tuples: DXFTuple[]
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export interface ImageDefReactorObject {
|
|
447
|
+
type: 'IMAGEDEF_REACTOR'
|
|
448
|
+
handle?: string | number
|
|
449
|
+
|
|
450
|
+
/** Object ID/handle for the associated IMAGE entity (when present). */
|
|
451
|
+
imageHandle?: string | number
|
|
452
|
+
|
|
453
|
+
/** Raw tuples for downstream consumers (excluding the initial 0/IMAGEDEF_REACTOR tuple). */
|
|
454
|
+
tuples: DXFTuple[]
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export type UnderlayDefinitionObjectType =
|
|
458
|
+
| 'UNDERLAYDEFINITION'
|
|
459
|
+
| 'PDFDEFINITION'
|
|
460
|
+
| 'DWFDEFINITION'
|
|
461
|
+
| 'DGNDEFINITION'
|
|
462
|
+
|
|
463
|
+
export interface UnderlayDefinitionObject {
|
|
464
|
+
type: UnderlayDefinitionObjectType
|
|
465
|
+
handle?: string | number
|
|
466
|
+
|
|
467
|
+
/** Soft-pointer ID/handle to the owning dictionary (when present). */
|
|
468
|
+
ownerHandle?: string | number
|
|
469
|
+
|
|
470
|
+
/** File name or path of the referenced underlay. */
|
|
471
|
+
fileName?: string
|
|
472
|
+
|
|
473
|
+
/** Underlay name within the file (e.g., sheet name). */
|
|
474
|
+
underlayName?: string
|
|
475
|
+
|
|
476
|
+
/** Raw tuples for downstream consumers (excluding the initial 0/<TYPE> tuple). */
|
|
477
|
+
tuples: DXFTuple[]
|
|
478
|
+
}
|
|
479
|
+
|
|
411
480
|
export interface ParsedObjects {
|
|
412
481
|
/** Layout objects */
|
|
413
482
|
layouts: LayoutInternal[]
|
|
483
|
+
|
|
484
|
+
/** DICTIONARY objects keyed by handle */
|
|
485
|
+
dictionaries?: Record<string, DictionaryObject>
|
|
486
|
+
|
|
487
|
+
/** XRECORD objects keyed by handle */
|
|
488
|
+
xRecords?: Record<string, XRecordObject>
|
|
489
|
+
|
|
490
|
+
/** IMAGEDEF objects keyed by handle */
|
|
491
|
+
imageDefs?: Record<string, ImageDefObject>
|
|
492
|
+
|
|
493
|
+
/** IMAGEDEF_REACTOR objects keyed by handle */
|
|
494
|
+
imageDefReactors?: Record<string, ImageDefReactorObject>
|
|
495
|
+
|
|
496
|
+
/** UNDERLAYDEFINITION objects keyed by handle */
|
|
497
|
+
underlayDefinitions?: Record<string, UnderlayDefinitionObject>
|
|
414
498
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// TOLERANCE entity type (feature control frame)
|
|
2
|
+
|
|
3
|
+
import type { BaseEntity } from './base-entity'
|
|
4
|
+
import type { Point3D } from './common'
|
|
5
|
+
|
|
6
|
+
export interface ToleranceEntity extends BaseEntity {
|
|
7
|
+
type: 'TOLERANCE'
|
|
8
|
+
|
|
9
|
+
/** Insertion point (WCS). */
|
|
10
|
+
insertionPoint: Point3D
|
|
11
|
+
|
|
12
|
+
/** Raw tolerance string (may include control codes like %%v). */
|
|
13
|
+
text?: string
|
|
14
|
+
|
|
15
|
+
/** Dimension style name (group code 3). */
|
|
16
|
+
dimensionStyleName?: string
|
|
17
|
+
|
|
18
|
+
/** X-axis direction vector (group codes 11/21/31). */
|
|
19
|
+
xAxisDirection?: Point3D
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// UNDERLAY entity types (DWFUNDERLAY / DGNUNDERLAY / PDFUNDERLAY)
|
|
2
|
+
|
|
3
|
+
import type { BaseEntity } from './base-entity'
|
|
4
|
+
import type { Point3D } from './common'
|
|
5
|
+
|
|
6
|
+
export interface UnderlayReferenceEntityBase extends BaseEntity {
|
|
7
|
+
insertionPoint: Point3D
|
|
8
|
+
scale: Point3D
|
|
9
|
+
rotation?: number
|
|
10
|
+
normal?: Point3D
|
|
11
|
+
|
|
12
|
+
/** Hard reference to UNDERLAYDEFINITION object. */
|
|
13
|
+
underlayDefinitionHandle?: string
|
|
14
|
+
|
|
15
|
+
/** Display properties bitmask. */
|
|
16
|
+
flags?: number
|
|
17
|
+
|
|
18
|
+
/** Contrast in range [0, 100]. */
|
|
19
|
+
contrast?: number
|
|
20
|
+
|
|
21
|
+
/** Fade in range [0, 100]. */
|
|
22
|
+
fade?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DwfUnderlayEntity extends UnderlayReferenceEntityBase {
|
|
26
|
+
type: 'DWFUNDERLAY'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DgnUnderlayEntity extends UnderlayReferenceEntityBase {
|
|
30
|
+
type: 'DGNUNDERLAY'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PdfUnderlayEntity extends UnderlayReferenceEntityBase {
|
|
34
|
+
type: 'PDFUNDERLAY'
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function escapeXmlText(value: string): string {
|
|
2
|
+
// Escape text for use inside XML/SVG text nodes.
|
|
3
|
+
// Keep it small and dependency-free.
|
|
4
|
+
return value
|
|
5
|
+
.split('&').join('&')
|
|
6
|
+
.split('<').join('<')
|
|
7
|
+
.split('>').join('>')
|
|
8
|
+
.split('"').join('"')
|
|
9
|
+
.split("'").join(''')
|
|
10
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const http = require('node:http')
|
|
2
|
+
const { createReadStream, existsSync, statSync } = require('node:fs')
|
|
3
|
+
const { extname, join, normalize, resolve } = require('node:path')
|
|
4
|
+
|
|
5
|
+
const projectRoot = resolve(__dirname, '..')
|
|
6
|
+
const port = Number(process.env.PORT || 4173)
|
|
7
|
+
|
|
8
|
+
const harnessHtmlPath = join(projectRoot, 'test', 'browser', 'harness.html')
|
|
9
|
+
const distBundlePath = join(projectRoot, 'dist', 'dxf.js')
|
|
10
|
+
const fixturesDir = join(projectRoot, 'test', 'resources')
|
|
11
|
+
|
|
12
|
+
function sendText(res, statusCode, text, contentType = 'text/plain; charset=utf-8') {
|
|
13
|
+
res.writeHead(statusCode, { 'Content-Type': contentType })
|
|
14
|
+
res.end(text)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function contentTypeForPath(filePath) {
|
|
18
|
+
const ext = extname(filePath).toLowerCase()
|
|
19
|
+
switch (ext) {
|
|
20
|
+
case '.html':
|
|
21
|
+
return 'text/html; charset=utf-8'
|
|
22
|
+
case '.js':
|
|
23
|
+
return 'text/javascript; charset=utf-8'
|
|
24
|
+
case '.dxf':
|
|
25
|
+
return 'text/plain; charset=utf-8'
|
|
26
|
+
case '.json':
|
|
27
|
+
return 'application/json; charset=utf-8'
|
|
28
|
+
default:
|
|
29
|
+
return 'application/octet-stream'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function safeJoin(baseDir, requestedPath) {
|
|
34
|
+
const cleaned = requestedPath.replace(/^\/+/, '')
|
|
35
|
+
const target = normalize(join(baseDir, cleaned))
|
|
36
|
+
const resolvedBase = resolve(baseDir)
|
|
37
|
+
const resolvedTarget = resolve(target)
|
|
38
|
+
if (!resolvedTarget.startsWith(resolvedBase + '/')) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
return resolvedTarget
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const server = http.createServer((req, res) => {
|
|
45
|
+
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`)
|
|
46
|
+
const pathname = url.pathname
|
|
47
|
+
|
|
48
|
+
if (pathname === '/' || pathname === '/harness') {
|
|
49
|
+
if (!existsSync(harnessHtmlPath)) {
|
|
50
|
+
return sendText(res, 500, 'Missing test/browser/harness.html')
|
|
51
|
+
}
|
|
52
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
|
|
53
|
+
return createReadStream(harnessHtmlPath).pipe(res)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (pathname === '/dist/dxf.js') {
|
|
57
|
+
if (!existsSync(distBundlePath)) {
|
|
58
|
+
return sendText(
|
|
59
|
+
res,
|
|
60
|
+
500,
|
|
61
|
+
'Missing dist/dxf.js. Run `yarn dist` before running browser tests.'
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' })
|
|
65
|
+
return createReadStream(distBundlePath).pipe(res)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (pathname.startsWith('/fixtures/')) {
|
|
69
|
+
const fixtureName = pathname.slice('/fixtures/'.length)
|
|
70
|
+
const fixturePath = safeJoin(fixturesDir, fixtureName)
|
|
71
|
+
if (!fixturePath) {
|
|
72
|
+
return sendText(res, 400, 'Invalid fixture path')
|
|
73
|
+
}
|
|
74
|
+
if (!existsSync(fixturePath) || !statSync(fixturePath).isFile()) {
|
|
75
|
+
return sendText(res, 404, 'Fixture not found')
|
|
76
|
+
}
|
|
77
|
+
res.writeHead(200, { 'Content-Type': contentTypeForPath(fixturePath) })
|
|
78
|
+
return createReadStream(fixturePath).pipe(res)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return sendText(res, 404, 'Not found')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
server.listen(port, () => {
|
|
85
|
+
// Used by Playwright webServer readiness checks.
|
|
86
|
+
console.log(`Browser test server listening on http://localhost:${port}`)
|
|
87
|
+
})
|