Inline an SVG file as scaled vector content at scene position (x, y). Top-left corner is anchored at (x, y); content is scaled to fit w × h scene units. IDs are prefixed to avoid collisions with other inlined SVGs. Any inside the embedded SVG are hoisted to the output docum
(parent, defs, svg_bytes: bytes,
x: float, y: float, w: float, h: float,
aspect_fit: bool = False)
| 264 | |
| 265 | |
| 266 | def _inline_image_svg(parent, defs, svg_bytes: bytes, |
| 267 | x: float, y: float, w: float, h: float, |
| 268 | aspect_fit: bool = False) -> None: |
| 269 | """ |
| 270 | Inline an SVG file as scaled vector content at scene position (x, y). |
| 271 | |
| 272 | Top-left corner is anchored at (x, y); content is scaled to fit w × h |
| 273 | scene units. IDs are prefixed to avoid collisions with other inlined SVGs. |
| 274 | Any <defs> inside the embedded SVG are hoisted to the output document's |
| 275 | top-level <defs>. |
| 276 | |
| 277 | When *aspect_fit* is True, content is scaled uniformly to the largest size |
| 278 | that fits within (w, h) and centred — matching the canvas _aspect_fit |
| 279 | behaviour used for LatexFragmentItem, ParameterItem, and ModelItem. |
| 280 | """ |
| 281 | global _image_uid |
| 282 | try: |
| 283 | sym = ET.fromstring(svg_bytes.decode("utf-8", errors="replace")) |
| 284 | except ET.ParseError: |
| 285 | return |
| 286 | |
| 287 | # Determine natural size from viewBox, then width/height attributes |
| 288 | vb_str = sym.get("viewBox", "") |
| 289 | parts = vb_str.split() if vb_str else [] |
| 290 | if len(parts) == 4: |
| 291 | try: |
| 292 | vb_x, vb_y, vb_w, vb_h = (float(p) for p in parts) |
| 293 | except ValueError: |
| 294 | vb_x, vb_y, vb_w, vb_h = 0.0, 0.0, 0.0, 0.0 |
| 295 | else: |
| 296 | # Convert to CSS px so content coordinates (always in px) match. |
| 297 | vb_w = _svg_length_to_px(sym.get("width"), w) |
| 298 | vb_h = _svg_length_to_px(sym.get("height"), h) |
| 299 | vb_x, vb_y = 0.0, 0.0 |
| 300 | |
| 301 | if vb_w <= 0 or vb_h <= 0: |
| 302 | return |
| 303 | |
| 304 | pfx = f"img{_image_uid}-" |
| 305 | _image_uid += 1 |
| 306 | _rename_svg_ids(sym, pfx) |
| 307 | |
| 308 | defs_tag = f"{{{_SVG_NS}}}defs" |
| 309 | content = [] |
| 310 | for child in list(sym): |
| 311 | if child.tag == defs_tag: |
| 312 | for grandchild in list(child): |
| 313 | defs.append(grandchild) |
| 314 | else: |
| 315 | content.append(child) |
| 316 | |
| 317 | if aspect_fit: |
| 318 | scale = min(w / vb_w, h / vb_h) |
| 319 | fit_w = vb_w * scale |
| 320 | fit_h = vb_h * scale |
| 321 | x_off = x + (w - fit_w) / 2 |
| 322 | y_off = y + (h - fit_h) / 2 |
| 323 | transform = f"translate({x_off:.3f},{y_off:.3f}) scale({scale:.6f},{scale:.6f})" |
no test coverage detected