xlsx/preserve-dxfs

Excel always emits <dxfs>, <tableStyles>, and an <extLst> wrapping <x14:slicerStyles> on xl/styles.xml. python-xlsx currently drops all three when they are empty-or-only-defaults on load, which breaks slicer-style references and pivot default styles. This manifest rides a real Excel-authored workbook through load_workbook + wb.save and re-checks the three survivors.

Library verdicts: python-xlsx: — xlsxjs: —

Metadata

Feature idxlsx/preserve-dxfs
Formatxlsx
Categoryround-trip
Spececma-376-5-part-1 § 18.8.15 dxfs

XPath assertions

ID / partPredicateXPathpython-xlsxxlsxjs
dxfs-element-preserved
xl/styles.xml
exist
The <dxfs> element must survive the load/save round-trip even if count="0".
//x:styleSheet/x:dxfs
tableStyles-element-preserved
xl/styles.xml
exist
The <tableStyles> element must survive. Its defaultTableStyle / defaultPivotStyle attributes are what Excel uses for newly-inserted tables and pivots.
//x:styleSheet/x:tableStyles
slicerStyles-ext-preserved
xl/styles.xml
exist
The <extLst>/<ext>/<x14:slicerStyles> chain must survive. Dropping it detaches every existing slicer from its style.
//x:styleSheet/x:extLst/x:ext/x14:slicerStyles

Render assertions

No render assertions declared.

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "xlsx/preserve-dxfs",
  "title": "Preserve <dxfs>, <tableStyles> and <extLst>/<slicerStyles> across round-trip",
  "format": "xlsx",
  "category": "round-trip",
  "summary": "Excel always emits <dxfs>, <tableStyles>, and an <extLst> wrapping <x14:slicerStyles> on xl/styles.xml. python-xlsx currently drops all three when they are empty-or-only-defaults on load, which breaks slicer-style references and pivot default styles. This manifest rides a real Excel-authored workbook through load_workbook + wb.save and re-checks the three survivors.",
  "roles": [
    "authoring"
  ],
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "18.8.15",
    "element": "dxfs",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/SpreadsheetML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
    "notes": "dxfs (differential formatting records) is the child-table that cell-level conditional formats, table styles, and pivot styles index into. Even empty (<dxfs count=\"0\"/>) Excel writes it because downstream readers treat it as a schema sentinel — removing it means the sibling <tableStyles defaultTableStyle=\"…\" defaultPivotStyle=\"…\"/> entry is re-evaluated at their own defaults on reopen. The <extLst>/x14:slicerStyles chain (defined by MS-OE376, namespace http://schemas.microsoft.com/office/spreadsheetml/2009/9/main) is likewise mandatory for workbooks that have ever carried a slicer. See ECMA-376 Part 1 clauses 18.8.15 (dxfs), 18.8.42 (tableStyles), 18.2.10 (extLst). Code: src/xlsx/styles/stylesheet.py::write_stylesheet — the writer skips the element when the in-memory list is empty, without checking whether the on-disk file had it."
  },
  "fixtures": {
    "machine": "xlsx/preserve-dxfs"
  },
  "round_trip": {
    "library": "python-xlsx",
    "source_fixture": "xlsx/preserve-dxfs",
    "notes": "Source is a copy of python-xlsx's own bundled book1.xlsx external-link test fixture — originally authored in Excel, shipped with the project's test suite as representative Excel output. Runner loads it, saves through python-xlsx, and evaluates the assertions against the re-saved bytes."
  },
  "assertions": [
    {
      "id": "dxfs-element-preserved",
      "part": "xl/styles.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:styleSheet/x:dxfs",
      "must": "exist",
      "description": "The <dxfs> element must survive the load/save round-trip even if count=\"0\"."
    },
    {
      "id": "tableStyles-element-preserved",
      "part": "xl/styles.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:styleSheet/x:tableStyles",
      "must": "exist",
      "description": "The <tableStyles> element must survive. Its defaultTableStyle / defaultPivotStyle attributes are what Excel uses for newly-inserted tables and pivots."
    },
    {
      "id": "slicerStyles-ext-preserved",
      "part": "xl/styles.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
        "x14": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
      },
      "xpath": "//x:styleSheet/x:extLst/x:ext/x14:slicerStyles",
      "must": "exist",
      "description": "The <extLst>/<ext>/<x14:slicerStyles> chain must survive. Dropping it detaches every existing slicer from its style."
    }
  ]
}

Fixture

Download preserve-dxfs.xlsx (9.8 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

dxfs (differential formatting records) is the child-table that cell-level conditional formats, table styles, and pivot styles index into. Even empty (<dxfs count="0"/>) Excel writes it because downstream readers treat it as a schema sentinel — removing it means the sibling <tableStyles defaultTableStyle="…" defaultPivotStyle="…"/> entry is re-evaluated at their own defaults on reopen. The <extLst>/x14:slicerStyles chain (defined by MS-OE376, namespace http://schemas.microsoft.com/office/spreadsheetml/2009/9/main) is likewise mandatory for workbooks that have ever carried a slicer. See ECMA-376 Part 1 clauses 18.8.15 (dxfs), 18.8.42 (tableStyles), 18.2.10 (extLst). Code: src/xlsx/styles/stylesheet.py::write_stylesheet — the writer skips the element when the in-memory list is empty, without checking whether the on-disk file had it.