← Todos los artículos

Generar DOCX, XLSX y PDF en .NET con un solo modelo de datos

Crea un reporte tabular en .NET 10 y expórtalo a Word, Excel y PDF con Lbe.Documents, una biblioteca abierta publicada en NuGet.

1 de octubre de 2026 · · 5 min de lectura
Una tabla de datos alimenta tres documentos genéricos: páginas de texto, hoja de cálculo y reporte paginado

Un mismo conjunto de pedidos puede necesitar una copia editable para oficina, una hoja de cálculo para análisis y un PDF para compartir. Mantener tres implementaciones independientes hace fácil que un importe, un encabezado o el orden de las filas termine siendo distinto en cada archivo. Para un reporte tabular sencillo, resulta más claro construir los datos una vez y elegir después el formato de salida.

Lbe.Documents 0.1.0 es una pequeña biblioteca de código abierto para .NET 10 que publiqué en NuGet con licencia MIT. Usa Open XML SDK para DOCX, ClosedXML para XLSX, PDFsharp para PDF y PdfPig para leer el texto de un PDF. Cada dependencia conserva su propia licencia; revisa esas condiciones para tu distribución.

La idea no es convertir un documento de Word a PDF. Las tres salidas se generan por caminos separados a partir del mismo TabularReport.

Instala el paquete

Necesitas un proyecto que apunte a net10.0 y un SDK capaz de compilarlo. Desde una carpeta nueva:

dotnet new console -n ReportesDemo --framework net10.0
cd ReportesDemo
dotnet add package Lbe.Documents --version 0.1.0

El último comando fija la versión usada en este artículo. Puedes comprobar el paquete y su código en NuGet y en el repositorio público. Para generar PDF también tendrás que proporcionar una fuente TTF que tengas derecho a usar; el repositorio no incorpora una fuente.

Un reporte, tres archivos

Reemplaza Program.cs con este ejemplo. Recibe como argumentos la carpeta de salida y la ruta de una fuente TTF existente:

using Lbe.Documents;
using PdfSharp.Drawing;
using PdfSharp.Fonts;

if (args.Length != 2 || !File.Exists(args[1]))
{
    Console.Error.WriteLine("Uso: dotnet run -- <directorio-salida> <fuente-ttf>");
    return 1;
}

var outputDirectory = Path.GetFullPath(args[0]);
Directory.CreateDirectory(outputDirectory);

var report = new TabularReport("Pedidos de ejemplo",
    ["Id", "Cliente", "Importe"],
    [["A-001", "Ana", "125.00"], ["A-002", "Luis", "89.50"]]);

using (var file = File.Create(Path.Combine(outputDirectory, "pedidos.docx")))
    DocumentExporter.WriteDocx(report, file);
using (var file = File.Create(Path.Combine(outputDirectory, "pedidos.xlsx")))
    DocumentExporter.WriteXlsx(report, file);

GlobalFontSettings.FontResolver = new SingleFontResolver(File.ReadAllBytes(args[1]));
using (var file = File.Create(Path.Combine(outputDirectory, "pedidos.pdf")))
    DocumentExporter.WritePdf(report, file, new XFont("LbeExample", 11));

using (var file = File.OpenRead(Path.Combine(outputDirectory, "pedidos.pdf")))
    Console.WriteLine(DocumentExporter.ExtractPdfText(file));
return 0;

sealed class SingleFontResolver(byte[] fontBytes) : IFontResolver
{
    public string DefaultFontName => "LbeExample";

    public FontResolverInfo? ResolveTypeface(string familyName, bool isBold, bool isItalic)
        => new("LbeExample");

    public byte[] GetFont(string faceName) => fontBytes;
}

En Windows, si dispones de Arial y su licencia permite el uso previsto, puedes ejecutar:

dotnet run -- ./salida C:/Windows/Fonts/arial.ttf

En Linux o macOS sustituye esa ruta por la de una TTF disponible y permitida en tu entorno. El programa genera pedidos.docx, pedidos.xlsx y pedidos.pdf en salida, y escribe en consola el texto que PdfPig recupera del PDF. El ejemplo completo del repositorio sigue la misma estructura.

SingleFontResolver devuelve siempre la misma TTF: basta para esta muestra, que usa una única fuente regular. Si necesitas combinar familias, negritas o cursivas, amplía el resolvedor para distinguirlas y comprueba el resultado.

GlobalFontSettings.FontResolver es configuración global de PDFsharp: en una aplicación web configúralo una sola vez al iniciar, antes de construir cualquier XFont, no en cada petición. La muestra usa File.Create, que sobrescribe archivos con el mismo nombre; elige una carpeta de prueba o añade una política explícita de nombres y reemplazo para producción.

Qué conserva cada formato

TabularReport guarda el título, los encabezados y las filas. Rechaza encabezados vacíos y filas con un número de celdas diferente al número de columnas. Así evita producir un archivo aparentemente correcto con una tabla desalineada.

El DOCX contiene un título y una tabla sencilla. El XLSX crea una hoja llamada Reporte y escribe las celdas como texto: A-001 conserva el cero, pero 125.00 no se convierte en número para sumar o graficar. Si necesitas fórmulas, formatos numéricos, varias hojas o estilos avanzados, trabaja directamente con ClosedXML.

El PDF dibuja una tabla simple. Cuando una celda es demasiado ancha, acorta visualmente el valor con puntos suspensivos; por eso no debes tratarlo como una salida de precisión contable o como prueba de que todo el texto original está visible. Si necesitas maquetación compleja, firmas o accesibilidad avanzada, utiliza la biblioteca especializada que corresponda y valida el resultado renderizado.

ExtractPdfText lee PDFs con una capa de texto. Un escaneo que solo contiene imágenes necesita OCR, una capacidad que este paquete no incluye. Tampoco convierte DOCX a PDF: genera cada formato desde los datos originales.

Qué se comprobó y cuándo conviene usarlo

Para esta guía ejecuté el ejemplo con una referencia al paquete Lbe.Documents 0.1.0: se crearon los tres archivos y la extracción del PDF devolvió el título y las dos filas. También pasaron las cuatro pruebas del repositorio. La verificación fue con un reporte pequeño en Windows; no mide rendimiento ni garantiza el diseño de reportes grandes, fuentes distintas o despliegues Linux.

Usaría esta fachada si el problema es exportar una tabla pequeña y consistente en varios formatos. Si tu dominio exige edición fina de Word, fórmulas de Excel, diseño PDF avanzado o reconocimiento de texto en escaneos, empezaría con las bibliotecas subyacentes o una solución especializada. El valor de este ejemplo está en compartir el modelo de datos y dejar claros sus límites, no en ocultar la complejidad de todos los formatos.

Fuentes y código

Comentarios

Cargando…