d1a3d58a6b
Mejoras transversales del motor de render (no del contenido de capítulos): 1. Fix negrita pisa texto (PDF): _place_rich_lines mide el ancho REAL de cada span con las métricas de fuente del renderer (peso correcto) en vez del grid de ancho medio; negrita y normal en la misma línea ya no se solapan. 2. Zebra striping: filas pares sombreadas (#f6f8fa) en DataTable (PDF + PPTX), coherente al partir tablas largas (índice de fila lógico, no por página). 3. Keep-together: bloque Group nuevo; el renderer mide el grupo entero y lo mueve completo a la página/slide siguiente si no cabe, y encoge la figura (height_in) para dejar sitio a su título y texto. num_distr lo usa. 4. Caption siempre visible en toda figura PPTX (fallback al heading); la figura reserva el alto de su caption para que ambos quepan en el mismo slide. 5. Portada construida al final (con resumen agregado del análisis vía ctx['document_summary']) pero colocada primera por build_document. 6. Glosario: capítulo nuevo (último) + GlossaryCollector en ctx; los capítulos registran términos y marcan apariciones con [[term:key]]...[[/term]]. Links clicables reales: PDF (PyMuPDF, link GOTO) y PPTX (slide-jump nativo). Enganchado "entropía" en cat_distr como ejemplo end-to-end. Funciones reutilizables delegadas a fn-constructor (tag eda): - add_pdf_internal_links_py_datascience (PyMuPDF) - pptx_link_run_to_slide_py_datascience (slide-jump) Contrato docs/automatic_eda_contract.md actualizado (§1/§3/§5 + §11 nueva) con la API de glosario, keep-together y zebra para la siguiente fase. PyMuPDF declarado en pyproject. Suite verde (90 tests); golden titanic verificado. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.8 KiB
5.8 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | params | output | tested | tests | test_file_path | file_path | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| add_pdf_internal_links | function | py | datascience | 1.0.0 | impure | def add_pdf_internal_links(pdf_path: str, links: list) -> dict | Postprocesa un PDF YA escrito insertando link annotations internos de tipo GOTO ('ir a') con PyMuPDF (import fitz). Pensado para PDFs generados por matplotlib PdfPages, que NO soporta hyperlinks internos: tras escribir el PDF se reabre y, por cada entrada de `links`, se añade una anotacion clicable desde un rectangulo de una pagina origen (src_page + src_rect en puntos top-left) hasta un punto de una pagina destino (dst_page + dst_point). Caso de uso tipico del grupo eda: hacer clicables los terminos de un AutomaticEDA que apuntan a su entrada en el glosario al final del documento. Estilo dict-no-throw: NUNCA lanza; valida cada link y SALTA (n_skipped++) los malformados o fuera de rango en vez de fallar. Guarda de forma segura escribiendo a un temporal en el mismo directorio y haciendo os.replace atomico (evita corromper el original). Devuelve {status:ok,n_links,n_skipped} o {status:error,error}; si pymupdf no esta disponible o el archivo no existe devuelve status error. |
|
false | error_go_core |
|
dict (NUNCA lanza): en exito {"status":"ok","n_links":int,"n_skipped":int} con n_links = anotaciones GOTO insertadas y n_skipped = entradas invalidas saltadas. En fallo {"status":"error","error":str}: pymupdf no disponible, pdf_path no es str / no existe, links no es lista, o cualquier excepcion global (el PDF original queda intacto porque el replace solo ocurre tras un save correcto). | true |
|
python/functions/datascience/add_pdf_internal_links_test.py | python/functions/datascience/add_pdf_internal_links.py |
Ejemplo
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience import add_pdf_internal_links
# Tienes un PDF ya escrito por matplotlib PdfPages (sin hyperlinks internos).
# Quieres que el texto "Margen bruto" de la pagina 0 (rectangulo en puntos
# top-left) salte a su entrada del glosario en la ultima pagina (indice 7).
res = add_pdf_internal_links(
"reports/eda.pdf",
[
{"src_page": 0, "src_rect": [72, 120, 180, 134], "dst_page": 7, "dst_point": [72, 200]},
{"src_page": 0, "src_rect": [72, 140, 180, 154], "dst_page": 7, "dst_point": [72, 260]},
],
)
# res == {"status": "ok", "n_links": 2, "n_skipped": 0}
Cuando usarla
Justo DESPUES de escribir un PDF con matplotlib PdfPages (o cualquier motor
que no genere hyperlinks internos) cuando necesitas que ciertos terminos o
referencias sean clicables y salten a otra pagina del mismo documento — el caso
canonico es enlazar los terminos de un AutomaticEDA con su entrada de glosario
al final. Es un paso de postproceso: primero generas el PDF y calculas en que
rectangulo quedo cada termino (en puntos PDF), luego pasas esa lista a esta
funcion para inyectar las anotaciones GOTO.
Gotchas
- Impura — reescribe el archivo IN SITU. El PDF en
pdf_pathse reemplaza por la version con los links. El guardado es seguro: escribe a un temporal.<base>.tmp_linksen el MISMO directorio y haceos.replaceatomico tras cerrar el documento, asi un fallo a mitad no corrompe el original. Aun asi, conserva una copia si el PDF es valioso. - Sistema de coordenadas: puntos top-left, igual que matplotlib. PyMuPDF y
matplotlib (PdfPages) usan ambos PUNTOS PDF (1/72") con el origen ARRIBA-
IZQUIERDA, asi que los rectangulos/puntos COINCIDEN: el
src_rectque calcules con la geometria de la figura matplotlib se pasa tal cual, sin invertir el eje Y. (Ojo: el espacio de datos de matplotlib SI tiene el origen abajo; lo que coincide es el espacio de la PAGINA en puntos.) - Indices de pagina 0-based.
src_page/dst_pageson indices base 0 (la primera pagina es 0). Fuera del rango[0, page_count)el link se SALTA (cuenta enn_skipped), no lanza. - dict-no-throw, validacion por-link. Las entradas malformadas (no dict,
page fuera de rango,
src_rectsin 4 numeros,dst_pointsin 2 numeros) se saltan individualmente e incrementann_skipped; el resto de links validos se insertan igual. La funcion solo devuelve{status:error}ante fallos globales (pymupdf ausente, archivo inexistente,linksno es lista). error_type: error_go_corees metadata del registry, no comportamiento. Toda funcion impura debe declararlo y el indexer lo exige, pero el codigo NUNCA lanza esa excepcion: degrada al dict de estado.- Requiere PyMuPDF (
import fitz). Si no esta instalado devuelve{"status":"error","error":"pymupdf no disponible: ..."}. En el registry el venvpython/.venvya lo trae.