Apariencia
CV en PDF
HUMAE genera automáticamente un CV profesional con la identidad de marca, listo para compartir. No requiere que el candidato diseñe nada — se produce desde el perfil ya capturado.
Cómo se genera
Endpoint: GET /api/v1/me/profile/cv.pdf
- Response type:
application/pdf - Content-Disposition:
attachment; filename="CV_Nombre_Apellido.pdf" - Rate limit: 30 por minuto (es operación cara)
Desde la UI
En /me/profile, botón "Descargar CV en PDF". El frontend:
- Llama al endpoint.
- Recibe un blob.
- Crea un
Blob URLy simula el click de descarga.
No se abre en nueva pestaña (para evitar que queden tabs huérfanas con el PDF).
Motor de generación
- Librería: DomPDF 3.x (
dompdf/dompdf). - Servicio:
App\Services\CvGenerationService. - Template Blade:
resources/views/pdf/cv.blade.php.
Flujo técnico
GET /me/profile/cv.pdf
│
▼
ProfileController@cv
│
▼
CvGenerationService::generate($user)
│
├── Carga el CandidateProfile con 8 relaciones (eager loading):
│ experiences, educations, courses, certifications,
│ references, documents, skills, languages
│
├── Transforma los datos a un array para la vista
│
├── Renderiza la vista Blade con los tokens de marca:
│ ─ Logo HUMAE (base64 inline)
│ ─ Paleta: primary #314259, dark #081828
│ ─ Font: Helvetica (DomPDF no soporta custom fonts trivialmente)
│
├── Instancia Dompdf, setea papel A4 portrait
│
└── Devuelve el stream binarioTransliteración del filename
Los nombres con acentos y ñ se transliteran para que el archivo sea válido en Windows/macOS/Linux:
"Pérez Núñez" → "Perez_Nunez"Se hace con un mapa manual (iconv con //TRANSLIT tenía bugs con ñ en algunos locales).
Estructura del CV
El template tiene 6 secciones fijas en orden:
1. Encabezado
┌──────────────────────────────────────┐
│ [FOTO] NOMBRE APELLIDO │
│ Headline del candidato │
│ email | teléfono | LinkedIn│
│ Ciudad, Estado, País │
└──────────────────────────────────────┘La foto viene de User.avatar_url. Si no hay, se omite y se ajusta el layout.
2. Resumen profesional
Muestra summary del perfil (hasta 5000 caracteres). Si está vacío, se omite la sección.
3. Experiencia laboral
Orden cronológico inverso. Cada experiencia:
[Mar 2023 — Actual] Software Engineer · HUMAE
CDMX, Remoto
Descripción de responsabilidades y logros...4. Educación
[2018 — 2022] Licenciatura en Ingeniería · UNAM
Mexico City · Computer Science5. Habilidades e idiomas
Dos columnas:
HABILIDADES IDIOMAS
React (Experto) Español (Nativo)
Python (Avanzado) Inglés (C1)
SQL (Avanzado) Portugués (B1)6. Pie de página
────────────────────────────────────────
CV generado por HUMAE · humae.com.mx · 19 de abril, 2026Personalización
MVP: el template es fijo. No se puede reordenar, elegir colores ni cambiar idioma.
Fase 2 (roadmap):
- Templates alternos (minimalista, corporativo, creativo).
- Selector de secciones a incluir/excluir.
- Idiomas: generar CV en inglés con
lang=enquery param.
Visto por recruiters
Los recruiters pueden descargar el CV de cualquier candidato visible en el directorio:
Endpoint: GET /api/v1/recruiter/candidates/{id}/cv.pdf
- Mismo template, mismo flujo.
- Requiere
role:recruiterorole:admin. - Se registra en el log de auditoría: "Recruiter Juan descargó CV de Ana Pérez".
Consideraciones técnicas
Performance
- Generar un PDF con DomPDF toma ~300–800ms (depende del número de experiencias/educaciones).
- Se ejecuta sincrono dentro del request. No se encola.
- Para candidatos con muchísimas entradas (>50 experiencias), considerar paginar o limitar el CV.
Fonts
DomPDF usa fuentes embebidas. El MVP usa Helvetica para garantizar renderizado consistente. Agregar custom fonts (Inter, por ejemplo) requiere:
- Descargar TTF y convertirlo con
dompdf font:convert. - Hospedarlo en
storage/fonts/. - Referenciarlo en el CSS del Blade con
@font-face.
Pendiente para fase 2.
Images
El logo y la foto de perfil se incrustan en base64 inline dentro del HTML. Esto evita llamadas HTTP durante el render (DomPDF no soporta bien fetching remoto).
php
$logoDataUri = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));Caching
El PDF no se cachea. Cada request regenera. Motivos:
- Los datos del candidato cambian con frecuencia.
- Regenerar es rápido comparado con cachear + invalidar.
- Evita guardar PDFs con datos personales en disco más tiempo del necesario.
Errores comunes
| Error | Causa | Solución |
|---|---|---|
| "No tienes permiso para acceder" | Candidato sin login o intentando bajar CV de otro | Login; solo puedes bajar tu CV |
| "Perfil incompleto" | Faltan datos básicos (nombre) | Completar perfil antes de descargar |
| PDF en blanco | Bug de DomPDF con caracteres especiales (raro) | Revisar logs, reportar |
| Filename con caracteres raros en Windows | Falla de transliteración | El mapa manual cubre acentos y ñ; reportar si ocurre |
Notificaciones relacionadas
Ninguna. La descarga del CV es silenciosa — no se notifica al candidato cuando un recruiter descarga su CV (por diseño: evita ansiedad).
Siguiente
Ya con CV y todo listo, el candidato puede recibir propuestas de entrevista cuando los recruiters lo asignen a una vacante. Entrevistas del candidato →

