feat(md2doc): table, pre, blockquote, hr renderers
This commit is contained in:
@@ -365,6 +365,71 @@ def _render_list(doc, node, style: dict, level: int = 0, num_state: dict = None)
|
||||
_render_list(doc, nested, style=style, level=level + 1, num_state=num_state)
|
||||
|
||||
|
||||
def _render_table(doc, node, style: dict):
|
||||
"""Render <table> with header row shading."""
|
||||
header_fill = style.get("table_header_fill", "1e3a8a")
|
||||
|
||||
rows = node.find_all("tr")
|
||||
if not rows:
|
||||
return
|
||||
|
||||
cols = max(len(row.find_all(["th", "td"])) for row in rows)
|
||||
table = doc.add_table(rows=len(rows), cols=cols)
|
||||
table.style = 'Table Grid'
|
||||
|
||||
for i, row in enumerate(rows):
|
||||
cells = row.find_all(["th", "td"])
|
||||
is_header = row.parent.name == "thead" or any(c.name == "th" for c in cells)
|
||||
for j, cell in enumerate(cells):
|
||||
if j >= cols:
|
||||
break
|
||||
tc = table.rows[i].cells[j]
|
||||
tc.text = cell.get_text(strip=True)
|
||||
if is_header:
|
||||
add_cell_shading(tc, fill=header_fill)
|
||||
|
||||
|
||||
def _render_pre(doc, node, style: dict):
|
||||
"""Render <pre> (code block) — shaded background + monospace font.
|
||||
|
||||
Preserves newlines via add_break() between lines (Word ignores literal \\n in runs).
|
||||
Leading whitespace on each line is preserved as literal spaces (monospace font
|
||||
keeps alignment). Trailing empty line from markdown's final \\n is dropped.
|
||||
"""
|
||||
code_fill = style.get("code_fill", "f7f8fa")
|
||||
text = node.get_text()
|
||||
lines = text.split("\n")
|
||||
while lines and lines[-1] == "":
|
||||
lines.pop()
|
||||
|
||||
p = doc.add_paragraph()
|
||||
for i, line in enumerate(lines):
|
||||
if i > 0:
|
||||
p.add_run().add_break()
|
||||
run = p.add_run(line)
|
||||
run.font.name = "Menlo"
|
||||
run.font.size = Pt(8)
|
||||
add_paragraph_shading(p, fill=code_fill)
|
||||
|
||||
|
||||
def _render_blockquote(doc, node, style: dict):
|
||||
"""Render <blockquote> — left indent + italic + shading."""
|
||||
quote_fill = style.get("blockquote_fill", "f7f8fa")
|
||||
text = node.get_text(strip=True)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.left_indent = Cm(1)
|
||||
run = p.add_run(text)
|
||||
run.italic = True
|
||||
add_paragraph_shading(p, fill=quote_fill)
|
||||
|
||||
|
||||
def _render_hr(doc, node, style: dict):
|
||||
"""Render <hr> as empty paragraph with bottom border."""
|
||||
p = doc.add_paragraph()
|
||||
add_paragraph_bottom_border(p, color="d8d8d8", size="6")
|
||||
|
||||
|
||||
def convert_md_to_doc(md_file: Path, output_file: Path, style: str = "elegant") -> Path:
|
||||
"""Convert a single markdown file to DOCX.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user