Skip to content

Add automatic Hebrew/Arabic RTL text and paragraph alignment - #300

Merged
bflatastic merged 3 commits into
storytold:mainfrom
daniel-efrat:codex/automatic-rtl-text
Oct 7, 2026
Merged

bflatastic merged 3 commits into
storytold:mainfrom
daniel-efrat:codex/automatic-rtl-text

Conversation

@daniel-efrat

Copy link
Copy Markdown
Contributor

What and why

Add automatic Hebrew/Arabic RTL text support and paragraph alignment. New text detects each paragraph’s base direction from its first strong Unicode character, shapes Arabic and Hebrew in RTL runs, and lays out mixed text and numbers in visual order while preserving logical text storage.

New paragraphs automatically align right for RTL and left for LTR, including point, area, and path text. The Paragraph panel exposes Automatic alignment; explicit alignment choices remain available, and existing documents retain their saved alignment. Caret movement, hit testing, selections, editing, and undo follow the updated layout.

Bidirectional SVG text is outlined with a warning to preserve its appearance; native documents retain editable text. Rendering requires an installed font covering the script. Explicit direction overrides, dual caret affinity at mixed-direction boundaries, and RTL tab refinements remain follow-up work. No fonts or assets are added.

How it was tested

  • cargo xtask ci passes all seven gates, including workspace tests and WASM.
  • Regression coverage for RTL shaping, mixed scripts/numbers, combining marks, wrapping, caret navigation, automatic per-paragraph alignment, explicit overrides, serialization, editing/undo, SVG export, and the Paragraph panel.
  • Inspected CLI-rendered Hebrew, Arabic, and mixed-text proofs and the native app’s automatic right-alignment state.
  • Verified native save/reopen preserves the rendered proof.

Checklist

  • cargo xtask ci passes (fmt, clippy, tests, assets, brands, layers, wasm).
  • No panics in shipped code; errors follow the project’s fallible handling conventions.
  • Clean-room: nothing copied from Adobe products or GPL/AGPL code.
  • Every new asset has a row in ASSETS.md (no new assets).

daniel-efrat and others added 3 commits October 8, 2026 05:24
…y its direction

- Unicode bidi (unicode-bidi, unicode-script, unicode-general-category):
  each paragraph's direction from its first strong character, Hebrew and
  Arabic shaped right to left, embedded Latin and numbers keep theirs,
  lines wrapped in logical order and reordered per line by whole
  clusters; source text, undo and style ranges stay logical.
- Caret, hit testing, selection and the arrow keys follow the visual
  order.
- New text aligns automatically: right-to-left paragraphs right, others
  left (Justify::Auto, text.setStyle {justify: "auto"}); the Paragraph
  panel highlights the resolved alignment and has an Automatic alignment
  toggle. Explicit alignments, and older documents', are kept.
- SVG export outlines bidirectional text (with a warning) until it can
  be written as directional live text.
…idi only where needed (review of storytold#300)

Illustrator sets a paragraph's direction (Left-to-Right / Right-to-Left
Paragraph Direction in the Paragraph panel, with the Middle Eastern
options) and keeps its alignment buttons physical; it has no automatic
alignment toggle. So:

- ParaStyle.direction (None: from each paragraph's first strong
  character, as before; written only when set), the Paragraph panel's two
  direction buttons with Show Indic Options (original icons), and
  text.setFormat {direction: "auto"|"leftToRight"|"rightToLeft"}. The
  layout uses it as the base direction.
- The Automatic alignment check box is gone. Justify::Auto stays as the
  start of the paragraph's direction, set on new type only (text.create,
  the Type tools, with the mojikumi default in one new_type_para()):
  TextObject::point is Left again, so importers (PDF, SVG, DXF, EMF) keep
  their own alignment. The alignment buttons show what Auto resolves to.
- Bidi only where there is something right to left: plain paragraphs skip
  the algorithm (no per-layout allocation), it runs once per paragraph
  (shaping takes the paragraph's levels instead of recomputing them; path
  type computed it four times), and the shaper keeps Han, Hiragana,
  Katakana and Bopomofo in one segment (splitting at every change of
  script cost Japanese text a shaper call per kana-kanji switch).
- Vertical type keeps its logical order, so its glyphs and lines are not
  marked right to left (the caret no longer runs backwards down a column).
- PDF import puts Hebrew and Arabic, which PDFs (and VectorCraft's own
  export) draw in visual order, back in logical order with the direction
  that shows it as drawn; with bidi layout it came back mirrored.
- SVG import pins the direction SVG implies (left to right unless
  direction: rtl); SVG export also outlines right-to-left paragraphs.
- Docs (development.md), ROADMAP (folded into the Type entry, Type core
  row) and README updated; catalog rows for the new labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…told#300)

New type's Auto alignment is saved as the alignment it has in the
paragraph direction (Right for right to left, else Left) plus an
optional `justify_auto` flag, so builds without Auto parse the file
and lay it out the same; this build reads Auto back. Paragraph styles
keep Auto as one `justify` attribute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bflatastic
bflatastic force-pushed the codex/automatic-rtl-text branch from ddc1ba8 to 3121bba Compare October 7, 2026 22:31
@bflatastic
bflatastic merged commit 80307f1 into storytold:main Oct 7, 2026
@bflatastic

Copy link
Copy Markdown
Member

Thank you so much @daniel-efrat, merged! Hebrew and Arabic now shape and lay out right to left, with mixed scripts and numbers in visual order and caret, selection and editing following them. It's a big step for VectorCraft. If you're not on our Discord yet, come say hi: https://discord.gg/artcraft

I rebased it on main (resolving it against the Japanese composition work that landed at the same time) and added two commits on top to match Illustrator's model and keep older builds working:

  • Paragraph Direction instead of an "Automatic" alignment toggle: Illustrator has Left-to-Right / Right-to-Left Paragraph Direction (shown with the Middle Eastern / Indic options), and its alignment buttons stay physical.
    • Each text object has a Paragraph Direction. When it isn't set, the direction comes from each paragraph's first strong character, as in your PR.
    • The Paragraph panel gets the two direction buttons; the icons are new, original and listed in ASSETS.md.
    • text.setFormat takes direction: "auto" | "leftToRight" | "rightToLeft".
    • New type still aligns to the start of its direction. Imported text (PDF, SVG, DXF, EMF) keeps Left as before.
  • Older builds: files saved with that alignment write a value v0.4.0 knows (Right or Left) plus an optional justify_auto flag. Without this, every new file containing text would fail to open in released builds.
  • Speed:
    • Plain left-to-right text skips the bidi algorithm entirely.
    • Bidi now runs once per paragraph (it ran in shaping and up to 4× for path type).
    • Han and kana are no longer split into separate shaper calls.
  • Fixes:
    • Hebrew and Arabic imported from PDF, including our own exports, came back mirrored. Import now restores logical order.
    • Vertical type is no longer marked right to left, so its caret no longer runs backwards.
    • Paragraph Styles with an alignment now replace an Auto one.
  • Docs: ROADMAP, README and docs/development.md.

Checked live: Hebrew and Arabic are right-aligned at the anchor, with punctuation, digits and parentheses placed correctly and Arabic joined. Over MCP, text.setFormat {direction: "rightToLeft"} works, and bad values return clean errors. Still to come: Character Direction, digit types, kashidas, the Middle Eastern composers, and editable bidi text in SVG export (it's outlined with a warning for now).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants