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 • 12.3 kB
Source Map (JSON)
{"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 { type ConvertedStrokeStyle } from '../../graphics/shared/FillTypes';\nimport { type SplitOptions } from '../../text-split/SplitText';\nimport { type TextSplitOutput } from '../../text-split/types';\nimport { CanvasTextMetrics } from '../canvas/CanvasTextMetrics';\nimport { Text } from '../Text';\nimport { type TextStyle } from '../TextStyle';\n\ninterface Segment\n{\n char: string;\n metric: CanvasTextMetrics;\n}\n\ninterface GroupedSegment\n{\n line: string;\n chars: Segment[];\n width: number;\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\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 * @param textStyle - The text style to use for measurements\n * @returns Array of grouped segments containing line information\n */\nfunction groupTextSegments(\n segments: string[],\n measuredText: { lines: string[] },\n textStyle: TextStyle,\n): GroupedSegment[]\n{\n const groupedSegments: GroupedSegment[] = [];\n let currentLine = measuredText.lines[0];\n let matchedLine = '';\n let chars: Segment[] = [];\n let lineCount = 0;\n\n // Disable word wrap for individual character measurements\n textStyle.wordWrap = false;\n\n segments.forEach((segment) =>\n {\n const isWhitespace = (/^\\s*$/).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 const metric = CanvasTextMetrics.measureText(segment, textStyle);\n\n chars.push({ char: segment, metric });\n\n if (matchedLine.length >= currentLine.length)\n {\n groupedSegments.push({\n line: matchedLine,\n chars,\n width: chars.reduce((acc, seg) => acc + seg.metric.width, 0),\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 // 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, textStyle.clone());\n\n const alignment = textStyle.align;\n const largestLine = measuredText.lineWidths.reduce((max, line) => Math.max(max, line), 0);\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 const strokeWidth = (textStyle.stroke as ConvertedStrokeStyle)?.width || 0;\n const dropShadowDistance = textStyle.dropShadow?.distance || 0;\n\n groupedSegments.forEach((group, i) =>\n {\n const lineContainer = new Container({ label: `line-${i}` });\n\n lineContainer.y = yOffset;\n lineContainers.push(lineContainer);\n\n const lineWidth = measuredText.lineWidths[i];\n let xOffset = getAlignmentOffset(alignment, lineWidth, largestLine);\n\n let currentWordContainer = new Container({ label: 'word' });\n\n currentWordContainer.x = xOffset;\n\n group.chars.forEach((segment, i) =>\n {\n if (segment.metric.width === 0)\n {\n return; // skip zero-width segments\n }\n\n if (isNewlineCharacter(segment.char))\n {\n xOffset += segment.metric.width - strokeWidth;\n\n return; // Skip newline characters\n }\n\n if (segment.char === ' ')\n {\n // Add current word container if it has content\n if (currentWordContainer.children.length > 0)\n {\n wordContainers.push(currentWordContainer);\n lineContainer.addChild(currentWordContainer);\n }\n\n // Start new word container\n xOffset += segment.metric.width + textStyle.letterSpacing - strokeWidth;\n currentWordContainer = new Container({ label: 'word' });\n currentWordContainer.x = xOffset;\n }\n else\n {\n // if there are existing characters, reuse them\n let char: Text;\n\n if (existingChars.length > 0)\n {\n char = existingChars.shift();\n\n char.text = segment.char;\n char.style = textStyle;\n char.setFromMatrix(Matrix.IDENTITY);\n char.x = xOffset - currentWordContainer.x - (dropShadowDistance * i);\n }\n else\n {\n char = new Text({\n text: segment.char,\n style: textStyle,\n x: xOffset - currentWordContainer.x - (dropShadowDistance * i),\n });\n }\n\n chars.push(char);\n currentWordContainer.addChild(char);\n xOffset += segment.metric.width + textStyle.letterSpacing - strokeWidth;\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 yOffset += measuredText.lineHeight;\n });\n\n return { chars, lines: lineContainers, words: wordContainers };\n}\n"],"names":["i"],"mappings":"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"}