ComfyUI Node

SVG Visual Normalize & Margin

Make Your SVG Actually Fill the Canvas, Not Just Sit in It

By coulterj·Created about a year ago·Updated about a year ago· 1
SVG Visual Normalize & Margin
    • STRING
    svg_string
    margin_percent0.000
    visual_bbox_padding_percent0.000
    center_offset_x_percent0.000
    center_offset_y_percent0.000
    output_width0
    output_height0
    render_scale_for_bbox1.0

    Every SVG workflow hits the same wall eventually: the vector comes back technically correct but framed like garbage. Tiny in a huge canvas, hugging the top-left corner, or with a giant empty margin you didn't ask for. This node fixes that - and it does it by looking, not by doing math on path coordinates.

    Here's the thing about geometric bounding boxes: they lie. Strokes overflow their path bounds, rotated text extends past its box, filters and blurs spill outside everything. For simple shapes a bounding-box calculation works. For the messy output of an LLM generating SVG or a raster-to-vector tracer, it drifts. SVGVisualBoundsNormalize (display name "SVG Visual Normalize & Margin") instead renders your SVG to a temporary raster and measures where the actual opaque pixels are. Then it scales that artwork to fill the canvas, centers it, and applies an optional margin.

    How it works

    Under the hood it's a short pipeline: CairoSVG rasterizes your svg_string to a temp PNG, Pillow's getbbox() finds the painted-pixel region, and that region is mapped back into SVG units. The node then wraps every child element in one <g> with a single translate(...) scale(...) transform and rewrites the canvas as viewBox="0 0 w h". One transform, no per-element surgery - which means it survives round-tripping through other SVG nodes in your graph. It's also MIT-licensed and one file, so it's easy to read if you want to see exactly what it's doing.

    The inputs that matter

    • svg_string - paste or wire in your SVG code. That's the whole job.
    • margin_percent - margin as a fraction of canvas, applied after fill and center. 0.05 = a 5% border. Capped at 0.499, because past half you're just drawing a rectangle.
    • visual_bbox_padding_percent - expands the detected visual bounds as a safety buffer against clipping. Start at 0, bump it only if you see cut-off edges.
    • center_offset_x_percent / center_offset_y_percent - manual nudge when the visual centering doesn't match your eye. Positive X shifts right, positive Y shifts down.
    • output_width / output_height - set the output canvas. Zero means "keep the other side proportional"; both zero means original dimensions. Max 8192.
    • render_scale_for_bbox (advanced) - the internal rasterization scale. Higher = more accurate bounds on very detailed SVGs, slower to run. Leave at 1.0 unless you're chasing clipped antialiasing.

    The single output is a STRING: your normalized SVG. That feeds into whatever renders or previews SVG in your graph.

    Installing it

    It's a one-node, one-commit pack, so install is the standard drill:

    cd ComfyUI/custom_nodes
    git clone https://github.com/coulterj/comfyui-svg-visual-normalize
    pip install -r requirements.txt   # in your ComfyUI environment
    

    It may also show up under "SVG Visual Normalize" in ComfyUI Manager. The real gotcha is the system dependency: CairoSVG needs the Cairo C library, and that's not a pip thing. On Debian/Ubuntu: sudo apt-get install libcairo2-dev libgdk-pixbuf2.0-dev python3-dev. On macOS: brew install cairo libffi pkg-config. Windows is the painful one - you typically need a GTK+ bundle. If the node spits a red [ERROR] The required 'CairoSVG' library is not installed line at startup, that's the missing piece, not your code.

    Where people get burned

    The node fails safe: if it can't parse your SVG, can't find a visual bbox (e.g. fully transparent input), or Cairo is missing, it returns your string unchanged rather than corrupting it. You'll also see its debug --- SVGVisualBoundsNormalize: Process Start --- output in the ComfyUI console - read that when something looks off; it logs the detected bbox and final transform. One thing to know: output is re-serialized through lxml, so your whitespace and comments get normalized away, and exotic namespaces or <foreignObject> content may not survive the trip. For normal text-to-vector and traced artwork, it just works - and it's the difference between an SVG that needs manual fixing and one that's ready to drop into your pipeline.

    Categorysvg

    Inputs (8)

    NameTypeDefaultDescription
    svg_stringSTRING
    margin_percentFLOAT0.0000–0.499
    visual_bbox_padding_percentFLOAT0.0000–0.1
    center_offset_x_percentFLOAT0.000-0.5–0.5
    center_offset_y_percentFLOAT0.000-0.5–0.5
    output_widthINT00–8192
    output_heightINT00–8192
    render_scale_for_bboxFLOAT1.00.5–4

    Outputs (1)

    NameTypeDescription
    STRINGSTRING