UNPKG

pixi.js

Version:

<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">

1 lines 29.9 kB
{"version":3,"file":"canvasTextSplit.mjs","sources":["../../../../src/scene/text/utils/canvasTextSplit.ts"],"sourcesContent":["import { Matrix } from '../../../maths/matrix/Matrix';\nimport { Container } from '../../container/Container';\nimport { FillGradient } from '../../graphics/shared/fill/FillGradient';\nimport { type SplitOptions } from '../../text-split/SplitText';\nimport { type TextSplitOutput } from '../../text-split/types';\nimport { CanvasTextGenerator } from '../canvas/CanvasTextGenerator';\nimport { CanvasTextMetrics } from '../canvas/CanvasTextMetrics';\nimport { type TextStyleRun } from '../canvas/utils/parseTaggedText';\nimport { Text } from '../Text';\nimport { type TextStyle } from '../TextStyle';\n\ninterface GroupedSegment\n{\n line: string;\n chars: string[];\n}\n\nfunction getAlignmentOffset(alignment: string, lineWidth: number, largestLine: number): number\n{\n switch (alignment)\n {\n case 'center':\n return (largestLine - lineWidth) / 2;\n case 'right':\n return largestLine - lineWidth;\n case 'left':\n default:\n return 0;\n }\n}\n\nfunction isNewlineCharacter(char: string): boolean\n{\n return char === '\\r' || char === '\\n' || char === '\\r\\n';\n}\n\nconst whitespaceRegex = /^\\s*$/;\n\n/**\n * Groups text segments into lines based on measured text metrics\n * @param segments - Array of text segments to group\n * @param measuredText - The pre-measured text metrics\n * @param measuredText.lines\n * @returns Array of grouped segments containing line information\n */\nfunction groupTextSegments(\n segments: string[],\n measuredText: { lines: string[] },\n): GroupedSegment[]\n{\n const groupedSegments: GroupedSegment[] = [];\n let currentLine = measuredText.lines[0];\n let matchedLine = '';\n let chars: string[] = [];\n let lineCount = 0;\n\n segments.forEach((segment) =>\n {\n const isWhitespace = whitespaceRegex.test(segment);\n const isNewline = isNewlineCharacter(segment);\n const isSpaceAtStart = matchedLine.length === 0 && isWhitespace;\n\n if (isWhitespace && !isNewline && isSpaceAtStart)\n {\n return;\n }\n\n if (!isNewline) matchedLine += segment;\n\n chars.push(segment);\n\n if (matchedLine.length >= currentLine.length)\n {\n groupedSegments.push({\n line: matchedLine,\n chars,\n });\n chars = [];\n matchedLine = '';\n lineCount++;\n currentLine = measuredText.lines[lineCount];\n }\n });\n\n return groupedSegments;\n}\n\n/**\n * Splits a Text object into segments based on the text's layout and style,\n * and adds these segments as individual Text objects to a specified container.\n *\n * This function handles word wrapping, alignment, and letter spacing,\n * ensuring that each segment is rendered correctly according to the original text's style.\n * It uses the CanvasTextMetrics to measure text dimensions and segment the text into lines.\n * @param options - Configuration options for the text split operation.\n * @returns An array of Text objects representing the split segments.\n * @internal\n */\nexport function canvasTextSplit(\n options: Pick<SplitOptions, 'text' | 'style'> & { chars: Text[] },\n): TextSplitOutput<Text>\n{\n const { text, style, chars: existingChars } = options;\n const textStyle = style as TextStyle;\n\n // measure the entire text to get the layout\n const measuredText = CanvasTextMetrics.measureText(text, textStyle);\n\n if (measuredText.runsByLine && measuredText.runsByLine.length > 0)\n {\n return canvasTaggedTextSplitFromRuns(measuredText, textStyle, existingChars, text);\n }\n\n // split the text into segments\n const segments = CanvasTextMetrics.graphemeSegmenter(text);\n // now group the segments into lines based on measured lines\n const groupedSegments: GroupedSegment[] = groupTextSegments(segments, measuredText);\n\n const alignment = textStyle.align;\n const maxLineWidth = measuredText.lineWidths.reduce((max, line) => Math.max(max, line), 0);\n const isSingleLine = measuredText.lines.length === 1;\n // For single-line text, alignment has no effect (nothing to align relative to)\n // Multi-line text uses wordWrapWidth when word wrap is enabled\n const useWordWrapWidth = !isSingleLine && textStyle.wordWrap;\n const alignWidth = useWordWrapWidth ? Math.max(textStyle.wordWrapWidth, maxLineWidth) : maxLineWidth;\n\n // Check if fill or stroke contains a gradient that needs offset/bounds\n const fillGradient = textStyle._fill?.fill;\n const strokeGradient = textStyle._stroke?.fill;\n\n const hasFillGradient = fillGradient instanceof FillGradient;\n const hasStrokeGradient = strokeGradient instanceof FillGradient;\n const hasGradient = hasFillGradient || hasStrokeGradient;\n const hasLocalGradient = (hasFillGradient && fillGradient.textureSpace === 'local')\n || (hasStrokeGradient && strokeGradient.textureSpace === 'local');\n\n // Store full text dimensions for gradient calculation\n const fullTextWidth = measuredText.width;\n const fullTextHeight = measuredText.height;\n\n // Clone style for individual characters with left alignment.\n // Container-level positioning handles alignment via getAlignmentOffset().\n // Without this, each character applies its own alignment offset within its measurement area.\n const baseCharStyle = textStyle.clone();\n\n baseCharStyle.align = 'left';\n\n // When trim is enabled on the style, calculate the trim offset for the whole text block once,\n // then disable trim on individual characters and offset all characters to compensate\n let trimOffsetX = 0;\n let trimOffsetY = 0;\n\n if (baseCharStyle.trim)\n {\n const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({\n text,\n style: textStyle,\n resolution: 1,\n });\n\n CanvasTextGenerator.returnCanvasAndContext(canvasAndContext);\n\n trimOffsetX = -frame.x;\n trimOffsetY = -frame.y;\n\n // Disable trim for individual characters; we'll use the whole-text trim offset instead\n baseCharStyle.trim = false;\n }\n\n // now create Text objects for each segment and add them to the container\n const chars: Text[] = [];\n const lineContainers: Container[] = [];\n const wordContainers: Container[] = [];\n let yOffset = 0;\n let existingCharIndex = 0;\n\n // Cache gradient bounds object; identical for every character\n const gradientBounds = hasLocalGradient ? { width: fullTextWidth, height: fullTextHeight } : null;\n\n groupedSegments.forEach((group, lineIndex) =>\n {\n const lineContainer = new Container({ label: `line-${lineIndex}` });\n\n lineContainer.y = yOffset + trimOffsetY;\n lineContainers.push(lineContainer);\n\n const lineWidth = measuredText.lineWidths[lineIndex];\n let xOffset = getAlignmentOffset(alignment, lineWidth, alignWidth);\n\n let currentWordContainer = new Container({ label: 'word' });\n\n currentWordContainer.x = xOffset + trimOffsetX;\n\n // Use remaining-width technique for kerning-aware character positioning\n const context = CanvasTextMetrics._context;\n\n context.font = baseCharStyle._fontString;\n if (CanvasTextMetrics.experimentalLetterSpacingSupported)\n {\n context.letterSpacing = '0px';\n context.textLetterSpacing = '0px';\n }\n\n let remainingLineText = group.line;\n let previousRemainingWidth = context.measureText(remainingLineText).width;\n\n group.chars.forEach((segment) =>\n {\n if (isNewlineCharacter(segment))\n {\n return;\n }\n\n remainingLineText = remainingLineText.slice(segment.length);\n const currentRemainingWidth = remainingLineText.length > 0\n ? context.measureText(remainingLineText).width : 0;\n const charAdvance = previousRemainingWidth - currentRemainingWidth;\n\n previousRemainingWidth = currentRemainingWidth;\n\n if (charAdvance === 0) return;\n\n if (segment === ' ')\n {\n if (currentWordContainer.children.length > 0)\n {\n wordContainers.push(currentWordContainer);\n lineContainer.addChild(currentWordContainer);\n }\n\n xOffset += charAdvance + textStyle.letterSpacing;\n currentWordContainer = new Container({ label: 'word' });\n currentWordContainer.x = xOffset + trimOffsetX;\n }\n else\n {\n let charStyle = baseCharStyle;\n\n if (hasGradient)\n {\n charStyle = baseCharStyle.clone();\n charStyle._gradientOffset = { x: -xOffset, y: -yOffset };\n if (gradientBounds)\n {\n charStyle._gradientBounds = gradientBounds;\n }\n }\n\n let char: Text;\n\n if (existingCharIndex < existingChars.length)\n {\n char = existingChars[existingCharIndex++];\n\n char.text = segment;\n char.style = charStyle;\n char.setFromMatrix(Matrix.IDENTITY);\n char.x = xOffset - currentWordContainer.x + trimOffsetX;\n }\n else\n {\n char = new Text({\n text: segment,\n style: charStyle,\n x: xOffset - currentWordContainer.x + trimOffsetX,\n });\n }\n\n chars.push(char);\n currentWordContainer.addChild(char);\n xOffset += charAdvance + textStyle.letterSpacing;\n }\n });\n\n // Add the last word container of the line if it has children\n if (currentWordContainer.children.length > 0)\n {\n wordContainers.push(currentWordContainer);\n lineContainer.addChild(currentWordContainer);\n }\n\n // Justify: distribute extra space among word gaps\n if (alignment === 'justify' && textStyle.wordWrap && lineIndex < groupedSegments.length - 1)\n {\n const lineWords = lineContainer.children;\n const wordGaps = lineWords.length - 1;\n\n if (wordGaps > 0)\n {\n const extraPerGap = (alignWidth - lineWidth) / wordGaps;\n\n for (let i = 1; i < lineWords.length; i++)\n {\n lineWords[i].x += i * extraPerGap;\n }\n }\n }\n\n yOffset += measuredText.lineHeight;\n });\n\n return { chars, lines: lineContainers, words: wordContainers };\n}\n\nfunction canvasTaggedTextSplitFromRuns(\n measuredText: CanvasTextMetrics,\n textStyle: TextStyle,\n existingChars: Text[],\n text: string,\n): TextSplitOutput<Text>\n{\n const { runsByLine } = measuredText;\n const alignment = textStyle.align;\n const maxLineWidth = measuredText.lineWidths.reduce((max, line) => Math.max(max, line), 0);\n const isSingleLine = measuredText.lines.length === 1;\n const useWordWrapWidth = !isSingleLine && textStyle.wordWrap;\n const alignWidth = useWordWrapWidth ? Math.max(textStyle.wordWrapWidth, maxLineWidth) : maxLineWidth;\n\n let trimOffsetX = 0;\n let trimOffsetY = 0;\n\n if (textStyle.trim)\n {\n const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({\n text,\n style: textStyle,\n resolution: 1,\n });\n\n CanvasTextGenerator.returnCanvasAndContext(canvasAndContext);\n trimOffsetX = -frame.x;\n trimOffsetY = -frame.y;\n }\n\n const chars: Text[] = [];\n const lineContainers: Container[] = [];\n const wordContainers: Container[] = [];\n let yOffset = 0;\n let existingCharIndex = 0;\n\n runsByLine.forEach((lineRuns: TextStyleRun[], lineIndex: number) =>\n {\n const lineContainer = new Container({ label: `line-${lineIndex}` });\n\n lineContainer.y = yOffset + trimOffsetY;\n lineContainers.push(lineContainer);\n\n const lineWidth = measuredText.lineWidths[lineIndex];\n let xOffset = getAlignmentOffset(alignment, lineWidth, alignWidth);\n\n let currentWordContainer = new Container({ label: 'word' });\n\n currentWordContainer.x = xOffset + trimOffsetX;\n\n for (const run of lineRuns)\n {\n const runStyle = run.style;\n\n const fillGradient = runStyle._fill?.fill;\n const strokeGradient = runStyle._stroke?.fill;\n const hasFillGradient = fillGradient instanceof FillGradient;\n const hasStrokeGradient = strokeGradient instanceof FillGradient;\n const hasGradient = hasFillGradient || hasStrokeGradient;\n const hasLocalGradient = (hasFillGradient && fillGradient.textureSpace === 'local')\n || (hasStrokeGradient && strokeGradient.textureSpace === 'local');\n\n const graphemes = CanvasTextMetrics.graphemeSegmenter(run.text);\n\n const baseRunStyle = runStyle.clone();\n\n baseRunStyle.align = 'left';\n baseRunStyle.wordWrap = false;\n if (baseRunStyle.trim) baseRunStyle.trim = false;\n baseRunStyle.tagStyles = undefined;\n\n // Use remaining-width technique for kerning-aware character positioning\n const context = CanvasTextMetrics._context;\n\n context.font = baseRunStyle._fontString;\n if (CanvasTextMetrics.experimentalLetterSpacingSupported)\n {\n context.letterSpacing = '0px';\n context.textLetterSpacing = '0px';\n }\n\n let remainingText = run.text;\n let previousRemainingWidth = context.measureText(remainingText).width;\n const runStartX = xOffset;\n const runTextWidth = previousRemainingWidth;\n const runFontProps = CanvasTextMetrics.measureFont(baseRunStyle._fontString);\n const runHeight = runStyle.lineHeight || runFontProps.fontSize;\n const runGradientBounds = hasLocalGradient\n ? { width: runTextWidth, height: runHeight } : null;\n\n for (const grapheme of graphemes)\n {\n remainingText = remainingText.slice(grapheme.length);\n const currentRemainingWidth = remainingText.length > 0\n ? context.measureText(remainingText).width : 0;\n const charAdvance = previousRemainingWidth - currentRemainingWidth;\n\n previousRemainingWidth = currentRemainingWidth;\n\n if (isNewlineCharacter(grapheme)) continue;\n if (charAdvance === 0) continue;\n\n if (grapheme === ' ')\n {\n if (currentWordContainer.children.length > 0)\n {\n wordContainers.push(currentWordContainer);\n lineContainer.addChild(currentWordContainer);\n }\n xOffset += charAdvance + runStyle.letterSpacing;\n currentWordContainer = new Container({ label: 'word' });\n currentWordContainer.x = xOffset + trimOffsetX;\n }\n else\n {\n let charStyle = baseRunStyle;\n\n if (hasGradient)\n {\n charStyle = baseRunStyle.clone();\n if (hasLocalGradient)\n {\n charStyle._gradientOffset = { x: -(xOffset - runStartX), y: 0 };\n charStyle._gradientBounds = runGradientBounds;\n }\n else\n {\n charStyle._gradientOffset = { x: -(xOffset - runStartX), y: 0 };\n }\n }\n\n let char: Text;\n\n if (existingCharIndex < existingChars.length)\n {\n char = existingChars[existingCharIndex++];\n char.text = grapheme;\n char.style = charStyle;\n char.setFromMatrix(Matrix.IDENTITY);\n char.x = xOffset - currentWordContainer.x + trimOffsetX;\n }\n else\n {\n char = new Text({\n text: grapheme,\n style: charStyle,\n x: xOffset - currentWordContainer.x + trimOffsetX,\n });\n }\n\n chars.push(char);\n currentWordContainer.addChild(char);\n xOffset += charAdvance + runStyle.letterSpacing;\n }\n }\n }\n\n if (currentWordContainer.children.length > 0)\n {\n wordContainers.push(currentWordContainer);\n lineContainer.addChild(currentWordContainer);\n }\n\n // Justify: distribute extra space among word gaps\n if (alignment === 'justify' && textStyle.wordWrap && lineIndex < runsByLine.length - 1)\n {\n const lineWords = lineContainer.children;\n const wordGaps = lineWords.length - 1;\n\n if (wordGaps > 0)\n {\n const extraPerGap = (alignWidth - lineWidth) / wordGaps;\n\n for (let i = 1; i < lineWords.length; i++)\n {\n lineWords[i].x += i * extraPerGap;\n }\n }\n }\n\n const lineHeight = measuredText.lineHeights?.[lineIndex] ?? measuredText.lineHeight;\n\n yOffset += lineHeight;\n });\n\n return { chars, lines: lineContainers, words: wordContainers };\n}\n"],"names":[],"mappings":"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"}