-
Notifications
You must be signed in to change notification settings - Fork 66
fix: anchor table overlaps text #1995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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".
| if (tablesForPara) { | ||
| const state = paginator.ensurePage(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 👍 / 👎.
| const bottom = anchorY + (tableMeasure.totalHeight ?? 0); | ||
| if (bottom > tableBottomY) tableBottomY = bottom; | ||
| } | ||
| state.cursorY = tableBottomY; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 👍 / 👎.
There was a problem hiding this comment.
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?
caio-pizzol
left a comment
There was a problem hiding this 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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
| 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) { |
There was a problem hiding this comment.
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?
Tables that have w:tblpPr devined are treated as anchor tables and can overlap the text


Before:
After: