Claude Code for svgwrite: SVG Vector Graphics in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for svgwrite: SVG Vector Graphics in Python
AI

Claude Code for svgwrite: SVG Vector Graphics in Python

Published: April 27, 2028
Read time: 5 min read
By: Claude Skills 360

svgwrite generates SVG files in pure Python. pip install svgwrite. Basic: import svgwrite; dwg = svgwrite.Drawing("out.svg", size=("200px","100px")). Add shape: dwg.add(dwg.rect((10,10),(180,80), fill="blue")). Save: dwg.save(). String: dwg.tostring(). Circle: dwg.circle(center=(cx,cy), r=radius, fill="red", stroke="black", stroke_width=2). Line: dwg.line(start=(x1,y1), end=(x2,y2), stroke="black"). Polyline: dwg.polyline([(0,0),(50,50),(100,0)], stroke="blue", fill="none"). Polygon: dwg.polygon([(0,0),(100,0),(50,86)], fill="green"). Text: dwg.text("Hello", insert=(x,y), fill="black", font_size="14px", font_family="Arial"). tspan: text.add(dwg.tspan("bold part", font_weight="bold")). Path: dwg.path(d="M10,10 L100,10 L100,100 Z", fill="none", stroke="black"). Group: g = dwg.g(transform="translate(10,10)"); g.add(...). Gradient: grad = dwg.linearGradient(...); grad.add_stop_color(0, "blue"); grad.add_stop_color(1, "red"); dwg.defs.add(grad). fill=grad.get_paint_server(). ViewBox: Drawing(size=("100%","100%"), viewBox="0 0 400 300"). Use: sym = dwg.symbol(); dwg.defs.add(sym); dwg.add(dwg.use(sym.get_iri(), insert=(x,y))). Opacity: fill_opacity=0.5. Clip: clip = dwg.clipPath(). Image: dwg.image(href="logo.png", insert=(0,0), size=(100,100)). Claude Code generates svgwrite bar charts, pie charts, SVG badges, and data visualization templates.

CLAUDE.md for svgwrite

## svgwrite Stack
- Version: svgwrite >= 1.4 | pip install svgwrite
- Init: dwg = svgwrite.Drawing(filename, size=(w, h), profile="full")
- Add: dwg.add(dwg.rect/circle/line/text/path(...))
- Group: g = dwg.g(transform="..."); g.add(shape); dwg.add(g)
- Gradient: defs.add(grad); use fill=grad.get_paint_server()
- Output: dwg.tostring() → str | dwg.save() → file

svgwrite SVG Generation Pipeline

# app/svg_gen.py — svgwrite bar chart, line chart, badges, and shape helpers
from __future__ import annotations

import math
from typing import Any

import svgwrite
from svgwrite import Drawing
from svgwrite.container import Group


# ─────────────────────────────────────────────────────────────────────────────
# 1. Drawing factory
# ─────────────────────────────────────────────────────────────────────────────

def make_drawing(
    width: int,
    height: int,
    filename: str = "out.svg",
    responsive: bool = False,
) -> Drawing:
    """
    Create an svgwrite Drawing.
    responsive=True: sets size to 100%/100% with a viewBox so the SVG scales.
    """
    if responsive:
        return svgwrite.Drawing(
            filename,
            size=("100%", "100%"),
            viewBox=f"0 0 {width} {height}",
            profile="full",
        )
    return svgwrite.Drawing(filename, size=(width, height), profile="full")


def svg_string(dwg: Drawing) -> str:
    """Return the SVG as a UTF-8 string."""
    return dwg.tostring()


# ─────────────────────────────────────────────────────────────────────────────
# 2. Bar chart
# ─────────────────────────────────────────────────────────────────────────────

def bar_chart(
    data: list[tuple[str, float]],
    title: str = "",
    width: int = 480,
    height: int = 300,
    bar_color: str = "#4a90d9",
    bg_color: str = "#ffffff",
    font_family: str = "Arial, sans-serif",
) -> str:
    """
    Generate a vertical bar chart SVG.
    data: [(label, value), ...]
    Returns SVG string.
    """
    pad_left, pad_right, pad_top, pad_bottom = 50, 20, 40, 60
    chart_w = width - pad_left - pad_right
    chart_h = height - pad_top - pad_bottom

    dwg = make_drawing(width, height)

    # Background
    dwg.add(dwg.rect((0, 0), (width, height), fill=bg_color))

    # Title
    if title:
        dwg.add(dwg.text(
            title,
            insert=(width / 2, 22),
            text_anchor="middle",
            fill="#333",
            font_size="13px",
            font_family=font_family,
            font_weight="bold",
        ))

    if not data:
        return svg_string(dwg)

    max_val = max(v for _, v in data) or 1
    n = len(data)
    bar_w = chart_w / n * 0.6
    gap   = chart_w / n

    for i, (label, value) in enumerate(data):
        x = pad_left + i * gap + (gap - bar_w) / 2
        bar_h = (value / max_val) * chart_h
        y = pad_top + chart_h - bar_h

        # Bar
        dwg.add(dwg.rect(
            (x, y), (bar_w, bar_h),
            fill=bar_color, rx=3, ry=3,
        ))

        # Value label above bar
        dwg.add(dwg.text(
            f"{value:,.0f}",
            insert=(x + bar_w / 2, y - 4),
            text_anchor="middle",
            fill="#333",
            font_size="9px",
            font_family=font_family,
        ))

        # X axis label
        dwg.add(dwg.text(
            label,
            insert=(x + bar_w / 2, pad_top + chart_h + 16),
            text_anchor="middle",
            fill="#555",
            font_size="10px",
            font_family=font_family,
        ))

    # Axis line
    dwg.add(dwg.line(
        start=(pad_left, pad_top + chart_h),
        end=(width - pad_right, pad_top + chart_h),
        stroke="#ccc",
        stroke_width=1,
    ))

    return svg_string(dwg)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Line chart
# ─────────────────────────────────────────────────────────────────────────────

def line_chart(
    series: dict[str, list[tuple[str, float]]],
    title: str = "",
    width: int = 480,
    height: int = 280,
    colors: list[str] | None = None,
    font_family: str = "Arial, sans-serif",
) -> str:
    """
    Multi-series line chart.
    series: {series_name: [(label, value), ...]}
    """
    COLORS = colors or ["#4a90d9", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6"]

    pad_left, pad_right, pad_top, pad_bottom = 50, 20, 40, 60
    chart_w = width - pad_left - pad_right
    chart_h = height - pad_top - pad_bottom

    dwg = make_drawing(width, height)
    dwg.add(dwg.rect((0, 0), (width, height), fill="#fff"))

    if title:
        dwg.add(dwg.text(title, insert=(width / 2, 22), text_anchor="middle",
                         fill="#333", font_size="13px", font_family=font_family,
                         font_weight="bold"))

    all_vals = [v for pts in series.values() for _, v in pts]
    if not all_vals:
        return svg_string(dwg)

    max_val  = max(all_vals) or 1
    all_labels = [lbl for _, pts in series.items() for lbl, _ in pts[:1]]

    def to_xy(idx: int, val: float, n: int) -> tuple[float, float]:
        x = pad_left + (idx / max(n - 1, 1)) * chart_w
        y = pad_top + chart_h - (val / max_val) * chart_h
        return x, y

    for s_idx, (name, points) in enumerate(series.items()):
        color = COLORS[s_idx % len(COLORS)]
        n = len(points)

        coords = [to_xy(i, v, n) for i, (_, v) in enumerate(points)]
        polyline_pts = [(round(x, 1), round(y, 1)) for x, y in coords]

        dwg.add(dwg.polyline(polyline_pts, stroke=color, stroke_width=2,
                             fill="none", stroke_linejoin="round",
                             stroke_linecap="round"))

        # Data points
        for x, y in coords:
            dwg.add(dwg.circle(center=(x, y), r=3, fill=color, stroke="#fff",
                               stroke_width=1.5))

        # Legend entry
        leg_x = pad_left + s_idx * 100
        if leg_x + 90 < width:
            dwg.add(dwg.line(start=(leg_x, height - 18), end=(leg_x + 16, height - 18),
                             stroke=color, stroke_width=2))
            dwg.add(dwg.text(name, insert=(leg_x + 20, height - 14), fill="#555",
                             font_size="9px", font_family=font_family))

    # X labels
    first_series = next(iter(series.values()), [])
    n = len(first_series)
    for i, (label, _) in enumerate(first_series):
        x, _ = to_xy(i, 0, n)
        dwg.add(dwg.text(label, insert=(x, pad_top + chart_h + 14),
                         text_anchor="middle", fill="#666", font_size="9px",
                         font_family=font_family))

    # Axes
    dwg.add(dwg.line(start=(pad_left, pad_top), end=(pad_left, pad_top + chart_h),
                     stroke="#ddd", stroke_width=1))
    dwg.add(dwg.line(start=(pad_left, pad_top + chart_h),
                     end=(width - pad_right, pad_top + chart_h),
                     stroke="#ddd", stroke_width=1))

    return svg_string(dwg)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Badge generator (shields.io style)
# ─────────────────────────────────────────────────────────────────────────────

def badge(
    label: str,
    value: str,
    label_color: str = "#555",
    value_color: str = "#4c1",
    height: int = 20,
    font_size: int = 11,
) -> str:
    """
    Generate a flat badge SVG (shields.io style).
    Returns SVG string.
    """
    char_w = font_size * 0.62
    label_w = int(len(label) * char_w + 14)
    value_w = int(len(value) * char_w + 14)
    total_w = label_w + value_w

    dwg = make_drawing(total_w, height)
    font_family = "DejaVu Sans,Verdana,Geneva,sans-serif"
    fy = height / 2 + font_size * 0.36  # vertical center

    # Background rects
    dwg.add(dwg.rect((0, 0), (label_w, height), fill=label_color, rx=3, ry=3))
    dwg.add(dwg.rect((label_w - 3, 0), (value_w + 3, height), fill=value_color))
    # Right cap
    dwg.add(dwg.rect((total_w - 3, 0), (3, height), fill=value_color, rx=3, ry=3))

    # Text shadow + text
    for txt, x_center, fill, shadow in [
        (label, label_w / 2,           "#fff", True),
        (value, label_w + value_w / 2, "#fff", True),
    ]:
        if shadow:
            dwg.add(dwg.text(txt, insert=(x_center, fy + 1),
                             text_anchor="middle", fill="#0003",
                             font_size=f"{font_size}px", font_family=font_family))
        dwg.add(dwg.text(txt, insert=(x_center, fy),
                         text_anchor="middle", fill=fill,
                         font_size=f"{font_size}px", font_family=font_family))

    return svg_string(dwg)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Pie / donut chart
# ─────────────────────────────────────────────────────────────────────────────

def pie_chart(
    data: list[tuple[str, float]],
    title: str = "",
    size: int = 260,
    donut: bool = False,
    colors: list[str] | None = None,
    font_family: str = "Arial, sans-serif",
) -> str:
    """
    Generate a pie or donut chart SVG.
    data: [(label, value), ...] — values are proportional (need not sum to 1).
    """
    COLORS = colors or ["#4a90d9","#e74c3c","#2ecc71","#f39c12","#9b59b6",
                        "#1abc9c","#e67e22","#3498db","#e91e63","#607d8b"]

    cx = cy = size / 2
    r  = size / 2 * 0.75
    ri = r * 0.5  # inner radius for donut

    total = sum(v for _, v in data) or 1
    dwg   = make_drawing(size, size)
    dwg.add(dwg.rect((0, 0), (size, size), fill="#fff"))

    if title:
        dwg.add(dwg.text(title, insert=(cx, 14), text_anchor="middle",
                         fill="#333", font_size="12px", font_family=font_family,
                         font_weight="bold"))

    def polar(angle: float, radius: float) -> tuple[float, float]:
        return (cx + radius * math.cos(angle), cy + radius * math.sin(angle))

    start = -math.pi / 2
    for i, (label, value) in enumerate(data):
        sweep = (value / total) * 2 * math.pi
        end   = start + sweep
        color = COLORS[i % len(COLORS)]

        # Arc path
        x1, y1 = polar(start, r)
        x2, y2 = polar(end,   r)
        large  = 1 if sweep > math.pi else 0

        if donut:
            xi1, yi1 = polar(start, ri)
            xi2, yi2 = polar(end,   ri)
            d = (f"M {x1:.2f} {y1:.2f} "
                 f"A {r:.2f} {r:.2f} 0 {large} 1 {x2:.2f} {y2:.2f} "
                 f"L {xi2:.2f} {yi2:.2f} "
                 f"A {ri:.2f} {ri:.2f} 0 {large} 0 {xi1:.2f} {yi1:.2f} Z")
        else:
            d = (f"M {cx:.2f} {cy:.2f} "
                 f"L {x1:.2f} {y1:.2f} "
                 f"A {r:.2f} {r:.2f} 0 {large} 1 {x2:.2f} {y2:.2f} Z")

        dwg.add(dwg.path(d=d, fill=color, stroke="#fff", stroke_width=1.5))

        # Label at midpoint
        mid   = start + sweep / 2
        lx, ly = polar(mid, r * (0.75 if donut else 0.6))
        pct = value / total * 100
        if pct > 5:
            dwg.add(dwg.text(f"{pct:.0f}%", insert=(lx, ly), text_anchor="middle",
                             fill="#fff", font_size="9px", font_family=font_family,
                             font_weight="bold"))
        start = end

    return svg_string(dwg)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    from pathlib import Path

    print("=== Bar chart ===")
    svg = bar_chart(
        [("Jan", 120), ("Feb", 85), ("Mar", 210), ("Apr", 175), ("May", 95)],
        title="Monthly Revenue ($K)",
    )
    Path("/tmp/bar.svg").write_text(svg)
    print(f"  bar.svg: {len(svg)} chars")

    print("=== Line chart ===")
    svg = line_chart({
        "Revenue": [("Q1", 120), ("Q2", 145), ("Q3", 98), ("Q4", 210)],
        "Costs":   [("Q1",  80), ("Q2",  95), ("Q3", 75), ("Q4", 130)],
    }, title="Revenue vs Costs")
    Path("/tmp/line.svg").write_text(svg)
    print(f"  line.svg: {len(svg)} chars")

    print("=== Badges ===")
    Path("/tmp/badge_build.svg").write_text(badge("build", "passing"))
    Path("/tmp/badge_cov.svg").write_text(badge("coverage", "94%", value_color="#007ec6"))
    Path("/tmp/badge_ver.svg").write_text(badge("version", "1.2.0", value_color="#e05d44"))
    print("  badges written")

    print("=== Pie / donut ===")
    sales_data = [("Python", 42), ("JavaScript", 28), ("Go", 15), ("Rust", 10), ("Other", 5)]
    Path("/tmp/pie.svg").write_text(pie_chart(sales_data, title="Language Usage"))
    Path("/tmp/donut.svg").write_text(pie_chart(sales_data, title="Language Usage", donut=True))
    print("  pie.svg and donut.svg written")

For the matplotlib SVG backend alternative — matplotlib’s savefig("out.svg") produces SVG but includes heavy NumPy/matplotlib dependencies and generates verbose output optimized for viewing, not embedding; svgwrite produces minimal, hand-readable SVG with precise coordinate control, ideal for badges, UI icons, and embed-in-HTML charts that don’t require a full scientific plotting backend. For the cairosvg alternative — cairosvg converts SVG→PNG/PDF using the Cairo graphics library (requires libcairo OS dependency); svgwrite is the generator side — you write SVG with svgwrite and optionally convert to raster/PDF with cairosvg in a two-step pipeline, which keeps SVG generation dependency-free. The Claude Skills 360 bundle includes svgwrite skill sets covering make_drawing()/svg_string(), bar_chart() with value labels and axis, line_chart() multi-series with legend, badge() shield-style flat badge, pie_chart()/donut chart with percentage labels, dwg.rect/circle/line/polyline/polygon primitives, dwg.text/tspan typography, dwg.path() with d-string, dwg.g() groups and transforms, and linearGradient with stop colors. Start with the free tier to try SVG vector graphics code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free