From 85f79c1a5e4c0b422ace552c430dd389c8477a44 Mon Sep 17 00:00:00 2001 From: ljsnagy Date: Sun, 11 Jul 2021 13:23:23 +0800 Subject: [PATCH] fix: use baseline for text positioning (#2109) --- src/render/canvas/canvas-renderer.ts | 36 ++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/render/canvas/canvas-renderer.ts b/src/render/canvas/canvas-renderer.ts index 05408e0..2635686 100644 --- a/src/render/canvas/canvas-renderer.ts +++ b/src/render/canvas/canvas-renderer.ts @@ -148,13 +148,13 @@ export class CanvasRenderer { } } - renderTextWithLetterSpacing(text: TextBounds, letterSpacing: number) { + renderTextWithLetterSpacing(text: TextBounds, letterSpacing: number, baseline: number) { if (letterSpacing === 0) { - this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); + this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline); } else { const letters = toCodePoints(text.text).map(i => fromCodePoint(i)); letters.reduce((left, letter) => { - this.ctx.fillText(letter, left, text.bounds.top + text.bounds.height); + this.ctx.fillText(letter, left, text.bounds.top + baseline); return left + this.ctx.measureText(letter).width; }, text.bounds.left); @@ -181,10 +181,13 @@ export class CanvasRenderer { const [font, fontFamily, fontSize] = this.createFontStyle(styles); this.ctx.font = font; + this.ctx.textBaseline = 'alphabetic'; + + const {baseline, middle} = this.fontMetrics.getMetrics(fontFamily, fontSize); text.textBounds.forEach(text => { this.ctx.fillStyle = asString(styles.color); - this.renderTextWithLetterSpacing(text, styles.letterSpacing); + this.renderTextWithLetterSpacing(text, styles.letterSpacing, baseline); const textShadows: TextShadow = styles.textShadow; if (textShadows.length && text.text.trim().length) { @@ -214,7 +217,6 @@ export class CanvasRenderer { // Draws a line at the baseline of the font // TODO As some browsers display the line as more than 1px if the font-size is big, // need to take that into account both in position and size - const {baseline} = this.fontMetrics.getMetrics(fontFamily, fontSize); this.ctx.fillRect( text.bounds.left, Math.round(text.bounds.top + baseline), @@ -228,7 +230,6 @@ export class CanvasRenderer { break; case TEXT_DECORATION_LINE.LINE_THROUGH: // TODO try and find exact position for line-through - const {middle} = this.fontMetrics.getMetrics(fontFamily, fontSize); this.ctx.fillRect( text.bounds.left, Math.ceil(text.bounds.top + middle), @@ -371,7 +372,10 @@ export class CanvasRenderer { } if (isTextInputElement(container) && container.value.length) { - [this.ctx.font] = this.createFontStyle(styles); + const [fontFamily, fontSize] = this.createFontStyle(styles); + const {baseline} = this.fontMetrics.getMetrics(fontFamily, fontSize); + + this.ctx.font = fontFamily; this.ctx.fillStyle = asString(styles.color); this.ctx.textBaseline = 'middle'; @@ -401,7 +405,11 @@ export class CanvasRenderer { ]); this.ctx.clip(); - this.renderTextWithLetterSpacing(new TextBounds(container.value, textBounds), styles.letterSpacing); + this.renderTextWithLetterSpacing( + new TextBounds(container.value, textBounds), + styles.letterSpacing, + baseline + ); this.ctx.restore(); this.ctx.textBaseline = 'bottom'; this.ctx.textAlign = 'left'; @@ -421,7 +429,9 @@ export class CanvasRenderer { } } } else if (paint.listValue && container.styles.listStyleType !== LIST_STYLE_TYPE.NONE) { - [this.ctx.font] = this.createFontStyle(styles); + const [fontFamily, fontSize] = this.createFontStyle(styles); + + this.ctx.font = fontFamily; this.ctx.fillStyle = asString(styles.color); this.ctx.textBaseline = 'middle'; @@ -433,7 +443,13 @@ export class CanvasRenderer { computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 1 ); - this.renderTextWithLetterSpacing(new TextBounds(paint.listValue, bounds), styles.letterSpacing); + const {baseline} = this.fontMetrics.getMetrics(fontFamily, fontSize); + + this.renderTextWithLetterSpacing( + new TextBounds(paint.listValue, bounds), + styles.letterSpacing, + baseline + ); this.ctx.textBaseline = 'bottom'; this.ctx.textAlign = 'left'; }