Fix: remove source sectPr to prevent Word corruption and integrate SDT filling
This commit is contained in:
+61
-1
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user