upload
This commit is contained in:
233
exportar.py
Normal file
233
exportar.py
Normal file
@@ -0,0 +1,233 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user