Skip to content

Closes #19902: add clip path to avoid overflow of device name #19913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion netbox/dcim/svg/racks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -177,12 +192,26 @@ def _draw_device(self, device, coords, size, color=None, image=None):
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:
Expand Down