From d558f53bca57994d00faa3058169dc49fd05c97e Mon Sep 17 00:00:00 2001 From: nazlo90 Date: Tue, 23 Jun 2026 17:11:16 -0300 Subject: [PATCH] fix(super-converter): table-cell shading export writes bare hex w:fill + w:val="clear" OOXML requires w:fill to be a bare 6-char hex string and a solid fill needs w:val="clear" (ECMA-376 17.4.32). The exporter wrote background.color as-is, keeping a leading '#' and omitting w:val. LibreOffice treats a missing w:val as nil and renders such cells black regardless of the fill. generateTableCellProperties now normalizes the color to bare hex via normalizeHexColor, guards with isValidHexColor so non-hex values like "auto" produce no w:shd, and emits w:val="clear". Updates the table-cell export tests accordingly. --- .../w/tc/helpers/translate-table-cell.js | 9 ++++++--- .../w/tc/helpers/translate-table-cell.test.js | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.js index 2b799627e3..873466c296 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.js @@ -1,4 +1,4 @@ -import { pixelsToTwips, inchesToTwips, twipsToPixels } from '@converter/helpers'; +import { pixelsToTwips, inchesToTwips, twipsToPixels, normalizeHexColor, isValidHexColor } from '@converter/helpers'; import { translateChildNodes } from '@converter/v2/exporter/helpers/index'; import { translator as tcPrTranslator } from '../../tcPr'; import { @@ -73,8 +73,11 @@ export function generateTableCellProperties(node) { // Background const { background = {} } = attrs; - if (background?.color && tableCellProperties.shading?.fill !== background?.color) { - tableCellProperties['shading'] = { fill: background.color }; + if (background?.color) { + const fill = normalizeHexColor(background.color); + if (fill && isValidHexColor(fill) && tableCellProperties.shading?.fill?.toUpperCase() !== fill) { + tableCellProperties['shading'] = { fill, val: 'clear' }; + } } else if (!background?.color && tableCellProperties?.shading?.fill) { delete tableCellProperties.shading; } diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.test.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.test.js index 2e44291fdf..90d67fb4b1 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/tc/helpers/translate-table-cell.test.js @@ -43,8 +43,9 @@ describe('translate-table-cell helpers', () => { // gridSpan expect(byName['w:gridSpan'].attributes['w:val']).toBe('2'); - // background - expect(byName['w:shd'].attributes['w:fill']).toBe('#FF00FF'); + // background - OOXML requires bare 6-char hex (no #) and w:val="clear" for solid fills + expect(byName['w:shd'].attributes['w:fill']).toBe('FF00FF'); + expect(byName['w:shd'].attributes['w:val']).toBe('clear'); // tcMar const mar = byName['w:tcMar']; @@ -348,6 +349,21 @@ describe('translate-table-cell helpers', () => { // mocked child from translateChildNodes expect(out.elements[1]).toMatchObject({ name: 'w:p' }); }); + + it('generateTableCellProperties strips # from background color and adds val:clear', () => { + const node = { attrs: { background: { color: '#A1B2C3' } } }; + const tcPr = generateTableCellProperties(node); + const shd = tcPr.elements.find((e) => e.name === 'w:shd'); + expect(shd.attributes['w:fill']).toBe('A1B2C3'); + expect(shd.attributes['w:val']).toBe('clear'); + }); + + it('generateTableCellProperties does not write w:shd for non-hex background (e.g. auto)', () => { + const node = { attrs: { colwidth: [100], widthUnit: 'px', background: { color: 'auto' } } }; + const tcPr = generateTableCellProperties(node); + const shd = tcPr.elements.find((e) => e.name === 'w:shd'); + expect(shd).toBeUndefined(); + }); }); /** Helper: extract w:tcW element from a generateTableCellProperties result */