| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- #!/usr/bin/env python3
- """将操作说明 Markdown 转为 docx(供交付文档使用)。"""
- import re
- import sys
- from pathlib import Path
- from docx import Document
- from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
- from docx.oxml.ns import qn
- from docx.shared import Pt
- def set_doc_font(doc):
- style = doc.styles["Normal"]
- style.font.name = "宋体"
- style.font.size = Pt(11)
- style._element.rPr.rFonts.set(qn("w:eastAsia"), "宋体")
- def add_rich_text(paragraph, text):
- pattern = re.compile(r"(\*\*[^*]+\*\*)")
- parts = pattern.split(text)
- for part in parts:
- if not part:
- continue
- if part.startswith("**") and part.endswith("**"):
- run = paragraph.add_run(part[2:-2])
- run.bold = True
- else:
- paragraph.add_run(part)
- def parse_table_row(line):
- line = line.strip()
- if not line.startswith("|"):
- return None
- cells = [c.strip() for c in line.strip("|").split("|")]
- return cells
- def is_separator_row(cells):
- return all(re.fullmatch(r":?-+:?", c.replace(" ", "")) for c in cells if c != "")
- def md_to_docx(md_path, docx_path):
- lines = Path(md_path).read_text(encoding="utf-8").splitlines()
- doc = Document()
- set_doc_font(doc)
- i = 0
- while i < len(lines):
- line = lines[i]
- stripped = line.strip()
- if not stripped:
- i += 1
- continue
- if stripped == "---":
- doc.add_paragraph("")
- i += 1
- continue
- if stripped.startswith("#"):
- level = len(stripped) - len(stripped.lstrip("#"))
- title = stripped[level:].strip()
- style = "Title" if level == 1 else f"Heading {min(level, 4)}"
- p = doc.add_paragraph(style=style)
- add_rich_text(p, title)
- i += 1
- continue
- if stripped.startswith("```"):
- lang = stripped[3:].strip()
- i += 1
- code_lines = []
- while i < len(lines) and not lines[i].strip().startswith("```"):
- code_lines.append(lines[i])
- i += 1
- if i < len(lines):
- i += 1
- p = doc.add_paragraph()
- run = p.add_run("\n".join(code_lines))
- run.font.name = "Consolas"
- run._element.rPr.rFonts.set(qn("w:eastAsia"), "宋体")
- run.font.size = Pt(9)
- doc.add_paragraph("")
- continue
- if stripped.startswith("|"):
- table_lines = []
- while i < len(lines) and lines[i].strip().startswith("|"):
- row = parse_table_row(lines[i])
- if row is not None:
- table_lines.append(row)
- i += 1
- data_rows = []
- for row in table_lines:
- if is_separator_row(row):
- continue
- data_rows.append(row)
- if not data_rows:
- continue
- cols = max(len(r) for r in data_rows)
- table = doc.add_table(rows=len(data_rows), cols=cols)
- table.style = "Table Grid"
- for r_idx, row in enumerate(data_rows):
- for c_idx in range(cols):
- cell_text = row[c_idx] if c_idx < len(row) else ""
- cell = table.rows[r_idx].cells[c_idx]
- cell.text = ""
- p = cell.paragraphs[0]
- add_rich_text(p, cell_text)
- doc.add_paragraph("")
- continue
- if stripped.startswith(">"):
- p = doc.add_paragraph(style="Intense Quote")
- add_rich_text(p, stripped.lstrip("> ").strip())
- i += 1
- continue
- if stripped.startswith("- "):
- p = doc.add_paragraph(style="List Bullet")
- add_rich_text(p, stripped[2:].strip())
- i += 1
- continue
- p = doc.add_paragraph()
- add_rich_text(p, stripped)
- i += 1
- for section in doc.sections:
- section.top_margin = Pt(72)
- section.bottom_margin = Pt(72)
- section.left_margin = Pt(90)
- section.right_margin = Pt(90)
- doc.save(docx_path)
- print(f"Generated: {docx_path}")
- def main():
- base = Path(__file__).parent
- pairs = [
- ("OPERATION_MANUAL_HEALTH.md", "OPERATION_MANUAL_HEALTH.docx"),
- ("OPERATION_MANUAL_ANOMALY.md", "OPERATION_MANUAL_ANOMALY.docx"),
- ("OPERATION_MANUAL_ANOMALY.md", "异常检测及风资源-操作说明.docx"),
- ("OPERATION_MANUAL_LEDGER.md", "OPERATION_MANUAL_LEDGER.docx"),
- ("OPERATION_MANUAL_LEDGER.md", "台账管理-操作说明.docx"),
- ("DEPLOY_FRONTEND.md", "DEPLOY_FRONTEND.docx"),
- ("DESIGN_SPEC_HEALTH.md", "健康评估系统设计说明.docx"),
- ("DESIGN_SPEC_ANOMALY_WRW.md", "异常检测及风资源尾流系统设计说明.docx"),
- ]
- for md_name, docx_name in pairs:
- md_to_docx(base / md_name, base / docx_name)
- if __name__ == "__main__":
- main()
|