Generación de Informes PDF Profesionales en Chino
🚨 Lecciones Aprendidas de Dos Correcciones del Usuario (2026-08-20, Prioridad Máxima, Prohibido Repetir)
- Maquetación Compacta: Prohibido añadir una página por cada sección con solo un poco de contenido. Solo agregar un PageBreak después de la portada; el cuerpo debe fluir naturalmente. El contenido que antes ocupaba 6 páginas debe comprimirse a 4.
- Fuente Grande (legible en móvil, el cliente lo ve en el móvil): Cuerpo ≥16pt, Tablas ≥15pt, H2 ≥17pt, H1 ≥21pt, Título ≥30pt. El cuerpo a 14pt y las tablas a 12pt son demasiado pequeños; el cliente no puede leerlos en el móvil y el usuario lo criticará en el acto.
- Estas dos reglas ya están escritas en el estándar de maquetación de memoria, pero al ejecutar aún se tiende a usar 14pt/12pt + saltos de página por capítulo – antes de generar el script, revisa las constantes de tamaño de fuente y la cantidad de PageBreaks.
⭐ Estándar de Maquetación General (2026-08-18, Aprobado por el Usuario, Válido para PDF/Word)
Esta lógica de maquetación es el estándar; todos los documentos PDF y Word futuros deben seguirla:
- A4 Vertical (210×297mm) – No usar horizontal/otros tamaños
- Fuente Grande, legible en móvil: Cuerpo ≥14pt, Tablas ≥12.5pt, H1 ≥20pt, Título ≥32pt
- Tablas con salto de línea inteligente sin desbordamiento: Las celdas usan Paragraph + wordWrap="CJK"
- Contenido de página equilibrado: No usar saltos de página forzados; KeepTogether para agrupar inteligentemente. Tras generar, revisar el número de caracteres por página (excepto portada/contraportada, todas deben estar equilibradas)
- Maquetación Compacta: Márgenes 18-20mm, padding de tabla 3-4, interlineado compacto, listo para imprimir directamente
Documentos Word con la misma lógica: A4 vertical, cuerpo ≥ Pequeño Cuatro / Cuatro (12-14pt), ancho de columna de tabla adaptativo sin desbordamiento, páginas equilibradas.
Condiciones de Activación
- El usuario/cliente pide "saca las conclusiones en PDF", "sube el PDF", "dame el estudio de viabilidad", "saca las conclusiones y envíamelas"
- Se necesita entregar un informe de análisis profesional con maquetación china (portada + tablas + pie de página + cuadro de conclusiones clave)
- Ejemplo de referencia: Informe de diligencia debida de Guoxun Robot (5 páginas, 02-Resultados del Proyecto/Informe de Viabilidad y Análisis de Debida Diligencia del Proyecto Guoxun Robot.pdf)
Entorno e Instalación
- Usar el intérprete de hermes venv:
python - Primera instalación:
$V -m pip install reportlab(ya instalado v5.0.0) - Fuente: Microsoft YaHei incluida en el sistema Windows, no es necesario descargar archivos de fuente
C:/Windows/Fonts/msyh.ttc(Regular, subfontIndex=0)C:/Windows/Fonts/msyhbd.ttc(Negrita)C:/Windows/Fonts/msyhl.ttc(Fina)
Flujo de Trabajo Estándar
- ⚠️ Regla de Entrega (2026-09-02, Usuario Explicitó, Prioridad Máxima): Después de generar el PDF, debes enviarlo al chat de Feishu con
MEDIA:, nunca solo guardarlo en la base de conocimiento / dar la ruta / solo puntos clave. Palabras del usuario: "El documento PDF debes enviarlo al chat de Feishu aquí, si no, ¿cómo voy a verlo? Tiene que ser una regla fija."- El usuario lo ve en el móvil; solo se ve si toca el archivo en el mensaje de Feishu; guardarlo en
02-Resultados del Proyecto/es solo una copia de seguridad, no es la entrega. - Formato de entrega:
<tu_directorio_de_salida>/<nombre_archivo>.pdf(ruta absoluta). - Después de enviar el PDF, adjuntar los puntos clave de las conclusiones (versión de 30 segundos). Primero enviar a Feishu, luego guardar en la base de conocimiento para revisión.
- El usuario lo ve en el móvil; solo se ve si toca el archivo en el mensaje de Feishu; guardarlo en
- Obtener Contenido: session_search para buscar análisis previos – el usuario suele responder a los adjuntos con "saca las conclusiones en PDF". Primero recupera el análisis/valoración anterior, no reescribas.
- Escribir Script: Copiar
templates/reportlab_chinese_template.py, solo reemplazar la zona de contenido (texto de portada, capítulos, datos de tablas). - Generar: Ejecutar con python → salida a Obsidian
Hermes工作成果/02-项目成果/(nombre: Informe de Viabilidad y Análisis de Debida Diligencia del Proyecto.pdf) - Verificar: Usar pymupdf para abrir y hacer get_text, confirmar número de páginas / renderizado chino / existencia de conclusiones al final.
- Entregar: Responder con la ruta
MEDIA:D:\...pdf+ puntos clave de conclusiones (versión de 30 segundos), sin rodeos.
⭐ Reglas de Colores (2026-09-02, Aprobado por el Usuario, Colores Favorables: Oro/Tierra – Prioridad Máxima)
Los ocho caracteres del usuario favorecen el oro y la tierra, evitan el agua (azul/negro) y el fuego (rojo/violeta). Todos los PDF deben usar la paleta de oro y tierra, prohibido usar azul. Las variables de color están en la palette a continuación.
- Título de Capítulo H1: Fondo oro metálico + texto blanco (
backColor=#C9A227,textColor=white,borderPadding=(6,8,6,8)) – Fondo dorado con texto blanco, no texto azul. - Encabezado de Tabla: Fondo oro metálico + texto blanco (
header_bg=#C9A227,FONTNAME=MSYHBD) – Fondo dorado con texto blanco. - Cuerpo: Negro puro
#1f1f1f. - Subtítulos / Líneas Separadoras: Oro tierra oscuro
#B8860B(sin rojo). - Bloques de Código: Oro tierra
#8a6d1f(sin azul). - Filas Alternas: Beige dorado claro
#FBF3DE(tono cálido, no gris). - Líneas de Cuadrícula: Oro tierra claro
#D9C9A3.
Paleta de Tonos Dorados (para reportlab)
C_DARK=colors.HexColor("#1f1f1f") # Cuerpo casi negro
C_RED=colors.HexColor("#B8860B") # Énfasis/Subtítulo – Oro Tierra Oscuro
C_BLUE=colors.HexColor("#C9A227") # Fondo de Encabezados/Títulos – Oro Metálico
C_LGRAY=colors.HexColor("#FBF3DE") # Filas Alternas – Beige Dorado Claro
C_MGRAY=colors.HexColor("#D9C9A3") # Líneas de Cuadrícula – Oro Tierra Claro
C_WHITE=colors.white
Énfasis en línea
S_CODE=st(..., textColor=colors.HexColor("#8a6d1f")); en línea<span color='#B8860B'>(no usar#c0392brojo).
Puntos Clave de Maquetación (Ya Encapsulados en la Plantilla)
- Portada: Título grande + subtítulo dorado + fecha/fuente + cuadro de conclusiones clave (fondo oro tierra oscuro con texto blanco)
- Cuerpo: H1 Capítulo (fondo oro metálico texto blanco MSYHBD 21pt) + H2 Subsección (17pt) + Cuerpo 15pt (2026-08-18 Indicación del Usuario: Legible en móvil, el tamaño debe ser grande)
- Tablas:
make_table()Encabezado fondo oro metálico texto blanco + filas alternas (ROWBACKGROUNDS) + grid + repeatRows=1, tamaño de letra de tabla 13pt - Pie de página: Callback onPage para dibujar nombre del informe + número de página
- Énfasis: Paragraph admite
<b>/<span color='#B8860B'>etiquetas en línea - Informes largos divididos en capítulos: PageBreak para separar portada del cuerpo
⚠️ Regla de Tamaño de Fuente (2026-08-18, Usuario Insistió): El PDF es para verse en el móvil, cuerpo ≥15pt, tablas ≥13pt, H1 ≥21pt, título ≥34pt. La plantilla original con 10.5pt/9.3pt es demasiado pequeña, no se ve en el móvil – todos los informes nuevos deben usar tamaños grandes. Que el tamaño grande aumente el número de páginas es normal (por ejemplo, de 5 a 8 páginas).
⭐ Reglas de Maquetación Inteligente (2026-08-18, Usuario Insistió, Amigable para Impresión) El contenido de las páginas debe estar equilibrado – no puede haber una página con solo 200 caracteres y otra con 800. Método:
- Usar pocos PageBreaks: Solo un salto de página forzado después de la portada; el cuerpo fluye naturalmente.
- Usar KeepTogether para envolver "título + contenido":
keep(título H2, tabla/párrafo)evita que las tablas se dividan en dos páginas y que los títulos queden huérfanos al final de la página. - Portada en página independiente: Elementos de portada + un PageBreak.
- Cada capítulo naturalmente separado por subsecciones: Título H2 + tabla/puntos clave, no forzar cambio de página por capítulo.
- Verificar el equilibrio: Tras generar, usar pymupdf para revisar el número de caracteres por página – en un informe normal, excepto portada/contraportada, todas las páginas deben tener un número similar de caracteres (ej. 600-900 caracteres). Si la diferencia es grande, hay que ajustar.
- Maquetación Compacta: Márgenes 20mm/16mm (no demasiado grandes), padding de tabla 4-5, interlineado leading=22 (cuerpo 14pt), reducir el desperdicio de espacio.
Errores Comunes (PITFALLS)
- ⛔ Prohibido usar Justificado Completo (TA_JUSTIFY) – El espaciado entre caracteres se ensancha (2026-09-03, el problema de legibilidad en móvil que más le importa al usuario):
TA_JUSTIFYde reportlab fuerza a cada línea a estirarse hasta el borde derecho en párrafos chinos. Cuando una línea tiene pocos caracteres, el espaciado entre ellos se ensancha enormemente, quedando muy feo. Todos los cuerpos/listas/párrafos deben usarTA_LEFTalineación a la izquierda.- Incorrecto:
b=dict(..., alignment=TA_JUSTIFY, ...) - Correcto:
b=dict(..., alignment=TA_LEFT, ...) - Verificación: Si el estilo de cuerpo/lista hereda
st()conalignment=TA_JUSTIFY, cámbialo a izquierda.
- Incorrecto:
- ⛔ Contenido de portada se desborda creando una página huérfana en blanco (2026-09-03): Todos los elementos de la portada (título principal + subtítulo + explicación + frase clave) deben caber en la página 1. Nunca permitas que el contenido de la portada se desplace a la página 2 formando "un pequeño fragmento de texto + gran espacio en blanco". Si la portada tiene demasiado contenido, elimina párrafos largos de introducción, comprime el espaciado de las listas para que quepa en 1 página. Entre capítulos, solo mantén un PageBreak después de la portada; el cuerpo fluye naturalmente.
- ⛔ Ajustar simultáneamente el tamaño de fuente y los márgenes: Al aumentar el tamaño de la fuente, los márgenes deben reducirse en paralelo (en esta sesión pasaron de 22mm a 17mm) para que cada página contenga más y no se generen páginas huérfanas. Cuerpo a 17pt + márgenes de 17mm es el punto de equilibrio probado; tamaño de letra de tabla 14.5pt.
- ⛔ KeepTogether envolviendo "tablas pequeñas con alineación personalizada" puede provocar errores de cálculo de altura → páginas huérfanas (2026-09-06 probado): Al usar KeepTogether para envolver "título + cuerpo + tabla de dos columnas de alineación personalizada (field_table)" + precio, reportlab estima incorrectamente la altura de ese bloque, a menudo empujando todo el bloque a la página siguiente, dejando la página anterior solo con "frase de aviso / última fila de la tabla" convirtiéndola en una página huérfana de unas decenas de caracteres (en esta sesión, la página 2 tenía solo 76 caracteres). Solución: ① Para el primer bloque "tabla de servicios + introducción", usa KeepTogether para envolver todo el head_block y comprime el tamaño de letra de la tabla / interlineado para asegurar que quepa en la página 1; ② Después de renderizar, usa pymupdf para ver el número de caracteres página por página. Las páginas con <120 caracteres son páginas huérfanas; comprime inmediatamente la tabla de la página anterior / elimina frases de aviso sobrantes.
- Distinción (2026-09-10 probado): Envolver solo "la tabla en sí" es preciso y debe hacerse; envolver bloques grandes como "título + cuerpo + tabla + precio" es propenso a errores. Haz que la función de fábrica de tablas devuelva uniformemente
return KeepTogether(t)(la tabla completa no se divide entre páginas), junto con mantener solo un PageBreak después de la portada. Probado: se corrigió un informe de 7 páginas (la página 5 tenía solo 77 caracteres) a 6 páginas, con 301/451/548/407/551/240 caracteres por página. Que una tabla se corte al dividir entre páginas, dejando 1–2 filas solas en una página, es la causa número uno de páginas huérfanas; si encuentras "una página solo con encabezado de tabla / última fila", verifica esto primero.
- Distinción (2026-09-10 probado): Envolver solo "la tabla en sí" es preciso y debe hacerse; envolver bloques grandes como "título + cuerpo + tabla + precio" es propenso a errores. Haz que la función de fábrica de tablas devuelva uniformemente
- ⛔ En tablas de dos columnas "etiqueta + contenido", la columna de etiquetas no debe ser demasiado estrecha – las etiquetas chinas se romperán en caracteres huérfanos (2026-09-06): Al usar una tabla de dos columnas para "qué puede hacer / hasta dónde llega / qué obtiene", si la columna de etiquetas es demasiado estrecha (ej. 32mm), "qué obtiene" se romperá en "qué obtie" + "ne", el carácter huérfano queda muy feo. Solución: El ancho de la columna de etiquetas debe ser > el espacio necesario para que la etiqueta más larga quepa en una línea (alrededor de 40mm), o acortar la etiqueta ("qué obtiene" → "beneficio").
- ⛔ El "." final de una oración larga puede romperse solo en la siguiente línea (2026-09-06): En campos largos como "hasta dónde llega / qué beneficio", el "." final puede ser empujado al principio de la siguiente línea al envolver. Solución: Cuando el valor del campo es largo y hay riesgo de salto de línea, elimina el punto final (o usa frases cortas que lo eviten). Mejor no tener punto que tener un punto huérfano.
- ⛔ "Habilidades personalizadas" deben escribirse correctamente (2026-09-06, corregido por el usuario): No confundir "personalizar/hacer skills" con "tareas programadas". Para la versión del cliente, usar uniformemente "personalización exclusiva de skills" y explicar coloquialmente "skill = una capacidad específica que la IA puede ejecutar directamente" (ver enterprise-ai-service-blueprint).
- Las fuentes
.ttcdeben tenersubfontIndex=0, de lo contrario dan error o se ven corruptas. - Para negrita, hay que registrar MSYHBD y establecer fontName – la etiqueta
<b>no aplica negrita silenciosamente si no hay una fuente de negrita registrada. - El escape de
<>se divide en dos casos (2026-09-10 probado y corregido): Los ángulos simples en el cuerpo (como "precisión<10mm") deben escaparse a</>; mientras que<b>,<span color=...>son etiquetas en línea y deben pasarse tal cual a Paragraph para que funcionen.- ⛔ Escritura fatal: Escapar todo el texto de la celda con
.replace('<','<')→ las negritas en las tablas se mostrarán literalmente como<b>texto(en esta sesión, una columna entera de la página 2 se imprimió como<b>mencionar apoyo..., descubierto justo antes de entregar). - ✅ Correcto: Las celdas de la tabla solo escapan
&simple (v.replace('&','&')), manteniendo las etiquetas en línea; los ángulos simples en el contenido los escribe el llamante con<. - Autoverificación: Antes de entregar, busca en todo el texto. Si
'<b>' in texto'</b>' in textes verdadero, significa que las etiquetas se están renderizando como texto, hay que revisar el escape en make_table.
- ⛔ Escritura fatal: Escapar todo el texto de la celda con
- La suma de los anchos de las columnas de la tabla debe ser ≤ el ancho utilizable de A4 (210mm - márgenes izquierdo y derecho), de lo contrario, se desborda y el salto de línea se ve mal.
- Desbordamiento de chino en tablas (2026-08-18, corrección importante): La Table de reportlab, si se coloca texto chino largo directamente, no saltará de línea automáticamente; ¡se desbordará a la derecha! Es obligatorio usar Paragraph para envolver las celdas +
wordWrap="CJK". Ya está integrado enmake_table()con_cell_style()para manejar esto – todas las celdas saltan de línea automáticamente. Verificación: Usa pymupdf para comprobar siget_text('words')tiene x2 que sobrepasa el borde derecho del área de contenido.- El umbral debe calcularse según el margen real, no fijarse a 540pt (2026-09-10 probado): Borde derecho =
A4[0] - rightMargin(ancho A4 595.3pt; margen 16mm → 549.9pt, 17mm → 547pt). Fijar en 540 hará que los saltos de línea normales se notifiquen erróneamente como desbordamiento, causando trabajo innecesario. - Debe excluirse el pie de página: El "Página N" dibujado en onPage siempre está fuera del área de contenido. Al evaluar, filtrar por texto (ej.
w[4] != '页'), de lo contrario, siempre habrá falsas alarmas. - Ejemplo de evaluación:
[w for w in page.get_text('words') if w[2] > page.rect.width - rightMargin + 1 and w[4] != '页']
- El umbral debe calcularse según el margen real, no fijarse a 540pt (2026-09-10 probado): Borde derecho =
- Regla de A4 Vertical (énfasis del usuario): Todos los PDF deben ser A4 vertical (210×297mm), pagesize=A4 es vertical por defecto, no cambiar a landscape.
- Antes de entregar, hay que verificar con pymupdf (número de páginas / chino / caracteres corruptos / sin desbordamiento) – solo enviar si la verificación es exitosa.
- ⛔ Al escribir el script de generación, las cadenas de Python deben usar comillas simples como delimitador (2026-09-10 probado): La cadena de herramientas normaliza las comillas chinas “ ” a comillas dobles semianchas
". Si la capa exterior de la cadena usa comillas dobles, las comillas en el cuerpo cerrarán el literal prematuramente → dandoSyntaxError: invalid character '+' (U+FF0B)o similar (el punto de error parece un carácter de ancho completo ilegal, pero la causa real es el truncamiento de comillas, lo que despista la depuración). Las comillas chinas en el cuerpo deben cambiarse a「」, o envolver toda la cadena con'...'. - ⛔ Nunca "leer y escribir" el mismo archivo al mismo tiempo:
open(f,'w').write(open(f,encoding='utf-8').read() + add)primero trunca el archivo a 0 bytes y luego lo lee; el resultado es que el contenido original se pierde por completo y solo queda el fragmento añadido (en esta sesión, una nota de la base de conocimiento se redujo de 13KB a 3.8KB, casi se entrega un archivo incompleto). Al añadir contenido, primerotxt = open(f).read()guarda en variable, luegoopen(f,'w').write(txt + add); o usa directamente una herramienta de parche. - Archivos >100KB suelen ser normales (subconjunto de fuente incrustada).
Comando de Verificación
$V -c "import pymupdf; d=pymupdf.open(r'D:\...\informe.pdf'); print(len(d)); print(d[0].get_text()[:120]); print('Última página contiene conclusiones:', '结论' in d[-1].get_text())"
Relacionados
- Marco de contenido:
feasibility-study-writing,dual-lens-business-analysis(cargar antes de generar informe con análisis de doble lente) - Reglas de revisión de informes: Ejecutar
report-reflection-reviewantes de entregar informes rigurosos. - Escenario PPT:
ultimate-ppt-master - Plantillas:
templates/reportlab_chinese_template.py(antigua);<b>templates/reportlab_gold_template.py(recomendada): Paleta dorada + alineación izquierda + letra grande 17pt + márgenes compactos 17mm + sin páginas huérfanas. Corregido el manejo de etiquetas en línea enmake_table()(las etiquetas<b>/<span>dentro de las celdas ahora funcionan realmente, ya no se escapan como literales).