Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docling/backend/msword_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
)
Expand Down
24 changes: 24 additions & 0 deletions tests/test_backend_msword.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
"<w:p><w:r><w:t>Smith, J. (2020). Example Study.</w:t></w:r></w:p>"
)
Expand Down
Loading