Inline LaTeX SVG content as a positioned, scaled element. Glyph are hoisted to the output SVG's top-level so that QSvgRenderer renders them as vector paths rather than rasterising them. IDs are prefixed with a unique token to avoid collisions when multiple exp
(parent, defs, svg_bytes, x, y, w, h)
| 445 | |
| 446 | |
| 447 | def _inline_latex_svg(parent, defs, svg_bytes, x, y, w, h): |
| 448 | """ |
| 449 | Inline LaTeX SVG content as a positioned, scaled <g> element. |
| 450 | |
| 451 | Glyph <defs> are hoisted to the output SVG's top-level <defs> so that |
| 452 | QSvgRenderer renders them as vector paths rather than rasterising them. |
| 453 | IDs are prefixed with a unique token to avoid collisions when multiple |
| 454 | expressions are inlined into the same document. |
| 455 | """ |
| 456 | global _latex_uid |
| 457 | try: |
| 458 | sym = ET.fromstring(svg_bytes.decode("utf-8", errors="replace")) |
| 459 | except ET.ParseError: |
| 460 | return |
| 461 | |
| 462 | vb_str = sym.get("viewBox", "") |
| 463 | parts = vb_str.split() if vb_str else [] |
| 464 | if len(parts) == 4: |
| 465 | vb_x, vb_y, vb_w, vb_h = (float(p) for p in parts) |
| 466 | else: |
| 467 | vb_x, vb_y, vb_w, vb_h = 0.0, 0.0, w, h |
| 468 | if vb_w <= 0 or vb_h <= 0: |
| 469 | return |
| 470 | |
| 471 | pfx = f"lt{_latex_uid}-" |
| 472 | _latex_uid += 1 |
| 473 | _rename_svg_ids(sym, pfx) |
| 474 | |
| 475 | defs_tag = f"{{{_SVG_NS}}}defs" |
| 476 | content = [] |
| 477 | for child in list(sym): |
| 478 | if child.tag == defs_tag: |
| 479 | for grandchild in list(child): |
| 480 | defs.append(grandchild) |
| 481 | else: |
| 482 | content.append(child) |
| 483 | |
| 484 | sx = w / vb_w |
| 485 | sy = h / vb_h |
| 486 | # y is the SVG bottom (bottom-aligned at label origin, matching text baseline). |
| 487 | # QSvgRenderer renders _svg_rect from y-h to y in canvas space. |
| 488 | transform = f"translate({x:.3f},{y - h:.3f}) scale({sx:.6f},{sy:.6f})" |
| 489 | if vb_x or vb_y: |
| 490 | transform += f" translate({-vb_x:.3f},{-vb_y:.3f})" |
| 491 | |
| 492 | g = ET.SubElement(parent, f"{{{_SVG_NS}}}g") |
| 493 | g.set("transform", transform) |
| 494 | for child in content: |
| 495 | g.append(child) |
| 496 | |
| 497 | |
| 498 | def _rename_svg_ids(element, prefix: str) -> None: |
no test coverage detected