wechat-robot-skills/skills/docx/scripts/create_document.py
2026-07-26 02:45:00 +08:00

122 lines
3.3 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
import tempfile
from pathlib import Path
from typing import Any
from _document_builder import (
add_blocks,
apply_core_properties,
apply_document_defaults,
apply_header_footer,
apply_named_styles,
apply_page_settings,
expect_list,
set_update_fields,
)
from _docx_common import (
SkillArgumentParser,
inspect_archive,
load_json_argument,
output_file,
publish_file,
run_cli,
)
def build_parser() -> argparse.ArgumentParser:
parser = SkillArgumentParser(
description="依据受控 JSON 说明创建专业 Word DOCX 文档。"
)
parser.add_argument("--output", required=True)
parser.add_argument("--spec", help="内联 JSON 文档说明")
parser.add_argument("--spec-file", help="JSON 文档说明文件")
parser.add_argument("--overwrite", action="store_true")
return parser
def main() -> dict[str, Any]:
from docx import Document
args = build_parser().parse_args()
destination = output_file(
args.output,
{".docx"},
overwrite=args.overwrite,
)
spec = load_json_argument(args.spec, args.spec_file, label="文档说明")
allowed = {
"properties",
"page",
"default_font",
"styles",
"header",
"footer",
"blocks",
}
unknown = set(spec) - allowed
if unknown:
raise ValueError(f"文档说明包含未知顶层字段:{sorted(unknown)}")
blocks = expect_list(spec.get("blocks"), "blocks")
if not blocks:
raise ValueError("blocks 不能为空")
document = Document()
apply_document_defaults(document, spec.get("default_font", {}))
if "styles" in spec:
apply_named_styles(document, spec["styles"])
if "properties" in spec:
apply_core_properties(document, spec["properties"])
apply_page_settings(document.sections[0], spec.get("page", {}))
counts = add_blocks(document, document, blocks)
apply_header_footer(
document,
spec.get("header"),
spec.get("footer"),
)
set_update_fields(document)
descriptor, temp_name = tempfile.mkstemp(
prefix=f".{destination.stem}.",
suffix=".docx",
dir=str(destination.parent),
)
os.close(descriptor)
temp_path = Path(temp_name)
try:
document.save(temp_path)
archive = inspect_archive(temp_path)
if archive["missing_required_parts"]:
raise ValueError(
"生成文档缺少必要部件:"
+ "".join(archive["missing_required_parts"])
)
Document(str(temp_path))
publish_file(temp_path, destination, overwrite=args.overwrite)
finally:
if temp_path.exists():
temp_path.unlink()
return {
"path": str(destination),
"properties": {
"title": document.core_properties.title,
"author": document.core_properties.author,
"subject": document.core_properties.subject,
},
"section_count": len(document.sections),
"paragraph_count": len(document.paragraphs),
"table_count": len(document.tables),
"counts": counts,
"archive": archive,
"requires_visual_review": True,
}
if __name__ == "__main__":
raise SystemExit(run_cli(main))