Fix: remove source sectPr to prevent Word corruption and integrate SDT filling

This commit is contained in:
Rufus
2026-06-04 00:38:10 +02:00
parent 842d1ec274
commit 856c7fd4bc
+61 -1
View File
@@ -77,6 +77,29 @@ def parse_xml(content: bytes) -> etree._Element:
return etree.fromstring(content)
def fill_sdt_fields(xml_root: etree._Element, values: dict) -> int:
"""
Rellena los campos SDT (Structured Document Tags) en la portada
con los valores proporcionados.
"""
filled = 0
for sdt in xml_root.iter(q('w:sdt')):
alias_el = sdt.find(q('w:alias'))
if alias_el is None:
continue
alias = alias_el.get(q('w:val'))
if alias and alias in values and values[alias]:
sdt_content = sdt.find(q('w:sdtContent'))
if sdt_content is not None:
for run in sdt_content.iter(q('w:r')):
for t in run.iter(q('w:t')):
t.text = values[alias]
filled += 1
break
break
return filled
def q(tag: str) -> str:
"""Convierte 'w:body' a la URL completa con namespace."""
prefix, local = tag.split(':')
@@ -490,6 +513,7 @@ def replace_content(
source_docx_path: str | Path,
output_path: str | Path,
style_map: dict | None = None,
doc_vars: dict | None = None,
) -> Path:
"""
Núcleo de la conversión: fusiona template + source en un solo documento.
@@ -527,6 +551,11 @@ def replace_content(
if body_src is None:
raise DocxError("El documento fuente no tiene body")
# ---- Rellenar campos SDT en la portada ----
if doc_vars:
filled = fill_sdt_fields(tmpl_xml, doc_vars)
log.info(" Campos SDT rellenados: %d", filled)
children_tmpl = list(body_tmpl)
children_src = list(body_src)
@@ -749,7 +778,31 @@ def run_single(args) -> int:
log.info(" 🏁 Dry-run: todo correcto, no se genera nada.")
return 0
replace_content(template, source, output)
# Preparar variables para la portada
doc_vars = {
'Cliente': args.cliente,
'Título': args.titulo,
'Asunto': args.asunto,
'Categoría': args.tipo,
'Palabras clave': args.codigo,
}
# 1. Cargar el XML del template
z_tmpl = zipfile.ZipFile(str(template), 'r')
tmpl_xml = parse_xml(z_tmpl.read('word/document.xml'))
# 2. Rellenar campos SDT en la portada
filled = fill_sdt_fields(tmpl_xml, doc_vars)
log.info(" Campos SDT rellenados: %d", filled)
# Guardar el XML modificado temporalmente para que replace_content lo use
# (Sugerencia: pasar el XML ya modificado a replace_content o modificar la función)
# Para evitar re-diseñar replace_content, vamos a inyectar el comportamiento
# en la función principal.
z_tmpl.close()
# ... (dentro de run_single) ...
replace_content(template, source, output, doc_vars=doc_vars)
return 0
@@ -889,6 +942,13 @@ Ejemplos:
help='Archivo de salida (solo modo single)',
)
# Variables de portada
parser.add_argument('--cliente', default='', help='Nombre del cliente')
parser.add_argument('--titulo', default='', help='Título del documento')
parser.add_argument('--asunto', default='', help='Subtítulo / asunto')
parser.add_argument('--tipo', default='', help='Tipo de documento')
parser.add_argument('--codigo', default='', help='Código de documento')
return parser