From 3fdaa687a5e6ec70d90a8109a268444c8b46c997 Mon Sep 17 00:00:00 2001 From: singlecoder Date: Wed, 21 Sep 2022 17:57:12 +0800 Subject: [PATCH 1/8] feat(text): support word split --- packages/core/src/2d/text/TextUtils.ts | 154 ++++++++++++++++++++----- 1 file changed, 123 insertions(+), 31 deletions(-) diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index 197179b824..5469a08f5f 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -111,52 +111,126 @@ export class TextUtils { let charsWidth = 0; let maxAscent = -1; let maxDescent = -1; + let wordChars = ""; + let wordCharsWidth = 0; + let wordMaxAscent = -1; + let wordMaxDescent = -1; + let curInEnglishWord = false; for (let j = 0, m = subText.length; j < m; ++j) { const char = subText[j]; const charInfo = TextUtils._getCharInfo(char, fontString, charFont); + const charCode = char.charCodeAt(0); + const isNotEnglish = charCode === 32 || charCode > 255; const { w, offsetY } = charInfo; const halfH = charInfo.h * 0.5; const ascent = halfH + offsetY; const descent = halfH - offsetY; - if (charsWidth + w > wrapWidth) { - if (charsWidth === 0) { - lines.push(char); - lineWidths.push(w); - lineMaxSizes.push({ - ascent, - descent, - size: ascent + descent - }); + + if (isNotEnglish) { + // If it is an English word before, need to handle the previous English word and chars. + if (curInEnglishWord) { + if (charsWidth + wordCharsWidth > wrapWidth) { + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + chars = wordChars; + charsWidth = wordCharsWidth; + maxAscent = wordMaxAscent; + maxDescent = wordMaxDescent; + } else { + chars += wordChars; + charsWidth += wordCharsWidth; + maxAscent < wordMaxAscent && (maxAscent = wordMaxAscent); + maxDescent < wordMaxDescent && (maxDescent = wordMaxDescent); + } + + curInEnglishWord = false; + wordChars = ""; + wordCharsWidth = 0; + wordMaxAscent = -1; + wordMaxDescent = -1; + } + + // Handle cur char. + if (charsWidth + w > wrapWidth) { + if (charsWidth === 0) { + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, char, w, ascent, descent); + } else { + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + chars = char; + charsWidth = charInfo.xAdvance; + maxAscent = ascent; + maxDescent = descent; + } } else { - lines.push(chars); - lineWidths.push(charsWidth); - lineMaxSizes.push({ - ascent: maxAscent, - descent: maxDescent, - size: maxAscent + maxDescent - }); - chars = char; - charsWidth = charInfo.xAdvance; - maxAscent = ascent; - maxDescent = descent; + chars += char; + charsWidth += charInfo.xAdvance; + maxAscent < ascent && (maxAscent = ascent); + maxDescent < descent && (maxDescent = descent); } } else { - chars += char; - charsWidth += charInfo.xAdvance; - maxAscent < ascent && (maxAscent = ascent); - maxDescent < descent && (maxDescent = descent); + curInEnglishWord = true; + // Total width from chars and wordChars and char exceed wrap width. + if (charsWidth + wordCharsWidth + charInfo.w > wrapWidth) { + // Handle chars if it not empty. + if (charsWidth > 0) { + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + + chars = ""; + charsWidth = 0; + maxAscent = -1; + maxDescent = -1; + } + + // Total width from wordChars and char exceed wrap width. + if (wordCharsWidth + charInfo.w > wrapWidth) { + // Push wordchars to a single line, char becomes the start of a new line. + this._pushCharsToLines( + lines, + lineWidths, + lineMaxSizes, + wordChars, + wordCharsWidth, + wordMaxAscent, + wordMaxDescent + ); + wordChars = char; + wordCharsWidth = charInfo.xAdvance; + wordMaxAscent = ascent; + wordMaxDescent = descent; + } else { + wordChars += char; + wordCharsWidth += charInfo.xAdvance; + wordMaxAscent < ascent && (maxAscent = ascent); + wordMaxDescent < descent && (maxDescent = descent); + } + } else { + wordChars += char; + wordCharsWidth += charInfo.xAdvance; + wordMaxAscent < ascent && (maxAscent = ascent); + wordMaxDescent < descent && (maxDescent = descent); + } + } + } + + if (wordCharsWidth > 0) { + // If the total width from chars and wordChars exceed wrap width. + if (charsWidth + wordCharsWidth > wrapWidth) { + // Push chars to a single line. + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + charsWidth = 0; + // Push wordChars to a single line. + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, wordChars, wordCharsWidth, wordMaxAscent, wordMaxDescent); + } else { + // Merge to chars. + chars += wordChars; + charsWidth += wordCharsWidth; + maxAscent < wordMaxAscent && (maxAscent = wordMaxAscent); + maxDescent < wordMaxDescent && (maxDescent = wordMaxDescent); } } if (charsWidth > 0) { - lines.push(chars); - lineWidths.push(charsWidth); - lineMaxSizes.push({ - ascent: maxAscent, - descent: maxDescent, - size: maxAscent + maxDescent - }); + this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); } } @@ -339,6 +413,24 @@ export class TextUtils { return charInfo; } + + private static _pushCharsToLines( + lines: Array, + lineWidths: Array, + lineMaxSizes: Array, + chars: string, + charsWidth: number, + ascent: number, + descent: number + ): void { + lines.push(chars); + lineWidths.push(charsWidth); + lineMaxSizes.push({ + ascent, + descent, + size: ascent + descent + }); + } } /** From 00d0faf4470e74cba1ed31c5fc598a4e000c1586 Mon Sep 17 00:00:00 2001 From: singlecoder Date: Sun, 29 Jan 2023 15:55:22 +0800 Subject: [PATCH 2/8] feat(text): opt code --- packages/core/src/2d/text/TextUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index df5ce882cb..3766213fd7 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -120,6 +120,7 @@ export class TextUtils { const char = subText[j]; const charInfo = TextUtils._getCharInfo(char, fontString, subFont); const charCode = char.charCodeAt(0); + // 32 is space. const isNotEnglish = charCode === 32 || charCode > 255; const { w, offsetY } = charInfo; const halfH = charInfo.h * 0.5; From 3eabec6de6aeb9d6c03b827869a36d33387e1a21 Mon Sep 17 00:00:00 2001 From: singlecoder Date: Mon, 13 Feb 2023 10:47:00 +0800 Subject: [PATCH 3/8] feat(text): add letter spacing api --- packages/core/src/2d/text/TextRenderer.ts | 18 +++++++++++++++++- packages/core/src/2d/text/TextUtils.ts | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index faa40c9d7f..2a83015ec5 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -54,6 +54,8 @@ export class TextRenderer extends Renderer implements ICustomClone { @assignmentClone private _lineSpacing: number = 0; @assignmentClone + private _letterSpacing: number = 0; + @assignmentClone private _horizontalAlignment: TextHorizontalAlignment = TextHorizontalAlignment.Center; @assignmentClone private _verticalAlignment: TextVerticalAlignment = TextVerticalAlignment.Center; @@ -168,7 +170,7 @@ export class TextRenderer extends Renderer implements ICustomClone { } /** - * The space between two lines (in pixels). + * The space between two lines. */ get lineSpacing(): number { return this._lineSpacing; @@ -181,6 +183,20 @@ export class TextRenderer extends Renderer implements ICustomClone { } } + /** + * The space between two letters. + */ + get letterSpacing(): number { + return this._letterSpacing; + } + + set letterSpacing(value: number) { + if (this._letterSpacing !== value) { + this._letterSpacing = value; + this._setDirtyFlagTrue(DirtyFlag.Position); + } + } + /** * The horizontal alignment. */ diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index 58a3163ca1..51d0b50a8b 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -185,6 +185,7 @@ export class TextUtils { const lineMaxSizes = new Array(); const { _pixelsPerUnit } = Engine; const lineHeight = fontSizeInfo.size + renderer.lineSpacing * _pixelsPerUnit; + const letterSpacing = renderer.letterSpacing * _pixelsPerUnit; let width = 0; let height = renderer.height * _pixelsPerUnit; if (renderer.overflowMode === OverflowMode.Overflow) { From 16bafbf7b084bdd53c44e24b2f9a6002f9e6b916 Mon Sep 17 00:00:00 2001 From: singlecoder Date: Mon, 13 Feb 2023 12:03:05 +0800 Subject: [PATCH 4/8] fix(text): fix position error from measureTextWithWrap --- packages/core/src/2d/text/TextUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index 3766213fd7..297693eb13 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -200,14 +200,14 @@ export class TextUtils { } else { wordChars += char; wordCharsWidth += charInfo.xAdvance; - wordMaxAscent < ascent && (maxAscent = ascent); - wordMaxDescent < descent && (maxDescent = descent); + wordMaxAscent < ascent && (wordMaxAscent = maxAscent = ascent); + wordMaxDescent < descent && (wordMaxDescent = maxDescent = descent); } } else { wordChars += char; wordCharsWidth += charInfo.xAdvance; - wordMaxAscent < ascent && (maxAscent = ascent); - wordMaxDescent < descent && (maxDescent = descent); + wordMaxAscent < ascent && (wordMaxAscent = maxAscent = ascent); + wordMaxDescent < descent && (wordMaxDescent = maxDescent = descent); } } } From 1af21272c0bb4807057e09312c5b675f976bfcdc Mon Sep 17 00:00:00 2001 From: singlecoder Date: Mon, 13 Feb 2023 18:13:26 +0800 Subject: [PATCH 5/8] feat(text): opt handle for space letter --- packages/core/src/2d/text/TextUtils.ts | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index 0cc2e03098..f443100b38 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -121,7 +121,8 @@ export class TextUtils { const charInfo = TextUtils._getCharInfo(char, fontString, subFont); const charCode = char.charCodeAt(0); // 32 is space. - const isNotEnglish = charCode === 32 || charCode > 255; + const isSpace = charCode === 32; + const isNotEnglish = isSpace || charCode > 255; const { w, offsetY } = charInfo; const halfH = charInfo.h * 0.5; const ascent = halfH + offsetY; @@ -156,16 +157,25 @@ export class TextUtils { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, char, w, ascent, descent); } else { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); - chars = char; - charsWidth = charInfo.xAdvance; - maxAscent = ascent; - maxDescent = descent; + if (isSpace) { + chars = ""; + charsWidth = 0; + maxAscent = -1; + maxDescent = -1; + } else { + chars = char; + charsWidth = charInfo.xAdvance; + maxAscent = ascent; + maxDescent = descent; + } } } else { - chars += char; - charsWidth += charInfo.xAdvance; - maxAscent < ascent && (maxAscent = ascent); - maxDescent < descent && (maxDescent = descent); + if (!(isSpace && chars.length !== j)) { + chars += char; + charsWidth += charInfo.xAdvance; + maxAscent < ascent && (maxAscent = ascent); + maxDescent < descent && (maxDescent = descent); + } } } else { curInEnglishWord = true; From fc7d0c7ac29a484d106602c521b666bd7de4ffe8 Mon Sep 17 00:00:00 2001 From: singlecoder Date: Tue, 14 Feb 2023 12:34:09 +0800 Subject: [PATCH 6/8] feat(text): fix space error --- packages/core/src/2d/text/TextUtils.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index f443100b38..ad64c27011 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -115,6 +115,7 @@ export class TextUtils { let wordMaxAscent = -1; let wordMaxDescent = -1; let curInEnglishWord = false; + let isNotFirstLine = false; for (let j = 0, m = subText.length; j < m; ++j) { const char = subText[j]; @@ -122,6 +123,10 @@ export class TextUtils { const charCode = char.charCodeAt(0); // 32 is space. const isSpace = charCode === 32; + if (isSpace && isNotFirstLine && chars.length === 0 && wordChars.length === 0) { + continue; + } + const isNotEnglish = isSpace || charCode > 255; const { w, offsetY } = charInfo; const halfH = charInfo.h * 0.5; @@ -133,6 +138,7 @@ export class TextUtils { if (curInEnglishWord) { if (charsWidth + wordCharsWidth > wrapWidth) { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + isNotFirstLine = true; chars = wordChars; charsWidth = wordCharsWidth; maxAscent = wordMaxAscent; @@ -155,8 +161,10 @@ export class TextUtils { if (charsWidth + w > wrapWidth) { if (charsWidth === 0) { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, char, w, ascent, descent); + isNotFirstLine = true; } else { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + isNotFirstLine = true; if (isSpace) { chars = ""; charsWidth = 0; @@ -170,12 +178,10 @@ export class TextUtils { } } } else { - if (!(isSpace && chars.length !== j)) { - chars += char; - charsWidth += charInfo.xAdvance; - maxAscent < ascent && (maxAscent = ascent); - maxDescent < descent && (maxDescent = descent); - } + chars += char; + charsWidth += charInfo.xAdvance; + maxAscent < ascent && (maxAscent = ascent); + maxDescent < descent && (maxDescent = descent); } } else { curInEnglishWord = true; @@ -184,7 +190,7 @@ export class TextUtils { // Handle chars if it not empty. if (charsWidth > 0) { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); - + isNotFirstLine = true; chars = ""; charsWidth = 0; maxAscent = -1; @@ -203,6 +209,7 @@ export class TextUtils { wordMaxAscent, wordMaxDescent ); + isNotFirstLine = true; wordChars = char; wordCharsWidth = charInfo.xAdvance; wordMaxAscent = ascent; @@ -238,6 +245,7 @@ export class TextUtils { wordMaxAscent, wordMaxDescent ); + isNotFirstLine = true; } else { // Merge to chars. chars += wordChars; @@ -249,6 +257,7 @@ export class TextUtils { if (charsWidth > 0) { this._pushCharsToLines(lines, lineWidths, lineMaxSizes, chars, charsWidth, maxAscent, maxDescent); + isNotFirstLine = true; } } From e17817c7118bea483c18d799b9574b15f44b309b Mon Sep 17 00:00:00 2001 From: singlecoder Date: Wed, 15 Feb 2023 15:11:33 +0800 Subject: [PATCH 7/8] feat(text): support letter spacing --- packages/core/src/2d/text/TextRenderer.ts | 7 ++++--- packages/core/src/2d/text/TextUtils.ts | 13 +++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index 2a83015ec5..fef9da97f7 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -508,6 +508,7 @@ export class TextRenderer extends Renderer implements ICustomClone { const rendererWidth = this.width * _pixelsPerUnit; const halfRendererWidth = rendererWidth * 0.5; const rendererHeight = this.height * _pixelsPerUnit; + const letterSpacing = this.letterSpacing * _pixelsPerUnit; const textMetrics = this.enableWrapping ? TextUtils.measureTextWithWrap(this) @@ -548,10 +549,10 @@ export class TextRenderer extends Renderer implements ICustomClone { startX = -halfRendererWidth; break; case TextHorizontalAlignment.Center: - startX = -lineWidth * 0.5; + startX = -lineWidth * 0.5 + letterSpacing * 0.5; break; case TextHorizontalAlignment.Right: - startX = halfRendererWidth - lineWidth; + startX = halfRendererWidth - lineWidth + letterSpacing; break; } @@ -582,7 +583,7 @@ export class TextRenderer extends Renderer implements ICustomClone { j === 0 && (minX = Math.min(minX, left)); j === m && (maxX = Math.max(maxX, right)); } - startX += charInfo.xAdvance; + startX += charInfo.xAdvance + letterSpacing; } startY -= lineHeight; diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index d1b9c18ee1..6ec78dd20a 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -101,6 +101,7 @@ export class TextUtils { const lineMaxSizes = new Array(); const { _pixelsPerUnit } = Engine; const lineHeight = fontSizeInfo.size + renderer.lineSpacing * _pixelsPerUnit; + const letterSpacing = renderer.letterSpacing * _pixelsPerUnit; const wrapWidth = renderer.width * _pixelsPerUnit; let width = 0; @@ -172,14 +173,14 @@ export class TextUtils { maxDescent = -1; } else { chars = char; - charsWidth = charInfo.xAdvance; + charsWidth = charInfo.xAdvance + letterSpacing; maxAscent = ascent; maxDescent = descent; } } } else { chars += char; - charsWidth += charInfo.xAdvance; + charsWidth += charInfo.xAdvance + letterSpacing; maxAscent < ascent && (maxAscent = ascent); maxDescent < descent && (maxDescent = descent); } @@ -211,18 +212,18 @@ export class TextUtils { ); isNotFirstLine = true; wordChars = char; - wordCharsWidth = charInfo.xAdvance; + wordCharsWidth = charInfo.xAdvance + letterSpacing; wordMaxAscent = ascent; wordMaxDescent = descent; } else { wordChars += char; - wordCharsWidth += charInfo.xAdvance; + wordCharsWidth += charInfo.xAdvance + letterSpacing; wordMaxAscent < ascent && (wordMaxAscent = maxAscent = ascent); wordMaxDescent < descent && (wordMaxDescent = maxDescent = descent); } } else { wordChars += char; - wordCharsWidth += charInfo.xAdvance; + wordCharsWidth += charInfo.xAdvance + letterSpacing; wordMaxAscent < ascent && (wordMaxAscent = maxAscent = ascent); wordMaxDescent < descent && (wordMaxDescent = maxDescent = descent); } @@ -302,7 +303,7 @@ export class TextUtils { for (let j = 0, m = line.length; j < m; ++j) { const charInfo = TextUtils._getCharInfo(line[j], fontString, subFont); - curWidth += charInfo.xAdvance; + curWidth += charInfo.xAdvance + letterSpacing; const { offsetY } = charInfo; const halfH = charInfo.h * 0.5; const ascent = halfH + offsetY; From be1ba72f5d016cdba6508853e383f9a122a4ba0c Mon Sep 17 00:00:00 2001 From: singlecoder Date: Wed, 31 Jul 2024 17:43:26 +0800 Subject: [PATCH 8/8] feat(text): support text letter spacing --- packages/core/src/2d/text/TextRenderer.ts | 7 ++++--- packages/core/src/2d/text/TextUtils.ts | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index f8175e8696..946bed5342 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -570,10 +570,11 @@ export class TextRenderer extends Renderer { const { _pixelsPerUnit } = Engine; const { horizontalAlignment } = this; const pixelsPerUnitReciprocal = 1.0 / _pixelsPerUnit; - const rendererWidth = this.width * _pixelsPerUnit; + const rendererWidth = this._width * _pixelsPerUnit; const halfRendererWidth = rendererWidth * 0.5; - const rendererHeight = this.height * _pixelsPerUnit; + const rendererHeight = this._height * _pixelsPerUnit; const halfLineHeight = lineHeight * 0.5; + const letterSpacing = this._letterSpacing * _pixelsPerUnit; let startY = 0; const topDiff = lineHeight * 0.5 - lineMaxSizes[0].ascent; @@ -635,7 +636,7 @@ export class TextRenderer extends Renderer { j === firstRow && (minX = Math.min(minX, left)); maxX = Math.max(maxX, right); } - startX += charInfo.xAdvance; + startX += charInfo.xAdvance + letterSpacing; } } startY -= lineHeight; diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts index d92ca12412..29407af6e7 100644 --- a/packages/core/src/2d/text/TextUtils.ts +++ b/packages/core/src/2d/text/TextUtils.ts @@ -109,6 +109,7 @@ export class TextUtils { const pixelsPerUnit = Engine._pixelsPerUnit; const lineHeight = fontSizeInfo.size + renderer.lineSpacing * pixelsPerUnit; const wrapWidth = renderer.width * pixelsPerUnit; + const letterSpacing = renderer.letterSpacing * pixelsPerUnit; let textWidth = 0; subFont.nativeFontString = fontString; @@ -155,6 +156,7 @@ export class TextUtils { if (lineWidth + wordWidth > wrapWidth) { // Push if before line is not empty if (lineWidth > 0) { + lineWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, line, lineWidth, lineMaxAscent, lineMaxDescent); } @@ -178,6 +180,7 @@ export class TextUtils { // Handle char // At least one char in a line if (lineWidth + w > wrapWidth && lineWidth > 0) { + lineWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, line, lineWidth, lineMaxAscent, lineMaxDescent); textWidth = Math.max(textWidth, lineWidth); notFirstLine = true; @@ -186,19 +189,20 @@ export class TextUtils { lineWidth = lineMaxAscent = lineMaxDescent = 0; } else { line = char; - lineWidth = charInfo.xAdvance; + lineWidth = charInfo.xAdvance + letterSpacing; lineMaxAscent = ascent; lineMaxDescent = descent; } } else { line += char; - lineWidth += charInfo.xAdvance; + lineWidth += charInfo.xAdvance + letterSpacing; lineMaxAscent = Math.max(lineMaxAscent, ascent); lineMaxDescent = Math.max(lineMaxDescent, descent); } } else { if (wordWidth + charInfo.w > wrapWidth) { if (lineWidth > 0) { + lineWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, line, lineWidth, lineMaxAscent, lineMaxDescent); textWidth = Math.max(textWidth, lineWidth); line = ""; @@ -213,12 +217,12 @@ export class TextUtils { textWidth = Math.max(textWidth, wordWidth); notFirstLine = true; word = char; - wordWidth = charInfo.xAdvance; + wordWidth = charInfo.xAdvance + letterSpacing; wordMaxAscent = ascent; wordMaxDescent = descent; } else { word += char; - wordWidth += charInfo.xAdvance; + wordWidth += charInfo.xAdvance + letterSpacing; wordMaxAscent = Math.max(wordMaxAscent, ascent); wordMaxDescent = Math.max(wordMaxDescent, descent); } @@ -230,6 +234,7 @@ export class TextUtils { if (lineWidth + wordWidth > wrapWidth) { // Push chars to a single line if (lineWidth > 0) { + lineWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, line, lineWidth, lineMaxAscent, lineMaxDescent); } textWidth = Math.max(textWidth, lineWidth); @@ -237,6 +242,7 @@ export class TextUtils { lineWidth = 0; // Push word to a single line if (wordWidth > 0) { + wordWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, word, wordWidth, wordMaxAscent, wordMaxDescent); } textWidth = Math.max(textWidth, wordWidth); @@ -250,6 +256,7 @@ export class TextUtils { } if (lineWidth > 0) { + lineWidth -= letterSpacing; this._pushLine(lines, lineWidths, lineMaxSizes, line, lineWidth, lineMaxAscent, lineMaxDescent); textWidth = Math.max(textWidth, lineWidth); } @@ -281,16 +288,18 @@ export class TextUtils { const lineMaxSizes = new Array(); const { _pixelsPerUnit } = Engine; const lineHeight = fontSizeInfo.size + renderer.lineSpacing * _pixelsPerUnit; + const letterSpacing = renderer.letterSpacing * _pixelsPerUnit; let width = 0; subFont.nativeFontString = fontString; for (let i = 0; i < textCount; ++i) { const line = subTexts[i]; - let curWidth = 0; + const lineLength = line.length; + let curWidth = lineLength > 1 ? letterSpacing * (lineLength - 1) : 0; let maxAscent = 0; let maxDescent = 0; - for (let j = 0, m = line.length; j < m; ++j) { + for (let j = 0; j < lineLength; ++j) { const charInfo = TextUtils._getCharInfo(line[j], fontString, subFont); curWidth += charInfo.xAdvance; const { offsetY } = charInfo;