diff --git a/packages/markitdown/src/markitdown/converter_utils/docx/math/latex_dict.py b/packages/markitdown/src/markitdown/converter_utils/docx/math/latex_dict.py
index 5657a6007..a25466d15 100644
--- a/packages/markitdown/src/markitdown/converter_utils/docx/math/latex_dict.py
+++ b/packages/markitdown/src/markitdown/converter_utils/docx/math/latex_dict.py
@@ -225,6 +225,8 @@
CHR_DEFAULT = {
"ACC_VAL": "\\hat{{{0}}}",
"GROUP_CHR_VAL": "\\underbrace{{{0}}}",
+ # Omitting m:chr under m:naryPr means U+222B INTEGRAL (ISO/IEC 29500-1).
+ "NARY_VAL": "\\int",
}
POS = {
diff --git a/packages/markitdown/src/markitdown/converter_utils/docx/math/omml.py b/packages/markitdown/src/markitdown/converter_utils/docx/math/omml.py
index f566f7d3b..3b068e4f2 100644
--- a/packages/markitdown/src/markitdown/converter_utils/docx/math/omml.py
+++ b/packages/markitdown/src/markitdown/converter_utils/docx/math/omml.py
@@ -363,10 +363,12 @@ def do_nary(self, elm):
the n-ary object
"""
res = []
- bo = ""
+ # m:naryPr is itself optional, so an absent element means every property
+ # takes its default, the operator included.
+ bo = CHR_DEFAULT.get("NARY_VAL", "")
for stag, t, e in self.process_children_list(elm):
if stag == "naryPr":
- bo = get_char(t.chr, store=CHR_BO)
+ bo = get_char(t.chr, default=CHR_DEFAULT.get("NARY_VAL"), store=CHR_BO)
else:
res.append(t)
return bo + BLANK.join(res)
diff --git a/packages/markitdown/tests/test_docx_math_nary.py b/packages/markitdown/tests/test_docx_math_nary.py
new file mode 100644
index 000000000..d64abb987
--- /dev/null
+++ b/packages/markitdown/tests/test_docx_math_nary.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3 -m pytest
+"""Tests for the default n-ary operator in DOCX math conversion.
+
+``m:chr`` under ``m:naryPr`` names the n-ary operator. ISO/IEC 29500-1 states
+that when the element is omitted the operator is U+222B INTEGRAL, so producers
+write ``m:chr`` only for the non-default operators such as the summation sign.
+``do_nary`` passed no default to ``get_char``, so an integral yielded ``None``
+and ``None + ""`` raised ``TypeError``.
+
+That failure is not local to the equation. ``pre_process_docx`` runs
+``_pre_process_math`` over the whole of ``word/document.xml`` inside a blanket
+``except Exception`` and, on error, writes the *original* unprocessed XML back.
+Mammoth does not render OMML, so one integral silently removes every equation
+in the document.
+"""
+
+from xml.etree import ElementTree as ET
+
+from markitdown.converter_utils.docx.math.omml import OMML_NS, oMath2Latex
+
+MATH_NS_DECL = f'xmlns:m="{OMML_NS[1:-1]}"'
+
+SUMMATION = "∑" # N-ARY SUMMATION, written out by Word as it is not the default
+
+
+def _nary(nary_pr: str):
+ xml = (
+ f""
+ f"{nary_pr}"
+ "0"
+ "1"
+ "x"
+ ""
+ )
+ return oMath2Latex(ET.fromstring(xml)).latex
+
+
+def test_nary_without_chr_defaults_to_integral():
+ # No m:chr, so the operator is U+222B. Previously raised TypeError.
+ latex = _nary('')
+ assert latex == "\\int_{0}^{1}x"
+
+
+def test_nary_with_explicit_chr_is_unchanged():
+ latex = _nary(f'')
+ assert latex == "\\sum_{0}^{1}x"
+
+
+def test_nary_without_nary_pr_defaults_to_integral():
+ # m:naryPr is optional. Absent, every property takes its default, so the
+ # operator is still U+222B rather than nothing.
+ assert _nary("") == "\\int_{0}^{1}x"