234 lines
6.6 KiB
Python
234 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Exportador de contratos a .docx con formato forense mexicano.
|
|
Convierte los archivos _v2.md a documentos Word con:
|
|
- Times New Roman 12
|
|
- Interlineado 1.5
|
|
- Sangría 1.27 cm en cuerpo
|
|
- Título centrado y MAYÚSCULAS
|
|
- Alineación justificada
|
|
|
|
Uso:
|
|
python3 exportar.py # Exporta todos los _v2.md
|
|
python3 exportar.py contrato-cdmx-ejemplo_v2.md # Exporta uno específico
|
|
"""
|
|
|
|
import re
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from docx import Document
|
|
from docx.shared import Pt, Cm
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
from docx.oxml.ns import qn
|
|
except ImportError:
|
|
print("ERROR: Se requiere python-docx. Instalar con: pip install python-docx")
|
|
sys.exit(1)
|
|
|
|
|
|
def configure_font(run, size=12, bold=False, italic=False, name="Times New Roman"):
|
|
run.font.name = name
|
|
run.font.size = Pt(size)
|
|
run.bold = bold
|
|
run.italic = italic
|
|
# Forzar fuente en XML para compatibilidad
|
|
rpr = run._element.get_or_add_rPr()
|
|
rFonts = rpr.find(qn('w:rFonts'))
|
|
if rFonts is None:
|
|
rFonts = run._element.makeelement(qn('w:rFonts'), {})
|
|
rpr.insert(0, rFonts)
|
|
rFonts.set(qn('w:eastAsia'), name)
|
|
rFonts.set(qn('w:cs'), name)
|
|
|
|
|
|
def set_paragraph_spacing(paragraph, line_spacing=1.5):
|
|
pf = paragraph.paragraph_format
|
|
pf.line_spacing = line_spacing
|
|
pf.space_before = Pt(0)
|
|
pf.space_after = Pt(6)
|
|
|
|
|
|
def add_formatted_paragraph(doc, text, alignment=WD_ALIGN_PARAGRAPH.JUSTIFY,
|
|
bold=False, size=12, indent_cm=0, space_after=6):
|
|
"""Agrega un párrafo con formato estándar."""
|
|
p = doc.add_paragraph()
|
|
p.alignment = alignment
|
|
set_paragraph_spacing(p)
|
|
pf = p.paragraph_format
|
|
pf.space_after = Pt(space_after)
|
|
if indent_cm > 0:
|
|
pf.first_line_indent = Cm(indent_cm)
|
|
|
|
run = p.add_run(text)
|
|
configure_font(run, size=size, bold=bold)
|
|
return p
|
|
|
|
|
|
def add_separator(doc):
|
|
"""Agrega una línea separadora."""
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
run = p.add_run("— — —")
|
|
configure_font(run, size=12, bold=False)
|
|
return p
|
|
|
|
|
|
def add_signature_block(doc, lines):
|
|
"""Agrega un bloque de firmas."""
|
|
for line in lines:
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
|
set_paragraph_spacing(p)
|
|
run = p.add_run(line)
|
|
configure_font(run, size=12, bold=False)
|
|
|
|
|
|
def markdown_to_docx(md_path, output_dir=None):
|
|
"""Convierte un archivo markdown de contrato a .docx."""
|
|
print(f"Procesando: {md_path}")
|
|
|
|
with open(md_path, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
doc = Document()
|
|
|
|
# Configurar márgenes
|
|
for section in doc.sections:
|
|
section.top_margin = Cm(2.5)
|
|
section.bottom_margin = Cm(2.5)
|
|
section.left_margin = Cm(3)
|
|
section.right_margin = Cm(2.5)
|
|
|
|
lines = content.split("\n")
|
|
i = 0
|
|
|
|
while i < len(lines):
|
|
line = lines[i].strip()
|
|
i += 1
|
|
|
|
if not line:
|
|
continue
|
|
|
|
# Separador de secciones
|
|
if line == "- - -":
|
|
add_separator(doc)
|
|
continue
|
|
|
|
# Título principal (primera línea en MAYÚSCULAS)
|
|
if line == line.upper() and len(line) > 10 and not line.startswith("**") and i < 10:
|
|
add_formatted_paragraph(
|
|
doc, line,
|
|
alignment=WD_ALIGN_PARAGRAPH.CENTER,
|
|
bold=True,
|
|
size=14,
|
|
space_after=12
|
|
)
|
|
continue
|
|
|
|
# Encabezados de secciones: D E C L A R A C I O N E S, C L Á U S U L A S
|
|
if line.startswith("**") and line.endswith("**") and i < len(lines):
|
|
clean = line.strip("*")
|
|
add_formatted_paragraph(
|
|
doc, clean,
|
|
alignment=WD_ALIGN_PARAGRAPH.CENTER,
|
|
bold=True,
|
|
size=12,
|
|
space_after=10
|
|
)
|
|
continue
|
|
|
|
if line.startswith("D E C L A R A C I O N E S") or line.startswith("C L Á U S U L A S"):
|
|
add_formatted_paragraph(
|
|
doc, line,
|
|
alignment=WD_ALIGN_PARAGRAPH.CENTER,
|
|
bold=True,
|
|
size=12,
|
|
space_after=10
|
|
)
|
|
continue
|
|
|
|
# Líneas en negritas con ** (títulos de cláusulas como PRIMERA. — OBJETO)
|
|
if line.startswith("**") and line.endswith("**") and len(line) > 4:
|
|
clean = line.strip("*")
|
|
add_formatted_paragraph(
|
|
doc, clean,
|
|
alignment=WD_ALIGN_PARAGRAPH.LEFT,
|
|
bold=True,
|
|
size=12,
|
|
space_after=6
|
|
)
|
|
continue
|
|
|
|
# Negritas dentro de la línea (formato **texto**)
|
|
if "**" in line:
|
|
add_formatted_paragraph(
|
|
doc, line.replace("**", ""),
|
|
alignment=WD_ALIGN_PARAGRAPH.JUSTIFY,
|
|
bold=False,
|
|
size=12,
|
|
indent_cm=1.27,
|
|
space_after=6
|
|
)
|
|
continue
|
|
|
|
# Bloque de ciudad y fecha
|
|
if " a " in line and (" de " in line) and i < 5:
|
|
add_formatted_paragraph(
|
|
doc, line,
|
|
alignment=WD_ALIGN_PARAGRAPH.LEFT,
|
|
bold=False,
|
|
size=12,
|
|
space_after=6
|
|
)
|
|
continue
|
|
|
|
# Texto normal — cuerpo del contrato
|
|
add_formatted_paragraph(
|
|
doc, line,
|
|
alignment=WD_ALIGN_PARAGRAPH.JUSTIFY,
|
|
bold=False,
|
|
size=12,
|
|
indent_cm=1.27,
|
|
space_after=6
|
|
)
|
|
|
|
# Guardar archivo
|
|
md_path = Path(md_path)
|
|
if output_dir:
|
|
output_dir = Path(output_dir)
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
output_path = output_dir / f"{md_path.stem}.docx"
|
|
else:
|
|
output_path = md_path.with_suffix(".docx")
|
|
|
|
doc.save(str(output_path))
|
|
print(f" ✓ Exportado: {output_path}")
|
|
return output_path
|
|
|
|
|
|
def main():
|
|
# Buscar archivos _v2.md
|
|
script_dir = Path(__file__).parent
|
|
md_files = list(script_dir.glob("*_v2.md"))
|
|
|
|
if not md_files:
|
|
print("No se encontraron archivos *_v2.md en el directorio.")
|
|
return
|
|
|
|
# Crear directorio de salida
|
|
output_dir = script_dir / "docx_output"
|
|
|
|
for md_file in md_files:
|
|
try:
|
|
markdown_to_docx(md_file, output_dir=output_dir)
|
|
except Exception as e:
|
|
print(f" ✗ Error en {md_file}: {e}")
|
|
|
|
print(f"\nExportación completa. Archivos en: {output_dir}/")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|