(parent, defs, item, lbl_color, lbl_fs)
| 335 | |
| 336 | |
| 337 | def _component(parent, defs, item, lbl_color, lbl_fs): |
| 338 | px, py = item.pos().x(), item.pos().y() |
| 339 | rot = item.rotation() |
| 340 | sx = -1 if item.h_flip else 1 |
| 341 | sy = -1 if item.v_flip else 1 |
| 342 | |
| 343 | parts = [f"translate({px:.2f},{py:.2f})"] |
| 344 | if rot: |
| 345 | parts.append(f"rotate({rot:.3f})") |
| 346 | if sx != 1 or sy != 1: |
| 347 | parts.append(f"scale({sx},{sy})") |
| 348 | |
| 349 | try: |
| 350 | sym = ET.fromstring(item._svg_bytes.decode("utf-8", errors="replace")) |
| 351 | except ET.ParseError: |
| 352 | return |
| 353 | |
| 354 | g = ET.SubElement(parent, f"{{{_SVG_NS}}}g") |
| 355 | g.set("transform", " ".join(parts)) |
| 356 | g.set("font-size", f"{lbl_fs}pt") # baseline for text within symbol (e.g. +/-) |
| 357 | for child in sym: |
| 358 | g.append(child) |
| 359 | |
| 360 | # Embedded symbol text was stripped from the artwork (item._svg_bytes); emit |
| 361 | # each piece upright at its transformed position so it stays readable under |
| 362 | # rotation/mirroring, matching the canvas. |
| 363 | from . import config as _cfg |
| 364 | for t in getattr(item, "symbol_texts", ()): |
| 365 | content = t.get("content") |
| 366 | if not content: |
| 367 | continue |
| 368 | sp = item.mapToScene(QPointF(t["x"], t["y"])) |
| 369 | te = ET.SubElement(parent, f"{{{_SVG_NS}}}text") |
| 370 | te.set("x", f"{sp.x():.2f}") |
| 371 | te.set("y", f"{sp.y():.2f}") |
| 372 | te.set("font-family", "sans-serif") |
| 373 | te.set("font-size", f"{t['size']:.1f}") |
| 374 | te.set("text-anchor", "middle") # centred horizontally and … |
| 375 | te.set("dominant-baseline", "central") # … vertically on the anchor point |
| 376 | te.set("fill", _qhex(_cfg.SYMBOL_TEXT_COLOR)) |
| 377 | te.text = content |
| 378 | |
| 379 | for lbl in item._labels.values(): |
| 380 | sp = lbl.mapToScene(QPointF(0.0, 0.0)) |
| 381 | font, color = lbl._font_and_color() |
| 382 | clr = _qhex(color) |
| 383 | fs = font.pointSizeF() |
| 384 | if lbl._text: |
| 385 | t = ET.SubElement(parent, f"{{{_SVG_NS}}}text") |
| 386 | t.set("x", f"{sp.x():.2f}") |
| 387 | t.set("y", f"{sp.y():.2f}") |
| 388 | t.set("font-family", font.family() or "sans-serif") |
| 389 | t.set("font-size", f"{fs:.1f}pt") |
| 390 | t.set("fill", clr) |
| 391 | # For h_flipped components the label's local origin is baseline-right; |
| 392 | # use text-anchor="end" so SVG anchors the text on the same side. |
| 393 | if item.h_flip: |
| 394 | t.set("text-anchor", "end") |
no test coverage detected