Skip to content

fix(docx): an equation without its properties child drops every equation - #2445

Open
kevin (kevin9327) wants to merge 1 commit into
microsoft:mainfrom
kevin9327:fix/docx-math-optional-properties
Open

fix(docx): an equation without its properties child drops every equation#2445
kevin (kevin9327) wants to merge 1 commit into
microsoft:mainfrom
kevin9327:fix/docx-math-optional-properties

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

Converting a .docx whose equations were written by something other than Word produces the document's prose with every equation missing — no error, no warning, just no math (issue #1512).

Every OMML element's properties child is optional. <m:f> without <m:fPr> is a fraction with default properties, and the same goes for <m:d>, <m:acc>, <m:bar> and <m:groupChr>. Word always writes the child, because it carries the control run's formatting — in tests/test_files/equations.docx every <m:fPr> holds nothing but a <m:ctrlPr> — but nothing else has to.

do_f, do_d, do_acc, do_bar and do_groupchr indexed the child directly (c_dict["fPr"]), so an equation that omits it raises KeyError. The five already carry the spec's defaults; they just crash before reaching them.

The damage is not local. _pre_process_math runs over all of word/document.xml inside a blanket except Exception, so one such equation discards the OMML rewrite for the whole part, and Mammoth cannot render raw OMML. Taking the existing fixture and dropping the element a non-Word producer would not have written:

equations.docx                 : 4 lines containing LaTeX
equations.docx without <m:fPr>: 0 lines containing LaTeX
equations.docx without <m:dPr>: 0 lines containing LaTeX

After the fix, 4 and 4 and 4.

This is the same failure class as #2368, which gives do_nary the spec default for an omitted m:chr; these five crash a step earlier, on the properties element itself. The fix reads the child through a helper that substitutes an empty one, so every property falls through to the default it already has.

Reproduction

New test file, on unmodified main (packages/markitdown):

$ python -m pytest tests/test_docx_math_optional_properties.py -q
FAILED tests/test_docx_math_optional_properties.py::test_omitted_properties_convert_like_default_properties[f]
FAILED tests/test_docx_math_optional_properties.py::test_omitted_properties_convert_like_default_properties[d]
FAILED tests/test_docx_math_optional_properties.py::test_omitted_properties_convert_like_default_properties[acc]
FAILED tests/test_docx_math_optional_properties.py::test_omitted_properties_convert_like_default_properties[bar]
FAILED tests/test_docx_math_optional_properties.py::test_omitted_properties_convert_like_default_properties[groupChr]
FAILED tests/test_docx_math_optional_properties.py::test_fraction_without_properties_still_converts
FAILED tests/test_docx_math_optional_properties.py::test_delimiter_without_properties_uses_the_default_parentheses
FAILED tests/test_docx_math_optional_properties.py::test_document_keeps_its_equations_without_the_properties_child[fPr]
FAILED tests/test_docx_math_optional_properties.py::test_document_keeps_its_equations_without_the_properties_child[dPr]
9 failed, 1 passed in 2.86s

The unit failures are KeyError: 'fPr' and friends; the end-to-end one is the whole document going dark:

>       assert _latex_line_count(_docx_without(element_name)) == expected
E       AssertionError: assert 0 == 4

The one test that passes before and after is the pin: <m:dPr> with an explicit begChr/endChr still converts to \left[x\right] rather than the default parentheses.

With the fix:

$ python -m pytest tests/test_docx_math_optional_properties.py -q
10 passed in 4.10s

Verification

No fixture is modified: the end-to-end test rebuilds equations.docx in memory.

$ python -m pytest tests/test_docx_math.py tests/test_docx_math_accents.py \
    tests/test_docx_math_symbols.py tests/test_docx_omml.py \
    tests/test_module_vectors.py -q
125 passed in 45.09s   # origin/main
125 passed in 38.38s   # this branch

black reports both touched files unchanged.

Every OMML element's properties child is optional -- <m:f> without
<m:fPr> is a fraction with default properties -- but do_f, do_d, do_acc,
do_bar and do_groupchr indexed it directly and raised KeyError.
_pre_process_math runs over the whole of word/document.xml inside a
blanket except Exception, so the crash discards the OMML rewrite for the
entire part and every equation in the document is lost.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant