@lightningjs/renderer 3.0.0-beta15 → 3.0.0-beta17
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/dist/src/common/CommonTypes.d.ts +11 -0
- package/dist/src/core/CoreNode.js +8 -0
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +14 -0
- package/dist/src/core/CoreTextNode.js +90 -5
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/Stage.js +2 -1
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/animations/Animation.d.ts +16 -0
- package/dist/src/core/animations/Animation.js +111 -0
- package/dist/src/core/animations/Animation.js.map +1 -0
- package/dist/src/core/animations/CoreAnimation.d.ts +0 -1
- package/dist/src/core/animations/CoreAnimation.js +0 -1
- package/dist/src/core/animations/CoreAnimation.js.map +1 -1
- package/dist/src/core/animations/CoreTransition.d.ts +24 -0
- package/dist/src/core/animations/CoreTransition.js +63 -0
- package/dist/src/core/animations/CoreTransition.js.map +1 -0
- package/dist/src/core/animations/Playback.d.ts +62 -0
- package/dist/src/core/animations/Playback.js +155 -0
- package/dist/src/core/animations/Playback.js.map +1 -0
- package/dist/src/core/animations/Transition.d.ts +25 -0
- package/dist/src/core/animations/Transition.js +63 -0
- package/dist/src/core/animations/Transition.js.map +1 -0
- package/dist/src/core/animations/utils.d.ts +2 -0
- package/dist/src/core/animations/utils.js +137 -0
- package/dist/src/core/animations/utils.js.map +1 -0
- package/dist/src/core/lib/collectionUtils.d.ts +5 -0
- package/dist/src/core/lib/collectionUtils.js +100 -0
- package/dist/src/core/lib/collectionUtils.js.map +1 -0
- package/dist/src/core/platforms/Platform.d.ts +5 -0
- package/dist/src/core/platforms/Platform.js.map +1 -1
- package/dist/src/core/platforms/web/WebPlatform.d.ts +1 -0
- package/dist/src/core/platforms/web/WebPlatform.js +3 -0
- package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js +4 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.js +7 -7
- package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js +3 -0
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/SdfTextRenderer.js +5 -5
- package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/TextLayoutEngine.d.ts +12 -13
- package/dist/src/core/text-rendering/TextLayoutEngine.js +242 -186
- package/dist/src/core/text-rendering/TextLayoutEngine.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +22 -7
- package/dist/src/core/utils.d.ts +1 -1
- package/dist/src/main-api/Inspector.js +9 -5
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/Renderer.js +3 -2
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/CommonTypes.ts +16 -0
- package/src/core/CoreNode.test.ts +50 -1
- package/src/core/CoreNode.ts +11 -0
- package/src/core/CoreTextNode.ts +112 -5
- package/src/core/Stage.ts +2 -1
- package/src/core/animations/CoreAnimation.ts +0 -2
- package/src/core/platforms/Platform.ts +6 -0
- package/src/core/platforms/web/WebPlatform.ts +11 -0
- package/src/core/text-rendering/CanvasFontHandler.ts +4 -7
- package/src/core/text-rendering/CanvasTextRenderer.ts +6 -8
- package/src/core/text-rendering/SdfFontHandler.ts +3 -0
- package/src/core/text-rendering/SdfTextRenderer.ts +4 -5
- package/src/core/text-rendering/TextLayoutEngine.ts +396 -226
- package/src/core/text-rendering/TextRenderer.ts +22 -7
- package/src/core/text-rendering/tests/{SdfTests.test.ts → TextLayoutEngine.test.ts} +103 -64
- package/src/main-api/Inspector.ts +9 -5
- package/src/main-api/Renderer.ts +3 -2
- package/dist/src/core/TextureError.d.ts +0 -11
- package/dist/src/core/TextureError.js +0 -37
- package/dist/src/core/TextureError.js.map +0 -1
- package/dist/src/core/platform.d.ts +0 -10
- package/dist/src/core/platform.js +0 -81
- package/dist/src/core/platform.js.map +0 -1
- package/dist/src/core/renderers/CoreShader.d.ts +0 -9
- package/dist/src/core/renderers/CoreShader.js +0 -28
- package/dist/src/core/renderers/CoreShader.js.map +0 -1
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +0 -33
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +0 -250
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +0 -1
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +0 -17
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +0 -125
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +0 -1
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +0 -14
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +0 -138
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +0 -1
- package/dist/src/core/renderers/canvas/internal/ColorUtils.d.ts +0 -19
- package/dist/src/core/renderers/canvas/internal/ColorUtils.js +0 -58
- package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +0 -1
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +0 -10
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +0 -43
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +0 -12
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +0 -58
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +0 -9
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +0 -38
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +0 -65
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +0 -269
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +0 -34
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +0 -114
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +0 -133
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +0 -616
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +0 -83
- package/dist/src/core/renderers/webgl/WebGlCoreShader.js +0 -233
- package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.d.ts +0 -9
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +0 -87
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.d.ts +0 -10
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +0 -119
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +0 -29
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +0 -413
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.d.ts +0 -28
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +0 -131
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/SdfShader.d.ts +0 -47
- package/dist/src/core/renderers/webgl/shaders/SdfShader.js +0 -160
- package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.d.ts +0 -31
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +0 -71
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.d.ts +0 -30
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +0 -58
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.d.ts +0 -31
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +0 -71
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.d.ts +0 -31
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +0 -71
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.d.ts +0 -31
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +0 -71
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.d.ts +0 -9
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js +0 -136
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.d.ts +0 -36
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +0 -85
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.d.ts +0 -45
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +0 -104
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.d.ts +0 -22
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +0 -45
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.d.ts +0 -58
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +0 -80
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.d.ts +0 -35
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +0 -129
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +0 -39
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +0 -116
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.d.ts +0 -61
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +0 -127
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.d.ts +0 -40
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +0 -71
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js.map +0 -1
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.d.ts +0 -115
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js +0 -61
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js.map +0 -1
- package/dist/src/core/text-rendering/TextRenderingUtils.d.ts +0 -12
- package/dist/src/core/text-rendering/TextRenderingUtils.js +0 -14
- package/dist/src/core/text-rendering/TextRenderingUtils.js.map +0 -1
- package/dist/src/core/text-rendering/TextTextureRendererUtils.d.ts +0 -72
- package/dist/src/core/text-rendering/TextTextureRendererUtils.js +0 -217
- package/dist/src/core/text-rendering/TextTextureRendererUtils.js.map +0 -1
- package/dist/src/core/text-rendering/TrFontManager.d.ts +0 -26
- package/dist/src/core/text-rendering/TrFontManager.js +0 -131
- package/dist/src/core/text-rendering/TrFontManager.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.d.ts +0 -39
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +0 -125
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.d.ts +0 -103
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.js +0 -21
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.d.ts +0 -62
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js +0 -88
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/TrFontFace.d.ts +0 -118
- package/dist/src/core/text-rendering/font-face-types/TrFontFace.js +0 -63
- package/dist/src/core/text-rendering/font-face-types/TrFontFace.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.d.ts +0 -14
- package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js +0 -66
- package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js.map +0 -1
- package/dist/src/core/text-rendering/font-face-types/utils.d.ts +0 -1
- package/dist/src/core/text-rendering/font-face-types/utils.js +0 -38
- package/dist/src/core/text-rendering/font-face-types/utils.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +0 -59
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +0 -397
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +0 -120
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +0 -551
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +0 -92
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +0 -607
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.d.ts +0 -12
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.js +0 -61
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.d.ts +0 -33
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.js +0 -52
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.d.ts +0 -13
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.js +0 -32
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.d.ts +0 -23
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js +0 -84
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.d.ts +0 -4
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.js +0 -34
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.d.ts +0 -20
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +0 -308
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.d.ts +0 -10
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.js +0 -40
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.d.ts +0 -26
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js +0 -70
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.d.ts +0 -16
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.js +0 -39
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.js.map +0 -1
- package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +0 -373
- package/dist/src/core/text-rendering/renderers/TextRenderer.js +0 -178
- package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +0 -1
- package/dist/src/main-api/DynamicShaderController.d.ts +0 -29
- package/dist/src/main-api/DynamicShaderController.js +0 -58
- package/dist/src/main-api/DynamicShaderController.js.map +0 -1
- package/dist/src/main-api/ShaderController.d.ts +0 -31
- package/dist/src/main-api/ShaderController.js +0 -37
- package/dist/src/main-api/ShaderController.js.map +0 -1
- package/src/core/text-rendering/tests/Canvas.test.ts +0 -378
|
@@ -7,6 +7,9 @@ import type {
|
|
|
7
7
|
WrappedLinesStruct,
|
|
8
8
|
} from './TextRenderer.js';
|
|
9
9
|
|
|
10
|
+
// Use the same space regex as Canvas renderer to handle ZWSP
|
|
11
|
+
const spaceRegex = /[ \u200B]+/g;
|
|
12
|
+
|
|
10
13
|
export const defaultFontMetrics: FontMetrics = {
|
|
11
14
|
ascender: 800,
|
|
12
15
|
descender: -200,
|
|
@@ -14,6 +17,24 @@ export const defaultFontMetrics: FontMetrics = {
|
|
|
14
17
|
unitsPerEm: 1000,
|
|
15
18
|
};
|
|
16
19
|
|
|
20
|
+
type WrapStrategyFn = (
|
|
21
|
+
measureText: MeasureTextFn,
|
|
22
|
+
word: string,
|
|
23
|
+
wordWidth: number,
|
|
24
|
+
fontFamily: string,
|
|
25
|
+
letterSpacing: number,
|
|
26
|
+
wrappedLines: TextLineStruct[],
|
|
27
|
+
currentLine: string,
|
|
28
|
+
currentLineWidth: number,
|
|
29
|
+
remainingLines: number,
|
|
30
|
+
remainingWord: string,
|
|
31
|
+
maxWidth: number,
|
|
32
|
+
space: string,
|
|
33
|
+
spaceWidth: number,
|
|
34
|
+
overflowSuffix: string,
|
|
35
|
+
overflowWidth: number,
|
|
36
|
+
) => [string, number, string];
|
|
37
|
+
|
|
17
38
|
export const normalizeFontMetrics = (
|
|
18
39
|
metrics: FontMetrics,
|
|
19
40
|
fontSize: number,
|
|
@@ -31,7 +52,6 @@ export const mapTextLayout = (
|
|
|
31
52
|
metrics: NormalizedFontMetrics,
|
|
32
53
|
text: string,
|
|
33
54
|
textAlign: string,
|
|
34
|
-
verticalAlign: string,
|
|
35
55
|
fontFamily: string,
|
|
36
56
|
lineHeight: number,
|
|
37
57
|
overflowSuffix: string,
|
|
@@ -51,13 +71,20 @@ export const mapTextLayout = (
|
|
|
51
71
|
const halfDelta = lineHeightDelta * 0.5;
|
|
52
72
|
|
|
53
73
|
let effectiveMaxLines = maxLines;
|
|
74
|
+
|
|
54
75
|
if (maxHeight > 0) {
|
|
55
|
-
|
|
76
|
+
let maxFromHeight = Math.floor(maxHeight / lineHeightPx);
|
|
77
|
+
//ensure at least 1 line
|
|
78
|
+
if (maxFromHeight < 1) {
|
|
79
|
+
maxFromHeight = 1;
|
|
80
|
+
}
|
|
56
81
|
if (effectiveMaxLines === 0 || maxFromHeight < effectiveMaxLines) {
|
|
57
82
|
effectiveMaxLines = maxFromHeight;
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
|
|
86
|
+
//trim start/end whitespace
|
|
87
|
+
// text = text.trim();
|
|
61
88
|
const wrappedText = maxWidth > 0;
|
|
62
89
|
//wrapText or just measureLines based on maxWidth
|
|
63
90
|
const [lines, remainingLines, remainingText] =
|
|
@@ -70,51 +97,49 @@ export const mapTextLayout = (
|
|
|
70
97
|
letterSpacing,
|
|
71
98
|
overflowSuffix,
|
|
72
99
|
wordBreak,
|
|
73
|
-
|
|
100
|
+
effectiveMaxLines,
|
|
74
101
|
)
|
|
75
102
|
: measureLines(
|
|
76
103
|
measureText,
|
|
77
104
|
text.split('\n'),
|
|
78
105
|
fontFamily,
|
|
79
106
|
letterSpacing,
|
|
80
|
-
|
|
107
|
+
effectiveMaxLines,
|
|
81
108
|
);
|
|
82
109
|
|
|
83
110
|
let effectiveLineAmount = lines.length;
|
|
84
|
-
let effectiveMaxWidth =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for
|
|
89
|
-
|
|
111
|
+
let effectiveMaxWidth = 0;
|
|
112
|
+
|
|
113
|
+
if (effectiveLineAmount > 0) {
|
|
114
|
+
effectiveMaxWidth = lines[0]![1];
|
|
115
|
+
//check for longest line
|
|
116
|
+
if (effectiveLineAmount > 1) {
|
|
117
|
+
for (let i = 1; i < effectiveLineAmount; i++) {
|
|
118
|
+
effectiveMaxWidth = Math.max(effectiveMaxWidth, lines[i]![1]);
|
|
119
|
+
}
|
|
90
120
|
}
|
|
91
121
|
}
|
|
92
122
|
|
|
93
123
|
//update line x offsets
|
|
94
124
|
if (textAlign !== 'left') {
|
|
95
|
-
const maxW = wrappedText === true ? maxWidth : effectiveMaxWidth;
|
|
96
125
|
for (let i = 0; i < effectiveLineAmount; i++) {
|
|
97
126
|
const line = lines[i]!;
|
|
98
127
|
const w = line[1];
|
|
99
|
-
line[
|
|
128
|
+
line[3] =
|
|
129
|
+
textAlign === 'right'
|
|
130
|
+
? effectiveMaxWidth - w
|
|
131
|
+
: (effectiveMaxWidth - w) / 2;
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
134
|
|
|
103
135
|
const effectiveMaxHeight = effectiveLineAmount * lineHeightPx;
|
|
104
136
|
|
|
105
137
|
let firstBaseLine = halfDelta;
|
|
106
|
-
if (maxHeight > 0 && verticalAlign !== 'top') {
|
|
107
|
-
if (verticalAlign === 'middle') {
|
|
108
|
-
firstBaseLine += (maxHeight - effectiveMaxHeight) / 2;
|
|
109
|
-
} else {
|
|
110
|
-
firstBaseLine += maxHeight - effectiveMaxHeight;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
138
|
|
|
114
139
|
const startY = firstBaseLine;
|
|
115
140
|
for (let i = 0; i < effectiveLineAmount; i++) {
|
|
116
141
|
const line = lines[i] as TextLineStruct;
|
|
117
|
-
line[
|
|
142
|
+
line[4] = startY + lineHeightPx * i;
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
return [
|
|
@@ -147,7 +172,7 @@ export const measureLines = (
|
|
|
147
172
|
continue;
|
|
148
173
|
}
|
|
149
174
|
const width = measureText(line, fontFamily, letterSpacing);
|
|
150
|
-
measuredLines.push([line, width, 0, 0]);
|
|
175
|
+
measuredLines.push([line, width, false, 0, 0]);
|
|
151
176
|
}
|
|
152
177
|
|
|
153
178
|
return [
|
|
@@ -172,9 +197,10 @@ export const wrapText = (
|
|
|
172
197
|
|
|
173
198
|
// Calculate space width for line wrapping
|
|
174
199
|
const spaceWidth = measureText(' ', fontFamily, letterSpacing);
|
|
200
|
+
const overflowWidth = measureText(overflowSuffix, fontFamily, letterSpacing);
|
|
175
201
|
|
|
176
202
|
let wrappedLine: TextLineStruct[] = [];
|
|
177
|
-
let remainingLines = maxLines;
|
|
203
|
+
let remainingLines = maxLines > 0 ? maxLines : 1000;
|
|
178
204
|
let hasRemainingText = true;
|
|
179
205
|
let hasMaxLines = maxLines > 0;
|
|
180
206
|
|
|
@@ -194,11 +220,11 @@ export const wrapText = (
|
|
|
194
220
|
letterSpacing,
|
|
195
221
|
spaceWidth,
|
|
196
222
|
overflowSuffix,
|
|
223
|
+
overflowWidth,
|
|
197
224
|
wordBreak,
|
|
198
225
|
remainingLines,
|
|
199
|
-
hasMaxLines,
|
|
200
226
|
)
|
|
201
|
-
: [[['', 0, 0, 0]], remainingLines, i < lines.length - 1];
|
|
227
|
+
: [[['', 0, false, 0, 0]], remainingLines, i < lines.length - 1];
|
|
202
228
|
|
|
203
229
|
remainingLines--;
|
|
204
230
|
wrappedLines.push(...wrappedLine);
|
|
@@ -206,15 +232,23 @@ export const wrapText = (
|
|
|
206
232
|
if (hasMaxLines === true && remainingLines <= 0) {
|
|
207
233
|
const lastLine = wrappedLines[wrappedLines.length - 1]!;
|
|
208
234
|
if (i < lines.length - 1) {
|
|
209
|
-
if
|
|
210
|
-
|
|
235
|
+
//check if line is truncated already
|
|
236
|
+
if (lastLine[2] === false) {
|
|
237
|
+
let remainingText = '';
|
|
238
|
+
const [line, lineWidth] = truncateLineEnd(
|
|
211
239
|
measureText,
|
|
212
|
-
lastLine[0],
|
|
213
240
|
fontFamily,
|
|
214
|
-
maxWidth,
|
|
215
241
|
letterSpacing,
|
|
242
|
+
lastLine[0],
|
|
243
|
+
lastLine[1],
|
|
244
|
+
remainingText,
|
|
245
|
+
maxWidth,
|
|
216
246
|
overflowSuffix,
|
|
247
|
+
overflowWidth,
|
|
217
248
|
);
|
|
249
|
+
lastLine[0] = line;
|
|
250
|
+
lastLine[1] = lineWidth;
|
|
251
|
+
lastLine[2] = true;
|
|
218
252
|
}
|
|
219
253
|
}
|
|
220
254
|
break;
|
|
@@ -232,12 +266,10 @@ export const wrapLine = (
|
|
|
232
266
|
letterSpacing: number,
|
|
233
267
|
spaceWidth: number,
|
|
234
268
|
overflowSuffix: string,
|
|
269
|
+
overflowWidth: number,
|
|
235
270
|
wordBreak: string,
|
|
236
271
|
remainingLines: number,
|
|
237
|
-
hasMaxLines: boolean,
|
|
238
272
|
): WrappedLinesStruct => {
|
|
239
|
-
// Use the same space regex as Canvas renderer to handle ZWSP
|
|
240
|
-
const spaceRegex = / |\u200B/g;
|
|
241
273
|
const words = line.split(spaceRegex);
|
|
242
274
|
const spaces = line.match(spaceRegex) || [];
|
|
243
275
|
const wrappedLines: TextLineStruct[] = [];
|
|
@@ -245,258 +277,396 @@ export const wrapLine = (
|
|
|
245
277
|
let currentLineWidth = 0;
|
|
246
278
|
let hasRemainingText = true;
|
|
247
279
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
const wrapFn = getWrapStrategy(wordBreak);
|
|
281
|
+
while (words.length > 0 && remainingLines > 0) {
|
|
282
|
+
let word = words.shift()!;
|
|
283
|
+
let wordWidth = measureText(word, fontFamily, letterSpacing);
|
|
284
|
+
let remainingWord = '';
|
|
285
|
+
|
|
286
|
+
//handle first word of new line separately to avoid empty line issues
|
|
287
|
+
if (currentLineWidth === 0) {
|
|
288
|
+
// Word doesn't fit on current line
|
|
289
|
+
//if first word doesn't fit on empty line
|
|
290
|
+
if (wordWidth > maxWidth) {
|
|
291
|
+
remainingLines--;
|
|
292
|
+
//truncate word to fit
|
|
293
|
+
[word, remainingWord, wordWidth] =
|
|
294
|
+
remainingLines === 0
|
|
295
|
+
? truncateWord(
|
|
296
|
+
measureText,
|
|
297
|
+
word,
|
|
298
|
+
wordWidth,
|
|
299
|
+
maxWidth,
|
|
300
|
+
fontFamily,
|
|
301
|
+
letterSpacing,
|
|
302
|
+
overflowSuffix,
|
|
303
|
+
overflowWidth,
|
|
304
|
+
)
|
|
305
|
+
: splitWord(
|
|
306
|
+
measureText,
|
|
307
|
+
word,
|
|
308
|
+
wordWidth,
|
|
309
|
+
maxWidth,
|
|
310
|
+
fontFamily,
|
|
311
|
+
letterSpacing,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (remainingWord.length > 0) {
|
|
315
|
+
words.unshift(remainingWord);
|
|
283
316
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
hasRemainingText = i < words.length;
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (wordBreak !== 'break-all' && currentLine.length > 0) {
|
|
292
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (wordBreak !== 'break-all') {
|
|
317
|
+
// first word doesn't fit on an empty line
|
|
318
|
+
wrappedLines.push([word, wordWidth, false, 0, 0]);
|
|
319
|
+
} else if (wordWidth + spaceWidth >= maxWidth) {
|
|
296
320
|
remainingLines--;
|
|
321
|
+
// word with space doesn't fit, but word itself fits - put on new line
|
|
322
|
+
wrappedLines.push([word, wordWidth, false, 0, 0]);
|
|
323
|
+
} else {
|
|
297
324
|
currentLine = word;
|
|
298
325
|
currentLineWidth = wordWidth;
|
|
299
326
|
}
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const space = spaces.shift() || '';
|
|
330
|
+
// For width calculation, treat ZWSP as having 0 width but regular space functionality
|
|
331
|
+
const effectiveSpaceWidth = space === '\u200B' ? 0 : spaceWidth;
|
|
332
|
+
const totalWidth = currentLineWidth + effectiveSpaceWidth + wordWidth;
|
|
300
333
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
remainingLines,
|
|
309
|
-
);
|
|
310
|
-
remainingLines = rl;
|
|
311
|
-
hasRemainingText = rt;
|
|
312
|
-
if (lines.length === 1) {
|
|
313
|
-
[currentLine, currentLineWidth] = lines[lines.length - 1]!;
|
|
314
|
-
} else {
|
|
315
|
-
for (let j = 0; j < lines.length; j++) {
|
|
316
|
-
[currentLine, currentLineWidth] = lines[j]!;
|
|
317
|
-
if (j < lines.length - 1) {
|
|
318
|
-
wrappedLines.push(lines[j]!);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
} else if (wordBreak === 'break-all') {
|
|
323
|
-
const firstLetterWidth = measureText(
|
|
324
|
-
word.charAt(0),
|
|
325
|
-
fontFamily,
|
|
326
|
-
letterSpacing,
|
|
327
|
-
);
|
|
328
|
-
let linebreak = false;
|
|
329
|
-
if (
|
|
330
|
-
currentLineWidth + firstLetterWidth + effectiveSpaceWidth >
|
|
331
|
-
maxWidth
|
|
332
|
-
) {
|
|
333
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
334
|
-
remainingLines -= 1;
|
|
335
|
-
currentLine = '';
|
|
336
|
-
currentLineWidth = 0;
|
|
337
|
-
linebreak = true;
|
|
338
|
-
}
|
|
339
|
-
const initial = maxWidth - currentLineWidth;
|
|
340
|
-
const [lines, rl, rt] = breakAll(
|
|
341
|
-
measureText,
|
|
342
|
-
word,
|
|
343
|
-
fontFamily,
|
|
344
|
-
initial,
|
|
345
|
-
maxWidth,
|
|
346
|
-
letterSpacing,
|
|
347
|
-
remainingLines,
|
|
348
|
-
);
|
|
349
|
-
remainingLines = rl;
|
|
350
|
-
hasRemainingText = rt;
|
|
351
|
-
if (linebreak === false) {
|
|
352
|
-
const [text, width] = lines[0]!;
|
|
353
|
-
currentLine += ' ' + text;
|
|
354
|
-
currentLineWidth = width;
|
|
355
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
356
|
-
}
|
|
334
|
+
if (totalWidth < maxWidth) {
|
|
335
|
+
currentLine += effectiveSpaceWidth > 0 ? space + word : word;
|
|
336
|
+
currentLineWidth = totalWidth;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// Will move to next line after loop finishes
|
|
340
|
+
remainingLines--;
|
|
357
341
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
342
|
+
if (totalWidth === maxWidth) {
|
|
343
|
+
currentLine += effectiveSpaceWidth > 0 ? space + word : word;
|
|
344
|
+
currentLineWidth = totalWidth;
|
|
345
|
+
wrappedLines.push([currentLine, currentLineWidth, false, 0, 0]);
|
|
346
|
+
currentLine = '';
|
|
347
|
+
currentLineWidth = 0;
|
|
348
|
+
continue;
|
|
365
349
|
}
|
|
366
|
-
}
|
|
367
350
|
|
|
368
|
-
|
|
369
|
-
if (currentLine.length > 0 && hasMaxLines === true && remainingLines === 0) {
|
|
370
|
-
currentLine = truncateLineWithSuffix(
|
|
351
|
+
[currentLine, currentLineWidth, remainingWord] = wrapFn(
|
|
371
352
|
measureText,
|
|
372
|
-
|
|
353
|
+
word,
|
|
354
|
+
wordWidth,
|
|
373
355
|
fontFamily,
|
|
374
|
-
maxWidth,
|
|
375
356
|
letterSpacing,
|
|
357
|
+
wrappedLines,
|
|
358
|
+
currentLine,
|
|
359
|
+
currentLineWidth,
|
|
360
|
+
remainingLines,
|
|
361
|
+
remainingWord,
|
|
362
|
+
maxWidth,
|
|
363
|
+
space,
|
|
364
|
+
spaceWidth,
|
|
376
365
|
overflowSuffix,
|
|
366
|
+
overflowWidth,
|
|
377
367
|
);
|
|
368
|
+
|
|
369
|
+
if (remainingWord.length > 0) {
|
|
370
|
+
words.unshift(remainingWord);
|
|
371
|
+
}
|
|
378
372
|
}
|
|
379
373
|
|
|
380
|
-
if (
|
|
381
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
374
|
+
if (currentLineWidth > 0 && remainingLines > 0) {
|
|
375
|
+
wrappedLines.push([currentLine, currentLineWidth, false, 0, 0]);
|
|
382
376
|
}
|
|
377
|
+
|
|
383
378
|
return [wrappedLines, remainingLines, hasRemainingText];
|
|
384
379
|
};
|
|
385
380
|
|
|
381
|
+
const getWrapStrategy = (wordBreak: string): WrapStrategyFn => {
|
|
382
|
+
//** default so probably first out */
|
|
383
|
+
if (wordBreak === 'break-word') {
|
|
384
|
+
return breakWord;
|
|
385
|
+
}
|
|
386
|
+
//** second most used */
|
|
387
|
+
if (wordBreak === 'break-all') {
|
|
388
|
+
return breakAll;
|
|
389
|
+
}
|
|
390
|
+
//** most similar to html/CSS 'normal' not really used in TV apps */
|
|
391
|
+
if (wordBreak === 'overflow') {
|
|
392
|
+
return overflow;
|
|
393
|
+
}
|
|
394
|
+
//fallback
|
|
395
|
+
return breakWord;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
//break strategies
|
|
399
|
+
|
|
386
400
|
/**
|
|
387
|
-
*
|
|
401
|
+
* Overflow wordBreak strategy, if a word partially fits add it to the line, start new line if necessary or add overflowSuffix.
|
|
402
|
+
*
|
|
403
|
+
* @remarks This strategy is similar to 'normal' in html/CSS. However
|
|
388
404
|
*/
|
|
389
|
-
export const
|
|
405
|
+
export const overflow = (
|
|
390
406
|
measureText: MeasureTextFn,
|
|
391
|
-
|
|
407
|
+
word: string,
|
|
408
|
+
wordWidth: number,
|
|
392
409
|
fontFamily: string,
|
|
393
|
-
maxWidth: number,
|
|
394
410
|
letterSpacing: number,
|
|
411
|
+
wrappedLines: TextLineStruct[],
|
|
412
|
+
currentLine: string,
|
|
413
|
+
currentLineWidth: number,
|
|
414
|
+
remainingLines: number,
|
|
415
|
+
remainingWord: string,
|
|
416
|
+
maxWidth: number,
|
|
417
|
+
space: string,
|
|
418
|
+
spaceWidth: number,
|
|
395
419
|
overflowSuffix: string,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
while (truncatedLine.length > 0) {
|
|
405
|
-
const lineWidth = measureText(truncatedLine, fontFamily, letterSpacing);
|
|
406
|
-
if (lineWidth + suffixWidth <= maxWidth) {
|
|
407
|
-
return truncatedLine + overflowSuffix;
|
|
408
|
-
}
|
|
409
|
-
truncatedLine = truncatedLine.substring(0, truncatedLine.length - 1);
|
|
420
|
+
overflowWidth: number,
|
|
421
|
+
): [string, number, string] => {
|
|
422
|
+
currentLine += space + word;
|
|
423
|
+
currentLineWidth += spaceWidth + wordWidth;
|
|
424
|
+
|
|
425
|
+
if (remainingLines === 0) {
|
|
426
|
+
currentLine += overflowSuffix;
|
|
427
|
+
currentLineWidth += overflowWidth;
|
|
410
428
|
}
|
|
411
429
|
|
|
412
|
-
|
|
430
|
+
wrappedLines.push([currentLine, currentLineWidth, true, 0, 0]);
|
|
431
|
+
return ['', 0, ''];
|
|
413
432
|
};
|
|
414
433
|
|
|
415
|
-
/**
|
|
416
|
-
* wordbreak function: https://developer.mozilla.org/en-US/docs/Web/CSS/word-break#break-word
|
|
417
|
-
*/
|
|
418
434
|
export const breakWord = (
|
|
419
435
|
measureText: MeasureTextFn,
|
|
420
436
|
word: string,
|
|
437
|
+
wordWidth: number,
|
|
421
438
|
fontFamily: string,
|
|
439
|
+
letterSpacing: number,
|
|
440
|
+
wrappedLines: TextLineStruct[],
|
|
441
|
+
currentLine: string,
|
|
442
|
+
currentLineWidth: number,
|
|
443
|
+
remainingLines: number,
|
|
444
|
+
remainingWord: string,
|
|
422
445
|
maxWidth: number,
|
|
446
|
+
space: string,
|
|
447
|
+
spaceWidth: number,
|
|
448
|
+
overflowSuffix: string,
|
|
449
|
+
overflowWidth: number,
|
|
450
|
+
): [string, number, string] => {
|
|
451
|
+
remainingWord = word;
|
|
452
|
+
if (remainingLines === 0) {
|
|
453
|
+
[currentLine, currentLineWidth, remainingWord] = truncateLineEnd(
|
|
454
|
+
measureText,
|
|
455
|
+
fontFamily,
|
|
456
|
+
letterSpacing,
|
|
457
|
+
currentLine,
|
|
458
|
+
currentLineWidth,
|
|
459
|
+
remainingWord,
|
|
460
|
+
maxWidth,
|
|
461
|
+
overflowSuffix,
|
|
462
|
+
overflowWidth,
|
|
463
|
+
);
|
|
464
|
+
wrappedLines.push([currentLine, currentLineWidth, true, 0, 0]);
|
|
465
|
+
} else {
|
|
466
|
+
wrappedLines.push([currentLine, currentLineWidth, false, 0, 0]);
|
|
467
|
+
currentLine = '';
|
|
468
|
+
currentLineWidth = 0;
|
|
469
|
+
}
|
|
470
|
+
return [currentLine, currentLineWidth, remainingWord];
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
export const breakAll = (
|
|
474
|
+
measureText: MeasureTextFn,
|
|
475
|
+
word: string,
|
|
476
|
+
wordWidth: number,
|
|
477
|
+
fontFamily: string,
|
|
423
478
|
letterSpacing: number,
|
|
479
|
+
wrappedLines: TextLineStruct[],
|
|
480
|
+
currentLine: string,
|
|
481
|
+
currentLineWidth: number,
|
|
424
482
|
remainingLines: number,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
483
|
+
remainingWord: string,
|
|
484
|
+
maxWidth: number,
|
|
485
|
+
space: string,
|
|
486
|
+
spaceWidth: number,
|
|
487
|
+
overflowSuffix: string,
|
|
488
|
+
overflowWidth: number,
|
|
489
|
+
): [string, number, string] => {
|
|
490
|
+
let remainingSpace = maxWidth - currentLineWidth;
|
|
491
|
+
if (currentLineWidth > 0) {
|
|
492
|
+
remainingSpace -= spaceWidth;
|
|
493
|
+
}
|
|
494
|
+
const truncate = remainingLines === 0;
|
|
495
|
+
[word, remainingWord, wordWidth] = truncate
|
|
496
|
+
? truncateWord(
|
|
497
|
+
measureText,
|
|
498
|
+
word,
|
|
499
|
+
wordWidth,
|
|
500
|
+
remainingSpace,
|
|
501
|
+
fontFamily,
|
|
502
|
+
letterSpacing,
|
|
503
|
+
overflowSuffix,
|
|
504
|
+
overflowWidth,
|
|
505
|
+
)
|
|
506
|
+
: splitWord(
|
|
507
|
+
measureText,
|
|
508
|
+
word,
|
|
509
|
+
wordWidth,
|
|
510
|
+
remainingSpace,
|
|
511
|
+
fontFamily,
|
|
512
|
+
letterSpacing,
|
|
513
|
+
);
|
|
514
|
+
currentLine += space + word;
|
|
515
|
+
currentLineWidth += spaceWidth + wordWidth;
|
|
516
|
+
|
|
517
|
+
// first word doesn't fit on an empty line
|
|
518
|
+
wrappedLines.push([currentLine, currentLineWidth, truncate, 0, 0]);
|
|
519
|
+
|
|
520
|
+
currentLine = '';
|
|
521
|
+
currentLineWidth = 0;
|
|
522
|
+
|
|
523
|
+
return [currentLine, currentLineWidth, remainingWord];
|
|
524
|
+
};
|
|
430
525
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
526
|
+
export const truncateLineEnd = (
|
|
527
|
+
measureText: MeasureTextFn,
|
|
528
|
+
fontFamily: string,
|
|
529
|
+
letterSpacing: number,
|
|
530
|
+
currentLine: string,
|
|
531
|
+
currentLineWidth: number,
|
|
532
|
+
remainingWord: string,
|
|
533
|
+
maxWidth: number,
|
|
534
|
+
overflowSuffix: string,
|
|
535
|
+
overflowWidth: number,
|
|
536
|
+
): [string, number, string] => {
|
|
537
|
+
if (currentLineWidth + overflowWidth <= maxWidth) {
|
|
538
|
+
currentLine += overflowSuffix;
|
|
539
|
+
currentLineWidth += overflowWidth;
|
|
540
|
+
remainingWord = '';
|
|
541
|
+
return [currentLine, currentLineWidth, remainingWord];
|
|
542
|
+
}
|
|
435
543
|
|
|
544
|
+
let truncated = false;
|
|
545
|
+
for (let i = currentLine.length - 1; i > 0; i--) {
|
|
546
|
+
const char = currentLine.charAt(i);
|
|
436
547
|
const charWidth = measureText(char, fontFamily, letterSpacing);
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
currentPart = char;
|
|
445
|
-
currentWidth = charWidth;
|
|
446
|
-
} else {
|
|
447
|
-
currentPart += char;
|
|
448
|
-
currentWidth += charWidth;
|
|
548
|
+
currentLineWidth -= charWidth;
|
|
549
|
+
if (currentLineWidth + overflowWidth <= maxWidth) {
|
|
550
|
+
currentLine = currentLine.substring(0, i) + overflowSuffix;
|
|
551
|
+
currentLineWidth += overflowWidth;
|
|
552
|
+
remainingWord = currentLine.substring(i) + ' ' + remainingWord;
|
|
553
|
+
truncated = true;
|
|
554
|
+
break;
|
|
449
555
|
}
|
|
450
556
|
}
|
|
451
557
|
|
|
452
|
-
if (
|
|
453
|
-
|
|
558
|
+
if (truncated === false) {
|
|
559
|
+
currentLine = overflowSuffix;
|
|
560
|
+
currentLineWidth = overflowWidth;
|
|
561
|
+
remainingWord = currentLine;
|
|
454
562
|
}
|
|
455
|
-
|
|
456
|
-
return [lines, remainingLines, i < word.length - 1];
|
|
563
|
+
return [currentLine, currentLineWidth, remainingWord];
|
|
457
564
|
};
|
|
458
565
|
|
|
459
|
-
|
|
460
|
-
* wordbreak function: https://developer.mozilla.org/en-US/docs/Web/CSS/word-break#break-word
|
|
461
|
-
*/
|
|
462
|
-
export const breakAll = (
|
|
566
|
+
export const truncateWord = (
|
|
463
567
|
measureText: MeasureTextFn,
|
|
464
568
|
word: string,
|
|
465
|
-
|
|
466
|
-
initial: number,
|
|
569
|
+
wordWidth: number,
|
|
467
570
|
maxWidth: number,
|
|
571
|
+
fontFamily: string,
|
|
468
572
|
letterSpacing: number,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
let currentWidth = 0;
|
|
474
|
-
let max = initial;
|
|
475
|
-
let i = 0;
|
|
476
|
-
let hasRemainingText = false;
|
|
573
|
+
overflowSuffix: string,
|
|
574
|
+
overflowWidth: number,
|
|
575
|
+
): [string, string, number] => {
|
|
576
|
+
const targetWidth = maxWidth - overflowWidth;
|
|
477
577
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
578
|
+
if (targetWidth <= 0) {
|
|
579
|
+
return ['', word, 0];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const excessWidth = wordWidth - targetWidth;
|
|
583
|
+
// If excess is small (< 50%), we're keeping most - start from back and remove
|
|
584
|
+
// If excess is large (>= 50%), we're removing most - start from front and add
|
|
585
|
+
const shouldStartFromBack = excessWidth < wordWidth / 2;
|
|
586
|
+
|
|
587
|
+
if (shouldStartFromBack === false) {
|
|
588
|
+
// Start from back - remove characters until it fits (keeping most of word)
|
|
589
|
+
let currentWidth = wordWidth;
|
|
590
|
+
for (let i = word.length - 1; i > 0; i--) {
|
|
591
|
+
const char = word.charAt(i);
|
|
592
|
+
const charWidth = measureText(char, fontFamily, letterSpacing);
|
|
593
|
+
currentWidth -= charWidth;
|
|
594
|
+
if (currentWidth <= targetWidth) {
|
|
595
|
+
const remainingWord = word.substring(i);
|
|
596
|
+
return [
|
|
597
|
+
word.substring(0, i) + overflowSuffix,
|
|
598
|
+
remainingWord,
|
|
599
|
+
currentWidth + overflowWidth,
|
|
600
|
+
];
|
|
601
|
+
}
|
|
482
602
|
}
|
|
603
|
+
// Even first character doesn't fit
|
|
604
|
+
return [overflowSuffix, word, overflowWidth];
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Start from front - add characters until we exceed limit (removing most of word)
|
|
608
|
+
let currentWidth = 0;
|
|
609
|
+
for (let i = 0; i < word.length; i++) {
|
|
483
610
|
const char = word.charAt(i);
|
|
484
611
|
const charWidth = measureText(char, fontFamily, letterSpacing);
|
|
485
|
-
if (currentWidth + charWidth >
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
currentPart += char;
|
|
493
|
-
currentWidth += charWidth;
|
|
612
|
+
if (currentWidth + charWidth > targetWidth) {
|
|
613
|
+
const remainingWord = word.substring(i);
|
|
614
|
+
return [
|
|
615
|
+
word.substring(0, i) + overflowSuffix,
|
|
616
|
+
remainingWord,
|
|
617
|
+
currentWidth + overflowWidth,
|
|
618
|
+
];
|
|
494
619
|
}
|
|
620
|
+
currentWidth += charWidth;
|
|
621
|
+
}
|
|
622
|
+
// Entire word fits (shouldn't happen, but safe fallback)
|
|
623
|
+
return [word + overflowSuffix, '', wordWidth + overflowWidth];
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
export const splitWord = (
|
|
627
|
+
measureText: MeasureTextFn,
|
|
628
|
+
word: string,
|
|
629
|
+
wordWidth: number,
|
|
630
|
+
maxWidth: number,
|
|
631
|
+
fontFamily: string,
|
|
632
|
+
letterSpacing: number,
|
|
633
|
+
): [string, string, number] => {
|
|
634
|
+
if (maxWidth <= 0) {
|
|
635
|
+
return ['', word, 0];
|
|
495
636
|
}
|
|
496
637
|
|
|
497
|
-
|
|
498
|
-
|
|
638
|
+
const excessWidth = wordWidth - maxWidth;
|
|
639
|
+
// If excess is small (< 50%), we're keeping most - start from back and remove
|
|
640
|
+
// If excess is large (>= 50%), we're removing most - start from front and add
|
|
641
|
+
const shouldStartFromBack = excessWidth < wordWidth / 2;
|
|
642
|
+
|
|
643
|
+
if (shouldStartFromBack === false) {
|
|
644
|
+
// Start from back - remove characters until it fits (keeping most of word)
|
|
645
|
+
let currentWidth = wordWidth;
|
|
646
|
+
for (let i = word.length - 1; i > 0; i--) {
|
|
647
|
+
const char = word.charAt(i);
|
|
648
|
+
const charWidth = measureText(char, fontFamily, letterSpacing);
|
|
649
|
+
currentWidth -= charWidth;
|
|
650
|
+
if (currentWidth <= maxWidth) {
|
|
651
|
+
const remainingWord = word.substring(i);
|
|
652
|
+
return [word.substring(0, i), remainingWord, currentWidth];
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// Even first character doesn't fit
|
|
656
|
+
return ['', word, 0];
|
|
499
657
|
}
|
|
500
658
|
|
|
501
|
-
|
|
659
|
+
// Start from front - add characters until we exceed limit (removing most of word)
|
|
660
|
+
let currentWidth = 0;
|
|
661
|
+
for (let i = 0; i < word.length; i++) {
|
|
662
|
+
const char = word.charAt(i);
|
|
663
|
+
const charWidth = measureText(char, fontFamily, letterSpacing);
|
|
664
|
+
if (currentWidth + charWidth > maxWidth) {
|
|
665
|
+
const remainingWord = word.substring(i);
|
|
666
|
+
return [word.substring(0, i), remainingWord, currentWidth];
|
|
667
|
+
}
|
|
668
|
+
currentWidth += charWidth;
|
|
669
|
+
}
|
|
670
|
+
// Entire word fits (shouldn't happen, but safe fallback)
|
|
671
|
+
return [word, '', wordWidth];
|
|
502
672
|
};
|