79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""Extract visible text from OOXML runs/paragraphs (incl. w:noBreakHyphen)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from docx.oxml.ns import qn
|
|
|
|
|
|
def _local(tag: str) -> str:
|
|
if "}" in tag:
|
|
return tag.rsplit("}", 1)[-1]
|
|
return tag
|
|
|
|
|
|
def run_element_text(run_el) -> str:
|
|
"""
|
|
Text of a w:r including non-breaking hyphens.
|
|
|
|
md2gost writes ASCII '-' as w:noBreakHyphen (python-docx run.text skips it).
|
|
Soft hyphens are optional break hints — omit them from Markdown.
|
|
"""
|
|
if run_el is None:
|
|
return ""
|
|
parts: list[str] = []
|
|
for child in run_el:
|
|
tag = _local(child.tag)
|
|
if tag == "t":
|
|
parts.append(child.text or "")
|
|
elif tag == "noBreakHyphen":
|
|
parts.append("-")
|
|
elif tag == "softHyphen":
|
|
continue
|
|
elif tag == "tab":
|
|
parts.append("\t")
|
|
elif tag == "br":
|
|
# page/line break inside run — treat as space for inline text
|
|
if child.get(qn("w:type")) == "page":
|
|
continue
|
|
parts.append("\n")
|
|
elif tag == "cr":
|
|
parts.append("\n")
|
|
return "".join(parts)
|
|
|
|
|
|
def hyperlink_element_text(hyperlink_el) -> str:
|
|
if hyperlink_el is None:
|
|
return ""
|
|
parts: list[str] = []
|
|
for run in hyperlink_el.findall(qn("w:r")):
|
|
parts.append(run_element_text(run))
|
|
return "".join(parts)
|
|
|
|
|
|
def paragraph_element_text(p_el) -> str:
|
|
"""Visible body text of w:p (runs + hyperlinks), without OMML."""
|
|
if p_el is None:
|
|
return ""
|
|
parts: list[str] = []
|
|
for child in p_el:
|
|
tag = child.tag
|
|
if tag == qn("w:r"):
|
|
parts.append(run_element_text(child))
|
|
elif tag == qn("w:hyperlink"):
|
|
parts.append(hyperlink_element_text(child))
|
|
return "".join(parts)
|
|
|
|
|
|
def paragraph_plain(paragraph) -> str:
|
|
"""Strip CR/bell from a python-docx Paragraph, keeping noBreakHyphen as '-'."""
|
|
return paragraph_raw(paragraph).strip()
|
|
|
|
|
|
def paragraph_raw(paragraph) -> str:
|
|
"""Full paragraph text with noBreakHyphen → '-', without strip (for Code lines)."""
|
|
try:
|
|
text = paragraph_element_text(paragraph._element)
|
|
except Exception:
|
|
text = paragraph.text or ""
|
|
return text.replace("\r", "").replace("\x07", "")
|