render-tag 0.1.30 → 0.1.32
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/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/layout.d.ts +17 -4
- package/lib/layout.d.ts.map +1 -1
- package/lib/layout.js +45 -49
- package/lib/layout.js.map +1 -1
- package/lib/render-tag.umd.js +6 -6
- package/lib/render-tag.umd.js.map +1 -1
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RenderConfig, RenderResult, LayoutConfig, LayoutResult, DrawConfig, LayoutLine, AnyCanvas } from './types.js';
|
|
2
2
|
export type { RenderConfig, RenderResult, LayoutConfig, LayoutResult, DrawConfig, LayoutLine };
|
|
3
3
|
export { setDOMParser, type DOMParserLike } from './dom.js';
|
|
4
|
-
export {
|
|
4
|
+
export { lineBaselineOffset } from './layout.js';
|
|
5
5
|
/**
|
|
6
6
|
* Compute layout for an HTML string without rendering.
|
|
7
7
|
* Returns a reusable LayoutResult that can be drawn onto multiple targets via drawLayout().
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAAE,UAAU,EACtC,UAAU,EAAE,SAAS,EACtB,MAAM,YAAY,CAAC;AAMpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC/F,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAAE,UAAU,EACtC,UAAU,EAAE,SAAS,EACtB,MAAM,YAAY,CAAC;AAMpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC/F,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAWjD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAgCzD;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAsCpE;AAID;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA8BzD"}
|
package/lib/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolveStylesFromCSS } from './css-resolver.js';
|
|
|
3
3
|
import { buildLayoutTree } from './layout.js';
|
|
4
4
|
import { renderNode } from './render.js';
|
|
5
5
|
export { setDOMParser } from './dom.js';
|
|
6
|
-
export {
|
|
6
|
+
export { lineBaselineOffset } from './layout.js';
|
|
7
7
|
import { createFallbackMeasureCtx } from './dom.js';
|
|
8
8
|
// Default measurement context, created lazily and reused across layout()
|
|
9
9
|
// calls — safe because font/letterSpacing state is set before every
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAsB,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/layout.d.ts
CHANGED
|
@@ -11,12 +11,25 @@ export declare function buildCanvasFont(style: ResolvedStyle): string;
|
|
|
11
11
|
* exact half-leading out — measured over the whole 530-case corpus, giving
|
|
12
12
|
* Safari the Blink branch cost 214 wins against 223 losses (avg 7.50% ->
|
|
13
13
|
* 9.21%) where the exact value wins 48 against 3 (7.50% -> 6.52%).
|
|
14
|
-
*
|
|
15
|
-
* Public API: the parity suites expect per engine, and every renderer that has
|
|
16
|
-
* to place a baseline beside a render-tag canvas (@polotno/svg-export) must
|
|
17
|
-
* round the same way this does.
|
|
18
14
|
*/
|
|
19
15
|
export declare const FLOORS_LINE_BASELINE: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* `super` and `sub` are engine constants, not CSS. Blink and WebKit share
|
|
18
|
+
* theirs (`fontSize/3 + 1`, `fontSize/5 + 1`); Gecko raises by 0.34em and
|
|
19
|
+
* lowers by 0.20em. A SEPARATE question from the rounding above — Safari
|
|
20
|
+
* rounds like nobody and shifts like Blink — so never gate one on the other.
|
|
21
|
+
*/
|
|
22
|
+
export declare const BLINK_SUPER_SUB: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Baseline offset from the top of a line box, the way the engine places it:
|
|
25
|
+
* the half-leading `(lineHeight - (ascent + descent)) / 2` below the line top,
|
|
26
|
+
* plus the ascent, rounded as `FLOORS_LINE_BASELINE` says.
|
|
27
|
+
*
|
|
28
|
+
* Public API, because this is the ONE rule every renderer that places a
|
|
29
|
+
* baseline beside a render-tag canvas has to share (@polotno/svg-export, the
|
|
30
|
+
* editor's list marker). Call it rather than restate it, or the two drift.
|
|
31
|
+
*/
|
|
32
|
+
export declare function lineBaselineOffset(lineHeight: number, ascent: number, descent: number): number;
|
|
20
33
|
export declare function isTransparent(color: string): boolean;
|
|
21
34
|
export declare function getFontMetrics(ctx: CanvasRenderingContext2D, style: ResolvedStyle): {
|
|
22
35
|
ascent: number;
|
package/lib/layout.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,SAAS,EAAc,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA4C5H;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAGnF;AAgBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAc5D;
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,SAAS,EAAc,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA4C5H;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAGnF;AAgBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAc5D;AAkHD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,SAA0B,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAAY,CAAC;AAEzC;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG9F;AAuDD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,aAAa,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAkBvH;AA0DD,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,GAAG,OAAO,CAiBlF;AA4CD;;kFAEkF;AAClF,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAIzD;AAytED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,wBAAwB,EAC7B,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,kBAAkB,UAAO,EACzB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,YAAY,EAAE,UAAU,KAAK,IAAI,GACvD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAmD1D"}
|
package/lib/layout.js
CHANGED
|
@@ -158,17 +158,22 @@ function getLineHeight(ctx, style, useBulletProbe = false) {
|
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
160
|
* Which engine's line rules to follow. Only the UA string can say, because
|
|
161
|
-
* `accuracy: 'performance'` promises not to touch the DOM
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
161
|
+
* `accuracy: 'performance'` promises not to touch the DOM.
|
|
162
|
+
*
|
|
163
|
+
* Blink is the DEFAULT, and the other two are what we detect: a server-side
|
|
164
|
+
* render (no navigator, or jsdom) targets headless Chrome, so anything we
|
|
165
|
+
* cannot positively identify has to round the way Chrome does.
|
|
166
|
+
*
|
|
167
|
+
* - Gecko is the one engine that still sends a real `Gecko/<date>` product
|
|
168
|
+
* token; Blink and WebKit carry only the "like Gecko" comment, no slash.
|
|
169
|
+
* - Safari is WebKit that says neither `Chrome/` nor `jsdom/`. jsdom borrows
|
|
170
|
+
* WebKit's UA and would otherwise be mistaken for it.
|
|
171
|
+
* - `Chrome/` is matched with NO word boundary, because headless Chrome sends
|
|
172
|
+
* `HeadlessChrome/`.
|
|
168
173
|
*/
|
|
169
174
|
const UA = typeof navigator === 'undefined' ? '' : navigator.userAgent;
|
|
170
|
-
const IS_BLINK = UA === '' || /Chrome\/\d/.test(UA);
|
|
171
175
|
const IS_GECKO = /\bGecko\/\d/.test(UA);
|
|
176
|
+
const IS_SAFARI = /AppleWebKit/.test(UA) && !/Chrome\/\d/.test(UA) && !/\bjsdom\//.test(UA);
|
|
172
177
|
/**
|
|
173
178
|
* True where the engine floors a line's baseline onto a whole CSS pixel.
|
|
174
179
|
*
|
|
@@ -176,35 +181,25 @@ const IS_GECKO = /\bGecko\/\d/.test(UA);
|
|
|
176
181
|
* exact half-leading out — measured over the whole 530-case corpus, giving
|
|
177
182
|
* Safari the Blink branch cost 214 wins against 223 losses (avg 7.50% ->
|
|
178
183
|
* 9.21%) where the exact value wins 48 against 3 (7.50% -> 6.52%).
|
|
179
|
-
*
|
|
180
|
-
* Public API: the parity suites expect per engine, and every renderer that has
|
|
181
|
-
* to place a baseline beside a render-tag canvas (@polotno/svg-export) must
|
|
182
|
-
* round the same way this does.
|
|
183
184
|
*/
|
|
184
|
-
export const FLOORS_LINE_BASELINE =
|
|
185
|
+
export const FLOORS_LINE_BASELINE = !IS_GECKO && !IS_SAFARI;
|
|
185
186
|
/**
|
|
186
187
|
* `super` and `sub` are engine constants, not CSS. Blink and WebKit share
|
|
187
188
|
* theirs (`fontSize/3 + 1`, `fontSize/5 + 1`); Gecko raises by 0.34em and
|
|
188
|
-
* lowers by 0.20em.
|
|
189
|
-
*
|
|
189
|
+
* lowers by 0.20em. A SEPARATE question from the rounding above — Safari
|
|
190
|
+
* rounds like nobody and shifts like Blink — so never gate one on the other.
|
|
190
191
|
*/
|
|
191
|
-
const BLINK_SUPER_SUB = !IS_GECKO;
|
|
192
|
+
export const BLINK_SUPER_SUB = !IS_GECKO;
|
|
192
193
|
/**
|
|
193
|
-
* Baseline offset from the top of a line box, the way the engine places it
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* baseline sits that far below the line top, plus the ascent. Blink FLOORS that
|
|
197
|
-
* sum to a whole CSS pixel (`FontHeight::AddLeading`), so its DOM text stands up
|
|
198
|
-
* to 1px HIGHER than the exact value. Gecko lays the exact value out in app
|
|
199
|
-
* units. WebKit floors on 80 of 90 measured size × line-height combinations and
|
|
200
|
-
* has no exact rule we can state, so it takes the Blink branch — the one that
|
|
201
|
-
* fits it best, not one it matches everywhere.
|
|
194
|
+
* Baseline offset from the top of a line box, the way the engine places it:
|
|
195
|
+
* the half-leading `(lineHeight - (ascent + descent)) / 2` below the line top,
|
|
196
|
+
* plus the ascent, rounded as `FLOORS_LINE_BASELINE` says.
|
|
202
197
|
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
198
|
+
* Public API, because this is the ONE rule every renderer that places a
|
|
199
|
+
* baseline beside a render-tag canvas has to share (@polotno/svg-export, the
|
|
200
|
+
* editor's list marker). Call it rather than restate it, or the two drift.
|
|
206
201
|
*/
|
|
207
|
-
function lineBaselineOffset(lineHeight, ascent, descent) {
|
|
202
|
+
export function lineBaselineOffset(lineHeight, ascent, descent) {
|
|
208
203
|
const exact = (lineHeight - (ascent + descent)) / 2 + ascent;
|
|
209
204
|
return FLOORS_LINE_BASELINE ? Math.floor(exact) : exact;
|
|
210
205
|
}
|
|
@@ -263,8 +258,15 @@ export function getFontMetrics(ctx, style) {
|
|
|
263
258
|
const cached = _fontMetricsCache.get(font);
|
|
264
259
|
if (cached)
|
|
265
260
|
return cached;
|
|
261
|
+
// Restore what the caller had set: this measures with its OWN font, and a
|
|
262
|
+
// measurement must not move the ctx. Leaving it moved made the function
|
|
263
|
+
// behave differently on a cache miss than on a hit, so a caller that set a
|
|
264
|
+
// font and then measured through this was correct only while the cache was
|
|
265
|
+
// warm — `addListMarker` measured its marker on the li's face on a cold one.
|
|
266
|
+
const prev = ctx.font;
|
|
266
267
|
ctx.font = font;
|
|
267
268
|
const m = ctx.measureText('M');
|
|
269
|
+
ctx.font = prev;
|
|
268
270
|
const ascent = m.fontBoundingBoxAscent ?? m.actualBoundingBoxAscent;
|
|
269
271
|
const descent = m.fontBoundingBoxDescent ?? m.actualBoundingBoxDescent;
|
|
270
272
|
const result = { ascent, descent };
|
|
@@ -1644,6 +1646,14 @@ function layoutInlineContent(ctx, node, x, y, contentWidth, useBulletProbe = fal
|
|
|
1644
1646
|
const strutBox = leadedBox(ctx, node.style, useBulletProbe);
|
|
1645
1647
|
let lineAscent = strutBox.ascent;
|
|
1646
1648
|
let lineDescent = strutBox.descent;
|
|
1649
|
+
// A shift moves the box, not the line's baseline: positive is downward, so
|
|
1650
|
+
// it lifts the box's demand on the ascent side and adds to the descent one.
|
|
1651
|
+
const grow = (b, shift) => {
|
|
1652
|
+
if (b.ascent - shift > lineAscent)
|
|
1653
|
+
lineAscent = b.ascent - shift;
|
|
1654
|
+
if (b.descent + shift > lineDescent)
|
|
1655
|
+
lineDescent = b.descent + shift;
|
|
1656
|
+
};
|
|
1647
1657
|
for (const word of line.words) {
|
|
1648
1658
|
if (word.text === '')
|
|
1649
1659
|
continue;
|
|
@@ -1656,14 +1666,7 @@ function layoutInlineContent(ctx, node, x, y, contentWidth, useBulletProbe = fal
|
|
|
1656
1666
|
// the DOM has 40). With the shift it is idempotent — a wrapper with
|
|
1657
1667
|
// direct text contributes the identical box through its own run.
|
|
1658
1668
|
if (word.parentStyle) {
|
|
1659
|
-
|
|
1660
|
-
const parentShift = verticalAlignShift(word.parentStyle.verticalAlign, ctx, word.parentStyle, blockStyle, useBulletProbe);
|
|
1661
|
-
if (parentBox.ascent - parentShift > lineAscent) {
|
|
1662
|
-
lineAscent = parentBox.ascent - parentShift;
|
|
1663
|
-
}
|
|
1664
|
-
if (parentBox.descent + parentShift > lineDescent) {
|
|
1665
|
-
lineDescent = parentBox.descent + parentShift;
|
|
1666
|
-
}
|
|
1669
|
+
grow(leadedBox(ctx, word.parentStyle, useBulletProbe), verticalAlignShift(word.parentStyle.verticalAlign, ctx, word.parentStyle, blockStyle, useBulletProbe));
|
|
1667
1670
|
}
|
|
1668
1671
|
const box = leadedBox(ctx, word.style, useBulletProbe);
|
|
1669
1672
|
// An inline-block joins the line as an ATOMIC box: its own content
|
|
@@ -1677,15 +1680,7 @@ function layoutInlineContent(ctx, node, x, y, contentWidth, useBulletProbe = fal
|
|
|
1677
1680
|
box.ascent += extra.top;
|
|
1678
1681
|
box.descent += extra.bottom;
|
|
1679
1682
|
}
|
|
1680
|
-
|
|
1681
|
-
// so it lifts the box's demand on the ascent side and adds to the descent
|
|
1682
|
-
// side. The parent-size fallback is the one the emit pass uses, so a line
|
|
1683
|
-
// whose only content is shifted still sizes around it.
|
|
1684
|
-
const shift = atomic ? 0 : verticalAlignShift(word.style.verticalAlign, ctx, word.style, word.parentStyle ?? blockStyle, useBulletProbe);
|
|
1685
|
-
if (box.ascent - shift > lineAscent)
|
|
1686
|
-
lineAscent = box.ascent - shift;
|
|
1687
|
-
if (box.descent + shift > lineDescent)
|
|
1688
|
-
lineDescent = box.descent + shift;
|
|
1683
|
+
grow(box, atomic ? 0 : verticalAlignShift(word.style.verticalAlign, ctx, word.style, word.parentStyle ?? blockStyle, useBulletProbe));
|
|
1689
1684
|
}
|
|
1690
1685
|
const lineBoxHeight = lineAscent + lineDescent;
|
|
1691
1686
|
const lineBaselineY = curY + lineAscent;
|
|
@@ -1932,9 +1927,10 @@ function layoutInlineContent(ctx, node, x, y, contentWidth, useBulletProbe = fal
|
|
|
1932
1927
|
text: line.words.map(w => w.text).join(''),
|
|
1933
1928
|
bounds: {
|
|
1934
1929
|
x: lineLeftX,
|
|
1935
|
-
// The line box starts at curY
|
|
1936
|
-
// `lineAscent`/`lineDescent`
|
|
1937
|
-
//
|
|
1930
|
+
// The line box starts at curY — this is the CSS line box, which
|
|
1931
|
+
// `lineAscent`/`lineDescent` grew to cover every box on the line. Ink
|
|
1932
|
+
// can still overflow it (an ascender under `line-height: 1`), exactly
|
|
1933
|
+
// as it does in the DOM; a caller that clips must allow for that.
|
|
1938
1934
|
y: curY,
|
|
1939
1935
|
width: lineWidth,
|
|
1940
1936
|
height: lineBoxHeight,
|