@twinfinity/printing 6.1.0-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
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { renderViewportElement } from './viewportRender';
|
|
2
|
+
import { prepareViewports } from './viewportPrep';
|
|
3
|
+
import { renderLabels } from './labelsRender';
|
|
4
|
+
import { renderQrElements } from './qrRender';
|
|
5
|
+
import { applyLegendFilter, buildLegendGroup } from './legendRender';
|
|
6
|
+
import { resolvePresets } from './presets';
|
|
7
|
+
import { resolveIncludeClasses } from './metadata';
|
|
8
|
+
import { pushWarning } from './warnings';
|
|
9
|
+
import { escapeAttr, escapeText, interpolate, getPathValue, formatNumber } from './renderUtils';
|
|
10
|
+
import { uniquePositiveDenoms } from './utils';
|
|
11
|
+
import { validateLayoutTemplate } from './validation';
|
|
12
|
+
import { validateLayoutData } from './preflight';
|
|
13
|
+
import { ELEMENT_KIND_VIEWPORT, ELEMENT_KIND_TEXT, ELEMENT_KIND_IMAGE, ELEMENT_KIND_LINE, ELEMENT_KIND_RECT, ELEMENT_KIND_CIRCLE, ELEMENT_KIND_POLYLINE, ELEMENT_KIND_POLYGON, ELEMENT_KIND_SCALE_BAR, ELEMENT_KIND_QR, ELEMENT_KIND_LABELS, ELEMENT_KIND_LEGEND, ELEMENT_KIND_GROUP } from './elementKinds';
|
|
14
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
15
|
+
const dashArray = (dash) => (dash ? ` stroke-dasharray="${escapeAttr(dash)}"` : '');
|
|
16
|
+
/**
|
|
17
|
+
* Render a layout template to SVG.
|
|
18
|
+
*
|
|
19
|
+
* This is the low-level rendering function. For most use cases, prefer {@link renderDrawing}
|
|
20
|
+
* which handles all data wiring automatically.
|
|
21
|
+
*
|
|
22
|
+
* @param template - Validated layout template (page, elements, styles, presets).
|
|
23
|
+
* @param data - Layout data with viewport items, template variables, and legend rows.
|
|
24
|
+
* @param models - Per-viewport SVG model views (geometry + transforms).
|
|
25
|
+
* @param options - Optional rendering settings (remote resources, scale overrides, metadata).
|
|
26
|
+
* @returns SVG markup string, computed viewport scales, and any warnings.
|
|
27
|
+
*/
|
|
28
|
+
export async function renderLayout(template, data, models, options) {
|
|
29
|
+
const presets = resolvePresets(template?.presets);
|
|
30
|
+
const validation = validateLayoutTemplate(template, {
|
|
31
|
+
allowRemoteResources: options?.allowRemoteResources,
|
|
32
|
+
allowDataUrls: options?.allowDataUrls,
|
|
33
|
+
maxViewports: options?.maxViewports
|
|
34
|
+
});
|
|
35
|
+
if (!validation.ok) {
|
|
36
|
+
throw new Error(`Template validation failed: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ')}`);
|
|
37
|
+
}
|
|
38
|
+
const preferredScales = uniquePositiveDenoms(options?.scaleDenominators);
|
|
39
|
+
const page = template.page;
|
|
40
|
+
const warnings = [];
|
|
41
|
+
// Flatten all elements to collect viewports, qrs, labels, legends regardless of grouping
|
|
42
|
+
const allElements = [];
|
|
43
|
+
const collectElements = (elements) => {
|
|
44
|
+
elements.forEach((el) => {
|
|
45
|
+
allElements.push(el);
|
|
46
|
+
if (el.kind === ELEMENT_KIND_GROUP) {
|
|
47
|
+
collectElements(el.children);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
collectElements(template.elements);
|
|
52
|
+
const viewports = allElements.filter((el) => el.kind === ELEMENT_KIND_VIEWPORT);
|
|
53
|
+
const dataValidation = validateLayoutData(data, viewports.map((v) => v.id));
|
|
54
|
+
if (!dataValidation.ok) {
|
|
55
|
+
throw new Error(`Layout data validation failed: ${dataValidation.errors.join('; ')}`);
|
|
56
|
+
}
|
|
57
|
+
const { viewportScales, viewportClipPolygonsLocal, viewportClipPolygonsWorld, viewportClipBounds, viewportClipLabels, viewportClipStyles,
|
|
58
|
+
// viewportClipWhere intentionally omitted — not needed for rendering
|
|
59
|
+
viewportHighlight, filteredModelsByViewport } = prepareViewports(viewports, models, data, preferredScales, presets, {
|
|
60
|
+
metadataById: options?.metadataById,
|
|
61
|
+
warnings
|
|
62
|
+
});
|
|
63
|
+
const viewportData = {};
|
|
64
|
+
Object.entries(viewportScales).forEach(([id, scale]) => {
|
|
65
|
+
viewportData[id] = {
|
|
66
|
+
scaleDenom: scale.denom,
|
|
67
|
+
scaleText: `1:${scale.denom}`,
|
|
68
|
+
mmPerModelUnit: scale.mmPerModelUnit,
|
|
69
|
+
fits: scale.fits
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const dataWithViewport = { ...data, viewport: viewportData };
|
|
73
|
+
const sorted = [...template.elements].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
74
|
+
const parts = [];
|
|
75
|
+
parts.push(`<svg xmlns="${SVG_NS}" width="${page.widthMm}mm" height="${page.heightMm}mm" viewBox="0 0 ${page.widthMm} ${page.heightMm}">`);
|
|
76
|
+
if (page.backgroundColor) {
|
|
77
|
+
parts.push(`<rect x="0" y="0" width="${page.widthMm}" height="${page.heightMm}" fill="${escapeAttr(page.backgroundColor)}"/>`);
|
|
78
|
+
}
|
|
79
|
+
const resourceMap = new Map();
|
|
80
|
+
(template.resources ?? []).forEach((resource) => resourceMap.set(resource.id, resource));
|
|
81
|
+
const qrElements = allElements.filter((el) => el.kind === ELEMENT_KIND_QR);
|
|
82
|
+
const labelElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LABELS);
|
|
83
|
+
const viewportRenderInfo = {};
|
|
84
|
+
// Compute every legend ONCE, before viewport rendering: fill: "legend"
|
|
85
|
+
// needs the color assignments first, and the drawn legend must be built
|
|
86
|
+
// from the exact same rows — computing twice is how fill colors drift
|
|
87
|
+
// from the legend swatches. applyLegendFilter works in world space, so no
|
|
88
|
+
// render-time state is needed here. Results are keyed by element
|
|
89
|
+
// reference (immune to duplicate ids) and reused by the render pass.
|
|
90
|
+
const legendColorMaps = new Map(); // viewport ID -> color assignment
|
|
91
|
+
const legendResults = new Map();
|
|
92
|
+
const legendElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LEGEND);
|
|
93
|
+
legendElements.forEach((legend) => {
|
|
94
|
+
const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);
|
|
95
|
+
const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];
|
|
96
|
+
const legendDataFiltered = applyLegendFilter(rawLegendItems, legend, viewportClipPolygonsWorld);
|
|
97
|
+
if (!legendDataFiltered.length) {
|
|
98
|
+
if (!rawLegendItems.length) {
|
|
99
|
+
pushWarning(warnings, `Legend '${legend.id}' has no items from viewport '${legend.viewport}'.`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
pushWarning(warnings, `Legend '${legend.id}' filter matched 0 of ${rawLegendItems.length} items (class=${JSON.stringify(legend.class)}, where=${JSON.stringify(legend.where)}).`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const vpEl = viewports.find((v) => v.id === legend.viewport);
|
|
106
|
+
// Swatch opacity mirrors the fill rule the legend actually drives
|
|
107
|
+
// (fill: "legend"), falling back to the first fill rule.
|
|
108
|
+
const fillRule = vpEl?.fills?.find((f) => f.fill === 'legend') ?? vpEl?.fills?.[0];
|
|
109
|
+
const fillOpacity = fillRule ? (fillRule.fillOpacity ?? 0.35) : undefined;
|
|
110
|
+
const result = buildLegendGroup(legend, template.styles, page, page.marginMm, legendDataFiltered, fillOpacity);
|
|
111
|
+
legendResults.set(legend, result);
|
|
112
|
+
if (result?.colors) {
|
|
113
|
+
if (legendColorMaps.has(legend.viewport)) {
|
|
114
|
+
pushWarning(warnings, `Multiple legends with assignColors target viewport '${legend.viewport}' — fill: "legend" uses the last one.`);
|
|
115
|
+
}
|
|
116
|
+
legendColorMaps.set(legend.viewport, result.colors);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const renderElement = (el) => {
|
|
120
|
+
if (el.kind === ELEMENT_KIND_RECT) {
|
|
121
|
+
const r = el;
|
|
122
|
+
parts.push(`<rect x="${r.xMm}" y="${r.yMm}" width="${r.widthMm}" height="${r.heightMm}"` +
|
|
123
|
+
(r.stroke ? ` stroke="${escapeAttr(r.stroke)}"` : '') +
|
|
124
|
+
(r.strokeWidthMm ? ` stroke-width="${r.strokeWidthMm}"` : '') +
|
|
125
|
+
(r.fill ? ` fill="${escapeAttr(r.fill)}"` : ' fill="none"') +
|
|
126
|
+
dashArray(r.dash) +
|
|
127
|
+
` />`);
|
|
128
|
+
}
|
|
129
|
+
else if (el.kind === ELEMENT_KIND_LINE) {
|
|
130
|
+
const l = el;
|
|
131
|
+
parts.push(`<line x1="${l.x1Mm}" y1="${l.y1Mm}" x2="${l.x2Mm}" y2="${l.y2Mm}"` +
|
|
132
|
+
(l.stroke ? ` stroke="${escapeAttr(l.stroke)}"` : '') +
|
|
133
|
+
(l.strokeWidthMm ? ` stroke-width="${l.strokeWidthMm}"` : '') +
|
|
134
|
+
dashArray(l.dash) +
|
|
135
|
+
` />`);
|
|
136
|
+
}
|
|
137
|
+
else if (el.kind === ELEMENT_KIND_CIRCLE) {
|
|
138
|
+
const c = el;
|
|
139
|
+
parts.push(`<circle cx="${c.cxMm}" cy="${c.cyMm}" r="${c.rMm}"` +
|
|
140
|
+
(c.stroke ? ` stroke="${escapeAttr(c.stroke)}"` : '') +
|
|
141
|
+
(c.strokeWidthMm ? ` stroke-width="${c.strokeWidthMm}"` : '') +
|
|
142
|
+
(c.fill ? ` fill="${escapeAttr(c.fill)}"` : ' fill="none"') +
|
|
143
|
+
dashArray(c.dash) +
|
|
144
|
+
` />`);
|
|
145
|
+
}
|
|
146
|
+
else if (el.kind === ELEMENT_KIND_POLYLINE) {
|
|
147
|
+
const pl = el;
|
|
148
|
+
const pointsStr = pl.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');
|
|
149
|
+
parts.push(`<polyline points="${pointsStr}" stroke="${escapeAttr(pl.stroke)}" stroke-width="${pl.strokeWidthMm}" fill="none"${dashArray(pl.dash)} />`);
|
|
150
|
+
}
|
|
151
|
+
else if (el.kind === ELEMENT_KIND_POLYGON) {
|
|
152
|
+
const pg = el;
|
|
153
|
+
const pointsStr = pg.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');
|
|
154
|
+
parts.push(`<polygon points="${pointsStr}"` +
|
|
155
|
+
(pg.stroke ? ` stroke="${escapeAttr(pg.stroke)}"` : '') +
|
|
156
|
+
(pg.strokeWidthMm ? ` stroke-width="${pg.strokeWidthMm}"` : '') +
|
|
157
|
+
(pg.fill ? ` fill="${escapeAttr(pg.fill)}"` : ' fill="none"') +
|
|
158
|
+
dashArray(pg.dash) +
|
|
159
|
+
` />`);
|
|
160
|
+
}
|
|
161
|
+
else if (el.kind === ELEMENT_KIND_TEXT) {
|
|
162
|
+
const tEl = el;
|
|
163
|
+
const styleName = tEl.textStyle;
|
|
164
|
+
const style = styleName ? template.styles?.[styleName] : undefined;
|
|
165
|
+
if (!style) {
|
|
166
|
+
pushWarning(warnings, `Missing text style '${styleName ?? '(none)'}' for element '${tEl.id}'.`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const text = interpolate(tEl.text, dataWithViewport, warnings);
|
|
170
|
+
const anchor = tEl.align === 'center' ? 'middle' : tEl.align === 'right' ? 'end' : 'start';
|
|
171
|
+
parts.push(`<text x="${tEl.xMm}" y="${tEl.yMm}" font-family="${escapeAttr(style.fontFamily)}" font-size="${style.fontSizeMm}"` +
|
|
172
|
+
(style.fontWeight ? ` font-weight="${escapeAttr(style.fontWeight)}"` : '') +
|
|
173
|
+
(style.color ? ` fill="${escapeAttr(style.color)}"` : '') +
|
|
174
|
+
// Escaped, not type-narrowed: validation only checks fontFamily and fontSizeMm,
|
|
175
|
+
// and these attributes legitimately accept a length with units ("0.5mm"), so
|
|
176
|
+
// forcing them to numbers would reject valid templates. Unescaped, a value of
|
|
177
|
+
// `0" onload="…` closed the attribute and injected a handler.
|
|
178
|
+
(style.letterSpacingMm !== undefined
|
|
179
|
+
? ` letter-spacing="${escapeAttr(style.letterSpacingMm)}"`
|
|
180
|
+
: '') +
|
|
181
|
+
(style.lineHeightMm !== undefined ? ` line-height="${escapeAttr(style.lineHeightMm)}"` : '') +
|
|
182
|
+
` text-anchor="${anchor}">${escapeText(text)}</text>`);
|
|
183
|
+
}
|
|
184
|
+
else if (el.kind === ELEMENT_KIND_IMAGE) {
|
|
185
|
+
const img = el;
|
|
186
|
+
const resource = resourceMap.get(img.resourceId);
|
|
187
|
+
if (!resource) {
|
|
188
|
+
pushWarning(warnings, `Missing resource '${img.resourceId}' for image '${img.id}'.`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (resource.src) {
|
|
192
|
+
parts.push(`<image href="${escapeAttr(resource.src)}" x="${img.xMm}" y="${img.yMm}" width="${img.widthMm}" height="${img.heightMm}"` +
|
|
193
|
+
(img.fit === 'cover'
|
|
194
|
+
? ' preserveAspectRatio="xMidYMid slice"'
|
|
195
|
+
: img.fit === 'stretch'
|
|
196
|
+
? ' preserveAspectRatio="none"'
|
|
197
|
+
: ' preserveAspectRatio="xMidYMid meet"') +
|
|
198
|
+
` />`);
|
|
199
|
+
}
|
|
200
|
+
else if (resource.content) {
|
|
201
|
+
parts.push(`<g transform="translate(${img.xMm} ${img.yMm}) scale(${img.widthMm} ${img.heightMm})">${resource.content}</g>`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (el.kind === ELEMENT_KIND_SCALE_BAR) {
|
|
205
|
+
const sb = el;
|
|
206
|
+
// Scale bar declares which viewport it binds to via `viewport`
|
|
207
|
+
const boundViewport = sb.viewport ?? viewports[0]?.id ?? null;
|
|
208
|
+
if (!boundViewport) {
|
|
209
|
+
pushWarning(warnings, `Scale bar '${sb.id}' is not bound to a viewport.`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const scaleInfo = viewportScales[boundViewport];
|
|
213
|
+
if (!scaleInfo) {
|
|
214
|
+
pushWarning(warnings, `Viewport '${boundViewport}' missing scale info for scale bar '${sb.id}'.`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const lengthMm = sb.maxLengthMm;
|
|
218
|
+
const segments = sb.segments ?? 10;
|
|
219
|
+
const segmentMm = lengthMm / segments;
|
|
220
|
+
const metersPerMm = 1 / scaleInfo.mmPerModelUnit;
|
|
221
|
+
const segmentMeters = segmentMm * metersPerMm;
|
|
222
|
+
const labelsEvery = Math.max(1, sb.labelEvery ?? 2);
|
|
223
|
+
const unitSuffix = sb.unit ? ` ${sb.unit}` : ' m';
|
|
224
|
+
const barParts = [];
|
|
225
|
+
const sbStroke = sb.stroke ?? '#000';
|
|
226
|
+
const sbStrokeWidth = sb.strokeWidthMm ?? 0.25;
|
|
227
|
+
for (let i = 0; i <= segments; i++) {
|
|
228
|
+
const x = sb.xMm + segmentMm * i;
|
|
229
|
+
const isLabel = i % labelsEvery === 0;
|
|
230
|
+
const height = isLabel ? 4 : 3;
|
|
231
|
+
barParts.push(`<line x1="${x}" y1="${sb.yMm}" x2="${x}" y2="${sb.yMm - height}" stroke="${escapeAttr(sbStroke)}" stroke-width="${sbStrokeWidth}" />`);
|
|
232
|
+
if (isLabel && sb.textStyle) {
|
|
233
|
+
const style = template.styles?.[sb.textStyle];
|
|
234
|
+
if (style) {
|
|
235
|
+
const labelValue = (segmentMeters * i).toFixed(2).replace(/\.?0+$/, '');
|
|
236
|
+
barParts.push(`<text x="${x}" y="${sb.yMm + style.fontSizeMm + 1}" font-family="${escapeAttr(style.fontFamily)}" font-size="${style.fontSizeMm}" text-anchor="middle">${escapeText(labelValue + unitSuffix)}</text>`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
barParts.push(`<line x1="${sb.xMm}" y1="${sb.yMm}" x2="${sb.xMm + lengthMm}" y2="${sb.yMm}" stroke="${escapeAttr(sbStroke)}" stroke-width="${sbStrokeWidth}" />`);
|
|
241
|
+
parts.push(barParts.join(''));
|
|
242
|
+
}
|
|
243
|
+
else if (el.kind === ELEMENT_KIND_VIEWPORT) {
|
|
244
|
+
const vp = el;
|
|
245
|
+
const model = filteredModelsByViewport[vp.id] ?? models[vp.id] ?? null;
|
|
246
|
+
if (!model) {
|
|
247
|
+
pushWarning(warnings, `Viewport '${vp.id}' missing model.`);
|
|
248
|
+
}
|
|
249
|
+
// Resolved once per viewport: the same includeClasses (incl. presets) that
|
|
250
|
+
// filters the line paths also gates generic fill rules in the renderer.
|
|
251
|
+
const vpIncludeFilters = resolveIncludeClasses(vp.includeClasses, presets.includeClasses ?? {});
|
|
252
|
+
const vpParts = renderViewportElement(vp, model, dataWithViewport, preferredScales, presets.scales, resourceMap, viewportScales, viewportClipPolygonsLocal, viewportClipBounds, viewportClipLabels, viewportClipStyles, viewportHighlight, viewportRenderInfo, warnings, legendColorMaps, undefined, vpIncludeFilters);
|
|
253
|
+
parts.push(...vpParts);
|
|
254
|
+
}
|
|
255
|
+
else if (el.kind === ELEMENT_KIND_LEGEND) {
|
|
256
|
+
// Computed once in the pre-pass (same rows as the color maps).
|
|
257
|
+
const legendResult = legendResults.get(el);
|
|
258
|
+
if (legendResult) {
|
|
259
|
+
parts.push(legendResult.svg);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (el.kind === ELEMENT_KIND_GROUP) {
|
|
263
|
+
const grp = el;
|
|
264
|
+
const children = Array.isArray(grp.children) ? grp.children : [];
|
|
265
|
+
if (!children.length)
|
|
266
|
+
return;
|
|
267
|
+
const t = grp.transform;
|
|
268
|
+
const transforms = [];
|
|
269
|
+
if (t?.translateXMm || t?.translateYMm) {
|
|
270
|
+
transforms.push(`translate(${t.translateXMm ?? 0} ${t.translateYMm ?? 0})`);
|
|
271
|
+
}
|
|
272
|
+
if (t?.rotateDeg)
|
|
273
|
+
transforms.push(`rotate(${t.rotateDeg})`);
|
|
274
|
+
if (t?.scaleX !== undefined || t?.scaleY !== undefined) {
|
|
275
|
+
transforms.push(`scale(${t.scaleX ?? 1} ${t.scaleY ?? 1})`);
|
|
276
|
+
}
|
|
277
|
+
let clipRef = '';
|
|
278
|
+
if (grp.clip && grp.clip.type === 'rect') {
|
|
279
|
+
// grp.id is template-authored and lands in both id="..." and url(#...); a quote in it
|
|
280
|
+
// would close the attribute and let the rest inject arbitrary ones. Every other
|
|
281
|
+
// template value in this function is escaped the same way.
|
|
282
|
+
const clipId = escapeAttr(`group-clip-${grp.id}-${parts.length}`);
|
|
283
|
+
parts.push(`<defs><clipPath id="${clipId}"><rect x="${grp.clip.xMm}" y="${grp.clip.yMm}" width="${grp.clip.widthMm}" height="${grp.clip.heightMm}" /></clipPath></defs>`);
|
|
284
|
+
clipRef = ` clip-path="url(#${clipId})"`;
|
|
285
|
+
}
|
|
286
|
+
parts.push(`<g${transforms.length ? ` transform="${transforms.join(' ')}"` : ''}` +
|
|
287
|
+
(grp.opacity !== undefined ? ` opacity="${grp.opacity}"` : '') +
|
|
288
|
+
clipRef +
|
|
289
|
+
`>`);
|
|
290
|
+
// Labels and QR are data-driven overlays rendered globally after
|
|
291
|
+
// the element pass (they need viewport render info / async QR
|
|
292
|
+
// encoding) — group transform/opacity/clip do not apply to them.
|
|
293
|
+
if (children.some((c) => c.kind === ELEMENT_KIND_LABELS || c.kind === ELEMENT_KIND_QR)) {
|
|
294
|
+
pushWarning(warnings, `Group '${grp.id}' contains labels/qr children — these render as page-level overlays; the group's transform/opacity/clip do not apply to them.`);
|
|
295
|
+
}
|
|
296
|
+
[...children]
|
|
297
|
+
.filter((c) => c.kind !== ELEMENT_KIND_LABELS && c.kind !== ELEMENT_KIND_QR)
|
|
298
|
+
.sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0))
|
|
299
|
+
.forEach(renderElement);
|
|
300
|
+
parts.push('</g>');
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
sorted.forEach(renderElement);
|
|
304
|
+
if (labelElements.length) {
|
|
305
|
+
const labelParts = renderLabels(labelElements, data, viewportRenderInfo, viewportClipLabels, viewportClipPolygonsLocal, viewportClipPolygonsWorld, template.styles ?? {}, dataWithViewport, warnings);
|
|
306
|
+
parts.push(...labelParts);
|
|
307
|
+
}
|
|
308
|
+
if (qrElements.length) {
|
|
309
|
+
const qrParts = await renderQrElements(qrElements, viewports, viewportScales, models, { qrContext: options?.qrContext }, dataWithViewport, warnings);
|
|
310
|
+
parts.push(...qrParts);
|
|
311
|
+
}
|
|
312
|
+
parts.push('</svg>');
|
|
313
|
+
return { svg: parts.join(''), viewportScales, warnings };
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=renderLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderLayout.js","sourceRoot":"/","sources":["layout/renderLayout.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderViewportElement } from './viewportRender';\nimport { prepareViewports } from './viewportPrep';\nimport { renderLabels, ViewportRenderInfo } from './labelsRender';\nimport { renderQrElements } from './qrRender';\nimport { applyLegendFilter, buildLegendGroup, type LegendColorAssignment, type LegendResult } from './legendRender';\nimport { RenderLayoutOptions } from './options';\nimport { resolvePresets } from './presets';\nimport { resolveIncludeClasses } from './metadata';\nimport { pushWarning } from './warnings';\nimport { escapeAttr, escapeText, interpolate, getPathValue, formatNumber } from './renderUtils';\nimport { uniquePositiveDenoms } from './utils';\nimport { validateLayoutTemplate } from './validation';\nimport { validateLayoutData } from './preflight';\nimport {\n ELEMENT_KIND_VIEWPORT,\n ELEMENT_KIND_TEXT,\n ELEMENT_KIND_IMAGE,\n ELEMENT_KIND_LINE,\n ELEMENT_KIND_RECT,\n ELEMENT_KIND_CIRCLE,\n ELEMENT_KIND_POLYLINE,\n ELEMENT_KIND_POLYGON,\n ELEMENT_KIND_SCALE_BAR,\n ELEMENT_KIND_QR,\n ELEMENT_KIND_LABELS,\n ELEMENT_KIND_LEGEND,\n ELEMENT_KIND_GROUP\n} from './elementKinds';\nimport {\n ResourceSpec,\n LayoutElement,\n ViewportElement,\n TextElement,\n ImageElement,\n LineElement,\n RectElement,\n CircleElement,\n PolylineElement,\n PolygonElement,\n ScaleBarElement,\n QrElement,\n LabelsElement,\n LegendElement,\n GroupElement,\n LayoutTemplate,\n ModelContext,\n LayoutData,\n ValidationIssue,\n ComputedViewportScale\n} from './types';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/** Result of {@link renderLayout}: the final SVG markup, per-viewport scale info, and any warnings. */\nexport interface RenderLayoutResult {\n /** Complete SVG markup string ready for embedding or PDF conversion. */\n svg: string;\n /** Per-viewport computed scale information (denominator, mm-per-model-unit, fit status). */\n viewportScales: Record<string, ComputedViewportScale>;\n /** Non-fatal issues encountered during rendering (missing resources, unresolved styles, etc.). */\n warnings: string[];\n}\n\nconst dashArray = (dash?: string): string => (dash ? ` stroke-dasharray=\"${escapeAttr(dash)}\"` : '');\n\n/**\n * Render a layout template to SVG.\n *\n * This is the low-level rendering function. For most use cases, prefer {@link renderDrawing}\n * which handles all data wiring automatically.\n *\n * @param template - Validated layout template (page, elements, styles, presets).\n * @param data - Layout data with viewport items, template variables, and legend rows.\n * @param models - Per-viewport SVG model views (geometry + transforms).\n * @param options - Optional rendering settings (remote resources, scale overrides, metadata).\n * @returns SVG markup string, computed viewport scales, and any warnings.\n */\nexport async function renderLayout(\n template: LayoutTemplate,\n data: LayoutData,\n models: ModelContext,\n options?: RenderLayoutOptions\n): Promise<RenderLayoutResult> {\n const presets = resolvePresets(template?.presets);\n\n const validation = validateLayoutTemplate(template, {\n allowRemoteResources: options?.allowRemoteResources,\n allowDataUrls: options?.allowDataUrls,\n maxViewports: options?.maxViewports\n });\n if (!validation.ok) {\n throw new Error(\n `Template validation failed: ${validation.errors.map((e: ValidationIssue) => `${e.path}: ${e.message}`).join('; ')}`\n );\n }\n\n const preferredScales = uniquePositiveDenoms(options?.scaleDenominators);\n const page = template.page;\n const warnings: string[] = [];\n\n // Flatten all elements to collect viewports, qrs, labels, legends regardless of grouping\n const allElements: LayoutElement[] = [];\n const collectElements = (elements: LayoutElement[]): void => {\n elements.forEach((el) => {\n allElements.push(el);\n if (el.kind === ELEMENT_KIND_GROUP) {\n collectElements((el as GroupElement).children);\n }\n });\n };\n collectElements(template.elements);\n\n const viewports = allElements.filter((el) => el.kind === ELEMENT_KIND_VIEWPORT) as ViewportElement[];\n\n const dataValidation = validateLayoutData(\n data,\n viewports.map((v) => v.id)\n );\n if (!dataValidation.ok) {\n throw new Error(`Layout data validation failed: ${dataValidation.errors.join('; ')}`);\n }\n\n const {\n viewportScales,\n viewportClipPolygonsLocal,\n viewportClipPolygonsWorld,\n viewportClipBounds,\n viewportClipLabels,\n viewportClipStyles,\n // viewportClipWhere intentionally omitted — not needed for rendering\n viewportHighlight,\n filteredModelsByViewport\n } = prepareViewports(viewports, models, data, preferredScales, presets, {\n metadataById: options?.metadataById,\n warnings\n });\n\n const viewportData: Record<\n string,\n {\n scaleDenom: number;\n scaleText: string;\n mmPerModelUnit: number;\n fits: boolean;\n }\n > = {};\n\n Object.entries(viewportScales).forEach(([id, scale]) => {\n viewportData[id] = {\n scaleDenom: scale.denom,\n scaleText: `1:${scale.denom}`,\n mmPerModelUnit: scale.mmPerModelUnit,\n fits: scale.fits\n };\n });\n\n const dataWithViewport = { ...data, viewport: viewportData };\n\n const sorted = [...template.elements].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n\n const parts: string[] = [];\n parts.push(\n `<svg xmlns=\"${SVG_NS}\" width=\"${page.widthMm}mm\" height=\"${page.heightMm}mm\" viewBox=\"0 0 ${page.widthMm} ${page.heightMm}\">`\n );\n\n if (page.backgroundColor) {\n parts.push(\n `<rect x=\"0\" y=\"0\" width=\"${page.widthMm}\" height=\"${page.heightMm}\" fill=\"${escapeAttr(page.backgroundColor)}\"/>`\n );\n }\n\n const resourceMap = new Map<string, ResourceSpec>();\n (template.resources ?? []).forEach((resource) => resourceMap.set(resource.id, resource));\n\n const qrElements = allElements.filter((el) => el.kind === ELEMENT_KIND_QR) as QrElement[];\n const labelElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LABELS) as LabelsElement[];\n const viewportRenderInfo: Record<string, ViewportRenderInfo> = {};\n\n // Compute every legend ONCE, before viewport rendering: fill: \"legend\"\n // needs the color assignments first, and the drawn legend must be built\n // from the exact same rows — computing twice is how fill colors drift\n // from the legend swatches. applyLegendFilter works in world space, so no\n // render-time state is needed here. Results are keyed by element\n // reference (immune to duplicate ids) and reused by the render pass.\n const legendColorMaps = new Map<string, LegendColorAssignment>(); // viewport ID -> color assignment\n const legendResults = new Map<LegendElement, LegendResult | null>();\n const legendElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LEGEND) as LegendElement[];\n legendElements.forEach((legend) => {\n const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);\n const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];\n const legendDataFiltered = applyLegendFilter(rawLegendItems, legend, viewportClipPolygonsWorld);\n if (!legendDataFiltered.length) {\n if (!rawLegendItems.length) {\n pushWarning(warnings, `Legend '${legend.id}' has no items from viewport '${legend.viewport}'.`);\n } else {\n pushWarning(\n warnings,\n `Legend '${legend.id}' filter matched 0 of ${rawLegendItems.length} items (class=${JSON.stringify(legend.class)}, where=${JSON.stringify(legend.where)}).`\n );\n }\n }\n const vpEl = viewports.find((v) => v.id === legend.viewport);\n // Swatch opacity mirrors the fill rule the legend actually drives\n // (fill: \"legend\"), falling back to the first fill rule.\n const fillRule = vpEl?.fills?.find((f) => f.fill === 'legend') ?? vpEl?.fills?.[0];\n const fillOpacity = fillRule ? (fillRule.fillOpacity ?? 0.35) : undefined;\n const result = buildLegendGroup(legend, template.styles, page, page.marginMm, legendDataFiltered, fillOpacity);\n legendResults.set(legend, result);\n if (result?.colors) {\n if (legendColorMaps.has(legend.viewport)) {\n pushWarning(\n warnings,\n `Multiple legends with assignColors target viewport '${legend.viewport}' — fill: \"legend\" uses the last one.`\n );\n }\n legendColorMaps.set(legend.viewport, result.colors);\n }\n });\n\n const renderElement = (el: LayoutElement): void => {\n if (el.kind === ELEMENT_KIND_RECT) {\n const r = el as RectElement;\n parts.push(\n `<rect x=\"${r.xMm}\" y=\"${r.yMm}\" width=\"${r.widthMm}\" height=\"${r.heightMm}\"` +\n (r.stroke ? ` stroke=\"${escapeAttr(r.stroke)}\"` : '') +\n (r.strokeWidthMm ? ` stroke-width=\"${r.strokeWidthMm}\"` : '') +\n (r.fill ? ` fill=\"${escapeAttr(r.fill)}\"` : ' fill=\"none\"') +\n dashArray(r.dash) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_LINE) {\n const l = el as LineElement;\n parts.push(\n `<line x1=\"${l.x1Mm}\" y1=\"${l.y1Mm}\" x2=\"${l.x2Mm}\" y2=\"${l.y2Mm}\"` +\n (l.stroke ? ` stroke=\"${escapeAttr(l.stroke)}\"` : '') +\n (l.strokeWidthMm ? ` stroke-width=\"${l.strokeWidthMm}\"` : '') +\n dashArray(l.dash) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_CIRCLE) {\n const c = el as CircleElement;\n parts.push(\n `<circle cx=\"${c.cxMm}\" cy=\"${c.cyMm}\" r=\"${c.rMm}\"` +\n (c.stroke ? ` stroke=\"${escapeAttr(c.stroke)}\"` : '') +\n (c.strokeWidthMm ? ` stroke-width=\"${c.strokeWidthMm}\"` : '') +\n (c.fill ? ` fill=\"${escapeAttr(c.fill)}\"` : ' fill=\"none\"') +\n dashArray(c.dash) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_POLYLINE) {\n const pl = el as PolylineElement;\n const pointsStr = pl.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');\n parts.push(\n `<polyline points=\"${pointsStr}\" stroke=\"${escapeAttr(pl.stroke)}\" stroke-width=\"${pl.strokeWidthMm}\" fill=\"none\"${dashArray(pl.dash)} />`\n );\n } else if (el.kind === ELEMENT_KIND_POLYGON) {\n const pg = el as PolygonElement;\n const pointsStr = pg.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');\n parts.push(\n `<polygon points=\"${pointsStr}\"` +\n (pg.stroke ? ` stroke=\"${escapeAttr(pg.stroke)}\"` : '') +\n (pg.strokeWidthMm ? ` stroke-width=\"${pg.strokeWidthMm}\"` : '') +\n (pg.fill ? ` fill=\"${escapeAttr(pg.fill)}\"` : ' fill=\"none\"') +\n dashArray(pg.dash) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_TEXT) {\n const tEl = el as TextElement;\n const styleName = tEl.textStyle;\n const style = styleName ? template.styles?.[styleName] : undefined;\n if (!style) {\n pushWarning(warnings, `Missing text style '${styleName ?? '(none)'}' for element '${tEl.id}'.`);\n return;\n }\n const text = interpolate(tEl.text, dataWithViewport, warnings);\n const anchor = tEl.align === 'center' ? 'middle' : tEl.align === 'right' ? 'end' : 'start';\n parts.push(\n `<text x=\"${tEl.xMm}\" y=\"${tEl.yMm}\" font-family=\"${escapeAttr(style.fontFamily)}\" font-size=\"${style.fontSizeMm}\"` +\n (style.fontWeight ? ` font-weight=\"${escapeAttr(style.fontWeight)}\"` : '') +\n (style.color ? ` fill=\"${escapeAttr(style.color)}\"` : '') +\n // Escaped, not type-narrowed: validation only checks fontFamily and fontSizeMm,\n // and these attributes legitimately accept a length with units (\"0.5mm\"), so\n // forcing them to numbers would reject valid templates. Unescaped, a value of\n // `0\" onload=\"…` closed the attribute and injected a handler.\n (style.letterSpacingMm !== undefined\n ? ` letter-spacing=\"${escapeAttr(style.letterSpacingMm)}\"`\n : '') +\n (style.lineHeightMm !== undefined ? ` line-height=\"${escapeAttr(style.lineHeightMm)}\"` : '') +\n ` text-anchor=\"${anchor}\">${escapeText(text)}</text>`\n );\n } else if (el.kind === ELEMENT_KIND_IMAGE) {\n const img = el as ImageElement;\n const resource = resourceMap.get(img.resourceId);\n if (!resource) {\n pushWarning(warnings, `Missing resource '${img.resourceId}' for image '${img.id}'.`);\n return;\n }\n if (resource.src) {\n parts.push(\n `<image href=\"${escapeAttr(resource.src)}\" x=\"${img.xMm}\" y=\"${img.yMm}\" width=\"${img.widthMm}\" height=\"${img.heightMm}\"` +\n (img.fit === 'cover'\n ? ' preserveAspectRatio=\"xMidYMid slice\"'\n : img.fit === 'stretch'\n ? ' preserveAspectRatio=\"none\"'\n : ' preserveAspectRatio=\"xMidYMid meet\"') +\n ` />`\n );\n } else if (resource.content) {\n parts.push(\n `<g transform=\"translate(${img.xMm} ${img.yMm}) scale(${img.widthMm} ${img.heightMm})\">${resource.content}</g>`\n );\n }\n } else if (el.kind === ELEMENT_KIND_SCALE_BAR) {\n const sb = el as ScaleBarElement;\n // Scale bar declares which viewport it binds to via `viewport`\n const boundViewport = sb.viewport ?? viewports[0]?.id ?? null;\n if (!boundViewport) {\n pushWarning(warnings, `Scale bar '${sb.id}' is not bound to a viewport.`);\n return;\n }\n const scaleInfo = viewportScales[boundViewport];\n if (!scaleInfo) {\n pushWarning(warnings, `Viewport '${boundViewport}' missing scale info for scale bar '${sb.id}'.`);\n return;\n }\n const lengthMm = sb.maxLengthMm;\n const segments = sb.segments ?? 10;\n const segmentMm = lengthMm / segments;\n const metersPerMm = 1 / scaleInfo.mmPerModelUnit;\n const segmentMeters = segmentMm * metersPerMm;\n const labelsEvery = Math.max(1, sb.labelEvery ?? 2);\n const unitSuffix = sb.unit ? ` ${sb.unit}` : ' m';\n const barParts: string[] = [];\n const sbStroke = sb.stroke ?? '#000';\n const sbStrokeWidth = sb.strokeWidthMm ?? 0.25;\n\n for (let i = 0; i <= segments; i++) {\n const x = sb.xMm + segmentMm * i;\n const isLabel = i % labelsEvery === 0;\n const height = isLabel ? 4 : 3;\n barParts.push(\n `<line x1=\"${x}\" y1=\"${sb.yMm}\" x2=\"${x}\" y2=\"${sb.yMm - height}\" stroke=\"${escapeAttr(\n sbStroke\n )}\" stroke-width=\"${sbStrokeWidth}\" />`\n );\n if (isLabel && sb.textStyle) {\n const style = template.styles?.[sb.textStyle];\n if (style) {\n const labelValue = (segmentMeters * i).toFixed(2).replace(/\\.?0+$/, '');\n barParts.push(\n `<text x=\"${x}\" y=\"${sb.yMm + style.fontSizeMm + 1}\" font-family=\"${escapeAttr(\n style.fontFamily\n )}\" font-size=\"${style.fontSizeMm}\" text-anchor=\"middle\">${escapeText(labelValue + unitSuffix)}</text>`\n );\n }\n }\n }\n\n barParts.push(\n `<line x1=\"${sb.xMm}\" y1=\"${sb.yMm}\" x2=\"${sb.xMm + lengthMm}\" y2=\"${sb.yMm}\" stroke=\"${escapeAttr(\n sbStroke\n )}\" stroke-width=\"${sbStrokeWidth}\" />`\n );\n\n parts.push(barParts.join(''));\n } else if (el.kind === ELEMENT_KIND_VIEWPORT) {\n const vp = el as ViewportElement;\n const model = filteredModelsByViewport[vp.id] ?? models[vp.id] ?? null;\n if (!model) {\n pushWarning(warnings, `Viewport '${vp.id}' missing model.`);\n }\n // Resolved once per viewport: the same includeClasses (incl. presets) that\n // filters the line paths also gates generic fill rules in the renderer.\n const vpIncludeFilters = resolveIncludeClasses(vp.includeClasses as unknown, presets.includeClasses ?? {});\n const vpParts = renderViewportElement(\n vp,\n model,\n dataWithViewport,\n preferredScales,\n presets.scales,\n resourceMap,\n viewportScales,\n viewportClipPolygonsLocal,\n viewportClipBounds,\n viewportClipLabels,\n viewportClipStyles,\n viewportHighlight,\n viewportRenderInfo,\n warnings,\n legendColorMaps,\n undefined,\n vpIncludeFilters\n );\n parts.push(...vpParts);\n } else if (el.kind === ELEMENT_KIND_LEGEND) {\n // Computed once in the pre-pass (same rows as the color maps).\n const legendResult = legendResults.get(el as LegendElement);\n if (legendResult) {\n parts.push(legendResult.svg);\n }\n } else if (el.kind === ELEMENT_KIND_GROUP) {\n const grp = el as GroupElement;\n const children = Array.isArray(grp.children) ? grp.children : [];\n if (!children.length) return;\n\n const t = grp.transform;\n const transforms: string[] = [];\n if (t?.translateXMm || t?.translateYMm) {\n transforms.push(`translate(${t.translateXMm ?? 0} ${t.translateYMm ?? 0})`);\n }\n if (t?.rotateDeg) transforms.push(`rotate(${t.rotateDeg})`);\n if (t?.scaleX !== undefined || t?.scaleY !== undefined) {\n transforms.push(`scale(${t.scaleX ?? 1} ${t.scaleY ?? 1})`);\n }\n\n let clipRef = '';\n if (grp.clip && grp.clip.type === 'rect') {\n // grp.id is template-authored and lands in both id=\"...\" and url(#...); a quote in it\n // would close the attribute and let the rest inject arbitrary ones. Every other\n // template value in this function is escaped the same way.\n const clipId = escapeAttr(`group-clip-${grp.id}-${parts.length}`);\n parts.push(\n `<defs><clipPath id=\"${clipId}\"><rect x=\"${grp.clip.xMm}\" y=\"${grp.clip.yMm}\" width=\"${grp.clip.widthMm}\" height=\"${grp.clip.heightMm}\" /></clipPath></defs>`\n );\n clipRef = ` clip-path=\"url(#${clipId})\"`;\n }\n\n parts.push(\n `<g${transforms.length ? ` transform=\"${transforms.join(' ')}\"` : ''}` +\n (grp.opacity !== undefined ? ` opacity=\"${grp.opacity}\"` : '') +\n clipRef +\n `>`\n );\n // Labels and QR are data-driven overlays rendered globally after\n // the element pass (they need viewport render info / async QR\n // encoding) — group transform/opacity/clip do not apply to them.\n if (children.some((c) => c.kind === ELEMENT_KIND_LABELS || c.kind === ELEMENT_KIND_QR)) {\n pushWarning(\n warnings,\n `Group '${grp.id}' contains labels/qr children — these render as page-level overlays; the group's transform/opacity/clip do not apply to them.`\n );\n }\n [...children]\n .filter((c) => c.kind !== ELEMENT_KIND_LABELS && c.kind !== ELEMENT_KIND_QR)\n .sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0))\n .forEach(renderElement);\n parts.push('</g>');\n }\n };\n sorted.forEach(renderElement);\n\n if (labelElements.length) {\n const labelParts = renderLabels(\n labelElements,\n data,\n viewportRenderInfo,\n viewportClipLabels,\n viewportClipPolygonsLocal,\n viewportClipPolygonsWorld,\n template.styles ?? {},\n dataWithViewport,\n warnings\n );\n parts.push(...labelParts);\n }\n\n if (qrElements.length) {\n const qrParts = await renderQrElements(\n qrElements,\n viewports,\n viewportScales,\n models,\n { qrContext: options?.qrContext },\n dataWithViewport,\n warnings\n );\n parts.push(...qrParts);\n }\n\n parts.push('</svg>');\n\n return { svg: parts.join(''), viewportScales, warnings };\n}\n"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type PlaneBasis, type Vec3 } from '@twinfinity/geometry';
|
|
2
|
+
import { type ProductLike, type SlabSectionResult } from '@twinfinity/product-geometry';
|
|
3
|
+
import { buildSectionStateForEntries } from '../sections';
|
|
4
|
+
import { type SectionDefaults } from './sectionDefs';
|
|
5
|
+
import type { LayoutTemplate, ViewportStairArrowOptions, ProductPolylineEntry } from './types';
|
|
6
|
+
type SectionEntries = Parameters<typeof buildSectionStateForEntries>[0];
|
|
7
|
+
/**
|
|
8
|
+
* The expensive template-independent half of one viewport's render: only the
|
|
9
|
+
* geometry is cached — the SvgModelView is built per viewport at render time,
|
|
10
|
+
* so template-driven styling (lineStyles incl. dash, fills) applies without
|
|
11
|
+
* re-sectioning.
|
|
12
|
+
*/
|
|
13
|
+
export type ComputedSection = {
|
|
14
|
+
kind: 'plan';
|
|
15
|
+
geometry: SlabSectionResult<ProductLike>;
|
|
16
|
+
basis: PlaneBasis;
|
|
17
|
+
segmentCount: number;
|
|
18
|
+
/** Per-product cut polylines (world x/z) — feeds labels/legend items. */
|
|
19
|
+
entries: ProductPolylineEntry[];
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'elevation';
|
|
22
|
+
perProduct: SectionEntries;
|
|
23
|
+
normal: Vec3;
|
|
24
|
+
segmentCount: number;
|
|
25
|
+
entries: ProductPolylineEntry[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Section-geometry cache, one entry per distinct section definition. Owned by
|
|
29
|
+
* the caller (its lifetime = one loaded model; clear it when the model
|
|
30
|
+
* changes); renderTemplateSheet evicts entries no viewport references any
|
|
31
|
+
* more, keeping it bounded while sharing repeated definitions.
|
|
32
|
+
*/
|
|
33
|
+
export declare class SectionCache {
|
|
34
|
+
private readonly _map;
|
|
35
|
+
get(key: string): ComputedSection | undefined;
|
|
36
|
+
set(key: string, value: ComputedSection): void;
|
|
37
|
+
evictExcept(used: ReadonlySet<string>): void;
|
|
38
|
+
clear(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface RenderTemplateSheetInput {
|
|
41
|
+
template: LayoutTemplate;
|
|
42
|
+
/** Products with geometry — pass ALL of them (unfiltered; see above). */
|
|
43
|
+
products: ProductLike[];
|
|
44
|
+
/**
|
|
45
|
+
* Fallback plan position (world) for viewports without a section
|
|
46
|
+
* definition — typically the model AABB centre. Without it such
|
|
47
|
+
* viewports are skipped with a warning.
|
|
48
|
+
*/
|
|
49
|
+
modelCenter?: [number, number, number];
|
|
50
|
+
/** Section defaults applied where viewports omit cutHeight/depth. */
|
|
51
|
+
defaults?: SectionDefaults;
|
|
52
|
+
/** Reuse section geometry across renders (strongly recommended). */
|
|
53
|
+
cache?: SectionCache;
|
|
54
|
+
/** Context for role:'main'/'anchor' QR elements. */
|
|
55
|
+
qrContext?: {
|
|
56
|
+
host?: string | null;
|
|
57
|
+
containerName?: string | null;
|
|
58
|
+
floorName?: string | null;
|
|
59
|
+
planeHeight?: number | null;
|
|
60
|
+
};
|
|
61
|
+
/** Values for the template's declared variables ({{vars.*}}). */
|
|
62
|
+
variables?: Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Host-side authorisation for fetching `https://` resources, matching the option `renderDrawing`
|
|
65
|
+
* already takes. Without it a caller rendering a sheet had no say at all — only the template
|
|
66
|
+
* could enable remote loading — so a host that wanted to permit it for its own templates could
|
|
67
|
+
* not, and one that wanted to observe the setting had nowhere to look.
|
|
68
|
+
*/
|
|
69
|
+
allowRemoteResources?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Progress callback: overall fraction 0–1 plus a human-readable phase.
|
|
72
|
+
* Section phases stream from SlabSection per viewport (cached sections
|
|
73
|
+
* skip ahead); the final render step reports as its own phase. Wire this
|
|
74
|
+
* to a busy indicator — large models can take tens of seconds, and
|
|
75
|
+
* without feedback a long compute is indistinguishable from a hang.
|
|
76
|
+
*/
|
|
77
|
+
onProgress?: (progress: number, phase: string) => void;
|
|
78
|
+
}
|
|
79
|
+
export interface RenderedTemplateSheet {
|
|
80
|
+
svg: string;
|
|
81
|
+
warnings: string[];
|
|
82
|
+
/** Sum over viewports — 0 means no cut produced drawable geometry. */
|
|
83
|
+
segmentCount: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read a viewport's `stairArrows` geometry options into a canonical object:
|
|
87
|
+
* known fields only, fixed order, invalid values dropped — so equal options
|
|
88
|
+
* always serialize to the same cache-key fragment and unknown junk from a
|
|
89
|
+
* hand-written template can't reach the geometry pass.
|
|
90
|
+
*/
|
|
91
|
+
export declare function readViewportStairArrowOptions(vp: Record<string, unknown>): ViewportStairArrowOptions | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Render a template against a product set: resolve each viewport's section
|
|
94
|
+
* definition, compute (or reuse from `cache`) the section per distinct
|
|
95
|
+
* definition, and feed renderDrawing one model per viewport id.
|
|
96
|
+
*/
|
|
97
|
+
export declare function renderTemplateSheet(input: RenderTemplateSheetInput): Promise<RenderedTemplateSheet>;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=renderTemplateSheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderTemplateSheet.d.ts","sourceRoot":"/","sources":["layout/renderTemplateSheet.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4C,KAAK,UAAU,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5G,OAAO,EAGH,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAI1D,OAAO,EAAkE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AA2BrH,OAAO,KAAK,EACR,cAAc,EAGd,yBAAyB,EACzB,oBAAoB,EACvB,MAAM,SAAS,CAAC;AAqBjB,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC,CAAC,CAAC,CAAC;AAGxE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACrB;IACI,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACnC,GACD;IACI,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,EAAE,IAAI,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACnC,CAAC;AAER;;;;;GAKG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsC;IAE3D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAI9C,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAM5C,KAAK,IAAI,IAAI;CAGhB;AAED,MAAM,WAAW,wBAAwB;IACrC,QAAQ,EAAE,cAAc,CAAC;IACzB,yEAAyE;IACzE,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,oEAAoE;IACpE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,oDAAoD;IACpD,SAAS,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;IACF,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,yBAAyB,GAAG,SAAS,CAgBhH;AA0FD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAuJzG"}
|