"""docx.py — the .docx surface projector: an OWN minimal OOXML emitter. Own-the-core: a .docx is just a ZIP of a few XML parts (WordprocessingML). We emit it with the standard library only — ``zipfile`` + string XML — no python-docx, no external dependency. This proves a "richer structured format" surface without importing anyone else's toolkit. Parts emitted (the minimal valid set + a styles part for real headings): [Content_Types].xml _rels/.rels word/_rels/document.xml.rels word/styles.xml (Title / Heading1 / Heading2 / Normal) word/document.xml (the content) Like the markdown projector it reads only the IR's realized sentences; it invents nothing. The surface differs, the faithful content does not. """ from __future__ import annotations import io import os import sys import zipfile from xml.sax.saxutils import escape sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from document_ir import DocumentIR # noqa: E402 from projectors.base import register # noqa: E402 _CONTENT_TYPES = """ """ _RELS = """ """ _DOC_RELS = """ """ _W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" _STYLES = f""" """ def _para(text: str, style: str | None = None) -> str: ppr = f"" if style else "" return (f"{ppr}" f"{escape(text)}") class DocxProjector: surface = "docx" media_type = ("application/vnd.openxmlformats-officedocument." "wordprocessingml.document") ext = "docx" modality = "text" def _document_xml(self, doc: DocumentIR) -> str: body: list[str] = [_para(doc.title, "Title")] if doc.subtitle: body.append(_para(doc.subtitle, "Subtitle")) abstract = doc.meta.get("abstract") if abstract is not None and abstract.sentences: body.append(_para(abstract.text())) for sec in doc.sections: style = "Heading1" if sec.level <= 1 else "Heading2" body.append(_para(sec.heading, style)) for block in sec.blocks: t = block.text() if t: body.append(_para(t)) return (f"" f"" + "".join(body) + "" "") def project(self, doc: DocumentIR) -> bytes: buf = io.BytesIO() with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z: z.writestr("[Content_Types].xml", _CONTENT_TYPES) z.writestr("_rels/.rels", _RELS) z.writestr("word/_rels/document.xml.rels", _DOC_RELS) z.writestr("word/styles.xml", _STYLES) z.writestr("word/document.xml", self._document_xml(doc)) return buf.getvalue() register(DocxProjector())