¿Qué hace swiss-pdf?
Convierte archivos md / docx / txt / html en un PDF A4 con estilo de diseño suizo internacional.
Fondo blanco, texto negro, líneas divisorias ultrafinas, números de sección grandes, números monoespaciados, y un único color rojo #E5484D para énfasis.
Los tokens de diseño provienen de un libro de estilo suizo (números de sección grandes, líneas divisorias ultrafinas, un solo color de acento), adaptado a un flujo de paginación automática, por lo que funciona con documentos de cualquier longitud directamente.
Cómo usar
Método 1: Arrastrar y soltar (recomendado)
Arrastra el archivo a swiss-pdf.bat → se genera un PDF con el mismo nombre en el mismo directorio, junto con una copia xxx.pdf-src.html (fuente HTML para revisión).
Método 2: Línea de comandos
python make.py archivo_entrada # Genera PDF
python make.py archivo_entrada --html # Solo genera HTML (se guarda como xxx.html, se puede abrir en el navegador e imprimir manualmente)
python make.py archivo_entrada -o salida.pdf
python make.py a.md b.docx c.txt # Procesamiento por lotes
python make.py archivo_entrada --no-cover --no-toc
python make.py archivo_entrada --title "Título" --kick "Nota al margen" --date 2026.11.09
Formatos de entrada soportados
| Formato | Método de conversión | Descripción |
|---|---|---|
.md | pandoc | Soporta YAML front-matter, tablas GFM, listas, bloques de código, imágenes |
.docx | pandoc | Estilos de título → títulos de sección; tablas → tabla con estilos; imágenes extraídas automáticamente |
.txt | Analizador interno | Párrafos separados por líneas en blanco; líneas cortas numeradas (一、/ 1. / 第X章) → títulos de sección |
.html | Analizador interno | Toma <body>, elimina script/style; <title> como título del documento |
Portada: YAML front-matter
Escrito al principio del archivo md (otros formatos usan parámetros como --title):
---
title: Libro Blanco de Seguridad en Ciclismo Urbano
subtitle: Observaciones y recomendaciones basadas en una encuesta en tres ciudades
kick: Investigación en Gestión del Tráfico
en: Urban Cycling Safety White Paper
slug: seguridad-ciclismo-urbano
nature: Versión Pública
project: Programa de Estudio de Sistemas de Movilidad Lenta
party_a: Nombre del Cliente
party_b: Nombre del Proveedor
stage: Investigación → Redacción → Aprobación
source: 01_Registros de observación 02_Resúmenes de entrevistas 03_Plan
date: 2026.11.10
version: v2.1
badges: Público; !Importante; Con Datos
---
badgesse separa con;, los prefijados con!son fondo negro con texto blanco.- Si no se completa
date, se usa la fecha actual automáticamente. - Funciona sin front-matter; la portada solo muestra el título + fecha + espacio para versión.
Cuerpo del texto: Siete tipos de bloques de estilo
En markdown, se envuelven con ::: (cada línea dentro del bloque = un elemento):
::: note
Nota complementaria, aclaración de alcance. Fondo gris.
:::
::: quote
Cita de conclusión clave, con línea vertical izquierda de 3pt.
:::
::: inverse
Bloque de fondo negro con texto blanco, para la frase más importante.
:::
::: warn
Advertencia con borde rojo, único lugar donde se permite color.
:::
::: stat
4,280 | Total de muestras observadas || 24 | Puntos de observación || 3 | Ciudades cubiertas
:::
::: check
Intersección de hora punta matutina (llegar antes de las 7:30)
Tramos de carretera iluminados por la noche (necesitan luz de relleno)
:::
::: kv
Páginas | Plan A 20 páginas / Plan C 32 páginas
Recomendación | Plan C, pero necesita completar datos de la segunda ciudad
:::
stat usa número | descripción por celda, || separa celdas. kv usa clave | valor.
Comportamiento automático
| Comportamiento | Regla |
|---|---|
| Numeración de secciones | # Título de nivel 1 → número grande 00/01/02…; si el título ya incluye numeración como «01 » o «一、», se mantiene |
| Tabla de contenidos | Solo se genera si hay al menos 5 elementos entre secciones (#) y subsecciones (##); de lo contrario se omite la página; los títulos de sección tienen anclajes enlaces dentro del PDF |
| Tablas | Si el número de columnas es ≥ 6, se reduce el tamaño de la fuente (table.wide); las celdas con solo números se alinean a la derecha y usan fuente monoespaciada |
| Encabezado y pie de página | La portada no tiene encabezado/pié; en el cuerpo, el encabezado izquierdo muestra el título del documento, y el pie derecho muestra «Página i / Total N» |
| Impresión | @page A4, márgenes 14/15/13mm; los títulos no se separan del texto del párrafo, las filas de las tablas no se rompen entre páginas |
Procesamiento adicional de fuentes HTML (complementos verificados)
Cuando la fuente es una «página web con diseño propio», esta habilidad aplica automáticamente las siguientes normalizaciones para evitar números sueltos o títulos duplicados después de reordenar:
| Estructura de fuente | Procesamiento |
|---|---|
<div class="sec-head"><span class="sec-num">3</span><h2>Título</h2></div> | Se convierte en el encabezado de sección .sec de la plantilla (el número se toma de span, el título de h2) |
<h1>03 Título</h1> / <h1>Título</h1> | Igual que arriba; si no hay número, se autoincrementa según el orden de aparición |
Párrafo de solo números <p>0</p> | Se descarta (común en diseños donde el número de sección se usa como decoración) |
<header class="masthead"> / <nav> | Se descarta (la portada ya se genera por separado; los kickers/títulos grandes/tablas de contenido duplicados contaminarían el cuerpo) |
Secciones <section> | Se mantienen como contenedores de sección; el título aún necesita sec-head o h1 para aparecer en la tabla de contenidos |
<div id="xxx" class="chart"> + echarts | Se captura la pantalla del gráfico con un navegador sin cabeza y se incrusta como PNG; antes de la captura se espera networkidle + 2.5s |
Requisito previo para la captura de gráficos: si echarts se carga desde una CDN, se necesita conexión a internet; en entornos sin conexión, incrusta echarts.min.js directamente en la fuente.
Fuentes y puntuación
Fuentes: El repositorio incluye Source Han Sans, incrustado con @font-face
La carpeta fonts/ del repositorio contiene dos archivos; make.py los incrusta como base64 en el <style> al generar el HTML,
registrándolos como la familia de fuentes Swiss Sans SC:
| Archivo | Peso |
|---|---|
SourceHanSansCN-Regular.subset.ttf | 400 |
SourceHanSansCN-Bold.subset.ttf | 700 |
El subconjunto abarca aproximadamente 7100 caracteres (ASCII + signos de puntuación chinos e ingleses + todos los 6763 caracteres chinos de GB2312). El conjunto de caracteres se encuentra en fonts/charset.txt.
Ventaja: En una máquina que no tenga Source Han Sans instalada, la salida es completamente consistente; el usuario no necesita instalar la fuente.
El usuario puede reemplazar o descargar la fuente, tres opciones (consulta fonts/README.md):
- Reemplazar archivos (recomendado): Renombra tu propio TTF con los nombres de los archivos anteriores y sobrescríbelos. Primero, crea un subconjunto, de lo contrario el HTML se llevará el archivo completo de 10 MB:
pyftsubset tu-fuente-Regular.ttf --text-file=fonts/charset.txt --output-file=fonts/SourceHanSansCN-Regular.subset.ttf - Eliminar la carpeta
fonts/: Simake.pyno detecta los archivos, omite@font-facey recurre a la pila de fuentes del sistema en CSS (Source Han Sans SC/CN → Noto Sans SC → Noto Sans CJK SC → 梦源黑体 CN → PingFang SC → Segoe UI Symbol). - Solo modificar CSS: Cambia el primer nombre de fuente en
--sansal principio detemplates/swiss.css.
⚠️ Debe ser .ttf (contornos TrueType), no .otf (CFF). Chromium, al imprimir PDF, degrada las fuentes web CFF a instrucciones de glifo Type3 (cada glifo almacena una ruta de dibujo), multiplicando el tamaño. El mismo documento de 4 páginas:
OTF → 328 KB / 205 Type3; TTF → 79 KB / 1 Type3.
Microsoft YaHei en Chromium carece de glifos para · (U+00B7), → ↓ ≤ ≥ y otros símbolos, que se muestran como cuadrados, por lo que se ha eliminado de la pila de fuentes principal.
Comillas: Se fuerza el uso de comillas curvas estándar nacionales en todo el texto
convert.normalize_quotes() se encarga de esto en el último paso de la conversión, cubriendo títulos, portada, encabezados, tabla de contenidos, tablas y bloques de estilo:
| Entrada | Salida |
|---|---|
「」 | “” |
『』 | ‘’ |
Comillas rectas " ' en código | Markdown pasa por pandoc +smart y las convierte automáticamente a comillas curvas; dentro de los bloques de código <code> / <pre> no se modifican |
La implementación solo reemplaza 4 puntos de código (U+300C/D/E/F → U+201C/D/8/9), sin tocar atributos de etiquetas HTML ni CSS/JS.
Registros de ejecución verificados
| Entrada | Tamaño | Páginas | Tiempo |
|---|---|---|---|
| md | 29 KB | 15 | ~6s |
| html | 105 KB + 2 gráficos echarts | 21 | ~15s |
| docx | Estilos de título + tabla | 2 | ~6s |
txt (samples/) | 1.2 KB | 2 | ~5s |
| md documento largo (regresión) | 71 KB | 21 | ~24s |
Dependencias del entorno
| Dependencia | Estado actual | Si falta |
|---|---|---|
| pandoc (≥2.19) | Ejecuta pandoc -v para confirmar | No se pueden convertir md/docx |
| playwright + chromium | O Chrome / Edge del sistema | No se puede renderizar PDF |
| python-docx (opcional) | Solo para análisis auxiliar | No afecta el flujo principal |
Para especificar un navegador: set SWISS_PDF_CHROME=<ruta a chrome.exe>
Estructura del proyecto
swiss-pdf/
├── SKILL.md Este archivo
├── swiss-pdf.bat Acceso por arrastrar y soltar
├── make.py Flujo principal: convertir → ensamblar → renderizar
├── convert.py Convertir cada formato → fragmento HTML + sintaxis de bloques
├── templates/
│ └── swiss.css Todos los estilos (tokens de diseño + reglas de impresión)
└── samples/ Ejemplos de muestra en tres formatos
Límites (lo que no puede hacer)
- Paginación manual como en un libro de lujo (colocación cuidadosa del contenido en cada página, diseño a doble página) la plantilla no lo puede hacer; aún se necesita escribir HTML a mano. El objetivo de esta habilidad es «redactar documentos comunes rápidamente».
- Maquetación de ilustraciones: depende del tamaño de las imágenes en el documento original; no se realizan recortes ni sangrados.
- Numeración de páginas de la tabla de contenidos a la derecha: no implementado (los elementos dentro de la página no pueden obtener el número de página); los títulos de sección tienen enlaces que se pueden hacer clic, y el pie de página muestra «Página i / Total N».
- Los bloques de tabla demasiado cortos pueden moverse a la página siguiente: para evitar que las tablas de valor clave se dividan, el bloque
.kvno se puede dividir entre páginas. - Estilos complejos de Word (cuadros de texto, WordArt, listas con numeración multinivel) se degradarán al ser convertidos por pandoc.