@twinfinity/printing 6.1.1-beta → 6.1.2-ci.30738-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/dist/BuildInfo.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/analyzeTemplate.d.ts +4 -4
- package/dist/layout/analyzeTemplate.d.ts.map +1 -1
- package/dist/layout/analyzeTemplate.js +18 -16
- package/dist/layout/analyzeTemplate.js.map +1 -1
- package/dist/layout/classClosure.d.ts +41 -0
- package/dist/layout/classClosure.d.ts.map +1 -0
- package/dist/layout/classClosure.js +143 -0
- package/dist/layout/classClosure.js.map +1 -0
- package/dist/layout/clipUtils.d.ts +11 -0
- package/dist/layout/clipUtils.d.ts.map +1 -1
- package/dist/layout/clipUtils.js +67 -6
- package/dist/layout/clipUtils.js.map +1 -1
- package/dist/layout/filters.d.ts +14 -8
- package/dist/layout/filters.d.ts.map +1 -1
- package/dist/layout/filters.js +28 -3
- package/dist/layout/filters.js.map +1 -1
- package/dist/layout/index.d.ts +24 -57
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +20 -337
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/itemBuilders.d.ts +11 -0
- package/dist/layout/itemBuilders.d.ts.map +1 -1
- package/dist/layout/itemBuilders.js +17 -3
- package/dist/layout/itemBuilders.js.map +1 -1
- package/dist/layout/labelsRender.d.ts.map +1 -1
- package/dist/layout/labelsRender.js +15 -27
- package/dist/layout/labelsRender.js.map +1 -1
- package/dist/layout/legendRender.d.ts +18 -6
- package/dist/layout/legendRender.d.ts.map +1 -1
- package/dist/layout/legendRender.js +49 -79
- package/dist/layout/legendRender.js.map +1 -1
- package/dist/layout/metadata.d.ts.map +1 -1
- package/dist/layout/metadata.js +26 -5
- package/dist/layout/metadata.js.map +1 -1
- package/dist/layout/model.d.ts +4 -3
- package/dist/layout/model.d.ts.map +1 -1
- package/dist/layout/model.js +17 -57
- package/dist/layout/model.js.map +1 -1
- package/dist/layout/options.d.ts +1 -2
- package/dist/layout/options.d.ts.map +1 -1
- package/dist/layout/options.js.map +1 -1
- package/dist/layout/preflight.d.ts +32 -0
- package/dist/layout/preflight.d.ts.map +1 -0
- package/dist/layout/preflight.js +69 -0
- package/dist/layout/preflight.js.map +1 -0
- package/dist/layout/qrRender.d.ts.map +1 -1
- package/dist/layout/qrRender.js +11 -10
- package/dist/layout/qrRender.js.map +1 -1
- package/dist/layout/renderDrawing.d.ts +28 -17
- package/dist/layout/renderDrawing.d.ts.map +1 -1
- package/dist/layout/renderDrawing.js +107 -88
- package/dist/layout/renderDrawing.js.map +1 -1
- package/dist/layout/renderLayout.d.ts +25 -0
- package/dist/layout/renderLayout.d.ts.map +1 -0
- package/dist/layout/renderLayout.js +315 -0
- package/dist/layout/renderLayout.js.map +1 -0
- package/dist/layout/renderTemplateSheet.d.ts +99 -0
- package/dist/layout/renderTemplateSheet.d.ts.map +1 -0
- package/dist/layout/renderTemplateSheet.js +296 -0
- package/dist/layout/renderTemplateSheet.js.map +1 -0
- package/dist/layout/renderUtils.d.ts +19 -0
- package/dist/layout/renderUtils.d.ts.map +1 -1
- package/dist/layout/renderUtils.js +47 -4
- package/dist/layout/renderUtils.js.map +1 -1
- package/dist/layout/sectionDefs.d.ts +65 -0
- package/dist/layout/sectionDefs.d.ts.map +1 -0
- package/dist/layout/sectionDefs.js +88 -0
- package/dist/layout/sectionDefs.js.map +1 -0
- package/dist/layout/slabModelView.d.ts +34 -0
- package/dist/layout/slabModelView.d.ts.map +1 -0
- package/dist/layout/slabModelView.js +175 -0
- package/dist/layout/slabModelView.js.map +1 -0
- package/dist/layout/templateVariables.d.ts +1 -1
- package/dist/layout/templateVariables.d.ts.map +1 -1
- package/dist/layout/templateVariables.js +21 -9
- package/dist/layout/templateVariables.js.map +1 -1
- package/dist/layout/types.d.ts +164 -83
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/layout/types.js.map +1 -1
- package/dist/layout/validation.d.ts.map +1 -1
- package/dist/layout/validation.js +78 -33
- package/dist/layout/validation.js.map +1 -1
- package/dist/layout/viewportPrep.d.ts +8 -6
- package/dist/layout/viewportPrep.d.ts.map +1 -1
- package/dist/layout/viewportPrep.js +35 -23
- package/dist/layout/viewportPrep.js.map +1 -1
- package/dist/layout/viewportRender.d.ts +9 -7
- package/dist/layout/viewportRender.d.ts.map +1 -1
- package/dist/layout/viewportRender.js +93 -126
- package/dist/layout/viewportRender.js.map +1 -1
- package/dist/layout/viewportUtils.d.ts +20 -0
- package/dist/layout/viewportUtils.d.ts.map +1 -1
- package/dist/layout/viewportUtils.js +107 -4
- package/dist/layout/viewportUtils.js.map +1 -1
- package/dist/printToPdf.d.ts +7 -1
- package/dist/printToPdf.d.ts.map +1 -1
- package/dist/printToPdf.js +81 -14
- package/dist/printToPdf.js.map +1 -1
- package/dist/printToSvg.d.ts.map +1 -1
- package/dist/printToSvg.js +7 -3
- package/dist/printToSvg.js.map +1 -1
- package/dist/schemaUtils.d.ts +9 -0
- package/dist/schemaUtils.d.ts.map +1 -0
- package/dist/schemaUtils.js +14 -0
- package/dist/schemaUtils.js.map +1 -0
- package/dist/templates/default-template.json +30 -8
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -3
- package/src/index.ts +4 -0
- package/src/layout/analyzeTemplate.ts +21 -19
- package/src/layout/classClosure.ts +142 -0
- package/src/layout/clipUtils.ts +65 -6
- package/src/layout/filters.ts +31 -3
- package/src/layout/index.ts +61 -571
- package/src/layout/itemBuilders.ts +20 -5
- package/src/layout/labelsRender.ts +23 -40
- package/src/layout/legendRender.ts +64 -84
- package/src/layout/metadata.ts +26 -5
- package/src/layout/model.ts +21 -66
- package/src/layout/options.ts +1 -2
- package/src/layout/preflight.ts +96 -0
- package/src/layout/qrRender.ts +11 -10
- package/src/layout/renderDrawing.ts +132 -110
- package/src/layout/renderLayout.ts +483 -0
- package/src/layout/renderTemplateSheet.ts +435 -0
- package/src/layout/renderUtils.ts +53 -4
- package/src/layout/sectionDefs.ts +131 -0
- package/src/layout/slabModelView.ts +196 -0
- package/src/layout/templateVariables.ts +25 -9
- package/src/layout/types.ts +160 -70
- package/src/layout/validation.ts +98 -32
- package/src/layout/viewportPrep.ts +47 -29
- package/src/layout/viewportRender.ts +114 -141
- package/src/layout/viewportUtils.ts +118 -5
- package/src/printToPdf.ts +84 -15
- package/src/printToSvg.ts +7 -3
- package/src/schemaUtils.ts +13 -0
- package/src/templates/default-template.json +30 -8
- package/src/types.ts +12 -2
- package/templates/default-a0-landscape.json +171 -66
- package/templates/default-a0-portrait.json +159 -60
- package/templates/default-a1-landscape.json +135 -48
- package/templates/default-a1-portrait.json +123 -42
- package/templates/default-a3-landscape.json +87 -24
- package/templates/default-a3-portrait.json +99 -30
- package/templates/default-a4-landscape.json +75 -18
- package/templates/default-a4-portrait.json +75 -18
- package/templates/llm-authoring-guide.md +247 -0
- package/templates/template-schema.json +295 -120
|
@@ -9,21 +9,124 @@ export const rotateBounds = (width, height, rotationDeg) => {
|
|
|
9
9
|
const rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);
|
|
10
10
|
return { width: rotatedWidth, height: rotatedHeight };
|
|
11
11
|
};
|
|
12
|
+
// Absolute move/line sequences only (what our section renderers emit). Anything
|
|
13
|
+
// else — relative commands, curves, arcs — falls back to AABB-based rotation.
|
|
14
|
+
const ABSOLUTE_POLYLINE_D = /^[MLZ\s,0-9eE+\-.]*$/;
|
|
15
|
+
const NUMBER_IN_D = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
|
|
16
|
+
const extentsCache = new WeakMap();
|
|
17
|
+
/**
|
|
18
|
+
* Tight bounds of the model's actual geometry under viewport rotation.
|
|
19
|
+
*
|
|
20
|
+
* Rotating the model's AABB *rectangle* only ever inflates it — for content that
|
|
21
|
+
* sits diagonally in model space (the main reason to set rotationDeg: straighten
|
|
22
|
+
* a building on the sheet), the true rotated bounds SHRINK instead, and fitting
|
|
23
|
+
* against the inflated rectangle picks a scale several preset steps too small.
|
|
24
|
+
* Returns null when tight bounds can't be derived (no rotation, no rawPaths /
|
|
25
|
+
* transform, or path data that isn't plain absolute polylines) — callers then
|
|
26
|
+
* keep the AABB-based rotateBounds behavior.
|
|
27
|
+
*/
|
|
28
|
+
export const rotatedContentExtents = (model, rotationDeg) => {
|
|
29
|
+
const norm = ((rotationDeg % 360) + 360) % 360;
|
|
30
|
+
const paths = model.rawPaths;
|
|
31
|
+
const t = model.transform;
|
|
32
|
+
if (!norm || !paths?.length || !t)
|
|
33
|
+
return null;
|
|
34
|
+
// Keyed on the transform origin as well as the rotation. The extents below are computed through
|
|
35
|
+
// t.viewMinX / t.viewMaxZ, so two models sharing a rawPaths array but placed with different
|
|
36
|
+
// origins have different answers — keying on rotation alone handed the second the first's.
|
|
37
|
+
const cacheKey = `${norm}|${t.viewMinX}|${t.viewMaxZ}`;
|
|
38
|
+
let perRotation = extentsCache.get(paths);
|
|
39
|
+
const cached = perRotation?.get(cacheKey);
|
|
40
|
+
if (cached !== undefined)
|
|
41
|
+
return cached;
|
|
42
|
+
if (!perRotation) {
|
|
43
|
+
perRotation = new Map();
|
|
44
|
+
extentsCache.set(paths, perRotation);
|
|
45
|
+
}
|
|
46
|
+
const rad = (norm * Math.PI) / 180;
|
|
47
|
+
const cos = Math.cos(rad);
|
|
48
|
+
const sin = Math.sin(rad);
|
|
49
|
+
let minX = Infinity;
|
|
50
|
+
let minY = Infinity;
|
|
51
|
+
let maxX = -Infinity;
|
|
52
|
+
let maxY = -Infinity;
|
|
53
|
+
for (const p of paths) {
|
|
54
|
+
if (!p.d)
|
|
55
|
+
continue;
|
|
56
|
+
if (!ABSOLUTE_POLYLINE_D.test(p.d)) {
|
|
57
|
+
perRotation.set(cacheKey, null);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const nums = p.d.match(NUMBER_IN_D);
|
|
61
|
+
if (!nums)
|
|
62
|
+
continue;
|
|
63
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
64
|
+
// Raw coords are projected y-up space; geometryTransform maps them to
|
|
65
|
+
// view space (y-down, origin at the model's min corner) — mirror that
|
|
66
|
+
// here so extents/center land in the space the render transform uses.
|
|
67
|
+
const vx = Number(nums[i]) - t.viewMinX;
|
|
68
|
+
const vy = t.viewMaxZ - Number(nums[i + 1]);
|
|
69
|
+
const rx = vx * cos - vy * sin;
|
|
70
|
+
const ry = vx * sin + vy * cos;
|
|
71
|
+
if (rx < minX)
|
|
72
|
+
minX = rx;
|
|
73
|
+
if (rx > maxX)
|
|
74
|
+
maxX = rx;
|
|
75
|
+
if (ry < minY)
|
|
76
|
+
minY = ry;
|
|
77
|
+
if (ry > maxY)
|
|
78
|
+
maxY = ry;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || maxX - minX <= 0 || maxY - minY <= 0) {
|
|
82
|
+
perRotation.set(cacheKey, null);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const cx = (minX + maxX) / 2;
|
|
86
|
+
const cy = (minY + maxY) / 2;
|
|
87
|
+
const result = {
|
|
88
|
+
width: maxX - minX,
|
|
89
|
+
height: maxY - minY,
|
|
90
|
+
// Inverse-rotate the rotated-space center: using this as the transform
|
|
91
|
+
// pivot makes the tight content box land centered in the viewport.
|
|
92
|
+
centerViewX: cx * cos + cy * sin,
|
|
93
|
+
centerViewY: -cx * sin + cy * cos
|
|
94
|
+
};
|
|
95
|
+
perRotation.set(cacheKey, result);
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
12
98
|
export const pickScale = (viewport, model, preferred, scalePresets) => {
|
|
99
|
+
const rotationDeg = viewport.rotationDeg ?? 0;
|
|
100
|
+
// Callers substitute clip-region dims into widthModel/heightModel; tight
|
|
101
|
+
// rotated bounds of the FULL geometry would be wrong there, so only use
|
|
102
|
+
// them when the passed dims still match the model's own view bounds.
|
|
103
|
+
const clipAdjusted = !!model.transform &&
|
|
104
|
+
(Math.abs(model.widthModel - model.transform.viewWidth) > 1e-9 ||
|
|
105
|
+
Math.abs(model.heightModel - model.transform.viewHeight) > 1e-9);
|
|
106
|
+
const tight = clipAdjusted ? null : rotatedContentExtents(model, rotationDeg);
|
|
107
|
+
// 'fit': exact fill of the viewport — the denominator is whatever ratio
|
|
108
|
+
// makes the (rotated) model touch the viewport bounds; no rounding to a
|
|
109
|
+
// preferred-scale list, so the scale text can be odd (e.g. 1:73.4).
|
|
110
|
+
if (viewport.scaleMode === 'fit') {
|
|
111
|
+
const rotatedModel = tight ?? rotateBounds(model.widthModel, model.heightModel, rotationDeg);
|
|
112
|
+
const mmPerModel = Math.min(viewport.widthMm / (rotatedModel.width || 1), viewport.heightMm / (rotatedModel.height || 1));
|
|
113
|
+
if (Number.isFinite(mmPerModel) && mmPerModel > 0) {
|
|
114
|
+
return { denom: Math.round((1000 / mmPerModel) * 10) / 10, mmPerModelUnit: mmPerModel, fits: true };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
13
117
|
const scalePresetName = viewport.scalePreset;
|
|
14
118
|
const scalePreset = scalePresetName && scalePresets[scalePresetName] ? scalePresets[scalePresetName] : null;
|
|
15
119
|
const denoms = viewport.scaleMode === 'fixed' && viewport.fixedScaleDenom
|
|
16
120
|
? [viewport.fixedScaleDenom]
|
|
17
121
|
: uniquePositiveDenoms(viewport.preferredScales ?? scalePreset?.preferredScales ?? preferred);
|
|
18
|
-
const rotationDeg = viewport.rotationDeg ?? 0;
|
|
19
122
|
let selectedDenom = denoms[denoms.length - 1];
|
|
20
123
|
let selectedMmPerModel = 1000 / selectedDenom;
|
|
21
124
|
let fits = false;
|
|
22
125
|
for (const denom of denoms) {
|
|
23
126
|
const mmPerModel = 1000 / denom;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
127
|
+
const rotated = tight
|
|
128
|
+
? { width: tight.width * mmPerModel, height: tight.height * mmPerModel }
|
|
129
|
+
: rotateBounds(model.widthModel * mmPerModel, model.heightModel * mmPerModel, rotationDeg);
|
|
27
130
|
if (rotated.width <= viewport.widthMm && rotated.height <= viewport.heightMm) {
|
|
28
131
|
selectedDenom = denom;
|
|
29
132
|
selectedMmPerModel = mmPerModel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewportUtils.js","sourceRoot":"/","sources":["layout/viewportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAG/C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,WAAmB,EAAqC,EAAE;IAClH,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACrE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CACrB,QAAyB,EACzB,KAAmB,EACnB,SAAmB,EACnB,YAA4D,EACvC,EAAE;IACvB,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC;IAC7C,MAAM,WAAW,GAAG,eAAe,IAAI,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5G,MAAM,MAAM,GACR,QAAQ,CAAC,SAAS,KAAK,OAAO,IAAI,QAAQ,CAAC,eAAe;QACtD,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC;QAC5B,CAAC,CAAC,oBAAoB,CAAC,QAAQ,CAAC,eAAe,IAAI,WAAW,EAAE,eAAe,IAAI,SAAS,CAAC,CAAC;IAEtG,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC;IAC9C,IAAI,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,kBAAkB,GAAG,IAAI,GAAG,aAAa,CAAC;IAC9C,IAAI,IAAI,GAAG,KAAK,CAAC;IAEjB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,IAAI,GAAG,KAAK,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;QAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,GAAG,UAAU,CAAC;QAChD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC3E,aAAa,GAAG,KAAK,CAAC;YACtB,kBAAkB,GAAG,UAAU,CAAC;YAChC,IAAI,GAAG,IAAI,CAAC;YACZ,MAAM;QACV,CAAC;IACL,CAAC;IAED,OAAO;QACH,KAAK,EAAE,aAAa;QACpB,cAAc,EAAE,kBAAkB;QAClC,IAAI;KACP,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { uniquePositiveDenoms } from './utils';\nimport { ComputedViewportScale, SvgModelView, ViewportElement } from './types';\n\nexport const rotateBounds = (width: number, height: number, rotationDeg: number): { width: number; height: number } => {\n if (!rotationDeg) return { width, height };\n const rad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedWidth = Math.abs(width * cos) + Math.abs(height * sin);\n const rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);\n return { width: rotatedWidth, height: rotatedHeight };\n};\n\nexport const pickScale = (\n viewport: ViewportElement,\n model: SvgModelView,\n preferred: number[],\n scalePresets: Record<string, { preferredScales?: number[] }>\n): ComputedViewportScale => {\n const scalePresetName = viewport.scalePreset;\n const scalePreset = scalePresetName && scalePresets[scalePresetName] ? scalePresets[scalePresetName] : null;\n const denoms =\n viewport.scaleMode === 'fixed' && viewport.fixedScaleDenom\n ? [viewport.fixedScaleDenom]\n : uniquePositiveDenoms(viewport.preferredScales ?? scalePreset?.preferredScales ?? preferred);\n\n const rotationDeg = viewport.rotationDeg ?? 0;\n let selectedDenom = denoms[denoms.length - 1];\n let selectedMmPerModel = 1000 / selectedDenom;\n let fits = false;\n\n for (const denom of denoms) {\n const mmPerModel = 1000 / denom;\n const widthMm = model.widthModel * mmPerModel;\n const heightMm = model.heightModel * mmPerModel;\n const rotated = rotateBounds(widthMm, heightMm, rotationDeg);\n if (rotated.width <= viewport.widthMm && rotated.height <= viewport.heightMm) {\n selectedDenom = denom;\n selectedMmPerModel = mmPerModel;\n fits = true;\n break;\n }\n }\n\n return {\n denom: selectedDenom,\n mmPerModelUnit: selectedMmPerModel,\n fits\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"viewportUtils.js","sourceRoot":"/","sources":["layout/viewportUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { uniquePositiveDenoms } from './utils';\nimport { ComputedViewportScale, SvgModelView, ViewportElement } from './types';\n\nexport const rotateBounds = (width: number, height: number, rotationDeg: number): { width: number; height: number } => {\n if (!rotationDeg) return { width, height };\n const rad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedWidth = Math.abs(width * cos) + Math.abs(height * sin);\n const rotatedHeight = Math.abs(width * sin) + Math.abs(height * cos);\n return { width: rotatedWidth, height: rotatedHeight };\n};\n\nexport interface RotatedContentExtents {\n /** Tight extents of the rotated content, model units (rotated/sheet axes). */\n width: number;\n height: number;\n /** Tight-bounds center mapped back to view space — the rotation pivot that centers the content. */\n centerViewX: number;\n centerViewY: number;\n}\n\n// Absolute move/line sequences only (what our section renderers emit). Anything\n// else — relative commands, curves, arcs — falls back to AABB-based rotation.\nconst ABSOLUTE_POLYLINE_D = /^[MLZ\\s,0-9eE+\\-.]*$/;\nconst NUMBER_IN_D = /-?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?/g;\n\nconst extentsCache = new WeakMap<object, Map<string, RotatedContentExtents | null>>();\n\n/**\n * Tight bounds of the model's actual geometry under viewport rotation.\n *\n * Rotating the model's AABB *rectangle* only ever inflates it — for content that\n * sits diagonally in model space (the main reason to set rotationDeg: straighten\n * a building on the sheet), the true rotated bounds SHRINK instead, and fitting\n * against the inflated rectangle picks a scale several preset steps too small.\n * Returns null when tight bounds can't be derived (no rotation, no rawPaths /\n * transform, or path data that isn't plain absolute polylines) — callers then\n * keep the AABB-based rotateBounds behavior.\n */\nexport const rotatedContentExtents = (model: SvgModelView, rotationDeg: number): RotatedContentExtents | null => {\n const norm = ((rotationDeg % 360) + 360) % 360;\n const paths = model.rawPaths;\n const t = model.transform;\n if (!norm || !paths?.length || !t) return null;\n // Keyed on the transform origin as well as the rotation. The extents below are computed through\n // t.viewMinX / t.viewMaxZ, so two models sharing a rawPaths array but placed with different\n // origins have different answers — keying on rotation alone handed the second the first's.\n const cacheKey = `${norm}|${t.viewMinX}|${t.viewMaxZ}`;\n let perRotation = extentsCache.get(paths);\n const cached = perRotation?.get(cacheKey);\n if (cached !== undefined) return cached;\n if (!perRotation) {\n perRotation = new Map();\n extentsCache.set(paths, perRotation);\n }\n\n const rad = (norm * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const p of paths) {\n if (!p.d) continue;\n if (!ABSOLUTE_POLYLINE_D.test(p.d)) {\n perRotation.set(cacheKey, null);\n return null;\n }\n const nums = p.d.match(NUMBER_IN_D);\n if (!nums) continue;\n for (let i = 0; i + 1 < nums.length; i += 2) {\n // Raw coords are projected y-up space; geometryTransform maps them to\n // view space (y-down, origin at the model's min corner) — mirror that\n // here so extents/center land in the space the render transform uses.\n const vx = Number(nums[i]) - t.viewMinX;\n const vy = t.viewMaxZ - Number(nums[i + 1]);\n const rx = vx * cos - vy * sin;\n const ry = vx * sin + vy * cos;\n if (rx < minX) minX = rx;\n if (rx > maxX) maxX = rx;\n if (ry < minY) minY = ry;\n if (ry > maxY) maxY = ry;\n }\n }\n if (!Number.isFinite(minX) || !Number.isFinite(minY) || maxX - minX <= 0 || maxY - minY <= 0) {\n perRotation.set(cacheKey, null);\n return null;\n }\n const cx = (minX + maxX) / 2;\n const cy = (minY + maxY) / 2;\n const result: RotatedContentExtents = {\n width: maxX - minX,\n height: maxY - minY,\n // Inverse-rotate the rotated-space center: using this as the transform\n // pivot makes the tight content box land centered in the viewport.\n centerViewX: cx * cos + cy * sin,\n centerViewY: -cx * sin + cy * cos\n };\n perRotation.set(cacheKey, result);\n return result;\n};\n\nexport const pickScale = (\n viewport: ViewportElement,\n model: SvgModelView,\n preferred: number[],\n scalePresets: Record<string, { preferredScales?: number[] }>\n): ComputedViewportScale => {\n const rotationDeg = viewport.rotationDeg ?? 0;\n // Callers substitute clip-region dims into widthModel/heightModel; tight\n // rotated bounds of the FULL geometry would be wrong there, so only use\n // them when the passed dims still match the model's own view bounds.\n const clipAdjusted =\n !!model.transform &&\n (Math.abs(model.widthModel - model.transform.viewWidth) > 1e-9 ||\n Math.abs(model.heightModel - model.transform.viewHeight) > 1e-9);\n const tight = clipAdjusted ? null : rotatedContentExtents(model, rotationDeg);\n\n // 'fit': exact fill of the viewport — the denominator is whatever ratio\n // makes the (rotated) model touch the viewport bounds; no rounding to a\n // preferred-scale list, so the scale text can be odd (e.g. 1:73.4).\n if (viewport.scaleMode === 'fit') {\n const rotatedModel = tight ?? rotateBounds(model.widthModel, model.heightModel, rotationDeg);\n const mmPerModel = Math.min(\n viewport.widthMm / (rotatedModel.width || 1),\n viewport.heightMm / (rotatedModel.height || 1)\n );\n if (Number.isFinite(mmPerModel) && mmPerModel > 0) {\n return { denom: Math.round((1000 / mmPerModel) * 10) / 10, mmPerModelUnit: mmPerModel, fits: true };\n }\n }\n\n const scalePresetName = viewport.scalePreset;\n const scalePreset = scalePresetName && scalePresets[scalePresetName] ? scalePresets[scalePresetName] : null;\n const denoms =\n viewport.scaleMode === 'fixed' && viewport.fixedScaleDenom\n ? [viewport.fixedScaleDenom]\n : uniquePositiveDenoms(viewport.preferredScales ?? scalePreset?.preferredScales ?? preferred);\n let selectedDenom = denoms[denoms.length - 1];\n let selectedMmPerModel = 1000 / selectedDenom;\n let fits = false;\n\n for (const denom of denoms) {\n const mmPerModel = 1000 / denom;\n const rotated = tight\n ? { width: tight.width * mmPerModel, height: tight.height * mmPerModel }\n : rotateBounds(model.widthModel * mmPerModel, model.heightModel * mmPerModel, rotationDeg);\n if (rotated.width <= viewport.widthMm && rotated.height <= viewport.heightMm) {\n selectedDenom = denom;\n selectedMmPerModel = mmPerModel;\n fits = true;\n break;\n }\n }\n\n return {\n denom: selectedDenom,\n mmPerModelUnit: selectedMmPerModel,\n fits\n };\n};\n"]}
|
package/dist/printToPdf.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
export interface PrintToPdfOptions {
|
|
7
7
|
svgMarkup: string;
|
|
8
8
|
fileName?: string;
|
|
9
|
-
|
|
9
|
+
/** ISO name or an arbitrary [width, height] in mm. */
|
|
10
|
+
pageFormat?: 'a0' | 'a1' | 'a2' | 'a3' | 'a4' | [number, number];
|
|
10
11
|
orientation?: 'portrait' | 'landscape';
|
|
11
12
|
marginMm?: number;
|
|
12
13
|
}
|
|
@@ -15,6 +16,11 @@ export interface PrintToPdfOptions {
|
|
|
15
16
|
* Requires jspdf and svg2pdf.js as peer dependencies.
|
|
16
17
|
*/
|
|
17
18
|
export declare function printToPdf(options: PrintToPdfOptions): Promise<Blob>;
|
|
19
|
+
/**
|
|
20
|
+
* External image references in an SVG, for error reporting. data: URIs are
|
|
21
|
+
* summarized rather than dumped (they can be hundreds of KB).
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectImageHrefs(svgMarkup: string): string[];
|
|
18
24
|
/**
|
|
19
25
|
* Export an SVG string directly as a downloadable SVG file.
|
|
20
26
|
*/
|
package/dist/printToPdf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printToPdf.d.ts","sourceRoot":"/","sources":["printToPdf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"printToPdf.d.ts","sourceRoot":"/","sources":["printToPdf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjE,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+D1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAY7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQxE;AAGD,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC1F;AAED,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAU3E"}
|
package/dist/printToPdf.js
CHANGED
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
* Requires jspdf and svg2pdf.js as peer dependencies.
|
|
9
9
|
*/
|
|
10
10
|
export async function printToPdf(options) {
|
|
11
|
-
// Dynamic imports for optional peer dependencies (installed by consumer,
|
|
11
|
+
// Dynamic imports for optional peer dependencies (installed by consumer,
|
|
12
|
+
// not this package). Typechecked against the workspace-hoisted installs
|
|
13
|
+
// (template-workbench depends on both), resolved at runtime for consumers.
|
|
12
14
|
let jspdfModule;
|
|
13
15
|
try {
|
|
14
|
-
// @ts-expect-error — optional peer dependency resolved at runtime
|
|
15
16
|
jspdfModule = await import('jspdf');
|
|
16
17
|
}
|
|
17
18
|
catch {
|
|
18
19
|
throw new Error('jspdf is required for PDF export. Install it: bun add jspdf');
|
|
19
20
|
}
|
|
20
21
|
try {
|
|
21
|
-
// @ts-expect-error — optional peer dependency resolved at runtime
|
|
22
22
|
await import('svg2pdf.js');
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
@@ -28,24 +28,59 @@ export async function printToPdf(options) {
|
|
|
28
28
|
const format = options.pageFormat ?? 'a3';
|
|
29
29
|
const orientation = options.orientation ?? 'landscape';
|
|
30
30
|
const margin = options.marginMm ?? 10;
|
|
31
|
-
|
|
31
|
+
// compress: jsPDF leaves content streams uncompressed by default — a full
|
|
32
|
+
// sheet of vector linework is megabytes of plain text. Flate matches what
|
|
33
|
+
// browser print-to-PDF produces (~5-10x smaller for typical sheets).
|
|
34
|
+
const doc = new jsPDF({ format, orientation, unit: 'mm', compress: true });
|
|
32
35
|
const pageWidth = doc.internal.pageSize.getWidth();
|
|
33
36
|
const pageHeight = doc.internal.pageSize.getHeight();
|
|
34
37
|
const parser = new DOMParser();
|
|
35
38
|
const svgDoc = parser.parseFromString(options.svgMarkup, 'image/svg+xml');
|
|
36
39
|
const svgElement = svgDoc.documentElement;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
try {
|
|
41
|
+
await doc.svg(svgElement, {
|
|
42
|
+
x: margin,
|
|
43
|
+
y: margin,
|
|
44
|
+
width: pageWidth - margin * 2,
|
|
45
|
+
height: pageHeight - margin * 2
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// svg2pdf loads every <image> href over XHR and rejects with the raw
|
|
50
|
+
// ProgressEvent on network/CORS failure — which stringifies to the
|
|
51
|
+
// useless "[object ProgressEvent]". Translate, and name the linked
|
|
52
|
+
// images so the failing one is findable. Remote images render fine in
|
|
53
|
+
// the browser preview but fail export unless the host sends CORS
|
|
54
|
+
// headers; data: URIs and inline resource `content` always work.
|
|
55
|
+
if (err instanceof Error)
|
|
56
|
+
throw err;
|
|
57
|
+
const hrefs = collectImageHrefs(options.svgMarkup);
|
|
58
|
+
throw new Error('PDF export failed while loading a linked <image> resource (network or CORS). ' +
|
|
59
|
+
'Template images must be data: URIs, inline SVG resource `content`, or served ' +
|
|
60
|
+
'with CORS headers — the on-screen preview does not enforce this, PDF export does.' +
|
|
61
|
+
(hrefs.length ? ` Linked images: ${hrefs.join(', ')}` : ''));
|
|
62
|
+
}
|
|
43
63
|
const blob = doc.output('blob');
|
|
44
64
|
if (options.fileName) {
|
|
45
65
|
doc.save(options.fileName);
|
|
46
66
|
}
|
|
47
67
|
return blob;
|
|
48
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* External image references in an SVG, for error reporting. data: URIs are
|
|
71
|
+
* summarized rather than dumped (they can be hundreds of KB).
|
|
72
|
+
*/
|
|
73
|
+
export function collectImageHrefs(svgMarkup) {
|
|
74
|
+
const hrefs = [];
|
|
75
|
+
// Both quote styles: SVG allows either, and this feeds an error message — matching only double
|
|
76
|
+
// quotes meant the "which image broke the export?" hint could come back empty on valid markup,
|
|
77
|
+
// which is worse than not having it.
|
|
78
|
+
for (const m of svgMarkup.matchAll(/<image\b[^>]*?(?:xlink:)?href\s*=\s*("([^"]*)"|'([^']*)')/g)) {
|
|
79
|
+
const href = m[2] ?? m[3] ?? '';
|
|
80
|
+
hrefs.push(href.startsWith('data:') ? `${href.slice(0, href.indexOf(',') + 1 || 32)}… (${href.length} chars)` : href);
|
|
81
|
+
}
|
|
82
|
+
return hrefs;
|
|
83
|
+
}
|
|
49
84
|
/**
|
|
50
85
|
* Export an SVG string directly as a downloadable SVG file.
|
|
51
86
|
*/
|
|
@@ -59,10 +94,42 @@ export function printToSvgFile(svgMarkup, fileName) {
|
|
|
59
94
|
URL.revokeObjectURL(url);
|
|
60
95
|
}
|
|
61
96
|
export async function exportSectionToPdf(options) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
97
|
+
const size = options.pageSizeMm;
|
|
98
|
+
// Claim the tab synchronously, while still inside the click that started this. Opening it after
|
|
99
|
+
// the await puts the call outside the user gesture, which is precisely what popup blockers stop
|
|
100
|
+
// — and rendering a sheet takes long enough that they always would.
|
|
101
|
+
const tab = options.openInNewTab ? window.open('', '_blank') : null;
|
|
102
|
+
let blob;
|
|
103
|
+
try {
|
|
104
|
+
blob = await printToPdf({
|
|
105
|
+
svgMarkup: options.svgMarkup,
|
|
106
|
+
// When opening in a tab, skip jsPDF's own save() download.
|
|
107
|
+
fileName: options.openInNewTab ? undefined : options.fileName,
|
|
108
|
+
pageFormat: size ? [size.width, size.height] : undefined,
|
|
109
|
+
orientation: size ? (size.width >= size.height ? 'landscape' : 'portrait') : undefined,
|
|
110
|
+
marginMm: size ? 0 : undefined
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
// Claiming the tab up front means a failed export would otherwise leave an empty one sitting
|
|
115
|
+
// there — and a broken linked <image> makes this throw, which is not a rare path.
|
|
116
|
+
tab?.close();
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
if (options.openInNewTab) {
|
|
120
|
+
const url = URL.createObjectURL(blob);
|
|
121
|
+
// A blocked window.open returns null; fall back to the download so the export still lands
|
|
122
|
+
// somewhere the user can find rather than appearing to do nothing.
|
|
123
|
+
if (tab)
|
|
124
|
+
tab.location.href = url;
|
|
125
|
+
else {
|
|
126
|
+
const link = document.createElement('a');
|
|
127
|
+
link.href = url;
|
|
128
|
+
link.download = options.fileName;
|
|
129
|
+
link.click();
|
|
130
|
+
}
|
|
131
|
+
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
|
132
|
+
}
|
|
66
133
|
}
|
|
67
134
|
export function exportSectionToSvg(options) {
|
|
68
135
|
if (options.openInNewTab) {
|
package/dist/printToPdf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printToPdf.js","sourceRoot":"/","sources":["printToPdf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PDF export via jsPDF + svg2pdf.js\n *\n * These are optional peer dependencies. Functions will throw at runtime if not installed.\n */\n\nexport interface PrintToPdfOptions {\n svgMarkup: string;\n fileName?: string;\n pageFormat?: 'a0' | 'a1' | 'a2' | 'a3' | 'a4';\n orientation?: 'portrait' | 'landscape';\n marginMm?: number;\n}\n\n/**\n * Export an SVG string to a PDF file.\n * Requires jspdf and svg2pdf.js as peer dependencies.\n */\nexport async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {\n // Dynamic imports for optional peer dependencies (installed by consumer, not this package)\n let jspdfModule: any;\n try {\n // @ts-expect-error — optional peer dependency resolved at runtime\n jspdfModule = await import('jspdf');\n } catch {\n throw new Error('jspdf is required for PDF export. Install it: bun add jspdf');\n }\n try {\n // @ts-expect-error — optional peer dependency resolved at runtime\n await import('svg2pdf.js');\n } catch {\n throw new Error('svg2pdf.js is required for PDF export. Install it: bun add svg2pdf.js');\n }\n\n const { jsPDF } = jspdfModule;\n const format = options.pageFormat ?? 'a3';\n const orientation = options.orientation ?? 'landscape';\n const margin = options.marginMm ?? 10;\n\n const doc = new jsPDF({ format, orientation, unit: 'mm' });\n const pageWidth = doc.internal.pageSize.getWidth();\n const pageHeight = doc.internal.pageSize.getHeight();\n\n const parser = new DOMParser();\n const svgDoc = parser.parseFromString(options.svgMarkup, 'image/svg+xml');\n const svgElement = svgDoc.documentElement;\n\n await doc.svg(svgElement, {\n x: margin,\n y: margin,\n width: pageWidth - margin * 2,\n height: pageHeight - margin * 2\n });\n\n const blob = doc.output('blob');\n\n if (options.fileName) {\n doc.save(options.fileName);\n }\n\n return blob;\n}\n\n/**\n * Export an SVG string directly as a downloadable SVG file.\n */\nexport function printToSvgFile(svgMarkup: string, fileName: string): void {\n const blob = new Blob([svgMarkup], { type: 'image/svg+xml' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.click();\n URL.revokeObjectURL(url);\n}\n\n// Compatibility wrappers for print-layout module\nexport interface ExportSectionToPdfOptions {\n svgMarkup: string;\n fileName: string;\n pageSizeMm?: { width: number; height: number };\n openInNewTab?: boolean;\n}\n\nexport async function exportSectionToPdf(options: ExportSectionToPdfOptions): Promise<void> {\n await printToPdf({\n svgMarkup: options.svgMarkup,\n fileName: options.fileName\n });\n}\n\nexport interface ExportSectionToSvgOptions {\n svgMarkup: string;\n fileName: string;\n openInNewTab?: boolean;\n}\n\nexport function exportSectionToSvg(options: ExportSectionToSvgOptions): void {\n if (options.openInNewTab) {\n const blob = new Blob([options.svgMarkup], { type: 'image/svg+xml' });\n const url = URL.createObjectURL(blob);\n window.open(url, '_blank');\n // Revoke after a delay to allow the new tab to load the blob\n setTimeout(() => URL.revokeObjectURL(url), 60_000);\n } else {\n printToSvgFile(options.svgMarkup, options.fileName);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"printToPdf.js","sourceRoot":"/","sources":["printToPdf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PDF export via jsPDF + svg2pdf.js\n *\n * These are optional peer dependencies. Functions will throw at runtime if not installed.\n */\n\nexport interface PrintToPdfOptions {\n svgMarkup: string;\n fileName?: string;\n /** ISO name or an arbitrary [width, height] in mm. */\n pageFormat?: 'a0' | 'a1' | 'a2' | 'a3' | 'a4' | [number, number];\n orientation?: 'portrait' | 'landscape';\n marginMm?: number;\n}\n\n/**\n * Export an SVG string to a PDF file.\n * Requires jspdf and svg2pdf.js as peer dependencies.\n */\nexport async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {\n // Dynamic imports for optional peer dependencies (installed by consumer,\n // not this package). Typechecked against the workspace-hoisted installs\n // (template-workbench depends on both), resolved at runtime for consumers.\n let jspdfModule: any;\n try {\n jspdfModule = await import('jspdf');\n } catch {\n throw new Error('jspdf is required for PDF export. Install it: bun add jspdf');\n }\n try {\n await import('svg2pdf.js');\n } catch {\n throw new Error('svg2pdf.js is required for PDF export. Install it: bun add svg2pdf.js');\n }\n\n const { jsPDF } = jspdfModule;\n const format = options.pageFormat ?? 'a3';\n const orientation = options.orientation ?? 'landscape';\n const margin = options.marginMm ?? 10;\n\n // compress: jsPDF leaves content streams uncompressed by default — a full\n // sheet of vector linework is megabytes of plain text. Flate matches what\n // browser print-to-PDF produces (~5-10x smaller for typical sheets).\n const doc = new jsPDF({ format, orientation, unit: 'mm', compress: true });\n const pageWidth = doc.internal.pageSize.getWidth();\n const pageHeight = doc.internal.pageSize.getHeight();\n\n const parser = new DOMParser();\n const svgDoc = parser.parseFromString(options.svgMarkup, 'image/svg+xml');\n const svgElement = svgDoc.documentElement;\n\n try {\n await doc.svg(svgElement, {\n x: margin,\n y: margin,\n width: pageWidth - margin * 2,\n height: pageHeight - margin * 2\n });\n } catch (err) {\n // svg2pdf loads every <image> href over XHR and rejects with the raw\n // ProgressEvent on network/CORS failure — which stringifies to the\n // useless \"[object ProgressEvent]\". Translate, and name the linked\n // images so the failing one is findable. Remote images render fine in\n // the browser preview but fail export unless the host sends CORS\n // headers; data: URIs and inline resource `content` always work.\n if (err instanceof Error) throw err;\n const hrefs = collectImageHrefs(options.svgMarkup);\n throw new Error(\n 'PDF export failed while loading a linked <image> resource (network or CORS). ' +\n 'Template images must be data: URIs, inline SVG resource `content`, or served ' +\n 'with CORS headers — the on-screen preview does not enforce this, PDF export does.' +\n (hrefs.length ? ` Linked images: ${hrefs.join(', ')}` : '')\n );\n }\n\n const blob = doc.output('blob');\n\n if (options.fileName) {\n doc.save(options.fileName);\n }\n\n return blob;\n}\n\n/**\n * External image references in an SVG, for error reporting. data: URIs are\n * summarized rather than dumped (they can be hundreds of KB).\n */\nexport function collectImageHrefs(svgMarkup: string): string[] {\n const hrefs: string[] = [];\n // Both quote styles: SVG allows either, and this feeds an error message — matching only double\n // quotes meant the \"which image broke the export?\" hint could come back empty on valid markup,\n // which is worse than not having it.\n for (const m of svgMarkup.matchAll(/<image\\b[^>]*?(?:xlink:)?href\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/g)) {\n const href = m[2] ?? m[3] ?? '';\n hrefs.push(\n href.startsWith('data:') ? `${href.slice(0, href.indexOf(',') + 1 || 32)}… (${href.length} chars)` : href\n );\n }\n return hrefs;\n}\n\n/**\n * Export an SVG string directly as a downloadable SVG file.\n */\nexport function printToSvgFile(svgMarkup: string, fileName: string): void {\n const blob = new Blob([svgMarkup], { type: 'image/svg+xml' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.click();\n URL.revokeObjectURL(url);\n}\n\n// Compatibility wrappers for print-layout module\nexport interface ExportSectionToPdfOptions {\n svgMarkup: string;\n fileName: string;\n pageSizeMm?: { width: number; height: number };\n openInNewTab?: boolean;\n}\n\nexport async function exportSectionToPdf(options: ExportSectionToPdfOptions): Promise<void> {\n const size = options.pageSizeMm;\n // Claim the tab synchronously, while still inside the click that started this. Opening it after\n // the await puts the call outside the user gesture, which is precisely what popup blockers stop\n // — and rendering a sheet takes long enough that they always would.\n const tab = options.openInNewTab ? window.open('', '_blank') : null;\n let blob: Blob;\n try {\n blob = await printToPdf({\n svgMarkup: options.svgMarkup,\n // When opening in a tab, skip jsPDF's own save() download.\n fileName: options.openInNewTab ? undefined : options.fileName,\n pageFormat: size ? [size.width, size.height] : undefined,\n orientation: size ? (size.width >= size.height ? 'landscape' : 'portrait') : undefined,\n marginMm: size ? 0 : undefined\n });\n } catch (err) {\n // Claiming the tab up front means a failed export would otherwise leave an empty one sitting\n // there — and a broken linked <image> makes this throw, which is not a rare path.\n tab?.close();\n throw err;\n }\n if (options.openInNewTab) {\n const url = URL.createObjectURL(blob);\n // A blocked window.open returns null; fall back to the download so the export still lands\n // somewhere the user can find rather than appearing to do nothing.\n if (tab) tab.location.href = url;\n else {\n const link = document.createElement('a');\n link.href = url;\n link.download = options.fileName;\n link.click();\n }\n setTimeout(() => URL.revokeObjectURL(url), 60_000);\n }\n}\n\nexport interface ExportSectionToSvgOptions {\n svgMarkup: string;\n fileName: string;\n openInNewTab?: boolean;\n}\n\nexport function exportSectionToSvg(options: ExportSectionToSvgOptions): void {\n if (options.openInNewTab) {\n const blob = new Blob([options.svgMarkup], { type: 'image/svg+xml' });\n const url = URL.createObjectURL(blob);\n window.open(url, '_blank');\n // Revoke after a delay to allow the new tab to load the blob\n setTimeout(() => URL.revokeObjectURL(url), 60_000);\n } else {\n printToSvgFile(options.svgMarkup, options.fileName);\n }\n}\n"]}
|
package/dist/printToSvg.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printToSvg.d.ts","sourceRoot":"/","sources":["printToSvg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAQ,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAkC,MAAM,SAAS,CAAC;AAMjF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"printToSvg.d.ts","sourceRoot":"/","sources":["printToSvg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAQ,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAkC,MAAM,SAAS,CAAC;AAMjF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAgFjG"}
|
package/dist/printToSvg.js
CHANGED
|
@@ -29,9 +29,10 @@ export function printToSvg(geometry, options = {}) {
|
|
|
29
29
|
const proj = {
|
|
30
30
|
hiddenLines: projectPolylines(geometry.hiddenLines, options.planeBasis),
|
|
31
31
|
outlineHidden: projectPolylines(geometry.outlineHidden, options.planeBasis),
|
|
32
|
+
silhouettesHidden: projectPolylines(geometry.silhouettesHidden, options.planeBasis),
|
|
32
33
|
visibleLines: projectPolylines(geometry.visibleLines, options.planeBasis),
|
|
33
34
|
outlineVisible: projectPolylines(geometry.outlineVisible, options.planeBasis),
|
|
34
|
-
|
|
35
|
+
silhouettesVisible: projectPolylines(geometry.silhouettesVisible, options.planeBasis),
|
|
35
36
|
featureEdges: projectPolylines(geometry.featureEdges, options.planeBasis),
|
|
36
37
|
cutLines: projectPolylines(geometry.cutLines, options.planeBasis),
|
|
37
38
|
stairArrows: projectPolylines(geometry.stairArrows, options.planeBasis)
|
|
@@ -49,12 +50,14 @@ export function printToSvg(geometry, options = {}) {
|
|
|
49
50
|
parts.push(`<rect x="${minX}" y="${minY}" width="${width}" height="${height}" fill="${options.backgroundColor}"/>`);
|
|
50
51
|
}
|
|
51
52
|
parts.push('<g fill="none" stroke-linecap="round" stroke-linejoin="round">');
|
|
53
|
+
// Hidden categories paint first so visible linework always draws over them.
|
|
52
54
|
const categories = [
|
|
53
55
|
['hiddenLines', 'hiddenLines'],
|
|
54
56
|
['outlineHidden', 'outlineHidden'],
|
|
57
|
+
['silhouettesHidden', 'silhouettesHidden'],
|
|
55
58
|
['visibleLines', 'visibleLines'],
|
|
56
59
|
['outlineVisible', 'outlineVisible'],
|
|
57
|
-
['
|
|
60
|
+
['silhouettesVisible', 'silhouettesVisible'],
|
|
58
61
|
['featureEdges', 'featureEdges'],
|
|
59
62
|
['cutLines', 'cutLines'],
|
|
60
63
|
['stairArrows', 'stairArrows']
|
|
@@ -158,7 +161,8 @@ function mergeStyles(userStyles) {
|
|
|
158
161
|
cutLines: { ...DEFAULT_SVG_STYLES.cutLines, ...userStyles?.cutLines },
|
|
159
162
|
visibleLines: { ...DEFAULT_SVG_STYLES.visibleLines, ...userStyles?.visibleLines },
|
|
160
163
|
hiddenLines: { ...DEFAULT_SVG_STYLES.hiddenLines, ...userStyles?.hiddenLines },
|
|
161
|
-
|
|
164
|
+
silhouettesVisible: { ...DEFAULT_SVG_STYLES.silhouettesVisible, ...userStyles?.silhouettesVisible },
|
|
165
|
+
silhouettesHidden: { ...DEFAULT_SVG_STYLES.silhouettesHidden, ...userStyles?.silhouettesHidden },
|
|
162
166
|
featureEdges: { ...DEFAULT_SVG_STYLES.featureEdges, ...userStyles?.featureEdges },
|
|
163
167
|
stairArrows: { ...DEFAULT_SVG_STYLES.stairArrows, ...userStyles?.stairArrows },
|
|
164
168
|
outlineVisible: { ...DEFAULT_SVG_STYLES.outlineVisible, ...userStyles?.outlineVisible },
|
package/dist/printToSvg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printToSvg.js","sourceRoot":"/","sources":["printToSvg.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SlabSectionGeometry, Vec3 } from '@twinfinity/geometry';\nimport type { PrintToSvgOptions, SvgRenderStyles, SvgPlaneBasis } from './types';\nimport { DEFAULT_SVG_STYLES } from './types';\n\ntype Pt2 = { x: number; y: number };\ntype Bounds = { minX: number; maxX: number; minY: number; maxY: number };\n\n/**\n * Render SlabSectionGeometry to an SVG string.\n *\n * @param geometry The computed slab section geometry\n * @param options Rendering options (viewBox, styles, planeBasis, etc.)\n * @returns Complete SVG string\n */\nexport function printToSvg(geometry: SlabSectionGeometry, options: PrintToSvgOptions = {}): string {\n if (!geometry) throw new Error('geometry is required');\n\n if (\n options.boundsPadding !== undefined &&\n (typeof options.boundsPadding !== 'number' || options.boundsPadding < 0)\n ) {\n throw new Error('boundsPadding must be a non-negative number');\n }\n\n if (options.viewBox) {\n if (!Array.isArray(options.viewBox) || options.viewBox.length !== 4) {\n throw new Error('viewBox must be an array of 4 numbers [minX, minY, width, height]');\n }\n for (let i = 0; i < 4; i++) {\n if (typeof options.viewBox[i] !== 'number' || !Number.isFinite(options.viewBox[i])) {\n throw new Error(`viewBox[${i}] must be a finite number`);\n }\n }\n }\n\n const styles = mergeStyles(options.styles);\n const includeXml = options.includeXmlDeclaration ?? true;\n\n // Project all polylines to 2D once\n const proj = {\n hiddenLines: projectPolylines(geometry.hiddenLines, options.planeBasis),\n outlineHidden: projectPolylines(geometry.outlineHidden, options.planeBasis),\n visibleLines: projectPolylines(geometry.visibleLines, options.planeBasis),\n outlineVisible: projectPolylines(geometry.outlineVisible, options.planeBasis),\n silhouettes: projectPolylines(geometry.silhouettes, options.planeBasis),\n featureEdges: projectPolylines(geometry.featureEdges, options.planeBasis),\n cutLines: projectPolylines(geometry.cutLines, options.planeBasis),\n stairArrows: projectPolylines(geometry.stairArrows, options.planeBasis)\n };\n\n const bounds = options.bounds ?? computeBoundsFromProjected(proj);\n const viewBox = options.viewBox ?? boundsToViewBox(bounds, options.boundsPadding ?? 0.05);\n\n const parts: string[] = [];\n\n if (includeXml) parts.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n\n const [minX, minY, width, height] = viewBox;\n parts.push(\n `<svg xmlns=\"http://www.w3.org/2000/svg\" ` +\n `viewBox=\"${minX} ${minY} ${width} ${height}\" ` +\n `width=\"${width}\" height=\"${height}\">`\n );\n\n if (options.backgroundColor) {\n parts.push(\n `<rect x=\"${minX}\" y=\"${minY}\" width=\"${width}\" height=\"${height}\" fill=\"${options.backgroundColor}\"/>`\n );\n }\n\n parts.push('<g fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\">');\n\n const categories: Array<[keyof typeof proj, keyof Required<SvgRenderStyles>]> = [\n ['hiddenLines', 'hiddenLines'],\n ['outlineHidden', 'outlineHidden'],\n ['visibleLines', 'visibleLines'],\n ['outlineVisible', 'outlineVisible'],\n ['silhouettes', 'silhouettes'],\n ['featureEdges', 'featureEdges'],\n ['cutLines', 'cutLines'],\n ['stairArrows', 'stairArrows']\n ];\n\n for (const [key, styleKey] of categories) {\n const style = styles[styleKey];\n if (style.enabled) renderProjectedCategory(parts, proj[key], style, bounds);\n }\n\n parts.push('</g>');\n parts.push('</svg>');\n return parts.join('\\n');\n}\n\nfunction projectPolylines(polylines: Vec3[][], planeBasis?: SvgPlaneBasis): Pt2[][] {\n const result: Pt2[][] = [];\n for (const polyline of polylines) {\n if (polyline.length === 0) continue;\n const projected: Pt2[] = [];\n for (const point of polyline) {\n if (!Number.isFinite(point[0]) || !Number.isFinite(point[1]) || !Number.isFinite(point[2])) continue;\n if (planeBasis) {\n const dx = point[0] - planeBasis.origin.x;\n const dy = point[1] - planeBasis.origin.y;\n const dz = point[2] - planeBasis.origin.z;\n projected.push({\n x: dx * planeBasis.right.x + dy * planeBasis.right.y + dz * planeBasis.right.z,\n y: dx * planeBasis.up.x + dy * planeBasis.up.y + dz * planeBasis.up.z\n });\n } else {\n projected.push({ x: point[0], y: -point[2] });\n }\n }\n if (projected.length > 0) result.push(projected);\n }\n return result;\n}\n\nfunction computeBoundsFromProjected(categories: Record<string, Pt2[][]>): Bounds {\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const polylines of Object.values(categories)) {\n for (const polyline of polylines) {\n for (const pt of polyline) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n }\n }\n if (!Number.isFinite(minX)) return { minX: 0, maxX: 1, minY: 0, maxY: 1 };\n return { minX, maxX, minY, maxY };\n}\n\nfunction renderProjectedCategory(\n parts: string[],\n polylines: Pt2[][],\n style: { stroke?: string; strokeWidth?: number; strokeDasharray?: string; opacity?: number },\n bounds: Bounds\n): void {\n if (!polylines.length) return;\n const pathData = projectedToPathData(polylines, bounds);\n if (!pathData) return;\n\n let pathElement = `<path d=\"${pathData}\"`;\n if (style.stroke) pathElement += ` stroke=\"${style.stroke}\"`;\n if (style.strokeWidth !== undefined) pathElement += ` stroke-width=\"${style.strokeWidth}\"`;\n if (style.strokeDasharray) pathElement += ` stroke-dasharray=\"${style.strokeDasharray}\"`;\n if (style.opacity !== undefined && style.opacity !== 1) pathElement += ` opacity=\"${style.opacity}\"`;\n pathElement += '/>';\n parts.push(pathElement);\n}\n\nfunction projectedToPathData(polylines: Pt2[][], bounds: Bounds): string {\n const parts: string[] = [];\n const ox = bounds.minX;\n const oy = bounds.maxY;\n for (const polyline of polylines) {\n if (polyline.length === 0) continue;\n parts.push(`M ${(polyline[0].x - ox).toFixed(6)} ${(oy - polyline[0].y).toFixed(6)}`);\n for (let i = 1; i < polyline.length; i++) {\n parts.push(`L ${(polyline[i].x - ox).toFixed(6)} ${(oy - polyline[i].y).toFixed(6)}`);\n }\n }\n return parts.join(' ');\n}\n\nfunction boundsToViewBox(bounds: Bounds, padding: number): [number, number, number, number] {\n const width = bounds.maxX - bounds.minX;\n const height = bounds.maxY - bounds.minY;\n const padAmount = Math.max(width, height) * padding;\n return [-padAmount, -padAmount, width + padAmount * 2, height + padAmount * 2];\n}\n\nfunction mergeStyles(userStyles?: SvgRenderStyles): Required<SvgRenderStyles> {\n return {\n cutLines: { ...DEFAULT_SVG_STYLES.cutLines, ...userStyles?.cutLines },\n visibleLines: { ...DEFAULT_SVG_STYLES.visibleLines, ...userStyles?.visibleLines },\n hiddenLines: { ...DEFAULT_SVG_STYLES.hiddenLines, ...userStyles?.hiddenLines },\n silhouettes: { ...DEFAULT_SVG_STYLES.silhouettes, ...userStyles?.silhouettes },\n featureEdges: { ...DEFAULT_SVG_STYLES.featureEdges, ...userStyles?.featureEdges },\n stairArrows: { ...DEFAULT_SVG_STYLES.stairArrows, ...userStyles?.stairArrows },\n outlineVisible: { ...DEFAULT_SVG_STYLES.outlineVisible, ...userStyles?.outlineVisible },\n outlineHidden: { ...DEFAULT_SVG_STYLES.outlineHidden, ...userStyles?.outlineHidden }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"printToSvg.js","sourceRoot":"/","sources":["printToSvg.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,QAA6B,EAAE,UAA6B,EAAE;IACrF,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAEvD,IACI,OAAO,CAAC,aAAa,KAAK,SAAS;QACnC,CAAC,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,IAAI,OAAO,CAAC,aAAa,GAAG,CAAC,CAAC,EAC1E,CAAC;QACC,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACzB,IAAI,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;YAC7D,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,qBAAqB,IAAI,IAAI,CAAC;IAEzD,mCAAmC;IACnC,MAAM,IAAI,GAAG;QACT,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC;QACvE,aAAa,EAAE,gBAAgB,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC;QAC3E,iBAAiB,EAAE,gBAAgB,CAAC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,UAAU,CAAC;QACnF,YAAY,EAAE,gBAAgB,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC;QACzE,cAAc,EAAE,gBAAgB,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC;QAC7E,kBAAkB,EAAE,gBAAgB,CAAC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC,UAAU,CAAC;QACrF,YAAY,EAAE,gBAAgB,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC;QACzE,QAAQ,EAAE,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC;QACjE,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC;KAC1E,CAAC;IAEF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,0BAA0B,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC;IAE1F,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAErE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;IAC5C,KAAK,CAAC,IAAI,CACN,0CAA0C;QACtC,YAAY,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,MAAM,IAAI;QAC/C,UAAU,KAAK,aAAa,MAAM,IAAI,CAC7C,CAAC;IAEF,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CACN,YAAY,IAAI,QAAQ,IAAI,YAAY,KAAK,aAAa,MAAM,WAAW,OAAO,CAAC,eAAe,KAAK,CAC1G,CAAC;IACN,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAE7E,4EAA4E;IAC5E,MAAM,UAAU,GAAgE;QAC5E,CAAC,aAAa,EAAE,aAAa,CAAC;QAC9B,CAAC,eAAe,EAAE,eAAe,CAAC;QAClC,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;QAC1C,CAAC,cAAc,EAAE,cAAc,CAAC;QAChC,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;QACpC,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;QAC5C,CAAC,cAAc,EAAE,cAAc,CAAC;QAChC,CAAC,UAAU,EAAE,UAAU,CAAC;QACxB,CAAC,aAAa,EAAE,aAAa,CAAC;KACjC,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,UAAU,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO;YAAE,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAmB,EAAE,UAA0B;IACrE,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,SAAS,GAAU,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAAE,SAAS;YACrG,IAAI,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC1C,SAAS,CAAC,IAAI,CAAC;oBACX,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;oBAC9E,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;iBACxE,CAAC,CAAC;YACP,CAAC;iBAAM,CAAC;gBACJ,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,SAAS,0BAA0B,CAAC,UAAmC;IACnE,IAAI,IAAI,GAAG,QAAQ,EACf,IAAI,GAAG,QAAQ,EACf,IAAI,GAAG,CAAC,QAAQ,EAChB,IAAI,GAAG,CAAC,QAAQ,CAAC;IACrB,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACxB,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC7B,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC7B,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC7B,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;YACjC,CAAC;QACL,CAAC;IACL,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC1E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,uBAAuB,CAC5B,KAAe,EACf,SAAkB,EAClB,KAA4F,EAC5F,MAAc;IAEd,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO;IAC9B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,IAAI,WAAW,GAAG,YAAY,QAAQ,GAAG,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM;QAAE,WAAW,IAAI,YAAY,KAAK,CAAC,MAAM,GAAG,CAAC;IAC7D,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,WAAW,IAAI,kBAAkB,KAAK,CAAC,WAAW,GAAG,CAAC;IAC3F,IAAI,KAAK,CAAC,eAAe;QAAE,WAAW,IAAI,sBAAsB,KAAK,CAAC,eAAe,GAAG,CAAC;IACzF,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,WAAW,IAAI,aAAa,KAAK,CAAC,OAAO,GAAG,CAAC;IACrG,WAAW,IAAI,IAAI,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,mBAAmB,CAAC,SAAkB,EAAE,MAAc;IAC3D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACvB,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACvB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1F,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,OAAe;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;IACpD,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,GAAG,SAAS,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,WAAW,CAAC,UAA4B;IAC7C,OAAO;QACH,QAAQ,EAAE,EAAE,GAAG,kBAAkB,CAAC,QAAQ,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAE;QACrE,YAAY,EAAE,EAAE,GAAG,kBAAkB,CAAC,YAAY,EAAE,GAAG,UAAU,EAAE,YAAY,EAAE;QACjF,WAAW,EAAE,EAAE,GAAG,kBAAkB,CAAC,WAAW,EAAE,GAAG,UAAU,EAAE,WAAW,EAAE;QAC9E,kBAAkB,EAAE,EAAE,GAAG,kBAAkB,CAAC,kBAAkB,EAAE,GAAG,UAAU,EAAE,kBAAkB,EAAE;QACnG,iBAAiB,EAAE,EAAE,GAAG,kBAAkB,CAAC,iBAAiB,EAAE,GAAG,UAAU,EAAE,iBAAiB,EAAE;QAChG,YAAY,EAAE,EAAE,GAAG,kBAAkB,CAAC,YAAY,EAAE,GAAG,UAAU,EAAE,YAAY,EAAE;QACjF,WAAW,EAAE,EAAE,GAAG,kBAAkB,CAAC,WAAW,EAAE,GAAG,UAAU,EAAE,WAAW,EAAE;QAC9E,cAAc,EAAE,EAAE,GAAG,kBAAkB,CAAC,cAAc,EAAE,GAAG,UAAU,EAAE,cAAc,EAAE;QACvF,aAAa,EAAE,EAAE,GAAG,kBAAkB,CAAC,aAAa,EAAE,GAAG,UAAU,EAAE,aAAa,EAAE;KACvF,CAAC;AACN,CAAC","sourcesContent":["import type { SlabSectionGeometry, Vec3 } from '@twinfinity/geometry';\nimport type { PrintToSvgOptions, SvgRenderStyles, SvgPlaneBasis } from './types';\nimport { DEFAULT_SVG_STYLES } from './types';\n\ntype Pt2 = { x: number; y: number };\ntype Bounds = { minX: number; maxX: number; minY: number; maxY: number };\n\n/**\n * Render SlabSectionGeometry to an SVG string.\n *\n * @param geometry The computed slab section geometry\n * @param options Rendering options (viewBox, styles, planeBasis, etc.)\n * @returns Complete SVG string\n */\nexport function printToSvg(geometry: SlabSectionGeometry, options: PrintToSvgOptions = {}): string {\n if (!geometry) throw new Error('geometry is required');\n\n if (\n options.boundsPadding !== undefined &&\n (typeof options.boundsPadding !== 'number' || options.boundsPadding < 0)\n ) {\n throw new Error('boundsPadding must be a non-negative number');\n }\n\n if (options.viewBox) {\n if (!Array.isArray(options.viewBox) || options.viewBox.length !== 4) {\n throw new Error('viewBox must be an array of 4 numbers [minX, minY, width, height]');\n }\n for (let i = 0; i < 4; i++) {\n if (typeof options.viewBox[i] !== 'number' || !Number.isFinite(options.viewBox[i])) {\n throw new Error(`viewBox[${i}] must be a finite number`);\n }\n }\n }\n\n const styles = mergeStyles(options.styles);\n const includeXml = options.includeXmlDeclaration ?? true;\n\n // Project all polylines to 2D once\n const proj = {\n hiddenLines: projectPolylines(geometry.hiddenLines, options.planeBasis),\n outlineHidden: projectPolylines(geometry.outlineHidden, options.planeBasis),\n silhouettesHidden: projectPolylines(geometry.silhouettesHidden, options.planeBasis),\n visibleLines: projectPolylines(geometry.visibleLines, options.planeBasis),\n outlineVisible: projectPolylines(geometry.outlineVisible, options.planeBasis),\n silhouettesVisible: projectPolylines(geometry.silhouettesVisible, options.planeBasis),\n featureEdges: projectPolylines(geometry.featureEdges, options.planeBasis),\n cutLines: projectPolylines(geometry.cutLines, options.planeBasis),\n stairArrows: projectPolylines(geometry.stairArrows, options.planeBasis)\n };\n\n const bounds = options.bounds ?? computeBoundsFromProjected(proj);\n const viewBox = options.viewBox ?? boundsToViewBox(bounds, options.boundsPadding ?? 0.05);\n\n const parts: string[] = [];\n\n if (includeXml) parts.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n\n const [minX, minY, width, height] = viewBox;\n parts.push(\n `<svg xmlns=\"http://www.w3.org/2000/svg\" ` +\n `viewBox=\"${minX} ${minY} ${width} ${height}\" ` +\n `width=\"${width}\" height=\"${height}\">`\n );\n\n if (options.backgroundColor) {\n parts.push(\n `<rect x=\"${minX}\" y=\"${minY}\" width=\"${width}\" height=\"${height}\" fill=\"${options.backgroundColor}\"/>`\n );\n }\n\n parts.push('<g fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\">');\n\n // Hidden categories paint first so visible linework always draws over them.\n const categories: Array<[keyof typeof proj, keyof Required<SvgRenderStyles>]> = [\n ['hiddenLines', 'hiddenLines'],\n ['outlineHidden', 'outlineHidden'],\n ['silhouettesHidden', 'silhouettesHidden'],\n ['visibleLines', 'visibleLines'],\n ['outlineVisible', 'outlineVisible'],\n ['silhouettesVisible', 'silhouettesVisible'],\n ['featureEdges', 'featureEdges'],\n ['cutLines', 'cutLines'],\n ['stairArrows', 'stairArrows']\n ];\n\n for (const [key, styleKey] of categories) {\n const style = styles[styleKey];\n if (style.enabled) renderProjectedCategory(parts, proj[key], style, bounds);\n }\n\n parts.push('</g>');\n parts.push('</svg>');\n return parts.join('\\n');\n}\n\nfunction projectPolylines(polylines: Vec3[][], planeBasis?: SvgPlaneBasis): Pt2[][] {\n const result: Pt2[][] = [];\n for (const polyline of polylines) {\n if (polyline.length === 0) continue;\n const projected: Pt2[] = [];\n for (const point of polyline) {\n if (!Number.isFinite(point[0]) || !Number.isFinite(point[1]) || !Number.isFinite(point[2])) continue;\n if (planeBasis) {\n const dx = point[0] - planeBasis.origin.x;\n const dy = point[1] - planeBasis.origin.y;\n const dz = point[2] - planeBasis.origin.z;\n projected.push({\n x: dx * planeBasis.right.x + dy * planeBasis.right.y + dz * planeBasis.right.z,\n y: dx * planeBasis.up.x + dy * planeBasis.up.y + dz * planeBasis.up.z\n });\n } else {\n projected.push({ x: point[0], y: -point[2] });\n }\n }\n if (projected.length > 0) result.push(projected);\n }\n return result;\n}\n\nfunction computeBoundsFromProjected(categories: Record<string, Pt2[][]>): Bounds {\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const polylines of Object.values(categories)) {\n for (const polyline of polylines) {\n for (const pt of polyline) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n }\n }\n if (!Number.isFinite(minX)) return { minX: 0, maxX: 1, minY: 0, maxY: 1 };\n return { minX, maxX, minY, maxY };\n}\n\nfunction renderProjectedCategory(\n parts: string[],\n polylines: Pt2[][],\n style: { stroke?: string; strokeWidth?: number; strokeDasharray?: string; opacity?: number },\n bounds: Bounds\n): void {\n if (!polylines.length) return;\n const pathData = projectedToPathData(polylines, bounds);\n if (!pathData) return;\n\n let pathElement = `<path d=\"${pathData}\"`;\n if (style.stroke) pathElement += ` stroke=\"${style.stroke}\"`;\n if (style.strokeWidth !== undefined) pathElement += ` stroke-width=\"${style.strokeWidth}\"`;\n if (style.strokeDasharray) pathElement += ` stroke-dasharray=\"${style.strokeDasharray}\"`;\n if (style.opacity !== undefined && style.opacity !== 1) pathElement += ` opacity=\"${style.opacity}\"`;\n pathElement += '/>';\n parts.push(pathElement);\n}\n\nfunction projectedToPathData(polylines: Pt2[][], bounds: Bounds): string {\n const parts: string[] = [];\n const ox = bounds.minX;\n const oy = bounds.maxY;\n for (const polyline of polylines) {\n if (polyline.length === 0) continue;\n parts.push(`M ${(polyline[0].x - ox).toFixed(6)} ${(oy - polyline[0].y).toFixed(6)}`);\n for (let i = 1; i < polyline.length; i++) {\n parts.push(`L ${(polyline[i].x - ox).toFixed(6)} ${(oy - polyline[i].y).toFixed(6)}`);\n }\n }\n return parts.join(' ');\n}\n\nfunction boundsToViewBox(bounds: Bounds, padding: number): [number, number, number, number] {\n const width = bounds.maxX - bounds.minX;\n const height = bounds.maxY - bounds.minY;\n const padAmount = Math.max(width, height) * padding;\n return [-padAmount, -padAmount, width + padAmount * 2, height + padAmount * 2];\n}\n\nfunction mergeStyles(userStyles?: SvgRenderStyles): Required<SvgRenderStyles> {\n return {\n cutLines: { ...DEFAULT_SVG_STYLES.cutLines, ...userStyles?.cutLines },\n visibleLines: { ...DEFAULT_SVG_STYLES.visibleLines, ...userStyles?.visibleLines },\n hiddenLines: { ...DEFAULT_SVG_STYLES.hiddenLines, ...userStyles?.hiddenLines },\n silhouettesVisible: { ...DEFAULT_SVG_STYLES.silhouettesVisible, ...userStyles?.silhouettesVisible },\n silhouettesHidden: { ...DEFAULT_SVG_STYLES.silhouettesHidden, ...userStyles?.silhouettesHidden },\n featureEdges: { ...DEFAULT_SVG_STYLES.featureEdges, ...userStyles?.featureEdges },\n stairArrows: { ...DEFAULT_SVG_STYLES.stairArrows, ...userStyles?.stairArrows },\n outlineVisible: { ...DEFAULT_SVG_STYLES.outlineVisible, ...userStyles?.outlineVisible },\n outlineHidden: { ...DEFAULT_SVG_STYLES.outlineHidden, ...userStyles?.outlineHidden }\n };\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shipped `templates/template-schema.json` contains VS Code snippet
|
|
3
|
+
* placeholders (`${1:value}`) inside `defaultSnippets` bodies — valid for
|
|
4
|
+
* Monaco/VS Code, but not strict JSON. Consumers that need to parse the
|
|
5
|
+
* schema (ajv validation, editors without snippet support) must strip them
|
|
6
|
+
* first; this is the one shared implementation.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sanitizeTemplateSchemaSnippets(schemaJsonText: string): string;
|
|
9
|
+
//# sourceMappingURL=schemaUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaUtils.d.ts","sourceRoot":"/","sources":["schemaUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAK7E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shipped `templates/template-schema.json` contains VS Code snippet
|
|
3
|
+
* placeholders (`${1:value}`) inside `defaultSnippets` bodies — valid for
|
|
4
|
+
* Monaco/VS Code, but not strict JSON. Consumers that need to parse the
|
|
5
|
+
* schema (ajv validation, editors without snippet support) must strip them
|
|
6
|
+
* first; this is the one shared implementation.
|
|
7
|
+
*/
|
|
8
|
+
export function sanitizeTemplateSchemaSnippets(schemaJsonText) {
|
|
9
|
+
return schemaJsonText
|
|
10
|
+
.replace(/\$\{\d+:(-?[\d.]+)\}/g, '$1') // ${1:420} -> 420
|
|
11
|
+
.replace(/\$\{\d+:([^}"]*)\}/g, '$1') // ${2:main} -> main
|
|
12
|
+
.replace(/\$\{\d+\}|\$\d+/g, ''); // ${3} / $0 -> (removed)
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=schemaUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaUtils.js","sourceRoot":"/","sources":["schemaUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAAC,cAAsB;IACjE,OAAO,cAAc;SAChB,OAAO,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAC,oBAAoB;SAC3D,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,qBAAqB;SAC1D,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,0BAA0B;AACpE,CAAC","sourcesContent":["/**\n * The shipped `templates/template-schema.json` contains VS Code snippet\n * placeholders (`${1:value}`) inside `defaultSnippets` bodies — valid for\n * Monaco/VS Code, but not strict JSON. Consumers that need to parse the\n * schema (ajv validation, editors without snippet support) must strip them\n * first; this is the one shared implementation.\n */\nexport function sanitizeTemplateSchemaSnippets(schemaJsonText: string): string {\n return schemaJsonText\n .replace(/\\$\\{\\d+:(-?[\\d.]+)\\}/g, '$1') // ${1:420} -> 420\n .replace(/\\$\\{\\d+:([^}\"]*)\\}/g, '$1') // ${2:main} -> main\n .replace(/\\$\\{\\d+\\}|\\$\\d+/g, ''); // ${3} / $0 -> (removed)\n}\n"]}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "a3-landscape-with-logo",
|
|
3
|
-
"page": {
|
|
3
|
+
"page": {
|
|
4
|
+
"widthMm": 420,
|
|
5
|
+
"heightMm": 297,
|
|
6
|
+
"marginMm": 10,
|
|
7
|
+
"backgroundColor": "#ffffff"
|
|
8
|
+
},
|
|
4
9
|
"styles": {
|
|
5
|
-
"body": {
|
|
10
|
+
"body": {
|
|
11
|
+
"fontFamily": "Arial",
|
|
12
|
+
"fontSizeMm": 3.5
|
|
13
|
+
}
|
|
6
14
|
},
|
|
7
|
-
"
|
|
8
|
-
|
|
15
|
+
"allowRemoteResources": true,
|
|
16
|
+
"resources": [
|
|
17
|
+
{
|
|
18
|
+
"id": "logo-main",
|
|
19
|
+
"src": "https://twinfinity.com/theme-assets/logo.svg"
|
|
20
|
+
}
|
|
9
21
|
],
|
|
10
22
|
"elements": [
|
|
11
23
|
{
|
|
@@ -15,7 +27,7 @@
|
|
|
15
27
|
"yMm": 10,
|
|
16
28
|
"widthMm": 80,
|
|
17
29
|
"heightMm": 20,
|
|
18
|
-
"
|
|
30
|
+
"resourceId": "logo-main",
|
|
19
31
|
"fit": "contain"
|
|
20
32
|
},
|
|
21
33
|
{
|
|
@@ -47,9 +59,19 @@
|
|
|
47
59
|
"widthMm": 410,
|
|
48
60
|
"heightMm": 230,
|
|
49
61
|
"scaleMode": "fitAndRound",
|
|
50
|
-
"preferredScales": [
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
"preferredScales": [
|
|
63
|
+
2,
|
|
64
|
+
10,
|
|
65
|
+
25,
|
|
66
|
+
50,
|
|
67
|
+
100,
|
|
68
|
+
150,
|
|
69
|
+
250,
|
|
70
|
+
500,
|
|
71
|
+
1000,
|
|
72
|
+
2500
|
|
73
|
+
],
|
|
74
|
+
"align": "center"
|
|
53
75
|
},
|
|
54
76
|
{
|
|
55
77
|
"kind": "scaleBar",
|
package/dist/types.d.ts
CHANGED
|
@@ -11,7 +11,8 @@ export interface SvgRenderStyles {
|
|
|
11
11
|
cutLines?: SvgLineStyle;
|
|
12
12
|
visibleLines?: SvgLineStyle;
|
|
13
13
|
hiddenLines?: SvgLineStyle;
|
|
14
|
-
|
|
14
|
+
silhouettesVisible?: SvgLineStyle;
|
|
15
|
+
silhouettesHidden?: SvgLineStyle;
|
|
15
16
|
featureEdges?: SvgLineStyle;
|
|
16
17
|
stairArrows?: SvgLineStyle;
|
|
17
18
|
outlineVisible?: SvgLineStyle;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"/","sources":["types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"/","sources":["types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAClC,iBAAiB,CAAC,EAAE,YAAY,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B,aAAa,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,EAAE,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,8BAA8B;AAC9B,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,eAAe,CAkBxD,CAAC"}
|