CFDI.BuildPdf
2.0.8
dotnet add package CFDI.BuildPdf --version 2.0.8
NuGet\Install-Package CFDI.BuildPdf -Version 2.0.8
<PackageReference Include="CFDI.BuildPdf" Version="2.0.8" />
<PackageVersion Include="CFDI.BuildPdf" Version="2.0.8" />
<PackageReference Include="CFDI.BuildPdf" />
paket add CFDI.BuildPdf --version 2.0.8
#r "nuget: CFDI.BuildPdf, 2.0.8"
#:package CFDI.BuildPdf@2.0.8
#addin nuget:?package=CFDI.BuildPdf&version=2.0.8
#tool nuget:?package=CFDI.BuildPdf&version=2.0.8
📦 CFDI.BuildPdf
Descripción general
CFDI.BuildPdf es una librería .NET 6+ que genera una representación impresa en PDF a partir de un XML CFDI 4.0. Detecta automáticamente el tipo de complemento y soporta actualmente:
- Complemento Carta Porte 3.1
- Complemento Nómina 1.2
Es cross-platform (Windows, Linux, macOS, contenedores) y no tiene dependencias nativas: usa QuestPDF como motor de renderizado y QRCoder para el código QR del timbre fiscal.
📥 Instalación
dotnet add package CFDI.BuildPdf
🚀 Características
- ✔️ Soporte para CFDI 4.0 con complementos Carta Porte 3.1 y Nómina 1.2.
- ✔️ Detección automática del tipo de complemento.
- ✔️ Traducción automática de claves SAT (
c_FormaPago,c_RegimenFiscal,c_UsoCFDI,c_TipoDeComprobante,c_TipoRelacion, etc.): el PDF muestraclave - descripciónlegible en lugar de códigos crudos. Ver CATALOGOS_SAT_MAPEADOS.md. - ✔️ Catálogos del Complemento Nómina 1.2 traducidos:
c_TipoContrato,c_TipoRegimen,c_PeriodicidadPago,c_RiesgoPuesto,c_Estado,c_TipoPercepcion,c_TipoDeduccion,c_TipoOtroPago,c_TipoIncapacidad,c_TipoHoras. - ✔️ Soporte para
<cfdi:CfdiRelacionados>: render condicional de los UUIDs relacionados con suTipoRelaciondescrito. - ✔️ Identificación del PAC timbrador por RFC (Buzón E, InvoiceOne, SAT pruebas; ampliable).
- ✔️ Múltiples formatos de entrada: ruta de archivo,
string,byte[]yStream. - ✔️ Escritura directa a archivo o
Streamde salida (ideal para respuestas HTTP). - ✔️ Opciones configurables: mostrar/ocultar mercancías, condiciones del contrato, addenda; logotipo en Base64; orientación portrait/landscape.
- ✔️ Inyección de dependencias con
Microsoft.Extensions.DependencyInjection. - ✔️ Integración opcional con
ILoggerpara diagnóstico. - ✔️ Excepciones de dominio claras (
CfdiXmlInvalidoException,CfdiComplementoNoSoportadoException).
📁 Estructura del PDF generado
- Datos del emisor y receptor (con
Régimen Fiscaltraducido). - Datos de Emisión: fecha, serie/folio, moneda, tipo de cambio, lugar de expedición,
Exportación(traducida) y sub-bloque CFDI Relacionados cuando el XML lo incluye. - Forma / Método de Pago: con
FormaPago,MetodoPagoyTipoDeComprobantetraducidos. - Conceptos facturados: clave producto/servicio,
ClaveUnidadtraducida, descripción, importes formateados (N2es-MX), descuentos yObjetoImpdescrito. - Totales e impuestos (
c_Impuesto: 001 ISR, 002 IVA, 003 IEPS). - Addenda genérica (opcional).
- Complemento Carta Porte: ubicaciones, mercancías (detalle o resumen), autotransporte (
c_TipoPermiso,c_ConfigAutotransportedescritos), seguros, remolques (c_SubTipoRemdescrito), figuras de transporte (c_FiguraTransportedescrito), página de condiciones del contrato. - Complemento Nómina: datos del empleado, percepciones, deducciones, otros pagos, incapacidades, totales.
- Bloque fiscal: UUID, fechas, certificados, PAC que timbró identificado por RFC.
- QR y sellos digitales (sello CFDI, sello SAT, cadena original del complemento de certificación).
⚠️ Licencia QuestPDF (léeme antes de producción)
Esta librería usa QuestPDF, que tiene licencia dual:
- Community (gratuita) — apta para la mayoría de proyectos open-source y empresas que cumplan los criterios de elegibilidad vigentes del proveedor.
- Professional / Enterprise (de pago) — requerida por QuestPDF para el resto de organizaciones.
El consumidor de esta librería es responsable de elegir y adquirir la licencia correcta. Consulta los términos vigentes en https://www.questpdf.com/license/.
Por defecto, CFDI.BuildPdf declara Community. Para cambiarlo:
Con la fachada estática
using CFDI.BuildPdf.Abstractions;
using CFDI.BuildPdf.Service;
// Llamar una sola vez al iniciar el proceso, antes de generar cualquier PDF
CfdiPdf.ConfigureQuestPdfLicense(CfdiPdfLicenseType.Professional);
Con inyección de dependencias
builder.Services.AddCfdiPdfServices(
configure: opts => opts.MostrarMercancias = true,
licenseType: CfdiPdfLicenseType.Professional);
📚 Requisitos
- .NET 6.0 o superior.
- Sin dependencias nativas (no requiere
wkhtmltopdfni binarios de sistema).
🔵 Uso básico (fachada estática)
Desde una ruta de archivo
using CFDI.BuildPdf.Service;
var pdfBytes = await CfdiPdf.DesdeRutaAsync(@"C:\facturas\cfdi.xml");
await File.WriteAllBytesAsync("cfdi_output.pdf", pdfBytes);
Desde un string XML
var pdfBytes = await CfdiPdf.DesdeXmlStringAsync(xmlString);
Desde bytes
var pdfBytes = await CfdiPdf.DesdeXmlBytesAsync(xmlBytes);
Desde un Stream (por ejemplo, IFormFile en ASP.NET Core)
await using var stream = archivoXml.OpenReadStream();
var pdfBytes = await CfdiPdf.DesdeStreamAsync(stream);
Guardar directamente a archivo
await CfdiPdf.GuardarDesdeRutaAsync(
rutaXml: @"C:\facturas\cfdi.xml",
rutaPdfDestino: @"C:\facturas\cfdi.pdf");
Escribir en un Stream de salida (respuesta HTTP)
[HttpPost("generar-pdf")]
public async Task<IActionResult> GenerarPdf(IFormFile xml)
{
Response.ContentType = "application/pdf";
await using var xmlStream = xml.OpenReadStream();
await CfdiPdf.EscribirEnStreamAsync(xmlStream, Response.Body);
return new EmptyResult();
}
🗂️ Traducción automática de catálogos SAT
El PDF traduce las claves del SAT a su descripción legible en tiempo de render — no necesitas pre-procesar el XML. Ejemplos del output:
| Campo | Valor en el XML | Valor en el PDF |
|---|---|---|
| Régimen Fiscal | 624 |
624 - Coordinados |
| Forma de Pago | 99 |
99 - Por definir |
| Método de Pago | PPD |
PPD - Pago en parcialidades o diferido |
| Uso del CFDI | G03 |
G03 - Gastos en general |
| Tipo de Comprobante | I |
I - Ingreso |
| Exportación | 01 |
01 - No aplica |
| Tipo Relación | 04 |
04 - Sustitución de los CFDI previos |
| Objeto Imp. | 02 |
Sí objeto de impuesto |
| Clave Unidad | E48 |
Unidad de Servicio |
| PAC que timbró | SST060807KU0 |
Buzón E (SST060807KU0) |
| Tipo Nómina (contrato) | 01 |
01 - Contrato de trabajo por tiempo indeterminado |
| Periodicidad Pago (Nómina) | 04 |
04 - Quincenal |
| Tipo Percepción (Nómina) | 001 |
001 - Sueldos, Salarios Rayas y Jornales |
| Clave Entidad Federativa | SIN |
SIN - Sinaloa |
Si una clave no está en el catálogo embebido se renderiza tal cual (fallback seguro, sin excepción). Catálogos soportados (23 helpers / 36 campos): c_ClaveUnidad, c_Impuesto, c_ObjetoImp, c_UsoCFDI, c_RegimenFiscal, c_FormaPago, c_MetodoPago, c_Exportacion, c_TipoDeComprobante, c_TipoRelacion, c_CveTransporte, c_TipoPermiso, c_ConfigAutotransporte, c_SubTipoRem, c_FiguraTransporte, c_TipoContrato, c_TipoRegimen, c_PeriodicidadPago, c_RiesgoPuesto, c_Estado, c_TipoPercepcion, c_TipoDeduccion, c_TipoOtroPago, c_TipoIncapacidad, c_TipoHoras. Inventario completo en CATALOGOS_SAT_MAPEADOS.md.
Agregar un PAC propio al diccionario
El RFC del PAC se traduce a nombre comercial para mostrarlo en el bloque fiscal. Si tu PAC no está en el diccionario embebido, el PDF mostrará "PAC no identificado". Envía un PR agregando la entrada en PdfBuilders/Common/CfdiPdfSections.cs (diccionario PacsConocidos) o ábrenos un issue con el RFC y nombre comercial.
⚙️ Opciones de generación
var options = new CfdiPdfOptions
{
MostrarMercancias = true, // Carta Porte: mostrar detalle de mercancías
MostrarCondicionesContrato = true, // Carta Porte: incluir página de condiciones
MostrarAddenda = true, // Incluir sección de addenda
LogoBase64 = logoBase64, // Logo de la empresa (opcional)
Orientacion = PdfOrientation.Portrait
};
var pdfBytes = await CfdiPdf.DesdeRutaAsync(rutaXml, options);
💉 Uso con inyección de dependencias
Registro de servicios
using CFDI.BuildPdf.Abstractions;
using CFDI.BuildPdf.Configuration;
builder.Services.AddCfdiPdfServices(
configure: opts =>
{
opts.MostrarMercancias = true;
opts.MostrarCondicionesContrato = true;
},
licenseType: CfdiPdfLicenseType.Community);
Consumo desde un servicio
using CFDI.BuildPdf.Abstractions;
public class FacturaService
{
private readonly ICfdiPdfGenerator _generator;
public FacturaService(ICfdiPdfGenerator generator)
{
_generator = generator;
}
public Task<byte[]> GenerarPdfAsync(string rutaXml)
=> _generator.GenerarDesdeRutaAsync(rutaXml);
}
🛑 Manejo de errores
Los métodos públicos lanzan excepciones de dominio específicas para facilitar el diagnóstico:
| Excepción | Cuándo ocurre |
|---|---|
ArgumentNullException / ArgumentException |
Inputs nulos, vacíos o inválidos. |
FileNotFoundException |
La ruta del XML no existe. |
CfdiXmlInvalidoException |
El contenido no es un XML bien formado. |
CfdiComplementoNoSoportadoException |
El CFDI no contiene Carta Porte 3.1 ni Nómina 1.2. |
CfdiPdfException |
Clase base para cualquier error de dominio de la librería. |
try
{
var pdf = await CfdiPdf.DesdeRutaAsync(ruta);
}
catch (CfdiXmlInvalidoException ex)
{
logger.LogWarning(ex, "XML no válido: {Mensaje}", ex.Message);
}
catch (CfdiComplementoNoSoportadoException ex)
{
logger.LogWarning(ex, "Complemento no soportado: {Mensaje}", ex.Message);
}
📝 Licencia
Este proyecto está bajo licencia MIT. Recuerda que QuestPDF (dependencia interna) tiene su propia licencia dual — ver sección arriba.
👤 Autor
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.4)
- Microsoft.Extensions.Options (>= 6.0.0)
- QRCoder (>= 1.6.0)
- QuestPDF (>= 2024.3.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.8 | 110 | 4/29/2026 |
| 2.0.7 | 97 | 4/22/2026 |
| 2.0.6 | 104 | 4/19/2026 |
| 2.0.5 | 95 | 4/16/2026 |
| 2.0.4 | 116 | 4/12/2026 |
| 2.0.3 | 97 | 4/12/2026 |
| 2.0.2 | 96 | 4/12/2026 |
| 2.0.1 | 105 | 4/12/2026 |
| 2.0.0 | 103 | 4/12/2026 |
| 1.0.5 | 275 | 6/16/2025 |
| 1.0.4 | 253 | 5/1/2025 |
| 1.0.3 | 241 | 4/30/2025 |
| 1.0.2 | 239 | 4/30/2025 |
| 1.0.1 | 223 | 4/30/2025 |
| 1.0.0 | 218 | 4/30/2025 |