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"))
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.
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}.
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.
• figure_annotation family guide
• dataset_conventions — Training-dataset conventions — COCO / YOLO / VOC and pitfalls in visual inspection
• 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.
• annotate_paper_tour — py -3.11 examples/annotate_paper_tour.py
table as input)—
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.