@twinfinity/printing 6.0.1-ci.28952-beta → 6.0.2-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/BuildInfo.js +3 -3
- package/dist/BuildInfo.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/analyzeTemplate.js.map +1 -1
- package/dist/layout/clipUtils.js.map +1 -1
- package/dist/layout/colorConstants.js.map +1 -1
- package/dist/layout/elementKinds.js.map +1 -1
- package/dist/layout/exportDrawing.js.map +1 -1
- package/dist/layout/filters.js.map +1 -1
- package/dist/layout/geometryConstants.js.map +1 -1
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/itemBuilders.js.map +1 -1
- package/dist/layout/labelsRender.js.map +1 -1
- package/dist/layout/legendConstants.js.map +1 -1
- package/dist/layout/legendRender.js.map +1 -1
- package/dist/layout/legendRows.js.map +1 -1
- package/dist/layout/legendUtils.js.map +1 -1
- package/dist/layout/metadata.js.map +1 -1
- package/dist/layout/model.js.map +1 -1
- package/dist/layout/options.js.map +1 -1
- package/dist/layout/presets.js.map +1 -1
- package/dist/layout/qrRender.js.map +1 -1
- package/dist/layout/renderDrawing.js.map +1 -1
- package/dist/layout/renderUtils.js.map +1 -1
- package/dist/layout/sectionValidation.js.map +1 -1
- package/dist/layout/templateVariables.js.map +1 -1
- package/dist/layout/typeGuards.js.map +1 -1
- package/dist/layout/types.js.map +1 -1
- package/dist/layout/utils.js.map +1 -1
- package/dist/layout/validation.js.map +1 -1
- package/dist/layout/validationHelpers.js.map +1 -1
- package/dist/layout/validationUtils.js.map +1 -1
- package/dist/layout/viewportPrep.js.map +1 -1
- package/dist/layout/viewportRender.js.map +1 -1
- package/dist/layout/viewportUtils.js.map +1 -1
- package/dist/layout/warnings.js.map +1 -1
- package/dist/printToPdf.js.map +1 -1
- package/dist/printToSvg.js.map +1 -1
- package/dist/sections.js.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/BuildInfo.ts +41 -41
- package/src/index.ts +10 -10
- package/src/layout/analyzeTemplate.ts +218 -218
- package/src/layout/clipUtils.ts +193 -193
- package/src/layout/colorConstants.ts +16 -16
- package/src/layout/elementKinds.ts +35 -35
- package/src/layout/exportDrawing.ts +206 -206
- package/src/layout/filters.ts +80 -80
- package/src/layout/geometryConstants.ts +11 -11
- package/src/layout/index.ts +671 -671
- package/src/layout/itemBuilders.ts +159 -159
- package/src/layout/labelsRender.ts +170 -170
- package/src/layout/legendConstants.ts +11 -11
- package/src/layout/legendRender.ts +543 -543
- package/src/layout/legendRows.ts +62 -62
- package/src/layout/legendUtils.ts +144 -144
- package/src/layout/metadata.ts +210 -210
- package/src/layout/model.ts +372 -372
- package/src/layout/options.ts +17 -17
- package/src/layout/presets.ts +51 -51
- package/src/layout/qrRender.ts +126 -126
- package/src/layout/qrcode.d.ts +2 -2
- package/src/layout/renderDrawing.ts +710 -710
- package/src/layout/renderUtils.ts +138 -138
- package/src/layout/sectionValidation.ts +2 -2
- package/src/layout/templateVariables.ts +317 -317
- package/src/layout/typeGuards.ts +9 -9
- package/src/layout/types.ts +835 -835
- package/src/layout/utils.ts +85 -85
- package/src/layout/validation.ts +392 -392
- package/src/layout/validationHelpers.ts +59 -59
- package/src/layout/validationUtils.ts +6 -6
- package/src/layout/viewportPrep.ts +351 -351
- package/src/layout/viewportRender.ts +442 -442
- package/src/layout/viewportUtils.ts +50 -50
- package/src/layout/warnings.ts +3 -3
- package/src/printToPdf.ts +108 -108
- package/src/printToSvg.ts +188 -188
- package/src/sections.ts +1019 -1019
- package/src/types.ts +49 -49
|
@@ -1,206 +1,206 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drawing export utilities for converting SVG markup to various formats.
|
|
3
|
-
*
|
|
4
|
-
* This module provides functions for exporting drawings to PDF and SVG files.
|
|
5
|
-
* These are pure functions that take SVG markup and export options, with no
|
|
6
|
-
* dependencies on BIM viewer APIs or application state.
|
|
7
|
-
*
|
|
8
|
-
* @module exportDrawing
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { exportSectionToPdf, exportSectionToSvg } from '../printToPdf';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Extracts page dimensions from SVG markup.
|
|
15
|
-
* Parses the width/height attributes from the root <svg> element.
|
|
16
|
-
*
|
|
17
|
-
* @param svg - SVG markup string
|
|
18
|
-
* @returns Page dimensions in millimeters
|
|
19
|
-
* @throws {Error} If SVG dimensions cannot be parsed
|
|
20
|
-
*/
|
|
21
|
-
function extractPageSizeFromSvg(svg: string): { widthMm: number; heightMm: number } {
|
|
22
|
-
// Match width and height attributes (supports both "420mm" and "420" formats)
|
|
23
|
-
const widthMatch = svg.match(/width="([0-9.]+)(?:mm)?"/);
|
|
24
|
-
const heightMatch = svg.match(/height="([0-9.]+)(?:mm)?"/);
|
|
25
|
-
|
|
26
|
-
if (!widthMatch || !heightMatch) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
'Cannot extract page size from SVG. SVG must have width and height attributes. ' +
|
|
29
|
-
'Provide pageSize option explicitly or ensure SVG contains width/height attributes.'
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const widthMm = parseFloat(widthMatch[1]);
|
|
34
|
-
const heightMm = parseFloat(heightMatch[1]);
|
|
35
|
-
|
|
36
|
-
if (!Number.isFinite(widthMm) || !Number.isFinite(heightMm) || widthMm <= 0 || heightMm <= 0) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
`Invalid page dimensions extracted from SVG: ${widthMm}mm × ${heightMm}mm. ` +
|
|
39
|
-
'Provide pageSize option explicitly.'
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { widthMm, heightMm };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Options for exporting a drawing to PDF format.
|
|
48
|
-
*/
|
|
49
|
-
export interface ExportPdfOptions {
|
|
50
|
-
/**
|
|
51
|
-
* Output filename (with or without .pdf extension).
|
|
52
|
-
* Default: "drawing.pdf"
|
|
53
|
-
*/
|
|
54
|
-
filename?: string;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Optional page dimensions in millimeters.
|
|
58
|
-
* If not provided, automatically extracted from SVG width/height attributes.
|
|
59
|
-
* Only needed if you want to override the SVG's embedded page size.
|
|
60
|
-
*/
|
|
61
|
-
pageSize?: {
|
|
62
|
-
widthMm: number;
|
|
63
|
-
heightMm: number;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* If true, opens PDF in a new browser tab.
|
|
68
|
-
* If false, downloads the PDF file.
|
|
69
|
-
* Default: false (download)
|
|
70
|
-
*/
|
|
71
|
-
openInNewTab?: boolean;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Options for exporting a drawing to SVG format.
|
|
76
|
-
*/
|
|
77
|
-
export interface ExportSvgOptions {
|
|
78
|
-
/**
|
|
79
|
-
* Output filename (with or without .svg extension).
|
|
80
|
-
* Default: "drawing.svg"
|
|
81
|
-
*/
|
|
82
|
-
filename?: string;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* If true, opens SVG in a new browser tab.
|
|
86
|
-
* If false, downloads the SVG file.
|
|
87
|
-
* Default: false (download)
|
|
88
|
-
*/
|
|
89
|
-
openInNewTab?: boolean;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Exports an SVG drawing to PDF format.
|
|
94
|
-
*
|
|
95
|
-
* Converts the SVG markup to a vector PDF using jsPDF and svg2pdf.js.
|
|
96
|
-
* The PDF maintains full vector quality with no rasterization.
|
|
97
|
-
*
|
|
98
|
-
* **Page size is automatically extracted from the SVG** (width/height attributes),
|
|
99
|
-
* so you don't need to specify it manually. The template's page dimensions are
|
|
100
|
-
* embedded in the SVG markup by renderDrawing() or renderLayout().
|
|
101
|
-
*
|
|
102
|
-
* **Features:**
|
|
103
|
-
* - ✅ Vector PDF output (scalable, searchable text)
|
|
104
|
-
* - ✅ Preserves colors, strokes, fills, fonts
|
|
105
|
-
* - ✅ No quality loss (unlike raster export)
|
|
106
|
-
* - ✅ Small file size (vector graphics)
|
|
107
|
-
* - ✅ Automatic page size detection from SVG
|
|
108
|
-
*
|
|
109
|
-
* **Use cases:**
|
|
110
|
-
* - Print-ready construction documents
|
|
111
|
-
* - Archival drawings
|
|
112
|
-
* - Sharing with clients/contractors
|
|
113
|
-
* - CAD import (vector format)
|
|
114
|
-
*
|
|
115
|
-
* @param svg - SVG markup string (from renderDrawing() or renderLayout())
|
|
116
|
-
* @param options - Export options (filename, optional page size override, open vs download)
|
|
117
|
-
* @returns Promise that resolves when export completes
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* ```typescript
|
|
121
|
-
* // Basic usage - page size is automatically extracted from SVG
|
|
122
|
-
* const { svg } = await renderDrawing({ template, models });
|
|
123
|
-
* await exportToPdf(svg, {
|
|
124
|
-
* filename: "floor-plan.pdf",
|
|
125
|
-
* openInNewTab: true
|
|
126
|
-
* });
|
|
127
|
-
*
|
|
128
|
-
* // With custom filename
|
|
129
|
-
* await exportToPdf(svg, {
|
|
130
|
-
* filename: `${template.id}-${new Date().toISOString().slice(0,10)}.pdf`
|
|
131
|
-
* });
|
|
132
|
-
*
|
|
133
|
-
* // Override page size if needed (rare)
|
|
134
|
-
* await exportToPdf(svg, {
|
|
135
|
-
* filename: "custom.pdf",
|
|
136
|
-
* pageSize: { widthMm: 420, heightMm: 297 }, // A3 landscape override
|
|
137
|
-
* openInNewTab: false
|
|
138
|
-
* });
|
|
139
|
-
* ```
|
|
140
|
-
*
|
|
141
|
-
* @throws {Error} If PDF generation fails or SVG page size cannot be extracted
|
|
142
|
-
*/
|
|
143
|
-
export async function exportToPdf(svg: string, options: ExportPdfOptions = {}): Promise<void> {
|
|
144
|
-
const filename = options.filename ?? 'drawing.pdf';
|
|
145
|
-
const filenameWithExt = filename.endsWith('.pdf') ? filename : `${filename}.pdf`;
|
|
146
|
-
|
|
147
|
-
// Extract page size from SVG if not provided
|
|
148
|
-
const pageSize = options.pageSize ?? extractPageSizeFromSvg(svg);
|
|
149
|
-
|
|
150
|
-
await exportSectionToPdf({
|
|
151
|
-
svgMarkup: svg,
|
|
152
|
-
fileName: filenameWithExt,
|
|
153
|
-
pageSizeMm: pageSize as any,
|
|
154
|
-
openInNewTab: options.openInNewTab ?? false
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Exports an SVG drawing to SVG file format.
|
|
160
|
-
*
|
|
161
|
-
* Saves or displays the SVG markup as a standalone .svg file.
|
|
162
|
-
* The SVG can be opened in browsers, vector editors (Inkscape, Illustrator),
|
|
163
|
-
* or imported into CAD software.
|
|
164
|
-
*
|
|
165
|
-
* **Features:**
|
|
166
|
-
* - ✅ Native vector format (editable in SVG editors)
|
|
167
|
-
* - ✅ Smallest file size (XML text)
|
|
168
|
-
* - ✅ Web-friendly (can embed in HTML)
|
|
169
|
-
* - ✅ Full styling preserved
|
|
170
|
-
*
|
|
171
|
-
* **Use cases:**
|
|
172
|
-
* - Web display (embed in HTML)
|
|
173
|
-
* - Vector editing (Inkscape, Illustrator)
|
|
174
|
-
* - Further processing (scripting, automation)
|
|
175
|
-
* - Archival (open standard format)
|
|
176
|
-
*
|
|
177
|
-
* @param svg - SVG markup string (from renderDrawing())
|
|
178
|
-
* @param options - Export options (filename, open vs download)
|
|
179
|
-
* @returns Promise that resolves when export completes
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* ```typescript
|
|
183
|
-
* // Basic usage
|
|
184
|
-
* const { svg } = await renderDrawing({ template, models });
|
|
185
|
-
* await exportToSvg(svg, {
|
|
186
|
-
* filename: "floor-plan.svg",
|
|
187
|
-
* openInNewTab: true
|
|
188
|
-
* });
|
|
189
|
-
*
|
|
190
|
-
* // Download with custom name
|
|
191
|
-
* await exportToSvg(svg, {
|
|
192
|
-
* filename: `${template.id}-${Date.now()}.svg`,
|
|
193
|
-
* openInNewTab: false
|
|
194
|
-
* });
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
export function exportToSvg(svg: string, options: ExportSvgOptions = {}): void {
|
|
198
|
-
const filename = options.filename ?? 'drawing.svg';
|
|
199
|
-
const filenameWithExt = filename.endsWith('.svg') ? filename : `${filename}.svg`;
|
|
200
|
-
|
|
201
|
-
exportSectionToSvg({
|
|
202
|
-
svgMarkup: svg,
|
|
203
|
-
fileName: filenameWithExt,
|
|
204
|
-
openInNewTab: options.openInNewTab ?? false
|
|
205
|
-
});
|
|
206
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Drawing export utilities for converting SVG markup to various formats.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for exporting drawings to PDF and SVG files.
|
|
5
|
+
* These are pure functions that take SVG markup and export options, with no
|
|
6
|
+
* dependencies on BIM viewer APIs or application state.
|
|
7
|
+
*
|
|
8
|
+
* @module exportDrawing
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { exportSectionToPdf, exportSectionToSvg } from '../printToPdf';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extracts page dimensions from SVG markup.
|
|
15
|
+
* Parses the width/height attributes from the root <svg> element.
|
|
16
|
+
*
|
|
17
|
+
* @param svg - SVG markup string
|
|
18
|
+
* @returns Page dimensions in millimeters
|
|
19
|
+
* @throws {Error} If SVG dimensions cannot be parsed
|
|
20
|
+
*/
|
|
21
|
+
function extractPageSizeFromSvg(svg: string): { widthMm: number; heightMm: number } {
|
|
22
|
+
// Match width and height attributes (supports both "420mm" and "420" formats)
|
|
23
|
+
const widthMatch = svg.match(/width="([0-9.]+)(?:mm)?"/);
|
|
24
|
+
const heightMatch = svg.match(/height="([0-9.]+)(?:mm)?"/);
|
|
25
|
+
|
|
26
|
+
if (!widthMatch || !heightMatch) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'Cannot extract page size from SVG. SVG must have width and height attributes. ' +
|
|
29
|
+
'Provide pageSize option explicitly or ensure SVG contains width/height attributes.'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const widthMm = parseFloat(widthMatch[1]);
|
|
34
|
+
const heightMm = parseFloat(heightMatch[1]);
|
|
35
|
+
|
|
36
|
+
if (!Number.isFinite(widthMm) || !Number.isFinite(heightMm) || widthMm <= 0 || heightMm <= 0) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Invalid page dimensions extracted from SVG: ${widthMm}mm × ${heightMm}mm. ` +
|
|
39
|
+
'Provide pageSize option explicitly.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { widthMm, heightMm };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Options for exporting a drawing to PDF format.
|
|
48
|
+
*/
|
|
49
|
+
export interface ExportPdfOptions {
|
|
50
|
+
/**
|
|
51
|
+
* Output filename (with or without .pdf extension).
|
|
52
|
+
* Default: "drawing.pdf"
|
|
53
|
+
*/
|
|
54
|
+
filename?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Optional page dimensions in millimeters.
|
|
58
|
+
* If not provided, automatically extracted from SVG width/height attributes.
|
|
59
|
+
* Only needed if you want to override the SVG's embedded page size.
|
|
60
|
+
*/
|
|
61
|
+
pageSize?: {
|
|
62
|
+
widthMm: number;
|
|
63
|
+
heightMm: number;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* If true, opens PDF in a new browser tab.
|
|
68
|
+
* If false, downloads the PDF file.
|
|
69
|
+
* Default: false (download)
|
|
70
|
+
*/
|
|
71
|
+
openInNewTab?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Options for exporting a drawing to SVG format.
|
|
76
|
+
*/
|
|
77
|
+
export interface ExportSvgOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Output filename (with or without .svg extension).
|
|
80
|
+
* Default: "drawing.svg"
|
|
81
|
+
*/
|
|
82
|
+
filename?: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* If true, opens SVG in a new browser tab.
|
|
86
|
+
* If false, downloads the SVG file.
|
|
87
|
+
* Default: false (download)
|
|
88
|
+
*/
|
|
89
|
+
openInNewTab?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Exports an SVG drawing to PDF format.
|
|
94
|
+
*
|
|
95
|
+
* Converts the SVG markup to a vector PDF using jsPDF and svg2pdf.js.
|
|
96
|
+
* The PDF maintains full vector quality with no rasterization.
|
|
97
|
+
*
|
|
98
|
+
* **Page size is automatically extracted from the SVG** (width/height attributes),
|
|
99
|
+
* so you don't need to specify it manually. The template's page dimensions are
|
|
100
|
+
* embedded in the SVG markup by renderDrawing() or renderLayout().
|
|
101
|
+
*
|
|
102
|
+
* **Features:**
|
|
103
|
+
* - ✅ Vector PDF output (scalable, searchable text)
|
|
104
|
+
* - ✅ Preserves colors, strokes, fills, fonts
|
|
105
|
+
* - ✅ No quality loss (unlike raster export)
|
|
106
|
+
* - ✅ Small file size (vector graphics)
|
|
107
|
+
* - ✅ Automatic page size detection from SVG
|
|
108
|
+
*
|
|
109
|
+
* **Use cases:**
|
|
110
|
+
* - Print-ready construction documents
|
|
111
|
+
* - Archival drawings
|
|
112
|
+
* - Sharing with clients/contractors
|
|
113
|
+
* - CAD import (vector format)
|
|
114
|
+
*
|
|
115
|
+
* @param svg - SVG markup string (from renderDrawing() or renderLayout())
|
|
116
|
+
* @param options - Export options (filename, optional page size override, open vs download)
|
|
117
|
+
* @returns Promise that resolves when export completes
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // Basic usage - page size is automatically extracted from SVG
|
|
122
|
+
* const { svg } = await renderDrawing({ template, models });
|
|
123
|
+
* await exportToPdf(svg, {
|
|
124
|
+
* filename: "floor-plan.pdf",
|
|
125
|
+
* openInNewTab: true
|
|
126
|
+
* });
|
|
127
|
+
*
|
|
128
|
+
* // With custom filename
|
|
129
|
+
* await exportToPdf(svg, {
|
|
130
|
+
* filename: `${template.id}-${new Date().toISOString().slice(0,10)}.pdf`
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* // Override page size if needed (rare)
|
|
134
|
+
* await exportToPdf(svg, {
|
|
135
|
+
* filename: "custom.pdf",
|
|
136
|
+
* pageSize: { widthMm: 420, heightMm: 297 }, // A3 landscape override
|
|
137
|
+
* openInNewTab: false
|
|
138
|
+
* });
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @throws {Error} If PDF generation fails or SVG page size cannot be extracted
|
|
142
|
+
*/
|
|
143
|
+
export async function exportToPdf(svg: string, options: ExportPdfOptions = {}): Promise<void> {
|
|
144
|
+
const filename = options.filename ?? 'drawing.pdf';
|
|
145
|
+
const filenameWithExt = filename.endsWith('.pdf') ? filename : `${filename}.pdf`;
|
|
146
|
+
|
|
147
|
+
// Extract page size from SVG if not provided
|
|
148
|
+
const pageSize = options.pageSize ?? extractPageSizeFromSvg(svg);
|
|
149
|
+
|
|
150
|
+
await exportSectionToPdf({
|
|
151
|
+
svgMarkup: svg,
|
|
152
|
+
fileName: filenameWithExt,
|
|
153
|
+
pageSizeMm: pageSize as any,
|
|
154
|
+
openInNewTab: options.openInNewTab ?? false
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Exports an SVG drawing to SVG file format.
|
|
160
|
+
*
|
|
161
|
+
* Saves or displays the SVG markup as a standalone .svg file.
|
|
162
|
+
* The SVG can be opened in browsers, vector editors (Inkscape, Illustrator),
|
|
163
|
+
* or imported into CAD software.
|
|
164
|
+
*
|
|
165
|
+
* **Features:**
|
|
166
|
+
* - ✅ Native vector format (editable in SVG editors)
|
|
167
|
+
* - ✅ Smallest file size (XML text)
|
|
168
|
+
* - ✅ Web-friendly (can embed in HTML)
|
|
169
|
+
* - ✅ Full styling preserved
|
|
170
|
+
*
|
|
171
|
+
* **Use cases:**
|
|
172
|
+
* - Web display (embed in HTML)
|
|
173
|
+
* - Vector editing (Inkscape, Illustrator)
|
|
174
|
+
* - Further processing (scripting, automation)
|
|
175
|
+
* - Archival (open standard format)
|
|
176
|
+
*
|
|
177
|
+
* @param svg - SVG markup string (from renderDrawing())
|
|
178
|
+
* @param options - Export options (filename, open vs download)
|
|
179
|
+
* @returns Promise that resolves when export completes
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* // Basic usage
|
|
184
|
+
* const { svg } = await renderDrawing({ template, models });
|
|
185
|
+
* await exportToSvg(svg, {
|
|
186
|
+
* filename: "floor-plan.svg",
|
|
187
|
+
* openInNewTab: true
|
|
188
|
+
* });
|
|
189
|
+
*
|
|
190
|
+
* // Download with custom name
|
|
191
|
+
* await exportToSvg(svg, {
|
|
192
|
+
* filename: `${template.id}-${Date.now()}.svg`,
|
|
193
|
+
* openInNewTab: false
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export function exportToSvg(svg: string, options: ExportSvgOptions = {}): void {
|
|
198
|
+
const filename = options.filename ?? 'drawing.svg';
|
|
199
|
+
const filenameWithExt = filename.endsWith('.svg') ? filename : `${filename}.svg`;
|
|
200
|
+
|
|
201
|
+
exportSectionToSvg({
|
|
202
|
+
svgMarkup: svg,
|
|
203
|
+
fileName: filenameWithExt,
|
|
204
|
+
openInNewTab: options.openInNewTab ?? false
|
|
205
|
+
});
|
|
206
|
+
}
|
package/src/layout/filters.ts
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import { resolveProperty } from './renderUtils';
|
|
2
|
-
|
|
3
|
-
import { isRecord, getProp } from './typeGuards';
|
|
4
|
-
|
|
5
|
-
import type { WhereCondition } from './types';
|
|
6
|
-
|
|
7
|
-
const normalizeClass = (cls: unknown): string | null => (typeof cls === 'string' ? cls.trim().toLowerCase() : null);
|
|
8
|
-
|
|
9
|
-
/** Compare two class strings, treating the 'Ifc' prefix as optional.
|
|
10
|
-
* e.g. "IfcSpace" matches "Space", "space", and "IfcSpace". */
|
|
11
|
-
const classEquals = (a: string | null, b: string | null): boolean => {
|
|
12
|
-
if (a === null || b === null) return false;
|
|
13
|
-
if (a === b) return true;
|
|
14
|
-
// Try with/without Ifc prefix in both directions
|
|
15
|
-
const stripIfc = (s: string): string => (s.startsWith('ifc') ? s.slice(3) : s);
|
|
16
|
-
return stripIfc(a) === stripIfc(b);
|
|
17
|
-
};
|
|
18
|
-
const normalizeValue = (v: unknown): unknown => (typeof v === 'string' ? v.toLowerCase().trim() : v);
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check whether an item's ifcClass matches the given class filter.
|
|
22
|
-
*
|
|
23
|
-
* Uses normalizeClass() for string comparison — works for extracted items
|
|
24
|
-
* where we have a normalized ifcClass string (not a live product).
|
|
25
|
-
*
|
|
26
|
-
* For live products, the caller should use product.class.is() directly.
|
|
27
|
-
*/
|
|
28
|
-
const matchesClass = (itemClass: unknown, classFilter: string | string[] | undefined): boolean => {
|
|
29
|
-
if (classFilter === undefined) return true;
|
|
30
|
-
const classes = Array.isArray(classFilter) ? classFilter : [classFilter];
|
|
31
|
-
const normalized = normalizeClass(itemClass);
|
|
32
|
-
if (normalized === null) return false;
|
|
33
|
-
return classes.some((c) => classEquals(normalizeClass(c), normalized));
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check whether an item matches a `where` condition.
|
|
38
|
-
*
|
|
39
|
-
* Each key in `where` is a short property path (e.g. "BIP.spacetype").
|
|
40
|
-
* Each value is either a scalar (equality check) or an array (any-of check).
|
|
41
|
-
* Multiple keys = AND. String comparison is case-insensitive.
|
|
42
|
-
*/
|
|
43
|
-
const matchesWhere = (item: unknown, where: WhereCondition | undefined): boolean => {
|
|
44
|
-
if (!where) return true;
|
|
45
|
-
for (const [path, expected] of Object.entries(where)) {
|
|
46
|
-
const value = resolveProperty(item, path);
|
|
47
|
-
if (Array.isArray(expected)) {
|
|
48
|
-
const target = normalizeValue(value);
|
|
49
|
-
if (!expected.some((v) => normalizeValue(v) === target)) return false;
|
|
50
|
-
} else {
|
|
51
|
-
if (normalizeValue(value) !== normalizeValue(expected)) return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* V2 filter: match items by class + where conditions.
|
|
59
|
-
*
|
|
60
|
-
* @param item - An object with ifcClass and propertySets
|
|
61
|
-
* @param classFilter - IFC class name(s) to match, or undefined for any
|
|
62
|
-
* @param where - Property conditions (short paths), or undefined for no conditions
|
|
63
|
-
*/
|
|
64
|
-
export const matchesV2Filter = (
|
|
65
|
-
item: unknown,
|
|
66
|
-
classFilter: string | string[] | undefined,
|
|
67
|
-
where: WhereCondition | undefined
|
|
68
|
-
): boolean => {
|
|
69
|
-
if (classFilter !== undefined && isRecord(item)) {
|
|
70
|
-
const itemClass = getProp(item, 'ifcClass');
|
|
71
|
-
const itemClassId = getProp(item, 'ifcClassId');
|
|
72
|
-
if (!matchesClass(itemClass, classFilter) && !matchesClass(itemClassId, classFilter)) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!matchesWhere(item, where)) return false;
|
|
77
|
-
return true;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export { normalizeClass, classEquals, normalizeValue, matchesClass, matchesWhere };
|
|
1
|
+
import { resolveProperty } from './renderUtils';
|
|
2
|
+
|
|
3
|
+
import { isRecord, getProp } from './typeGuards';
|
|
4
|
+
|
|
5
|
+
import type { WhereCondition } from './types';
|
|
6
|
+
|
|
7
|
+
const normalizeClass = (cls: unknown): string | null => (typeof cls === 'string' ? cls.trim().toLowerCase() : null);
|
|
8
|
+
|
|
9
|
+
/** Compare two class strings, treating the 'Ifc' prefix as optional.
|
|
10
|
+
* e.g. "IfcSpace" matches "Space", "space", and "IfcSpace". */
|
|
11
|
+
const classEquals = (a: string | null, b: string | null): boolean => {
|
|
12
|
+
if (a === null || b === null) return false;
|
|
13
|
+
if (a === b) return true;
|
|
14
|
+
// Try with/without Ifc prefix in both directions
|
|
15
|
+
const stripIfc = (s: string): string => (s.startsWith('ifc') ? s.slice(3) : s);
|
|
16
|
+
return stripIfc(a) === stripIfc(b);
|
|
17
|
+
};
|
|
18
|
+
const normalizeValue = (v: unknown): unknown => (typeof v === 'string' ? v.toLowerCase().trim() : v);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check whether an item's ifcClass matches the given class filter.
|
|
22
|
+
*
|
|
23
|
+
* Uses normalizeClass() for string comparison — works for extracted items
|
|
24
|
+
* where we have a normalized ifcClass string (not a live product).
|
|
25
|
+
*
|
|
26
|
+
* For live products, the caller should use product.class.is() directly.
|
|
27
|
+
*/
|
|
28
|
+
const matchesClass = (itemClass: unknown, classFilter: string | string[] | undefined): boolean => {
|
|
29
|
+
if (classFilter === undefined) return true;
|
|
30
|
+
const classes = Array.isArray(classFilter) ? classFilter : [classFilter];
|
|
31
|
+
const normalized = normalizeClass(itemClass);
|
|
32
|
+
if (normalized === null) return false;
|
|
33
|
+
return classes.some((c) => classEquals(normalizeClass(c), normalized));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check whether an item matches a `where` condition.
|
|
38
|
+
*
|
|
39
|
+
* Each key in `where` is a short property path (e.g. "BIP.spacetype").
|
|
40
|
+
* Each value is either a scalar (equality check) or an array (any-of check).
|
|
41
|
+
* Multiple keys = AND. String comparison is case-insensitive.
|
|
42
|
+
*/
|
|
43
|
+
const matchesWhere = (item: unknown, where: WhereCondition | undefined): boolean => {
|
|
44
|
+
if (!where) return true;
|
|
45
|
+
for (const [path, expected] of Object.entries(where)) {
|
|
46
|
+
const value = resolveProperty(item, path);
|
|
47
|
+
if (Array.isArray(expected)) {
|
|
48
|
+
const target = normalizeValue(value);
|
|
49
|
+
if (!expected.some((v) => normalizeValue(v) === target)) return false;
|
|
50
|
+
} else {
|
|
51
|
+
if (normalizeValue(value) !== normalizeValue(expected)) return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* V2 filter: match items by class + where conditions.
|
|
59
|
+
*
|
|
60
|
+
* @param item - An object with ifcClass and propertySets
|
|
61
|
+
* @param classFilter - IFC class name(s) to match, or undefined for any
|
|
62
|
+
* @param where - Property conditions (short paths), or undefined for no conditions
|
|
63
|
+
*/
|
|
64
|
+
export const matchesV2Filter = (
|
|
65
|
+
item: unknown,
|
|
66
|
+
classFilter: string | string[] | undefined,
|
|
67
|
+
where: WhereCondition | undefined
|
|
68
|
+
): boolean => {
|
|
69
|
+
if (classFilter !== undefined && isRecord(item)) {
|
|
70
|
+
const itemClass = getProp(item, 'ifcClass');
|
|
71
|
+
const itemClassId = getProp(item, 'ifcClassId');
|
|
72
|
+
if (!matchesClass(itemClass, classFilter) && !matchesClass(itemClassId, classFilter)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!matchesWhere(item, where)) return false;
|
|
77
|
+
return true;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export { normalizeClass, classEquals, normalizeValue, matchesClass, matchesWhere };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Geometric computation constants
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
// Epsilon values for floating-point comparisons
|
|
6
|
-
export const EPSILON_INTERSECTION = 1e-9; // Used for detecting parallel lines
|
|
7
|
-
export const EPSILON_DIVISION_SAFETY = 1e-9; // Used to prevent division by zero
|
|
8
|
-
export const EPSILON_MIN_DIMENSION = 1e-6; // Minimum valid dimension for model bounds
|
|
9
|
-
|
|
10
|
-
// Geometric multipliers
|
|
11
|
-
export const CENTER_FRACTION = 0.5; // For calculating center/midpoint (x * 0.5)
|
|
1
|
+
/**
|
|
2
|
+
* Geometric computation constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Epsilon values for floating-point comparisons
|
|
6
|
+
export const EPSILON_INTERSECTION = 1e-9; // Used for detecting parallel lines
|
|
7
|
+
export const EPSILON_DIVISION_SAFETY = 1e-9; // Used to prevent division by zero
|
|
8
|
+
export const EPSILON_MIN_DIMENSION = 1e-6; // Minimum valid dimension for model bounds
|
|
9
|
+
|
|
10
|
+
// Geometric multipliers
|
|
11
|
+
export const CENTER_FRACTION = 0.5; // For calculating center/midpoint (x * 0.5)
|