usebeq.helpers.pdf 1.1.0

Suggested Alternatives

usebeq.wrappers.core.itext7

dotnet add package usebeq.helpers.pdf --version 1.1.0
NuGet\Install-Package usebeq.helpers.pdf -Version 1.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="usebeq.helpers.pdf" Version="1.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add usebeq.helpers.pdf --version 1.1.0
#r "nuget: usebeq.helpers.pdf, 1.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install usebeq.helpers.pdf as a Cake Addin
#addin nuget:?package=usebeq.helpers.pdf&version=1.1.0

// Install usebeq.helpers.pdf as a Cake Tool
#tool nuget:?package=usebeq.helpers.pdf&version=1.1.0

Pdf Builder - C# .NET Standard 2.0

🔥 ACERCA DEL PROYECTO

Este proyecto brinda una interfaz simple para insertar texto en documentos pdf haciendo uso de la librería iText7.

⚙️ INSTALACIÓN

A través de NET CLI.

gh dotnet add package usebeq.helpers.pdf --version 1.0.0

Con la consola de administración de paquetes.

NuGet\Install-Package usebeq.helpers.pdf -Version 1.0.0

Agregando una entrada al archivo de proyecto (.net).

<PackageReference Include="usebeq.helpers.pdf" Version="1.0.0" />

Agregando una entrada al archivo packages.config (.net framework 4.8).

<package id="usebeq.helpers.pdf" version="1.0.0" targetFramework="net48" />

Casos de uso

Este paquete está orientado a la inserción de texto en archivos pdf haciendo uso de otro archivo pdf como plantilla y de objetos cs class con estructura previamente definida que contienen el texto a insertar, así como el posicionamiento y estilamiento de este. Adicionalmente, es compatible la inserción de imágenes y la creación de tablas automatizadas.

¿Cómo usar este paquete?

Paso 1: Instala la última versión del paquete con alguno de los métodos arriba mencionados.

Paso 2: Necesitas un documento pdf que sirva como plantilla para tu documento pdf final. Este documento debe contener páginas únicas, es decir, si necesitas que una página de tu documento pdf de plantilla se repita más de una vez, este paquete cuenta con una interfaz que te ayudará a replicar páginas de forma fácil y rápida.

Nota : El documento pdf de plantilla debe estar alojado en un directorio accesible por la aplicación.

Paso 3: A continuación, necesitamos crear un MemoryStream que va a contener nuestro nuevo documento pdf de plantilla; para esto, creamos una instancia de la interfaz ITemplateAdapter que nos ayudará a replicar las páginas de nuestro pdf de plantilla tantas veces como sea necesario.

ITemplateAdapter templateAdapter = new TemplateAdapter();

Paso 4: Establecemos el path donde se encuentra localizado nuestro archivo pdf de plantilla así como el número de iteraciones de cada una de las páginas haciendo uso de un Dictionary<int, int> // Dictionary<[número de páginas], [número de veces que se va a repetir]>. Con esta información generaremos un nuevo documento pdf contenido en un MemoryStream y será en este donde volcaremos el texto de nuestro reporte.

// El directorio donde se encuentra nuestra plantilla en formato pdf
// Ejemplo .NET 6
string path = Path.Combine(
	_webHostEnvironment.WebRootPath,
	"plantillas",
	"reporte-de-ventas.pdf"
);
// Ejemplo .NET Framework 4.8
string path = Server.MapPath("~/content/plantillas/reporte-de-ventas.pdf");

var iterations = new Dictionary<int, int> {
	{ 1, 3}, // La primera página de nuestra plantilla se va a repetir 3 veces en el nuevo pdf
	{ 2, 1}, // Solo se agrega una copia de la segunda página del template en el nuevo pdf
};

ITemplateAdapter templateAdapter = new TemplateAdapter();
MemoryStream template = templateAdapter.AdapTemplate(path, iterations);

Nota: Trabajar con MemoryStreams nos da la ventaja de que todo el tiempo estamos trabajando en memoria y en ningún momento tenemos que crear "archivos físicos" que se tengan que alojar en el servidor.

IMPORTANTE: El paso siguiente será definir el texto que será insertado en nuestro plantilla pdf recién creada, hay que tomar en cuenta las siguientes consideraciones:

1: Se recomienda ampliamente que los datos que se agreguen a la plantilla pdf sean de tipo cadena y que estos ya tengan el formato adecuado para impresión.

2: Se recomienda contar con un catálogo de reportes y un catálogo de estilos alojados en una bases de datos persistente por cada texto que se insertará en el documento pdf, lo cual te va a permitir agilizar el desarrollo y testeo de los documentos pdf generados. A continuación te brindamos una estructura sugerida para contener estos catálogos.

-- Para contener tu catálogo de reportes, de ser necesario, puedes agregar más columnas
CREATE TABLE [cnf].[Reportes](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [varchar](250) NOT NULL,
	[DownloadName] [varchar](250) NOT NULL,
	[PathLayout] [varchar](250) NOT NULL
);

-- Para contener la información de posicionamiento y estilamiento de los textos de tu documento,
--de ser necesario, puedes agregar más columnas.
CREATE TABLE [cnf].[Diccionario](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[IdReporte] [int] NOT NULL,
	[Page] [int] NOT NULL,
	[Variable] [varchar](45) NOT NULL,
	[OutputType] [int] NOT NULL,
	[ElementType] [int] NOT NULL,
	[Align] [int] NOT NULL,
	[Valign] [int] NOT NULL,
	[Font] [varchar](120) NOT NULL,
	[FontSize] [float] NOT NULL,
	[R] [int] NOT NULL,
	[G] [int] NOT NULL,
	[B] [int] NOT NULL,
	[X] [float] NOT NULL,
	[Y] [float] NOT NULL,
	[Xend] [float] NOT NULL,
	[Yend] [float] NOT NULL,
	[RotateAngle] [float] NOT NULL
)

Es necesario contar con conocimientos básicos sobre el uso de LINQ, pues será necesario hacer uso de este componente para transformar la información de nuestros usuarios en un objeto que sea compatible con la interfaz de este componente.

Paso 6: Dicho todo lo anterior, ahora procederemos a formar el objeto que contendra el texto que será insertado en nuestro documento pdf plantilla. Los DTO del proyecto nos permiten definir información para renderizar textos simples, conjunto de datos y arreglos de conjuntos de datos. Ejemplo:

DTO principal que va a contener la información del pdf, la plantilla en formato MemoryStream, así como todo el texto que vamos a insertar en el template es el siguiente:
	public class PdfDetails {
		private string downloadName;

		public MemoryStream Template { get; set; }

		/// <summary>
		/// Nombre con el que se va a descargar el documento pdf
		/// </summary>
		public string DownloadName {
			get {
				return string.IsNullOrWhiteSpace(downloadName)
					? "document.pdf"
					: downloadName;
			}
			set {
				downloadName = value.EndsWith(".pdf")
					? downloadName
					: $"{value}.pdf";
			}
		}

		/// <summary>
		/// Colección de objetos PdfPageContent con la data, coordenadas de caída y estilos de texto
		/// </summary>
		public List<PdfPage> Pages { get; set; }
	}
DTO para contener el texto de cada página es el siguiente:
	public class PdfPage {
		public int Number { get; set; }
		public IList<dynamic> Data { get; set; }
	}

Basicamente se trata del número de página donde se insertará el texto y el conjunto de textos que se incrustaran en esta página. Data se trata de una lista de objetos dinámicos pero definidos, pues los DTO's para la inserción de textos simples, conjuntos de textos o arreglos de conjuntos de textos difieren un poco en estructura.

DTO para la inserción de un texto simple
	public class SingleData {
		public string Data { get; set; }
		public Coor Coor { get; set; }
	}

Puedes observar que este DTO solo contiene el texto simple a insertar así como la información de posicionamiento y estilamiento que tendrá el texto en el documento pdf.

Este es un ejemplo de como se define una instancia de un objeto SingleData

			// SingleData nos permite incrustar un texto simple en nuestro pdf
			new SingleData {
				Data = "¡Hola Mundo Cruel!",
				Coor = new Coor {
					// No es necesario definir el nombre de la propiedad
					// a la que pertenece la coordenada, pues se trata
					// de solo una cadena
					X = 300,
					Y = 500,
					Align = TextAlign.CENTER,
					FontColor = new RGB{ R = 150, G = 50, B = 10 },
					FontSize = 14,
					RotateAngle = 90
				}
			},
			// Con el atributo ElementType podemos definir si deseamos pintar
			//texto a una línea o texto con salto de línea, para esto.
			new SingleData {
				Data = "¡Hola Mundo Cruel, este es un texto mucho más grande para mostrar el salto de línea automático!",
				Coor = new Coor {
					// No es necesario definir el nombre de la propiedad
					// a la que pertenece la coordenada, pues se trata
					// de solo una cadena
					X = 300,
					Y = 500,
					Xend = 80, // Necesario para definir el ancho de la caja de texto
					Yend = 60, // Necesario para definir el alto de la caja de texto
					ElementType = ElementType.TEXTAREA
				}
			}
DTO para la inserción de un conjunto de datos
	public class SingleRowData {
		public dynamic Data { get; set; }
		public IEnumerable<Coor> Dictionary { get; set; }
	}

Este DTO nos ofrece la gran ventaja de que podemos insertar cualquier conjunto de datos, ya sea, con un DTO bien definido o con un objeto formado dinámicamente, es decir, podemos usar cualquiera de los siguiente objetos:

	// Con un DTO definido
	public class VmVenta {
		public int Folio { get; set; }
		public float Importe { get; set; }
		public int IdCliente { get; set; }
	}

	var venta = new VmVenta {
		Folio = 235,
		Importe = 235.45m,
		IdCliente = 6
	};

	var data = new SingleRowData {
		Data = venta,
		Dictionary = ...
	}

	// Sin DTO definido
	var venta = new {
		Folio = 235,
		Importe = 235.45m,
		IdCliente = 6
	};

	var data = new SingleRowData {
		Data = venta,
		Dictionary = ...
	}

La propiedad Dictionary es un conjunto de objetos Coor que es el DTO que contiene la información del posicionamiento y estilado de los textos. Necesitamos una instancia del DTO Coor por cada una de las propiedades de nuestro objeto Data. El valor de la propiedad Name de la instancia Coor debe ser el nombre de la propiedad en el objeto Data.

IMPORTANTE: Nuestro objeto Data puede contener propiedades que no estén definidas en el objeto Dictionary pero no viceversa, esto generará una excepción en tiempo de ejecución.

Este es un ejemplo de como se define una instancia de un objeto SingleRowData

new SingleRowData {
	Data = new { V1 = "1", V2 = "2", V3 = "3", V4 = "4" },
	Dictionary = new Collection<Coor> {
		new Coor { Name = "V1", X = 100, Y = 300 },
		new Coor { Name = "V2", X = 120, Y = 320 },
		new Coor { Name = "V3", X = 140, Y = 340 },
		new Coor { Name = "V4", X = 160, Y = 360 }
	}
}
DTO para la inserción de un arreglo de conjuntos de datos
public class TableData : SingleRowData {
	private float stepInY;
	public float StepInY {
		get => stepInY == 0 ? 15 : stepInY;
		set => stepInY = value == 0 ? 15 : value;
	}
}

Podemos observar que TableData hereda de SingleRowData pues al ser Data de tipo dinámico, su valor se puede definir tanto como un objeto como con un arreglo de objetos. El diccionario de coordenadas se aplicará para todos los objetos del arreglo y se usará la propiedad StepInY para ajustar el salto de línea entre conjunto de datos.

Este es un ejemplo de como se define un a instancia de un objeto TableData

new TableData {
	Data = new List<dynamic>{
		new { V1 = "1", V2 = "2", V3 = "3", V4 = "4" },
		new { V1 = "5", V2 = "6", V3 = "7", V4 = "8" },
		new { V1 = "9", V2 = "10", V3 = "11", V4 = "12" }
	},
	Dictionary = new Collection<Coor> {
		new Coor { Name = "V1", X = 100, Y = 500, Align = TextAlign.RIGHT },
		new Coor { Name = "V2", X = 120, Y = 510, Align = TextAlign.RIGHT },
		new Coor { Name = "V3", X = 140, Y = 520, Align = TextAlign.RIGHT },
		new Coor { Name = "V4", X = 160, Y = 530, Align = TextAlign.RIGHT}
	}
}

A continuación se muestra un ejemplo de como se puede formar un objeto PdfPage con distintos tipos de DTO's.

// Definimos el conjunto de datos por página que serán representados en nuestro documento pdf
var pages = new List<PdfPage> {
	new PdfPage {
		// Definimos el número de página en el que serán renderizados nuestros datos
		Number = 1,
		Data = new List<dynamic> {
			// SingleData nos permite incrustar un texto simple en nuestro pdf
			new SingleData {
				Data = "¡Hola Mundo Cruel!",
				Coor = new Coor { X = 300, Y = 500, Align = TextAlign.CENTER, FontColor = new RGB{ R = 150, G = 50, B = 10 }, FontSize = 14, RotateAngle = 90 }
			},
			// Con el atributo ElementType podemos definir si deseamos pintar texto a una línea o texto con salto de línea, para esto.
			new SingleData {
				Data = "¡Hola Mundo Cruel, este es un texto mucho más grande para mostrar el salto de línea automático!",
				Coor = new Coor { X = 300, Y = 500, Xend = 80, Yend = 60, ElementType = ElementType.TEXTAREA }
			},
			// SingleRowData nos permite pintar un conjunto de datos
			new SingleRowData {
				Data = new { V1 = "1", V2 = "2", V3 = "3", V4 = "4" },
				Dictionary = new Collection<Coor> {
					new Coor { Name = "V1", X = 100, Y = 300 },
					new Coor { Name = "V2", X = 120, Y = 320 },
					new Coor { Name = "V3", X = 140, Y = 340 },
					new Coor { Name = "V4", X = 160, Y = 360 }
				}
			},
			// TableData nos permite pintar un arreglo de conjuntos de datos, simulando una tabla
			new TableData {
				Data = new List<dynamic>{
					new { V1 = "1", V2 = "2", V3 = "3", V4 = "4" },
					new { V1 = "5", V2 = "6", V3 = "7", V4 = "8" },
					new { V1 = "9", V2 = "10", V3 = "11", V4 = "12" }
				},
				Dictionary = new Collection<Coor> {
					new Coor { Name = "V1", X = 100, Y = 500, Align = TextAlign.RIGHT },
					new Coor { Name = "V2", X = 120, Y = 510, Align = TextAlign.RIGHT },
					new Coor { Name = "V3", X = 140, Y = 520, Align = TextAlign.RIGHT },
					new Coor { Name = "V4", X = 160, Y = 530, Align = TextAlign.RIGHT}
				}
			}
		},
	}
};

Paso 7: Formamos el objeto final que necesitamos para crear nuestro pdf

var pdfDetails = new PdfDetails {
	Pages = pages,
	Template = template
};

Paso 8: Creamos una instancia de la interfaz ICreator y al método InsertText le pasamos como argumento el objeto que acabamos de crear.

ICreator creator = new Creator();
creator.InsertText(pdfDetails);

Adicionalmente podemos incrustar imágenes a nuestro document pdf, así se trate de una imagen guardada localmente en el directorio de nuestra aplicación o una imagen contenida en un MemoryStream, ejemplo:

// .NET 6
path = Path.Combine(_webHostEnvironment.WebRootPath, "images", "demo.png");
// .NET Framework 4.8
path = Server.MapPath("~/content/images/demo.png");

var imageOptions = new ImageOptions {
	Widht = 100,
	Higth = 100,
	PageNumber = 1,
	Left = 100,
	Botton = 100
};
creator.AddImage(imageOptions, path);

Finalmente ejecutamos el método Generate para crear nuestro nuevo pdf.

MemoryStream report = creator.Generate();

Nota: Usa el método PreparaPdfToDownload para preparar el MemoryStream para ser visualizado en el navegador sin necesidad de descargarse, este método en última instancia se usa en el controlador de tu API/MVC.

private FileStreamResult PreparaPdfToDownload(MemoryStream report, string name) {
	var contentDispositionHeader = new System.Net.Mime.ContentDisposition {
		Inline = true,
		FileName = name
	};
	Response.Headers.Add(
		"Content-Disposition",
		contentDispositionHeader.ToString()
	);
	return File(report, "application/pdf");
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.1.0 206 3/30/2023
1.0.0 173 3/29/2023