@lightningjs/renderer 3.0.0-beta16 → 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 +13 -0
- package/dist/src/core/CoreTextNode.js +68 -17
- 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/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 +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.js +3 -3
- package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/SdfTextRenderer.js +3 -3
- 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 +239 -181
- 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/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 +78 -17
- package/src/core/Stage.ts +2 -1
- package/src/core/platforms/Platform.ts +6 -0
- package/src/core/platforms/web/WebPlatform.ts +11 -0
- package/src/core/text-rendering/CanvasFontHandler.ts +1 -7
- package/src/core/text-rendering/CanvasTextRenderer.ts +2 -4
- package/src/core/text-rendering/SdfTextRenderer.ts +2 -3
- package/src/core/text-rendering/TextLayoutEngine.ts +393 -223
- 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/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 -69
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +0 -272
- 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 -641
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +0 -78
- package/dist/src/core/renderers/webgl/WebGlCoreShader.js +0 -202
- 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 -84
- 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 -108
- 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 -408
- 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 -126
- 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 -148
- 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 -134
- 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 -40
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +0 -143
- 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/CanvasFont.d.ts +0 -14
- package/dist/src/core/text-rendering/CanvasFont.js +0 -111
- package/dist/src/core/text-rendering/CanvasFont.js.map +0 -1
- package/dist/src/core/text-rendering/CoreFont.d.ts +0 -33
- package/dist/src/core/text-rendering/CoreFont.js +0 -48
- package/dist/src/core/text-rendering/CoreFont.js.map +0 -1
- package/dist/src/core/text-rendering/FontManager.d.ts +0 -11
- package/dist/src/core/text-rendering/FontManager.js +0 -42
- package/dist/src/core/text-rendering/FontManager.js.map +0 -1
- package/dist/src/core/text-rendering/SdfFont.d.ts +0 -29
- package/dist/src/core/text-rendering/SdfFont.js +0 -142
- package/dist/src/core/text-rendering/SdfFont.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,23 +97,26 @@ 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
|
|
|
@@ -95,7 +125,7 @@ export const mapTextLayout = (
|
|
|
95
125
|
for (let i = 0; i < effectiveLineAmount; i++) {
|
|
96
126
|
const line = lines[i]!;
|
|
97
127
|
const w = line[1];
|
|
98
|
-
line[
|
|
128
|
+
line[3] =
|
|
99
129
|
textAlign === 'right'
|
|
100
130
|
? effectiveMaxWidth - w
|
|
101
131
|
: (effectiveMaxWidth - w) / 2;
|
|
@@ -109,7 +139,7 @@ export const mapTextLayout = (
|
|
|
109
139
|
const startY = firstBaseLine;
|
|
110
140
|
for (let i = 0; i < effectiveLineAmount; i++) {
|
|
111
141
|
const line = lines[i] as TextLineStruct;
|
|
112
|
-
line[
|
|
142
|
+
line[4] = startY + lineHeightPx * i;
|
|
113
143
|
}
|
|
114
144
|
|
|
115
145
|
return [
|
|
@@ -142,7 +172,7 @@ export const measureLines = (
|
|
|
142
172
|
continue;
|
|
143
173
|
}
|
|
144
174
|
const width = measureText(line, fontFamily, letterSpacing);
|
|
145
|
-
measuredLines.push([line, width, 0, 0]);
|
|
175
|
+
measuredLines.push([line, width, false, 0, 0]);
|
|
146
176
|
}
|
|
147
177
|
|
|
148
178
|
return [
|
|
@@ -167,9 +197,10 @@ export const wrapText = (
|
|
|
167
197
|
|
|
168
198
|
// Calculate space width for line wrapping
|
|
169
199
|
const spaceWidth = measureText(' ', fontFamily, letterSpacing);
|
|
200
|
+
const overflowWidth = measureText(overflowSuffix, fontFamily, letterSpacing);
|
|
170
201
|
|
|
171
202
|
let wrappedLine: TextLineStruct[] = [];
|
|
172
|
-
let remainingLines = maxLines;
|
|
203
|
+
let remainingLines = maxLines > 0 ? maxLines : 1000;
|
|
173
204
|
let hasRemainingText = true;
|
|
174
205
|
let hasMaxLines = maxLines > 0;
|
|
175
206
|
|
|
@@ -189,11 +220,11 @@ export const wrapText = (
|
|
|
189
220
|
letterSpacing,
|
|
190
221
|
spaceWidth,
|
|
191
222
|
overflowSuffix,
|
|
223
|
+
overflowWidth,
|
|
192
224
|
wordBreak,
|
|
193
225
|
remainingLines,
|
|
194
|
-
hasMaxLines,
|
|
195
226
|
)
|
|
196
|
-
: [[['', 0, 0, 0]], remainingLines, i < lines.length - 1];
|
|
227
|
+
: [[['', 0, false, 0, 0]], remainingLines, i < lines.length - 1];
|
|
197
228
|
|
|
198
229
|
remainingLines--;
|
|
199
230
|
wrappedLines.push(...wrappedLine);
|
|
@@ -201,15 +232,23 @@ export const wrapText = (
|
|
|
201
232
|
if (hasMaxLines === true && remainingLines <= 0) {
|
|
202
233
|
const lastLine = wrappedLines[wrappedLines.length - 1]!;
|
|
203
234
|
if (i < lines.length - 1) {
|
|
204
|
-
if
|
|
205
|
-
|
|
235
|
+
//check if line is truncated already
|
|
236
|
+
if (lastLine[2] === false) {
|
|
237
|
+
let remainingText = '';
|
|
238
|
+
const [line, lineWidth] = truncateLineEnd(
|
|
206
239
|
measureText,
|
|
207
|
-
lastLine[0],
|
|
208
240
|
fontFamily,
|
|
209
|
-
maxWidth,
|
|
210
241
|
letterSpacing,
|
|
242
|
+
lastLine[0],
|
|
243
|
+
lastLine[1],
|
|
244
|
+
remainingText,
|
|
245
|
+
maxWidth,
|
|
211
246
|
overflowSuffix,
|
|
247
|
+
overflowWidth,
|
|
212
248
|
);
|
|
249
|
+
lastLine[0] = line;
|
|
250
|
+
lastLine[1] = lineWidth;
|
|
251
|
+
lastLine[2] = true;
|
|
213
252
|
}
|
|
214
253
|
}
|
|
215
254
|
break;
|
|
@@ -227,12 +266,10 @@ export const wrapLine = (
|
|
|
227
266
|
letterSpacing: number,
|
|
228
267
|
spaceWidth: number,
|
|
229
268
|
overflowSuffix: string,
|
|
269
|
+
overflowWidth: number,
|
|
230
270
|
wordBreak: string,
|
|
231
271
|
remainingLines: number,
|
|
232
|
-
hasMaxLines: boolean,
|
|
233
272
|
): WrappedLinesStruct => {
|
|
234
|
-
// Use the same space regex as Canvas renderer to handle ZWSP
|
|
235
|
-
const spaceRegex = / |\u200B/g;
|
|
236
273
|
const words = line.split(spaceRegex);
|
|
237
274
|
const spaces = line.match(spaceRegex) || [];
|
|
238
275
|
const wrappedLines: TextLineStruct[] = [];
|
|
@@ -240,263 +277,396 @@ export const wrapLine = (
|
|
|
240
277
|
let currentLineWidth = 0;
|
|
241
278
|
let hasRemainingText = true;
|
|
242
279
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
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);
|
|
278
316
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
hasRemainingText = i < words.length;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (wordBreak !== 'break-all' && currentLine.length > 0) {
|
|
287
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
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) {
|
|
291
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 {
|
|
292
324
|
currentLine = word;
|
|
293
325
|
currentLineWidth = wordWidth;
|
|
294
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;
|
|
295
333
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
remainingLines,
|
|
304
|
-
);
|
|
305
|
-
remainingLines = rl;
|
|
306
|
-
hasRemainingText = rt;
|
|
307
|
-
if (lines.length === 1) {
|
|
308
|
-
[currentLine, currentLineWidth] = lines[lines.length - 1]!;
|
|
309
|
-
} else {
|
|
310
|
-
for (let j = 0; j < lines.length; j++) {
|
|
311
|
-
[currentLine, currentLineWidth] = lines[j]!;
|
|
312
|
-
if (j < lines.length - 1) {
|
|
313
|
-
wrappedLines.push(lines[j]!);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
} else if (wordBreak === 'break-all') {
|
|
318
|
-
const firstLetterWidth = measureText(
|
|
319
|
-
word.charAt(0),
|
|
320
|
-
fontFamily,
|
|
321
|
-
letterSpacing,
|
|
322
|
-
);
|
|
323
|
-
let linebreak = false;
|
|
324
|
-
if (
|
|
325
|
-
currentLineWidth + firstLetterWidth + effectiveSpaceWidth >
|
|
326
|
-
maxWidth
|
|
327
|
-
) {
|
|
328
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
329
|
-
remainingLines -= 1;
|
|
330
|
-
currentLine = '';
|
|
331
|
-
currentLineWidth = 0;
|
|
332
|
-
linebreak = true;
|
|
333
|
-
}
|
|
334
|
-
const initial = maxWidth - currentLineWidth;
|
|
335
|
-
const [lines, rl, rt] = breakAll(
|
|
336
|
-
measureText,
|
|
337
|
-
word,
|
|
338
|
-
fontFamily,
|
|
339
|
-
initial,
|
|
340
|
-
maxWidth,
|
|
341
|
-
letterSpacing,
|
|
342
|
-
remainingLines,
|
|
343
|
-
);
|
|
344
|
-
remainingLines = rl;
|
|
345
|
-
hasRemainingText = rt;
|
|
346
|
-
if (linebreak === false) {
|
|
347
|
-
const [text, width] = lines[0]!;
|
|
348
|
-
currentLine += text;
|
|
349
|
-
currentLineWidth += width;
|
|
350
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
for (let j = 1; j < lines.length; j++) {
|
|
354
|
-
[currentLine, currentLineWidth] = lines[j]!;
|
|
355
|
-
if (j < lines.length - 1) {
|
|
356
|
-
wrappedLines.push([currentLine, currentLineWidth, 0, 0]);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
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--;
|
|
359
341
|
|
|
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
|
};
|