Fix non-square icons squashed instead of scaled to fit - #2142
Open
mjuarros wants to merge 1 commit into
Open
Conversation
A custom icon whose width and height differ was stretched into a square on both the graph canvas and DOM surfaces (search results, legends), instead of being scaled down while preserving its aspect ratio. Affects raster images and SVGs alike. Root causes, all now fixed: - defaultNodeStyle forced backgroundWidth/backgroundHeight to the same 60% for every icon regardless of shape. useGraphStyles now computes per-icon percentages from the icon's real aspect ratio, falling back to 60%/60% only when dimensions are unknown. - Icon dimensions were never measured: raster icons resolved synchronously with no size info, and SVGs weren't inspected at all. iconRegistry now measures a raster's natural size via `Image`, and extracts an SVG's size from its width/height or viewBox. - iconImageUrl forced every SVG's own intrinsic width/height to a fixed 24x24 square before handing it to cytoscape. For a non-square icon this baked a mismatched-aspect letterbox into the rasterized image, which cytoscape's own aspect-aware background-width/height then stretched a second time — distorting worse than doing nothing. It now scales the intrinsic box to the icon's real aspect ratio instead. - An SVG with width/height but no viewBox has no coordinate system to scale from, so forcing a different display box just clips the content instead of scaling it. Added ensureSvgViewBox() to synthesize one when missing, applied wherever an SVG is sanitized (DOM render and canvas resolution) via a new shared SVG_ALLOWED_ATTR allowlist — DOMPurify's default SVG profile otherwise strips width/height/viewBox outright. - VertexIcon's plain `<img>` (non-SVG raster fallback) had no `object-fit`, so the browser's default `fill` stretched it; added `object-contain`. Added an `Image` test double to setupTests.ts, since jsdom never decodes images and would otherwise hang any test that resolves a raster icon's dimensions.
mjuarros
marked this pull request as ready for review
August 31, 2026 22:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Non-square custom vertex icons (raster images and SVG) were stretched into a square instead of scaled down to fit, distorting them. This happened consistently on both the graph canvas and DOM surfaces (search results, legend).
The root cause: Cytoscape's
drawInscribedImagedoes not preserve aspect ratio whenbackground-widthandbackground-heightare both set to percentages. They override the image's natural dimensions, thenbackground-fit: containscales the already-squashed result — the image is already distorted by then. The DOM rendered images withobject-fit: fill(default), which has identical behavior.How it's fixed
Icon measurement and aspect-ratio carry: The icon registry now measures the natural width/height of raster images when they resolve. That ratio is carried on the icon object through the system.
Aspect-fit computation: The background width/height for the Cytoscape canvas are computed from the target box (60% of node size) and the image's aspect ratio. The shorter axis is shrunk so the image fits inside the box without stretching — e.g., a 4:1 wide logo becomes
60%width,15%height.DOM surface fix:
VertexIcongainsobject-containso its<img>respects the aspect ratio.SVG handling: SVG images already carry a viewBox, so they letterbox themselves naturally with
preserveAspectRatio=xMidYMid meet. New utility functions extract and validate viewBox data.How to read
Supporting files (tests, minor updates): CONTEXT.md, setupTests.ts, and test files for all new/modified functions.
Validation
All unit tests pass. Manual testing confirmed non-square icons now render at their natural aspect ratio on the graph canvas and in DOM surfaces (search results, legend).
Before Fix

After Fix
