Nodes/draw_tools/Text Box Auto Wrap
ComfyUI Node

Text Box Auto Wrap

When you need words on a picture, ComfyUI has no text node — this one fixes it

By Ky11le·Created about a year ago·Updated about a year ago· 1
Text Box Auto Wrap
    • IMAGE
    • MASK
    text
    font_path
    box_width512
    box_height256
    font_size48
    letter_spacing0
    base_chars_per_line10
    max_lines3
    min_hscale0.60
    font_hex#FFFFFFFF
    stroke_width0
    stroke_hex#000000FF
    enable_small_capsfalse
    small_caps_scale0.80
    align_rightfalse

    Base ComfyUI ships with a lot, but it won't draw a single letter onto an image for you. There's no built-in text renderer - your prompt feeds the CLIP encoder, it doesn't become pixels. So the day you need a subtitle, a card text box, or a watermark, you're browsing third-party text nodes. TextBoxAutoWrap is the one inside Ky11le's small draw_tools pack, and it earns the "auto": it wraps text to a fixed box, squeezes it to fit, and hands you both the rendered image and a matching mask for compositing.

    What it actually is

    draw_tools is a three-node utility pack - Detect Inner Box, Paste Into Frame, and this one - under the DrawTools category. No models, no checkpoints, no API key: it's pure Pillow drawing at CPU speed, milliseconds per run. One honest warning: the README is in Chinese and only documents the other two nodes. TextBoxAutoWrap is fully in the code and registered in __init__.py, but the README never mentions it. The docs rotted behind the code; treat them as stale.

    The ancestry leaks out of the knobs: the info_schema lists the Python module as custom_nodes.ygo_tools - clearly lifted from a card-text / visual-novel tool. Hence stroke outlines, right-alignment, and digits rendered smaller than letters. Game-card conventions, and they make this the node to reach for when text should look like it belongs in a UI.

    How the wrapping works

    The interesting part is the auto-fit. The node measures the real pixel width of every character in your chosen font (Pillow's getlength), so proportional fonts wrap correctly - no "10 characters per line" guesswork. It wraps onto a virtual canvas box_width / hscale wide, then LANCZOS-resizes back to box_width × box_height and sharpens. That resize is the auto-shrink: more text → wider canvas → smaller horizontal scale.

    The gotcha hiding inside: the scale is horizontal only. Height never changes, so as text grows, letters get narrower rather than uniformly smaller - condensed type, great for a card box, weird for paragraphs. That's what min_hscale (default 0.6) caps; squished text means the mechanism is working, not bugging out.

    The inputs that matter

    info_schema lists fourteen inputs; you'll actually touch about six:

    • text - the words (multiline widget). It strips your newlines and re-wraps everything, so you can't force a line break.
    • font_path - a plain text field pointing at a .ttf/.otf on disk. More below; this is the trap.
    • box_width / box_height - the pixel size of the output (and the box text wraps into).
    • font_size - point size; bigger text pushes the auto-shrink harder.
    • max_lines and base_chars_per_line - together they decide how much text "fits" before the squeeze kicks in; the defaults of 3 lines × 10 chars are basically a card text box.
    • font_hex / stroke_hex - colors, but in RRGGBBAA order with alpha at the end (#FFFFFFFF is opaque white). A plain #FFFFFF silently gets FF appended, and a malformed string just falls back to white - no error, which is its own kind of mean.

    Beyond those: stroke_width/stroke_hex outline the text, enable_small_caps + small_caps_scale render digits at ~80% height (despite the name, only numerals shrink), letter_spacing is self-explanatory, and align_right flips the alignment.

    Outputs and wiring

    Two outputs: IMAGE and MASK. The IMAGE is RGBA - real alpha in the 4th channel, unusual for ComfyUI tensors - so if a downstream node expects plain RGB, use the MASK instead. It's the same text drawn white-on-black, stroke included, and composites cleanly via Image Composite Masked or similar. One quirk: the MASK comes out with an extra leading dimension [1,1,H,W]; if a node rejects the shape, squeeze it to [1,H,W].

    Installing it

    Two ways, both boring in a good way. Via ComfyUI Manager → Custom Nodes Manager, search "draw_tools", install, restart. Or by hand:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Ky11le/draw_tools
    cd draw_tools
    pip install -r requirements.txt
    

    Restart ComfyUI and the nodes appear under DrawTools. No model files to fetch. The requirements.txt lists opencv-python, torch, and numpy, but this node itself only touches Pillow - which ComfyUI already ships - so it's one of the friendlier installs around.

    The one thing that bites everyone

    font_path is not a file-browser widget - it's a raw string, so you type the whole path yourself. Windows: C:\Windows\Fonts\arial.ttf. Linux: /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf or wherever your fonts live. Get it wrong and the first run dies with a "cannot open resource" error. There's no built-in font list - that, plus the RRGGBBAA hex format, is basically all the confusion this node causes.

    Second: keep stroke_width small. The stroke re-renders each glyph at every offset in a square around it - (2w+1)² draws per character, so 5 on a long sentence gets slow. 1–2 looks right on card text and stays instant.

    Bottom line: it's a niche node, but for game-card text, subtitles, or watermark-style overlays it does one job well - and it's the only node in its pack that renders text at all. The hand-typed font path and RRGGBBAA hex are the price of admission; know those and it just works.

    CategoryDrawTools

    Inputs (15)

    NameTypeDefaultDescription
    textSTRING
    font_pathSTRING
    box_widthINT51264–4096
    box_heightINT25664–4096
    font_sizeINT484–512
    letter_spacingINT0-20–100
    base_chars_per_lineINT101–100
    max_linesINT31–10
    min_hscaleFLOAT0.600.3–1
    font_hexSTRING#FFFFFFFF
    stroke_widthINT00–20
    stroke_hexSTRING#000000FF
    enable_small_capsBOOLEANfalse
    small_caps_scaleFLOAT0.800.5–1
    align_rightBOOLEANfalse

    Outputs (2)

    NameTypeDescription
    IMAGEIMAGE
    MASKMASK