diff --git a/packages/layout-engine/style-engine/src/ooxml/index.test.ts b/packages/layout-engine/style-engine/src/ooxml/index.test.ts index 7cd3505210..0286290c44 100644 --- a/packages/layout-engine/style-engine/src/ooxml/index.test.ts +++ b/packages/layout-engine/style-engine/src/ooxml/index.test.ts @@ -886,6 +886,92 @@ describe('ooxml - resolveTableCellProperties basedOn tblStylePr inheritance', () }); }); +// ────────────────────────────────────────────────────────────────────────────── +// Style base-level tcPr as the wholeTable layer (ECMA-376 17.7.6, SD-3035) +// A table style's base-level is stored on the style +// def's own tableCellProperties (sibling of tableStyleProperties) and IS the +// wholeTable conditional layer. Word paints it on every cell. +// ────────────────────────────────────────────────────────────────────────────── + +describe('ooxml - style base-level tcPr surfaces as wholeTable (SD-3035)', () => { + const interiorCell = (styleId: string) => ({ + tableProperties: { tableStyleId: styleId, tblLook: { noHBand: true, noVBand: true } }, + rowIndex: 1, + cellIndex: 1, + numRows: 3, + numCells: 3, + }); + + it('resolves a base-level shading with no explicit wholeTable region', () => { + const styles = { + ...emptyStyles, + styles: { + CondStyle: { + type: 'table', + tableProperties: {}, + tableCellProperties: { shading: { val: 'clear', color: 'auto', fill: 'F2F2F2' } }, + }, + }, + }; + const result = resolveTableCellProperties(null, interiorCell('CondStyle'), styles); + expect(result.shading).toEqual({ val: 'clear', color: 'auto', fill: 'F2F2F2' }); + }); + + it('leaf base-level shading beats an ancestor base-level shading via basedOn', () => { + const styles = { + ...emptyStyles, + styles: { + BaseStyle: { + type: 'table', + tableProperties: {}, + tableCellProperties: { shading: { fill: 'AAAAAA' } }, + }, + LeafStyle: { + type: 'table', + basedOn: 'BaseStyle', + tableProperties: {}, + tableCellProperties: { shading: { fill: 'F2F2F2' } }, + }, + }, + }; + const result = resolveTableCellProperties(null, interiorCell('LeafStyle'), styles); + expect(result.shading).toEqual({ fill: 'F2F2F2' }); + }); + + it('an explicit tableStyleProperties.wholeTable entry beats the base-level tcPr', () => { + const styles = { + ...emptyStyles, + styles: { + CondStyle: { + type: 'table', + tableProperties: {}, + tableCellProperties: { shading: { fill: 'BASE99' } }, + tableStyleProperties: { + wholeTable: { tableCellProperties: { shading: { fill: 'EXPL77' } } }, + }, + }, + }, + }; + const result = resolveTableCellProperties(null, interiorCell('CondStyle'), styles); + expect(result.shading).toEqual({ fill: 'EXPL77' }); + }); + + it('inline cell shading still wins over the base-level wholeTable fill', () => { + const styles = { + ...emptyStyles, + styles: { + CondStyle: { + type: 'table', + tableProperties: {}, + tableCellProperties: { shading: { fill: 'F2F2F2' } }, + }, + }, + }; + const result = resolveTableCellProperties({ shading: { fill: '4472C4' } }, interiorCell('CondStyle'), styles); + expect(result.shading).toEqual({ fill: '4472C4' }); + }); +}); + // ────────────────────────────────────────────────────────────────────────────── // cnfStyle supplementing index-based conditional type detection // ────────────────────────────────────────────────────────────────────────────── diff --git a/packages/layout-engine/style-engine/src/ooxml/index.ts b/packages/layout-engine/style-engine/src/ooxml/index.ts index 0791ee9472..0f28d8cbd4 100644 --- a/packages/layout-engine/style-engine/src/ooxml/index.ts +++ b/packages/layout-engine/style-engine/src/ooxml/index.ts @@ -483,6 +483,15 @@ function resolveConditionalProps( const def: StyleDefinition | undefined = translatedLinkedStyles.styles?.[currentId]; const props = def?.tableStyleProperties?.[styleType]?.[propertyType] as T | undefined; if (props) chain.push(props); + // ECMA-376 17.7.6: a table style's BASE-LEVEL (stored on the def's own + // tableCellProperties, a sibling of tableStyleProperties) IS the wholeTable + // conditional layer; Word paints e.g. its w:shd on every cell. Pushed after the + // explicit wholeTable entry so, post-reverse, the explicit entry still wins within + // one def while a leaf's base props beat any ancestor's. (SD-3035) + if (styleType === 'wholeTable' && propertyType === 'tableCellProperties') { + const baseProps = def?.tableCellProperties as T | undefined; + if (baseProps) chain.push(baseProps); + } currentId = def?.basedOn; } if (chain.length === 0) return undefined;