Skip to content

Commit bead74d

Browse files
committed
fix: gate header/footer X normalization on horizontal anchor
normalizeFragmentsForRegion was rewriting column-relative X whenever the vertical anchor was page-relative, conflating the two independent OOXML axes. Switch the gate to the horizontal anchor (hRelativeFrom === 'page') so column-relative X stays content-local and the painter applies the physical page margin exactly once. Update tests to cover both axes independently.
1 parent eb3fdb1 commit bead74d

2 files changed

Lines changed: 9 additions & 5 deletions

File tree

packages/layout-engine/layout-engine/src/normalize-header-footer-fragments.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('normalizeFragmentsForRegion (header/footer page-relative anchors)', ()
107107
expect(fragment.x).toBe(24);
108108
});
109109

110-
it('normalizes column-relative X using the physical page margin when Y is page-relative', () => {
110+
it('does not normalize column-relative X when Y is page-relative', () => {
111111
const imgWidth = 830;
112112
const block: FlowBlock = {
113113
kind: 'image',
@@ -123,11 +123,12 @@ describe('normalizeFragmentsForRegion (header/footer page-relative anchors)', ()
123123
},
124124
};
125125
const fragment = makeAnchoredImageFragment('header-column-background', 0, 40, imgWidth);
126+
fragment.x = -76;
126127
const pages = [{ number: 1, fragments: [fragment] }];
127128

128129
normalizeFragmentsForRegion(pages, [block], [makeDummyMeasure()], 'header', fullConstraints);
129130

130-
expect(fragment.x).toBe(-4);
131+
expect(fragment.x).toBe(-76);
131132
expect(fragment.y).toBe(8);
132133
});
133134
});
@@ -207,7 +208,7 @@ describe('normalizeFragmentsForRegion (header/footer page-relative anchors)', ()
207208
expect(fragment.y).toBe(PAGE_HEIGHT - 40 - FOOTER_BAND_ORIGIN);
208209
});
209210

210-
it('does not rewrite page-relative X when the vertical anchor is not page-relative', () => {
211+
it('normalizes page-relative X when the vertical anchor is not page-relative', () => {
211212
const block: FlowBlock = {
212213
kind: 'image',
213214
id: 'footer-cross-axis',
@@ -226,7 +227,7 @@ describe('normalizeFragmentsForRegion (header/footer page-relative anchors)', ()
226227

227228
normalizeFragmentsForRegion(pages, [block], [makeDummyMeasure()], 'footer', fullConstraints);
228229

229-
expect(fragment.x).toBe(24);
230+
expect(fragment.x).toBe((816 - 120) / 2);
230231
expect(fragment.y).toBe(12);
231232
});
232233

packages/layout-engine/layout-engine/src/normalize-header-footer-fragments.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ function isAnchoredFragment(fragment: Fragment): boolean {
7272
}
7373

7474
function isPageRelativeBlock(block: FlowBlock): block is ImageBlock | DrawingBlock {
75+
// This is a union-of-axes gate; each normalization branch re-checks its own axis.
7576
return (
7677
(block.kind === 'image' || block.kind === 'drawing') &&
7778
(block.anchor?.hRelativeFrom === 'page' || block.anchor?.vRelativeFrom === 'page')
@@ -121,7 +122,9 @@ export function normalizeFragmentsForRegion(
121122
const block = blockById.get(fragment.blockId);
122123
if (!block || !isPageRelativeBlock(block)) continue;
123124

124-
if (pageWidth != null && block.anchor?.vRelativeFrom === 'page') {
125+
// Horizontal and vertical anchors are independent in OOXML. Keep column-relative X
126+
// content-local so the painter can add the physical page margin exactly once.
127+
if (pageWidth != null && block.anchor?.hRelativeFrom === 'page') {
125128
const fragmentWidth = (fragment as { width?: number }).width ?? 0;
126129
const marginLeft = Math.max(0, constraints.margins.left ?? 0);
127130
const marginRight = Math.max(0, constraints.margins.right ?? 0);

0 commit comments

Comments
 (0)