Skip to content

Caret on an empty paragraph does not sit on the start of an RTL line #3955

Open

Description

@palmoni5

What happened?

In a right-to-left document, putting the caret on an empty paragraph does not
place it on the start of the line. It sits a space width inside the line.

Expected: the caret sits on the line start, which in RTL is the right edge of
the line box, the same place the first typed character will appear.
Actual: it sits ~4.4px to the left of it at Arial 16px, and the gap is
proportional to the font size, so it grows with zoom and with larger text.

Steps to reproduce

  1. Open a Hebrew (or any RTL) document, or set a paragraph to right-to-left.
  2. Type a line of text, press Enter.
  3. Look at the caret on the new empty line: it is not on the right edge where
    the text will start.

SuperDoc version

2.10.0 (@superdoc/docx-engine 0.9.0)

Browser

Chrome

Additional context

renderLine paints an empty line with a superdoc-empty-run span that carries
the line's PM positions, filled with  
(packages/layout-engine/painters/dom/src/runs/render-line.ts). A non-breaking
space has a real advance width, and the caret for an empty line is drawn at the
placeholder's left edge. On an LTR line that edge is the line start, so nothing
shows; on an RTL line the line starts at the right edge, so the caret lands one
space width inside it.

Measured on a Hebrew document, Arial 16px, empty paragraph after a line of text:

x
line box right edge (= line start, RTL) 697.7
.superdoc-empty-run box 693.3 → 697.7
caret 693.3

Two checks that the placeholder's advance is what moves the caret, both done
from the page with CSS only:

  • .superdoc-empty-run { letter-spacing: 20px } → the box widens to
    673.3 → 697.7 and the caret moves to 673.3. It tracks the box.
  • placeholder given zero advance → the caret lands on 697.7, the line start,
    and its height is unchanged.

A fix is up in #3950: fill the placeholder with a zero-width space, which keeps
the span's font metrics (and the caret's height) while contributing no advance.

Activity

  1. linear-code commented on Sep 1, 2026

    @linear-code
  2. palmoni5 commented on Sep 1, 2026

    @palmoni5
    Author

    By the way, you discovered the bug while developing the new add-on for otzaria that is based on your package, thank you very much!

  3. added
    status: investigatingThe report is being investigated or reproduced.
    and removed
    status: triageNew report awaiting initial assessment.
    on Sep 3, 2026
  4. self-assigned this
    on Sep 3, 2026
  5. added
    status: assessedInitial assessment is complete; work is not necessarily prioritized.
    status: triageNew report awaiting initial assessment.
    and removed
    status: investigatingThe report is being investigated or reproduced.
    status: assessedInitial assessment is complete; work is not necessarily prioritized.
    on Sep 3, 2026
  6. ghost added
    status: investigatingThe report is being investigated or reproduced.
    on Sep 4, 2026
  7. ghost removed
    status: triageNew report awaiting initial assessment.
    on Sep 4, 2026
  8. vivienne-sd commented on Sep 4, 2026

    @vivienne-sd
    Collaborator

    @palmoni5 thank you for this report as well! We're reviewing the PR you created and will follow up once code review is complete. We'll also confirm as soon as the fix is live in production.

  9. ghost added
    status: queuedEngineering work is queued; no delivery date is committed.
    on Sep 5, 2026
  10. ghost removed
    status: investigatingThe report is being investigated or reproduced.
    on Sep 5, 2026
  11. caio-pizzol commented on Sep 11, 2026

    @caio-pizzol
    Contributor

    @palmoni5 following up on this: we confirmed the caret offset is still happening in 2.13.0 and added fresh reproduction evidence to the engineering item linked to your PR. We’ll get back to you when we have an update.

    gh3955-empty-rtl-caret.webm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

status: queuedEngineering work is queued; no delivery date is committed.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions