@linkiez/dxf-renew 5.3.1 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/{docs/CODE_PATTERNS.md → .github/instructions/code-patterns.instructions.md} +4 -1
  2. package/.github/instructions/exdxf.instruction.md +161 -0
  3. package/.github/instructions/tdd.instructions.md +271 -0
  4. package/.github/workflows/release.yml +4 -5
  5. package/.releaserc.json +1 -1
  6. package/.yarn/install-state.gz +0 -0
  7. package/CHANGELOG.md +59 -0
  8. package/CONTRIBUTING.md +16 -14
  9. package/PLAN.md +34 -84
  10. package/README.md +43 -8
  11. package/dist/dxf.js +1388 -376
  12. package/docs/DIMENSION_SUMMARY.md +11 -5
  13. package/docs/DXF_VERSION_SUPPORT.md +45 -0
  14. package/docs/ENTITY_SVG_ROADMAP.md +96 -0
  15. package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
  16. package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
  17. package/docs/README.md +22 -0
  18. package/docs/SVG_RENDERING_INTEGRATION_TESTS.md +119 -0
  19. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +1 -1
  20. package/lib/Helper.cjs +2 -2
  21. package/lib/Helper.cjs.map +2 -2
  22. package/lib/Helper.js +2 -2
  23. package/lib/Helper.js.map +2 -2
  24. package/lib/denormalise.cjs +131 -91
  25. package/lib/denormalise.cjs.map +2 -2
  26. package/lib/denormalise.js +131 -91
  27. package/lib/denormalise.js.map +2 -2
  28. package/lib/dimensionToSVG.cjs +318 -53
  29. package/lib/dimensionToSVG.cjs.map +3 -3
  30. package/lib/dimensionToSVG.js +316 -52
  31. package/lib/dimensionToSVG.js.map +2 -2
  32. package/lib/handlers/entities.cjs +90 -26
  33. package/lib/handlers/entities.cjs.map +3 -3
  34. package/lib/handlers/entities.js +90 -26
  35. package/lib/handlers/entities.js.map +3 -3
  36. package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
  37. package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
  38. package/lib/handlers/entity/dgnUnderlay.js +71 -0
  39. package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
  40. package/lib/handlers/entity/dimension.cjs +24 -0
  41. package/lib/handlers/entity/dimension.cjs.map +2 -2
  42. package/lib/handlers/entity/dimension.js +24 -0
  43. package/lib/handlers/entity/dimension.js.map +2 -2
  44. package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
  45. package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
  46. package/lib/handlers/entity/dwfUnderlay.js +71 -0
  47. package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
  48. package/lib/handlers/entity/image.cjs +123 -0
  49. package/lib/handlers/entity/image.cjs.map +7 -0
  50. package/lib/handlers/entity/image.js +88 -0
  51. package/lib/handlers/entity/image.js.map +7 -0
  52. package/lib/handlers/entity/leader.cjs +148 -0
  53. package/lib/handlers/entity/leader.cjs.map +7 -0
  54. package/lib/handlers/entity/leader.js +113 -0
  55. package/lib/handlers/entity/leader.js.map +7 -0
  56. package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
  57. package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
  58. package/lib/handlers/entity/pdfUnderlay.js +71 -0
  59. package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
  60. package/lib/handlers/entity/tolerance.cjs +90 -0
  61. package/lib/handlers/entity/tolerance.cjs.map +7 -0
  62. package/lib/handlers/entity/tolerance.js +55 -0
  63. package/lib/handlers/entity/tolerance.js.map +7 -0
  64. package/lib/handlers/objects.cjs +257 -136
  65. package/lib/handlers/objects.cjs.map +2 -2
  66. package/lib/handlers/objects.js +257 -136
  67. package/lib/handlers/objects.js.map +2 -2
  68. package/lib/toSVG.cjs +71 -8
  69. package/lib/toSVG.cjs.map +3 -3
  70. package/lib/toSVG.js +72 -9
  71. package/lib/toSVG.js.map +2 -2
  72. package/lib/types/dimension-entity.cjs.map +1 -1
  73. package/lib/types/entity.cjs.map +1 -1
  74. package/lib/types/image-entity.cjs +17 -0
  75. package/lib/types/image-entity.cjs.map +7 -0
  76. package/lib/types/image-entity.js +1 -0
  77. package/lib/types/image-entity.js.map +7 -0
  78. package/lib/types/index.cjs +8 -0
  79. package/lib/types/index.cjs.map +2 -2
  80. package/lib/types/index.js +4 -0
  81. package/lib/types/index.js.map +2 -2
  82. package/lib/types/leader-entity.cjs +17 -0
  83. package/lib/types/leader-entity.cjs.map +7 -0
  84. package/lib/types/leader-entity.js +1 -0
  85. package/lib/types/leader-entity.js.map +7 -0
  86. package/lib/types/options.cjs.map +1 -1
  87. package/lib/types/tables.cjs.map +1 -1
  88. package/lib/types/tolerance-entity.cjs +17 -0
  89. package/lib/types/tolerance-entity.cjs.map +7 -0
  90. package/lib/types/tolerance-entity.js +1 -0
  91. package/lib/types/tolerance-entity.js.map +7 -0
  92. package/lib/types/underlay-entity.cjs +17 -0
  93. package/lib/types/underlay-entity.cjs.map +7 -0
  94. package/lib/types/underlay-entity.js +1 -0
  95. package/lib/types/underlay-entity.js.map +7 -0
  96. package/lib/util/escapeXmlText.cjs +27 -0
  97. package/lib/util/escapeXmlText.cjs.map +7 -0
  98. package/lib/util/escapeXmlText.js +7 -0
  99. package/lib/util/escapeXmlText.js.map +7 -0
  100. package/package.json +13 -4
  101. package/playwright.config.cjs +20 -0
  102. package/src/Helper.ts +3 -3
  103. package/src/denormalise.ts +182 -116
  104. package/src/dimensionToSVG.ts +466 -54
  105. package/src/handlers/entities.ts +109 -34
  106. package/src/handlers/entity/dgnUnderlay.ts +94 -0
  107. package/src/handlers/entity/dimension.ts +27 -1
  108. package/src/handlers/entity/dwfUnderlay.ts +94 -0
  109. package/src/handlers/entity/image.ts +118 -0
  110. package/src/handlers/entity/leader.ts +153 -0
  111. package/src/handlers/entity/pdfUnderlay.ts +94 -0
  112. package/src/handlers/entity/tolerance.ts +75 -0
  113. package/src/handlers/objects.ts +323 -139
  114. package/src/toSVG.ts +98 -7
  115. package/src/types/dimension-entity.ts +11 -0
  116. package/src/types/entity.ts +10 -0
  117. package/src/types/image-entity.ts +35 -0
  118. package/src/types/index.ts +4 -0
  119. package/src/types/leader-entity.ts +40 -0
  120. package/src/types/options.ts +41 -0
  121. package/src/types/tables.ts +84 -0
  122. package/src/types/tolerance-entity.ts +20 -0
  123. package/src/types/underlay-entity.ts +35 -0
  124. package/src/util/escapeXmlText.ts +10 -0
  125. package/tools/browser_test_server.cjs +87 -0
  126. package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
  127. package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
  128. package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
  129. package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
  130. package/tools/ezdxf_validate_fixtures.py +165 -0
  131. package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
  132. package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
  133. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The `DIMENSION` entity has been fully implemented with complete support for the `DIMSTYLE` system, including parsing, storage, and SVG rendering of all 6 DXF dimension types.
5
+ The `DIMENSION` entity is implemented with support for the `DIMSTYLE` system, including parsing, storage, and SVG rendering.
6
+
7
+ SVG rendering supports DXF dimension types 0/1/2/3/4/5/6.
6
8
 
7
9
  ## Implementation Status
8
10
 
@@ -45,7 +47,7 @@ The `DIMENSION` entity has been fully implemented with complete support for the
45
47
 
46
48
  ### ✅ Phase 3: Advanced Rendering (100%)
47
49
 
48
- All 6 dimension types implemented with precise geometry:
50
+ Dimension types implemented with precise geometry:
49
51
 
50
52
  #### 1. Linear Dimension (types 0 and 1)
51
53
 
@@ -91,6 +93,8 @@ All 6 dimension types implemented with precise geometry:
91
93
  * Text only at textMidpoint position
92
94
  * File: `renderFallbackDimension()`
93
95
 
96
+
97
+
94
98
  ### ✅ Phase 4: Colors and Refinements (100%)
95
99
 
96
100
  #### DIMSTYLE Colors (100%)
@@ -111,19 +115,21 @@ Implementation:
111
115
 
112
116
  #### Optional Features (Not Implemented)
113
117
 
114
- **Custom Arrow Blocks (⏸️ Low Priority)**
118
+ ##### Custom Arrow Blocks (⏸️ Low Priority)
115
119
 
116
120
  * DIMBLK, DIMBLK1, DIMBLK2 parsed and available
117
121
  * Requires complex integration with INSERT renderer
118
122
  * Fallback to standard triangular arrows works in most cases
119
123
 
120
- **Tolerances and Alternate Units (⏸️ Low Priority)**
124
+
125
+ ##### Tolerances and Alternate Units (⏸️ Low Priority)
121
126
 
122
127
  * DIMTOL, DIMALT and related properties parsed
123
128
  * Requires complex multi-line text formatting
124
129
  * Simple text sufficient for most cases
125
130
 
126
- **XDATA Overrides (⏸️ Rare Edge Case)**
131
+
132
+ ##### XDATA Overrides (⏸️ Rare Edge Case)
127
133
 
128
134
  * Per-entity DIMSTYLE property overrides via XDATA
129
135
  * Not implemented
@@ -0,0 +1,45 @@
1
+ # DXF Version Support
2
+
3
+ ## Current Position
4
+
5
+ The parser is designed around DXF group codes and sections, and does not currently gate behavior on `$ACADVER`.
6
+
7
+ In practice, this means:
8
+
9
+ - We aim to **parse and render** supported entities across many DXF versions.
10
+ - We treat `$ACADVER` primarily as a fixture metadata signal and future compatibility hook.
11
+
12
+ ## What “Latest AutoCAD DXF” Means Here
13
+
14
+ AutoCAD introduces newer DXF versions over time (reflected in `$ACADVER`). Supporting “the latest” is best defined as:
15
+
16
+ - The library can load DXF files produced by recent AutoCAD versions **without rejecting due to version**, and
17
+ - The library has fixtures that include a recent `$ACADVER` value, validated by ezdxf.
18
+
19
+ ## Recommended Strategy
20
+
21
+ 1. **Track `$ACADVER` in fixture validation**
22
+ - Use `yarn validate:fixtures` to report `$ACADVER` for every fixture.
23
+
24
+ 2. **Add at least one fixture exported from a recent AutoCAD version**
25
+ - Keep it small and single-purpose.
26
+ - Prefer a fixture that includes DIMENSION + text (to exercise newer text encoding and styles).
27
+
28
+ 3. **Add an integration test for that fixture**
29
+ - The goal is “does not throw + expected key SVG features exist”.
30
+
31
+ 4. **Add a policy (doc + CI)**
32
+ - Document which `$ACADVER` values are present in fixtures.
33
+ - Optionally enforce a minimum accepted `$ACADVER` in fixtures if the project wants to keep up with AutoCAD releases.
34
+
35
+ ## Non-Goals
36
+
37
+ - Guarantee support for every new entity introduced by new DXF versions.
38
+ - Guarantee binary DXF support (unless explicitly added).
39
+
40
+ ## Follow-ups (If Needed)
41
+
42
+ If we need version-specific behavior later, add:
43
+
44
+ - A header helper that exposes `$ACADVER` in a typed way.
45
+ - Tests that cover version-specific parsing/rendering differences.
@@ -0,0 +1,96 @@
1
+ # Entity SVG Roadmap (Fixture → ezdxf → SVG Integration)
2
+
3
+ This roadmap is a **repeatable checklist** for each DXF entity:
4
+
5
+ 1. Confirm/produce a fixture in `test/resources/`
6
+ 2. Validate it with ezdxf
7
+ 3. Add/extend unit parsing tests
8
+ 4. Add final SVG integration tests
9
+
10
+ The goal is incremental coverage: one entity at a time, with fixtures and tests proving end-to-end behavior.
11
+
12
+ ## Template (Use For Every Entity)
13
+
14
+ ### 1) Fixture
15
+
16
+ - Add or pick a DXF fixture in `test/resources/<entity>-<scenario>.dxf`.
17
+ - Keep fixtures minimal: only the needed sections/entities.
18
+
19
+ ### 2) ezdxf validation
20
+
21
+ - Run `yarn validate:fixtures`
22
+ - Confirm:
23
+ - The file loads in ezdxf
24
+ - `$ACADVER` is present and sane
25
+ - Entity type counts include your target entity
26
+
27
+ ### 3) Unit tests
28
+
29
+ - Add unit parsing tests under `test/unit/`.
30
+ - Assert minimum required fields for the entity type.
31
+
32
+ ### 4) SVG integration tests
33
+
34
+ - Add `test/integration-browser/<entity>-rendering.browser.spec.js`.
35
+ - Assert:
36
+ - `<svg ...>` envelope
37
+ - `viewBox="..."`
38
+ - Required SVG elements exist
39
+ - Key attributes exist (markers, transforms, href, etc)
40
+ - Expected text content (when applicable)
41
+
42
+ #### Saved PNG per entity (required)
43
+
44
+ Every browser integration test should save a screenshot to `test/rendered/` for manual review.
45
+
46
+ - Use deterministic naming tied to the fixture: `test/rendered/<fixture-name>.png`
47
+ - Overwrite is expected (re-running tests updates the same file).
48
+
49
+ See: `docs/SVG_RENDERING_INTEGRATION_TESTS.md` (Saved Render Artifacts section)
50
+
51
+ ## DIMENSION (Start Here)
52
+
53
+ ### Fixture(s)
54
+
55
+ - `test/resources/dimension-vertical.dxf`
56
+ - `test/resources/dimensions.dxf` (optional secondary)
57
+
58
+ ### ezdxf validation
59
+
60
+ - Run `yarn validate:fixtures`
61
+ - Confirm `dimension-vertical.dxf` includes `DIMENSION` in modelspace.
62
+
63
+ ### Unit tests
64
+
65
+ Already present:
66
+
67
+ - `test/unit/dimensions.test.js`
68
+
69
+ ### SVG integration test
70
+
71
+ Implemented:
72
+
73
+ - `test/integration-browser/dimension-rendering.browser.spec.js`
74
+
75
+ Assertions:
76
+
77
+ - Marker definitions exist (prefix-based regex)
78
+ - Dimension line references `marker-start` and `marker-end`
79
+ - Dimension text appears in the SVG (`5.35`)
80
+
81
+ ## Next Entities (Suggested Order)
82
+
83
+ This order prioritizes entities already in the codebase and/or those that tend to break rendering pipelines.
84
+
85
+ 1. TEXT
86
+ 2. MTEXT
87
+ 3. LEADER
88
+ 4. TOLERANCE
89
+ 5. IMAGE / IMAGEDEF
90
+ 6. UNDERLAY (DWF/DGN/PDF underlay entities)
91
+ 7. INSERT (block expansion)
92
+ 8. HATCH
93
+ 9. LWPOLYLINE / POLYLINE
94
+ 10. SPLINE
95
+
96
+ For each, create at least one “single entity” fixture and one “mixed scene” fixture.
@@ -0,0 +1,55 @@
1
+ # ezdxf Reference Sitemap (Stable)
2
+
3
+ This project uses the official **ezdxf** documentation as the primary, practical reference when implementing DXF parsing behavior.
4
+
5
+ - Reference home: <https://ezdxf.readthedocs.io/en/stable/reference.html>
6
+
7
+ ## What to use for what
8
+
9
+ - **High-level DXF structure (sections/tables/blocks/entities/objects):**
10
+ - Sections index: <https://ezdxf.readthedocs.io/en/stable/sections/index.html>
11
+ - Tables index: <https://ezdxf.readthedocs.io/en/stable/tables/index.html>
12
+ - Blocks index: <https://ezdxf.readthedocs.io/en/stable/blocks/index.html>
13
+
14
+ - **Entity-specific group codes and attributes (our roadmap A.1):**
15
+ - DXF Entities index: <https://ezdxf.readthedocs.io/en/stable/dxfentities/index.html>
16
+
17
+ Common 2D targets:
18
+ - TEXT: <https://ezdxf.readthedocs.io/en/stable/dxfentities/text.html>
19
+ - MTEXT: <https://ezdxf.readthedocs.io/en/stable/dxfentities/mtext.html>
20
+ - DIMENSION: <https://ezdxf.readthedocs.io/en/stable/dxfentities/dimension.html>
21
+ - LEADER: <https://ezdxf.readthedocs.io/en/stable/dxfentities/leader.html>
22
+ - MLEADER: <https://ezdxf.readthedocs.io/en/stable/dxfentities/mleader.html>
23
+ - IMAGE: <https://ezdxf.readthedocs.io/en/stable/dxfentities/image.html>
24
+ - UNDERLAY: <https://ezdxf.readthedocs.io/en/stable/dxfentities/underlay.html>
25
+ - WIPEOUT: <https://ezdxf.readthedocs.io/en/stable/dxfentities/wipeout.html>
26
+ - RAY: <https://ezdxf.readthedocs.io/en/stable/dxfentities/ray.html>
27
+ - XLINE: <https://ezdxf.readthedocs.io/en/stable/dxfentities/xline.html>
28
+ - TRACE: <https://ezdxf.readthedocs.io/en/stable/dxfentities/trace.html>
29
+ - REGION: <https://ezdxf.readthedocs.io/en/stable/dxfentities/region.html>
30
+ - SHAPE: <https://ezdxf.readthedocs.io/en/stable/dxfentities/shape.html>
31
+ - MLINE: <https://ezdxf.readthedocs.io/en/stable/dxfentities/mline.html>
32
+
33
+ - **Objects (our roadmap A.3):**
34
+ - DXF Objects index: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/index.html>
35
+
36
+ Common reference-based objects:
37
+ - DICTIONARY: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/dictionary.html>
38
+ - XRECORD: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/xrecord.html>
39
+ - ImageDef / ImageDefReactor: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/imagedef.html>
40
+ - UnderlayDefinition: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/underlaydef.html>
41
+ - MLeaderStyle: <https://ezdxf.readthedocs.io/en/stable/dxfobjects/mleaderstyle.html>
42
+
43
+ - **Tables (our roadmap A.2):**
44
+ - Layer: <https://ezdxf.readthedocs.io/en/stable/tables/layer_table_entry.html>
45
+ - DimStyle: <https://ezdxf.readthedocs.io/en/stable/tables/dimstyle_table_entry.html>
46
+ - Style: <https://ezdxf.readthedocs.io/en/stable/tables/style_table_entry.html>
47
+ - Linetype: <https://ezdxf.readthedocs.io/en/stable/tables/linetype_table_entry.html>
48
+ - View: <https://ezdxf.readthedocs.io/en/stable/tables/view_table_entry.html>
49
+ - AppID: <https://ezdxf.readthedocs.io/en/stable/tables/appid_table_entry.html>
50
+ - UCS: <https://ezdxf.readthedocs.io/en/stable/tables/ucs_table_entry.html>
51
+ - BlockRecord: <https://ezdxf.readthedocs.io/en/stable/tables/blockrecord_table_entry.html>
52
+
53
+ ## Notes
54
+
55
+ - ezdxf tracks DXF internals closely, but it is still an implementation. When behavior is ambiguous, treat Autodesk’s DXF reference as the authoritative source.
@@ -0,0 +1,62 @@
1
+ # DXF Fixture Validation (ezdxf)
2
+
3
+ ## Goal
4
+
5
+ Ensure every DXF fixture under `test/resources/` is:
6
+
7
+ - Readable by **ezdxf** (independent parser), and
8
+ - Free of audit fatal errors (and optionally free of audit errors).
9
+
10
+ This provides an external sanity check so our test fixtures stay “real DXF” and not just “files our parser happens to accept”.
11
+
12
+ ## How It Works
13
+
14
+ The project includes a small validator:
15
+
16
+ - `tools/ezdxf_validate_fixtures.py`
17
+
18
+ It:
19
+
20
+ 1. Enumerates `test/resources/*.dxf`
21
+ 2. Loads each file via `ezdxf.readfile(...)`
22
+ 3. Extracts `$ACADVER`
23
+ 4. Counts entity types in modelspace
24
+ 5. Runs `doc.audit()` and reports counts
25
+
26
+ ## Run Locally
27
+
28
+ - `yarn validate:fixtures`
29
+
30
+ Notes:
31
+
32
+ - If `./.venv-ezdxf/bin/python` exists, the script uses it.
33
+ - Otherwise it falls back to `python3`.
34
+
35
+ ## Failure Policy
36
+
37
+ By default (current yarn script):
38
+
39
+ - Fails if any fixture cannot be read by ezdxf
40
+ - Fails if audit reports fatal errors or errors
41
+
42
+ If you need a “report-only” run, execute the script directly:
43
+
44
+ - `./.venv-ezdxf/bin/python tools/ezdxf_validate_fixtures.py`
45
+
46
+ ## Using the Report
47
+
48
+ Use the printed per-file summary to:
49
+
50
+ - Verify a new fixture’s `$ACADVER` is what you expect
51
+ - Confirm an entity appears in the file (entity type counts)
52
+ - Catch malformed DXF output from generators
53
+
54
+ ## Adding/Updating Fixtures
55
+
56
+ When adding a new fixture:
57
+
58
+ 1. Add the DXF file under `test/resources/`.
59
+ 2. Run `yarn validate:fixtures`.
60
+ 3. Add unit tests (parsing) and integration tests (final SVG).
61
+
62
+ See also: `docs/ENTITY_SVG_ROADMAP.md` (fixture → ezdxf → tests checklist)
package/docs/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Documentation
2
+
3
+ This folder contains the project documentation. The goal is to keep docs small, focused, and non-overlapping.
4
+
5
+ ## Where to Start
6
+
7
+ - Entity support overview: `docs/IMPLEMENTED-2D-ENTITIES.md`
8
+ - DXF version support notes: `docs/DXF_VERSION_SUPPORT.md`
9
+ - TEXT / MTEXT / DIMENSION notes: `docs/TEXT-MTEXT-DIMENSION-SUPPORT.md`
10
+ - DIMENSION summary (implementation-specific): `docs/DIMENSION_SUMMARY.md`
11
+
12
+ ## Testing & Fixtures
13
+
14
+ - End-to-end SVG tests (Playwright): `docs/SVG_RENDERING_INTEGRATION_TESTS.md`
15
+ - Fixture validation via ezdxf: `docs/FIXTURE_VALIDATION_EZDXF.md`
16
+ - Entity-by-entity workflow (fixture → ezdxf → tests): `docs/ENTITY_SVG_ROADMAP.md`
17
+
18
+ ## References
19
+
20
+ - ezdxf reference sitemap for this repo: `docs/EZDXF_REFERENCE_SITEMAP.md`
21
+
22
+ Note: `docs/autocad_2012_pdf_dxf-reference_enu.md` is a local reference copy and is not part of the maintained documentation flow.
@@ -0,0 +1,119 @@
1
+ # SVG Rendering Integration Tests
2
+
3
+ ## Goal
4
+
5
+ Add end-to-end tests that validate the **final SVG output** for real DXF fixtures:
6
+
7
+ `DXF fixture → parseString() → (optional denormalise) → toSVG() → SVG assertions`
8
+
9
+ The project already has strong unit tests for geometry and parsing. This document defines a predictable integration-test layer to catch regressions at the “final render” boundary.
10
+
11
+ ## Framework Choice
12
+
13
+ Run the final rendering integration tests in a **real browser** using **Playwright**.
14
+
15
+ Rationale:
16
+
17
+ - Executes the same rendering code path in a browser runtime.
18
+ - Enables future pixel/screenshot tests (if ever needed) without changing frameworks.
19
+ - Still supports stable structural assertions (regex/DOM parsing) for deterministic checks.
20
+
21
+ ## Where Tests Live
22
+
23
+ - Integration tests: `test/integration/**`
24
+ - Browser integration tests: `test/integration-browser/**`
25
+ - DXF fixtures: `test/resources/*.dxf`
26
+
27
+ Browser harness:
28
+
29
+ - `test/browser/harness.html`
30
+ - Served by `tools/browser_test_server.cjs`
31
+
32
+ Scripts:
33
+
34
+ - `yarn test:integration` (browser, via Playwright)
35
+ - `yarn test:integration:node` (legacy Node-only integration tests)
36
+
37
+ ## What Integration Tests Should Assert
38
+
39
+ Integration tests should focus on **stable, user-visible output** and avoid overfitting internal implementation details.
40
+
41
+ Recommended assertions (in order):
42
+
43
+ 1. **SVG well-formed envelope**
44
+ - Contains `<svg` and `</svg>`
45
+ - Includes `viewBox="..."`
46
+
47
+ 2. **Entity-specific rendering markers**
48
+ - Presence of SVG elements expected for the entity type (`<line>`, `<path>`, `<circle>`, `<text>`, `<image>`)
49
+
50
+ 3. **Critical attributes**
51
+ - For example: `marker-start/marker-end` for DIMENSION arrows
52
+ - For example: correct path command type for ARC (`A`) or SPLINE (`C/Q`)
53
+
54
+ 4. **Text content (when applicable)**
55
+ - Assert the final SVG contains the expected text payload
56
+
57
+ 5. **No crashes on full fixture set**
58
+ - A single integration test can iterate a curated list of fixtures and ensure rendering does not throw.
59
+
60
+ ### Saved Render Artifacts (PNG)
61
+
62
+ Browser integration tests should save a screenshot under `test/rendered/` for manual review.
63
+
64
+ - The file can be overwritten on re-run.
65
+ - Prefer a deterministic filename tied to the fixture name, e.g. `test/rendered/dimension-vertical.png`.
66
+
67
+ Note: these PNGs are tracked in git. If you regenerate them, include the updated files in your PR.
68
+
69
+ ### Handling Non-Deterministic IDs
70
+
71
+ Some renderers generate IDs using `Date.now()` (e.g. DIMENSION arrow markers). Integration tests should validate **prefix-based patterns**:
72
+
73
+ - `dim-arrow-start-\d+`
74
+ - `dim-arrow-end-\d+`
75
+
76
+ Avoid asserting the full exact ID.
77
+
78
+ ## Golden Files (Optional)
79
+
80
+ If/when the project wants pixel-perfect or full-SVG snapshots, consider adding a “golden SVG” snapshot layer.
81
+
82
+ Guidelines:
83
+
84
+ - Only snapshot **small, stable fixtures**.
85
+ - Normalize dynamic values (e.g. marker IDs) before snapshotting.
86
+ - Keep snapshots per-fixture under `test/snapshots/`.
87
+
88
+ This is intentionally optional; regex-based structural assertions are the default.
89
+
90
+ ## DIMENSION: First Integration Target
91
+
92
+ Start with fixtures that include real DIMENSION output:
93
+
94
+ - `test/resources/dimension-vertical.dxf`
95
+
96
+ Suggested assertions:
97
+
98
+ - Markers exist (`<marker id="dim-arrow-start-..."`)
99
+ - Dimension line uses `marker-start/marker-end`
100
+ - Text appears in SVG (fixture includes `5.35`)
101
+
102
+ See:
103
+
104
+ - `test/integration-browser/dimension-rendering.browser.spec.js`
105
+
106
+ ## Adding a New Integration Test (Checklist)
107
+
108
+ 1. Pick or create a DXF fixture under `test/resources/`.
109
+ 2. (Recommended) Validate it with `yarn validate:fixtures` (see `docs/FIXTURE_VALIDATION_EZDXF.md`).
110
+ 3. Add a new `test/integration/*.integration.test.js`.
111
+ - Or, for browser execution, add `test/integration-browser/*.browser.spec.js`.
112
+ 4. Assert:
113
+ - SVG envelope + viewBox
114
+ - Entity-specific elements
115
+ - Critical attributes
116
+ - Screenshot is saved to `test/rendered/<fixture-name>.png` (overwrite on re-run)
117
+ 5. Run:
118
+ - `yarn test:unit`
119
+ - `yarn test:integration`
@@ -154,7 +154,7 @@ const helper = new Helper(dxfString)const helper = new Helper(dxfString)
154
154
 
155
155
  const svg = helper.toSVG()const svg = helper.toSVG()
156
156
 
157
- ``````
157
+ ```
158
158
 
159
159
 
160
160
 
package/lib/Helper.cjs CHANGED
@@ -79,8 +79,8 @@ class Helper {
79
79
  }
80
80
  return this._groups;
81
81
  }
82
- toSVG() {
83
- return (0, import_toSVG.default)(this.parsed);
82
+ toSVG(options) {
83
+ return (0, import_toSVG.default)(this.parsed, options);
84
84
  }
85
85
  toPolylines() {
86
86
  return (0, import_toPolylines.default)(this.parsed);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Helper.ts"],
4
- "sourcesContent": ["import denormalise from './denormalise'\nimport groupEntitiesByLayer from './groupEntitiesByLayer'\nimport parseString from './parseString'\nimport toPolylines from './toPolylines'\nimport toSVG from './toSVG'\nimport logger from './util/logger'\n\nimport type { Entity, LayerGroups, ParsedDXF } from './types'\n\nexport default class Helper {\n private readonly _contents: string\n private _parsed: ParsedDXF | null\n private _denormalised: Entity[] | null\n private _groups: LayerGroups | null\n\n constructor(contents: string) {\n if (typeof contents !== 'string') {\n throw new TypeError('Helper constructor expects a DXF string')\n }\n this._contents = contents\n this._parsed = null\n this._denormalised = null\n this._groups = null\n }\n\n parse(): ParsedDXF {\n this._parsed = parseString(this._contents)\n logger.info('parsed:', this.parsed)\n return this._parsed\n }\n\n get parsed(): ParsedDXF {\n if (this._parsed === null) {\n this.parse()\n }\n return this._parsed as ParsedDXF\n }\n\n denormalise(): Entity[] {\n this._denormalised = denormalise(this.parsed)\n logger.info('denormalised:', this._denormalised)\n return this._denormalised\n }\n\n get denormalised(): Entity[] {\n if (!this._denormalised) {\n this.denormalise()\n }\n return this._denormalised as Entity[]\n }\n\n group(): LayerGroups {\n this._groups = groupEntitiesByLayer(this.denormalised)\n return this._groups\n }\n\n get groups(): LayerGroups {\n if (!this._groups) {\n this.group()\n }\n return this._groups as LayerGroups\n }\n\n toSVG(): string {\n return toSVG(this.parsed)\n }\n\n toPolylines(): ReturnType<typeof toPolylines> {\n return toPolylines(this.parsed)\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAwB;AACxB,kCAAiC;AACjC,yBAAwB;AACxB,yBAAwB;AACxB,mBAAkB;AAClB,oBAAmB;AAInB,MAAO,OAAqB;AAAA,EAM1B,YAAY,UAAkB;AAC5B,QAAI,OAAO,aAAa,UAAU;AAChC,YAAM,IAAI,UAAU,yCAAyC;AAAA,IAC/D;AACA,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAmB;AACjB,SAAK,cAAU,mBAAAA,SAAY,KAAK,SAAS;AACzC,kBAAAC,QAAO,KAAK,WAAW,KAAK,MAAM;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAoB;AACtB,QAAI,KAAK,YAAY,MAAM;AACzB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAwB;AACtB,SAAK,oBAAgB,mBAAAC,SAAY,KAAK,MAAM;AAC5C,kBAAAD,QAAO,KAAK,iBAAiB,KAAK,aAAa;AAC/C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,eAAyB;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,YAAY;AAAA,IACnB;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAqB;AACnB,SAAK,cAAU,4BAAAE,SAAqB,KAAK,YAAY;AACrD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAsB;AACxB,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAgB;AACd,eAAO,aAAAC,SAAM,KAAK,MAAM;AAAA,EAC1B;AAAA,EAEA,cAA8C;AAC5C,eAAO,mBAAAC,SAAY,KAAK,MAAM;AAAA,EAChC;AACF;",
4
+ "sourcesContent": ["import denormalise from './denormalise'\nimport groupEntitiesByLayer from './groupEntitiesByLayer'\nimport parseString from './parseString'\nimport toPolylines from './toPolylines'\nimport toSVG from './toSVG'\nimport logger from './util/logger'\n\nimport type { Entity, LayerGroups, ParsedDXF, ToSVGOptions } from './types'\n\nexport default class Helper {\n private readonly _contents: string\n private _parsed: ParsedDXF | null\n private _denormalised: Entity[] | null\n private _groups: LayerGroups | null\n\n constructor(contents: string) {\n if (typeof contents !== 'string') {\n throw new TypeError('Helper constructor expects a DXF string')\n }\n this._contents = contents\n this._parsed = null\n this._denormalised = null\n this._groups = null\n }\n\n parse(): ParsedDXF {\n this._parsed = parseString(this._contents)\n logger.info('parsed:', this.parsed)\n return this._parsed\n }\n\n get parsed(): ParsedDXF {\n if (this._parsed === null) {\n this.parse()\n }\n return this._parsed as ParsedDXF\n }\n\n denormalise(): Entity[] {\n this._denormalised = denormalise(this.parsed)\n logger.info('denormalised:', this._denormalised)\n return this._denormalised\n }\n\n get denormalised(): Entity[] {\n if (!this._denormalised) {\n this.denormalise()\n }\n return this._denormalised as Entity[]\n }\n\n group(): LayerGroups {\n this._groups = groupEntitiesByLayer(this.denormalised)\n return this._groups\n }\n\n get groups(): LayerGroups {\n if (!this._groups) {\n this.group()\n }\n return this._groups as LayerGroups\n }\n\n toSVG(options?: ToSVGOptions): string {\n return toSVG(this.parsed, options)\n }\n\n toPolylines(): ReturnType<typeof toPolylines> {\n return toPolylines(this.parsed)\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAwB;AACxB,kCAAiC;AACjC,yBAAwB;AACxB,yBAAwB;AACxB,mBAAkB;AAClB,oBAAmB;AAInB,MAAO,OAAqB;AAAA,EAM1B,YAAY,UAAkB;AAC5B,QAAI,OAAO,aAAa,UAAU;AAChC,YAAM,IAAI,UAAU,yCAAyC;AAAA,IAC/D;AACA,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAmB;AACjB,SAAK,cAAU,mBAAAA,SAAY,KAAK,SAAS;AACzC,kBAAAC,QAAO,KAAK,WAAW,KAAK,MAAM;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAoB;AACtB,QAAI,KAAK,YAAY,MAAM;AACzB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAwB;AACtB,SAAK,oBAAgB,mBAAAC,SAAY,KAAK,MAAM;AAC5C,kBAAAD,QAAO,KAAK,iBAAiB,KAAK,aAAa;AAC/C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,eAAyB;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,YAAY;AAAA,IACnB;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAqB;AACnB,SAAK,cAAU,4BAAAE,SAAqB,KAAK,YAAY;AACrD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAsB;AACxB,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,SAAgC;AACpC,eAAO,aAAAC,SAAM,KAAK,QAAQ,OAAO;AAAA,EACnC;AAAA,EAEA,cAA8C;AAC5C,eAAO,mBAAAC,SAAY,KAAK,MAAM;AAAA,EAChC;AACF;",
6
6
  "names": ["parseString", "logger", "denormalise", "groupEntitiesByLayer", "toSVG", "toPolylines"]
7
7
  }
package/lib/Helper.js CHANGED
@@ -46,8 +46,8 @@ class Helper {
46
46
  }
47
47
  return this._groups;
48
48
  }
49
- toSVG() {
50
- return toSVG(this.parsed);
49
+ toSVG(options) {
50
+ return toSVG(this.parsed, options);
51
51
  }
52
52
  toPolylines() {
53
53
  return toPolylines(this.parsed);
package/lib/Helper.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Helper.ts"],
4
- "sourcesContent": ["import denormalise from './denormalise'\nimport groupEntitiesByLayer from './groupEntitiesByLayer'\nimport parseString from './parseString'\nimport toPolylines from './toPolylines'\nimport toSVG from './toSVG'\nimport logger from './util/logger'\n\nimport type { Entity, LayerGroups, ParsedDXF } from './types'\n\nexport default class Helper {\n private readonly _contents: string\n private _parsed: ParsedDXF | null\n private _denormalised: Entity[] | null\n private _groups: LayerGroups | null\n\n constructor(contents: string) {\n if (typeof contents !== 'string') {\n throw new TypeError('Helper constructor expects a DXF string')\n }\n this._contents = contents\n this._parsed = null\n this._denormalised = null\n this._groups = null\n }\n\n parse(): ParsedDXF {\n this._parsed = parseString(this._contents)\n logger.info('parsed:', this.parsed)\n return this._parsed\n }\n\n get parsed(): ParsedDXF {\n if (this._parsed === null) {\n this.parse()\n }\n return this._parsed as ParsedDXF\n }\n\n denormalise(): Entity[] {\n this._denormalised = denormalise(this.parsed)\n logger.info('denormalised:', this._denormalised)\n return this._denormalised\n }\n\n get denormalised(): Entity[] {\n if (!this._denormalised) {\n this.denormalise()\n }\n return this._denormalised as Entity[]\n }\n\n group(): LayerGroups {\n this._groups = groupEntitiesByLayer(this.denormalised)\n return this._groups\n }\n\n get groups(): LayerGroups {\n if (!this._groups) {\n this.group()\n }\n return this._groups as LayerGroups\n }\n\n toSVG(): string {\n return toSVG(this.parsed)\n }\n\n toPolylines(): ReturnType<typeof toPolylines> {\n return toPolylines(this.parsed)\n }\n}\n"],
5
- "mappings": "AAAA,OAAO,iBAAiB;AACxB,OAAO,0BAA0B;AACjC,OAAO,iBAAiB;AACxB,OAAO,iBAAiB;AACxB,OAAO,WAAW;AAClB,OAAO,YAAY;AAInB,MAAO,OAAqB;AAAA,EAM1B,YAAY,UAAkB;AAC5B,QAAI,OAAO,aAAa,UAAU;AAChC,YAAM,IAAI,UAAU,yCAAyC;AAAA,IAC/D;AACA,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAmB;AACjB,SAAK,UAAU,YAAY,KAAK,SAAS;AACzC,WAAO,KAAK,WAAW,KAAK,MAAM;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAoB;AACtB,QAAI,KAAK,YAAY,MAAM;AACzB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAwB;AACtB,SAAK,gBAAgB,YAAY,KAAK,MAAM;AAC5C,WAAO,KAAK,iBAAiB,KAAK,aAAa;AAC/C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,eAAyB;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,YAAY;AAAA,IACnB;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAqB;AACnB,SAAK,UAAU,qBAAqB,KAAK,YAAY;AACrD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAsB;AACxB,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAgB;AACd,WAAO,MAAM,KAAK,MAAM;AAAA,EAC1B;AAAA,EAEA,cAA8C;AAC5C,WAAO,YAAY,KAAK,MAAM;AAAA,EAChC;AACF;",
4
+ "sourcesContent": ["import denormalise from './denormalise'\nimport groupEntitiesByLayer from './groupEntitiesByLayer'\nimport parseString from './parseString'\nimport toPolylines from './toPolylines'\nimport toSVG from './toSVG'\nimport logger from './util/logger'\n\nimport type { Entity, LayerGroups, ParsedDXF, ToSVGOptions } from './types'\n\nexport default class Helper {\n private readonly _contents: string\n private _parsed: ParsedDXF | null\n private _denormalised: Entity[] | null\n private _groups: LayerGroups | null\n\n constructor(contents: string) {\n if (typeof contents !== 'string') {\n throw new TypeError('Helper constructor expects a DXF string')\n }\n this._contents = contents\n this._parsed = null\n this._denormalised = null\n this._groups = null\n }\n\n parse(): ParsedDXF {\n this._parsed = parseString(this._contents)\n logger.info('parsed:', this.parsed)\n return this._parsed\n }\n\n get parsed(): ParsedDXF {\n if (this._parsed === null) {\n this.parse()\n }\n return this._parsed as ParsedDXF\n }\n\n denormalise(): Entity[] {\n this._denormalised = denormalise(this.parsed)\n logger.info('denormalised:', this._denormalised)\n return this._denormalised\n }\n\n get denormalised(): Entity[] {\n if (!this._denormalised) {\n this.denormalise()\n }\n return this._denormalised as Entity[]\n }\n\n group(): LayerGroups {\n this._groups = groupEntitiesByLayer(this.denormalised)\n return this._groups\n }\n\n get groups(): LayerGroups {\n if (!this._groups) {\n this.group()\n }\n return this._groups as LayerGroups\n }\n\n toSVG(options?: ToSVGOptions): string {\n return toSVG(this.parsed, options)\n }\n\n toPolylines(): ReturnType<typeof toPolylines> {\n return toPolylines(this.parsed)\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,iBAAiB;AACxB,OAAO,0BAA0B;AACjC,OAAO,iBAAiB;AACxB,OAAO,iBAAiB;AACxB,OAAO,WAAW;AAClB,OAAO,YAAY;AAInB,MAAO,OAAqB;AAAA,EAM1B,YAAY,UAAkB;AAC5B,QAAI,OAAO,aAAa,UAAU;AAChC,YAAM,IAAI,UAAU,yCAAyC;AAAA,IAC/D;AACA,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAmB;AACjB,SAAK,UAAU,YAAY,KAAK,SAAS;AACzC,WAAO,KAAK,WAAW,KAAK,MAAM;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAoB;AACtB,QAAI,KAAK,YAAY,MAAM;AACzB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAwB;AACtB,SAAK,gBAAgB,YAAY,KAAK,MAAM;AAC5C,WAAO,KAAK,iBAAiB,KAAK,aAAa;AAC/C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,eAAyB;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,YAAY;AAAA,IACnB;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAqB;AACnB,SAAK,UAAU,qBAAqB,KAAK,YAAY;AACrD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAsB;AACxB,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,MAAM;AAAA,IACb;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,SAAgC;AACpC,WAAO,MAAM,KAAK,QAAQ,OAAO;AAAA,EACnC;AAAA,EAEA,cAA8C;AAC5C,WAAO,YAAY,KAAK,MAAM;AAAA,EAChC;AACF;",
6
6
  "names": []
7
7
  }