annotate_text_path_layout — ANNOTATE paper op

• Data kinds: text → table

• Call: import fullseye as fs; fs.ledger.annotate_text_path_layout(text, path, font_size=13, font_path=None, spacing=1.0, start=0.0, anchor='start', offset=0.0, upright=False, line_spacing=1.15) (to call the implementation directly, import annotate; annotate.annotate_text_path_layout(text, path, font_size=13, font_path=None, spacing=1.0, start=0.0, anchor='start', offset=0.0, upright=False, line_spacing=1.15); from the registry, opsannotate.get("annotate_text_path_layout"))

Usage

Returns a table (dict): the position and slope of each glyph along a polyline (set by arc length).

The centre of character i is at arc length s_i = s0 + Σ_{j<i} a_j*spacing + a_i/2, and its tilt is the tangent angle of the segment at that position (screen coordinates, degrees). A string longer than the path is a ValueError.

How the position is determined (added 2026-09-08; the default matches the earlier behaviour):

• anchor -- alignment along the path. "start" (default, as before) / "center" (align to the middle of the path) / "end" (align to the end). start acts as an amount to shift further from the position set by the anchor.

• offset -- a shift perpendicular to the path [px]. Positive is right of the direction of travel (screen coordinates; since y points down, for text running left→right this is "down"). For placing text beside a line without touching it.

• \n makes a line break. The 2nd and later lines shift by the line height times line_spacing in the same direction as offset (right of the direction of travel). ★With this 1 rule, horizontal text moves "down" and vertical text moves "left", both as typesetting requires (vertical text travels downward, so its right is the left of the screen).

• upright -- keeps glyphs upright instead of rotating them to the tangent angle. If the path is mainly vertical (|dy| > |dx| from start→end), the advance uses the glyph height instead of the glyph width, so this becomes vertical writing. Only the glyphs in VERTICAL_ROTATED_CHARS are rotated 90 degrees.

Punctuation (、。,.) is shifted to the upper right as vertical typesetting convention requires (VERTICAL_SHIFTED_CHARS; the shift is _VPUNCT_SHIFT times the advance). What Windows @-prefixed fonts did on the font side is done here by synthesis -- Pillow opens fonts by file path, so it cannot reach the @ face, and HarfBuzz's direction="ttb" also gives different results depending on whether Raqm is present, so we took the approach that is consistent across platforms.

★Not implemented for vertical writing (no silent approximation): position correction for small kana, tate-chu-yoko (horizontal-in-vertical), and line-end kinsoku. This is a feature for short notes, not body-text typesetting.

Returns

dict

{"chars": [{"char","s","xy","angle_deg","width","advance","line"}], "length": path length, "used": **sum of advances** of the longest line (as before, Σ advance*spacing), "span": arc length the longest line **actually occupies** (excluding the advance after the last glyph -- anchors align on this one), "lines": number of lines, "line_height": line pitch [px], "vertical": whether vertical}.

Raises

ValueError

Empty text, a path with fewer than 2 points or zero length, non-finite values, a line longer than the path,

or when the combination of anchor and start pushes a line off the path.

Detailed usage guide

• figure_annotation family guide

Background guides (the physics and conventions behind this op)

• dataset_conventions — Training-dataset conventions — COCO / YOLO / VOC and pitfalls in visual inspection

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

• The canonical algorithm (author, year) and its uses are named in the family usage guide above.

Runnable examples (verified samples that actually call this op)

• annotate_paper_tour — py -3.11 examples/annotate_paper_tour.py

Ops the type connects to (they accept table as input)

—

Same category (paper)

annotate_leader_layout · annotate_leader · annotate_markers · annotate_legend · annotate_dimension_layout · annotate_dimension · annotate_angle_layout · annotate_angle


*Provenance: annotate.py — ANNOTATE operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.