@schemd/core 0.2.1 → 0.3.1

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.
@@ -57,9 +57,11 @@ export declare function mathLabelTextWidth(value: string, cellAdvance?: number):
57
57
  /**
58
58
  * Emit escaped inline SVG text with explicit baseline restoration.
59
59
  *
60
- * Plain labels avoid `<tspan>` allocation entirely. Shifted segments carry
61
- * absolute scale and baseline metrics; each emitted `dy` is the delta from the
62
- * preceding run, so nested and consecutive scripts cannot accumulate drift.
60
+ * Baseline shifts are tracked in root-em units, but SVG resolves a `<tspan>`'s
61
+ * `dy="…em"` against *that tspan's own* (scaled) font-size. Each emitted `dy` is
62
+ * therefore the root-em delta divided by the tspan's font scale, so a shift of
63
+ * `d` root-em applied on a `s`-scaled run renders as exactly `d` — nested and
64
+ * consecutive scripts return to the parent baseline with zero drift.
63
65
  *
64
66
  * @param value - Raw micro-math label.
65
67
  * @returns Trusted compiler-owned XML text suitable inside an SVG `<text>` node.
@@ -191,33 +191,19 @@ export function renderMathLabelTspans(value) {
191
191
  if (segments.length === 1 && segments[0]?.kind === 'text' && segments[0].value === value) {
192
192
  return escapeXml(value);
193
193
  }
194
- const nested = segments.some((segment) => segment.fontScale !== undefined);
195
- if (nested) {
196
- let previousShift = 0;
197
- let markup = '';
198
- for (const segment of segments) {
199
- const shift = segment.baselineShiftEm ??
200
- (segment.kind === 'subscript' ? 0.35 : segment.kind === 'superscript' ? -0.55 : 0);
201
- const scale = segment.fontScale ?? (segment.kind === 'text' ? 1 : 0.7);
202
- const delta = Number((shift - previousShift).toFixed(4));
203
- const fontPercent = Number((scale * 100).toFixed(2));
204
- markup += `<tspan dy="${delta}em" font-size="${fontPercent}%">${escapeXml(segment.value)}</tspan>`;
205
- previousShift = shift;
206
- }
207
- if (previousShift !== 0) {
208
- markup += `<tspan dy="${Number((-previousShift).toFixed(4))}em" font-size="100%"></tspan>`;
209
- }
210
- return markup;
194
+ let previousShift = 0;
195
+ let markup = '';
196
+ for (const segment of segments) {
197
+ const shift = segment.baselineShiftEm ??
198
+ (segment.kind === 'subscript' ? 0.35 : segment.kind === 'superscript' ? -0.55 : 0);
199
+ const scale = segment.fontScale ?? (segment.kind === 'text' ? 1 : 0.7);
200
+ const delta = Number(((shift - previousShift) / scale).toFixed(4));
201
+ const fontPercent = Number((scale * 100).toFixed(2));
202
+ markup += `<tspan dy="${delta}em" font-size="${fontPercent}%">${escapeXml(segment.value)}</tspan>`;
203
+ previousShift = shift;
211
204
  }
212
- return segments
213
- .map((segment) => {
214
- const content = escapeXml(segment.value);
215
- if (segment.kind === 'text')
216
- return `<tspan dy="0">${content}</tspan>`;
217
- if (segment.kind === 'subscript') {
218
- return `<tspan dy="0.35em" font-size="70%">${content}</tspan><tspan dy="-0.35em" font-size="100%"></tspan>`;
219
- }
220
- return `<tspan dy="-0.55em" font-size="70%">${content}</tspan><tspan dy="0.55em" font-size="100%"></tspan>`;
221
- })
222
- .join('');
205
+ if (previousShift !== 0) {
206
+ markup += `<tspan dy="${Number((-previousShift).toFixed(4))}em" font-size="100%"></tspan>`;
207
+ }
208
+ return markup;
223
209
  }