abstract-image 13.0.13 → 13.0.15

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.
@@ -53,14 +53,14 @@ export const DXF_ENTITIES: string[] = [
53
53
  "VERTEX",
54
54
  "VIEWPORT",
55
55
  "WIPEOUT",
56
- "XLINE"
56
+ "XLINE",
57
57
  ];
58
58
 
59
59
  export type DxfOptions = {
60
60
  readonly imageDataByUrl: Record<string, `${typeof DXF_DATA_URL}${string}`>;
61
- readonly fillPolygons?: boolean;
62
- readonly useStrokeThickness?: boolean;
63
- readonly useColor?: boolean;
61
+ readonly fillPolygons: boolean;
62
+ readonly useStrokeThickness: boolean;
63
+ readonly useColor: boolean;
64
64
  };
65
65
 
66
66
  export type DxfSpace = "*Model_Space" | "*Paper_Space";
@@ -96,7 +96,7 @@ export function dxf2dExportImage(root: AbstractImage, options?: Optional<DxfOpti
96
96
  const modelSpaceHandle = newHandle();
97
97
  const paperSpaceHandle = newHandle();
98
98
 
99
- const opts: Required<DxfOptions> = {
99
+ const opts: DxfOptions = {
100
100
  imageDataByUrl: options?.imageDataByUrl ?? {},
101
101
  fillPolygons: options?.fillPolygons ?? false,
102
102
  useColor: options?.useColor ?? false,
@@ -112,7 +112,15 @@ export function dxf2dExportImage(root: AbstractImage, options?: Optional<DxfOpti
112
112
  blocks += createSpaceBlock("*Model_Space", modelSpaceHandle, newHandle);
113
113
  blocks += createSpaceBlock("*Paper_Space", paperSpaceHandle, newHandle);
114
114
  for (const component of root.components) {
115
- const [newEntities, newBlocks, newBlockRecords] = componentDxf(component, layer, root.size, modelSpaceHandle, externalCache, opts, newHandle);
115
+ const [newEntities, newBlocks, newBlockRecords] = componentDxf(
116
+ component,
117
+ layer,
118
+ root.size,
119
+ modelSpaceHandle,
120
+ externalCache,
121
+ opts,
122
+ newHandle
123
+ );
116
124
  entities += newEntities;
117
125
  blocks += newBlocks;
118
126
  blockRecords.push(...newBlockRecords);
@@ -165,14 +173,30 @@ export function dxf2dExportImage(root: AbstractImage, options?: Optional<DxfOpti
165
173
  return header + dxf;
166
174
  }
167
175
 
168
- function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle: string, externalCache: Map<string, DxfInsert>, options: Required<DxfOptions>, newHandle: () => string): readonly [string, string, ReadonlyArray<BlockRecord>] {
176
+ function componentDxf(
177
+ c: Component,
178
+ layer: number,
179
+ size: Size,
180
+ modelSpaceHandle: string,
181
+ externalCache: Map<string, DxfInsert>,
182
+ options: Required<DxfOptions>,
183
+ newHandle: () => string
184
+ ): readonly [string, string, ReadonlyArray<BlockRecord>] {
169
185
  let entities = "";
170
186
  let blocks = "";
171
187
  let blockRecords: Array<BlockRecord> = [];
172
188
 
173
189
  if (c.type === "group") {
174
190
  for (const child of c.children) {
175
- const [newEntities, newBlocks, newBlockRecords] = componentDxf(child, layer, size, modelSpaceHandle, externalCache, options, newHandle);
191
+ const [newEntities, newBlocks, newBlockRecords] = componentDxf(
192
+ child,
193
+ layer,
194
+ size,
195
+ modelSpaceHandle,
196
+ externalCache,
197
+ options,
198
+ newHandle
199
+ );
176
200
  entities += newEntities;
177
201
  blocks += newBlocks;
178
202
  blockRecords.push(...newBlockRecords);
@@ -193,7 +217,10 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
193
217
  if (!dxfStringRaw) {
194
218
  return [entities, blocks, blockRecords];
195
219
  }
196
- const dxfString = dxfStringRaw.split("\n").map((v) => v.trim()).join("\n");
220
+ const dxfString = dxfStringRaw
221
+ .split("\n")
222
+ .map((v) => v.trim())
223
+ .join("\n");
197
224
 
198
225
  const version = extractStandard(dxfString);
199
226
  if (version !== DXF_STANDARD) {
@@ -216,7 +243,9 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
216
243
  const externalBlockRecords = extractBlockRecords(dxfString);
217
244
 
218
245
  const newBlockRecordHandle = newHandle();
219
- const newName = `EMBEDED_IMAGE_${randomID()}_${scale.x.toPrecision(4).replace(".", "_")}X${scale.y.toPrecision(4).replace(".", "_")}`;
246
+ const newName = `EMBEDED_IMAGE_${randomID()}_${scale.x.toPrecision(4).replace(".", "_")}X${scale.y
247
+ .toPrecision(4)
248
+ .replace(".", "_")}`;
220
249
 
221
250
  const initOldHandlesMap = new Map<string, string>();
222
251
 
@@ -225,7 +254,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
225
254
  initOldHandlesMap.set(br.id, newBlockRecordHandle);
226
255
  } else if (br.name === "*Paper_Space") {
227
256
  initOldHandlesMap.set(br.id, newBlockRecordHandle);
228
- } else if(!initOldHandlesMap.has(br.id)) {
257
+ } else if (!initOldHandlesMap.has(br.id)) {
229
258
  const newId = newHandle();
230
259
  initOldHandlesMap.set(br.id, newId);
231
260
  blockRecords.push({ name: br.name, id: newId });
@@ -249,7 +278,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
249
278
  let blockRecordName = newName;
250
279
  let blockRecordId = "0";
251
280
  const existingBlockRecord = blockRecords.find((v) => v.name.localeCompare(newName) === 0);
252
- if(existingBlockRecord !== undefined) {
281
+ if (existingBlockRecord !== undefined) {
253
282
  blockRecordName = existingBlockRecord.name;
254
283
  blockRecordId = existingBlockRecord.id;
255
284
  } else {
@@ -262,23 +291,35 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
262
291
 
263
292
  const newBlock =
264
293
  "0\nBLOCK\n" +
265
- "5\n" + newHandle() + "\n" +
266
- "330\n" + blockRecordId + "\n" +
294
+ "5\n" +
295
+ newHandle() +
296
+ "\n" +
297
+ "330\n" +
298
+ blockRecordId +
299
+ "\n" +
267
300
  "100\nAcDbEntity\n" +
268
301
  "8\n0\n" +
269
302
  "100\nAcDbBlockBegin\n" +
270
- "2\n" + blockRecordName + "\n" +
303
+ "2\n" +
304
+ blockRecordName +
305
+ "\n" +
271
306
  "70\n0\n" +
272
307
  "10\n0\n" +
273
- "20\n0\n" +
308
+ "20\n0\n" +
274
309
  "30\n0\n" +
275
- "3\n" + blockRecordName + "\n" +
310
+ "3\n" +
311
+ blockRecordName +
312
+ "\n" +
276
313
  "1\n\n" +
277
314
  newEntities +
278
315
  "\n" +
279
316
  "0\nENDBLK\n" +
280
- "5\n" + newHandle() + "\n" +
281
- "330\n" + blockRecordId + "\n" +
317
+ "5\n" +
318
+ newHandle() +
319
+ "\n" +
320
+ "330\n" +
321
+ blockRecordId +
322
+ "\n" +
282
323
  "100\nAcDbEntity\n" +
283
324
  "8\n0\n" +
284
325
  "100\nAcDbBlockEnd\n";
@@ -289,7 +330,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
289
330
  const insert: DxfInsert = {
290
331
  blockRecordId: blockRecordId,
291
332
  name: blockRecordName,
292
- extents
333
+ extents,
293
334
  };
294
335
  externalCache.set(cachKey, insert);
295
336
  entities += createExternalInsert(insert, c, size, modelSpaceHandle, newHandle);
@@ -305,7 +346,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
305
346
  entities += "100\nAcDbEntity\n";
306
347
  entities += "8\n" + layer + "\n";
307
348
 
308
- if(options.useColor && !isBlack(c.strokeColor)) {
349
+ if (options.useColor && !isBlack(c.strokeColor)) {
309
350
  entities += "60\n0\n";
310
351
  entities += "62\n256\n";
311
352
  entities += "420\n" + colorToInteger(c.strokeColor) + "\n";
@@ -315,12 +356,15 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
315
356
  entities += "10\n0\n20\n0\n30\n0\n";
316
357
  entities += "70\n0\n";
317
358
 
318
- if(options.useStrokeThickness) {
359
+ if (options.useStrokeThickness) {
319
360
  entities += "40\n" + (c.strokeThickness ?? 0) + "\n";
320
361
  entities += "41\n" + (c.strokeThickness ?? 0) + "\n";
321
362
  }
322
363
 
323
- const points: ReadonlyArray<Point> = [{ x: c.start.x, y: invert(c.start.y, size.height)}, { x: c.end.x, y: invert(c.end.y, size.height) }];
364
+ const points: ReadonlyArray<Point> = [
365
+ { x: c.start.x, y: invert(c.start.y, size.height) },
366
+ { x: c.end.x, y: invert(c.end.y, size.height) },
367
+ ];
324
368
  for (const point of points) {
325
369
  entities += "0\nVERTEX\n";
326
370
  entities += "5\n" + newHandle() + "\n";
@@ -351,7 +395,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
351
395
  entities += "100\nAcDbEntity\n";
352
396
  entities += "8\n" + layer + "\n";
353
397
 
354
- if(options.useColor && !isBlack(c.strokeColor)) {
398
+ if (options.useColor && !isBlack(c.strokeColor)) {
355
399
  entities += "60\n0\n";
356
400
  entities += "62\n256\n";
357
401
  entities += "420\n" + colorToInteger(c.strokeColor) + "\n";
@@ -361,7 +405,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
361
405
  entities += "10\n0\n20\n0\n30\n0\n";
362
406
  entities += "70\n0\n";
363
407
 
364
- if(options.useStrokeThickness) {
408
+ if (options.useStrokeThickness) {
365
409
  entities += "40\n" + (c.strokeThickness ?? 0) + "\n";
366
410
  entities += "41\n" + (c.strokeThickness ?? 0) + "\n";
367
411
  }
@@ -399,7 +443,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
399
443
  entities += "100\nAcDbEntity\n";
400
444
  entities += "8\nText\n";
401
445
 
402
- if(options.useColor && !isBlack(c.textColor)) {
446
+ if (options.useColor && !isBlack(c.textColor)) {
403
447
  entities += "60\n0\n";
404
448
  entities += "62\n256\n";
405
449
  entities += "420\n" + colorToInteger(c.textColor) + "\n";
@@ -417,7 +461,7 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
417
461
  entities += "71\n0\n";
418
462
  entities += "72\n" + horizontalAlignment.toString() + "\n";
419
463
 
420
- if(horizontalAlignment === 0 && verticalAlignment === 0) {
464
+ if (horizontalAlignment === 0 && verticalAlignment === 0) {
421
465
  entities += "100\nAcDbText\n";
422
466
  entities += "73\n0\n";
423
467
  } else {
@@ -441,21 +485,51 @@ function componentDxf(c: Component, layer: number, size: Size, modelSpaceHandle:
441
485
  const t = (2 * Math.PI * i) / numPoints;
442
486
  const x = c.topLeft.x + r1 + r1 * Math.cos(t);
443
487
  const y = c.topLeft.y + r2 + r2 * Math.sin(t);
444
- points.push({x, y});
488
+ points.push({ x, y });
445
489
  }
446
490
 
447
- entities += createPolygon(modelSpaceHandle, layer.toString(), points, c.strokeColor, c.fillColor, c.strokeThickness, size.height, options, newHandle);
491
+ entities += createPolygon(
492
+ modelSpaceHandle,
493
+ layer.toString(),
494
+ points,
495
+ c.strokeColor,
496
+ c.fillColor,
497
+ c.strokeThickness,
498
+ size.height,
499
+ options,
500
+ newHandle
501
+ );
448
502
  return [entities, blocks, blockRecords];
449
503
  }
450
504
 
451
505
  if (c.type === "polygon") {
452
- entities += createPolygon(modelSpaceHandle, layer.toString(), c.points, c.strokeColor, c.fillColor, c.strokeThickness, size.height, options, newHandle);
506
+ entities += createPolygon(
507
+ modelSpaceHandle,
508
+ layer.toString(),
509
+ c.points,
510
+ c.strokeColor,
511
+ c.fillColor,
512
+ c.strokeThickness,
513
+ size.height,
514
+ options,
515
+ newHandle
516
+ );
453
517
  return [entities, blocks, blockRecords];
454
518
  }
455
519
 
456
520
  if (c.type === "rectangle") {
457
521
  const cors = corners(c);
458
- entities += createPolygon(modelSpaceHandle, layer.toString(), cors, c.strokeColor, c.fillColor, c.strokeThickness, size.height, options, newHandle);
522
+ entities += createPolygon(
523
+ modelSpaceHandle,
524
+ layer.toString(),
525
+ cors,
526
+ c.strokeColor,
527
+ c.fillColor,
528
+ c.strokeThickness,
529
+ size.height,
530
+ options,
531
+ newHandle
532
+ );
459
533
  return [entities, blocks, blockRecords];
460
534
  }
461
535
 
@@ -474,11 +548,10 @@ function stripBlocks(blocks: string, blocksToStrip: ReadonlyArray<string>): stri
474
548
  let currentBlock: Array<string> = [];
475
549
  let currentBlockName = undefined;
476
550
  let i = 0;
477
- while(i < b.length) {
478
- if(b[i] === "0" && b[i+1] === "BLOCK") {
479
-
480
- if(i !== 0) {
481
- if(currentBlockName && !s.has(currentBlockName)) {
551
+ while (i < b.length) {
552
+ if (b[i] === "0" && b[i + 1] === "BLOCK") {
553
+ if (i !== 0) {
554
+ if (currentBlockName && !s.has(currentBlockName)) {
482
555
  validBlocks.push(...currentBlock);
483
556
  currentBlock = [];
484
557
  currentBlockName = undefined;
@@ -488,10 +561,10 @@ function stripBlocks(blocks: string, blocksToStrip: ReadonlyArray<string>): stri
488
561
  }
489
562
  }
490
563
 
491
- currentBlock.push(b[i], b[i+1]);
564
+ currentBlock.push(b[i], b[i + 1]);
492
565
  i += 2;
493
- } else if(b[i] === "2") {
494
- currentBlockName = b[i+1];
566
+ } else if (b[i] === "2") {
567
+ currentBlockName = b[i + 1];
495
568
  currentBlock.push(b[i]);
496
569
  i += 1;
497
570
  } else {
@@ -500,7 +573,7 @@ function stripBlocks(blocks: string, blocksToStrip: ReadonlyArray<string>): stri
500
573
  }
501
574
  }
502
575
 
503
- if(currentBlockName && !s.has(currentBlockName)) {
576
+ if (currentBlockName && !s.has(currentBlockName)) {
504
577
  validBlocks.push(...currentBlock);
505
578
  }
506
579
 
@@ -526,7 +599,9 @@ function extractEntities(dxf: string): string | undefined {
526
599
  }
527
600
 
528
601
  function extractBlockRecords(dxf: string): ReadonlyArray<BlockRecord> {
529
- return [...dxf.matchAll(/^\s*0\s*\n\s*BLOCK_RECORD[\s\S]*?\n\s*5\s*\n\s*([0-9A-Fa-f]+)[\s\S]*?\n\s*2\s*\n\s*([^\r\n]+)/gm)].map((match) => ({
602
+ return [
603
+ ...dxf.matchAll(/^\s*0\s*\n\s*BLOCK_RECORD[\s\S]*?\n\s*5\s*\n\s*([0-9A-Fa-f]+)[\s\S]*?\n\s*2\s*\n\s*([^\r\n]+)/gm),
604
+ ].map((match) => ({
530
605
  name: match[2],
531
606
  id: match[1],
532
607
  }));
@@ -534,7 +609,7 @@ function extractBlockRecords(dxf: string): ReadonlyArray<BlockRecord> {
534
609
 
535
610
  function extractBlocks(dxf: string, newModelSpace: string, newPaperSpace: string): string {
536
611
  const blockMatch = /0\s+SECTION\s+2\s+BLOCKS\s+([\s\S]*?)0\s+ENDSEC/m.exec(dxf);
537
- if(blockMatch === null) {
612
+ if (blockMatch === null) {
538
613
  return "";
539
614
  }
540
615
 
@@ -564,7 +639,13 @@ function extractStandard(dxf: string): string | undefined {
564
639
  return version ? version[1] : undefined;
565
640
  }
566
641
 
567
- function remapHandleIds(entities: string | undefined, blocks: string | undefined, initHandleMap: Map<string, string>, rootModelHandle: string, newHandle: () => string): [string | undefined, string] {
642
+ function remapHandleIds(
643
+ entities: string | undefined,
644
+ blocks: string | undefined,
645
+ initHandleMap: Map<string, string>,
646
+ rootModelHandle: string,
647
+ newHandle: () => string
648
+ ): [string | undefined, string] {
568
649
  if (entities === undefined || blocks === undefined) {
569
650
  return [undefined, ""];
570
651
  }
@@ -582,16 +663,16 @@ function remapHandleIds(entities: string | undefined, blocks: string | undefined
582
663
  while (i < lines.length) {
583
664
  const groupCode = lines[i].trim();
584
665
  const next = (lines[i + 1] ?? "").trim();
585
-
586
- if(groupCode === "0" && ents.has(next)) {
666
+
667
+ if (groupCode === "0" && ents.has(next)) {
587
668
  inEntityHeader = true;
588
669
  }
589
670
 
590
- if(groupCode === "100" && next === "AcDbEntity") {
671
+ if (groupCode === "100" && next === "AcDbEntity") {
591
672
  inEntityHeader = false;
592
673
  }
593
674
 
594
- if (inEntityHeader && (i + 1 < lines.length && handleGroupCodes.has(groupCode))) {
675
+ if (inEntityHeader && i + 1 < lines.length && handleGroupCodes.has(groupCode)) {
595
676
  const value = lines[i + 1].trim();
596
677
  if (/^[0-9A-Fa-f]+$/.test(value)) {
597
678
  out.push(groupCode);
@@ -618,7 +699,7 @@ function remapHandleIds(entities: string | undefined, blocks: string | undefined
618
699
  }
619
700
 
620
701
  function scaleDxf(dxfString: string | undefined, sx: number, sy: number): string | undefined {
621
- if(!dxfString) {
702
+ if (!dxfString) {
622
703
  return undefined;
623
704
  }
624
705
 
@@ -629,11 +710,11 @@ function scaleDxf(dxfString: string | undefined, sx: number, sy: number): string
629
710
  const scaledLines: string[] = [];
630
711
 
631
712
  let currentAcDbEntity = "";
632
- for (let i = 0; i < lines.length; i+=2) {
713
+ for (let i = 0; i < lines.length; i += 2) {
633
714
  const codeLine = lines[i];
634
715
  const valueLine = lines[i + 1];
635
716
 
636
- if(codeLine === "100") {
717
+ if (codeLine === "100") {
637
718
  currentAcDbEntity = valueLine;
638
719
  }
639
720
 
@@ -650,17 +731,19 @@ function scaleDxf(dxfString: string | undefined, sx: number, sy: number): string
650
731
  if (!isNaN(num)) {
651
732
  value = (num * sx).toString();
652
733
  }
653
- } else if(yCoordinateCodes.has(code)) {
734
+ } else if (yCoordinateCodes.has(code)) {
654
735
  const num = parseFloat(valueLine);
655
736
  if (!isNaN(num)) {
656
737
  value = (num * sy).toString();
657
738
  }
658
- } else if(code === 40 && currentAcDbEntity === "AcDbText") { //font size
739
+ } else if (code === 40 && currentAcDbEntity === "AcDbText") {
740
+ //font size
659
741
  const num = parseInt(valueLine.trim(), 10);
660
742
  if (!isNaN(num)) {
661
743
  value = Math.round((num + 2) * Math.max(sx, sy)).toString();
662
744
  }
663
- } else if((code === 41 || code === 40) && currentAcDbEntity === "AcDb2dPolyline") { // stroke thickness
745
+ } else if ((code === 41 || code === 40) && currentAcDbEntity === "AcDb2dPolyline") {
746
+ // stroke thickness
664
747
  const num = parseInt(valueLine.trim(), 10);
665
748
  if (!isNaN(num)) {
666
749
  value = (num * Math.max(sx, sy)).toString();
@@ -686,12 +769,17 @@ function getScale(extents: DxfExtents, c: BinaryImage): { readonly x: number; re
686
769
  const sy = targetH / srcH;
687
770
  return {
688
771
  x: sx,
689
- y: sy
690
- }
772
+ y: sy,
773
+ };
691
774
  }
692
775
 
693
- function createExternalInsert(ins: DxfInsert, c: BinaryImage, size: Size, modelSpaceHandle: string, newHandle: () => string): string {
694
-
776
+ function createExternalInsert(
777
+ ins: DxfInsert,
778
+ c: BinaryImage,
779
+ size: Size,
780
+ modelSpaceHandle: string,
781
+ newHandle: () => string
782
+ ): string {
695
783
  const minX = Math.min(c.topLeft.x, c.bottomRight.x);
696
784
  const maxY = Math.max(c.topLeft.y, c.bottomRight.y);
697
785
  const x = minX - ins.extents.minX;
@@ -791,7 +879,6 @@ function createLTypeTable(newHandle: () => string): string {
791
879
  }
792
880
 
793
881
  function createLayerTable(newHandle: () => string): string {
794
-
795
882
  const layers = ["0", "1", "A3D", "Lines", "Text"];
796
883
  const layerId = newHandle();
797
884
 
@@ -803,7 +890,7 @@ function createLayerTable(newHandle: () => string): string {
803
890
  table += "100\nAcDbSymbolTable\n";
804
891
  table += "70\n" + layers.length.toString() + "\n";
805
892
 
806
- for(const layer of layers) {
893
+ for (const layer of layers) {
807
894
  table += "0\nLAYER\n";
808
895
  table += "5\n" + newHandle() + "\n";
809
896
  table += "330\n" + layerId + "\n";
@@ -952,11 +1039,16 @@ function createDimStyleTable(newHandle: () => string): string {
952
1039
  return table;
953
1040
  }
954
1041
 
955
- function createBlockRecordsTable(modelSpaceHandle: string, paperSpaceHandle: string, blockRecords: ReadonlyArray<BlockRecord>, newHandle: () => string): string {
1042
+ function createBlockRecordsTable(
1043
+ modelSpaceHandle: string,
1044
+ paperSpaceHandle: string,
1045
+ blockRecords: ReadonlyArray<BlockRecord>,
1046
+ newHandle: () => string
1047
+ ): string {
956
1048
  const br: ReadonlyArray<BlockRecord> = [
957
1049
  { id: modelSpaceHandle, name: "*Model_Space" },
958
1050
  { id: paperSpaceHandle, name: "*Paper_Space" },
959
- ...blockRecords
1051
+ ...blockRecords,
960
1052
  ];
961
1053
 
962
1054
  const blockRecordsHandle = newHandle();
@@ -969,7 +1061,7 @@ function createBlockRecordsTable(modelSpaceHandle: string, paperSpaceHandle: str
969
1061
  table += "100\nAcDbSymbolTable\n";
970
1062
  table += "70\n" + br.length.toString() + "\n";
971
1063
 
972
- for(const record of br) {
1064
+ for (const record of br) {
973
1065
  table += "0\nBLOCK_RECORD\n";
974
1066
  table += "5\n" + record.id + "\n";
975
1067
  table += "330\n" + blockRecordsHandle + "\n";
@@ -985,7 +1077,12 @@ function createBlockRecordsTable(modelSpaceHandle: string, paperSpaceHandle: str
985
1077
  return table;
986
1078
  }
987
1079
 
988
- function createObjects(modelSpaceHandle: string, paperSpaceHandle: string, root: AbstractImage, newHandle: () => string): string {
1080
+ function createObjects(
1081
+ modelSpaceHandle: string,
1082
+ paperSpaceHandle: string,
1083
+ root: AbstractImage,
1084
+ newHandle: () => string
1085
+ ): string {
989
1086
  let objects = "";
990
1087
  const rootDictId = newHandle();
991
1088
  const groupDictId = newHandle();
@@ -1012,7 +1109,8 @@ function createObjects(modelSpaceHandle: string, paperSpaceHandle: string, root:
1012
1109
  objects += "0\nLAYOUT\n5\n" + layoutModelId + "\n102\n{ACAD_REACTORS\n330\n" + layoutDictId + "\n102\n}\n";
1013
1110
  objects += "330\n" + layoutDictId + "\n100\nAcDbPlotSettings\n1\n\n2\nnone_device\n4\n\n6\n\n";
1014
1111
  objects += "40\n0.0\n41\n0.0\n42\n0.0\n43\n0.0\n44\n0.0\n45\n0.0\n46\n0.0\n47\n0.0\n48\n0.0\n49\n0.0\n";
1015
- objects += "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n70\n1712\n72\n0\n73\n0\n74\n0\n7\n\n75\n0\n147\n1.0\n148\n0.0\n149\n0.0\n";
1112
+ objects +=
1113
+ "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n70\n1712\n72\n0\n73\n0\n74\n0\n7\n\n75\n0\n147\n1.0\n148\n0.0\n149\n0.0\n";
1016
1114
  objects += "100\nAcDbLayout\n1\nModel\n70\n1\n71\n0\n";
1017
1115
  objects += "10\n0.0\n20\n0.0\n11\n" + root.size.width + "\n21\n" + root.size.height + "\n";
1018
1116
  objects += "12\n0.0\n22\n0.0\n32\n0.0\n14\n0.0\n24\n0.0\n34\n0.0\n";
@@ -1024,7 +1122,8 @@ function createObjects(modelSpaceHandle: string, paperSpaceHandle: string, root:
1024
1122
  objects += "0\nLAYOUT\n5\n" + layoutPaperId + "\n102\n{ACAD_REACTORS\n330\n" + layoutDictId + "\n102\n}\n";
1025
1123
  objects += "330\n" + layoutDictId + "\n100\nAcDbPlotSettings\n1\n\n2\nnone_device\n4\n\n6\n\n";
1026
1124
  objects += "40\n0.0\n41\n0.0\n42\n0.0\n43\n0.0\n44\n0.0\n45\n0.0\n46\n0.0\n47\n0.0\n48\n0.0\n49\n0.0\n";
1027
- objects += "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n70\n688\n72\n0\n73\n0\n74\n5\n7\n\n75\n16\n147\n1.0\n148\n0.0\n149\n0.0\n";
1125
+ objects +=
1126
+ "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n70\n688\n72\n0\n73\n0\n74\n5\n7\n\n75\n16\n147\n1.0\n148\n0.0\n149\n0.0\n";
1028
1127
  objects += "100\nAcDbLayout\n1\nLayout1\n70\n1\n71\n1\n";
1029
1128
  objects += "10\n0.0\n20\n0.0\n11\n" + root.size.width + "\n21\n" + root.size.height + "\n";
1030
1129
  objects += "12\n0.0\n22\n0.0\n32\n0.0\n14\n1.0E+20\n24\n1.0E+20\n34\n1.0E+20\n";
@@ -1069,7 +1168,7 @@ function createHatch(
1069
1168
  height: number,
1070
1169
  newHandle: () => string
1071
1170
  ): string {
1072
- const invertedPoints = points.map(p => ({ x: p.x, y: invert(p.y, height) }));
1171
+ const invertedPoints = points.map((p) => ({ x: p.x, y: invert(p.y, height) }));
1073
1172
  const cx = invertedPoints.reduce((s, p) => s + p.x, 0) / invertedPoints.length;
1074
1173
  const cy = invertedPoints.reduce((s, p) => s + p.y, 0) / invertedPoints.length;
1075
1174
 
@@ -1108,21 +1207,31 @@ function createHatch(
1108
1207
  return hatch;
1109
1208
  }
1110
1209
 
1111
- function createPolygon(modelSpaceHandle: string, layer: string, points: ReadonlyArray<Point>, strokeColor: Color, fillColor: Color, strokeThickness: number | undefined, height: number, options: Required<DxfOptions>, newHandle: () => string): string {
1210
+ function createPolygon(
1211
+ modelSpaceHandle: string,
1212
+ layer: string,
1213
+ points: ReadonlyArray<Point>,
1214
+ strokeColor: Color,
1215
+ fillColor: Color,
1216
+ strokeThickness: number | undefined,
1217
+ height: number,
1218
+ options: Required<DxfOptions>,
1219
+ newHandle: () => string
1220
+ ): string {
1112
1221
  const handle = newHandle();
1113
1222
  let polygon = "";
1114
1223
 
1115
- if(options.fillPolygons) {
1224
+ if (options.fillPolygons) {
1116
1225
  polygon += createHatch(modelSpaceHandle, layer, fillColor, points, height, newHandle);
1117
1226
  }
1118
-
1227
+
1119
1228
  polygon += "0\nPOLYLINE\n";
1120
1229
  polygon += "5\n" + handle + "\n";
1121
1230
  polygon += "330\n" + modelSpaceHandle + "\n";
1122
1231
  polygon += "100\nAcDbEntity\n";
1123
1232
  polygon += "8\n" + layer.toString() + "\n";
1124
1233
 
1125
- if(options.useColor && !isBlack(strokeColor)) {
1234
+ if (options.useColor && !isBlack(strokeColor)) {
1126
1235
  polygon += "60\n0\n";
1127
1236
  polygon += "62\n256\n";
1128
1237
  polygon += "420\n" + colorToInteger(strokeColor) + "\n";
@@ -1133,7 +1242,7 @@ function createPolygon(modelSpaceHandle: string, layer: string, points: Readonly
1133
1242
  polygon += "70\n1\n";
1134
1243
  polygon += "10\n0.0\n20\n0.0\n30\n0.0\n";
1135
1244
 
1136
- if(options.useStrokeThickness && strokeThickness && strokeThickness >= Number.EPSILON) {
1245
+ if (options.useStrokeThickness && strokeThickness && strokeThickness >= Number.EPSILON) {
1137
1246
  polygon += "40\n" + strokeThickness.toPrecision(4) + "\n";
1138
1247
  polygon += "41\n" + strokeThickness.toPrecision(4) + "\n";
1139
1248
  }
@@ -1161,16 +1270,16 @@ function createPolygon(modelSpaceHandle: string, layer: string, points: Readonly
1161
1270
  return polygon;
1162
1271
  }
1163
1272
 
1164
- function handleGenerator(): { newHandle: () => string, currentHandle: () => string } {
1273
+ function handleGenerator(): { newHandle: () => string; currentHandle: () => string } {
1165
1274
  let index = 0x1000;
1166
1275
  return {
1167
1276
  newHandle: () => (index++).toString(16).toUpperCase(),
1168
- currentHandle: () => (index).toString(16).toUpperCase(),
1277
+ currentHandle: () => index.toString(16).toUpperCase(),
1169
1278
  };
1170
1279
  }
1171
1280
 
1172
1281
  function randomID(): string {
1173
- return "xxxxxxxxxxxxxxxx".replaceAll("x", () => (Math.round(Math.random() * 16)).toString(16)).toLocaleUpperCase();
1282
+ return "xxxxxxxxxxxxxxxx".replaceAll("x", () => Math.round(Math.random() * 16).toString(16)).toLocaleUpperCase();
1174
1283
  }
1175
1284
 
1176
1285
  function isBlack(color: Color): boolean {
@@ -1179,8 +1288,8 @@ function isBlack(color: Color): boolean {
1179
1288
 
1180
1289
  function colorToInteger(color: Color): number {
1181
1290
  const colorAsInt = (color.r << 16) + (color.g << 8) + color.b;
1182
- if(Number.isNaN(colorAsInt)) {
1291
+ if (Number.isNaN(colorAsInt)) {
1183
1292
  return 0;
1184
1293
  }
1185
1294
  return colorAsInt;
1186
- }
1295
+ }
@@ -4,6 +4,7 @@ import { AbstractImage } from "../model/abstract-image.js";
4
4
  import { createPoint, Point } from "../model/point.js";
5
5
  import { AbstractFontWeight, BinaryFormat, Component, GrowthDirection, ImageData } from "../model/component.js";
6
6
  import { Color } from "../model/color.js";
7
+ import { Optional } from "../model/shared.js";
7
8
 
8
9
  export interface ReactSvgCallbacks {
9
10
  readonly onClick?: MouseCallback;
@@ -14,13 +15,22 @@ export interface ReactSvgCallbacks {
14
15
 
15
16
  export type MouseCallback = (id: string | undefined, point: Point) => void;
16
17
 
18
+ export type ReactSvgOptions = {
19
+ readonly imageDataByUrl: Record<string, `data:image/${string},${string}`>;
20
+ };
21
+
17
22
  export function ReactSvg({
18
23
  image,
19
24
  callbacks,
25
+ options,
20
26
  }: {
21
27
  readonly image: AbstractImage;
22
28
  readonly callbacks?: ReactSvgCallbacks;
29
+ readonly options?: Optional<ReactSvgOptions>;
23
30
  }): React.JSX.Element {
31
+ const opts: ReactSvgOptions = {
32
+ imageDataByUrl: options?.imageDataByUrl ?? {},
33
+ };
24
34
  const cb = callbacks || {};
25
35
  const id = "ai_root";
26
36
  return (
@@ -35,7 +45,7 @@ export function ReactSvg({
35
45
  onContextMenu={_callback(cb.onContextMenu, id)}
36
46
  >
37
47
  {image.components.map((c, i) => (
38
- <JsxComponent key={i} component={c} />
48
+ <JsxComponent key={i} component={c} options={opts} />
39
49
  ))}
40
50
  </svg>
41
51
  );
@@ -74,18 +84,24 @@ function getIdAttr(target: Element | undefined, rootId: string): string | undefi
74
84
  return parts[1];
75
85
  }
76
86
 
77
- function JsxComponent({ component }: { readonly component: Component }): React.JSX.Element {
87
+ function JsxComponent({
88
+ component,
89
+ options,
90
+ }: {
91
+ readonly component: Component;
92
+ readonly options: ReactSvgOptions;
93
+ }): React.JSX.Element {
78
94
  switch (component.type) {
79
95
  case "group":
80
96
  return (
81
97
  <g name={component.name}>
82
98
  {component.children.flatMap((c, i) => (
83
- <JsxComponent key={i} component={c} />
99
+ <JsxComponent key={i} component={c} options={options} />
84
100
  ))}
85
101
  </g>
86
102
  );
87
103
  case "binaryimage":
88
- const url = getImageUrl(component.format, component.data);
104
+ const url = getImageUrl(component.format, component.data, options);
89
105
  return (
90
106
  <image
91
107
  x={component.topLeft.x}
@@ -270,9 +286,9 @@ function getTextFontWeight(fontWeight: AbstractFontWeight): React.CSSProperties[
270
286
  }
271
287
  }
272
288
 
273
- function getImageUrl(format: BinaryFormat, data: ImageData): string {
289
+ function getImageUrl(format: BinaryFormat, data: ImageData, options: ReactSvgOptions): string {
274
290
  if (data.type === "url") {
275
- return data.url;
291
+ return options.imageDataByUrl[data.url] ?? data.url;
276
292
  } else if (format === "png") {
277
293
  const base64 = fromByteArray(data.bytes);
278
294
  return `data:image/png;base64,${base64}`;