Text & Typography

Clear typography and precise coordinate alignment are essential for readable technical diagrams. Drawlib provides horizontal text, vertical East Asian (CJK) text, arbitrary rotation angles, multi-line formatting, padded background badges, and seamless embedded shape labels.


1. Overview of Text Rendering

text_1
Overview of Text Alignment, Rotation, Badges, and Vertical Typography
Source Code
from drawlib.canvas import save, setup
from drawlib.lines import line
from drawlib.shapes import circle, rectangle
from drawlib.styles import Colors, Styles
from drawlib.text import text, text_vertical

setup(width=124, height=54)

# 1. Anchor alignment along x=28 guide line (left column)
line((28, 6), (28, 48), style=Styles.MutedDashed)
for y, align, label in [(42, "left", "halign='left'"), (27, "center", "halign='center'"), (12, "right", "halign='right'")]:
    circle((28, y), radius=1.0, style=Styles.DangerFlat)
    text((28, y + 2.2), label, style=Styles.DarkBold.patch(text_size=10.5, halign=align, valign="bottom"))

# 2. Rotated text & background badge over a connector line (middle column)
line((56, 12), (94, 42), arrow_head="->", style=Styles.DarkBold)
text((66, 33), "Rotated 39°", style=Styles.DarkBold.patch(text_size=10.5, angle=39))

badge_style = Styles.DarkBold.patch(
    text_size=10.5,
    text_bg_fill_color=Colors.Primary1,
    text_bg_line_color=Colors.Primary4,
    text_bg_line_width=1.2,
)
text((78, 28.5), "gRPC :8443", style=badge_style)

# 3. Vertical CJK typography (right column)
rectangle((111, 27), width=15, height=40, style=Styles.Neutral.patch(shape_r=2))
text_vertical((111, 27), "縦書き日本語", style=Styles.DarkBold.patch(text_size=11.5))

save()

2. Core Text Functions (drawlib.text)

All standalone text primitives are imported from drawlib.text:

from drawlib.text import text, text_vertical

2.1. Horizontal & Rotated Text (text)

text(
    xy: tuple[float, float],
    text: str,
    *,
    style: Style | None = None,
) -> None
  • xy: Coordinate anchor point (x, y) in canvas units.
  • text: String content to render. Supports \n newline characters for multi-line paragraphs.
  • style: Optional Style instance (defaults to Styles.Dark / active theme default). Controls font family, point size, color, alignment (halign, valign), rotation (angle), coordinate offsets (xy_shift, xy_abs_shift), and background framing (text_bg_*).

2.2. Vertical CJK Text (text_vertical)

Renders East Asian characters (Japanese, Chinese) in traditional top-to-bottom vertical layout by stacking glyphs along a vertical centerline (halign="center"):

from drawlib.canvas import save, setup
from drawlib.shapes import rectangle
from drawlib.styles import Styles
from drawlib.text import text_vertical

setup(width=80, height=55)

rectangle((25, 27.5), width=18, height=42, style=Styles.Neutral.patch(shape_r=2))
text_vertical((25, 27.5), "設計仕様書", style=Styles.DarkBold.patch(text_size=14))

rectangle((55, 27.5), width=18, height=42, style=Styles.PrimaryFlat.patch(shape_r=2))
text_vertical((55, 27.5), "基盤構成図", style=Styles.WhiteBold.patch(text_size=14))

save()
text_2
Vertical Japanese Typography with text_vertical()

3. Typography & Background Attributes on Style

Rather than passing separate TextStyle objects or scattered keyword arguments, Drawlib consolidates all typography, alignment, transform, and background badge attributes onto the immutable Style model (customizable via .patch()):

Attribute Type Default Description
text_color Color | tuple | str Theme default Glyph fill color (e.g. Colors.Gray7, Colors.White, "#1a73e8").
text_size float | str 16.0 Font size in points (e.g. 10, 14, 20) or "small", "medium", "large".
text_font FontBase | FontFile Font.SANSSERIF_REGULAR Font family and weight token from drawlib.fonts.
halign "left" | "center" | "right" "center" Horizontal alignment relative to anchor x.
valign "bottom" | "center" | "top" "center" Vertical alignment relative to anchor y.
text_line_spacing float 1.2 Vertical line height multiplier for multi-line strings.
angle float 0.0 Counter-clockwise rotation angle in degrees around anchor xy.
text_flip bool False Rotates embedded shape text by 180° to prevent upside-down labels.
xy_shift tuple[float, float] None Relative (dx, dy) offset rotated along with angle.
xy_abs_shift tuple[float, float] None Absolute (dx, dy) canvas offset independent of rotation.
text_bg_fill_color Color | tuple | str None Padded background badge fill color behind the text block.
text_bg_line_color Color | tuple | str None Background badge border stroke color.
text_bg_line_width float 1.0 Background badge border stroke width (0 for borderless).
text_bg_line_style "solid" | "dashed" | "dotted" | "dashdot" "solid" Background badge border stroke pattern.

4. Coordinate Anchor Alignment (halign & valign)

By default, text() centers the text bounding box horizontally (halign="center") and vertically (valign="center") on xy. Adjusting halign and valign pins specific edges of the text box to xy:

  • halign="left": Text starts at x and extends to the right.
  • halign="center" (default): Text is horizontally centered on x.
  • halign="right": Text ends at x and extends to the left.
  • valign="top": Top edge of the text hangs downward from y.
  • valign="center" (default): Text is vertically centered on y.
  • valign="bottom": Bottom baseline of the text sits above y.
from drawlib.canvas import save, setup
from drawlib.lines import line
from drawlib.shapes import circle
from drawlib.styles import Styles
from drawlib.text import text

setup(width=128, height=56)

# Crosshair guide lines
for y in [42, 28, 14]:
    line((8, y), (120, y), style=Styles.MutedDashed)
for x in [20, 64, 108]:
    line((x, 6), (x, 50), style=Styles.MutedDashed)

# Left-aligned column (x = 20)
for y, v_mode in [(42, "bottom"), (28, "center"), (14, "top")]:
    circle((20, y), radius=0.9, style=Styles.DangerFlat)
    text((20, y), f"left + {v_mode}", style=Styles.DarkBold.patch(text_size=10.5, halign="left", valign=v_mode))

# Center-aligned column (x = 64)
for y, v_mode in [(42, "bottom"), (28, "center"), (14, "top")]:
    circle((64, y), radius=0.9, style=Styles.PrimaryFlat)
    text((64, y), f"center + {v_mode}", style=Styles.DarkBold.patch(text_size=10.5, halign="center", valign=v_mode))

# Right-aligned column (x = 108)
for y, v_mode in [(42, "bottom"), (28, "center"), (14, "top")]:
    circle((108, y), radius=0.9, style=Styles.DangerFlat)
    text((108, y), f"right + {v_mode}", style=Styles.DarkBold.patch(text_size=10.5, halign="right", valign=v_mode))

save()
text_3
Horizontal and Vertical Anchor Alignment Combinations

5. Multi-Line Text & Line Spacing (text_line_spacing)

Insert \n to break strings into multiple lines. Use text_line_spacing on Style to control vertical rhythm (default multiplier is 1.2):

from drawlib.canvas import save, setup
from drawlib.shapes import rectangle
from drawlib.styles import Styles

setup(width=120, height=50)

sample = "API Gateway\nRate Limiting\nJWT Validation"

# Default line spacing (1.2x)
rectangle(
    (34, 25),
    width=44,
    height=34,
    style=Styles.Neutral.patch(shape_r=2),
    text=sample,
    text_style=Styles.DarkBold.patch(text_size=11),
)

# Relaxed line spacing (1.7x)
rectangle(
    (86, 25),
    width=44,
    height=34,
    style=Styles.PrimaryFlat.patch(shape_r=2),
    text=sample,
    text_style=Styles.WhiteBold.patch(text_size=11, text_line_spacing=1.7),
)

save()
text_4
Multi-Line Text Formatting and Line Spacing Control

6. Rotated Labels (angle) & Background Badges (text_bg_*)

  • Rotation (angle): Rotates the text counter-clockwise in degrees around xy.
  • Background Badges (text_bg_fill_color, text_bg_line_color, text_bg_line_width, text_bg_line_style): Draws a padded rectangular pill behind the text—ideal for placing protocol or latency callouts directly over connector lines without the line cutting through glyphs.
from drawlib.canvas import save, setup
from drawlib.lines import line
from drawlib.shapes import rectangle
from drawlib.styles import Colors, Styles
from drawlib.text import text

setup(width=124, height=48)

# Rotated vertical Y-axis title (angle=90)
text((9, 24), "Throughput (req/s)", style=Styles.DarkBold.patch(text_size=11.0, angle=90))

# Service nodes
rectangle((34, 24), width=28, height=20, style=Styles.PrimaryFlat.patch(shape_r=2), text="Ingress", text_style=Styles.WhiteBold.patch(text_size=12.0))
rectangle((100, 24), width=28, height=20, style=Styles.SecondaryNeutral.patch(shape_r=2), text="Backend", text_style=Styles.DarkBold.patch(text_size=12.0))

# Connector with overlaid background badge
line((48, 24), (86, 24), arrow_head="->", style=Styles.DarkBold)

badge = Styles.DarkBold.patch(
    text_size=10.5,
    text_bg_fill_color=Colors.White,
    text_bg_line_color=Colors.Gray4,
    text_bg_line_width=1.2,
    text_bg_line_style="solid",
)
text((67, 24), "mTLS / HTTP2", style=badge)

save()
text_5
Rotated Axis Labels and Padded Background Badges

7. Embedded Shape Text (text= & text_style=)

Every shape primitive in drawlib.shapes (rectangle, circle, cylinder, rhombus, chevron, etc.) accepts text and text_style directly, eliminating the need to calculate center coordinates for a separate text() call:

  • Automatic Luminance Contrast: When text_style is omitted, Drawlib inspects the shape's fill luminance and automatically selects high-contrast dark or white text.
  • Offsetting Embedded Labels (xy_shift / xy_abs_shift): Patch xy_shift=(dx, dy) on text_style to nudge the label relative to the shape's center (for example, pushing a title toward the top of a container card).
  • Independent Label Angle (angle / text_flip): Embedded text rotates with the shape by default; set angle or text_flip=True on text_style to override the label orientation.
from drawlib.canvas import save, setup
from drawlib.shapes import rectangle
from drawlib.styles import Styles

setup(width=126, height=48)

# 1. Centered embedded text with explicit WhiteBold style
rectangle(
    (23, 24),
    width=32,
    height=24,
    style=Styles.PrimaryFlat.patch(shape_r=2),
    text="Core Router\n10.0.0.1",
    text_style=Styles.WhiteBold.patch(text_size=11.0),
)

# 2. Shifted embedded header using xy_shift=(0, 8)
rectangle(
    (65, 24),
    width=36,
    height=28,
    style=Styles.Neutral.patch(shape_r=2),
    text="Worker Pool (Top)",
    text_style=Styles.DarkBold.patch(text_size=10.5, xy_shift=(0, 8)),
)

# 3. Rotated shape with horizontal text override (angle=0 on text_style)
rectangle(
    (106, 24),
    width=28,
    height=18,
    style=Styles.SecondaryNeutral.patch(shape_r=2, angle=25),
    text="Horizontal\nOverride",
    text_style=Styles.DarkBold.patch(text_size=10.5, angle=0),
)

save()
text_6
Embedded Shape Text with Automatic Contrast, xy_shift, and Angle Override