diff --git a/docling/backend/msword_backend.py b/docling/backend/msword_backend.py index c0324f6fb1..aa9d88aa49 100644 --- a/docling/backend/msword_backend.py +++ b/docling/backend/msword_backend.py @@ -68,6 +68,7 @@ from docx.enum.style import WD_STYLE_TYPE from docx.opc.constants import RELATIONSHIP_TYPE as DOCX_RT from docx.oxml import parse_xml as docx_parse_xml + from docx.oxml.ns import qn from docx.oxml.simpletypes import ST_Merge from docx.oxml.table import CT_Tc from docx.oxml.xmlchemy import BaseOxmlElement @@ -649,6 +650,19 @@ def _normalize_strict_ooxml(archive: zipfile.ZipFile) -> BytesIO: return normalized +def _normalize_list_style_types(docx_obj: DocxDocument) -> None: + """Change the style type ``list`` to ``numbering`` in the styles part. + + ``ST_StyleType`` has no ``list`` value; the numbering style type is + ``WD_STYLE_TYPE.LIST`` in python-docx. python-docx raises ``ValueError`` + when it reads the type ``list``, and it reads the type of every style to + find a default style, so one such style fails the whole conversion. + """ + for style in docx_obj.styles.element.findall(qn("w:style")): + if style.get(qn("w:type")) == "list": + style.set(qn("w:type"), "numbering") + + class _NestedList(NamedTuple): """A list opened under an open list's item for its larger left indentation. @@ -879,6 +893,7 @@ def __init__( if self.docx_obj: self.valid = True self.current_part = self.docx_obj.part + _normalize_list_style_types(self.docx_obj) self._default_paragraph_style = self.docx_obj.styles.default( WD_STYLE_TYPE.PARAGRAPH ) diff --git a/tests/test_backend_msword.py b/tests/test_backend_msword.py index 314fa1c6f3..f5851f2c3e 100644 --- a/tests/test_backend_msword.py +++ b/tests/test_backend_msword.py @@ -2353,6 +2353,30 @@ def test_fragment_only_rel_does_not_crash_backend(): assert text_items[0].hyperlink is None +@pytest.mark.parametrize("body_style", ["Body Text", None]) +def test_list_style_type_does_not_fail_conversion(tmp_path, body_style): + """A style with the invalid type ``list`` must not fail the conversion. + + python-docx raises ``ValueError`` when it reads that type. Both the default + style lookup on load and the style of a paragraph without ``w:pStyle`` + read the type of every style. + """ + document = Document() + document.add_paragraph("Quarterly results", style="Heading 1") + document.add_paragraph("Revenue grew 3%.", style=body_style) + list_style = OxmlElement("w:style") + list_style.set(qn("w:type"), "list") + list_style.set(qn("w:styleId"), "OutlineList") + document.styles.element.append(list_style) + + doc = _convert_built(document, tmp_path) + + assert [item.text for item in doc.texts] == [ + "Quarterly results", + "Revenue grew 3%.", + ] + + _PLAIN_FOOTNOTE_BODY = ( "Smith, J. (2020). Example Study." )