@linkiez/dxf-renew 7.0.0 → 7.2.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 (252) hide show
  1. package/.eslintrc.json +1 -16
  2. package/.github/instructions/code-patterns.instructions.md +1 -1
  3. package/.github/instructions/exdxf.instruction.md +161 -0
  4. package/.github/instructions/tdd.instructions.md +271 -0
  5. package/.yarn/install-state.gz +0 -0
  6. package/ARCHITECTURE.md +163 -0
  7. package/CHANGELOG.md +39 -0
  8. package/CONTRIBUTING.md +16 -14
  9. package/README.md +113 -16
  10. package/{PLAN.md → ROADMAP.md} +244 -102
  11. package/dist/dxf.js +2212 -454
  12. package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
  13. package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
  14. package/lib/Helper.cjs +6 -2
  15. package/lib/Helper.cjs.map +3 -3
  16. package/lib/Helper.js +6 -2
  17. package/lib/Helper.js.map +2 -2
  18. package/lib/denormalise.cjs +131 -91
  19. package/lib/denormalise.cjs.map +2 -2
  20. package/lib/denormalise.js +131 -91
  21. package/lib/denormalise.js.map +2 -2
  22. package/lib/dimensionToSVG.cjs +318 -53
  23. package/lib/dimensionToSVG.cjs.map +3 -3
  24. package/lib/dimensionToSVG.js +316 -52
  25. package/lib/dimensionToSVG.js.map +2 -2
  26. package/lib/entityToPolyline.cjs +95 -0
  27. package/lib/entityToPolyline.cjs.map +3 -3
  28. package/lib/entityToPolyline.js +95 -0
  29. package/lib/entityToPolyline.js.map +2 -2
  30. package/lib/handlers/entities.cjs +111 -27
  31. package/lib/handlers/entities.cjs.map +3 -3
  32. package/lib/handlers/entities.js +111 -27
  33. package/lib/handlers/entities.js.map +3 -3
  34. package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
  35. package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
  36. package/lib/handlers/entity/dgnUnderlay.js +71 -0
  37. package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
  38. package/lib/handlers/entity/dimension.cjs +24 -0
  39. package/lib/handlers/entity/dimension.cjs.map +2 -2
  40. package/lib/handlers/entity/dimension.js +24 -0
  41. package/lib/handlers/entity/dimension.js.map +2 -2
  42. package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
  43. package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
  44. package/lib/handlers/entity/dwfUnderlay.js +71 -0
  45. package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
  46. package/lib/handlers/entity/image.cjs +123 -0
  47. package/lib/handlers/entity/image.cjs.map +7 -0
  48. package/lib/handlers/entity/image.js +88 -0
  49. package/lib/handlers/entity/image.js.map +7 -0
  50. package/lib/handlers/entity/leader.cjs +148 -0
  51. package/lib/handlers/entity/leader.cjs.map +7 -0
  52. package/lib/handlers/entity/leader.js +113 -0
  53. package/lib/handlers/entity/leader.js.map +7 -0
  54. package/lib/handlers/entity/mleader.cjs +69 -0
  55. package/lib/handlers/entity/mleader.cjs.map +7 -0
  56. package/lib/handlers/entity/mleader.js +34 -0
  57. package/lib/handlers/entity/mleader.js.map +7 -0
  58. package/lib/handlers/entity/mline.cjs +91 -0
  59. package/lib/handlers/entity/mline.cjs.map +7 -0
  60. package/lib/handlers/entity/mline.js +56 -0
  61. package/lib/handlers/entity/mline.js.map +7 -0
  62. package/lib/handlers/entity/oleframe.cjs +98 -0
  63. package/lib/handlers/entity/oleframe.cjs.map +7 -0
  64. package/lib/handlers/entity/oleframe.js +63 -0
  65. package/lib/handlers/entity/oleframe.js.map +7 -0
  66. package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
  67. package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
  68. package/lib/handlers/entity/pdfUnderlay.js +71 -0
  69. package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
  70. package/lib/handlers/entity/ray.cjs +81 -0
  71. package/lib/handlers/entity/ray.cjs.map +7 -0
  72. package/lib/handlers/entity/ray.js +46 -0
  73. package/lib/handlers/entity/ray.js.map +7 -0
  74. package/lib/handlers/entity/region.cjs +67 -0
  75. package/lib/handlers/entity/region.cjs.map +7 -0
  76. package/lib/handlers/entity/region.js +32 -0
  77. package/lib/handlers/entity/region.js.map +7 -0
  78. package/lib/handlers/entity/shape.cjs +95 -0
  79. package/lib/handlers/entity/shape.cjs.map +7 -0
  80. package/lib/handlers/entity/shape.js +60 -0
  81. package/lib/handlers/entity/shape.js.map +7 -0
  82. package/lib/handlers/entity/table.cjs +71 -0
  83. package/lib/handlers/entity/table.cjs.map +7 -0
  84. package/lib/handlers/entity/table.js +36 -0
  85. package/lib/handlers/entity/table.js.map +7 -0
  86. package/lib/handlers/entity/tolerance.cjs +90 -0
  87. package/lib/handlers/entity/tolerance.cjs.map +7 -0
  88. package/lib/handlers/entity/tolerance.js +55 -0
  89. package/lib/handlers/entity/tolerance.js.map +7 -0
  90. package/lib/handlers/entity/trace.cjs +101 -0
  91. package/lib/handlers/entity/trace.cjs.map +7 -0
  92. package/lib/handlers/entity/trace.js +66 -0
  93. package/lib/handlers/entity/trace.js.map +7 -0
  94. package/lib/handlers/entity/wipeout.cjs +122 -0
  95. package/lib/handlers/entity/wipeout.cjs.map +7 -0
  96. package/lib/handlers/entity/wipeout.js +87 -0
  97. package/lib/handlers/entity/wipeout.js.map +7 -0
  98. package/lib/handlers/entity/xline.cjs +81 -0
  99. package/lib/handlers/entity/xline.cjs.map +7 -0
  100. package/lib/handlers/entity/xline.js +46 -0
  101. package/lib/handlers/entity/xline.js.map +7 -0
  102. package/lib/handlers/objects.cjs +299 -136
  103. package/lib/handlers/objects.cjs.map +2 -2
  104. package/lib/handlers/objects.js +299 -136
  105. package/lib/handlers/objects.js.map +2 -2
  106. package/lib/handlers/tables.cjs +96 -17
  107. package/lib/handlers/tables.cjs.map +2 -2
  108. package/lib/handlers/tables.js +96 -17
  109. package/lib/handlers/tables.js.map +2 -2
  110. package/lib/index.cjs +5 -2
  111. package/lib/index.cjs.map +3 -3
  112. package/lib/index.js +18 -16
  113. package/lib/index.js.map +3 -3
  114. package/lib/toJson.cjs +29 -0
  115. package/lib/toJson.cjs.map +7 -0
  116. package/lib/toJson.js +9 -0
  117. package/lib/toJson.js.map +7 -0
  118. package/lib/toSVG.cjs +105 -11
  119. package/lib/toSVG.cjs.map +3 -3
  120. package/lib/toSVG.js +106 -12
  121. package/lib/toSVG.js.map +2 -2
  122. package/lib/types/dimension-entity.cjs.map +1 -1
  123. package/lib/types/entity.cjs.map +1 -1
  124. package/lib/types/helper.cjs.map +1 -1
  125. package/lib/types/image-entity.cjs +17 -0
  126. package/lib/types/image-entity.cjs.map +7 -0
  127. package/lib/types/image-entity.js +1 -0
  128. package/lib/types/image-entity.js.map +7 -0
  129. package/lib/types/index.cjs +28 -0
  130. package/lib/types/index.cjs.map +2 -2
  131. package/lib/types/index.js +14 -0
  132. package/lib/types/index.js.map +2 -2
  133. package/lib/types/leader-entity.cjs +17 -0
  134. package/lib/types/leader-entity.cjs.map +7 -0
  135. package/lib/types/leader-entity.js +1 -0
  136. package/lib/types/leader-entity.js.map +7 -0
  137. package/lib/types/mleader-entity.cjs +17 -0
  138. package/lib/types/mleader-entity.cjs.map +7 -0
  139. package/lib/types/mleader-entity.js +1 -0
  140. package/lib/types/mleader-entity.js.map +7 -0
  141. package/lib/types/mline-entity.cjs +17 -0
  142. package/lib/types/mline-entity.cjs.map +7 -0
  143. package/lib/types/mline-entity.js +1 -0
  144. package/lib/types/mline-entity.js.map +7 -0
  145. package/lib/types/oleframe-entity.cjs +17 -0
  146. package/lib/types/oleframe-entity.cjs.map +7 -0
  147. package/lib/types/oleframe-entity.js +1 -0
  148. package/lib/types/oleframe-entity.js.map +7 -0
  149. package/lib/types/options.cjs.map +1 -1
  150. package/lib/types/ray-entity.cjs +17 -0
  151. package/lib/types/ray-entity.cjs.map +7 -0
  152. package/lib/types/ray-entity.js +1 -0
  153. package/lib/types/ray-entity.js.map +7 -0
  154. package/lib/types/region-entity.cjs +17 -0
  155. package/lib/types/region-entity.cjs.map +7 -0
  156. package/lib/types/region-entity.js +1 -0
  157. package/lib/types/region-entity.js.map +7 -0
  158. package/lib/types/shape-entity.cjs +17 -0
  159. package/lib/types/shape-entity.cjs.map +7 -0
  160. package/lib/types/shape-entity.js +1 -0
  161. package/lib/types/shape-entity.js.map +7 -0
  162. package/lib/types/table-entity.cjs +17 -0
  163. package/lib/types/table-entity.cjs.map +7 -0
  164. package/lib/types/table-entity.js +1 -0
  165. package/lib/types/table-entity.js.map +7 -0
  166. package/lib/types/tables.cjs.map +1 -1
  167. package/lib/types/tolerance-entity.cjs +17 -0
  168. package/lib/types/tolerance-entity.cjs.map +7 -0
  169. package/lib/types/tolerance-entity.js +1 -0
  170. package/lib/types/tolerance-entity.js.map +7 -0
  171. package/lib/types/trace-entity.cjs +17 -0
  172. package/lib/types/trace-entity.cjs.map +7 -0
  173. package/lib/types/trace-entity.js +1 -0
  174. package/lib/types/trace-entity.js.map +7 -0
  175. package/lib/types/underlay-entity.cjs +17 -0
  176. package/lib/types/underlay-entity.cjs.map +7 -0
  177. package/lib/types/underlay-entity.js +1 -0
  178. package/lib/types/underlay-entity.js.map +7 -0
  179. package/lib/types/wipeout-entity.cjs +17 -0
  180. package/lib/types/wipeout-entity.cjs.map +7 -0
  181. package/lib/types/wipeout-entity.js +1 -0
  182. package/lib/types/wipeout-entity.js.map +7 -0
  183. package/lib/types/xline-entity.cjs +17 -0
  184. package/lib/types/xline-entity.cjs.map +7 -0
  185. package/lib/types/xline-entity.js +1 -0
  186. package/lib/types/xline-entity.js.map +7 -0
  187. package/lib/util/escapeXmlText.cjs +27 -0
  188. package/lib/util/escapeXmlText.cjs.map +7 -0
  189. package/lib/util/escapeXmlText.js +7 -0
  190. package/lib/util/escapeXmlText.js.map +7 -0
  191. package/package.json +9 -18
  192. package/playwright.config.cjs +20 -0
  193. package/src/Helper.ts +8 -3
  194. package/src/denormalise.ts +182 -116
  195. package/src/dimensionToSVG.ts +466 -54
  196. package/src/entityToPolyline.ts +124 -2
  197. package/src/handlers/entities.ts +129 -34
  198. package/src/handlers/entity/dgnUnderlay.ts +94 -0
  199. package/src/handlers/entity/dimension.ts +27 -1
  200. package/src/handlers/entity/dwfUnderlay.ts +94 -0
  201. package/src/handlers/entity/image.ts +118 -0
  202. package/src/handlers/entity/leader.ts +153 -0
  203. package/src/handlers/entity/mleader.ts +46 -0
  204. package/src/handlers/entity/mline.ts +74 -0
  205. package/src/handlers/entity/oleframe.ts +62 -0
  206. package/src/handlers/entity/pdfUnderlay.ts +94 -0
  207. package/src/handlers/entity/ray.ts +52 -0
  208. package/src/handlers/entity/region.ts +42 -0
  209. package/src/handlers/entity/shape.ts +73 -0
  210. package/src/handlers/entity/table.ts +49 -0
  211. package/src/handlers/entity/tolerance.ts +75 -0
  212. package/src/handlers/entity/trace.ts +72 -0
  213. package/src/handlers/entity/wipeout.ts +114 -0
  214. package/src/handlers/entity/xline.ts +52 -0
  215. package/src/handlers/objects.ts +379 -139
  216. package/src/handlers/tables.ts +134 -21
  217. package/src/index.ts +9 -18
  218. package/src/toJson.ts +8 -0
  219. package/src/toSVG.ts +143 -10
  220. package/src/types/dimension-entity.ts +11 -0
  221. package/src/types/entity.ts +30 -0
  222. package/src/types/helper.ts +2 -1
  223. package/src/types/image-entity.ts +35 -0
  224. package/src/types/index.ts +14 -0
  225. package/src/types/leader-entity.ts +40 -0
  226. package/src/types/mleader-entity.ts +8 -0
  227. package/src/types/mline-entity.ts +12 -0
  228. package/src/types/oleframe-entity.ts +40 -0
  229. package/src/types/options.ts +48 -0
  230. package/src/types/ray-entity.ts +12 -0
  231. package/src/types/region-entity.ts +11 -0
  232. package/src/types/shape-entity.ts +19 -0
  233. package/src/types/table-entity.ts +14 -0
  234. package/src/types/tables.ts +160 -0
  235. package/src/types/tolerance-entity.ts +20 -0
  236. package/src/types/trace-entity.ts +14 -0
  237. package/src/types/underlay-entity.ts +35 -0
  238. package/src/types/wipeout-entity.ts +20 -0
  239. package/src/types/xline-entity.ts +12 -0
  240. package/src/util/escapeXmlText.ts +10 -0
  241. package/tools/browser_test_server.cjs +87 -0
  242. package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
  243. package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
  244. package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
  245. package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
  246. package/tools/ezdxf_validate_fixtures.py +165 -0
  247. package/docs/DIMENSION_SUMMARY.md +0 -248
  248. package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
  249. package/docs/IMPLEMENTED-2D-ENTITIES.md +0 -54
  250. package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
  251. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +0 -241
  252. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
package/lib/toSVG.cjs CHANGED
@@ -37,6 +37,7 @@ var import_denormalise = __toESM(require("./denormalise"), 1);
37
37
  var import_dimensionToSVG = __toESM(require("./dimensionToSVG"), 1);
38
38
  var import_entityToPolyline = __toESM(require("./entityToPolyline"), 1);
39
39
  var import_getRGBForEntity = __toESM(require("./getRGBForEntity"), 1);
40
+ var import_escapeXmlText = __toESM(require("./util/escapeXmlText"), 1);
40
41
  var import_logger = __toESM(require("./util/logger"), 1);
41
42
  var import_rgbToColorAttribute = __toESM(require("./util/rgbToColorAttribute"), 1);
42
43
  var import_rotate = __toESM(require("./util/rotate"), 1);
@@ -93,6 +94,23 @@ const lwpolyline = (entity) => {
93
94
  entity.transforms ?? []
94
95
  );
95
96
  };
97
+ const leader = (entity) => {
98
+ if (!entity.vertices || entity.vertices.length < 2) return null;
99
+ const bbox0 = entity.vertices.reduce(
100
+ (acc, p) => acc.expandByPoint({ x: p.x, y: p.y }),
101
+ new import_vecks.Box2()
102
+ );
103
+ const d = entity.vertices.reduce((acc, p, i) => {
104
+ acc += i === 0 ? "M" : "L";
105
+ acc += p.x + "," + p.y;
106
+ return acc;
107
+ }, "");
108
+ return (0, import_transformBoundingBoxAndElement.default)(
109
+ bbox0,
110
+ `<path d="${d}" />`,
111
+ entity.transforms ?? []
112
+ );
113
+ };
96
114
  const circle = (entity) => {
97
115
  const bbox0 = new import_vecks.Box2().expandByPoint({
98
116
  x: entity.x + entity.r,
@@ -232,7 +250,7 @@ const text = (entity) => {
232
250
  const textWidth = content.length * height * 0.6;
233
251
  const bbox0 = new import_vecks.Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
234
252
  const rotationDegrees = rotation * 180 / Math.PI;
235
- const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`;
253
+ const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${(0, import_escapeXmlText.default)(content)}</text>`;
236
254
  const { bbox, element } = addFlipXIfApplicable(entity, {
237
255
  bbox: bbox0,
238
256
  element: element0
@@ -248,15 +266,47 @@ const mtext = (entity) => {
248
266
  const bbox0 = new import_vecks.Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
249
267
  const rotation = entity.xAxisX !== void 0 && entity.xAxisY !== void 0 ? Math.atan2(entity.xAxisY, entity.xAxisX) : 0;
250
268
  const rotationDegrees = rotation * 180 / Math.PI;
251
- const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`;
269
+ const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${(0, import_escapeXmlText.default)(content)}</text>`;
270
+ const { bbox, element } = addFlipXIfApplicable(entity, {
271
+ bbox: bbox0,
272
+ element: element0
273
+ });
274
+ return (0, import_transformBoundingBoxAndElement.default)(bbox, element, entity.transforms ?? []);
275
+ };
276
+ const tolerance = (entity) => {
277
+ const x = entity.insertionPoint?.x ?? 0;
278
+ const y = entity.insertionPoint?.y ?? 0;
279
+ const height = 1;
280
+ const content = entity.text ?? "";
281
+ const rotation = entity.xAxisDirection ? Math.atan2(entity.xAxisDirection.y, entity.xAxisDirection.x) : 0;
282
+ const rotationDegrees = rotation * 180 / Math.PI;
283
+ const textWidth = content.length * height * 0.6;
284
+ const bbox0 = new import_vecks.Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
285
+ const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${(0, import_escapeXmlText.default)(content)}</text>`;
252
286
  const { bbox, element } = addFlipXIfApplicable(entity, {
253
287
  bbox: bbox0,
254
288
  element: element0
255
289
  });
256
290
  return (0, import_transformBoundingBoxAndElement.default)(bbox, element, entity.transforms ?? []);
257
291
  };
258
- const dimension = (entity, dimStyle) => {
259
- const result = (0, import_dimensionToSVG.default)(entity, dimStyle);
292
+ const shape = (entity) => {
293
+ const x = entity.insertionPoint?.x ?? 0;
294
+ const y = entity.insertionPoint?.y ?? 0;
295
+ const height = entity.size ?? 1;
296
+ const rotation = entity.rotation ?? 0;
297
+ const content = entity.name ?? "";
298
+ const textWidth = content.length * height * 0.6;
299
+ const bbox0 = new import_vecks.Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
300
+ const rotationDegrees = rotation * 180 / Math.PI;
301
+ const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${(0, import_escapeXmlText.default)(content)}</text>`;
302
+ const { bbox, element } = addFlipXIfApplicable(entity, {
303
+ bbox: bbox0,
304
+ element: element0
305
+ });
306
+ return (0, import_transformBoundingBoxAndElement.default)(bbox, element, entity.transforms ?? []);
307
+ };
308
+ const dimension = (entity, dimStyle, options, viewport) => {
309
+ const result = (0, import_dimensionToSVG.default)(entity, dimStyle, options, viewport);
260
310
  return (0, import_transformBoundingBoxAndElement.default)(
261
311
  result.bbox,
262
312
  result.element,
@@ -295,7 +345,7 @@ const bezier = (entity) => {
295
345
  const element = `<g>${paths.join("")}</g>`;
296
346
  return (0, import_transformBoundingBoxAndElement.default)(bbox, element, entity.transforms ?? []);
297
347
  };
298
- const entityToBoundsAndElement = (entity, dimStyles) => {
348
+ const entityToBoundsAndElement = (entity, dimStyles, options, viewport) => {
299
349
  switch (entity.type) {
300
350
  case "CIRCLE":
301
351
  return circle(entity);
@@ -311,7 +361,7 @@ const entityToBoundsAndElement = (entity, dimStyles) => {
311
361
  const dimEntity = entity;
312
362
  const styleName = typeof dimEntity.styleName === "string" ? dimEntity.styleName : void 0;
313
363
  const dimStyle = styleName && dimStyles ? dimStyles[styleName] : void 0;
314
- return dimension(dimEntity, dimStyle);
364
+ return dimension(dimEntity, dimStyle, options, viewport);
315
365
  }
316
366
  case "SPLINE": {
317
367
  const splineEntity = entity;
@@ -329,33 +379,77 @@ const entityToBoundsAndElement = (entity, dimStyles) => {
329
379
  }
330
380
  }
331
381
  case "LINE":
382
+ case "RAY":
383
+ case "XLINE":
332
384
  case "POLYLINE": {
333
385
  return polyline(entity);
334
386
  }
387
+ case "SOLID":
388
+ case "TRACE": {
389
+ return polyline(entity);
390
+ }
335
391
  case "LWPOLYLINE": {
336
392
  return lwpolyline(entity);
337
393
  }
394
+ case "WIPEOUT": {
395
+ return polyline(entity);
396
+ }
397
+ case "LEADER": {
398
+ return leader(entity);
399
+ }
400
+ case "TOLERANCE": {
401
+ return tolerance(entity);
402
+ }
403
+ case "SHAPE": {
404
+ return shape(entity);
405
+ }
338
406
  default:
339
407
  import_logger.default.warn("entity type not supported in SVG rendering:", entity.type);
340
408
  return null;
341
409
  }
342
410
  };
343
- function toSVG(parsed) {
411
+ function toSVG(parsed, options = {}) {
344
412
  const entities = (0, import_denormalise.default)(parsed);
345
413
  const dimStyles = parsed.tables.dimStyles;
414
+ const geometryBBox = entities.reduce((acc, entity) => {
415
+ if (entity.type === "DIMENSION") {
416
+ const bbox2 = (0, import_dimensionToSVG.getDimensionGeometryBBox)(entity);
417
+ if (bbox2.valid) {
418
+ acc.expandByPoint(bbox2.min);
419
+ acc.expandByPoint(bbox2.max);
420
+ }
421
+ return acc;
422
+ }
423
+ const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options);
424
+ if (boundsAndElement?.bbox.valid) {
425
+ acc.expandByPoint(boundsAndElement.bbox.min);
426
+ acc.expandByPoint(boundsAndElement.bbox.max);
427
+ }
428
+ return acc;
429
+ }, new import_vecks.Box2());
430
+ const viewport = geometryBBox.valid ? {
431
+ width: geometryBBox.max.x - geometryBBox.min.x,
432
+ height: geometryBBox.max.y - geometryBBox.min.y
433
+ } : {
434
+ width: 0,
435
+ height: 0
436
+ };
346
437
  const { bbox, elements } = entities.reduce(
347
438
  (acc, entity) => {
348
439
  const rgb = (0, import_getRGBForEntity.default)(parsed.tables.layers, entity);
349
- const boundsAndElement = entityToBoundsAndElement(entity, dimStyles);
440
+ const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options, viewport);
350
441
  if (boundsAndElement) {
351
442
  const { bbox: bbox2, element } = boundsAndElement;
352
443
  if (bbox2.valid) {
353
444
  acc.bbox.expandByPoint(bbox2.min);
354
445
  acc.bbox.expandByPoint(bbox2.max);
355
446
  }
356
- acc.elements.push(
357
- `<g stroke="${(0, import_rgbToColorAttribute.default)(rgb)}">${element}</g>`
358
- );
447
+ const color = (0, import_rgbToColorAttribute.default)(rgb);
448
+ if (entity.type === "SOLID" || entity.type === "TRACE") {
449
+ acc.elements.push(`<g fill="${color}" stroke="none">${element}</g>`);
450
+ } else {
451
+ acc.elements.push(`<g stroke="${color}">${element}</g>`);
452
+ }
359
453
  }
360
454
  return acc;
361
455
  },
package/lib/toSVG.cjs.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/toSVG.ts"],
4
- "sourcesContent": ["import { Box2 } from 'vecks'\n\nimport denormalise from './denormalise'\nimport dimensionToSVG from './dimensionToSVG'\nimport entityToPolyline from './entityToPolyline'\nimport getRGBForEntity from './getRGBForEntity'\nimport logger from './util/logger'\nimport rgbToColorAttribute from './util/rgbToColorAttribute'\nimport rotate from './util/rotate'\nimport toPiecewiseBezier, { multiplicity } from './util/toPiecewiseBezier'\nimport transformBoundingBoxAndElement from './util/transformBoundingBoxAndElement'\n\nimport type {\n ArcEntity,\n CircleEntity,\n DimensionEntity,\n EllipseEntity,\n Entity,\n MTextEntity,\n ParsedDXF,\n SplineEntity,\n TextEntity,\n} from './types'\nimport type { BoundsAndElement } from './types/svg'\n\nconst addFlipXIfApplicable = (\n entity: Entity,\n { bbox, element }: BoundsAndElement,\n): BoundsAndElement => {\n if (entity.extrusionZ === -1) {\n return {\n bbox: new Box2()\n .expandByPoint({ x: -bbox.min.x, y: bbox.min.y })\n .expandByPoint({ x: -bbox.max.x, y: bbox.max.y }),\n element: `<g transform=\"matrix(-1 0 0 1 0 0)\">\n ${element}\n </g>`,\n }\n } else {\n return { bbox, element }\n }\n}\n\n/**\n * Create a <path /> element. Interpolates curved entities.\n */\nconst polyline = (entity: Entity): BoundsAndElement => {\n const vertices = entityToPolyline(entity as any)\n const bbox = vertices.reduce(\n (acc, [x, y]) => acc.expandByPoint({ x, y }),\n new Box2(),\n )\n const d = vertices.reduce((acc, point, i) => {\n acc += i === 0 ? 'M' : 'L'\n acc += point[0] + ',' + point[1]\n return acc\n }, '')\n // Empirically it appears that flipping horizontally does not apply to polyline\n return transformBoundingBoxAndElement(\n bbox,\n `<path d=\"${d}\" />`,\n entity.transforms ?? [],\n )\n}\n\n/**\n * Create a <path /> element. Interpolates curved entities.\n * lwpolyline is the same as polyline but addFlipXIfApplicable does apply\n */\nconst lwpolyline = (entity: Entity): BoundsAndElement => {\n const vertices = entityToPolyline(entity as any)\n const bbox0 = vertices.reduce(\n (acc, [x, y]) => acc.expandByPoint({ x, y }),\n new Box2(),\n )\n const d = vertices.reduce((acc, point, i) => {\n acc += i === 0 ? 'M' : 'L'\n acc += point[0] + ',' + point[1]\n return acc\n }, '')\n const element0 = `<path d=\"${d}\" />`\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(\n bbox,\n element,\n entity.transforms ?? [],\n )\n}\n\n\n/**\n * Create a <circle /> element for the CIRCLE entity.\n */\nconst circle = (entity: CircleEntity): BoundsAndElement => {\n const bbox0 = new Box2()\n .expandByPoint({\n x: entity.x + entity.r,\n y: entity.y + entity.r,\n })\n .expandByPoint({\n x: entity.x - entity.r,\n y: entity.y - entity.r,\n })\n const element0 = `<circle cx=\"${entity.x}\" cy=\"${entity.y}\" r=\"${entity.r}\" />`\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\ninterface EllipticArcParams {\n cx: number\n cy: number\n majorX: number\n majorY: number\n axisRatio: number\n startAngle: number\n endAngle: number\n flipX?: boolean\n}\n\n/**\n * Create a a <path d=\"A...\" /> or <ellipse /> element for the ARC or ELLIPSE\n * DXF entity (<ellipse /> if start and end point are the same).\n */\nconst ellipseOrArc = (params: EllipticArcParams): BoundsAndElement => {\n const { cx, cy, majorX, majorY, axisRatio, startAngle, endAngle } = params\n const rx = Math.hypot(majorX, majorY)\n const ry = axisRatio * rx\n const rotationAngle = -Math.atan2(-majorY, majorX)\n\n const bbox = bboxEllipseOrArc(params)\n\n if (\n Math.abs(startAngle - endAngle) < 1e-9 ||\n Math.abs(startAngle - endAngle + Math.PI * 2) < 1e-9\n ) {\n // Use a native <ellipse> when start and end angles are the same, and\n // arc paths with same start and end points don't render (at least on Safari)\n const element = `<g transform=\"rotate(${\n (rotationAngle / Math.PI) * 180\n } ${cx}, ${cy})\">\n <ellipse cx=\"${cx}\" cy=\"${cy}\" rx=\"${rx}\" ry=\"${ry}\" />\n </g>`\n return { bbox, element }\n } else {\n const startOffset = rotate(\n {\n x: Math.cos(startAngle) * rx,\n y: Math.sin(startAngle) * ry,\n },\n rotationAngle,\n )\n const startPoint = {\n x: cx + startOffset.x,\n y: cy + startOffset.y,\n }\n const endOffset = rotate(\n {\n x: Math.cos(endAngle) * rx,\n y: Math.sin(endAngle) * ry,\n },\n rotationAngle,\n )\n const endPoint = {\n x: cx + endOffset.x,\n y: cy + endOffset.y,\n }\n const adjustedEndAngle =\n endAngle < startAngle ? endAngle + Math.PI * 2 : endAngle\n const largeArcFlag = adjustedEndAngle - startAngle < Math.PI ? 0 : 1\n const d = `M ${startPoint.x} ${startPoint.y} A ${rx} ${ry} ${\n (rotationAngle / Math.PI) * 180\n } ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y}`\n const element = `<path d=\"${d}\" />`\n return { bbox, element }\n }\n}\n\n/**\n * Compute the bounding box of an elliptical arc, given the DXF entity parameters\n */\n\nconst bboxEllipseOrArc = (params: EllipticArcParams): Box2 => {\n const { cx, cy, majorX, majorY, axisRatio } = params\n let { startAngle, endAngle } = params\n\n // The bounding box will be defined by the starting point of the ellipse, and ending point,\n // and any extrema on the ellipse that are between startAngle and endAngle.\n // The extrema are found by setting either the x or y component of the ellipse's\n // tangent vector to zero and solving for the angle.\n\n // Ensure start and end angles are > 0 and well-ordered\n while (startAngle < 0) startAngle += Math.PI * 2\n while (endAngle <= startAngle) endAngle += Math.PI * 2\n\n // When rotated, the extrema of the ellipse will be found at these angles\n const angles = []\n\n if (Math.abs(majorX) < 1e-12 || Math.abs(majorY) < 1e-12) {\n // Special case for majorX or majorY = 0\n for (let i = 0; i < 4; i++) {\n angles.push((i / 2) * Math.PI)\n }\n } else {\n // reference https://github.com/bjnortier/dxf/issues/47#issuecomment-545915042\n angles[0] = Math.atan((-majorY * axisRatio) / majorX) - Math.PI // Ensure angles < 0\n angles[1] = Math.atan((majorX * axisRatio) / majorY) - Math.PI\n angles[2] = angles[0] - Math.PI\n angles[3] = angles[1] - Math.PI\n }\n\n // Remove angles not falling between start and end\n for (let i = 4; i >= 0; i--) {\n while (angles[i] < startAngle) angles[i] += Math.PI * 2\n if (angles[i] > endAngle) {\n angles.splice(i, 1)\n }\n }\n\n // Also to consider are the starting and ending points:\n angles.push(startAngle, endAngle)\n\n // Compute points lying on the unit circle at these angles\n const pts = angles.map((a) => ({\n x: Math.cos(a),\n y: Math.sin(a),\n }))\n\n // Transformation matrix, formed by the major and minor axes\n const M = [\n [majorX, -majorY * axisRatio],\n [majorY, majorX * axisRatio],\n ]\n\n // Rotate, scale, and translate points\n const rotatedPts = pts.map((p) => ({\n x: p.x * M[0][0] + p.y * M[0][1] + cx,\n y: p.x * M[1][0] + p.y * M[1][1] + cy,\n }))\n\n // Compute extents of bounding box\n const bbox = rotatedPts.reduce((acc, p) => {\n acc.expandByPoint(p)\n return acc\n }, new Box2())\n\n return bbox\n}\n\n/**\n * An ELLIPSE is defined by the major axis, convert to X and Y radius with\n * a rotation angle\n */\nconst ellipse = (entity: EllipseEntity): BoundsAndElement => {\n const { bbox: bbox0, element: element0 } = ellipseOrArc({\n cx: entity.x,\n cy: entity.y,\n majorX: entity.majorX,\n majorY: entity.majorY,\n axisRatio: entity.axisRatio,\n startAngle: entity.startAngle,\n endAngle: entity.endAngle,\n })\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * An ARC is an ellipse with equal radii\n */\nconst arc = (entity: ArcEntity): BoundsAndElement => {\n const { bbox: bbox0, element: element0 } = ellipseOrArc({\n cx: entity.x,\n cy: entity.y,\n majorX: entity.r,\n majorY: 0,\n axisRatio: 1,\n startAngle: entity.startAngle,\n endAngle: entity.endAngle,\n flipX: entity.extrusionZ === -1,\n })\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create a <text /> element for TEXT entity\n */\nconst text = (entity: TextEntity): BoundsAndElement => {\n const x = entity.x ?? 0\n const y = entity.y ?? 0\n const height = entity.textHeight ?? 1\n const rotation = entity.rotation ?? 0\n const content = entity.string ?? ''\n\n // Estimate text bounding box (approximate)\n const textWidth = content.length * height * 0.6\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n const rotationDegrees = (rotation * 180) / Math.PI\n const element0 = `<text x=\"${x}\" y=\"${y}\" font-size=\"${height}\" transform=\"rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})\">${content}</text>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create a <text /> element for MTEXT entity\n */\nconst mtext = (entity: MTextEntity): BoundsAndElement => {\n const x = entity.x ?? 0\n const y = entity.y ?? 0\n const height = entity.nominalTextHeight ?? entity.textHeight ?? 1\n const content = entity.string ?? ''\n\n // Estimate text bounding box (approximate)\n const textWidth = (entity.refRectangleWidth ?? content.length * height * 0.6)\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n // Calculate rotation from x-axis direction\n const rotation = entity.xAxisX !== undefined && entity.xAxisY !== undefined\n ? Math.atan2(entity.xAxisY, entity.xAxisX)\n : 0\n const rotationDegrees = (rotation * 180) / Math.PI\n\n const element0 = `<text x=\"${x}\" y=\"${y}\" font-size=\"${height}\" transform=\"rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})\">${content}</text>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create dimension visualization with DIMSTYLE support\n */\nconst dimension = (\n entity: DimensionEntity,\n dimStyle?: any,\n): BoundsAndElement => {\n const result = dimensionToSVG(entity, dimStyle)\n return transformBoundingBoxAndElement(\n result.bbox,\n result.element,\n entity.transforms ?? [],\n )\n}\n\nexport const piecewiseToPaths = (\n k: number,\n knots: number[],\n controlPoints: Array<{ x: number; y: number }>,\n): string[] => {\n const paths: string[] = []\n let controlPointIndex = 0\n let knotIndex = k\n while (knotIndex < knots.length - k + 1) {\n const m = multiplicity(knots, knotIndex)\n const cp = controlPoints.slice(controlPointIndex, controlPointIndex + k)\n if (k === 4) {\n paths.push(\n `<path d=\"M ${cp[0].x} ${cp[0].y} C ${cp[1].x} ${cp[1].y} ${cp[2].x} ${cp[2].y} ${cp[3].x} ${cp[3].y}\" />`,\n )\n } else if (k === 3) {\n paths.push(\n `<path d=\"M ${cp[0].x} ${cp[0].y} Q ${cp[1].x} ${cp[1].y} ${cp[2].x} ${cp[2].y}\" />`,\n )\n }\n controlPointIndex += m\n knotIndex += m\n }\n return paths\n}\n\nconst bezier = (entity: SplineEntity): BoundsAndElement => {\n let bbox = new Box2()\n for (const p of entity.controlPoints) {\n bbox = bbox.expandByPoint(p)\n }\n const k = entity.degree + 1\n const piecewise = toPiecewiseBezier(k, entity.controlPoints, entity.knots)\n const paths = piecewiseToPaths(k, piecewise.knots, piecewise.controlPoints)\n const element = `<g>${paths.join('')}</g>`\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Switch the appropriate function on entity type. CIRCLE, ARC and ELLIPSE\n * produce native SVG elements, the rest produce interpolated polylines.\n */\nconst entityToBoundsAndElement = (\n entity: Entity,\n dimStyles?: { [name: string]: any },\n): BoundsAndElement | null => {\n switch (entity.type) {\n case 'CIRCLE':\n return circle(entity as CircleEntity)\n case 'ELLIPSE':\n return ellipse(entity as EllipseEntity)\n case 'ARC':\n return arc(entity as ArcEntity)\n case 'TEXT':\n return text(entity as TextEntity)\n case 'MTEXT':\n return mtext(entity as MTextEntity)\n case 'DIMENSION': {\n const dimEntity = entity as DimensionEntity\n const styleName = typeof dimEntity.styleName === 'string'\n ? dimEntity.styleName\n : undefined\n const dimStyle = styleName && dimStyles\n ? dimStyles[styleName]\n : undefined\n return dimension(dimEntity, dimStyle)\n }\n case 'SPLINE': {\n const splineEntity = entity as SplineEntity\n const hasWeights = splineEntity.weights?.some((w: number) => w !== 1)\n if ((splineEntity.degree === 2 || splineEntity.degree === 3) && !hasWeights) {\n try {\n return bezier(splineEntity)\n } catch (err) {\n const error = err as Error\n logger.warn('bezier conversion failed, using polyline:', error.message)\n return polyline(entity)\n }\n } else {\n return polyline(entity)\n }\n }\n case 'LINE':\n case 'POLYLINE': {\n return polyline(entity)\n }\n case 'LWPOLYLINE': {\n return lwpolyline(entity)\n }\n default:\n logger.warn('entity type not supported in SVG rendering:', entity.type)\n return null\n }\n}\n\nexport default function toSVG(parsed: ParsedDXF): string {\n const entities = denormalise(parsed)\n const dimStyles = parsed.tables.dimStyles\n const { bbox, elements } = entities.reduce(\n (\n acc: { bbox: Box2; elements: string[] },\n entity: Entity,\n ): { bbox: Box2; elements: string[] } => {\n const rgb = getRGBForEntity(parsed.tables.layers, entity)\n const boundsAndElement = entityToBoundsAndElement(entity, dimStyles)\n // Ignore entities that don't produce SVG elements or have unsupported types\n if (boundsAndElement) {\n const { bbox, element } = boundsAndElement\n // Ignore invalid bounding boxes\n if (bbox.valid) {\n acc.bbox.expandByPoint(bbox.min)\n acc.bbox.expandByPoint(bbox.max)\n }\n acc.elements.push(\n `<g stroke=\"${rgbToColorAttribute(rgb)}\">${element}</g>`,\n )\n }\n return acc\n },\n {\n bbox: new Box2(),\n elements: [],\n },\n )\n\n const viewBox = bbox.valid\n ? {\n x: bbox.min.x,\n y: -bbox.max.y,\n width: bbox.max.x - bbox.min.x,\n height: bbox.max.y - bbox.min.y,\n }\n : {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n }\n return `<?xml version=\"1.0\"?>\n<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\"\n preserveAspectRatio=\"xMinYMin meet\"\n viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\"\n width=\"100%\" height=\"100%\"\n>\n <g stroke=\"#000000\" stroke-width=\"0.1%\" fill=\"none\" transform=\"matrix(1,0,0,-1,0,0)\">\n ${elements.join('\\n')}\n </g>\n</svg>`\n}\n"],
5
- "mappings": "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",
6
- "names": ["entityToPolyline", "transformBoundingBoxAndElement", "rotate", "dimensionToSVG", "toPiecewiseBezier", "logger", "denormalise", "getRGBForEntity", "bbox", "rgbToColorAttribute"]
4
+ "sourcesContent": ["import { Box2 } from 'vecks'\n\nimport denormalise from './denormalise'\nimport dimensionToSVG, { getDimensionGeometryBBox } from './dimensionToSVG'\nimport entityToPolyline from './entityToPolyline'\nimport getRGBForEntity from './getRGBForEntity'\nimport escapeXmlText from './util/escapeXmlText'\nimport logger from './util/logger'\nimport rgbToColorAttribute from './util/rgbToColorAttribute'\nimport rotate from './util/rotate'\nimport toPiecewiseBezier, { multiplicity } from './util/toPiecewiseBezier'\nimport transformBoundingBoxAndElement from './util/transformBoundingBoxAndElement'\n\nimport type {\n ArcEntity,\n CircleEntity,\n DimensionEntity,\n EllipseEntity,\n Entity,\n LeaderEntity,\n MTextEntity,\n ParsedDXF,\n ShapeEntity,\n SplineEntity,\n TextEntity,\n ToleranceEntity,\n ToSVGOptions,\n} from './types'\nimport type { BoundsAndElement } from './types/svg'\n\nconst addFlipXIfApplicable = (\n entity: Entity,\n { bbox, element }: BoundsAndElement,\n): BoundsAndElement => {\n if (entity.extrusionZ === -1) {\n return {\n bbox: new Box2()\n .expandByPoint({ x: -bbox.min.x, y: bbox.min.y })\n .expandByPoint({ x: -bbox.max.x, y: bbox.max.y }),\n element: `<g transform=\"matrix(-1 0 0 1 0 0)\">\n ${element}\n </g>`,\n }\n } else {\n return { bbox, element }\n }\n}\n\n/**\n * Create a <path /> element. Interpolates curved entities.\n */\nconst polyline = (entity: Entity): BoundsAndElement => {\n const vertices = entityToPolyline(entity as any)\n const bbox = vertices.reduce(\n (acc, [x, y]) => acc.expandByPoint({ x, y }),\n new Box2(),\n )\n const d = vertices.reduce((acc, point, i) => {\n acc += i === 0 ? 'M' : 'L'\n acc += point[0] + ',' + point[1]\n return acc\n }, '')\n // Empirically it appears that flipping horizontally does not apply to polyline\n return transformBoundingBoxAndElement(\n bbox,\n `<path d=\"${d}\" />`,\n entity.transforms ?? [],\n )\n}\n\n/**\n * Create a <path /> element. Interpolates curved entities.\n * lwpolyline is the same as polyline but addFlipXIfApplicable does apply\n */\nconst lwpolyline = (entity: Entity): BoundsAndElement => {\n const vertices = entityToPolyline(entity as any)\n const bbox0 = vertices.reduce(\n (acc, [x, y]) => acc.expandByPoint({ x, y }),\n new Box2(),\n )\n const d = vertices.reduce((acc, point, i) => {\n acc += i === 0 ? 'M' : 'L'\n acc += point[0] + ',' + point[1]\n return acc\n }, '')\n const element0 = `<path d=\"${d}\" />`\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(\n bbox,\n element,\n entity.transforms ?? [],\n )\n}\n\nconst leader = (entity: LeaderEntity): BoundsAndElement | null => {\n if (!entity.vertices || entity.vertices.length < 2) return null\n\n const bbox0 = entity.vertices.reduce(\n (acc, p) => acc.expandByPoint({ x: p.x, y: p.y }),\n new Box2(),\n )\n const d = entity.vertices.reduce((acc, p, i) => {\n acc += i === 0 ? 'M' : 'L'\n acc += p.x + ',' + p.y\n return acc\n }, '')\n\n return transformBoundingBoxAndElement(\n bbox0,\n `<path d=\"${d}\" />`,\n entity.transforms ?? [],\n )\n}\n\n\n/**\n * Create a <circle /> element for the CIRCLE entity.\n */\nconst circle = (entity: CircleEntity): BoundsAndElement => {\n const bbox0 = new Box2()\n .expandByPoint({\n x: entity.x + entity.r,\n y: entity.y + entity.r,\n })\n .expandByPoint({\n x: entity.x - entity.r,\n y: entity.y - entity.r,\n })\n const element0 = `<circle cx=\"${entity.x}\" cy=\"${entity.y}\" r=\"${entity.r}\" />`\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\ninterface EllipticArcParams {\n cx: number\n cy: number\n majorX: number\n majorY: number\n axisRatio: number\n startAngle: number\n endAngle: number\n flipX?: boolean\n}\n\n/**\n * Create a a <path d=\"A...\" /> or <ellipse /> element for the ARC or ELLIPSE\n * DXF entity (<ellipse /> if start and end point are the same).\n */\nconst ellipseOrArc = (params: EllipticArcParams): BoundsAndElement => {\n const { cx, cy, majorX, majorY, axisRatio, startAngle, endAngle } = params\n const rx = Math.hypot(majorX, majorY)\n const ry = axisRatio * rx\n const rotationAngle = -Math.atan2(-majorY, majorX)\n\n const bbox = bboxEllipseOrArc(params)\n\n if (\n Math.abs(startAngle - endAngle) < 1e-9 ||\n Math.abs(startAngle - endAngle + Math.PI * 2) < 1e-9\n ) {\n // Use a native <ellipse> when start and end angles are the same, and\n // arc paths with same start and end points don't render (at least on Safari)\n const element = `<g transform=\"rotate(${\n (rotationAngle / Math.PI) * 180\n } ${cx}, ${cy})\">\n <ellipse cx=\"${cx}\" cy=\"${cy}\" rx=\"${rx}\" ry=\"${ry}\" />\n </g>`\n return { bbox, element }\n } else {\n const startOffset = rotate(\n {\n x: Math.cos(startAngle) * rx,\n y: Math.sin(startAngle) * ry,\n },\n rotationAngle,\n )\n const startPoint = {\n x: cx + startOffset.x,\n y: cy + startOffset.y,\n }\n const endOffset = rotate(\n {\n x: Math.cos(endAngle) * rx,\n y: Math.sin(endAngle) * ry,\n },\n rotationAngle,\n )\n const endPoint = {\n x: cx + endOffset.x,\n y: cy + endOffset.y,\n }\n const adjustedEndAngle =\n endAngle < startAngle ? endAngle + Math.PI * 2 : endAngle\n const largeArcFlag = adjustedEndAngle - startAngle < Math.PI ? 0 : 1\n const d = `M ${startPoint.x} ${startPoint.y} A ${rx} ${ry} ${\n (rotationAngle / Math.PI) * 180\n } ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y}`\n const element = `<path d=\"${d}\" />`\n return { bbox, element }\n }\n}\n\n/**\n * Compute the bounding box of an elliptical arc, given the DXF entity parameters\n */\n\nconst bboxEllipseOrArc = (params: EllipticArcParams): Box2 => {\n const { cx, cy, majorX, majorY, axisRatio } = params\n let { startAngle, endAngle } = params\n\n // The bounding box will be defined by the starting point of the ellipse, and ending point,\n // and any extrema on the ellipse that are between startAngle and endAngle.\n // The extrema are found by setting either the x or y component of the ellipse's\n // tangent vector to zero and solving for the angle.\n\n // Ensure start and end angles are > 0 and well-ordered\n while (startAngle < 0) startAngle += Math.PI * 2\n while (endAngle <= startAngle) endAngle += Math.PI * 2\n\n // When rotated, the extrema of the ellipse will be found at these angles\n const angles = []\n\n if (Math.abs(majorX) < 1e-12 || Math.abs(majorY) < 1e-12) {\n // Special case for majorX or majorY = 0\n for (let i = 0; i < 4; i++) {\n angles.push((i / 2) * Math.PI)\n }\n } else {\n // reference https://github.com/bjnortier/dxf/issues/47#issuecomment-545915042\n angles[0] = Math.atan((-majorY * axisRatio) / majorX) - Math.PI // Ensure angles < 0\n angles[1] = Math.atan((majorX * axisRatio) / majorY) - Math.PI\n angles[2] = angles[0] - Math.PI\n angles[3] = angles[1] - Math.PI\n }\n\n // Remove angles not falling between start and end\n for (let i = 4; i >= 0; i--) {\n while (angles[i] < startAngle) angles[i] += Math.PI * 2\n if (angles[i] > endAngle) {\n angles.splice(i, 1)\n }\n }\n\n // Also to consider are the starting and ending points:\n angles.push(startAngle, endAngle)\n\n // Compute points lying on the unit circle at these angles\n const pts = angles.map((a) => ({\n x: Math.cos(a),\n y: Math.sin(a),\n }))\n\n // Transformation matrix, formed by the major and minor axes\n const M = [\n [majorX, -majorY * axisRatio],\n [majorY, majorX * axisRatio],\n ]\n\n // Rotate, scale, and translate points\n const rotatedPts = pts.map((p) => ({\n x: p.x * M[0][0] + p.y * M[0][1] + cx,\n y: p.x * M[1][0] + p.y * M[1][1] + cy,\n }))\n\n // Compute extents of bounding box\n const bbox = rotatedPts.reduce((acc, p) => {\n acc.expandByPoint(p)\n return acc\n }, new Box2())\n\n return bbox\n}\n\n/**\n * An ELLIPSE is defined by the major axis, convert to X and Y radius with\n * a rotation angle\n */\nconst ellipse = (entity: EllipseEntity): BoundsAndElement => {\n const { bbox: bbox0, element: element0 } = ellipseOrArc({\n cx: entity.x,\n cy: entity.y,\n majorX: entity.majorX,\n majorY: entity.majorY,\n axisRatio: entity.axisRatio,\n startAngle: entity.startAngle,\n endAngle: entity.endAngle,\n })\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * An ARC is an ellipse with equal radii\n */\nconst arc = (entity: ArcEntity): BoundsAndElement => {\n const { bbox: bbox0, element: element0 } = ellipseOrArc({\n cx: entity.x,\n cy: entity.y,\n majorX: entity.r,\n majorY: 0,\n axisRatio: 1,\n startAngle: entity.startAngle,\n endAngle: entity.endAngle,\n flipX: entity.extrusionZ === -1,\n })\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create a <text /> element for TEXT entity\n */\nconst text = (entity: TextEntity): BoundsAndElement => {\n const x = entity.x ?? 0\n const y = entity.y ?? 0\n const height = entity.textHeight ?? 1\n const rotation = entity.rotation ?? 0\n const content = entity.string ?? ''\n\n // Estimate text bounding box (approximate)\n const textWidth = content.length * height * 0.6\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n const rotationDegrees = (rotation * 180) / Math.PI\n 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>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create a <text /> element for MTEXT entity\n */\nconst mtext = (entity: MTextEntity): BoundsAndElement => {\n const x = entity.x ?? 0\n const y = entity.y ?? 0\n const height = entity.nominalTextHeight ?? entity.textHeight ?? 1\n const content = entity.string ?? ''\n\n // Estimate text bounding box (approximate)\n const textWidth = (entity.refRectangleWidth ?? content.length * height * 0.6)\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n // Calculate rotation from x-axis direction\n const rotation = entity.xAxisX !== undefined && entity.xAxisY !== undefined\n ? Math.atan2(entity.xAxisY, entity.xAxisX)\n : 0\n const rotationDegrees = (rotation * 180) / Math.PI\n 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>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Minimal <text /> fallback for TOLERANCE entities.\n * DXF uses special control codes; we preserve the raw string.\n */\nconst tolerance = (entity: ToleranceEntity): BoundsAndElement => {\n const x = entity.insertionPoint?.x ?? 0\n const y = entity.insertionPoint?.y ?? 0\n const height = 1\n const content = entity.text ?? ''\n\n const rotation = entity.xAxisDirection\n ? Math.atan2(entity.xAxisDirection.y, entity.xAxisDirection.x)\n : 0\n const rotationDegrees = (rotation * 180) / Math.PI\n\n const textWidth = content.length * height * 0.6\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n 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>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Minimal <text /> fallback for SHAPE entities.\n * Rendering SHX-based shapes is out of scope; we render the name as text.\n */\nconst shape = (entity: ShapeEntity): BoundsAndElement => {\n const x = entity.insertionPoint?.x ?? 0\n const y = entity.insertionPoint?.y ?? 0\n const height = entity.size ?? 1\n const rotation = entity.rotation ?? 0\n const content = entity.name ?? ''\n\n const textWidth = content.length * height * 0.6\n const bbox0 = new Box2()\n .expandByPoint({ x, y })\n .expandByPoint({ x: x + textWidth, y: y + height })\n\n const rotationDegrees = (rotation * 180) / Math.PI\n 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>`\n\n const { bbox, element } = addFlipXIfApplicable(entity, {\n bbox: bbox0,\n element: element0,\n })\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Create dimension visualization with DIMSTYLE support\n */\nconst dimension = (\n entity: DimensionEntity,\n dimStyle?: any,\n options?: ToSVGOptions,\n viewport?: { width: number; height: number },\n): BoundsAndElement => {\n const result = dimensionToSVG(entity, dimStyle, options, viewport)\n return transformBoundingBoxAndElement(\n result.bbox,\n result.element,\n entity.transforms ?? [],\n )\n}\n\nexport const piecewiseToPaths = (\n k: number,\n knots: number[],\n controlPoints: Array<{ x: number; y: number }>,\n): string[] => {\n const paths: string[] = []\n let controlPointIndex = 0\n let knotIndex = k\n while (knotIndex < knots.length - k + 1) {\n const m = multiplicity(knots, knotIndex)\n const cp = controlPoints.slice(controlPointIndex, controlPointIndex + k)\n if (k === 4) {\n paths.push(\n `<path d=\"M ${cp[0].x} ${cp[0].y} C ${cp[1].x} ${cp[1].y} ${cp[2].x} ${cp[2].y} ${cp[3].x} ${cp[3].y}\" />`,\n )\n } else if (k === 3) {\n paths.push(\n `<path d=\"M ${cp[0].x} ${cp[0].y} Q ${cp[1].x} ${cp[1].y} ${cp[2].x} ${cp[2].y}\" />`,\n )\n }\n controlPointIndex += m\n knotIndex += m\n }\n return paths\n}\n\nconst bezier = (entity: SplineEntity): BoundsAndElement => {\n let bbox = new Box2()\n for (const p of entity.controlPoints) {\n bbox = bbox.expandByPoint(p)\n }\n const k = entity.degree + 1\n const piecewise = toPiecewiseBezier(k, entity.controlPoints, entity.knots)\n const paths = piecewiseToPaths(k, piecewise.knots, piecewise.controlPoints)\n const element = `<g>${paths.join('')}</g>`\n return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? [])\n}\n\n/**\n * Switch the appropriate function on entity type. CIRCLE, ARC and ELLIPSE\n * produce native SVG elements, the rest produce interpolated polylines.\n */\nconst entityToBoundsAndElement = (\n entity: Entity,\n dimStyles?: { [name: string]: any },\n options?: ToSVGOptions,\n viewport?: { width: number; height: number },\n): BoundsAndElement | null => {\n switch (entity.type) {\n case 'CIRCLE':\n return circle(entity as CircleEntity)\n case 'ELLIPSE':\n return ellipse(entity as EllipseEntity)\n case 'ARC':\n return arc(entity as ArcEntity)\n case 'TEXT':\n return text(entity as TextEntity)\n case 'MTEXT':\n return mtext(entity as MTextEntity)\n case 'DIMENSION': {\n const dimEntity = entity as DimensionEntity\n const styleName = typeof dimEntity.styleName === 'string'\n ? dimEntity.styleName\n : undefined\n const dimStyle = styleName && dimStyles\n ? dimStyles[styleName]\n : undefined\n return dimension(dimEntity, dimStyle, options, viewport)\n }\n case 'SPLINE': {\n const splineEntity = entity as SplineEntity\n const hasWeights = splineEntity.weights?.some((w: number) => w !== 1)\n if ((splineEntity.degree === 2 || splineEntity.degree === 3) && !hasWeights) {\n try {\n return bezier(splineEntity)\n } catch (err) {\n const error = err as Error\n logger.warn('bezier conversion failed, using polyline:', error.message)\n return polyline(entity)\n }\n } else {\n return polyline(entity)\n }\n }\n case 'LINE':\n case 'RAY':\n case 'XLINE':\n case 'POLYLINE': {\n return polyline(entity)\n }\n case 'SOLID':\n case 'TRACE': {\n return polyline(entity)\n }\n case 'LWPOLYLINE': {\n return lwpolyline(entity)\n }\n case 'WIPEOUT': {\n return polyline(entity)\n }\n case 'LEADER': {\n return leader(entity as LeaderEntity)\n }\n case 'TOLERANCE': {\n return tolerance(entity as ToleranceEntity)\n }\n case 'SHAPE': {\n return shape(entity as ShapeEntity)\n }\n default:\n logger.warn('entity type not supported in SVG rendering:', entity.type)\n return null\n }\n}\n\nexport default function toSVG(parsed: ParsedDXF, options: ToSVGOptions = {}): string {\n const entities = denormalise(parsed)\n const dimStyles = parsed.tables.dimStyles\n\n const geometryBBox = entities.reduce((acc: Box2, entity: Entity): Box2 => {\n if (entity.type === 'DIMENSION') {\n const bbox = getDimensionGeometryBBox(entity as DimensionEntity)\n if (bbox.valid) {\n acc.expandByPoint(bbox.min)\n acc.expandByPoint(bbox.max)\n }\n return acc\n }\n\n const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options)\n if (boundsAndElement?.bbox.valid) {\n acc.expandByPoint(boundsAndElement.bbox.min)\n acc.expandByPoint(boundsAndElement.bbox.max)\n }\n return acc\n }, new Box2())\n\n const viewport = geometryBBox.valid\n ? {\n width: geometryBBox.max.x - geometryBBox.min.x,\n height: geometryBBox.max.y - geometryBBox.min.y,\n }\n : {\n width: 0,\n height: 0,\n }\n\n const { bbox, elements } = entities.reduce(\n (\n acc: { bbox: Box2; elements: string[] },\n entity: Entity,\n ): { bbox: Box2; elements: string[] } => {\n const rgb = getRGBForEntity(parsed.tables.layers, entity)\n const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options, viewport)\n // Ignore entities that don't produce SVG elements or have unsupported types\n if (boundsAndElement) {\n const { bbox, element } = boundsAndElement\n // Ignore invalid bounding boxes\n if (bbox.valid) {\n acc.bbox.expandByPoint(bbox.min)\n acc.bbox.expandByPoint(bbox.max)\n }\n const color = rgbToColorAttribute(rgb)\n if (entity.type === 'SOLID' || entity.type === 'TRACE') {\n acc.elements.push(`<g fill=\"${color}\" stroke=\"none\">${element}</g>`)\n } else {\n acc.elements.push(`<g stroke=\"${color}\">${element}</g>`)\n }\n }\n return acc\n },\n {\n bbox: new Box2(),\n elements: [],\n },\n )\n\n const viewBox = bbox.valid\n ? {\n x: bbox.min.x,\n y: -bbox.max.y,\n width: bbox.max.x - bbox.min.x,\n height: bbox.max.y - bbox.min.y,\n }\n : {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n }\n return `<?xml version=\"1.0\"?>\n<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\"\n preserveAspectRatio=\"xMinYMin meet\"\n viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\"\n width=\"100%\" height=\"100%\"\n>\n <g stroke=\"#000000\" stroke-width=\"0.1%\" fill=\"none\" transform=\"matrix(1,0,0,-1,0,0)\">\n ${elements.join('\\n')}\n </g>\n</svg>`\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["entityToPolyline", "transformBoundingBoxAndElement", "rotate", "escapeXmlText", "dimensionToSVG", "toPiecewiseBezier", "logger", "denormalise", "bbox", "getRGBForEntity", "rgbToColorAttribute"]
7
7
  }
package/lib/toSVG.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { Box2 } from "vecks";
2
2
  import denormalise from "./denormalise";
3
- import dimensionToSVG from "./dimensionToSVG";
3
+ import dimensionToSVG, { getDimensionGeometryBBox } from "./dimensionToSVG";
4
4
  import entityToPolyline from "./entityToPolyline";
5
5
  import getRGBForEntity from "./getRGBForEntity";
6
+ import escapeXmlText from "./util/escapeXmlText";
6
7
  import logger from "./util/logger";
7
8
  import rgbToColorAttribute from "./util/rgbToColorAttribute";
8
9
  import rotate from "./util/rotate";
@@ -59,6 +60,23 @@ const lwpolyline = (entity) => {
59
60
  entity.transforms ?? []
60
61
  );
61
62
  };
63
+ const leader = (entity) => {
64
+ if (!entity.vertices || entity.vertices.length < 2) return null;
65
+ const bbox0 = entity.vertices.reduce(
66
+ (acc, p) => acc.expandByPoint({ x: p.x, y: p.y }),
67
+ new Box2()
68
+ );
69
+ const d = entity.vertices.reduce((acc, p, i) => {
70
+ acc += i === 0 ? "M" : "L";
71
+ acc += p.x + "," + p.y;
72
+ return acc;
73
+ }, "");
74
+ return transformBoundingBoxAndElement(
75
+ bbox0,
76
+ `<path d="${d}" />`,
77
+ entity.transforms ?? []
78
+ );
79
+ };
62
80
  const circle = (entity) => {
63
81
  const bbox0 = new Box2().expandByPoint({
64
82
  x: entity.x + entity.r,
@@ -198,7 +216,7 @@ const text = (entity) => {
198
216
  const textWidth = content.length * height * 0.6;
199
217
  const bbox0 = new Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
200
218
  const rotationDegrees = rotation * 180 / Math.PI;
201
- const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`;
219
+ 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>`;
202
220
  const { bbox, element } = addFlipXIfApplicable(entity, {
203
221
  bbox: bbox0,
204
222
  element: element0
@@ -214,15 +232,47 @@ const mtext = (entity) => {
214
232
  const bbox0 = new Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
215
233
  const rotation = entity.xAxisX !== void 0 && entity.xAxisY !== void 0 ? Math.atan2(entity.xAxisY, entity.xAxisX) : 0;
216
234
  const rotationDegrees = rotation * 180 / Math.PI;
217
- const element0 = `<text x="${x}" y="${y}" font-size="${height}" transform="rotate(${-rotationDegrees} ${x} ${y}) scale(1,-1) translate(0 ${-2 * y})">${content}</text>`;
235
+ 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>`;
236
+ const { bbox, element } = addFlipXIfApplicable(entity, {
237
+ bbox: bbox0,
238
+ element: element0
239
+ });
240
+ return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? []);
241
+ };
242
+ const tolerance = (entity) => {
243
+ const x = entity.insertionPoint?.x ?? 0;
244
+ const y = entity.insertionPoint?.y ?? 0;
245
+ const height = 1;
246
+ const content = entity.text ?? "";
247
+ const rotation = entity.xAxisDirection ? Math.atan2(entity.xAxisDirection.y, entity.xAxisDirection.x) : 0;
248
+ const rotationDegrees = rotation * 180 / Math.PI;
249
+ const textWidth = content.length * height * 0.6;
250
+ const bbox0 = new Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
251
+ 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>`;
218
252
  const { bbox, element } = addFlipXIfApplicable(entity, {
219
253
  bbox: bbox0,
220
254
  element: element0
221
255
  });
222
256
  return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? []);
223
257
  };
224
- const dimension = (entity, dimStyle) => {
225
- const result = dimensionToSVG(entity, dimStyle);
258
+ const shape = (entity) => {
259
+ const x = entity.insertionPoint?.x ?? 0;
260
+ const y = entity.insertionPoint?.y ?? 0;
261
+ const height = entity.size ?? 1;
262
+ const rotation = entity.rotation ?? 0;
263
+ const content = entity.name ?? "";
264
+ const textWidth = content.length * height * 0.6;
265
+ const bbox0 = new Box2().expandByPoint({ x, y }).expandByPoint({ x: x + textWidth, y: y + height });
266
+ const rotationDegrees = rotation * 180 / Math.PI;
267
+ 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>`;
268
+ const { bbox, element } = addFlipXIfApplicable(entity, {
269
+ bbox: bbox0,
270
+ element: element0
271
+ });
272
+ return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? []);
273
+ };
274
+ const dimension = (entity, dimStyle, options, viewport) => {
275
+ const result = dimensionToSVG(entity, dimStyle, options, viewport);
226
276
  return transformBoundingBoxAndElement(
227
277
  result.bbox,
228
278
  result.element,
@@ -261,7 +311,7 @@ const bezier = (entity) => {
261
311
  const element = `<g>${paths.join("")}</g>`;
262
312
  return transformBoundingBoxAndElement(bbox, element, entity.transforms ?? []);
263
313
  };
264
- const entityToBoundsAndElement = (entity, dimStyles) => {
314
+ const entityToBoundsAndElement = (entity, dimStyles, options, viewport) => {
265
315
  switch (entity.type) {
266
316
  case "CIRCLE":
267
317
  return circle(entity);
@@ -277,7 +327,7 @@ const entityToBoundsAndElement = (entity, dimStyles) => {
277
327
  const dimEntity = entity;
278
328
  const styleName = typeof dimEntity.styleName === "string" ? dimEntity.styleName : void 0;
279
329
  const dimStyle = styleName && dimStyles ? dimStyles[styleName] : void 0;
280
- return dimension(dimEntity, dimStyle);
330
+ return dimension(dimEntity, dimStyle, options, viewport);
281
331
  }
282
332
  case "SPLINE": {
283
333
  const splineEntity = entity;
@@ -295,33 +345,77 @@ const entityToBoundsAndElement = (entity, dimStyles) => {
295
345
  }
296
346
  }
297
347
  case "LINE":
348
+ case "RAY":
349
+ case "XLINE":
298
350
  case "POLYLINE": {
299
351
  return polyline(entity);
300
352
  }
353
+ case "SOLID":
354
+ case "TRACE": {
355
+ return polyline(entity);
356
+ }
301
357
  case "LWPOLYLINE": {
302
358
  return lwpolyline(entity);
303
359
  }
360
+ case "WIPEOUT": {
361
+ return polyline(entity);
362
+ }
363
+ case "LEADER": {
364
+ return leader(entity);
365
+ }
366
+ case "TOLERANCE": {
367
+ return tolerance(entity);
368
+ }
369
+ case "SHAPE": {
370
+ return shape(entity);
371
+ }
304
372
  default:
305
373
  logger.warn("entity type not supported in SVG rendering:", entity.type);
306
374
  return null;
307
375
  }
308
376
  };
309
- function toSVG(parsed) {
377
+ function toSVG(parsed, options = {}) {
310
378
  const entities = denormalise(parsed);
311
379
  const dimStyles = parsed.tables.dimStyles;
380
+ const geometryBBox = entities.reduce((acc, entity) => {
381
+ if (entity.type === "DIMENSION") {
382
+ const bbox2 = getDimensionGeometryBBox(entity);
383
+ if (bbox2.valid) {
384
+ acc.expandByPoint(bbox2.min);
385
+ acc.expandByPoint(bbox2.max);
386
+ }
387
+ return acc;
388
+ }
389
+ const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options);
390
+ if (boundsAndElement?.bbox.valid) {
391
+ acc.expandByPoint(boundsAndElement.bbox.min);
392
+ acc.expandByPoint(boundsAndElement.bbox.max);
393
+ }
394
+ return acc;
395
+ }, new Box2());
396
+ const viewport = geometryBBox.valid ? {
397
+ width: geometryBBox.max.x - geometryBBox.min.x,
398
+ height: geometryBBox.max.y - geometryBBox.min.y
399
+ } : {
400
+ width: 0,
401
+ height: 0
402
+ };
312
403
  const { bbox, elements } = entities.reduce(
313
404
  (acc, entity) => {
314
405
  const rgb = getRGBForEntity(parsed.tables.layers, entity);
315
- const boundsAndElement = entityToBoundsAndElement(entity, dimStyles);
406
+ const boundsAndElement = entityToBoundsAndElement(entity, dimStyles, options, viewport);
316
407
  if (boundsAndElement) {
317
408
  const { bbox: bbox2, element } = boundsAndElement;
318
409
  if (bbox2.valid) {
319
410
  acc.bbox.expandByPoint(bbox2.min);
320
411
  acc.bbox.expandByPoint(bbox2.max);
321
412
  }
322
- acc.elements.push(
323
- `<g stroke="${rgbToColorAttribute(rgb)}">${element}</g>`
324
- );
413
+ const color = rgbToColorAttribute(rgb);
414
+ if (entity.type === "SOLID" || entity.type === "TRACE") {
415
+ acc.elements.push(`<g fill="${color}" stroke="none">${element}</g>`);
416
+ } else {
417
+ acc.elements.push(`<g stroke="${color}">${element}</g>`);
418
+ }
325
419
  }
326
420
  return acc;
327
421
  },