From d571cb48678fd80d684031b949ebbd39a8cb3aae Mon Sep 17 00:00:00 2001 From: Marco Spizzuoco <49124921+MarcoSpiz@users.noreply.github.com> Date: Tue, 22 Jul 2025 18:44:14 +0200 Subject: [PATCH] Closes #19902: add clip path to avoid overflow of device name, truncate text to improve centering (#19913) --- netbox/dcim/svg/racks.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/svg/racks.py b/netbox/dcim/svg/racks.py index de695664a..7bea9d91d 100644 --- a/netbox/dcim/svg/racks.py +++ b/netbox/dcim/svg/racks.py @@ -3,6 +3,7 @@ import svgwrite from svgwrite.container import Hyperlink from svgwrite.image import Image from svgwrite.gradients import LinearGradient +from svgwrite.masking import ClipPath from svgwrite.shapes import Rect from svgwrite.text import Text @@ -67,6 +68,20 @@ def get_device_description(device): return description +def truncate_text(text, width, font_size=15): + """ + Truncate text to fit within the width of a rectangle. + + :param text: The text to truncate + :param width: Width of rectangle + :param font_size: Font size (default is 15, ~0.875rem) + """ + char_width = font_size * 0.6 # 0.6 is an approximation of the average character width in pixels + max_char = int(width / char_width) + + return text if len(text) <= max_char else text[:max_char] + '...' + + class RackElevationSVG: """ Use this class to render a rack elevation as an SVG image. @@ -177,12 +192,26 @@ class RackElevationSVG: link = Hyperlink(href=f'{self.base_url}{device.get_absolute_url()}', target="_parent") link.set_desc(description) + # Create clipPath element + # This is necessary as fallback because the truncate_text method is an approximation + clip_id = f"clip-{device.id}" + clip_path = ClipPath(id=clip_id) + clip_path.add(Rect(coords, size)) + + self.drawing.defs.add(clip_path) + + # Name to display + display_name = truncate_text(name, size[0]) + # Add rect element to hyperlink if color: link.add(Rect(coords, size, style=f'fill: #{color}', class_=f'slot{css_extra}')) else: link.add(Rect(coords, size, class_=f'slot blocked{css_extra}')) - link.add(Text(name, insert=text_coords, fill=text_color, class_=f'label{css_extra}')) + link.add( + Text(display_name, insert=text_coords, fill=text_color, clip_path=f"url(#{clip_id})", + class_=f'label{css_extra}') + ) # Embed device type image if provided if self.include_images and image: