@meonode/canvas 1.0.0-beta.1
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/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/Readme.md +382 -0
- package/dist/cjs/canvas/canvas.helper.d.ts +57 -0
- package/dist/cjs/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/cjs/canvas/canvas.helper.js +239 -0
- package/dist/cjs/canvas/canvas.helper.js.map +1 -0
- package/dist/cjs/canvas/canvas.type.d.ts +657 -0
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -0
- package/dist/cjs/canvas/grid.canvas.util.d.ts +39 -0
- package/dist/cjs/canvas/grid.canvas.util.d.ts.map +1 -0
- package/dist/cjs/canvas/grid.canvas.util.js +263 -0
- package/dist/cjs/canvas/grid.canvas.util.js.map +1 -0
- package/dist/cjs/canvas/image.canvas.util.d.ts +34 -0
- package/dist/cjs/canvas/image.canvas.util.d.ts.map +1 -0
- package/dist/cjs/canvas/image.canvas.util.js +310 -0
- package/dist/cjs/canvas/image.canvas.util.js.map +1 -0
- package/dist/cjs/canvas/layout.canvas.util.d.ts +123 -0
- package/dist/cjs/canvas/layout.canvas.util.d.ts.map +1 -0
- package/dist/cjs/canvas/layout.canvas.util.js +785 -0
- package/dist/cjs/canvas/layout.canvas.util.js.map +1 -0
- package/dist/cjs/canvas/root.canvas.util.d.ts +42 -0
- package/dist/cjs/canvas/root.canvas.util.d.ts.map +1 -0
- package/dist/cjs/canvas/root.canvas.util.js +140 -0
- package/dist/cjs/canvas/root.canvas.util.js.map +1 -0
- package/dist/cjs/canvas/text.canvas.util.d.ts +148 -0
- package/dist/cjs/canvas/text.canvas.util.d.ts.map +1 -0
- package/dist/cjs/canvas/text.canvas.util.js +1112 -0
- package/dist/cjs/canvas/text.canvas.util.js.map +1 -0
- package/dist/cjs/constant/common.const.d.ts +37 -0
- package/dist/cjs/constant/common.const.d.ts.map +1 -0
- package/dist/cjs/constant/common.const.js +51 -0
- package/dist/cjs/constant/common.const.js.map +1 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/canvas/canvas.helper.d.ts +57 -0
- package/dist/esm/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/esm/canvas/canvas.helper.js +214 -0
- package/dist/esm/canvas/canvas.type.d.ts +657 -0
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -0
- package/dist/esm/canvas/grid.canvas.util.d.ts +39 -0
- package/dist/esm/canvas/grid.canvas.util.d.ts.map +1 -0
- package/dist/esm/canvas/grid.canvas.util.js +259 -0
- package/dist/esm/canvas/image.canvas.util.d.ts +34 -0
- package/dist/esm/canvas/image.canvas.util.d.ts.map +1 -0
- package/dist/esm/canvas/image.canvas.util.js +306 -0
- package/dist/esm/canvas/layout.canvas.util.d.ts +123 -0
- package/dist/esm/canvas/layout.canvas.util.d.ts.map +1 -0
- package/dist/esm/canvas/layout.canvas.util.js +777 -0
- package/dist/esm/canvas/root.canvas.util.d.ts +42 -0
- package/dist/esm/canvas/root.canvas.util.d.ts.map +1 -0
- package/dist/esm/canvas/root.canvas.util.js +116 -0
- package/dist/esm/canvas/text.canvas.util.d.ts +148 -0
- package/dist/esm/canvas/text.canvas.util.d.ts.map +1 -0
- package/dist/esm/canvas/text.canvas.util.js +1108 -0
- package/dist/esm/constant/common.const.d.ts +37 -0
- package/dist/esm/constant/common.const.d.ts.map +1 -0
- package/dist/esm/constant/common.const.js +23 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/meonode-canvas-1.0.0-beta.1.tgz +0 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.canvas.util.js","sources":["../../../../src/canvas/text.canvas.util.ts"],"sourcesContent":["import type { TextProps, TextSegment } from '@/canvas/canvas.type.js'\nimport { Canvas, type CanvasRenderingContext2D, type FontVariantSetting } from 'skia-canvas'\nimport { BoxNode } from '@/canvas/layout.canvas.util.js'\nimport { Style, MeasureMode } from '@/constant/common.const.js'\n\n/**\n * Node for rendering text content with rich text styling support\n * Supports color and weight variations through HTML-like tags\n */\nexport class TextNode extends BoxNode {\n private readonly segments: TextSegment[] = []\n private lines: TextSegment[][] = []\n private static measurementContext: CanvasRenderingContext2D | null = null\n private readonly metricsString = 'Ag|\\``'\n private lineHeights: number[] = []\n private lineAscents: number[] = []\n private lineContentHeights: number[] = []\n\n declare props: TextProps & { lineGap: number }\n\n constructor(text: number | string = '', props: TextProps = {}) {\n const initialProps = {\n name: 'TextNode',\n flexShrink: 1,\n lineGap: 0,\n ...props,\n children: undefined,\n }\n super(initialProps)\n this.props = initialProps\n // Process escape sequences before parsing rich text\n const processedText = this.processEscapeSequences(String(text ?? ''))\n this.segments = this.parseRichText(processedText, {\n color: this.props.color,\n weight: this.props.fontWeight,\n size: this.props.fontSize,\n b: this.props.fontWeight === 'bold',\n i: this.props.fontStyle === 'italic',\n })\n this.node.setMeasureFunc(this.measureText.bind(this))\n this.applyDefaults()\n }\n\n protected override applyDefaults(): void {\n const textDefaults: Required<\n Pick<\n TextProps,\n | 'fontSize'\n | 'fontFamily'\n | 'fontWeight'\n | 'fontStyle'\n | 'color'\n | 'textAlign'\n | 'verticalAlign'\n | 'ellipsis'\n | 'lineGap'\n >\n > & {\n lineHeight: undefined | number\n maxLines: undefined | number\n letterSpacing: undefined | number\n wordSpacing: undefined | number\n fontVariant: undefined | FontVariantSetting\n } = {\n fontSize: 16,\n fontFamily: 'sans-serif',\n fontWeight: 'normal',\n fontStyle: 'normal',\n color: 'black',\n textAlign: 'left',\n verticalAlign: 'top',\n fontVariant: undefined,\n lineHeight: undefined,\n lineGap: 0,\n maxLines: undefined,\n ellipsis: false,\n letterSpacing: undefined,\n wordSpacing: undefined,\n }\n\n let defaultsApplied = false\n for (const key of Object.keys(textDefaults) as (keyof typeof textDefaults)[]) {\n if (this.props[key] === undefined && textDefaults[key] !== undefined) {\n this.props[key as string] = textDefaults[key]\n defaultsApplied = true\n }\n }\n\n if (defaultsApplied && !this.node.isDirty()) {\n const affectsMeasurement = [\n 'fontSize',\n 'fontFamily',\n 'fontWeight',\n 'fontStyle',\n 'lineHeight',\n 'maxLines',\n 'lineGap',\n 'letterSpacing',\n 'wordSpacing',\n ].some(measureKey => this.props[measureKey] === textDefaults[measureKey])\n if (affectsMeasurement) {\n this.node.markDirty()\n }\n }\n }\n\n /**\n * Processes Unix-like escape sequences in text strings.\n * Converts escaped characters into their actual representations.\n *\n * Supported escape sequences:\n * - \\n - Newline (line feed)\n * - \\t - Tab (converted to 4 spaces)\n * - \\r - Carriage return (treated as newline)\n * - \\\\ - Literal backslash\n * - \\' - Single quote\n * - \\\" - Double quote\n * - \\0 - Null character (removed)\n * - \\b - Backspace (removed)\n * - \\f - Form feed (treated as newline)\n * - \\v - Vertical tab (treated as newline)\n *\n * @param input - Raw text string potentially containing escape sequences\n * @returns Processed string with escape sequences converted\n */\n private processEscapeSequences(input: string): string {\n return input.replace(/\\\\(.)/g, (match, char) => {\n switch (char) {\n case 'n':\n return '\\n' // Newline\n case 't':\n return ' ' // Tab as 4 spaces\n case 'r':\n return '\\n' // Carriage return treated as newline\n case '\\\\':\n return '\\\\' // Literal backslash\n case \"'\":\n return \"'\" // Single quote\n case '\"':\n return '\"' // Double quote\n case '0':\n return '' // Null character (remove)\n case 'b':\n return '' // Backspace (remove)\n case 'f':\n return '\\n' // Form feed as newline\n case 'v':\n return '\\n' // Vertical tab as newline\n default:\n // Unknown escape sequence - keep original\n return match\n }\n })\n }\n\n /**\n * Parses input text with HTML-style markup into styled text segments.\n *\n * Supported tags:\n * - <color=\"value\"> - Sets text color (hex code or CSS color name)\n * - <weight=\"value\"> - Sets font weight (100-900 or keywords like \"bold\")\n * - <size=\"value\"> - Sets font size in pixels\n * - <b> - Makes text bold (shorthand for weight=\"bold\")\n * - <i> - Makes text italic\n *\n * Tag values can use double quotes, single quotes, or no quotes:\n * <color=\"red\">, <color='red'>, <color=red>\n *\n * Tags can be nested and must be properly closed with </tag>\n *\n * @param input - Text string containing markup tags\n * @param baseStyle - Default style properties to apply to all segments\n * @returns Array of styled text segments with consistent style properties\n */\n private parseRichText(input: string, baseStyle: Partial<TextSegment>): TextSegment[] {\n // Match opening/closing tags with optional quoted/unquoted values\n // Capture groups: (1) closing slash, (2) tag name, (3) double quoted value, (4) single quoted value, (5) unquoted value\n const tagRegex = /<(\\/?)(\\w+)(?:=(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+)))?>/g\n const stack: Partial<TextSegment>[] = []\n const segments: TextSegment[] = []\n let lastIndex = 0\n let currentStyle: Partial<TextSegment> = { ...baseStyle }\n\n // Helper to create a styled segment ensuring all style properties are included\n const applyStyle = (text: string) => {\n if (!text) return\n segments.push({\n text,\n color: currentStyle.color,\n weight: currentStyle.weight,\n size: currentStyle.size,\n b: currentStyle.b,\n i: currentStyle.i,\n })\n }\n\n let match: RegExpExecArray | null\n while ((match = tagRegex.exec(input))) {\n const [, closingSlash, tagNameStr, quotedVal1, quotedVal2, unquotedVal] = match\n const tagName = tagNameStr.toLowerCase()\n const value = quotedVal1 || quotedVal2 || unquotedVal\n\n // Process text content before the current tag\n applyStyle(input.slice(lastIndex, match.index))\n lastIndex = tagRegex.lastIndex\n\n if (!closingSlash) {\n // Opening tag: Save current style state and apply new style\n stack.push({ ...currentStyle })\n\n switch (tagName) {\n case 'color':\n // Support any valid CSS color value\n currentStyle.color = value as TextSegment['color']\n break\n\n case 'weight':\n // Support numeric weights (100-900) or keywords\n currentStyle.weight = value as TextSegment['weight']\n break\n\n case 'size':\n // Parse pixel size as number, revert to default if invalid\n currentStyle.size = value ? Number(value) : undefined\n if (isNaN(currentStyle.size as number)) {\n console.warn(`[TextNode ${this.key || ''}] Invalid numeric value for size tag: ${value}`)\n currentStyle.size = undefined\n }\n break\n\n case 'b':\n // Simple bold flag\n currentStyle.b = true\n break\n\n case 'i':\n // Simple italic flag\n currentStyle.i = true\n break\n }\n } else {\n // Closing tag: Restore previous style state\n currentStyle = stack.pop() || { ...baseStyle }\n }\n }\n\n // Process remaining text after last tag\n applyStyle(input.slice(lastIndex))\n\n // Don't filter out empty segments - they might represent empty lines\n return segments\n }\n\n private formatSpacing(value: TextProps['letterSpacing'] | TextProps['wordSpacing']) {\n if (typeof value === 'number') return `${value}px`\n return value || 'normal'\n }\n\n private parseSpacingToPx(spacingValue: number | string | undefined, fontSize: number): number {\n if (spacingValue === undefined || spacingValue === 'normal') {\n return 0\n }\n if (typeof spacingValue === 'number') {\n return spacingValue // Treat raw number as px\n }\n if (typeof spacingValue === 'string') {\n const trimmed = spacingValue.trim()\n if (trimmed.endsWith('px')) {\n return parseFloat(trimmed) || 0\n }\n if (trimmed.endsWith('em')) {\n // Convert em based on the current font size\n return (parseFloat(trimmed) || 0) * fontSize\n }\n // Attempt to parse as a raw number (pixels) if no unit\n const parsed = parseFloat(trimmed)\n if (!isNaN(parsed)) {\n return parsed\n }\n }\n return 0 // Default fallback\n }\n\n /**\n * Generates a CSS font string by combining base TextProps with optional TextSegment styling.\n * Follows browser font string format: \"font-style font-weight font-size font-family\"\n *\n * Priority for style properties:\n * - Weight: segment <weight> tag > segment <b> tag > base fontWeight prop\n * - Style: segment <i> > base fontStyle\n * - Size: segment size > base fontSize\n * - Family: base fontFamily\n *\n * @param segmentStyle - Optional TextSegment styling to override base props\n * @returns Formatted CSS font string for canvas context\n */\n private getFontString(segmentStyle?: Partial<TextSegment>): string {\n const baseStyle = this.props\n let effectiveWeight: TextSegment['weight'] | number | undefined\n\n // Determine italic style - segment <i> tag overrides base style\n const effectiveStyle = segmentStyle?.i ? 'italic' : baseStyle.fontStyle || 'normal'\n\n // Determine font weight with priority:\n // 1. Segment explicit weight (<weight> tag)\n // 2. Segment bold flag (<b> tag)\n // 3. Base font weight prop\n if (segmentStyle?.weight) {\n effectiveWeight = segmentStyle.weight\n } else if (segmentStyle?.b) {\n effectiveWeight = 'bold'\n } else {\n effectiveWeight = baseStyle.fontWeight || 'normal'\n }\n\n // Use segment size if specified, otherwise base size with 16px default\n const effectiveSize = segmentStyle?.size ? segmentStyle.size : baseStyle.fontSize || 16\n\n // Combine properties into CSS font string format\n const style = {\n fontStyle: effectiveStyle,\n fontWeight: effectiveWeight,\n fontSize: effectiveSize,\n fontFamily: baseStyle.fontFamily || 'sans-serif',\n }\n\n return `${style.fontStyle} ${style.fontWeight} ${style.fontSize}px ${style.fontFamily}`\n }\n\n /**\n * Gets lines to process respecting maxLines constraint\n */\n private getLinesToMeasureOrRender(): TextSegment[][] {\n const maxLines = this.props.maxLines\n if (maxLines !== undefined && maxLines > 0 && this.lines.length > maxLines) {\n return this.lines.slice(0, maxLines)\n }\n return this.lines\n }\n\n /**\n * Measures text dimensions and calculates layout metrics for the YogaLayout engine.\n * Handles text wrapping, line height calculations, and dynamic leading.\n *\n * Line heights are determined by:\n * 1. Using props.lineHeight as fixed pixel value if provided\n * 2. Otherwise calculating dynamic height based on largest font size per line\n * 3. Adding leading space above/below text content\n * 4. Including specified line gaps between lines\n *\n * @param widthConstraint - Maximum allowed width in pixels for text layout\n * @param widthMode - YogaLayout mode determining how width constraint is applied\n * @returns Calculated minimum dimensions required to render text content\n * - width: Total width needed for text layout\n * - height: Total height including line heights and gaps\n */\n private measureText(widthConstraint: number, widthMode: MeasureMode): { width: number; height: number } {\n // Create measurement canvas if not exists\n if (!TextNode.measurementContext) {\n TextNode.measurementContext = new Canvas(1, 1).getContext('2d')\n }\n const baseFontSize = this.props.fontSize || 16\n const ctx = TextNode.measurementContext!\n ctx.save()\n\n // Setup text measurement context\n ctx.letterSpacing = this.formatSpacing(this.props.letterSpacing)\n ctx.wordSpacing = 'normal' // Handled manually via parsedWordSpacingPx\n const parsedWordSpacingPx = this.parseSpacingToPx(this.props.wordSpacing, baseFontSize)\n\n // Pre-measure each text segment width with its specific styling\n for (const segment of this.segments) {\n ctx.font = this.getFontString(segment)\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (segment width):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n segment.width = ctx.measureText(segment.text).width\n }\n\n // Calculate available layout width\n const availableWidthForContent = widthMode === Style.MeasureMode.Undefined ? Infinity : Math.max(0, widthConstraint)\n const epsilon = 0.001 // Float precision compensation\n\n // Wrap text into lines based on available width\n this.lines = this.wrapTextRich(ctx, this.segments, availableWidthForContent + epsilon, parsedWordSpacingPx)\n\n // Initialize line metrics arrays\n this.lineHeights = [] // Final heights including leading\n this.lineAscents = [] // Text ascent heights\n this.lineContentHeights = [] // Raw content heights (ascent + descent)\n\n let totalTextHeight = 0\n const linesToMeasure = this.getLinesToMeasureOrRender()\n const numLines = linesToMeasure.length\n const defaultLineHeightMultiplier = 1.2 // Base leading multiplier\n\n // Calculate metrics for each line\n for (const line of linesToMeasure) {\n let maxAscent = 0\n let maxDescent = 0\n let maxFontSizeOnLine = 0\n\n // Handle empty line metrics\n if (line.length === 0) {\n ctx.font = this.getFontString()\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (empty line):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n const metrics = ctx.measureText(this.metricsString)\n maxAscent = metrics.actualBoundingBoxAscent ?? baseFontSize * 0.8\n maxDescent = metrics.actualBoundingBoxDescent ?? baseFontSize * 0.2\n maxFontSizeOnLine = baseFontSize\n } else {\n // Calculate max metrics across all segments in line\n for (const segment of line) {\n if (/^\\s+$/.test(segment.text)) continue\n\n const segmentSize = segment.size || baseFontSize\n maxFontSizeOnLine = Math.max(maxFontSizeOnLine, segmentSize)\n\n ctx.font = this.getFontString(segment)\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (segment height):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n\n const metrics = ctx.measureText(this.metricsString)\n const ascent = metrics.actualBoundingBoxAscent ?? segmentSize * 0.8\n const descent = metrics.actualBoundingBoxDescent ?? segmentSize * 0.2\n\n maxAscent = Math.max(maxAscent, ascent)\n maxDescent = Math.max(maxDescent, descent)\n }\n }\n\n // Fallback metrics for lines with only whitespace\n if (maxAscent === 0 && maxDescent === 0 && line.length > 0) {\n ctx.font = this.getFontString()\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (fallback):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n const metrics = ctx.measureText(this.metricsString)\n maxAscent = metrics.actualBoundingBoxAscent ?? baseFontSize * 0.8\n maxDescent = metrics.actualBoundingBoxDescent ?? baseFontSize * 0.2\n maxFontSizeOnLine = maxFontSizeOnLine || baseFontSize\n }\n\n maxFontSizeOnLine = maxFontSizeOnLine || baseFontSize\n\n // Calculate total content height for line\n const actualContentHeight = maxAscent + maxDescent\n\n // Determine final line box height with leading\n const targetLineBoxHeight =\n typeof this.props.lineHeight === 'number' && this.props.lineHeight > 0\n ? this.props.lineHeight\n : maxFontSizeOnLine * defaultLineHeightMultiplier\n\n // Use larger of target height or content height to prevent clipping\n const finalLineHeight = Math.max(actualContentHeight, targetLineBoxHeight)\n\n // Store line metrics for rendering\n this.lineHeights.push(finalLineHeight)\n this.lineAscents.push(maxAscent)\n this.lineContentHeights.push(actualContentHeight)\n\n totalTextHeight += finalLineHeight\n }\n\n // Add line gap spacing to total height\n const lineGapValue = this.props.lineGap\n const totalGapHeight = Math.max(0, (numLines - 1) * lineGapValue)\n const calculatedContentHeight = totalTextHeight + totalGapHeight\n\n // Calculate width required for text content\n const spaceWidth = this.measureSpaceWidth(ctx)\n let singleLineWidth = 0\n let firstWordInSingleLine = true\n for (const segment of this.segments) {\n const words = segment.text.split(/(\\s+)/).filter(Boolean)\n for (const word of words) {\n if (/^\\s+$/.test(word)) continue\n ctx.font = this.getFontString(segment)\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (single line width):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n const wordWidth = ctx.measureText(word).width\n if (!firstWordInSingleLine) {\n singleLineWidth += spaceWidth + parsedWordSpacingPx\n }\n singleLineWidth += wordWidth\n firstWordInSingleLine = false\n }\n }\n\n // Determine final content width based on wrapping\n let requiredContentWidth: number\n if (singleLineWidth <= availableWidthForContent) {\n requiredContentWidth = singleLineWidth\n if (linesToMeasure.length > 1 && this.props.maxLines !== 1 && !this.segments.some(s => s.text.includes('\\n'))) {\n console.warn(\n `[TextNode ${this.key || ''}] Rich text should fit (${singleLineWidth.toFixed(2)} <= ${availableWidthForContent.toFixed(2)}) but wrapTextRich produced ${linesToMeasure.length} lines. Width calculation might be slightly off due to complex spacing/kerning.`,\n )\n let maxWrappedLineWidth = 0\n for (const line of linesToMeasure) {\n let currentLineWidth = 0\n let firstWordOnWrappedLine = true\n for (const segment of line) {\n const segmentWidth = segment.width ?? 0\n const isSpaceSegment = /^\\s+$/.test(segment.text)\n if (!isSpaceSegment) {\n if (!firstWordOnWrappedLine) {\n currentLineWidth += spaceWidth + parsedWordSpacingPx\n }\n currentLineWidth += segmentWidth\n firstWordOnWrappedLine = false\n }\n }\n maxWrappedLineWidth = Math.max(maxWrappedLineWidth, currentLineWidth)\n }\n requiredContentWidth = Math.max(singleLineWidth, maxWrappedLineWidth)\n }\n } else {\n let maxWrappedLineWidth = 0\n for (const line of linesToMeasure) {\n let currentLineWidth = 0\n let firstWordOnWrappedLine = true\n for (const segment of line) {\n const segmentWidth = segment.width ?? 0\n const isSpaceSegment = /^\\s+$/.test(segment.text)\n if (!isSpaceSegment) {\n if (!firstWordOnWrappedLine) {\n currentLineWidth += spaceWidth + parsedWordSpacingPx\n }\n currentLineWidth += segmentWidth\n firstWordOnWrappedLine = false\n }\n }\n maxWrappedLineWidth = Math.max(maxWrappedLineWidth, currentLineWidth)\n }\n requiredContentWidth = maxWrappedLineWidth\n }\n\n // Constrain width if needed\n let finalContentWidth = requiredContentWidth\n if (availableWidthForContent !== Infinity) {\n finalContentWidth = Math.min(requiredContentWidth, availableWidthForContent)\n }\n\n ctx.restore()\n return {\n width: Math.max(0, finalContentWidth),\n height: Math.max(0, calculatedContentHeight),\n }\n }\n\n /**\n * Wraps text segments into multiple lines while respecting width constraints and preserving styling.\n * Handles rich text attributes (color, weight, size, bold, italic) and proper word wrapping.\n * Also respects explicit newline characters (\\n) for forced line breaks.\n *\n * @param ctx - Canvas rendering context used for text measurements\n * @param segments - Array of text segments with styling information\n * @param maxWidth - Maximum allowed width for each line in pixels\n * @param parsedWordSpacingPx - Additional spacing to add between words in pixels\n * @returns Array of lines, where each line contains styled text segments\n */\n private wrapTextRich(\n ctx: CanvasRenderingContext2D,\n segments: TextSegment[],\n maxWidth: number,\n parsedWordSpacingPx: number,\n ): TextSegment[][] {\n const lines: TextSegment[][] = []\n\n if (segments.length === 0 || maxWidth <= 0) return lines\n\n let currentLineSegments: TextSegment[] = []\n let currentLineWidth = 0\n const spaceWidth = this.measureSpaceWidth(ctx)\n\n // Helper to finalize current line and start new one\n const finalizeLine = (forceEmpty = false) => {\n // Remove trailing whitespace segments unless we're forcing an empty line\n if (!forceEmpty) {\n while (\n currentLineSegments.length > 0 &&\n /^\\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text)\n ) {\n currentLineSegments.pop()\n }\n }\n // Always push the line, even if empty (for \\n\\n cases)\n lines.push(currentLineSegments)\n currentLineSegments = []\n currentLineWidth = 0\n }\n\n for (const segment of segments) {\n // Preserve all style attributes for consistency\n const segmentStyle = {\n color: segment.color,\n weight: segment.weight,\n size: segment.size,\n b: segment.b,\n i: segment.i,\n }\n\n // Check if segment contains newline characters\n if (segment.text.includes('\\n')) {\n // Split by newlines and process each part\n const parts = segment.text.split('\\n')\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const isLastPart = i === parts.length - 1\n\n if (part.length > 0) {\n // Process this part normally\n const wordsAndSpaces = part.split(/(\\s+)/).filter(Boolean)\n\n for (const wordOrSpace of wordsAndSpaces) {\n const isSpace = /^\\s+$/.test(wordOrSpace)\n let wordSegment: TextSegment\n let wordWidth: number\n\n if (isSpace) {\n wordSegment = { text: wordOrSpace, ...segmentStyle, width: 0 }\n wordWidth = 0\n } else {\n ctx.font = this.getFontString(segmentStyle)\n if (this.props.fontVariant) ctx.fontVariant = this.props.fontVariant\n wordWidth = ctx.measureText(wordOrSpace).width\n wordSegment = { text: wordOrSpace, ...segmentStyle, width: wordWidth }\n }\n\n const needsSpace =\n currentLineSegments.length > 0 &&\n !/^\\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text)\n const spaceToAdd = needsSpace ? spaceWidth + parsedWordSpacingPx : 0\n\n if (currentLineWidth + spaceToAdd + wordWidth <= maxWidth || currentLineSegments.length === 0) {\n if (needsSpace) {\n currentLineSegments.push({ text: ' ', ...segmentStyle, width: 0 })\n currentLineWidth += spaceToAdd\n }\n currentLineSegments.push(wordSegment)\n currentLineWidth += wordWidth\n } else {\n if (currentLineSegments.length > 0) {\n finalizeLine()\n }\n\n if (!isSpace) {\n if (wordWidth > maxWidth && maxWidth > 0) {\n const brokenParts = this.breakWordRich(ctx, wordSegment, maxWidth)\n\n if (brokenParts.length > 0) {\n for (let k = 0; k < brokenParts.length - 1; k++) {\n lines.push([brokenParts[k]])\n }\n currentLineSegments = [brokenParts[brokenParts.length - 1]]\n currentLineWidth = brokenParts[brokenParts.length - 1].width ?? 0\n } else {\n currentLineSegments = [wordSegment]\n currentLineWidth = wordWidth\n }\n } else {\n currentLineSegments = [wordSegment]\n currentLineWidth = wordWidth\n }\n }\n }\n }\n }\n\n // Force line break after each part except the last\n // If part is empty, this creates an empty line (like \\n\\n)\n if (!isLastPart) {\n finalizeLine(part.length === 0)\n }\n }\n } else {\n // No newlines - process normally\n const wordsAndSpaces = segment.text.split(/(\\s+)/).filter(Boolean)\n\n for (const wordOrSpace of wordsAndSpaces) {\n const isSpace = /^\\s+$/.test(wordOrSpace)\n let wordSegment: TextSegment\n let wordWidth: number\n\n if (isSpace) {\n wordSegment = { text: wordOrSpace, ...segmentStyle, width: 0 }\n wordWidth = 0\n } else {\n ctx.font = this.getFontString(segmentStyle)\n if (this.props.fontVariant) ctx.fontVariant = this.props.fontVariant\n wordWidth = ctx.measureText(wordOrSpace).width\n wordSegment = { text: wordOrSpace, ...segmentStyle, width: wordWidth }\n }\n\n const needsSpace =\n currentLineSegments.length > 0 && !/^\\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text)\n const spaceToAdd = needsSpace ? spaceWidth + parsedWordSpacingPx : 0\n\n if (currentLineWidth + spaceToAdd + wordWidth <= maxWidth || currentLineSegments.length === 0) {\n if (needsSpace) {\n currentLineSegments.push({ text: ' ', ...segmentStyle, width: 0 })\n currentLineWidth += spaceToAdd\n }\n currentLineSegments.push(wordSegment)\n currentLineWidth += wordWidth\n } else {\n if (currentLineSegments.length > 0) {\n finalizeLine()\n }\n\n if (!isSpace) {\n if (wordWidth > maxWidth && maxWidth > 0) {\n const brokenParts = this.breakWordRich(ctx, wordSegment, maxWidth)\n\n if (brokenParts.length > 0) {\n for (let k = 0; k < brokenParts.length - 1; k++) {\n lines.push([brokenParts[k]])\n }\n currentLineSegments = [brokenParts[brokenParts.length - 1]]\n currentLineWidth = brokenParts[brokenParts.length - 1].width ?? 0\n } else {\n currentLineSegments = [wordSegment]\n currentLineWidth = wordWidth\n }\n } else {\n currentLineSegments = [wordSegment]\n currentLineWidth = wordWidth\n }\n }\n }\n }\n }\n }\n\n finalizeLine()\n return lines\n }\n\n /**\n * Breaks a word segment into multiple segments that each fit within the specified width constraint.\n * Maintains all styling properties (color, weight, size, bold, italic) across broken segments.\n *\n * @param ctx - Canvas rendering context used for text measurements\n * @param segmentToBreak - Original text segment to split\n * @param maxWidth - Maximum width allowed for each resulting segment\n * @returns Array of TextSegments, each fitting maxWidth, or original segment if no breaking needed\n */\n private breakWordRich(ctx: CanvasRenderingContext2D, segmentToBreak: TextSegment, maxWidth: number): TextSegment[] {\n const word = segmentToBreak.text\n\n // Copy all style properties to maintain consistent styling across broken segments\n const style = {\n color: segmentToBreak.color,\n weight: segmentToBreak.weight,\n size: segmentToBreak.size,\n b: segmentToBreak.b,\n i: segmentToBreak.i,\n }\n\n if (maxWidth <= 0) return [segmentToBreak]\n\n const brokenSegments: TextSegment[] = []\n let currentPartText = ''\n\n // Configure context with segment style for accurate measurements\n ctx.font = this.getFontString(style)\n if (this.props.fontVariant) ctx.fontVariant = this.props.fontVariant\n\n // Process word character by character to find valid break points\n for (const char of word) {\n const testPartText = currentPartText + char\n const testPartWidth = ctx.measureText(testPartText).width\n\n if (testPartWidth > maxWidth) {\n // Current accumulated text exceeds width - create new segment\n if (currentPartText) {\n brokenSegments.push({\n text: currentPartText,\n ...style,\n width: ctx.measureText(currentPartText).width,\n })\n }\n\n // Handle current character that caused overflow\n currentPartText = char\n const currentCharWidth = ctx.measureText(currentPartText).width\n\n if (currentCharWidth > maxWidth) {\n // Single character is too wide - force break after it\n brokenSegments.push({\n text: currentPartText,\n ...style,\n width: currentCharWidth,\n })\n currentPartText = ''\n }\n } else {\n // Character fits - add to current part\n currentPartText = testPartText\n }\n }\n\n // Handle any remaining text as final segment\n if (currentPartText) {\n brokenSegments.push({\n text: currentPartText,\n ...style,\n width: ctx.measureText(currentPartText).width,\n })\n }\n\n return brokenSegments.length > 0 ? brokenSegments : [segmentToBreak]\n }\n\n /**\n * Measures width of space character using base font\n */\n private measureSpaceWidth(ctx: CanvasRenderingContext2D): number {\n const originalFont = ctx.font\n ctx.font = this.getFontString()\n const width = ctx.measureText(' ').width\n ctx.font = originalFont\n return width > 0 ? width : (this.props.fontSize || 16) * 0.3\n }\n\n /**\n * Renders multi-line text content with rich text styling and layout features\n *\n * Core features:\n * - Dynamic line heights with leading/spacing controls\n * - Vertical text alignment (top/middle/bottom)\n * - Horizontal text alignment (left/center/right/justify)\n * - Text wrapping within bounds\n * - Ellipsis truncation\n * - Rich text styling per segment (color, weight, size, etc)\n * - Performance optimizations (clipping, visibility checks)\n *\n * @param ctx - Canvas rendering context\n * @param x - Content box left position in pixels\n * @param y - Content box top position in pixels\n * @param width - Content box total width including padding\n * @param height - Content box total height including padding\n */\n protected override _renderContent(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n ) {\n super._renderContent(ctx, x, y, width, height)\n\n const linesToRender = this.getLinesToMeasureOrRender()\n const numLinesToRender = linesToRender.length\n\n // Validate required data is available\n if (\n numLinesToRender === 0 ||\n this.segments.length === 0 ||\n this.lineHeights.length !== numLinesToRender ||\n this.lineAscents.length !== numLinesToRender ||\n this.lineContentHeights.length !== numLinesToRender\n ) {\n return\n }\n\n ctx.save()\n ctx.textBaseline = 'alphabetic'\n ctx.letterSpacing = this.formatSpacing(this.props.letterSpacing)\n ctx.wordSpacing = 'normal'\n\n const baseFontSize = this.props.fontSize || 16\n const parsedWordSpacingPx = this.parseSpacingToPx(this.props.wordSpacing, baseFontSize)\n\n // Calculate content box with padding\n const paddingLeft = this.node.getComputedPadding(Style.Edge.Left) ?? 0\n const paddingTop = this.node.getComputedPadding(Style.Edge.Top) ?? 0\n const paddingRight = this.node.getComputedPadding(Style.Edge.Right) ?? 0\n const paddingBottom = this.node.getComputedPadding(Style.Edge.Bottom) ?? 0\n const contentX = x + paddingLeft\n const contentY = y + paddingTop\n const contentWidth = Math.max(0, width - paddingLeft - paddingRight)\n const contentHeight = Math.max(0, height - paddingTop - paddingBottom)\n\n if (contentWidth <= 0 || contentHeight <= 0) {\n ctx.restore()\n return\n }\n\n // Calculate vertical alignment offset\n const lineGapValue = this.props.lineGap\n const totalCalculatedTextHeight =\n this.lineHeights.reduce((sum, h) => sum + h, 0) + Math.max(0, numLinesToRender - 1) * lineGapValue\n\n let blockStartY: number\n switch (this.props.verticalAlign) {\n case 'middle':\n blockStartY = contentY + (contentHeight - totalCalculatedTextHeight) / 2\n break\n case 'bottom':\n blockStartY = contentY + contentHeight - totalCalculatedTextHeight\n break\n case 'top':\n default:\n blockStartY = contentY\n }\n\n let currentLineTopY = blockStartY\n\n // Setup text content clipping region\n ctx.beginPath()\n ctx.rect(contentX, contentY, contentWidth, contentHeight)\n ctx.clip()\n\n // Configure ellipsis if needed\n const ellipsisChar = typeof this.props.ellipsis === 'string' ? this.props.ellipsis : '...'\n const needsEllipsis = this.props.ellipsis && this.lines.length > numLinesToRender\n let ellipsisWidth = 0\n let ellipsisStyle: Partial<TextSegment> | undefined = undefined\n\n if (needsEllipsis) {\n const lastRenderedLine = linesToRender[numLinesToRender - 1]\n const lastTextStyleSegment = [...lastRenderedLine].reverse().find(seg => !/^\\s+$/.test(seg.text))\n\n // Inherit styles from last non-whitespace segment\n ellipsisStyle = lastTextStyleSegment\n ? {\n color: lastTextStyleSegment.color,\n weight: lastTextStyleSegment.weight,\n size: lastTextStyleSegment.size,\n b: lastTextStyleSegment.b,\n i: lastTextStyleSegment.i,\n }\n : undefined\n\n const originalFont = ctx.font\n const originalVariant = ctx.fontVariant\n ctx.font = this.getFontString(ellipsisStyle)\n\n // Handle font variant setting and validation\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in _renderContent (ellipsis measure):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n\n ellipsisWidth = ctx.measureText(ellipsisChar).width\n ctx.font = originalFont\n\n if (originalVariant !== 'normal') {\n ctx.fontVariant = originalVariant\n } else if (ctx.fontVariant !== 'normal') {\n ctx.fontVariant = 'normal'\n }\n }\n\n const spaceWidth = this.measureSpaceWidth(ctx)\n\n // Render text content line by line\n for (let i = 0; i < numLinesToRender; i++) {\n const lineSegments = linesToRender[i]\n const currentLineFinalHeight = this.lineHeights[i]\n const currentLineMaxAscent = this.lineAscents[i]\n const currentLineContentHeight = this.lineContentHeights[i]\n\n // Calculate line spacing metrics\n const currentLineLeading = currentLineFinalHeight - currentLineContentHeight\n const currentLineSpaceAbove = Math.max(0, currentLineLeading / 2)\n const lineY = currentLineTopY + currentLineSpaceAbove + currentLineMaxAscent\n\n // Visibility culling check\n const lineTop = currentLineTopY\n const lineBottom = currentLineTopY + currentLineFinalHeight\n\n // Don't skip empty lines - they're intentional (from \\n\\n)\n // Only skip if the line is completely outside the visible area\n if (lineBottom <= contentY || lineTop >= contentY + contentHeight) {\n currentLineTopY += currentLineFinalHeight + lineGapValue\n continue\n }\n\n const isLastRenderedLine = i === numLinesToRender - 1\n\n // Calculate line width metrics for alignment\n let totalLineWidth = 0\n let totalWordsWidth = 0\n let numWordGaps = 0\n let firstWordOnLine = true\n const noSpaceBeforePunctuation = /^[.,!?;:)\\]}]/\n\n for (const segment of lineSegments) {\n const segmentWidth = segment.width ?? 0\n const isSpaceSegment = /^\\s+$/.test(segment.text)\n\n if (!isSpaceSegment) {\n if (!firstWordOnLine) {\n totalLineWidth += spaceWidth + parsedWordSpacingPx\n if (!noSpaceBeforePunctuation.test(segment.text)) {\n numWordGaps++\n }\n }\n totalLineWidth += segmentWidth\n totalWordsWidth += segmentWidth\n firstWordOnLine = false\n }\n }\n\n // Calculate horizontal alignment position\n const isJustify = this.props.textAlign === 'justify' && !isLastRenderedLine\n const lineTextAlign = isJustify ? 'left' : this.props.textAlign || 'left'\n let currentX: number\n\n switch (lineTextAlign) {\n case 'center':\n currentX = contentX + (contentWidth - totalLineWidth) / 2\n break\n case 'right':\n case 'end':\n currentX = contentX + contentWidth - totalLineWidth\n break\n case 'left':\n case 'start':\n default:\n currentX = contentX\n }\n currentX = Math.max(contentX, currentX)\n\n // Calculate justification spacing\n let spacePerWordGapPlusSpacing = spaceWidth + parsedWordSpacingPx\n if (isJustify && numWordGaps > 0 && totalLineWidth < contentWidth) {\n const totalBaseSpacingWidth = numWordGaps * (spaceWidth + parsedWordSpacingPx)\n const remainingSpace = contentWidth - totalWordsWidth - totalBaseSpacingWidth\n if (remainingSpace > 0) {\n spacePerWordGapPlusSpacing += remainingSpace / numWordGaps\n }\n }\n\n // Render line segments (skip rendering for truly empty lines)\n if (lineSegments.length > 0 && !lineSegments.every(s => s.text.trim() === '')) {\n let accumulatedWidth = 0\n let ellipsisApplied = false\n let firstWordDrawn = false\n\n for (let j = 0; j < lineSegments.length; j++) {\n const segment = lineSegments[j]\n const segmentWidth = segment.width ?? 0\n const isLastSegmentOnLine = j === lineSegments.length - 1\n const isSpaceSegment = /^\\s+$/.test(segment.text)\n\n // Calculate word spacing\n let spaceToAddBefore = 0\n if (!isSpaceSegment && firstWordDrawn && !noSpaceBeforePunctuation.test(segment.text)) {\n spaceToAddBefore = isJustify ? spacePerWordGapPlusSpacing : spaceWidth + parsedWordSpacingPx\n }\n\n // Apply segment styles\n ctx.font = this.getFontString(segment)\n ctx.fillStyle = segment.color || this.props.color || 'black'\n\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in _renderContent (segment render):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n\n // Handle text truncation and ellipsis\n let textToDraw = segment.text\n let currentSegmentRenderWidth = segmentWidth\n let applyEllipsisAfter = false\n\n if (isLastRenderedLine && needsEllipsis && !isSpaceSegment) {\n const currentTotalWidth = accumulatedWidth + spaceToAddBefore + segmentWidth\n const spaceNeededAfter = isLastSegmentOnLine\n ? 0\n : isJustify\n ? spacePerWordGapPlusSpacing\n : spaceWidth + parsedWordSpacingPx\n\n if (currentTotalWidth > contentWidth - spaceNeededAfter) {\n const availableWidthForSegment = contentWidth - accumulatedWidth - spaceToAddBefore - ellipsisWidth\n if (availableWidthForSegment > 0) {\n let truncatedText = ''\n for (const char of segment.text) {\n if (ctx.measureText(truncatedText + char).width <= availableWidthForSegment) {\n truncatedText += char\n } else {\n break\n }\n }\n textToDraw = truncatedText\n currentSegmentRenderWidth = ctx.measureText(textToDraw).width\n } else {\n textToDraw = ''\n currentSegmentRenderWidth = 0\n }\n applyEllipsisAfter = true\n ellipsisApplied = true\n } else if (isLastSegmentOnLine) {\n applyEllipsisAfter = true\n ellipsisApplied = true\n }\n }\n\n // Render text segment\n currentX += spaceToAddBefore\n accumulatedWidth += spaceToAddBefore\n\n const remainingRenderWidth = contentX + contentWidth - currentX\n if (currentSegmentRenderWidth > 0 && remainingRenderWidth > 0 && !isSpaceSegment) {\n ctx.textAlign = 'left'\n\n const shadows = this.props.textShadow\n ? Array.isArray(this.props.textShadow)\n ? this.props.textShadow\n : [this.props.textShadow]\n : []\n\n ctx.save()\n\n // Draw shadows\n for (const shadow of shadows) {\n ctx.shadowColor = shadow.color || 'transparent'\n ctx.shadowBlur = shadow.blur || 0\n ctx.shadowOffsetX = shadow.offsetX || 0\n ctx.shadowOffsetY = shadow.offsetY || 0\n ctx.fillText(textToDraw, currentX, lineY, Math.max(0, remainingRenderWidth + 1))\n }\n\n // Reset shadow to draw the main text\n ctx.shadowColor = 'transparent'\n ctx.shadowBlur = 0\n ctx.shadowOffsetX = 0\n ctx.shadowOffsetY = 0\n\n ctx.fillText(textToDraw, currentX, lineY, Math.max(0, remainingRenderWidth + 1))\n\n ctx.restore()\n\n firstWordDrawn = true\n }\n\n currentX += currentSegmentRenderWidth\n accumulatedWidth += currentSegmentRenderWidth\n\n // Render ellipsis\n if (applyEllipsisAfter) {\n const ellipsisRemainingWidth = contentX + contentWidth - currentX\n if (ellipsisRemainingWidth >= ellipsisWidth) {\n const originalFont = ctx.font\n const originalVariant = ctx.fontVariant\n const originalFill = ctx.fillStyle\n\n ctx.font = this.getFontString(ellipsisStyle)\n\n if (typeof this.props.fontVariant === 'string') {\n ctx.fontVariant = this.props.fontVariant\n } else if (this.props.fontVariant !== undefined) {\n console.warn(\n `[TextNode ${this.key || ''}] Invalid fontVariant prop type in _renderContent (ellipsis draw):`,\n this.props.fontVariant,\n )\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n } else {\n if (ctx.fontVariant !== 'normal') ctx.fontVariant = 'normal'\n }\n\n ctx.fillStyle = ellipsisStyle?.color || this.props.color || 'black'\n ctx.fillText(ellipsisChar, currentX, lineY, Math.max(0, ellipsisRemainingWidth + 1))\n\n ctx.font = originalFont\n if (originalVariant !== 'normal') {\n ctx.fontVariant = originalVariant\n } else if (ctx.fontVariant !== 'normal') {\n ctx.fontVariant = 'normal'\n }\n ctx.fillStyle = originalFill\n }\n break\n }\n\n if (ellipsisApplied && currentX >= contentX + contentWidth) break\n }\n }\n\n currentLineTopY += currentLineFinalHeight + lineGapValue\n }\n\n ctx.restore()\n }\n}\n\n/**\n * Creates a new TextNode instance with rich text support\n */\nexport const Text = (text: number | string, props?: TextProps) => new TextNode(text, props)\n"],"names":["BoxNode","Canvas","Style"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Yoga, * as All from 'yoga-layout';
|
|
2
|
+
/**
|
|
3
|
+
* Style constants extending Yoga layout engine with additional border styles
|
|
4
|
+
*/
|
|
5
|
+
export declare enum Border {
|
|
6
|
+
Solid = 0,
|
|
7
|
+
Dashed = 1,
|
|
8
|
+
Dotted = 2
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Consolidated Style object combining Yoga layout constants and custom border styles
|
|
12
|
+
*/
|
|
13
|
+
export declare const Style: {
|
|
14
|
+
Border: typeof Border;
|
|
15
|
+
default: import("node_modules/yoga-layout/dist/src/wrapAssembly.js").Yoga;
|
|
16
|
+
Align: typeof All.Align;
|
|
17
|
+
BoxSizing: typeof All.BoxSizing;
|
|
18
|
+
Dimension: typeof All.Dimension;
|
|
19
|
+
Direction: typeof All.Direction;
|
|
20
|
+
Display: typeof All.Display;
|
|
21
|
+
Edge: typeof All.Edge;
|
|
22
|
+
Errata: typeof All.Errata;
|
|
23
|
+
ExperimentalFeature: typeof All.ExperimentalFeature;
|
|
24
|
+
FlexDirection: typeof All.FlexDirection;
|
|
25
|
+
Gutter: typeof All.Gutter;
|
|
26
|
+
Justify: typeof All.Justify;
|
|
27
|
+
LogLevel: typeof All.LogLevel;
|
|
28
|
+
MeasureMode: typeof All.MeasureMode;
|
|
29
|
+
NodeType: typeof All.NodeType;
|
|
30
|
+
Overflow: typeof All.Overflow;
|
|
31
|
+
PositionType: typeof All.PositionType;
|
|
32
|
+
Unit: typeof All.Unit;
|
|
33
|
+
Wrap: typeof All.Wrap;
|
|
34
|
+
};
|
|
35
|
+
export * from 'yoga-layout';
|
|
36
|
+
export default Yoga;
|
|
37
|
+
//# sourceMappingURL=common.const.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.const.d.ts","sourceRoot":"","sources":["../../../src/constant/common.const.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,EAAE,KAAK,GAAG,MAAM,aAAa,CAAA;AAExC;;GAEG;AACH,oBAAY,MAAM;IAChB,KAAK,IAAA;IACL,MAAM,IAAA;IACN,MAAM,IAAA;CACP;AAED;;GAEG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;CAGjB,CAAA;AAED,cAAc,aAAa,CAAA;AAC3B,eAAe,IAAI,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var YogaTypes = require('yoga-layout');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var YogaTypes__namespace = /*#__PURE__*/_interopNamespaceDefault(YogaTypes);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Style constants extending Yoga layout engine with additional border styles
|
|
28
|
+
*/
|
|
29
|
+
exports.Border = void 0;
|
|
30
|
+
(function (Border) {
|
|
31
|
+
Border[Border["Solid"] = 0] = "Solid";
|
|
32
|
+
Border[Border["Dashed"] = 1] = "Dashed";
|
|
33
|
+
Border[Border["Dotted"] = 2] = "Dotted";
|
|
34
|
+
})(exports.Border || (exports.Border = {}));
|
|
35
|
+
/**
|
|
36
|
+
* Consolidated Style object combining Yoga layout constants and custom border styles
|
|
37
|
+
*/
|
|
38
|
+
const Style = {
|
|
39
|
+
...YogaTypes__namespace,
|
|
40
|
+
Border: exports.Border,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
exports.default = YogaTypes;
|
|
44
|
+
exports.Style = Style;
|
|
45
|
+
Object.keys(YogaTypes).forEach(function (k) {
|
|
46
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function () { return YogaTypes[k]; }
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=common.const.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.const.js","sources":["../../../../src/constant/common.const.ts"],"sourcesContent":["import Yoga, * as All from 'yoga-layout'\n\n/**\n * Style constants extending Yoga layout engine with additional border styles\n */\nexport enum Border {\n Solid,\n Dashed,\n Dotted,\n}\n\n/**\n * Consolidated Style object combining Yoga layout constants and custom border styles\n */\nexport const Style = {\n ...All,\n Border,\n}\n\nexport * from 'yoga-layout'\nexport default Yoga\n"],"names":["Border","All"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAEA;;AAEG;AACSA;AAAZ,CAAA,UAAY,MAAM,EAAA;AAChB,IAAA,MAAA,CAAA,MAAA,CAAA,OAAA,CAAA,GAAA,CAAA,CAAA,GAAA,OAAK;AACL,IAAA,MAAA,CAAA,MAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAM;AACN,IAAA,MAAA,CAAA,MAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAM;AACR,CAAC,EAJWA,cAAM,KAANA,cAAM,GAAA,EAAA,CAAA,CAAA;AAMlB;;AAEG;AACI,MAAM,KAAK,GAAG;AACnB,IAAA,GAAGC,oBAAG;YACND,cAAM;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './constant/common.const.js';
|
|
2
|
+
export { Box, Column, Row, type BoxNode } from './canvas/layout.canvas.util.js';
|
|
3
|
+
export { Image } from './canvas/image.canvas.util.js';
|
|
4
|
+
export { Text } from './canvas/text.canvas.util.js';
|
|
5
|
+
export { Root } from './canvas/root.canvas.util.js';
|
|
6
|
+
export { Grid } from './canvas/grid.canvas.util.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAA;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var common_const = require('./constant/common.const.js');
|
|
4
|
+
var layout_canvas_util = require('./canvas/layout.canvas.util.js');
|
|
5
|
+
var image_canvas_util = require('./canvas/image.canvas.util.js');
|
|
6
|
+
var text_canvas_util = require('./canvas/text.canvas.util.js');
|
|
7
|
+
var root_canvas_util = require('./canvas/root.canvas.util.js');
|
|
8
|
+
var grid_canvas_util = require('./canvas/grid.canvas.util.js');
|
|
9
|
+
var YogaTypes = require('yoga-layout');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(exports, "Border", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return common_const.Border; }
|
|
16
|
+
});
|
|
17
|
+
exports.Style = common_const.Style;
|
|
18
|
+
exports.Box = layout_canvas_util.Box;
|
|
19
|
+
exports.Column = layout_canvas_util.Column;
|
|
20
|
+
exports.Row = layout_canvas_util.Row;
|
|
21
|
+
exports.Image = image_canvas_util.Image;
|
|
22
|
+
exports.Text = text_canvas_util.Text;
|
|
23
|
+
exports.Root = root_canvas_util.Root;
|
|
24
|
+
exports.Grid = grid_canvas_util.Grid;
|
|
25
|
+
Object.keys(YogaTypes).forEach(function (k) {
|
|
26
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return YogaTypes[k]; }
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CanvasRenderingContext2D } from 'skia-canvas';
|
|
2
|
+
import * as YogaTypes from 'yoga-layout';
|
|
3
|
+
import type { BoxProps } from '../canvas/canvas.type.js';
|
|
4
|
+
export declare const drawBorders: ({ ctx, node, x, y, width, height, radii, borderColor, borderStyle, }: {
|
|
5
|
+
ctx: CanvasRenderingContext2D;
|
|
6
|
+
node: YogaTypes.Node;
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
radii: {
|
|
12
|
+
TopLeft: number;
|
|
13
|
+
TopRight: number;
|
|
14
|
+
BottomLeft: number;
|
|
15
|
+
BottomRight: number;
|
|
16
|
+
};
|
|
17
|
+
borderColor: BoxProps["borderColor"];
|
|
18
|
+
borderStyle: BoxProps["borderStyle"];
|
|
19
|
+
}) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Draws an optimized rounded rectangle path on the canvas context.
|
|
22
|
+
* Automatically clamps radius values to prevent visual artifacts based on box dimensions.
|
|
23
|
+
* Uses arc-based rendering for crisp corners and consistent border appearance.
|
|
24
|
+
*
|
|
25
|
+
* @param ctx - The canvas 2D rendering context to draw on
|
|
26
|
+
* @param x - Left position of the rectangle
|
|
27
|
+
* @param y - Top position of the rectangle
|
|
28
|
+
* @param width - Width of the rectangle
|
|
29
|
+
* @param height - Height of the rectangle
|
|
30
|
+
* @param radii - Corner radius values for each corner. Values are clamped to box constraints.
|
|
31
|
+
*/
|
|
32
|
+
export declare const drawRoundedRectPath: (ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radii: {
|
|
33
|
+
TopLeft: number;
|
|
34
|
+
TopRight: number;
|
|
35
|
+
BottomRight: number;
|
|
36
|
+
BottomLeft: number;
|
|
37
|
+
}) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Calculates border radius values from props
|
|
40
|
+
* @param radiusProp - Border radius property value
|
|
41
|
+
* @returns Calculated border radii for all corners
|
|
42
|
+
*/
|
|
43
|
+
export declare const parseBorderRadius: (radiusProp: BoxProps["borderRadius"]) => {
|
|
44
|
+
TopLeft: number;
|
|
45
|
+
TopRight: number;
|
|
46
|
+
BottomRight: number;
|
|
47
|
+
BottomLeft: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Parses a percentage value or a number, returning the calculated value based on the base.
|
|
51
|
+
*
|
|
52
|
+
* @param value - The value to parse, can be a number, a percentage string, or undefined.
|
|
53
|
+
* @param base - The base value to calculate the percentage from.
|
|
54
|
+
* @returns The parsed number, or 0 if the value is not a number or a valid percentage.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parsePercentage(value: number | string | undefined, base: number): number;
|
|
57
|
+
//# sourceMappingURL=canvas.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.helper.d.ts","sourceRoot":"","sources":["../../../src/canvas/canvas.helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,KAAK,SAAS,MAAM,aAAa,CAAA;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD,eAAO,MAAM,WAAW,GAAI,sEAUzB;IACD,GAAG,EAAE,wBAAwB,CAAA;IAC7B,IAAI,EAAE,SAAS,CAAC,IAAI,CAAA;IACpB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;QAChB,UAAU,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAA;IACpC,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAA;CACrC,SAkJA,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAC9B,KAAK,wBAAwB,EAC7B,GAAG,MAAM,EACT,GAAG,MAAM,EACT,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,OAAO;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,SAwCtF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAC5B,YAAY,QAAQ,CAAC,cAAc,CAAC,KACnC;IACD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CAYnB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxF"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import * as YogaTypes from 'yoga-layout';
|
|
2
|
+
import { Style } from '../constant/common.const.js';
|
|
3
|
+
|
|
4
|
+
const drawBorders = ({ ctx, node, x, y, width, height, radii, borderColor, borderStyle, }) => {
|
|
5
|
+
const borderAll = node.getBorder(YogaTypes.Edge.All) || 0;
|
|
6
|
+
const borderTop = Math.max(0, node.getBorder(YogaTypes.Edge.Top) || borderAll);
|
|
7
|
+
const borderRight = Math.max(0, node.getBorder(YogaTypes.Edge.Right) || borderAll);
|
|
8
|
+
const borderBottom = Math.max(0, node.getBorder(YogaTypes.Edge.Bottom) || borderAll);
|
|
9
|
+
const borderLeft = Math.max(0, node.getBorder(YogaTypes.Edge.Left) || borderAll);
|
|
10
|
+
const hasBorder = borderTop > 0 || borderRight > 0 || borderBottom > 0 || borderLeft > 0;
|
|
11
|
+
const boxSizing = node.getBoxSizing();
|
|
12
|
+
if (hasBorder && borderColor) {
|
|
13
|
+
ctx.strokeStyle = borderColor;
|
|
14
|
+
ctx.lineCap = 'butt';
|
|
15
|
+
ctx.lineJoin = 'miter'; // Use miter for sharp corners unless rounded
|
|
16
|
+
const setDash = (width) => {
|
|
17
|
+
if (borderStyle === Style.Border.Dashed && width > 0) {
|
|
18
|
+
const dashLength = Math.max(2, width * 1.5);
|
|
19
|
+
const gapLength = Math.max(1, width);
|
|
20
|
+
ctx.setLineDash([dashLength, gapLength]);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
ctx.setLineDash([]); // Solid line
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Draws a rounded corner arc for the border.
|
|
28
|
+
*
|
|
29
|
+
* @param cx - The x-coordinate of the visual center of the corner curve.
|
|
30
|
+
* @param cy - The y-coordinate of the visual center of the corner curve.
|
|
31
|
+
* @param radius - The visual radius of the corner curve.
|
|
32
|
+
* @param startAngle - The starting angle of the arc in radians.
|
|
33
|
+
* @param endAngle - The ending angle of the arc in radians.
|
|
34
|
+
* @param border1 - The border width leading into the corner.
|
|
35
|
+
* @param border2 - The border width leading out of the corner.
|
|
36
|
+
*/
|
|
37
|
+
const drawCornerArc = (cx, cy, radius, startAngle, endAngle, border1, border2) => {
|
|
38
|
+
if (radius <= 0)
|
|
39
|
+
return;
|
|
40
|
+
const cornerWidth = Math.max(border1, border2);
|
|
41
|
+
if (cornerWidth <= 0)
|
|
42
|
+
return;
|
|
43
|
+
let centerlineArcRadius;
|
|
44
|
+
if (boxSizing === Style.BoxSizing.ContentBox) {
|
|
45
|
+
// For content-box, the border is outside the box, so the centerline radius is the visual radius plus half the border width.
|
|
46
|
+
centerlineArcRadius = radius + cornerWidth / 2;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// For border-box, the border is inside the box, so the centerline radius is the visual radius minus half the border width.
|
|
50
|
+
// Ensure the centerline radius is not negative.
|
|
51
|
+
centerlineArcRadius = Math.max(0, radius - cornerWidth / 2);
|
|
52
|
+
if (centerlineArcRadius <= 0 && radius > 0) {
|
|
53
|
+
// Draw cap for border-box when border is thicker than radius allows for centerline arc
|
|
54
|
+
ctx.fillStyle = borderColor; // Use border color for fill
|
|
55
|
+
ctx.beginPath();
|
|
56
|
+
// Cap is centered on the visual corner center with the visual radius
|
|
57
|
+
ctx.arc(cx, cy, radius, 0, 2 * Math.PI);
|
|
58
|
+
ctx.fill();
|
|
59
|
+
return; // Cap drawn, skip arc stroke
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Draw the normal arc stroke using the calculated centerline radius
|
|
63
|
+
ctx.beginPath();
|
|
64
|
+
ctx.lineWidth = cornerWidth;
|
|
65
|
+
setDash(cornerWidth);
|
|
66
|
+
ctx.arc(cx, cy, centerlineArcRadius, startAngle, endAngle);
|
|
67
|
+
ctx.stroke();
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Draws a straight line segment for the border.
|
|
71
|
+
*
|
|
72
|
+
* @param x1 - The x-coordinate of the starting point.
|
|
73
|
+
* @param y1 - The y-coordinate of the starting point.
|
|
74
|
+
* @param x2 - The x-coordinate of the ending point.
|
|
75
|
+
* @param y2 - The y-coordinate of the ending point.
|
|
76
|
+
* @param borderWidth - The width of the border.
|
|
77
|
+
*/
|
|
78
|
+
const drawLine = (x1, y1, x2, y2, borderWidth) => {
|
|
79
|
+
if (borderWidth <= 0)
|
|
80
|
+
return;
|
|
81
|
+
ctx.beginPath();
|
|
82
|
+
ctx.lineWidth = borderWidth;
|
|
83
|
+
setDash(borderWidth);
|
|
84
|
+
ctx.moveTo(x1, y1);
|
|
85
|
+
ctx.lineTo(x2, y2);
|
|
86
|
+
ctx.stroke();
|
|
87
|
+
};
|
|
88
|
+
// Calculate half-border widths
|
|
89
|
+
const halfBt = borderTop / 2;
|
|
90
|
+
const halfBr = borderRight / 2;
|
|
91
|
+
const halfBb = borderBottom / 2;
|
|
92
|
+
const halfBl = borderLeft / 2;
|
|
93
|
+
// Calculate effective visual radii, clamped to half dimensions of the *layout box*
|
|
94
|
+
const maxRadiusX = width / 2; // This matches CSS behavior where radius is relative to the box it's applied to.
|
|
95
|
+
const maxRadiusY = height / 2;
|
|
96
|
+
const rTL = Math.max(0, Math.min(radii.TopLeft, maxRadiusX, maxRadiusY));
|
|
97
|
+
const rTR = Math.max(0, Math.min(radii.TopRight, maxRadiusX, maxRadiusY));
|
|
98
|
+
const rBR = Math.max(0, Math.min(radii.BottomRight, maxRadiusX, maxRadiusY));
|
|
99
|
+
const rBL = Math.max(0, Math.min(radii.BottomLeft, maxRadiusX, maxRadiusY));
|
|
100
|
+
// --- Draw border segments based on boxSizing ---
|
|
101
|
+
// For content-box, coordinates are offset *outwards* from x, y, width, height
|
|
102
|
+
if (boxSizing === Style.BoxSizing.ContentBox) {
|
|
103
|
+
// Top line segment
|
|
104
|
+
drawLine(x + rTL, y - halfBt, x + width - rTR, y - halfBt, borderTop);
|
|
105
|
+
// Right line segment
|
|
106
|
+
drawLine(x + width + halfBr, y + rTR, x + width + halfBr, y + height - rBR, borderRight);
|
|
107
|
+
// Bottom line segment
|
|
108
|
+
drawLine(x + width - rBR, y + height + halfBb, x + rBL, y + height + halfBb, borderBottom);
|
|
109
|
+
// Left line segment
|
|
110
|
+
drawLine(x - halfBl, y + height - rBL, x - halfBl, y + rTL, borderLeft);
|
|
111
|
+
drawCornerArc(x + rTL, y + rTL, rTL, Math.PI, 1.5 * Math.PI, borderLeft, borderTop);
|
|
112
|
+
drawCornerArc(x + width - rTR, y + rTR, rTR, 1.5 * Math.PI, 2 * Math.PI, borderTop, borderRight);
|
|
113
|
+
drawCornerArc(x + width - rBR, y + height - rBR, rBR, 0, 0.5 * Math.PI, borderRight, borderBottom);
|
|
114
|
+
drawCornerArc(x + rBL, y + height - rBL, rBL, 0.5 * Math.PI, Math.PI, borderBottom, borderLeft);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// For border-box, coordinates are offset *inwards* from x, y, width, height
|
|
118
|
+
// Top line segment
|
|
119
|
+
drawLine(x + rTL, y + halfBt, x + width - rTR, y + halfBt, borderTop);
|
|
120
|
+
// Right line segment
|
|
121
|
+
drawLine(x + width - halfBr, y + rTR, x + width - halfBr, y + height - rBR, borderRight);
|
|
122
|
+
// Bottom line segment
|
|
123
|
+
drawLine(x + width - rBR, y + height - halfBb, x + rBL, y + height - halfBb, borderBottom);
|
|
124
|
+
// Left line segment
|
|
125
|
+
drawLine(x + halfBl, y + height - rBL, x + halfBl, y + rTL, borderLeft);
|
|
126
|
+
// Draw corner arcs (centers relative to layout box corners, adjusted for inward border)
|
|
127
|
+
// Pass visual radius (rTL, rTR etc.) to drawCornerArc
|
|
128
|
+
drawCornerArc(x + rTL, y + rTL, rTL, Math.PI, 1.5 * Math.PI, borderLeft, borderTop); // Top-Left
|
|
129
|
+
drawCornerArc(x + width - rTR, y + rTR, rTR, 1.5 * Math.PI, 2 * Math.PI, borderTop, borderRight); // Top-Right
|
|
130
|
+
drawCornerArc(x + width - rBR, y + height - rBR, rBR, 0, 0.5 * Math.PI, borderRight, borderBottom); // Bottom-Right
|
|
131
|
+
drawCornerArc(x + rBL, y + height - rBL, rBL, 0.5 * Math.PI, Math.PI, borderBottom, borderLeft); // Bottom-Left
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Draws an optimized rounded rectangle path on the canvas context.
|
|
137
|
+
* Automatically clamps radius values to prevent visual artifacts based on box dimensions.
|
|
138
|
+
* Uses arc-based rendering for crisp corners and consistent border appearance.
|
|
139
|
+
*
|
|
140
|
+
* @param ctx - The canvas 2D rendering context to draw on
|
|
141
|
+
* @param x - Left position of the rectangle
|
|
142
|
+
* @param y - Top position of the rectangle
|
|
143
|
+
* @param width - Width of the rectangle
|
|
144
|
+
* @param height - Height of the rectangle
|
|
145
|
+
* @param radii - Corner radius values for each corner. Values are clamped to box constraints.
|
|
146
|
+
*/
|
|
147
|
+
const drawRoundedRectPath = (ctx, x, y, width, height, radii) => {
|
|
148
|
+
if (width <= 0 || height <= 0) {
|
|
149
|
+
ctx.beginPath();
|
|
150
|
+
ctx.rect(x, y, width, height);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
ctx.beginPath();
|
|
154
|
+
// Clamp radius values to prevent visual artifacts
|
|
155
|
+
const maxRadius = Math.min(width / 2, height / 2);
|
|
156
|
+
const clampedTL = Math.max(0, Math.min(radii.TopLeft, maxRadius));
|
|
157
|
+
const clampedTR = Math.max(0, Math.min(radii.TopRight, maxRadius));
|
|
158
|
+
const clampedBR = Math.max(0, Math.min(radii.BottomRight, maxRadius));
|
|
159
|
+
const clampedBL = Math.max(0, Math.min(radii.BottomLeft, maxRadius));
|
|
160
|
+
ctx.moveTo(x + clampedTL, y);
|
|
161
|
+
// Draw top edge and top-right corner
|
|
162
|
+
ctx.lineTo(x + width - clampedTR, y);
|
|
163
|
+
clampedTR > 0 ? ctx.arc(x + width - clampedTR, y + clampedTR, clampedTR, 1.5 * Math.PI, 0) : ctx.lineTo(x + width, y);
|
|
164
|
+
// Draw right edge and bottom-right corner
|
|
165
|
+
ctx.lineTo(x + width, y + height - clampedBR);
|
|
166
|
+
clampedBR > 0
|
|
167
|
+
? ctx.arc(x + width - clampedBR, y + height - clampedBR, clampedBR, 0, 0.5 * Math.PI)
|
|
168
|
+
: ctx.lineTo(x + width, y + height);
|
|
169
|
+
// Draw bottom edge and bottom-left corner
|
|
170
|
+
ctx.lineTo(x + clampedBL, y + height);
|
|
171
|
+
clampedBL > 0
|
|
172
|
+
? ctx.arc(x + clampedBL, y + height - clampedBL, clampedBL, 0.5 * Math.PI, Math.PI)
|
|
173
|
+
: ctx.lineTo(x, y + height);
|
|
174
|
+
// Draw left edge and top-left corner
|
|
175
|
+
ctx.lineTo(x, y + clampedTL);
|
|
176
|
+
clampedTL > 0 ? ctx.arc(x + clampedTL, y + clampedTL, clampedTL, Math.PI, 1.5 * Math.PI) : ctx.lineTo(x, y);
|
|
177
|
+
ctx.closePath();
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Calculates border radius values from props
|
|
181
|
+
* @param radiusProp - Border radius property value
|
|
182
|
+
* @returns Calculated border radii for all corners
|
|
183
|
+
*/
|
|
184
|
+
const parseBorderRadius = (radiusProp) => {
|
|
185
|
+
const radii = { TopLeft: 0, TopRight: 0, BottomRight: 0, BottomLeft: 0 };
|
|
186
|
+
if (typeof radiusProp === 'number') {
|
|
187
|
+
radii.TopLeft = radii.TopRight = radii.BottomRight = radii.BottomLeft = Math.max(0, radiusProp);
|
|
188
|
+
}
|
|
189
|
+
else if (typeof radiusProp === 'object' && radiusProp !== null) {
|
|
190
|
+
radii.TopLeft = Math.max(0, radiusProp.TopLeft ?? 0);
|
|
191
|
+
radii.TopRight = Math.max(0, radiusProp.TopRight ?? 0);
|
|
192
|
+
radii.BottomRight = Math.max(0, radiusProp.BottomRight ?? 0);
|
|
193
|
+
radii.BottomLeft = Math.max(0, radiusProp.BottomLeft ?? 0);
|
|
194
|
+
}
|
|
195
|
+
return radii;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Parses a percentage value or a number, returning the calculated value based on the base.
|
|
199
|
+
*
|
|
200
|
+
* @param value - The value to parse, can be a number, a percentage string, or undefined.
|
|
201
|
+
* @param base - The base value to calculate the percentage from.
|
|
202
|
+
* @returns The parsed number, or 0 if the value is not a number or a valid percentage.
|
|
203
|
+
*/
|
|
204
|
+
function parsePercentage(value, base) {
|
|
205
|
+
if (typeof value === 'number') {
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
if (typeof value === 'string' && value.endsWith('%')) {
|
|
209
|
+
return base !== 0 ? (parseFloat(value) / 100) * base : 0;
|
|
210
|
+
}
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export { drawBorders, drawRoundedRectPath, parseBorderRadius, parsePercentage };
|