Skip to content

fix: anchor table overlaps text#1995

Open
VladaHarbour wants to merge 1 commit intomainfrom
sd-1679_anchored-table
Open

fix: anchor table overlaps text#1995
VladaHarbour wants to merge 1 commit intomainfrom
sd-1679_anchored-table

Conversation

@VladaHarbour
Copy link
Contributor

Tables that have w:tblpPr devined are treated as anchor tables and can overlap the text
Before:
Screenshot 2026-02-11 at 14 46 56
After:
Screenshot 2026-02-11 at 14 46 33

@linear
Copy link

linear bot commented Feb 11, 2026

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d55da94c54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1903 to +1904
if (tablesForPara) {
const state = paginator.ensurePage();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pre-register page/margin anchored tables before laying paragraphs

With getTableAnchorBaseY returning page-top/margin-top anchors, registering tables only in this post-paragraph block means earlier text on the same page has already been laid out without the table exclusion zone. If a table uses anchor.vRelativeFrom = 'page' or 'margin' and its anchor paragraph appears later on the page, the table fragment can be placed near the top and overlap previously laid-out content, because wrapping was applied too late.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

@VladaHarbour VladaHarbour Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it since it require more position calculations. Now the default case will be paragraph anchored tables

const bottom = anchorY + (tableMeasure.totalHeight ?? 0);
if (bottom > tableBottomY) tableBottomY = bottom;
}
state.cursorY = tableBottomY;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid advancing flow cursor for non-wrapping anchored tables

This unconditional cursor update makes anchored tables consume document flow even when they are non-wrapping (wrap.type: 'None' or missing wrap), but registerTable explicitly treats that mode as absolute positioning with no exclusion zone. For such inputs, pushing state.cursorY to the table bottom adds unintended whitespace and can change pagination compared to expected absolute overlay behavior.

Useful? React with 👍 / 👎.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

state.cursorY = tableBottomY runs regardless of wrap.type. for wrap.type='None' (absolute-positioned), Word doesn't advance the flow cursor.

should this be gated on wrap type?

Copy link
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VladaHarbour this is great!

One thing to note here: since this touches rendering of documents, I think it's worth uploading a sample file or the file that caused the issue to our visual testing suite

(let me know if you want to sync on how to do that)

for (const { block: tableBlock, measure: tableMeasure } of tablesForPara) {
if (placedAnchoredTableIds.has(tableBlock.id)) continue;
const totalWidth = tableMeasure.totalWidth ?? 0;
if (columnWidthForTable > 0 && totalWidth >= columnWidthForTable * 0.99) continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.99 threshold is in two files — extract to a shared constant so they stay in sync.

if (block.anchor?.isAnchored) {
return;
const totalWidth = measure.totalWidth ?? 0;
treatAsInline = columnWidth > 0 && totalWidth >= columnWidth * 0.99;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here.

const bottom = anchorY + (tableMeasure.totalHeight ?? 0);
if (bottom > tableBottomY) tableBottomY = bottom;
}
state.cursorY = tableBottomY;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

state.cursorY = tableBottomY runs regardless of wrap.type. for wrap.type='None' (absolute-positioned), Word doesn't advance the flow cursor.

should this be gated on wrap type?

* Compute the base Y coordinate for an anchored table based on vRelativeFrom.
* Ignores tblpYSpec (alignV) by design.
*/
function getTableAnchorBaseY(block: TableBlock, state: PageState): number {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state.cursorY here is after layoutParagraphBlock, so vRelativeFrom='paragraph' anchors to paragraph bottom, not top. OOXML says paragraph start.

is this intentional to prevent overlap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it intentionally in order to be sure that table doesn't overlap text. Most likely Word anchors to paragraph top but has some offset calculation to be sure that table goes after the text.

const tableProps = block.attrs?.tableProperties as Record<string, unknown> | undefined;
const floatingProps = tableProps?.floatingTableProperties as Record<string, unknown> | undefined;
if (floatingProps && Object.keys(floatingProps).length > 0) {
if (floatingProps && Object.keys(floatingProps).length > 0 && !treatAsInline) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

full-width anchored tables also skip layoutMonolithicTable. if the table has floatingTableProperties but is full-width, it falls through to paginated table layout.

is that the right path for a floating table?

@caio-pizzol caio-pizzol linked an issue Feb 12, 2026 that may be closed by this pull request
@caio-pizzol caio-pizzol requested a review from tupizz February 12, 2026 15:03
Copy link
Contributor

@luccas-harbour luccas-harbour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@tupizz tupizz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Table overlaps with text

4 participants