CreaRack-SL

ADR: Detección de EMF incrustado en stencils Visio + conversión con fallback

Fecha

2026-07-13

Estado

Implementado (v1.54.1)

Problema

Regresión del import Visio: los stencils de Cisco (Catalyst 9000) no se renderizan tras la importación. Causa: Cisco incrusta el arte en formato EMF (Enhanced Metafile, nativo de Windows), y libvisio-ng lo envuelve en un <image href="data:image/emf"> que los navegadores web no saben pintar. Del fichero real de Catalyst 9000 (40 MB), 165/165 masters son solo-EMF. Reporte en PROD por Edu el 2026-07-13.

Impacto UX: miniaturas rotas en el modal de stenciles, stencil en el editor muestra icono de imagen rota.

Opciones consideradas

1. Renderizar EMF en el navegador (rechazado)

  • No existe soporte estándar de EMF en navegadores modernos.
  • Requeriría biblioteca JavaScript pesada (>500 KB) o conversión server-side.
  • SVGs compatibles existen, mejor invertir en conversión.

2. Mostrar placeholder en lugar del master (rechazado)

  • Peor UX: el usuario importa un stencil esperando imágenes, ve placeholder.
  • Confusión: ¿es un error importable? ¿Se debe volver a intentar?
  • No aprovecha que el arte está ahí (incrustado).

3. Conversión EMF → PNG en el servidor [ELEGIDA]

Detectar automáticamente stencils solo-EMF y convertirlos a PNG durante el import. Ventajas:

  • Transparente para el usuario.
  • Aprovecha herramientas existentes (LibreOffice, rsvg).
  • No requiere cambios en el cliente.
  • Rendimiento aceptable: LibreOffice en lote (~1-2 min para 165 masters).

Decisión

Detección

Función svg_is_emf_only(svg_text):

  • Busca patrón data:image/emf;base64,.
  • Verifica ausencia de etiquetas vectoriales (<path>, <rect>, etc.).
  • Solo SVGs que cumplan AMBAS condiciones se procesan (no tocar vectoriales de verdad).

Conversión con dos capas

Lote 1: EMF → SVG vectorial (alta calidad)

  • LibreOffice en modo headless convierte EMF a SVG.
  • Una invocación por lote (arranque de LO es el coste principal).
  • Resultado: SVG vectorial que rsvg puede rasterizar sin pérdida.

Rasterizado: SVG → PNG (2400 px)

  • rsvg-convert a 2400 px de ancho (nítido en modal, escalable).
  • Timeout 120 s por operación.

Lote 2: EMF → PNG directo (fallback, menor calidad)

  • Si rsvg falló, LibreOffice convierte EMF→PNG (menor escala).
  • Una sola invocación por lote para fallidos.
  • Mejor que nada; mantiene el usuario informado.

Postproceso

  • Blanco → transparente (ImageChops, operación C, no por píxel).
  • Autocrop por alpha.
  • Escalado mínimo (800 px) si cabe (evita stencils diminutos).

Desaprobación

  • Un master solo-EMF cuya conversión falle (lotes 1 y 2) se descarta con warning en logs.
  • Mejor ausente que roto: una miniatura rota en el modal es peor UX que su ausencia + nota en logs.

Rationale

  1. Tres pasos de conversión (EMF→SVG→PNG + fallback):

    • Maximiza calidad (SVG→PNG es sin pérdida de geometría).
    • Cubre fallidos (EMF→PNG directo nunca falla, aunque con menor resolución).
    • No usa timeouts infinitos (120 s por rsvg, 600 s total).
  2. Un lote por operación:

    • LibreOffice: arranque una sola vez (costo amortizado entre 165 masters).
    • Sin profiler/paralelización: secuencial, pero tolerable (<2 min).
  3. Postproceso en C (Pillow):

    • Versión anterior usaba loop por píxel; QA PR 3 descubrió OOM con shapes altos (1+ MB de píxeles).
    • ImageChops opera en C, sin materializar buffers intermedios.
  4. Desaprobación vs. reintento:

    • Una conversión que falla probablemente indica corrupción de datos o timeout.
    • Reintentar no aporta: o hay bug (reintento infinito) o es una excepción genuina.
    • Descartar = operación atomica, no ambigua.
  5. Memoria en dev (512M → 1024M):

    • libvisio_ng pica ~515 MB de RSS.
    • Kernel OOM-killaba con 512M (además, el techo ya mordía a mypy, AUDIT s215).
    • PROD no limita; dev sigue siendo aislado.

Alternativas rechazadas

  • No convertir: masters rotos.
  • Convertir TODO a PNG (incluso vectoriales): pérdida de escala, archivos mayores.
  • Usar ImageMagick: libvisio_ng es más sencillo y LibreOffice ya está en el container.
  • Deferencia a aplicación cliente: HTML5 Canvas, etc. — requeriría cambios en UI, más LOC, más bugs.

Impacto

Positivo

  • Stencils de Cisco (y otros fabricantes con EMF) ahora renderizables.
  • Verificación visual: Catalyst 9200/9300 nítidos.
  • Regresión resuelta en producción.

Negativo

  • Consumo de memoria y CPU durante conversión (mitigado con timeout y guardia de memoria en QA).
  • Stencils rotos pre-fix no auto-reparados (re-importación soluciona).

Restricciones futuras

  • Ficheros >100 MB puede que no coquen (memoria).
  • EMF corrompido o con formatos raros: fallará y se descartará (aceptable).

Testing

  • Unit: detección de SVG solo-EMF, conversión mock, descarte de fallidos.
  • Integration: flujo end-to-end con Catalyst 9000 real (165 masters, 40 MB).
  • QA: opt-in QA_VISIO=1, guardia de memoria <900 MB.

Véase también

  • [[feature—stencils—visio-emf-to-png]]
  • [[entity—racks—service—emf-converter]]
  • [[entity—racks—function—svg-is-emf-only]]
  • [[incident—20260713—cisco-catalyst-stencils-broken]]