Skip to content

SVG Renderer

SVG rendering engine for converting diagrams to SVG files.

render_svg

SVG rendering for GPIO diagrams.

SVGRenderer

SVGRenderer(layout_config: LayoutConfig | None = None)

Render GPIO wiring diagrams to SVG format.

Converts a Diagram object into a scalable SVG image showing the Raspberry Pi board, connected devices, wire connections, and optional GPIO reference diagram.

The renderer handles
  • Board SVG asset embedding
  • Device box rendering with labeled pins
  • Wire routing with rounded corners
  • Inline component symbols (resistors, capacitors, diodes)
  • GPIO pin numbering and color coding
  • Optional GPIO reference diagram
  • Automatic layout via LayoutEngine

Examples:

>>> from pinviz import boards, devices, Connection, Diagram, SVGRenderer
>>>
>>> diagram = Diagram(
...     title="LED Circuit",
...     board=boards.raspberry_pi_5(),
...     devices=[devices.led()],
...     connections=[
...         Connection(11, "LED", "Anode"),
...         Connection(6, "LED", "Cathode")
...     ]
... )
>>>
>>> renderer = SVGRenderer()
>>> renderer.render(diagram, "led_circuit.svg")

Initialize SVG renderer with optional layout configuration.

PARAMETER DESCRIPTION
layout_config

Layout configuration for spacing and margins. If None, uses default LayoutConfig.

TYPE: LayoutConfig | None DEFAULT: None

Source code in src/pinviz/render_svg.py
def __init__(self, layout_config: LayoutConfig | None = None):
    """
    Initialize SVG renderer with optional layout configuration.

    Args:
        layout_config: Layout configuration for spacing and margins.
            If None, uses default LayoutConfig.
    """
    self.layout_config = layout_config or LayoutConfig()
    self.layout_engine = LayoutEngine(self.layout_config)
    self._init_svg_handlers()

render

render(diagram: Diagram, output_path: str | Path) -> None

Render a diagram to an SVG file.

PARAMETER DESCRIPTION
diagram

The diagram to render

TYPE: Diagram

output_path

Output file path

TYPE: str | Path

Source code in src/pinviz/render_svg.py
def render(self, diagram: Diagram, output_path: str | Path) -> None:
    """
    Render a diagram to an SVG file.

    Args:
        diagram: The diagram to render
        output_path: Output file path
    """
    log.info(
        "render_started",
        output_path=str(output_path),
        title=diagram.title,
        device_count=len(diagram.devices),
        connection_count=len(diagram.connections),
    )

    # Get color scheme from theme
    color_scheme = get_color_scheme(diagram.theme)

    # Initialize renderers with color scheme
    wire_renderer = WireRenderer(self.layout_config, color_scheme)
    component_renderer = ComponentRenderer(color_scheme)

    # Calculate layout (returns immutable LayoutResult)
    log.debug("calculating_layout")
    layout_result = self.layout_engine.layout_diagram(diagram)
    log.debug(
        "layout_calculated",
        canvas_width=layout_result.canvas_width,
        canvas_height=layout_result.canvas_height,
        wire_count=len(layout_result.routed_wires),
    )

    # Extract layout data for rendering
    canvas_width = layout_result.canvas_width
    canvas_height = layout_result.canvas_height
    routed_wires = layout_result.routed_wires
    board_margin_top = layout_result.board_margin_top

    # Create SVG drawing
    dwg = draw.Drawing(canvas_width, canvas_height)

    # Add background
    dwg.append(
        draw.Rectangle(0, 0, canvas_width, canvas_height, fill=color_scheme.canvas_background)
    )

    # Draw title
    if diagram.title and diagram.show_title:
        dwg.append(
            draw.Text(
                diagram.title,
                RENDER_CONSTANTS.TITLE_FONT_SIZE,
                canvas_width / 2,
                RENDER_CONSTANTS.TITLE_Y_OFFSET,
                text_anchor="middle",
                font_family="Arial, sans-serif",
                font_weight="bold",
                fill=color_scheme.text_primary,
            )
        )

    # Draw board
    log.debug("drawing_board", board_name=diagram.board.name)
    self._draw_board(
        dwg,
        diagram.board,
        board_margin_top,
        diagram.show_board_name,
        color_scheme,
        component_renderer,
    )

    # Draw GPIO pin numbers on the header
    x = self.layout_config.board_margin_left
    y = board_margin_top
    self._draw_gpio_pin_numbers(dwg, diagram.board, x, y, color_scheme)

    # Draw wires first so they appear behind devices
    # Sort wires for proper z-order to prevent overlapping/hiding
    # Primary: source pin X position (left column pins first, right column on top)
    # Secondary: destination Y (lower devices first)
    # Tertiary: rail X position (further right first)
    # This ensures wires from right GPIO column are always on top
    log.debug("drawing_wires", wire_count=len(routed_wires))
    sorted_wires = sorted(
        routed_wires,
        key=lambda w: (
            w.from_pin_pos.x,  # Left column first, right column on top
            -w.to_pin_pos.y,  # Lower Y (higher devices) drawn last
            -(w.path_points[2].x if len(w.path_points) > 2 else w.path_points[0].x),
        ),
    )
    for wire in sorted_wires:
        wire_renderer.draw_wire(dwg, wire, draw_connection_segment=False)

    # Draw device boxes (without pins yet)
    log.debug("drawing_devices", device_count=len(diagram.devices))
    for device in diagram.devices:
        component_renderer.draw_device_box(dwg, device)

    # Draw wire connection segments on top of device boxes
    for wire in sorted_wires:
        wire_renderer.draw_wire_connection_segment(dwg, wire)

    # Draw device pins on top of everything
    for device in diagram.devices:
        component_renderer.draw_device_pins(dwg, device)

    # Draw device specifications table if legend is enabled
    if diagram.show_legend:
        self._draw_device_specs_table(dwg, diagram, board_margin_top, color_scheme)

    # Save
    log.debug("saving_svg", output_path=str(output_path))
    dwg.save_svg(str(output_path))
    log.info("render_completed", output_path=str(output_path))

render_to_string

render_to_string(diagram: Diagram) -> str

Render a diagram to an SVG string.

PARAMETER DESCRIPTION
diagram

The diagram to render

TYPE: Diagram

RETURNS DESCRIPTION
str

SVG content as string

Source code in src/pinviz/render_svg.py
def render_to_string(self, diagram: Diagram) -> str:
    """
    Render a diagram to an SVG string.

    Args:
        diagram: The diagram to render

    Returns:
        SVG content as string
    """
    import tempfile

    with tempfile.NamedTemporaryFile(mode="w", suffix=".svg", delete=False) as f:
        temp_path = f.name

    try:
        self.render(diagram, temp_path)
        with open(temp_path) as f:
            return f.read()
    finally:
        Path(temp_path).unlink(missing_ok=True)