Pruebas en un entorno real
Pruebe en producción sin marcas de agua.
Funciona donde lo necesites.
Este artículo explora la integración de un escáner de códigos de respuesta rápida(Escáner de códigos QR) en una aplicación Blazor utilizando IronQR, una biblioteca .NET. Un código QR es un código de barras bidimensional que almacena muchos más datos que un código de barras unidimensional normal.
Blazor, un framework de Microsoft, permite a los desarrolladores crear aplicaciones de una sola página.(utilizando la aplicación Blazor WebAssembly) o utilizar C# para crear interfaces web interactivas,(Blazor Server, en el que nos centraremos en esta guía).
La integración de IronQR con Blazor Server para el escaneado de códigos QR es una combinación estratégica que aprovecha los puntos fuertes de ambas tecnologías. Mediante la integración de IronQR con las aplicaciones Blazor, puede gestionar eficazmente la generación y el escaneado de códigos QR. Esta funcionalidad de un lector de códigos QR es cada vez más demandada en diversos contextos empresariales, como la gestión de inventarios, los sistemas de venta de entradas y el intercambio de información sin contacto.
Servidor Blazor es un marco de aplicaciones web que forma parte de la plataforma ASP.NET Core. Permite a los desarrolladores crear interfaces web interactivas utilizando C# en lugar de JavaScript. Este modelo del lado del servidor funciona gestionando las interacciones de los usuarios a través de una conexión SignalR, una funcionalidad web en tiempo real. Esto ayuda a los desarrolladores a crear aplicaciones web eficaces e interactivas.
IronQR es una biblioteca .NET que destaca por su capacidad para leer, interpretar ygenerar códigos QR con gran precisión. Ofrece una amplia gama de funciones, incluida la capacidad de gestionar distintos tipos de contenido de códigos QR. La fuerza de IronQR radica en su sencillez y facilidad de integración en aplicaciones .NET, lo que lo convierte en la opción preferida de los desarrolladores que desean incorporar y crear funciones de código QR.
Crear una aplicación de servidor Blazor en Visual Studio Code
Instalar la biblioteca de clases de código QR mediante el gestor de paquetes NuGet
Crear la interfaz de usuario utilizando HTML y CSS en el index.razor
Escribir la lógica de gestión de archivos cargados
Escribir la lógica de escaneo QR utilizando la biblioteca QR
Inicie Visual Studio y seleccione "Crear un nuevo proyecto". En la pantalla de selección de plantilla de proyecto, busque y seleccione la plantilla "Blazor Server App". Haga clic en Siguiente.
Una vez elegida la plantilla, introduzca un Nombre de proyecto y una Ubicación(mantener todo lo demás como valores por defecto) y pulse el botón Siguiente.
Ahora seleccione el .NET Framework deseado y pulse el botón crear. Creará una aplicación Blazor Server.
Haga clic en Herramientas en la barra de menús. En el menú desplegable, seleccione NuGet Package Manager. En el menú contextual, seleccione "Gestionar paquetes NuGet para la solución". Se abrirá la pestaña Gestor de paquetes NuGet.
En el gestor de paquetes NuGet, busque "IronQR" en la pestaña "Examinar". A continuación, localice el paquete "IronQR" en la lista. Haz clic en el botón "Instalar".
Ahora que lo tienes todo instalado, podemos repasar la estructura del proyecto y cómo implementarlo todo en tu proyecto.
La interfaz de usuario del escáner de códigos QR se construye principalmente dentro del archivo Index.razor. Este archivo, parte del proyecto Blazor Server, utiliza una combinación de HTML y Razor Syntax para crear una página web dinámica e interactiva. La estructura incluye:
@page "/"
@using System.IO
@using Microsoft.AspNetCore.Components.Forms
@using IronQr
@using IronSoftware.Drawing
@inject IJSRuntime JSRuntime
<PageTitle>QR Code Scanner</PageTitle>
<div>
<h1>QR Code Scanner</h1>
<InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" />
@if (!string.IsNullOrEmpty(qrImageSrc))
{
<img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
}
<button @onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button">Scan QR Code</button>
@if (!string.IsNullOrEmpty(scannedText))
{
<div class="result-section">
<button @onclick="CopyToClipboard" class="button copy-button">Copy</button>
</div>
}
</div>
@page "/"
@using System.IO
@using Microsoft.AspNetCore.Components.Forms
@using IronQr
@using IronSoftware.Drawing
@inject IJSRuntime JSRuntime
<PageTitle>QR Code Scanner</PageTitle>
<div>
<h1>QR Code Scanner</h1>
<InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" />
@if (!string.IsNullOrEmpty(qrImageSrc))
{
<img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
}
<button @onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button">Scan QR Code</button>
@if (!string.IsNullOrEmpty(scannedText))
{
<div class="result-section">
<button @onclick="CopyToClipboard" class="button copy-button">Copy</button>
</div>
}
</div>
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: @page "/" using System.IO using Microsoft.AspNetCore.Components.Forms using IronQr using IronSoftware.Drawing inject IJSRuntime JSRuntime <PageTitle> QR Code Scanner</PageTitle> <div> <h1> QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" /> if(!string.IsNullOrEmpty(qrImageSrc))
"image/*" Class="file-input" /> [if](Not String.IsNullOrEmpty(qrImageSrc))
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: Friend @page "/" using System.IO using Microsoft.AspNetCore.Components.Forms using IronQr using IronSoftware.Drawing inject IJSRuntime JSRuntime <PageTitle> QR Code Scanner</PageTitle> <div> <h1> QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept="image/*" Class
"HandleSelectedFile" accept="image/*" Class
Friend page "/" [using] System.IO [using] Microsoft.AspNetCore.Components.Forms [using] IronQr [using] IronSoftware.Drawing inject IJSRuntime JSRuntime (Of PageTitle) QR Code Scanner</PageTitle> (Of div) (Of h1) QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
"QR Code Image" class="qr-image" />
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <img src="@qrImageSrcForDisplay" alt="QR Code Image" class
"@qrImageSrcForDisplay" alt="QR Code Image" class
<img src="@qrImageSrcForDisplay" alt
End Class
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <button onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button"> Scan QR Code</button> if(!string.IsNullOrEmpty(scannedText))
"@(!fileSelected)" class="button scan-button"> Scan QR Code</button> [if](Not String.IsNullOrEmpty(scannedText))
If True Then
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <button onclick="ScanQRCode" disabled="@(!fileSelected)" class
"ScanQRCode" disabled="@(!fileSelected)" class
<button onclick="ScanQRCode" disabled
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <div class="result-section"> <button onclick="CopyToClipboard" class="button copy-button"> Copy</button> </div>
"CopyToClipboard" class="button copy-button"> Copy</button> </div>
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <div class="result-section"> <button onclick="CopyToClipboard" class
"result-section"> <button onclick="CopyToClipboard" class
<div class="result-section"> <button onclick
End If
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'</div>
**Título y encabezamiento El <PageTitle>y<h1>
definen el título de la página y el encabezamiento principal, respectivamente, estableciendo el contexto para el usuario.
Control de carga de imágenes: Un `
Visualización de imágenes: Una vez cargada una imagen, se muestra utilizando un <img>
etiqueta. Esta información visual es crucial para garantizar al usuario que ha cargado el archivo correcto.
Botón de escaneado: Un botón etiquetado con @onclick="ScanQRCode
activa el proceso de escaneo. Su disponibilidad depende de si se ha seleccionado un archivo, lo que mejora la intuitividad de la interfaz.
Visualización de resultados: El texto del código QR escaneado se muestra en un campo de entrada de texto para facilitar su visualización. Otro botón permite copiar este texto en el portapapeles.
La estética visual y el diseño del escáner de códigos QR se definen en el archivo site.css
.
.content {
padding: 20px;
margin: 10px auto; /* Centers the content */
max-width: 500px; /* Sets a max width for the content */
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
text-align: center;
}
.file-input, .result-input {
margin: 10px 0;
padding: 10px;
border-radius: 5px;
border: 1px solid #ddd;
width: 100%;
}
.button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
transition: background-color 0.3s, box-shadow 0.3s;
width: auto; /* Adjusts button width */
display: inline-block; /* Allows the width to adjust to content */
}
.button:hover {
background-color: #45a049;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.qr-image {
max-width: 300px;
max-height: 300px;
display: block;
margin: 10px auto;
border-radius: 10px;
}
.result-section {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.result-input {
width: 100%;
box-sizing: border-box;
}
.copy-button {
margin-top: 10px;
white-space: nowrap;
}
.content
: Esta clase estiliza el área de contenido principal, dándole un ancho definido, alineación centrada y una sombra sutil para darle profundidad.
.file-input, .result-input
: Estas clases dan estilo a los elementos de entrada de archivo y visualización de resultados, asegurando que sean visualmente consistentes y ocupen completamente el ancho de su contenedor.
.button
: Los botones tienen un fondo verde, esquinas redondeadas y un efecto hover para mejorar la interacción con el usuario.
.qr-image
: Los estilos aplicados a la imagen del código QR incluyen restricciones de tamaño y margen automático para centrar, haciendo que la imagen sea prominente pero no abrumadora.
.result-section
: Esta clase garantiza que los elementos de la sección de resultados estén alineados en el centro y espaciados adecuadamente.
El método HandleSelectedFile
es una parte crucial del proceso de escaneo de códigos QR, manejando la carga del archivo del usuario y preparándolo para el escaneo. Este método se activa cuando el usuario selecciona un archivo a través de la función <InputFile>
componente. Esto se muestra en el siguiente código:
private async Task HandleSelectedFile(InputFileChangeEventArgs e)
{
selectedFile = e.File;
fileSelected = true;
var imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages");
Directory.CreateDirectory(imagesDirectory); // Ensure the directory exists
// Use a GUID as the unique file name
var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(selectedFile.Name);
var fullPath = Path.Combine(imagesDirectory, uniqueFileName);
await using (var fileStream = new FileStream(fullPath, FileMode.Create))
{
await selectedFile.OpenReadStream().CopyToAsync(fileStream);
}
// Store the full path in qrImageSrc for scanning
qrImageSrc = fullPath;
// Optionally, create a base64 string for displaying the image (if needed)
byte [] imageBytes = await File.ReadAllBytesAsync(fullPath);
var base64String = Convert.ToBase64String(imageBytes);
qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart('.')};base64,{base64String}";
}
private async Task HandleSelectedFile(InputFileChangeEventArgs e)
{
selectedFile = e.File;
fileSelected = true;
var imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages");
Directory.CreateDirectory(imagesDirectory); // Ensure the directory exists
// Use a GUID as the unique file name
var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(selectedFile.Name);
var fullPath = Path.Combine(imagesDirectory, uniqueFileName);
await using (var fileStream = new FileStream(fullPath, FileMode.Create))
{
await selectedFile.OpenReadStream().CopyToAsync(fileStream);
}
// Store the full path in qrImageSrc for scanning
qrImageSrc = fullPath;
// Optionally, create a base64 string for displaying the image (if needed)
byte [] imageBytes = await File.ReadAllBytesAsync(fullPath);
var base64String = Convert.ToBase64String(imageBytes);
qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart('.')};base64,{base64String}";
}
Private Async Function HandleSelectedFile(ByVal e As InputFileChangeEventArgs) As Task
selectedFile = e.File
fileSelected = True
Dim imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages")
Directory.CreateDirectory(imagesDirectory) ' Ensure the directory exists
' Use a GUID as the unique file name
Dim uniqueFileName = Guid.NewGuid().ToString() & Path.GetExtension(selectedFile.Name)
Dim fullPath = Path.Combine(imagesDirectory, uniqueFileName)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
' await using(var fileStream = New FileStream(fullPath, FileMode.Create))
' {
' await selectedFile.OpenReadStream().CopyToAsync(fileStream);
' }
' Store the full path in qrImageSrc for scanning
qrImageSrc = fullPath
' Optionally, create a base64 string for displaying the image (if needed)
Dim imageBytes() As Byte = Await File.ReadAllBytesAsync(fullPath)
Dim base64String = Convert.ToBase64String(imageBytes)
qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart("."c)};base64,{base64String}"
End Function
Aquí tienes un desglose detallado de su funcionalidad:
Selección y validación de ficheros: Cuando un usuario sube un archivo, el método captura los detalles del archivo usando InputFileChangeEventArgs
e. La variable selectedFile
se asigna entonces a este fichero, y se establece un booleano fileSelected
a true, indicando que los datos/archivo de entrada están listos para ser procesados.
Creación de la ruta del archivo: El método prepara un directorio para almacenar la imagen subida. Utiliza Path.Combine
para crear una ruta al directorio UploadedImages
, asegurándose de que existe con Directory.CreateDirectory
. Este paso es crucial para organizar sistemáticamente los archivos cargados.
Generación de un nombre de archivo único: Para evitar conflictos con archivos existentes, se genera un nombre de archivo único utilizando un GUID(Identificador único global) con la extensión del archivo original. Esto asegura que cada archivo cargado es uniquePathdentified
.
Guardar el archivo: El fichero se guarda en el servidor. El método crea un flujo de archivos que apunta a la ruta del archivo recién generado, y el contenido del archivo cargado se copia en este flujo utilizando await selectedFile.OpenReadStream().CopyToAsync(fileStream)
. Este paso finaliza el proceso de carga.
Preparación de la imagen para su visualización: Después de guardar el archivo, es necesario mostrar la imagen al usuario para su confirmación. El método lee el archivo en una matriz de bytes y lo convierte en una cadena base64, adecuada para incrustar directamente en un <img>del atributo
src` de la etiqueta. Esta conversión permite mostrar la imagen sin necesidad de solicitar por separado el archivo de imagen al servidor.
El método ScanQRCode
es el corazón de la funcionalidad de escaneo de códigos QR en la aplicación Blazor Server. Este método toma la imagen cargada y utiliza IronQR para extraer los datos del código QR.
private async Task ScanQRCode()
{
// Check if there is a valid image to work with
if (string.IsNullOrEmpty(qrImageSrc)) return;
try
{
var inputBmp = AnyBitmap.FromFile(qrImageSrc);
QrImageInput imageInput = new QrImageInput(inputBmp);
QrReader reader = new QrReader();
IEnumerable<QrResult> results = reader.Read(imageInput);
// Check if there are any results and if the first result contains text
var firstResult = results.FirstOrDefault();
if (firstResult != null && !string.IsNullOrWhiteSpace(firstResult.Value.ToString()))
{
scannedText = firstResult.Value.ToString();
}
else
{
scannedText = "QR value not found!";
}
}
catch (Exception ex)
{
scannedText = "Error scanning QR code: " + ex.Message;
}
}
private async Task ScanQRCode()
{
// Check if there is a valid image to work with
if (string.IsNullOrEmpty(qrImageSrc)) return;
try
{
var inputBmp = AnyBitmap.FromFile(qrImageSrc);
QrImageInput imageInput = new QrImageInput(inputBmp);
QrReader reader = new QrReader();
IEnumerable<QrResult> results = reader.Read(imageInput);
// Check if there are any results and if the first result contains text
var firstResult = results.FirstOrDefault();
if (firstResult != null && !string.IsNullOrWhiteSpace(firstResult.Value.ToString()))
{
scannedText = firstResult.Value.ToString();
}
else
{
scannedText = "QR value not found!";
}
}
catch (Exception ex)
{
scannedText = "Error scanning QR code: " + ex.Message;
}
}
Private Async Function ScanQRCode() As Task
' Check if there is a valid image to work with
If String.IsNullOrEmpty(qrImageSrc) Then
Return
End If
Try
Dim inputBmp = AnyBitmap.FromFile(qrImageSrc)
Dim imageInput As New QrImageInput(inputBmp)
Dim reader As New QrReader()
Dim results As IEnumerable(Of QrResult) = reader.Read(imageInput)
' Check if there are any results and if the first result contains text
Dim firstResult = results.FirstOrDefault()
If firstResult IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(firstResult.Value.ToString()) Then
scannedText = firstResult.Value.ToString()
Else
scannedText = "QR value not found!"
End If
Catch ex As Exception
scannedText = "Error scanning QR code: " & ex.Message
End Try
End Function
Inicialmente, el método comprueba si la variable qrImageSrc
, que contiene la ruta a la imagen cargada, no está vacía. Esta comprobación garantiza que existe una imagen válida con la que trabajar antes de continuar.
Una vez confirmado que una imagen está lista para ser procesada, el método procede a la funcionalidad principal de lectura de códigos QR. Esto implica unos cuantos pasos clave, empezando por cargar la imagen desde su ubicación almacenada a un formato adecuado para el análisis de códigos QR. Esta conversión es posible gracias a la función AnyBitmap.FromFile(qrImageSrc)
, que prepara la imagen para el proceso de escaneado.
El siguiente paso consiste en la creación de un objeto QrReader
. Este objeto forma parte de la biblioteca IronQR y es la herramienta principal para descodificar códigos QR a partir de imágenes. Con la instancia QrReader
lista, la aplicación procede a escanear la imagen cargada. La función reader.Read(imageInput)
se encarga de esta acción, buscando metódicamente en la imagen los códigos QR y extrayendo sus datos.
Los resultados de la exploración se almacenan en un IEnumerable<QrResult>
colección. A continuación, se examina esta colección para encontrar el primer resultado del código QR. Si se detecta un código QR, y contiene texto legible, este texto es capturado y almacenado en la variable scannedText
. Sin embargo, en los casos en los que no se encuentra un código QR o no contiene texto, la aplicación establece un mensaje predeterminado para informar al usuario de que no se ha detectado ningún valor QR.
Una vez que el código QR se ha escaneado correctamente, la cadena de texto se muestra en un campo de entrada de texto, gracias a las capacidades de enlace de datos bidireccional de Blazor. Esto se consigue vinculando la variable scannedText
a un elemento de entrada de texto. El campo de entrada está desactivado, por lo que es de sólo lectura. Esta elección de diseño centra la interacción del usuario hacia la visualización del resultado y su copia en lugar de la edición del contenido.
Todo el proceso de escaneado está encerrado dentro de un bloque try-catch, que protege contra cualquier error imprevisto durante la operación de escaneado. Esto podría incluir problemas relacionados con el formato del archivo de imagen o errores inesperados durante el proceso de lectura. Si se produce una excepción, se captura y se formula un mensaje de error que se muestra al usuario. Este enfoque no sólo ayuda a detectar problemas, sino que también mantiene la transparencia con el usuario, lo que aumenta la fiabilidad de la aplicación.
Para activar la función de copia al portapapeles, se define una función JavaScript llamada copyTextToClipboard
en el archivo _Host.cshtml
. Este script es una forma sencilla pero eficaz de interactuar con el portapapeles:
<script>
function copyTextToClipboard(text) {
navigator.clipboard.writeText(text).then(function () {
console.log('Copying to clipboard was successful!');
}, function (err) {
console.error('Could not copy text: ', err);
});
}
</script>
Esta función acepta un parámetro de texto, que es el texto a copiar. Utiliza el método navigator.clipboard.writeText
, un enfoque moderno para interactuar con el portapapeles. Este método es el preferido por su sencillez y su conformidad con los estándares web. Está diseñado para registrar un mensaje de éxito en la consola tras una copia correcta, lo que ayuda en la depuración y garantiza un funcionamiento sin problemas. En caso de error, se registra un mensaje de error en la consola, que proporciona información sobre los problemas encontrados durante la operación.
El método CopyToClipboard
está en la parte @code
del index.razor sirve de puente entre la aplicación Blazor y la función JavaScript. Un botón activa el clic de este método en la interfaz de usuario. Cuando se activa, invoca la función JavaScript copyTextToClipboard
utilizando las capacidades JavaScript InterOp de Blazor. El texto escaneado
se pasa como argumento a esta función, copiando efectivamente el texto al portapapeles del usuario.
private async Task CopyToClipboard()
{
await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText);
}
private async Task CopyToClipboard()
{
await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText);
}
Private Async Function CopyToClipboard() As Task
Await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText)
End Function
Al ejecutar el proyecto, el usuario verá la siguiente interfaz limpia y sencilla. La pantalla inicial muestra de forma destacada el módulo de escáner de códigos QR. Este módulo incluye un botón para cargar el archivo de imagen del código QR(Elegir archivo) y otro para iniciar el proceso de escaneado(Escanear código QR). Inicialmente, no se selecciona ningún archivo y el área de escaneado está en blanco, a la espera de que el usuario introduzca los datos.
El usuario selecciona y carga una imagen de código QR mediante el botón "Elegir archivo", que ahora muestra el nombre del archivo seleccionado.(por ejemplo, 'qrvalue.png'). El código QR cargado es visible en la zona designada de la interfaz, lo que confirma al usuario que la imagen está lista para ser escaneada.
Cuando el usuario pulsa el botón "Escanear código QR", la aplicación procesa la imagen. Si el escaneado es correcto, el texto codificado en el código QR aparece justo debajo de la imagen. En este caso, el resultado escaneado( '<https://ironsoftware.com/csharp/qr/>'
) es una URL que indica a dónde dirigirá el código QR al usuario cuando lo escanee con un lector QR. Junto al resultado aparece el botón Copiar, que permite al usuario copiar fácilmente el texto escaneado en el portapapeles para su uso posterior.
En resumen, el proceso de integración del IronQR en una aplicación Blazor Server es fluido y eficaz, dando como resultado una solución de escaneado de códigos QR. Sensible y fácil de usar, desde el inicio de la configuración de este proyecto hasta su implementación de la funcionalidad de escaneado, gracias a una mezcla del potente procesamiento de IronQR con el renderizado dinámico de la interfaz de usuario de Blazor. El proceso, desde la configuración del entorno hasta la implantación, pone de relieve la practicidad y eficacia de esta integración en aplicaciones del mundo real. Aunque IronQR es experto en códigos QR, para proyectos que requieren la función de escaneo de códigos de barras,IronBarcode es una opción ideal, ya que ofrece un nivel similar de facilidad e integración.
IronQR ofrece unprueba gratuita para que los desarrolladores exploren sus características antes de comprarlo. Para un uso extendido y acceso a todas sus características profesionales en producción, las licencias de IronQR comienzan en $749.
9 productos API .NET para sus documentos de oficina