Pin Excel's Markup-Compatibility shape for post-2010 spreadsheet features: the worksheet root declares the x14ac + x14r prefixes and lists them in mc:Ignorable; an x14 sparklineGroup extension lives inside <extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">, which is the Microsoft-allocated GUID for the sparkline extension.
Library verdicts: python-xlsx: — xlsxjs: —
| Feature id | xlsx/mc-ignorable-ext |
|---|---|
| Format | xlsx |
| Category | markup-compatibility |
| Spec | ecma-376-5-part-3 § 10 mc:Ignorable |
| ID / part | Predicate | XPath | python-xlsx | xlsxjs |
|---|---|---|---|---|
mc-ignorable-presentxl/worksheets/sheet1.xml | existThe <worksheet> root must carry an mc:Ignorable attribute. | /s:worksheet/@mc:Ignorable | — | — |
mc-ignorable-lists-x14acxl/worksheets/sheet1.xml | match = (^|\s)x14ac(\s|$)The x14ac prefix (Excel 2010 Ac extension) must appear as a whitespace-separated token in mc:Ignorable. | /s:worksheet/@mc:Ignorable | — | — |
mc-ignorable-lists-x14rxl/worksheets/sheet1.xml | match = (^|\s)x14r(\s|$)The x14r prefix (Excel 2013 Revisions) must appear as a whitespace-separated token in mc:Ignorable. | /s:worksheet/@mc:Ignorable | — | — |
worksheet-namespace-x14ac-declaredxl/worksheets/sheet1.xml | equal = http://schemas.microsoft.com/office/spreadsheetml/2009/9/acThe x14ac prefix on <worksheet> must be bound to the Excel 2010 Ac-extension namespace URI. | string(/s:worksheet/namespace::*[name()='x14ac']) | — | — |
worksheet-namespace-x14r-declaredxl/worksheets/sheet1.xml | equal = http://schemas.microsoft.com/office/spreadsheetml/2009/9/main/revisionsThe x14r prefix on <worksheet> must be bound to the Excel 2013 revisions namespace URI. | string(/s:worksheet/namespace::*[name()='x14r']) | — | — |
sparkline-ext-urixl/worksheets/sheet1.xml | equal = {05C60535-1F16-4fd2-B633-F4F36F0B64E0}The <ext> wrapping sparkline content must carry Microsoft's sparkline GUID. | //s:extLst/s:ext/@uri | — | — |
sparkline-group-presentxl/worksheets/sheet1.xml | existThe sparkline payload under <ext> must carry at least one <x14:sparklineGroup>. | //s:extLst/s:ext/x14:sparklineGroups/x14:sparklineGroup | — | — |
sparkline-formulaxl/worksheets/sheet1.xml | equal = Sheet!A1:G1The sparkline's source formula must point at the 7-cell trend range. | string(//x14:sparkline/xm:f) | — | — |
sparkline-target-cellxl/worksheets/sheet1.xml | equal = A3The sparkline must render into the expected cell (A3). | string(//x14:sparkline/xm:sqref) | — | — |
No render assertions declared.
scripts/gen_xlsx_mc_ignorable_ext.py
#!/usr/bin/env python3
"""Generate ``fixtures/xlsx/mc-ignorable-ext.xlsx``.
A minimal workbook with one sparkline, used to pin the
Markup-Compatibility shape Excel 2010+ writes: a ``<worksheet>`` root
that carries ``xmlns:x14ac`` / ``xmlns:x14r`` namespace declarations
plus ``mc:Ignorable="x14ac x14r"``, and an ``<extLst>/<ext uri="{GUID}">``
wrapping an ``<x14:sparklineGroups>`` payload under the x14 namespace.
python-xlsx emits the sparkline payload but does not declare the Excel
2010-Ac-extension or Excel 2013-Row-Runtime prefixes on the sheet root
and does not populate ``mc:Ignorable``. This generator patches the
saved sheet XML to add the missing namespace declarations and
mc:Ignorable value — matching Office's own output — so that the corpus
assertion has a stable target. A future python-xlsx change that brings
native mc:Ignorable support can drop the patch step.
"""
from __future__ import annotations
import datetime as _dt
import io
import zipfile
from pathlib import Path
from lxml import etree
from xlsx import Workbook
_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "xlsx" / "mc-ignorable-ext.xlsx"
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)
_S_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
_MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006"
_X14AC_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"
_X14R_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main/revisions"
def _rewrite_sheet(xml: bytes) -> bytes:
"""Rewrite the sheet XML root to declare x14ac + x14r and mc:Ignorable.
Rebuilds the root element with an augmented nsmap, copies children
and attributes across verbatim, then sets ``mc:Ignorable`` so
downstream readers know they can skip x14ac-only / x14r-only
extensions without bailing out.
"""
tree = etree.fromstring(xml)
nsmap = {
None: _S_NS,
"mc": _MC_NS,
"x14ac": _X14AC_NS,
"x14r": _X14R_NS,
}
new_root = etree.Element(f"{{{_S_NS}}}worksheet", nsmap=nsmap)
# Preserve xml:space and any other attributes.
for k, v in tree.attrib.items():
new_root.set(k, v)
new_root.set(f"{{{_MC_NS}}}Ignorable", "x14ac x14r")
for child in list(tree):
new_root.append(child)
return etree.tostring(
new_root, xml_declaration=True, encoding="UTF-8", standalone=True
)
def _repack(src: bytes, patched_sheet_xml: bytes) -> bytes:
out = io.BytesIO()
with zipfile.ZipFile(io.BytesIO(src)) as zin, zipfile.ZipFile(
out, "w", zipfile.ZIP_DEFLATED
) as zout:
for info in zin.infolist():
data = (
patched_sheet_xml
if info.filename == "xl/worksheets/sheet1.xml"
else zin.read(info.filename)
)
new_info = zipfile.ZipInfo(filename=info.filename, date_time=info.date_time)
new_info.compress_type = info.compress_type
zout.writestr(new_info, data)
return out.getvalue()
def main() -> int:
wb = Workbook()
ws = wb.active
# 7-point trend series in row 1.
for col, value in enumerate([3, 7, 4, 9, 5, 8, 6], start=1):
ws.cell(row=1, column=col, value=value)
# Sparkline over the series, rendered into cell A3.
ws.add_sparkline(location="A3", data="Sheet!A1:G1", type="line")
buf = io.BytesIO()
wb.save(buf, zip_date_time=_REPRODUCIBLE_DT)
with zipfile.ZipFile(io.BytesIO(buf.getvalue())) as zf:
sheet_xml = zf.read("xl/worksheets/sheet1.xml")
patched = _repack(buf.getvalue(), _rewrite_sheet(sheet_xml))
_OUT.parent.mkdir(parents=True, exist_ok=True)
_OUT.write_bytes(patched)
print(_OUT)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "xlsx/mc-ignorable-ext",
"title": "Markup Compatibility: mc:Ignorable + x14 sparkline extLst",
"format": "xlsx",
"category": "markup-compatibility",
"summary": "Pin Excel's Markup-Compatibility shape for post-2010 spreadsheet features: the worksheet root declares the x14ac + x14r prefixes and lists them in mc:Ignorable; an x14 sparklineGroup extension lives inside <extLst><ext uri=\"{05C60535-1F16-4fd2-B633-F4F36F0B64E0}\">, which is the Microsoft-allocated GUID for the sparkline extension.",
"spec": {
"source": "ecma-376-5-part-3",
"clause": "10",
"element": "mc:Ignorable",
"notes": "ECMA-376 Part 3 §10.1.1 defines mc:Ignorable. In SpreadsheetML, Excel 2010 introduced sparklines, table styles' theme colors, and several other features in the x14 namespace (http://schemas.microsoft.com/office/spreadsheetml/2009/9/main) and attributes like x14ac:dyDescent under x14ac (http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac). Excel 2013+ added x14r (revisions). The convention is: producers declare xmlns:x14ac and xmlns:x14r on the <worksheet> root and list both prefixes in mc:Ignorable so a pre-2010 reader can skip x14ac attributes and x14r elements without error. The sparkline payload itself lives under <extLst>/<ext uri=\"{05C60535-1F16-4fd2-B633-F4F36F0B64E0}\"> — that GUID is Microsoft's registered identifier for the sparkline extension. This fixture pins both sides of that convention."
},
"fixtures": {
"machine": "xlsx/mc-ignorable-ext"
},
"generator": {
"python": "scripts/gen_xlsx_mc_ignorable_ext.py"
},
"assertions": [
{
"id": "mc-ignorable-present",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
},
"xpath": "/s:worksheet/@mc:Ignorable",
"must": "exist",
"description": "The <worksheet> root must carry an mc:Ignorable attribute."
},
{
"id": "mc-ignorable-lists-x14ac",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
},
"xpath": "/s:worksheet/@mc:Ignorable",
"must": "match",
"value": "(^|\\s)x14ac(\\s|$)",
"description": "The x14ac prefix (Excel 2010 Ac extension) must appear as a whitespace-separated token in mc:Ignorable."
},
{
"id": "mc-ignorable-lists-x14r",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
},
"xpath": "/s:worksheet/@mc:Ignorable",
"must": "match",
"value": "(^|\\s)x14r(\\s|$)",
"description": "The x14r prefix (Excel 2013 Revisions) must appear as a whitespace-separated token in mc:Ignorable."
},
{
"id": "worksheet-namespace-x14ac-declared",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "string(/s:worksheet/namespace::*[name()='x14ac'])",
"must": "equal",
"value": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac",
"description": "The x14ac prefix on <worksheet> must be bound to the Excel 2010 Ac-extension namespace URI."
},
{
"id": "worksheet-namespace-x14r-declared",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "string(/s:worksheet/namespace::*[name()='x14r'])",
"must": "equal",
"value": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main/revisions",
"description": "The x14r prefix on <worksheet> must be bound to the Excel 2013 revisions namespace URI."
},
{
"id": "sparkline-ext-uri",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//s:extLst/s:ext/@uri",
"must": "equal",
"value": "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}",
"description": "The <ext> wrapping sparkline content must carry Microsoft's sparkline GUID."
},
{
"id": "sparkline-group-present",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"x14": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
},
"xpath": "//s:extLst/s:ext/x14:sparklineGroups/x14:sparklineGroup",
"must": "exist",
"description": "The sparkline payload under <ext> must carry at least one <x14:sparklineGroup>."
},
{
"id": "sparkline-formula",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"x14": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main",
"xm": "http://schemas.microsoft.com/office/excel/2006/main"
},
"xpath": "string(//x14:sparkline/xm:f)",
"must": "equal",
"value": "Sheet!A1:G1",
"description": "The sparkline's source formula must point at the 7-cell trend range."
},
{
"id": "sparkline-target-cell",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
"x14": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main",
"xm": "http://schemas.microsoft.com/office/excel/2006/main"
},
"xpath": "string(//x14:sparkline/xm:sqref)",
"must": "equal",
"value": "A3",
"description": "The sparkline must render into the expected cell (A3)."
}
]
}
No rendered reference is available for this case.