fix(text): center glyphs within line height (#422)

Enable CanvasKit half-leading for solid and gradient paragraph rendering while preserving the existing font fallback system.\n\nCo-authored-by: jongwong <jongwong@aliyun.com>
This commit is contained in:
Danila Poyarkov 2026-07-25 23:52:01 +03:00 committed by GitHub
parent 33fa9ee320
commit 0cd0ee6356
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 20 additions and 5 deletions

View file

@ -2,6 +2,10 @@
## Unreleased
### Fixed
- Center text glyphs within explicit line-height leading in CanvasKit paragraph rendering.
### Added
- Import HTML, CSS, Tailwind, and JSX as editable documents from the app, CLI, and SDK, and export standalone browser-ready HTML with compiled CSS and optional external assets.

View file

@ -636,7 +636,9 @@ function drawOutlinedText(
function drawGradientText(r: SkiaRenderer, canvas: Canvas, node: SceneNode): boolean {
if (!r.fontsLoaded || !r.fontProvider) return false
const paragraph = r.buildParagraph(node, r.ck.Color4f(0, 0, 0, 1))
const paragraph = r.buildParagraph(node, r.ck.Color4f(0, 0, 0, 1), {
halfLeading: true
})
try {
const paragraphY = textVerticalOffset(node, paragraph.getHeight())
r.effectLayerPaint.setImageFilter(null)
@ -693,7 +695,9 @@ export function renderText(r: SkiaRenderer, canvas: Canvas, node: SceneNode, fil
if (shouldRenderTextAsOutline(fill)) {
let paragraphY = 0
if (node.textAlignVertical !== 'TOP') {
const paragraph = r.buildParagraph(node, r.ck.Color4f(0, 0, 0, 1))
const paragraph = r.buildParagraph(node, r.ck.Color4f(0, 0, 0, 1), {
halfLeading: true
})
paragraphY = textVerticalOffset(node, paragraph.getHeight())
paragraph.delete()
}
@ -707,7 +711,9 @@ export function renderText(r: SkiaRenderer, canvas: Canvas, node: SceneNode, fil
return
}
if (r.fontsLoaded && r.fontProvider) {
const paragraph = r.buildParagraph(node, r.fillPaint.getColor())
const paragraph = r.buildParagraph(node, r.fillPaint.getColor(), {
halfLeading: true
})
const paragraphY = textVerticalOffset(node, paragraph.getHeight())
canvas.drawParagraph(paragraph, 0, paragraphY)
paragraph.delete()

View file

@ -35,6 +35,7 @@ test('text case vertical alignment and ending truncation', async () => {
height: 106,
text: item.label,
fontSize: 20,
lineHeight: 36,
textCase: item.textCase,
textAlignVertical: item.vertical
})

View file

@ -129,7 +129,9 @@ describe('renderText', () => {
renderText(r, canvas as never, textNode())
expect(r.buildParagraph).toHaveBeenCalledTimes(1)
expect(r.buildParagraph).toHaveBeenCalledWith(expect.anything(), expect.anything(), {
halfLeading: true
})
expect(canvas.drawParagraph).toHaveBeenCalledTimes(1)
expect(canvas.drawText).not.toHaveBeenCalled()
expect(r._paragraph.delete).toHaveBeenCalledTimes(1)
@ -158,7 +160,9 @@ describe('renderText', () => {
gradientTransform: { m00: 1, m01: 0, m02: 0, m10: 0, m11: 1, m12: 0 }
})
expect(r.buildParagraph).toHaveBeenCalledTimes(1)
expect(r.buildParagraph).toHaveBeenCalledWith(expect.anything(), expect.anything(), {
halfLeading: true
})
expect(canvas.saveLayer).toHaveBeenCalledTimes(2)
expect(canvas.drawParagraph).toHaveBeenCalledTimes(1)
expect(canvas.drawRect).toHaveBeenCalledTimes(1)