What happens
In a section that carries w:sectPr/w:bidi, columns fill left to right. The first paragraph of a two-column Hebrew section lands in the left column and overflow spills into the right one.
Word does the opposite: in an RTL section the first column is the rightmost.
Why it is more than a cosmetic flip
Selection follows the fill order, so the mirrored layout breaks dragging in a way that looks like a selection bug:
- Press inside the column a Hebrew reader starts from — the right one.
- Drag down a few lines. So far so good.
- Keep dragging into the left column.
Everything already highlighted below the anchor disappears, and the left column lights up instead. Measured on a 44-paragraph document: 8 highlighted lines in the right column drop to 5 the moment the pointer crosses the gutter. The engine is faithful to the order it built; the order is what is mirrored.
Two more consequences fall out of the same axis:
- A point inside the right column but below its last line is not clamped to that column — the head lands in the left column at the same y.
Shift+ArrowDown does not cross the column boundary at all: from a verified caret on line 23 it ends up selecting 04–23, i.e. the head jumped above the anchor to the top of the first column.
So there is no keyboard workaround either. Selecting inside a single column works, and Ctrl+A works.
Reproduction
Any Hebrew document with two columns. Minimal path:
- New document, section with
<w:bidi/> (Word writes this for a Hebrew document).
- Layout → Columns → Two.
- Type or paste enough text to overflow into the second column.
- Observe that paragraph 1 is on the left.
The exported .docx is correct throughout — <w:cols w:num="2" w:equalWidth="1"/> and <w:bidi/> both round-trip, and the file opens correctly in Word. The damage is in rendering and interaction only.
Where it comes from
SectionDirectionContext in packages/layout-engine/contracts/src/direction-context.ts already documents pageDirection (from w:sectPr/w:bidi) as governing "section-level chrome only (page numbers, columns, gutters)". But ColumnGeometry measures x "from the content-area left edge", and no function in the column geometry takes a direction. The axis is documented and never consumed.
A control run confirms it: the same document in English, with no w:bidi at all, produces byte-identical geometry — so direction is not read on the column axis in either case.
Environment
superdoc@2.10.0 (@superdoc/docx-engine@0.9.0). Also checked 2.11.0-next.9: layout-engine/contracts/src/column-layout.d.ts is byte-identical there, and no column-geometry signature takes a direction, so this is still open on next.
I have a fix for the geometry side and will open a PR referencing this issue.
What happens
In a section that carries
w:sectPr/w:bidi, columns fill left to right. The first paragraph of a two-column Hebrew section lands in the left column and overflow spills into the right one.Word does the opposite: in an RTL section the first column is the rightmost.
Why it is more than a cosmetic flip
Selection follows the fill order, so the mirrored layout breaks dragging in a way that looks like a selection bug:
Everything already highlighted below the anchor disappears, and the left column lights up instead. Measured on a 44-paragraph document: 8 highlighted lines in the right column drop to 5 the moment the pointer crosses the gutter. The engine is faithful to the order it built; the order is what is mirrored.
Two more consequences fall out of the same axis:
Shift+ArrowDowndoes not cross the column boundary at all: from a verified caret on line 23 it ends up selecting 04–23, i.e. the head jumped above the anchor to the top of the first column.So there is no keyboard workaround either. Selecting inside a single column works, and
Ctrl+Aworks.Reproduction
Any Hebrew document with two columns. Minimal path:
<w:bidi/>(Word writes this for a Hebrew document).The exported
.docxis correct throughout —<w:cols w:num="2" w:equalWidth="1"/>and<w:bidi/>both round-trip, and the file opens correctly in Word. The damage is in rendering and interaction only.Where it comes from
SectionDirectionContextinpackages/layout-engine/contracts/src/direction-context.tsalready documentspageDirection(fromw:sectPr/w:bidi) as governing "section-level chrome only (page numbers, columns, gutters)". ButColumnGeometrymeasuresx"from the content-area left edge", and no function in the column geometry takes a direction. The axis is documented and never consumed.A control run confirms it: the same document in English, with no
w:bidiat all, produces byte-identical geometry — so direction is not read on the column axis in either case.Environment
superdoc@2.10.0(@superdoc/docx-engine@0.9.0). Also checked2.11.0-next.9:layout-engine/contracts/src/column-layout.d.tsis byte-identical there, and no column-geometry signature takes a direction, so this is still open onnext.I have a fix for the geometry side and will open a PR referencing this issue.