"""doc_convert.py — convierte un .doc viejo a .docx usando Microsoft Word (COM). Necesario para Marcos (marroro@telefonica.net), que envía .doc (Word antiguo) y python-docx no sabe leer .doc. El contenido ya viene en EML (lo etiqueta Inma al revisar); aquí solo cambiamos el FORMATO de archivo. Requiere Microsoft Word instalado + pywin32 (igual que marcos_review). API: doc_to_docx(path, force=False) -> Path | None """ from __future__ import annotations from pathlib import Path WD_FORMAT_DOCX = 16 # wdFormatDocumentDefault (.docx) def doc_to_docx(path, force: bool = False): """Convierte un .doc a .docx con Word COM. Idempotente: si el .docx ya existe y no force, lo devuelve sin reconvertir. Devuelve la ruta .docx, o None si falla.""" path = Path(path) if path.suffix.lower() != ".doc": return None out = path.with_suffix(".docx") if out.exists() and not force: return out try: import win32com.client # type: ignore except ImportError: return None word = win32com.client.Dispatch("Word.Application") word.Visible = False try: d = word.Documents.Open(str(path)) d.SaveAs(str(out), FileFormat=WD_FORMAT_DOCX) d.Close(SaveChanges=False) finally: word.Quit() if out.exists(): quitar_smarttags(out) return out if out.exists() else None def quitar_smarttags(docx_path) -> int: """Desenvuelve los del .docx. Devuelve cuántos ha quitado. ⚠️ CRÍTICO, no es cosmético. Los .doc de Fray Marcos traen `smartTag` (el viejo reconocimiento de nombres de Word) envolviendo expresiones como "la Iglesia" o "la Cananea". **python-docx solo lee los que cuelgan directamente del párrafo, así que el texto de dentro del smartTag DESAPARECE sin dar error**: se publicaron frases mutiladas en las cartas 736, 737 y 739 ("...no va dirigida a los apóstoles, sino a dureza de la respuesta..."). Detectado el 12-ago-2026. """ import re, shutil, zipfile from pathlib import Path docx_path = Path(docx_path) with zipfile.ZipFile(docx_path) as z: piezas = {n: z.read(n) for n in z.namelist()} xml = piezas.get("word/document.xml", b"").decode("utf-8", "replace") nuevo, n = re.subn(r"]*>|]*/>", "", xml) if not n: return 0 piezas["word/document.xml"] = nuevo.encode("utf-8") tmp = docx_path.with_suffix(".docx.tmp") with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as z: for nombre, datos in piezas.items(): z.writestr(nombre, datos) shutil.move(str(tmp), str(docx_path)) return n if __name__ == "__main__": import sys if len(sys.argv) < 2: print("uso: python doc_convert.py path/al/archivo.doc", file=sys.stderr) sys.exit(2) res = doc_to_docx(Path(sys.argv[1]), force="--force" in sys.argv) print(f"OK: {res}" if res else "FALLO (¿Word/pywin32?)") sys.exit(0 if res else 1)