Skip to content

Commit 7e73ebf

Browse files
authored
fix(arrows): skip rendering empty label in SVG export (tldraw#8137)
Closes tldraw#7055. When exporting arrows without text labels via `getSvgElement()`, the `<foreignObject>` element receives negative width/height values (e.g. `-8.5`), causing browser console errors. The root cause is in `ArrowShapeUtil.toSvg()`: it unconditionally renders a `RichTextSVG` for the arrow label and applies `.expandBy(-ARROW_LABEL_PADDING * scale)` to shrink the bounds by padding. For empty labels, `getArrowLabelPosition()` returns a zero-sized box, so subtracting `4.25 * 2 = 8.5` produces negative dimensions. The fix skips rendering `RichTextSVG` entirely when the label is empty, matching the existing guard in the canvas rendering path. ### Change type - [x] `bugfix` ### Test plan 1. Create an arrow with no text label 2. Export as SVG (File > Export as SVG) 3. Check the browser console — no more `<foreignObject> attribute width: A negative value is not valid` errors 4. Create an arrow with a text label and export — label renders correctly ### Release notes - Fix arrow SVG export producing invalid negative `<foreignObject>` dimensions when arrows have no text label <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: a small conditional render change limited to SVG export output, with no impact on arrow geometry or editing behavior beyond avoiding invalid `<foreignObject>` dimensions. > > **Overview** > Fixes arrow SVG export emitting invalid `<foreignObject>` sizes when an arrow has no text label. > > `ArrowShapeUtil.toSvg()` now conditionally renders `RichTextSVG` only when `richText` is non-empty, avoiding negative label bounds after padding is applied. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 58fb598. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 11911bd commit 7e73ebf

1 file changed

Lines changed: 17 additions & 13 deletions

File tree

packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,22 +1080,26 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
10801080
const theme = getDefaultColorTheme(ctx)
10811081
const scaleFactor = 1 / shape.props.scale
10821082

1083+
const showArrowLabel = !isEmptyRichText(shape.props.richText)
1084+
10831085
return (
10841086
<g transform={`scale(${scaleFactor})`}>
10851087
<ArrowSvg shape={shape} shouldDisplayHandles={false} />
1086-
<RichTextSVG
1087-
fontSize={getArrowLabelFontSize(shape)}
1088-
font={shape.props.font}
1089-
align="middle"
1090-
verticalAlign="middle"
1091-
labelColor={getColorValue(theme, shape.props.labelColor, 'solid')}
1092-
richText={shape.props.richText}
1093-
bounds={getArrowLabelPosition(this.editor, shape, false)
1094-
.box.clone()
1095-
.expandBy(-ARROW_LABEL_PADDING * shape.props.scale)}
1096-
padding={0}
1097-
showTextOutline={this.options.showTextOutline}
1098-
/>
1088+
{showArrowLabel && (
1089+
<RichTextSVG
1090+
fontSize={getArrowLabelFontSize(shape)}
1091+
font={shape.props.font}
1092+
align="middle"
1093+
verticalAlign="middle"
1094+
labelColor={getColorValue(theme, shape.props.labelColor, 'solid')}
1095+
richText={shape.props.richText}
1096+
bounds={getArrowLabelPosition(this.editor, shape, false)
1097+
.box.clone()
1098+
.expandBy(-ARROW_LABEL_PADDING * shape.props.scale)}
1099+
padding={0}
1100+
showTextOutline={this.options.showTextOutline}
1101+
/>
1102+
)}
10991103
</g>
11001104
)
11011105
}

0 commit comments

Comments
 (0)