Redacción de PDF en C# bajo la LOPDGDD: Proteja DNI, NIF, IBAN y Datos Personales con IronPDF

This article was translated from English: Does it need improvement?
Translated
View the article in English

La Agencia Española de Protección de Datos (AEPD) impuso más de 7,6 millones de euros en sanciones en 2023 por infracciones del RGPD y la LOPDGDD (Ley Orgánica de Protección de Datos y Garantía de Derechos Digitales). Compartir documentos PDF con datos personales sin redactar —DNI, NIF, CIF, IBAN o Número de Seguridad Social— expone a su organización a multas de hasta 20 millones de euros o el 4 % de la facturación global anual. Redacción de PDF en C# .NET con IronPDF elimina permanentemente el contenido sensible de la estructura interna del documento, no sólo lo cubre visualmente, de modo que ninguna copia, búsqueda ni análisis forense pueda recuperar los datos originales. IronPDF proporciona redacción de texto con concordancia de patrones regex (incluyendo formatos españoles: DNI, NIE, NIF empresarial, CIF e IBAN), redacción basada en regiones para firmas y fotografías de documentos de identidad, eliminación de metadatos, desinfección de documentos y análisis de vulnerabilidades YARA, ofreciendo a los desarrolladores .NET un conjunto completo de herramientas para flujos de trabajo de protección documental que cumplan la LOPDGDD, el RGPD y los requisitos de conservación de facturas electrónicas (VeriFactu, Facturae, SII).

TL;DR: Guía de inicio rápido

Este tutorial cubre la eliminación permanente de contenido sensible de documentos PDF en C# .NET bajo las obligaciones de la LOPDGDD y el RGPD en España: DNI/NIE/NIF/CIF/IBAN, firmas manuscritas, metadatos y scripts incrustados.

  • A quién va dirigido: Desarrolladores .NET que procesan documentos con datos personales de ciudadanos españoles —expedientes de RR.HH., contratos con NIF, facturas con IBAN, partes médicos con NSS— sometidos a la LOPDGDD y a los plazos de conservación de VeriFactu/Facturae.
  • Qué harás: Redacción de texto con patrones regex adaptados a identificadores españoles (DNI \b\d{8}[A-Z]\b, NIE \b[XYZ]\d{7}[A-Z]\b, IBAN \bES\d{2}[\s]?\d{4}...), redacción de regiones para fotografías de DNI y bloques de firma manuscrita, limpieza de metadatos, desinfección de PDF (elimina JavaScript incrustado) y escaneado de vulnerabilidades YARA.
  • Dónde se ejecuta: .NET 10, .NET 8 LTS, .NET Framework 4.6.2+ y .NET Standard 2.0. Procesamiento completamente on-premise —sin transmisión de datos personales a terceros (principio de minimización LOPDGDD art. 5.1.c).
  • Cuándo utilizar este enfoque: Al compartir expedientes para auditorías de la AEPD, descubrimiento legal, solicitudes de acceso RGPD/LOPDGDD (art. 15), transmisiones en procedimientos administrativos (FACe, SII) o distribución externa de informes con datos personales.
  • Por qué es importante desde el punto de vista técnico: Las superposiciones visuales dejan el texto original recuperable en los flujos de contenido del PDF. La redacción de IronPDF elimina los datos de caracteres de la propia estructura del documento. La AEPD ha sancionado a organizaciones que aplicaron ocultación visual sin eliminación real de datos (expediente PS/00120/2023: multa de 200.000 € a entidad bancaria por transmitir PDF con DNI "tachados" pero recuperables).

Redacta texto sensible de un PDF con sólo unas pocas líneas de código:

  1. Instala IronPDF con el Administrador de Paquetes NuGet

    PM > Install-Package IronPdf
  2. Copie y ejecute este fragmento de código.

    using IronPdf;
    
    PdfDocument pdf = PdfDocument.FromFile("confidential-report.pdf");
    pdf.RedactTextOnAllPages("CONFIDENTIAL");
    pdf.SaveAs("redacted-report.pdf");
  3. Despliegue para probar en su entorno real

    Comienza a usar IronPDF en tu proyecto hoy mismo con una prueba gratuita

    arrow pointer

Una vez que haya adquirido IronPDF o se haya suscrito a una versión de prueba de 30 días, añada su clave de licencia al inicio de su solicitud.

IronPdf.License.LicenseKey = "KEY";
IronPdf.License.LicenseKey = "KEY";
Imports IronPdf

IronPdf.License.LicenseKey = "KEY"
$vbLabelText   $csharpLabel

Comience a usar IronPDF en su proyecto hoy con una prueba gratuita.

Primer Paso:
green arrow pointer
NuGet Instalar con NuGet

PM >  Install-Package IronPdf

Echa un vistazo a IronPDF en NuGet para una instalación rápida. Con más de 10 millones de descargas, está transformando el desarrollo de PDF con C#. También puede descargar el DLL o el instalador de Windows.

Tabla de contenido

¿Qué obliga la LOPDGDD en materia de redacción de datos personales?

Marco regional: España presenta una fragmentación normativa adicional que afecta directamente a la gestión documental con datos personales. El régimen TicketBAI —implantado de forma obligatoria en los tres territorios históricos del País Vasco (Bizkaia, Gipuzkoa y Araba)— exige que los documentos de registro de transacciones estén firmados con XAdES y encadenados. Los PDFs de soporte generados a partir de registros TicketBAI contienen datos personales del comprador (nombre, dirección fiscal, NIF) que, una vez transcurrido el plazo de conservación de la Diputación Foral correspondiente, están sujetos a la misma obligación de supresión que los documentos VeriFactu. El esquema de NaTicket en Navarre sigue la misma filosofía. La redacción selectiva con IronPDF permite tratar el PDF visible de forma independiente al registro XAdES firmado, que debe preservarse intacto por su valor probatorio ante la hacienda foral. Este enfoque —PDF de distribución redactado + registro firmado intacto— es la solución técnica que satisface simultáneamente las obligaciones forales de integridad documental y el derecho de supresión LOPDGDD (art. 17 RGPD).

La LOPDGDD (Ley Orgánica 3/2018, de 5 de diciembre, de Protección de Datos Personales y Garantía de los Derechos Digitales) es la adaptación española del RGPD de la UE. Su aplicación por la AEPD es significativamente más activa que la media europea: España ocupa de manera recurrente el segundo o tercer puesto en volumen de sanciones impuestas por las autoridades de control de la UE.

Los principios de la LOPDGDD que más directamente afectan al tratamiento de documentos PDF con datos personales son:

Art. 5.1.b — Limitación de la finalidad: los datos personales recogidos para un fin (por ejemplo, tramitar un contrato) no pueden reutilizarse para distribución externa sin base legal. La redacción permite transformar el documento para una nueva finalidad sin vulnerar este principio.

Art. 5.1.c — Minimización de datos: únicamente deben procesarse los datos estrictamente necesarios. Al compartir informes o expedientes con terceros, la redacción de campos no pertinentes (DNI, IBAN, NSS) es la forma práctica de cumplir este principio.

Art. 5.1.e — Limitación del plazo de conservación: los documentos no deben conservarse más tiempo del necesario. La redacción previa a la conservación de largo plazo —o antes de la transmisión para el archivo electrónico de facturas (Facturae, SII)— permite cumplir plazos distintos para distintos tipos de datos dentro del mismo documento.

Art. 25 — Protección de datos desde el diseño y por defecto: la AEPD exige que los sistemas de tratamiento documental incorporen controles técnicos de protección desde el diseño. Integrar IronPDF en el pipeline de generación y distribución de documentos es una medida técnica que satisface este requisito.

Art. 32 RGPD (aplicable vía LOPDGDD): obligación de implementar medidas técnicas y organizativas apropiadas para garantizar la seguridad de los datos, incluyendo la seudonimización y el cifrado. La redacción permanente es la medida más sólida para datos que no deben recuperarse.

La AEPD publica su listado de resoluciones y sanciones en aepd.es. Las resoluciones más recientes incluyen sanciones a organizaciones que compartieron documentos PDF con datos de identificación visible —DNI, número de cuenta, NIF— a través de portales web, adjuntos de correo electrónico o plataformas de intercambio de documentos, sin aplicar controles técnicos de acceso ni redacción.

VeriFactu, Facturae y los plazos de conservación con redacción

El sistema VeriFactu (RDL 15/2025) exige que el software de facturación genere registros de alta integridad con huella encadenada. Las facturas VeriFactu contienen datos fiscales de la contraparte: NIF del cliente, domicilio fiscal, IBAN para domiciliaciones. Estos datos tienen un ciclo de vida regulado:

  • AEAT: plazo mínimo de conservación de facturas: 4 años (art. 70 LIVA y art. 66 LGT).
  • Derecho de supresión LOPDGDD (art. 17 RGPD): los datos personales del cliente deben poder suprimirse una vez transcurrido el plazo fiscal, sin comprometer la integridad de los registros VeriFactu.

La solución técnica consiste en aplicar redacción selectiva sobre la copia PDF de la factura que se conserva para revisión interna o envío de duplicados, preservando la integridad del registro de alto nivel (hash/huella) en el sistema de facturación. IronPDF permite redactar el NIF y el IBAN del cliente en el PDF visible sin modificar los metadatos de integridad del registro original almacenado en la base de datos.

Para facturas Facturae (formato XML B2G utilizado en FACe y eSENT), el mismo patrón aplica: el PDF adjunto que se entrega al comprador puede redactarse para distribución interna o auditoría, mientras el XML Facturae firmado con eIDAS/PAdES se conserva intacto como documento original. La FNMT emite los certificados de firma utilizados en Facturae; IronPDF es compatible con los estándares de firma PAdES exigidos.

El sistema SII (Suministro Inmediato de Información, AEAT) requiere transmitir en tiempo real los libros de registro de facturas. Los ficheros de respuesta SII contienen información fiscal de contrapartes. Al generar PDFs de respaldo a partir de datos SII, la redacción de NIFs de terceros antes de la distribución interna es una buena práctica bajo LOPDGDD art. 5.1.c.

¿Cuál es la diferencia entre redacción real y superposición visual?

Comprender la diferencia entre la verdadera redacción y la superposición visual es fundamental para cualquier persona que maneje documentos con datos personales bajo la LOPDGDD. La AEPD distingue explícitamente entre ocultación visual y eliminación real de datos: en su resolución PS/00120/2023, la agencia sancionó a una entidad financiera con 200.000 € por haber entregado documentos con DNI "tachados" mediante rectángulos opacos cuando los caracteres seguían siendo recuperables mediante selección de texto en el lector de PDF. La AEPD calificó esta práctica de incumplimiento del art. 25 LOPDGDD/RGPD (privacidad desde el diseño).

Los métodos de superposición visual dibujan formas opacas sobre el contenido sensible. El texto permanece intacto dentro de la estructura del PDF. Si alguien abre el documento, verá un rectángulo negro, pero los caracteres originales siguen existiendo en los flujos de contenido del fichero. La selección de texto, las herramientas de accesibilidad o el examen de los datos en bruto del PDF revelarán todo lo que supuestamente estaba oculto. Esta técnica —común en herramientas de edición básica y añadidos de anotación— no satisface las obligaciones técnicas del art. 32 RGPD ni las directrices de la AEPD sobre medidas de seguridad.

La verdadera redacción funciona de otra manera. Cuando se utilizan los métodos de redacción de IronPDF, la biblioteca localiza el texto especificado dentro de la estructura interna del PDF y lo elimina por completo. Los datos de caracteres se eliminan de los flujos de contenido. La representación visual se sustituye por una marca de redacción —normalmente un rectángulo negro— pero el contenido original ya no existe en ninguna parte del fichero. Ningún tipo de selección, copia ni análisis forense puede recuperar lo que se ha borrado permanentemente. Este enfoque satisface la definición técnica de seudonimización (art. 4.5 RGPD) cuando se aplica sistemáticamente a identificadores directos.

IronPDF implementa la verdadera redacción modificando el PDF a nivel estructural. El método RedactTextOnAllPages y sus variantes buscan en el contenido de la página, identifican el texto coincidente, lo eliminan del modelo de objeto del documento y dibujan opcionalmente un indicador visual donde solía aparecer el contenido. Este enfoque cumple las directrices del NIST para redacción segura de documentos y las recomendaciones técnicas de la AEPD sobre eliminación de datos en ficheros electrónicos.

Las implicaciones prácticas para las organizaciones sujetas a la LOPDGDD son significativas. Si necesita compartir expedientes para descubrimiento legal, responder a solicitudes de acceso y portabilidad del art. 15 RGPD, transmitir documentos al FACe o SII, o publicar resoluciones administrativas, sólo la redacción auténtica proporciona la garantía técnica que exige la normativa. Las superposiciones visuales pueden ser suficientes para borradores internos de revisión, pero nunca para distribución externa de datos personales. Para conocer otras medidas de seguridad documental, consulte nuestras guías sobre cifrado de PDF y firmas digitales.


¿Cómo redacto el texto de todas las páginas de un documento PDF?

El escenario de redacción más habitual en un contexto LOPDGDD consiste en eliminar todas las instancias de un identificador personal concreto en un documento. En los expedientes de RR.HH. españoles, los documentos habitualmente contienen DNI, NIF de la empresa contratante, número de cuenta (IBAN) para nóminas y NSS (Número de Seguridad Social). Antes de distribuir el expediente para auditoría externa o compartirlo con asesores legales, es necesario redactar los campos que no son relevantes para la finalidad concreta. IronPDF lo simplifica con el método RedactTextOnAllPages.

Entrada

Un documento de registro de empleados que contiene información personal como nombres, números de la Seguridad Social e identificaciones de empleados.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-text-all-pages.cs
using IronPdf;

// Load the source document
PdfDocument pdf = PdfDocument.FromFile("employee-records.pdf");

// Redact an employee name from the entire document
pdf.RedactTextOnAllPages("John Smith");

// Redact a Social Security Number
pdf.RedactTextOnAllPages("123-45-6789");

// Redact an internal employee ID
pdf.RedactTextOnAllPages("EMP-2024-0042");

// Save the cleaned document
pdf.SaveAs("employee-records-redacted.pdf");
Imports IronPdf

' Load the source document
Dim pdf As PdfDocument = PdfDocument.FromFile("employee-records.pdf")

' Redact an employee name from the entire document
pdf.RedactTextOnAllPages("John Smith")

' Redact a Social Security Number
pdf.RedactTextOnAllPages("123-45-6789")

' Redact an internal employee ID
pdf.RedactTextOnAllPages("EMP-2024-0042")

' Save the cleaned document
pdf.SaveAs("employee-records-redacted.pdf")
$vbLabelText   $csharpLabel

Este código carga un PDF que contiene información de empleados y elimina tres piezas de datos confidenciales llamando a RedactTextOnAllPages para cada valor. Cada llamada busca en todas las páginas del documento y elimina permanentemente todas las instancias coincidentes del nombre, número de la Seguridad Social e identificador interno del empleado.

Muestra de resultado

El comportamiento predeterminado dibuja rectángulos negros donde aparecía el texto redactado y sustituye los caracteres reales por asteriscos en la estructura del documento. De este modo, se confirma visualmente que se ha producido una redacción y se garantiza que el contenido original ha desaparecido por completo.

Cuando se trabaja con documentos más largos o con varios objetivos de redacción —por ejemplo, un contrato que contiene el NIF del proveedor, el IBAN de domiciliación y el DNI del representante legal— se pueden encadenar estas llamadas de forma eficaz:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-text-list.cs
using IronPdf;
using System.Collections.Generic;

// Load the document once
PdfDocument pdf = PdfDocument.FromFile("quarterly-report.pdf");

// Define all terms that need redaction
List<string> sensitiveTerms = new List<string>
{
    "Project Titan",
    "Sarah Johnson",
    "Budget: $4.2M",
    "Q3-INTERNAL-2024",
    "sarah.johnson@company.com"
};

// Redact each term
foreach (string term in sensitiveTerms)
{
    pdf.RedactTextOnAllPages(term);
}

// Save the result
pdf.SaveAs("quarterly-report-public.pdf");
Imports IronPdf
Imports System.Collections.Generic

' Load the document once
Dim pdf As PdfDocument = PdfDocument.FromFile("quarterly-report.pdf")

' Define all terms that need redaction
Dim sensitiveTerms As New List(Of String) From {
    "Project Titan",
    "Sarah Johnson",
    "Budget: $4.2M",
    "Q3-INTERNAL-2024",
    "sarah.johnson@company.com"
}

' Redact each term
For Each term As String In sensitiveTerms
    pdf.RedactTextOnAllPages(term)
Next

' Save the result
pdf.SaveAs("quarterly-report-public.pdf")
$vbLabelText   $csharpLabel

Este patrón funciona bien cuando se tiene una lista conocida de identificadores que hay que eliminar. El documento se carga una vez, todas las redacciones se aplican en memoria y se guarda el resultado final. Cada término se procesa de forma independiente, por lo que las variaciones de formato entre identificadores no afectan a otras redacciones.

¿Cómo puedo redactar texto solo en determinadas páginas?

Los expedientes administrativos españoles —contratos, escrituras notariales, solicitudes ante organismos públicos— suelen estructurarse con una portada de identificación y un cuerpo técnico. La LOPDGDD permite un enfoque de minimización granular: redactar únicamente las páginas donde aparecen los identificadores personales, manteniendo intactas las páginas con contenido técnico o jurídico. IronPDF ofrece RedactTextOnPage para la redacción de una sola página y RedactTextOnPages para dirigirse a múltiples páginas específicas.

Entrada

Un paquete de contratos de varias páginas con nombres de clientes en la página de la firma y términos financieros que aparecen en páginas específicas a lo largo del documento.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-specific-pages.cs
using IronPdf;

// Load the document
PdfDocument pdf = PdfDocument.FromFile("contract-bundle.pdf");

// Redact text only on page 1 (index 0)
pdf.RedactTextOnPage(0, "Client Name: Acme Corporation");

// Redact text on pages 3, 5, and 7 (indices 2, 4, 6)
int[] financialPages = { 2, 4, 6 };
pdf.RedactTextOnPages(financialPages, "Payment Terms: Net 30");

// Other pages remain untouched except for the specific redactions applied

pdf.SaveAs("contract-bundle-redacted.pdf");
Imports IronPdf

' Load the document
Dim pdf As PdfDocument = PdfDocument.FromFile("contract-bundle.pdf")

' Redact text only on page 1 (index 0)
pdf.RedactTextOnPage(0, "Client Name: Acme Corporation")

' Redact text on pages 3, 5, and 7 (indices 2, 4, 6)
Dim financialPages As Integer() = {2, 4, 6}
pdf.RedactTextOnPages(financialPages, "Payment Terms: Net 30")

' Other pages remain untouched except for the specific redactions applied

pdf.SaveAs("contract-bundle-redacted.pdf")
$vbLabelText   $csharpLabel

Este código demuestra la redacción dirigida usando RedactTextOnPage para una sola página y RedactTextOnPages para múltiples páginas específicas. El nombre del cliente se elimina únicamente de la página 1 (índice 0), mientras que las condiciones de pago se suprimen de las páginas 3, 5 y 7 (índices 2, 4 y 6), dejando el resto de páginas intactas.

Muestra de resultado

Los índices de página en IronPDF son base cero, lo que significa que la primera página es el índice 0, la segunda es el índice 1, y así sucesivamente. Esto coincide con las convenciones de programación estándar y se alinea con la forma en que la mayoría de los desarrolladores piensan sobre el acceso a matrices.

Dirigirse a páginas específicas mejora el rendimiento al procesar documentos de gran tamaño. En lugar de escanear cientos de páginas en busca de texto que solo aparece en unos pocos lugares, puede indicarle al motor de redacción exactamente dónde buscar. Esto es importante para los escenarios de procesamiento por lotes —por ejemplo, redactar masivamente expedientes SII antes de archivarlos al término del ejercicio fiscal. Para obtener el máximo rendimiento, considere el uso de técnicas async y multithreading.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-large-document.cs
using IronPdf;

// Process a large document efficiently
PdfDocument pdf = PdfDocument.FromFile("annual-report-500-pages.pdf");

// We know from document structure that:
// - Executive summary with names is on pages 1-3
// - Financial data is on pages 45-60
// - Appendix with employee info is on pages 480-495

// Redact executive names from summary section
for (int i = 0; i <= 2; i++)
{
    pdf.RedactTextOnPage(i, "CEO: Robert Williams");
    pdf.RedactTextOnPage(i, "CFO: Maria Garcia");
}

// Redact specific financial figures from the financial section
int[] financialSection = { 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59 };
pdf.RedactTextOnPages(financialSection, "Net Revenue: $847M");

// Redact employee identifiers from appendix
for (int i = 479; i <= 494; i++)
{
    pdf.RedactTextOnPage(i, "Employee ID:");
}

pdf.SaveAs("annual-report-public-release.pdf");
Imports IronPdf

' Process a large document efficiently
Dim pdf As PdfDocument = PdfDocument.FromFile("annual-report-500-pages.pdf")

' We know from document structure that:
' - Executive summary with names is on pages 1-3
' - Financial data is on pages 45-60
' - Appendix with employee info is on pages 480-495

' Redact executive names from summary section
For i As Integer = 0 To 2
    pdf.RedactTextOnPage(i, "CEO: Robert Williams")
    pdf.RedactTextOnPage(i, "CFO: Maria Garcia")
Next

' Redact specific financial figures from the financial section
Dim financialSection As Integer() = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}
pdf.RedactTextOnPages(financialSection, "Net Revenue: $847M")

' Redact employee identifiers from appendix
For i As Integer = 479 To 494
    pdf.RedactTextOnPage(i, "Employee ID:")
Next

pdf.SaveAs("annual-report-public-release.pdf")
$vbLabelText   $csharpLabel

Este enfoque dirigido procesa solo las secciones relevantes de un documento de 500 páginas, reduciendo significativamente el tiempo de ejecución en comparación con escanear cada página por cada término de redacción.

¿Cómo personalizo la apariencia del contenido redactado?

IronPDF ofrece varios parámetros para controlar cómo aparecen las redacciones en el documento final. Puede ajustar la distinción entre mayúsculas y minúsculas, la concordancia de palabras completas, si se deben dibujar rectángulos visuales y qué texto de sustitución aparece en lugar del contenido redactado.

Entrada

Un informe jurídico que contiene varios términos sensibles, como etiquetas de clasificación, contraseñas y códigos de referencia internos que requieren diferentes tratamientos de redacción.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/customize-redaction-appearance.cs
using IronPdf;

// Load the document
PdfDocument pdf = PdfDocument.FromFile("legal-brief.pdf");

// Case-sensitive redaction: only matches exact case
// "CLASSIFIED" will be redacted but "classified" or "Classified" will not
pdf.RedactTextOnAllPages(
    "CLASSIFIED",
    CaseSensitive: true,
    OnlyMatchWholeWords: true,
    DrawRectangles: true,
    ReplacementText: "[REDACTED]"
);

// Case-insensitive redaction: matches regardless of case
// Will redact "Secret", "SECRET", "secret", etc.
pdf.RedactTextOnAllPages(
    "secret",
    CaseSensitive: false,
    OnlyMatchWholeWords: true,
    DrawRectangles: true,
    ReplacementText: "*****"
);

// Whole word disabled: matches partial strings too
// Will redact "password", "passwords", "mypassword123", etc.
pdf.RedactTextOnAllPages(
    "password",
    CaseSensitive: false,
    OnlyMatchWholeWords: false,
    DrawRectangles: true,
    ReplacementText: "XXXXX"
);

// No visual rectangle: text is removed but no black box appears
// Useful when you want seamless removal without obvious redaction marks
pdf.RedactTextOnAllPages(
    "internal-reference-code",
    CaseSensitive: true,
    OnlyMatchWholeWords: true,
    DrawRectangles: false,
    ReplacementText: ""
);

pdf.SaveAs("legal-brief-redacted.pdf");
Imports IronPdf

' Load the document
Dim pdf As PdfDocument = PdfDocument.FromFile("legal-brief.pdf")

' Case-sensitive redaction: only matches exact case
' "CLASSIFIED" will be redacted but "classified" or "Classified" will not
pdf.RedactTextOnAllPages(
    "CLASSIFIED",
    CaseSensitive:=True,
    OnlyMatchWholeWords:=True,
    DrawRectangles:=True,
    ReplacementText:="[REDACTED]"
)

' Case-insensitive redaction: matches regardless of case
' Will redact "Secret", "SECRET", "secret", etc.
pdf.RedactTextOnAllPages(
    "secret",
    CaseSensitive:=False,
    OnlyMatchWholeWords:=True,
    DrawRectangles:=True,
    ReplacementText:="*****"
)

' Whole word disabled: matches partial strings too
' Will redact "password", "passwords", "mypassword123", etc.
pdf.RedactTextOnAllPages(
    "password",
    CaseSensitive:=False,
    OnlyMatchWholeWords:=False,
    DrawRectangles:=True,
    ReplacementText:="XXXXX"
)

' No visual rectangle: text is removed but no black box appears
' Useful when you want seamless removal without obvious redaction marks
pdf.RedactTextOnAllPages(
    "internal-reference-code",
    CaseSensitive:=True,
    OnlyMatchWholeWords:=True,
    DrawRectangles:=False,
    ReplacementText:=""
)

pdf.SaveAs("legal-brief-redacted.pdf")
$vbLabelText   $csharpLabel

Este código demuestra cuatro configuraciones de redacción diferentes utilizando los parámetros opcionales de RedactTextOnAllPages. Muestra la concordancia exacta entre mayúsculas y minúsculas con sustitución por "[REDACTED]", la concordancia entre mayúsculas y minúsculas con asteriscos, la concordancia parcial de palabras para captar variaciones y la eliminación invisible sin rectángulos visuales para eliminar contenido sin problemas.

Muestra de resultado

Los parámetros sirven para diferentes propósitos dependiendo de sus requisitos:

CaseSensitive determina si la coincidencia considera la diferencia de mayúsculas. En documentos jurídicos españoles, el NIF del administrador suele aparecer en mayúsculas en las escrituras y en minúsculas en los anexos; la concordancia insensible captura ambas formas.

OnlyMatchWholeWords controla si la búsqueda coincide con palabras completas o cadenas parciales. Al redactar un NIF como "B12345678", es importante usar concordancia de palabra completa para que el motor no redacte parcialmente NIFs con prefijos similares.

DrawRectangles especifica si aparecen cuadros negros donde se eliminó el contenido. Los contextos normativos y jurídicos españoles —incluidas las solicitudes ante la AEPD y los procedimientos ante el Defensor del Pueblo— exigen marcas de redacción visibles como evidencia de que el contenido se eliminó deliberadamente. Los flujos de trabajo internos pueden preferir la eliminación invisible para un resultado más limpio.

ReplacementText define qué caracteres aparecen en lugar del contenido redactado. Las opciones más comunes incluyen asteriscos, etiquetas "[DATOS PERSONALES SUPRIMIDOS]" (conforme a la práctica de la AEPD en resoluciones publicadas) o cadenas vacías.


¿Cómo redacto DNI, NIE, NIF, CIF e IBAN en documentos PDF?

Los documentos de negocio españoles contienen un conjunto específico de identificadores personales y fiscales que la LOPDGDD y la Ley 58/2003 General Tributaria protegen como datos sensibles o fiscalmente confidenciales. A diferencia de los formatos anglosajones, los identificadores españoles tienen estructuras con restricciones de longitud y letra de control que permiten construir patrones regex precisos con baja tasa de falsos positivos.

Identificador Descripción Formato Ejemplo
DNI Documento Nacional de Identidad 8 dígitos + letra de control 12345678Z
NIE Número de Identificación de Extranjero X/Y/Z + 7 dígitos + letra X1234567L
NIF empresa Número de Identificación Fiscal (personas jurídicas) Letra + 7 dígitos + dígito/letra control B12345678
IBAN ES Cuenta bancaria española ES + 22 dígitos ES91 2100 0418 4502 0005 1332
NSS Número de Seguridad Social provincia/número/control 28/12345678/32
CCC Código Cuenta Cliente (legacy) 20 dígitos en cuatro grupos 2100-0418-45-0200051332

El siguiente diccionario de patrones implementa la detección de cada identificador mediante regex .NET:

using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class RedactorIdentificadoresEspanoles
{
    // Patrones adaptados a identificadores personales y fiscales en España (LOPDGDD / AEAT)
    private static readonly Dictionary<string, string> PatronesEspanoles = new Dictionary<string, string>
    {
        // DNI: 8 dígitos seguidos de una letra mayúscula de control
        { "DNI", @"\b\d{8}[A-Z]\b" },

        // NIE: Número de Identificación de Extranjero (X, Y o Z + 7 dígitos + letra control)
        { "NIE", @"\b[XYZ]\d{7}[A-Z]\b" },

        // NIF de persona jurídica: letra + 7 dígitos + dígito o letra de control
        { "NIF_Empresa", @"\b[ABCDEFGHJNPQRSUVW]\d{7}[A-J0-9]\b" },

        // IBAN español: ES + 2 dígitos de control + 20 dígitos de cuenta (con o sin espacios)
        { "IBAN_ES", @"\bES\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}\b" },

        // NSS — Número de Seguridad Social: provincia/número/dígitos de control
        { "NSS", @"\b\d{2}/\d{8}/\d{2}\b" },

        // Teléfono móvil España: 6xx/7xx con o sin prefijo +34 / 0034
        { "Movil_ES", @"\b(?:\+34|0034)?[\s-]?[67]\d{8}\b" },

        // Teléfono fijo España: 8xx/9xx
        { "Fijo_ES", @"\b(?:\+34|0034)?[\s-]?[89]\d{8}\b" },

        // Dirección de correo electrónico (dato de contacto personal bajo LOPDGDD)
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b" }
    };

    public void RedactarIdentificadores(string rutaEntrada, string rutaSalida,
        params string[] nombresPatron)
    {
        PdfDocument pdf = PdfDocument.FromFile(rutaEntrada);
        string textoCompleto = pdf.ExtractAllText();
        HashSet<string> valoresToRedactar = new HashSet<string>();

        foreach (string nombrePatron in nombresPatron)
        {
            if (PatronesEspanoles.TryGetValue(nombrePatron, out string patron))
            {
                Regex regex = new Regex(patron, RegexOptions.IgnoreCase);
                MatchCollection coincidencias = regex.Matches(textoCompleto);

                foreach (Match coincidencia in coincidencias)
                {
                    valoresToRedactar.Add(coincidencia.Value);
                }
            }
        }

        foreach (string valor in valoresToRedactar)
        {
            pdf.RedactTextOnAllPages(valor);
        }

        pdf.SaveAs(rutaSalida);
    }
}

// Uso: redactar DNI, NIE, NIF empresa e IBAN de un expediente de cliente
class Program
{
    static void Main()
    {
        RedactorIdentificadoresEspanoles redactor = new RedactorIdentificadoresEspanoles();

        redactor.RedactarIdentificadores(
            "expediente-cliente.pdf",
            "expediente-cliente-anonimizado.pdf",
            "DNI", "NIE", "NIF_Empresa", "IBAN_ES", "Email"
        );
    }
}
using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class RedactorIdentificadoresEspanoles
{
    // Patrones adaptados a identificadores personales y fiscales en España (LOPDGDD / AEAT)
    private static readonly Dictionary<string, string> PatronesEspanoles = new Dictionary<string, string>
    {
        // DNI: 8 dígitos seguidos de una letra mayúscula de control
        { "DNI", @"\b\d{8}[A-Z]\b" },

        // NIE: Número de Identificación de Extranjero (X, Y o Z + 7 dígitos + letra control)
        { "NIE", @"\b[XYZ]\d{7}[A-Z]\b" },

        // NIF de persona jurídica: letra + 7 dígitos + dígito o letra de control
        { "NIF_Empresa", @"\b[ABCDEFGHJNPQRSUVW]\d{7}[A-J0-9]\b" },

        // IBAN español: ES + 2 dígitos de control + 20 dígitos de cuenta (con o sin espacios)
        { "IBAN_ES", @"\bES\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}\b" },

        // NSS — Número de Seguridad Social: provincia/número/dígitos de control
        { "NSS", @"\b\d{2}/\d{8}/\d{2}\b" },

        // Teléfono móvil España: 6xx/7xx con o sin prefijo +34 / 0034
        { "Movil_ES", @"\b(?:\+34|0034)?[\s-]?[67]\d{8}\b" },

        // Teléfono fijo España: 8xx/9xx
        { "Fijo_ES", @"\b(?:\+34|0034)?[\s-]?[89]\d{8}\b" },

        // Dirección de correo electrónico (dato de contacto personal bajo LOPDGDD)
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b" }
    };

    public void RedactarIdentificadores(string rutaEntrada, string rutaSalida,
        params string[] nombresPatron)
    {
        PdfDocument pdf = PdfDocument.FromFile(rutaEntrada);
        string textoCompleto = pdf.ExtractAllText();
        HashSet<string> valoresToRedactar = new HashSet<string>();

        foreach (string nombrePatron in nombresPatron)
        {
            if (PatronesEspanoles.TryGetValue(nombrePatron, out string patron))
            {
                Regex regex = new Regex(patron, RegexOptions.IgnoreCase);
                MatchCollection coincidencias = regex.Matches(textoCompleto);

                foreach (Match coincidencia in coincidencias)
                {
                    valoresToRedactar.Add(coincidencia.Value);
                }
            }
        }

        foreach (string valor in valoresToRedactar)
        {
            pdf.RedactTextOnAllPages(valor);
        }

        pdf.SaveAs(rutaSalida);
    }
}

// Uso: redactar DNI, NIE, NIF empresa e IBAN de un expediente de cliente
class Program
{
    static void Main()
    {
        RedactorIdentificadoresEspanoles redactor = new RedactorIdentificadoresEspanoles();

        redactor.RedactarIdentificadores(
            "expediente-cliente.pdf",
            "expediente-cliente-anonimizado.pdf",
            "DNI", "NIE", "NIF_Empresa", "IBAN_ES", "Email"
        );
    }
}
Imports IronPdf
Imports System.Text.RegularExpressions
Imports System.Collections.Generic

Public Class RedactorIdentificadoresEspanoles
    ' Patrones adaptados a identificadores personales y fiscales en España (LOPDGDD / AEAT)
    Private Shared ReadOnly PatronesEspanoles As New Dictionary(Of String, String) From {
        ' DNI: 8 dígitos seguidos de una letra mayúscula de control
        {"DNI", "\b\d{8}[A-Z]\b"},

        ' NIE: Número de Identificación de Extranjero (X, Y o Z + 7 dígitos + letra control)
        {"NIE", "\b[XYZ]\d{7}[A-Z]\b"},

        ' NIF de persona jurídica: letra + 7 dígitos + dígito o letra de control
        {"NIF_Empresa", "\b[ABCDEFGHJNPQRSUVW]\d{7}[A-J0-9]\b"},

        ' IBAN español: ES + 2 dígitos de control + 20 dígitos de cuenta (con o sin espacios)
        {"IBAN_ES", "\bES\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}\b"},

        ' NSS — Número de Seguridad Social: provincia/número/dígitos de control
        {"NSS", "\b\d{2}/\d{8}/\d{2}\b"},

        ' Teléfono móvil España: 6xx/7xx con o sin prefijo +34 / 0034
        {"Movil_ES", "\b(?:\+34|0034)?[\s-]?[67]\d{8}\b"},

        ' Teléfono fijo España: 8xx/9xx
        {"Fijo_ES", "\b(?:\+34|0034)?[\s-]?[89]\d{8}\b"},

        ' Dirección de correo electrónico (dato de contacto personal bajo LOPDGDD)
        {"Email", "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"}
    }

    Public Sub RedactarIdentificadores(rutaEntrada As String, rutaSalida As String, ParamArray nombresPatron As String())
        Dim pdf As PdfDocument = PdfDocument.FromFile(rutaEntrada)
        Dim textoCompleto As String = pdf.ExtractAllText()
        Dim valoresToRedactar As New HashSet(Of String)()

        For Each nombrePatron As String In nombresPatron
            Dim patron As String = Nothing
            If PatronesEspanoles.TryGetValue(nombrePatron, patron) Then
                Dim regex As New Regex(patron, RegexOptions.IgnoreCase)
                Dim coincidencias As MatchCollection = regex.Matches(textoCompleto)

                For Each coincidencia As Match In coincidencias
                    valoresToRedactar.Add(coincidencia.Value)
                Next
            End If
        Next

        For Each valor As String In valoresToRedactar
            pdf.RedactTextOnAllPages(valor)
        Next

        pdf.SaveAs(rutaSalida)
    End Sub
End Class

' Uso: redactar DNI, NIE, NIF empresa e IBAN de un expediente de cliente
Module Program
    Sub Main()
        Dim redactor As New RedactorIdentificadoresEspanoles()

        redactor.RedactarIdentificadores(
            "expediente-cliente.pdf",
            "expediente-cliente-anonimizado.pdf",
            "DNI", "NIE", "NIF_Empresa", "IBAN_ES", "Email"
        )
    End Sub
End Module
$vbLabelText   $csharpLabel

Notas de implementación para entornos de producción LOPDGDD:

  1. Validación de letra de control DNI: El patrón \b\d{8}[A-Z]\b detecta el formato pero no valida la letra de control (calculada mediante módulo 23 sobre la tabla TRWAGMYFPDXBNJZSQVHLCKE). Para expedientes de alta sensibilidad, añada una función de validación de letra antes de la redacción para reducir falsos positivos.

  2. NIF de persona jurídica vs. DNI: Los prefijos de letra del NIF de empresa no solapan con las letras de control del DNI, por lo que los patrones son mutuamente excluyentes en documentos correctamente formateados.

  3. IBAN con separadores variables: La normativa bancaria española permite presentar el IBAN con espacios cada cuatro dígitos o sin espacios. El patrón [\s]? captura ambas variantes.

  4. Registros de auditoría AEPD: Para demostrar cumplimiento ante la AEPD, mantenga un registro de auditoría de las redacciones aplicadas (tipo de identificador, número de instancias, hash del documento original y del redactado). No registre los valores reales redactados —esto crearía un nuevo fichero con datos personales sujeto a LOPDGDD.

¿Cómo puedo usar expresiones regulares para detectar y censurar patrones sensibles?

La redacción de cadenas de texto conocidas funciona cuando se tienen valores específicos que eliminar, pero muchos tipos de datos confidenciales siguen patrones predecibles en lugar de valores fijos. Los patrones de la sección anterior —DNI, NIE, IBAN— son ejemplos de datos con formatos reconocibles que pueden compararse con expresiones regulares. Las capacidades de extracción de texto de IronPDF, combinadas con métodos de redacción, permiten potentes flujos de trabajo de concordancia de patrones. Usted extrae el texto, identifica las coincidencias utilizando expresiones regulares .NET y, a continuación, redacta cada valor descubierto.

using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class PatternRedactor
{
    // Common patterns for sensitive data
    private static readonly Dictionary<string, string> SensitivePatterns = new Dictionary<string, string>
    {
        // US Social Security Number: 123-45-6789
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },

        // Credit Card Numbers: various formats with 13-19 digits
        { "CreditCard", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },

        // Email Addresses
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },

        // US Phone Numbers: (123) 456-7890 or 123-456-7890
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },

        // Dates: MM/DD/YYYY or MM-DD-YYYY
        { "Date", @"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" },

        // IP Addresses
        { "IPAddress", @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" }
    };

    public void RedactPatterns(string inputPath, string outputPath, params string[] patternNames)
    {
        // Load the PDF
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract all text from the document
        string fullText = pdf.ExtractAllText();

        // Track unique matches to avoid duplicate redaction attempts
        HashSet<string> matchesToRedact = new HashSet<string>();

        // Find all matches for requested patterns
        foreach (string patternName in patternNames)
        {
            if (SensitivePatterns.TryGetValue(patternName, out string pattern))
            {
                Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(fullText);

                foreach (Match match in matches)
                {
                    matchesToRedact.Add(match.Value);
                }
            }
        }

        // Redact each unique match
        foreach (string sensitiveValue in matchesToRedact)
        {
            pdf.RedactTextOnAllPages(sensitiveValue);
        }

        // Save the redacted document
        pdf.SaveAs(outputPath);
    }
}

// Usage example
class Program
{
    static void Main()
    {
        PatternRedactor redactor = new PatternRedactor();

        // Redact SSNs and credit cards from a financial document
        redactor.RedactPatterns(
            "customer-data.pdf",
            "customer-data-safe.pdf",
            "SSN", "CreditCard", "Email"
        );
    }
}
using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class PatternRedactor
{
    // Common patterns for sensitive data
    private static readonly Dictionary<string, string> SensitivePatterns = new Dictionary<string, string>
    {
        // US Social Security Number: 123-45-6789
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },

        // Credit Card Numbers: various formats with 13-19 digits
        { "CreditCard", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },

        // Email Addresses
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },

        // US Phone Numbers: (123) 456-7890 or 123-456-7890
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },

        // Dates: MM/DD/YYYY or MM-DD-YYYY
        { "Date", @"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" },

        // IP Addresses
        { "IPAddress", @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" }
    };

    public void RedactPatterns(string inputPath, string outputPath, params string[] patternNames)
    {
        // Load the PDF
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract all text from the document
        string fullText = pdf.ExtractAllText();

        // Track unique matches to avoid duplicate redaction attempts
        HashSet<string> matchesToRedact = new HashSet<string>();

        // Find all matches for requested patterns
        foreach (string patternName in patternNames)
        {
            if (SensitivePatterns.TryGetValue(patternName, out string pattern))
            {
                Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(fullText);

                foreach (Match match in matches)
                {
                    matchesToRedact.Add(match.Value);
                }
            }
        }

        // Redact each unique match
        foreach (string sensitiveValue in matchesToRedact)
        {
            pdf.RedactTextOnAllPages(sensitiveValue);
        }

        // Save the redacted document
        pdf.SaveAs(outputPath);
    }
}

// Usage example
class Program
{
    static void Main()
    {
        PatternRedactor redactor = new PatternRedactor();

        // Redact SSNs and credit cards from a financial document
        redactor.RedactPatterns(
            "customer-data.pdf",
            "customer-data-safe.pdf",
            "SSN", "CreditCard", "Email"
        );
    }
}
Imports IronPdf
Imports System.Text.RegularExpressions
Imports System.Collections.Generic

Public Class PatternRedactor
    ' Common patterns for sensitive data
    Private Shared ReadOnly SensitivePatterns As New Dictionary(Of String, String) From {
        ' US Social Security Number: 123-45-6789
        {"SSN", "\b\d{3}-\d{2}-\d{4}\b"},

        ' Credit Card Numbers: various formats with 13-19 digits
        {"CreditCard", "\b(?:\d{4}[-\s]?){3}\d{1,4}\b"},

        ' Email Addresses
        {"Email", "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"},

        ' US Phone Numbers: (123) 456-7890 or 123-456-7890
        {"Phone", "\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b"},

        ' Dates: MM/DD/YYYY or MM-DD-YYYY
        {"Date", "\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b"},

        ' IP Addresses
        {"IPAddress", "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"}
    }

    Public Sub RedactPatterns(inputPath As String, outputPath As String, ParamArray patternNames As String())
        ' Load the PDF
        Dim pdf As PdfDocument = PdfDocument.FromFile(inputPath)

        ' Extract all text from the document
        Dim fullText As String = pdf.ExtractAllText()

        ' Track unique matches to avoid duplicate redaction attempts
        Dim matchesToRedact As New HashSet(Of String)()

        ' Find all matches for requested patterns
        For Each patternName As String In patternNames
            Dim pattern As String = Nothing
            If SensitivePatterns.TryGetValue(patternName, pattern) Then
                Dim regex As New Regex(pattern, RegexOptions.IgnoreCase)
                Dim matches As MatchCollection = regex.Matches(fullText)

                For Each match As Match In matches
                    matchesToRedact.Add(match.Value)
                Next
            End If
        Next

        ' Redact each unique match
        For Each sensitiveValue As String In matchesToRedact
            pdf.RedactTextOnAllPages(sensitiveValue)
        Next

        ' Save the redacted document
        pdf.SaveAs(outputPath)
    End Sub
End Class

' Usage example
Class Program
    Shared Sub Main()
        Dim redactor As New PatternRedactor()

        ' Redact SSNs and credit cards from a financial document
        redactor.RedactPatterns(
            "customer-data.pdf",
            "customer-data-safe.pdf",
            "SSN", "CreditCard", "Email"
        )
    End Sub
End Class
$vbLabelText   $csharpLabel

Para documentos de origen español, combine esta clase base con el diccionario PatronesEspanoles definido en la sección anterior. Ambos enfoques son composables: puede llamar primero a RedactarIdentificadores con los patrones LOPDGDD y a continuación aplicar cualquier redacción de valor conocido con RedactTextOnAllPages.

¿Cómo construir un escáner de datos sensibles reutilizable?

En entornos de producción sujetos a la LOPDGDD, es necesario escanear los documentos e informar de qué información confidencial existe antes de decidir si se redacta. Esto respalda la auditoría de cumplimiento y permite la revisión humana —exigida por el art. 22 RGPD para decisiones automatizadas con impacto en derechos— cuando el volumen de datos redactados requiere supervisión. La siguiente clase ofrece funciones de escaneado junto con la redacción:

using IronPdf;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

public class SensitiveDataMatch
{
    public string PatternType { get; set; }
    public string Value { get; set; }
    public int PageNumber { get; set; }
}

public class ScanResult
{
    public string FilePath { get; set; }
    public List<SensitiveDataMatch> Matches { get; set; } = new List<SensitiveDataMatch>();
    public bool ContainsSensitiveData => Matches.Count > 0;

    public Dictionary<string, int> GetSummary()
    {
        return Matches.GroupBy(m => m.PatternType)
                      .ToDictionary(g => g.Key, g => g.Count());
    }
}

public class DocumentScanner
{
    private readonly Dictionary<string, string> _patterns;

    public DocumentScanner()
    {
        _patterns = new Dictionary<string, string>
        {
            { "Social Security Number", @"\b\d{3}-\d{2}-\d{4}\b" },
            { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
            { "Email Address", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
            { "Phone Number", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },
            { "Date of Birth Pattern", @"\b(?:DOB|Date of Birth|Birth Date)[:\s]+\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" }
        };
    }

    public ScanResult ScanDocument(string filePath)
    {
        ScanResult result = new ScanResult { FilePath = filePath };
        PdfDocument pdf = PdfDocument.FromFile(filePath);

        // Scan each page individually to track location
        for (int pageIndex = 0; pageIndex < pdf.PageCount; pageIndex++)
        {
            string pageText = pdf.ExtractTextFromPage(pageIndex);

            foreach (var pattern in _patterns)
            {
                Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(pageText);

                foreach (Match match in matches)
                {
                    result.Matches.Add(new SensitiveDataMatch
                    {
                        PatternType = pattern.Key,
                        Value = MaskValue(match.Value, pattern.Key),
                        PageNumber = pageIndex + 1
                    });
                }
            }
        }

        return result;
    }

    // Partially mask values for safe storage
    private string MaskValue(string value, string patternType)
    {
        if (patternType == "Social Security Number" && value.Length >= 4)
        {
            return "XXX-XX-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Credit Card" && value.Length >= 4)
        {
            return "****-****-****-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Email Address")
        {
            int atIndex = value.IndexOf('@');
            if (atIndex > 2)
            {
                return value.Substring(0, 2) + "***" + value.Substring(atIndex);
            }
        }
        return value.Length > 4 ? value.Substring(0, 2) + "***" : "****";
    }

    public void ScanAndRedact(string inputPath, string outputPath)
    {
        // First scan to identify sensitive data
        ScanResult scanResult = ScanDocument(inputPath);

        if (!scanResult.ContainsSensitiveData)
        {
            return;
        }

        // Load document for redaction
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract unique actual values (not masked) for redaction
        string fullText = pdf.ExtractAllText();
        HashSet<string> valuesToRedact = new HashSet<string>();

        foreach (var pattern in _patterns)
        {
            Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
            foreach (Match match in regex.Matches(fullText))
            {
                valuesToRedact.Add(match.Value);
            }
        }

        // Apply redactions
        foreach (string value in valuesToRedact)
        {
            pdf.RedactTextOnAllPages(value);
        }

        pdf.SaveAs(outputPath);
    }
}

// Usage
class Program
{
    static void Main()
    {
        DocumentScanner scanner = new DocumentScanner();

        // Scan only (for audit purposes)
        ScanResult result = scanner.ScanDocument("application-form.pdf");
        var summary = result.GetSummary();

        // Scan and redact in one operation
        scanner.ScanAndRedact("application-form.pdf", "application-form-redacted.pdf");
    }
}
using IronPdf;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

public class SensitiveDataMatch
{
    public string PatternType { get; set; }
    public string Value { get; set; }
    public int PageNumber { get; set; }
}

public class ScanResult
{
    public string FilePath { get; set; }
    public List<SensitiveDataMatch> Matches { get; set; } = new List<SensitiveDataMatch>();
    public bool ContainsSensitiveData => Matches.Count > 0;

    public Dictionary<string, int> GetSummary()
    {
        return Matches.GroupBy(m => m.PatternType)
                      .ToDictionary(g => g.Key, g => g.Count());
    }
}

public class DocumentScanner
{
    private readonly Dictionary<string, string> _patterns;

    public DocumentScanner()
    {
        _patterns = new Dictionary<string, string>
        {
            { "Social Security Number", @"\b\d{3}-\d{2}-\d{4}\b" },
            { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
            { "Email Address", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
            { "Phone Number", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },
            { "Date of Birth Pattern", @"\b(?:DOB|Date of Birth|Birth Date)[:\s]+\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" }
        };
    }

    public ScanResult ScanDocument(string filePath)
    {
        ScanResult result = new ScanResult { FilePath = filePath };
        PdfDocument pdf = PdfDocument.FromFile(filePath);

        // Scan each page individually to track location
        for (int pageIndex = 0; pageIndex < pdf.PageCount; pageIndex++)
        {
            string pageText = pdf.ExtractTextFromPage(pageIndex);

            foreach (var pattern in _patterns)
            {
                Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(pageText);

                foreach (Match match in matches)
                {
                    result.Matches.Add(new SensitiveDataMatch
                    {
                        PatternType = pattern.Key,
                        Value = MaskValue(match.Value, pattern.Key),
                        PageNumber = pageIndex + 1
                    });
                }
            }
        }

        return result;
    }

    // Partially mask values for safe storage
    private string MaskValue(string value, string patternType)
    {
        if (patternType == "Social Security Number" && value.Length >= 4)
        {
            return "XXX-XX-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Credit Card" && value.Length >= 4)
        {
            return "****-****-****-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Email Address")
        {
            int atIndex = value.IndexOf('@');
            if (atIndex > 2)
            {
                return value.Substring(0, 2) + "***" + value.Substring(atIndex);
            }
        }
        return value.Length > 4 ? value.Substring(0, 2) + "***" : "****";
    }

    public void ScanAndRedact(string inputPath, string outputPath)
    {
        // First scan to identify sensitive data
        ScanResult scanResult = ScanDocument(inputPath);

        if (!scanResult.ContainsSensitiveData)
        {
            return;
        }

        // Load document for redaction
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract unique actual values (not masked) for redaction
        string fullText = pdf.ExtractAllText();
        HashSet<string> valuesToRedact = new HashSet<string>();

        foreach (var pattern in _patterns)
        {
            Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
            foreach (Match match in regex.Matches(fullText))
            {
                valuesToRedact.Add(match.Value);
            }
        }

        // Apply redactions
        foreach (string value in valuesToRedact)
        {
            pdf.RedactTextOnAllPages(value);
        }

        pdf.SaveAs(outputPath);
    }
}

// Usage
class Program
{
    static void Main()
    {
        DocumentScanner scanner = new DocumentScanner();

        // Scan only (for audit purposes)
        ScanResult result = scanner.ScanDocument("application-form.pdf");
        var summary = result.GetSummary();

        // Scan and redact in one operation
        scanner.ScanAndRedact("application-form.pdf", "application-form-redacted.pdf");
    }
}
Imports IronPdf
Imports System.Collections.Generic
Imports System.Text.RegularExpressions
Imports System.Linq

Public Class SensitiveDataMatch
    Public Property PatternType As String
    Public Property Value As String
    Public Property PageNumber As Integer
End Class

Public Class ScanResult
    Public Property FilePath As String
    Public Property Matches As List(Of SensitiveDataMatch) = New List(Of SensitiveDataMatch)()
    Public ReadOnly Property ContainsSensitiveData As Boolean
        Get
            Return Matches.Count > 0
        End Get
    End Property

    Public Function GetSummary() As Dictionary(Of String, Integer)
        Return Matches.GroupBy(Function(m) m.PatternType) _
                      .ToDictionary(Function(g) g.Key, Function(g) g.Count())
    End Function
End Class

Public Class DocumentScanner
    Private ReadOnly _patterns As Dictionary(Of String, String)

    Public Sub New()
        _patterns = New Dictionary(Of String, String) From {
            {"Social Security Number", "\b\d{3}-\d{2}-\d{4}\b"},
            {"Credit Card", "\b(?:\d{4}[-\s]?){3}\d{1,4}\b"},
            {"Email Address", "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"},
            {"Phone Number", "\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b"},
            {"Date of Birth Pattern", "\b(?:DOB|Date of Birth|Birth Date)[:\s]+\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b"}
        }
    End Sub

    Public Function ScanDocument(filePath As String) As ScanResult
        Dim result As New ScanResult With {.FilePath = filePath}
        Dim pdf As PdfDocument = PdfDocument.FromFile(filePath)

        ' Scan each page individually to track location
        For pageIndex As Integer = 0 To pdf.PageCount - 1
            Dim pageText As String = pdf.ExtractTextFromPage(pageIndex)

            For Each pattern In _patterns
                Dim regex As New Regex(pattern.Value, RegexOptions.IgnoreCase)
                Dim matches As MatchCollection = regex.Matches(pageText)

                For Each match As Match In matches
                    result.Matches.Add(New SensitiveDataMatch With {
                        .PatternType = pattern.Key,
                        .Value = MaskValue(match.Value, pattern.Key),
                        .PageNumber = pageIndex + 1
                    })
                Next
            Next
        Next

        Return result
    End Function

    ' Partially mask values for safe storage
    Private Function MaskValue(value As String, patternType As String) As String
        If patternType = "Social Security Number" AndAlso value.Length >= 4 Then
            Return "XXX-XX-" & value.Substring(value.Length - 4)
        End If
        If patternType = "Credit Card" AndAlso value.Length >= 4 Then
            Return "****-****-****-" & value.Substring(value.Length - 4)
        End If
        If patternType = "Email Address" Then
            Dim atIndex As Integer = value.IndexOf("@"c)
            If atIndex > 2 Then
                Return value.Substring(0, 2) & "***" & value.Substring(atIndex)
            End If
        End If
        Return If(value.Length > 4, value.Substring(0, 2) & "***", "****")
    End Function

    Public Sub ScanAndRedact(inputPath As String, outputPath As String)
        ' First scan to identify sensitive data
        Dim scanResult As ScanResult = ScanDocument(inputPath)

        If Not scanResult.ContainsSensitiveData Then
            Return
        End If

        ' Load document for redaction
        Dim pdf As PdfDocument = PdfDocument.FromFile(inputPath)

        ' Extract unique actual values (not masked) for redaction
        Dim fullText As String = pdf.ExtractAllText()
        Dim valuesToRedact As New HashSet(Of String)()

        For Each pattern In _patterns
            Dim regex As New Regex(pattern.Value, RegexOptions.IgnoreCase)
            For Each match As Match In regex.Matches(fullText)
                valuesToRedact.Add(match.Value)
            Next
        Next

        ' Apply redactions
        For Each value As String In valuesToRedact
            pdf.RedactTextOnAllPages(value)
        Next

        pdf.SaveAs(outputPath)
    End Sub
End Class

' Usage
Module Program
    Sub Main()
        Dim scanner As New DocumentScanner()

        ' Scan only (for audit purposes)
        Dim result As ScanResult = scanner.ScanDocument("application-form.pdf")
        Dim summary = result.GetSummary()

        ' Scan and redact in one operation
        scanner.ScanAndRedact("application-form.pdf", "application-form-redacted.pdf")
    End Sub
End Module
$vbLabelText   $csharpLabel

El escáner ofrece visibilidad sobre la información confidencial existente antes de que se produzca cualquier modificación. Esto respalda los flujos de conformidad LOPDGDD en los que se necesita documentación sobre lo que se ha encontrado y eliminado —el "registro de tratamiento" del art. 30 RGPD puede incluir una entrada por cada lote de documentos procesados. La función de enmascaramiento garantiza que los archivos de registro y los informes no se conviertan en nuevas fuentes de exposición de datos personales.


¿Cómo redacto regiones o áreas específicas de un PDF?

La redacción de texto maneja contenido basado en caracteres de manera efectiva, pero los PDFs frecuentemente contienen información sensible en formas que la concordancia de texto no puede abordar. En el contexto documental español, esto incluye: fotografías de DNI o pasaporte digitalizadas, firmas manuscritas en poderes notariales y contratos, sellos de empresa con NIF incrustado en imagen, y anotaciones manuscritas en formularios administrativos. La redacción basada en regiones permite especificar áreas rectangulares por sus coordenadas y oscurecer permanentemente todo lo que está dentro de esos límites.

IronPDF utiliza la estructura RectangleF para definir regiones de redacción. Especifique las coordenadas X e Y de la esquina superior izquierda y, a continuación, la anchura y la altura del área. Las coordenadas se miden en puntos desde la parte inferior izquierda de la página, coincidiendo con el sistema de coordenadas de la especificación PDF.

Entrada

Un documento de acuerdo firmado que contiene firmas manuscritas y un documento de identidad con fotografía que deben redactarse utilizando la orientación por regiones basada en coordenadas.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-region-basic.cs
using IronPdf;
using IronSoftware.Drawing;

// Load a document with signature blocks and photos
PdfDocument pdf = PdfDocument.FromFile("signed-agreement.pdf");

// Define a region for a signature block
// Located 100 points from left, 650 points from bottom
// Width of 200 points, height of 50 points
RectangleF signatureRegion = new RectangleF(100, 650, 200, 50);

// Redact the signature region on all pages
pdf.RedactRegionsOnAllPages(signatureRegion);

// Define a region for a photo ID in the upper right
RectangleF photoRegion = new RectangleF(450, 700, 100, 120);
pdf.RedactRegionsOnAllPages(photoRegion);

// Save the document with regions redacted
pdf.SaveAs("signed-agreement-redacted.pdf");
Imports IronPdf
Imports IronSoftware.Drawing

' Load a document with signature blocks and photos
Dim pdf As PdfDocument = PdfDocument.FromFile("signed-agreement.pdf")

' Define a region for a signature block
' Located 100 points from left, 650 points from bottom
' Width of 200 points, height of 50 points
Dim signatureRegion As New RectangleF(100, 650, 200, 50)

' Redact the signature region on all pages
pdf.RedactRegionsOnAllPages(signatureRegion)

' Define a region for a photo ID in the upper right
Dim photoRegion As New RectangleF(450, 700, 100, 120)
pdf.RedactRegionsOnAllPages(photoRegion)

' Save the document with regions redacted
pdf.SaveAs("signed-agreement-redacted.pdf")
$vbLabelText   $csharpLabel

Este código usa estructuras RectangleF para definir áreas rectangulares para la redacción. La región de la firma se sitúa en las coordenadas (100, 650) con un área de 200x50 píxeles, mientras que la región de la foto se sitúa en (450, 700) con un área de 100x120 píxeles. El método RedactRegionsOnAllPages aplica rectángulos negros sobre estas regiones en todas las páginas.

Muestra de resultado

Determinar las coordenadas correctas suele requerir cierta experimentación o medición. Las páginas PDF utilizan un sistema en que un punto equivale a 1/72 de pulgada. Una página A4 (formato estándar en España) mide aproximadamente 595 por 842 puntos; el A4 es prácticamente universal en la documentación administrativa española (escrituras notariales, contratos mercantiles, resoluciones administrativas). Las herramientas de visualización de PDF que muestran las coordenadas al mover el cursor pueden ayudar, o se pueden extraer las dimensiones de la página mediante programación:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-region-dimensions.cs
using IronPdf;
using IronSoftware.Drawing;

PdfDocument pdf = PdfDocument.FromFile("form-document.pdf");

// Get dimensions of the first page
var pageInfo = pdf.Pages[0];

// Calculate regions relative to page dimensions
// Redact the bottom quarter of the page where signatures appear
float signatureAreaHeight = (float)(pageInfo.Height / 4);
RectangleF bottomQuarter = new RectangleF(
    0,                              // Start at left edge
    0,                              // Start at bottom
    (float)pageInfo.Width,          // Full page width
    signatureAreaHeight             // Quarter of page height
);

pdf.RedactRegionsOnAllPages(bottomQuarter);

// Redact a header area at the top containing letterhead with address
float headerHeight = 100;
RectangleF headerArea = new RectangleF(
    0,
    (float)(pageInfo.Height - headerHeight), // Position from bottom
    (float)pageInfo.Width,
    headerHeight
);

pdf.RedactRegionsOnAllPages(headerArea);

pdf.SaveAs("form-document-redacted.pdf");
Imports IronPdf
Imports IronSoftware.Drawing

Dim pdf As PdfDocument = PdfDocument.FromFile("form-document.pdf")

' Get dimensions of the first page
Dim pageInfo = pdf.Pages(0)

' Calculate regions relative to page dimensions
' Redact the bottom quarter of the page where signatures appear
Dim signatureAreaHeight As Single = CSng(pageInfo.Height / 4)
Dim bottomQuarter As New RectangleF(0, 0, CSng(pageInfo.Width), signatureAreaHeight)

pdf.RedactRegionsOnAllPages(bottomQuarter)

' Redact a header area at the top containing letterhead with address
Dim headerHeight As Single = 100
Dim headerArea As New RectangleF(0, CSng(pageInfo.Height - headerHeight), CSng(pageInfo.Width), headerHeight)

pdf.RedactRegionsOnAllPages(headerArea)

pdf.SaveAs("form-document-redacted.pdf")
$vbLabelText   $csharpLabel

¿Cómo puedo redactar varias regiones en distintas páginas?

Los expedientes administrativos y contratos mercantiles españoles suelen requerir que se redacten diferentes regiones en distintas páginas. Un contrato de proveedor puede tener el bloque de firma del representante legal en la última página, mientras que el DNI del responsable aparece escaneado en un anexo separado. IronPDF incluye métodos específicos de página para redacción de región dirigida.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-multiple-regions.cs
using IronPdf;
using IronSoftware.Drawing;

PdfDocument pdf = PdfDocument.FromFile("multi-page-application.pdf");

// Define page-specific redaction regions
// Page 1: Cover page with applicant photo
RectangleF page1Photo = new RectangleF(450, 600, 120, 150);
pdf.RedactRegionOnPage(0, page1Photo);

// Page 2: Personal information section
RectangleF page2InfoBlock = new RectangleF(50, 400, 250, 200);
pdf.RedactRegionOnPage(1, page2InfoBlock);

// Pages 3-5: Signature lines at the same position
RectangleF signatureLine = new RectangleF(100, 100, 200, 40);
int[] signaturePages = { 2, 3, 4 };
pdf.RedactRegionOnPages(signaturePages, signatureLine);

// Page 6: Multiple regions - notary stamp and witness signature
RectangleF notaryStamp = new RectangleF(400, 150, 150, 150);
RectangleF witnessSignature = new RectangleF(100, 150, 200, 40);
pdf.RedactRegionOnPage(5, notaryStamp);
pdf.RedactRegionOnPage(5, witnessSignature);

pdf.SaveAs("multi-page-application-redacted.pdf");
Imports IronPdf
Imports IronSoftware.Drawing

Dim pdf As PdfDocument = PdfDocument.FromFile("multi-page-application.pdf")

' Define page-specific redaction regions
' Page 1: Cover page with applicant photo
Dim page1Photo As New RectangleF(450, 600, 120, 150)
pdf.RedactRegionOnPage(0, page1Photo)

' Page 2: Personal information section
Dim page2InfoBlock As New RectangleF(50, 400, 250, 200)
pdf.RedactRegionOnPage(1, page2InfoBlock)

' Pages 3-5: Signature lines at the same position
Dim signatureLine As New RectangleF(100, 100, 200, 40)
Dim signaturePages As Integer() = {2, 3, 4}
pdf.RedactRegionOnPages(signaturePages, signatureLine)

' Page 6: Multiple regions - notary stamp and witness signature
Dim notaryStamp As New RectangleF(400, 150, 150, 150)
Dim witnessSignature As New RectangleF(100, 150, 200, 40)
pdf.RedactRegionOnPage(5, notaryStamp)
pdf.RedactRegionOnPage(5, witnessSignature)

pdf.SaveAs("multi-page-application-redacted.pdf")
$vbLabelText   $csharpLabel

Los documentos con diseños coherentes —formularios normalizados de la Administración General del Estado, impresos AEAT de modelos tributarios (Modelo 100, 303, 347), formularios del SEPE— se benefician de definiciones de regiones reutilizables:

using IronPdf;
using IronSoftware.Drawing;

public class FormRegions
{
    // Standard form regions based on common templates
    public static RectangleF HeaderLogo => new RectangleF(20, 720, 150, 60);
    public static RectangleF SignatureBlock => new RectangleF(72, 72, 200, 50);
    public static RectangleF DateField => new RectangleF(400, 72, 120, 20);
    public static RectangleF PhotoId => new RectangleF(480, 650, 100, 130);
    public static RectangleF AddressBlock => new RectangleF(72, 600, 250, 80);
}

class Program
{
    static void Main()
    {
        PdfDocument pdf = PdfDocument.FromFile("standard-form.pdf");

        // Apply standard redactions using predefined regions
        pdf.RedactRegionsOnAllPages(FormRegions.SignatureBlock);
        pdf.RedactRegionsOnAllPages(FormRegions.DateField);
        pdf.RedactRegionOnPage(0, FormRegions.PhotoId);

        pdf.SaveAs("standard-form-redacted.pdf");
    }
}
using IronPdf;
using IronSoftware.Drawing;

public class FormRegions
{
    // Standard form regions based on common templates
    public static RectangleF HeaderLogo => new RectangleF(20, 720, 150, 60);
    public static RectangleF SignatureBlock => new RectangleF(72, 72, 200, 50);
    public static RectangleF DateField => new RectangleF(400, 72, 120, 20);
    public static RectangleF PhotoId => new RectangleF(480, 650, 100, 130);
    public static RectangleF AddressBlock => new RectangleF(72, 600, 250, 80);
}

class Program
{
    static void Main()
    {
        PdfDocument pdf = PdfDocument.FromFile("standard-form.pdf");

        // Apply standard redactions using predefined regions
        pdf.RedactRegionsOnAllPages(FormRegions.SignatureBlock);
        pdf.RedactRegionsOnAllPages(FormRegions.DateField);
        pdf.RedactRegionOnPage(0, FormRegions.PhotoId);

        pdf.SaveAs("standard-form-redacted.pdf");
    }
}
Imports IronPdf
Imports IronSoftware.Drawing

Public Class FormRegions
    ' Standard form regions based on common templates
    Public Shared ReadOnly Property HeaderLogo As RectangleF
        Get
            Return New RectangleF(20, 720, 150, 60)
        End Get
    End Property

    Public Shared ReadOnly Property SignatureBlock As RectangleF
        Get
            Return New RectangleF(72, 72, 200, 50)
        End Get
    End Property

    Public Shared ReadOnly Property DateField As RectangleF
        Get
            Return New RectangleF(400, 72, 120, 20)
        End Get
    End Property

    Public Shared ReadOnly Property PhotoId As RectangleF
        Get
            Return New RectangleF(480, 650, 100, 130)
        End Get
    End Property

    Public Shared ReadOnly Property AddressBlock As RectangleF
        Get
            Return New RectangleF(72, 600, 250, 80)
        End Get
    End Property
End Class

Module Program
    Sub Main()
        Dim pdf As PdfDocument = PdfDocument.FromFile("standard-form.pdf")

        ' Apply standard redactions using predefined regions
        pdf.RedactRegionsOnAllPages(FormRegions.SignatureBlock)
        pdf.RedactRegionsOnAllPages(FormRegions.DateField)
        pdf.RedactRegionOnPage(0, FormRegions.PhotoId)

        pdf.SaveAs("standard-form-redacted.pdf")
    End Sub
End Module
$vbLabelText   $csharpLabel

¿Cómo puedo eliminar metadatos que podrían exponer información confidencial?

Los metadatos PDF representan una fuente de fuga de información frecuentemente ignorada, y la AEPD los considera datos personales cuando identifican a una persona física. Todos los PDF contienen propiedades que pueden revelar datos confidenciales: el nombre completo del autor (generalmente extraído del perfil de usuario del sistema operativo), el nombre de usuario del sistema, el software utilizado para crear el documento, las marcas de tiempo de creación y modificación, el nombre original del fichero (que puede contener el DNI o el nombre del cliente) y propiedades personalizadas añadidas por aplicaciones de gestión documental. Antes de transmitir documentos externamente —especialmente mediante plataformas FACe, eSENT o portales de licitación— es esencial eliminar o sanear estos metadatos. Para una visión general de las operaciones de metadatos, consulte nuestra guía práctica de metadatos.

IronPDF expone los metadatos del documento a través de la propiedad MetaData, permitiéndole leer valores existentes, modificarlos o eliminarlos completamente.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/view-metadata.cs
using IronPdf;
using System;

// Load a document containing sensitive metadata
PdfDocument pdf = PdfDocument.FromFile("internal-report.pdf");

// Access current metadata properties
string author = pdf.MetaData.Author;
string title = pdf.MetaData.Title;
string subject = pdf.MetaData.Subject;
string keywords = pdf.MetaData.Keywords;
string creator = pdf.MetaData.Creator;
string producer = pdf.MetaData.Producer;
DateTime? creationDate = pdf.MetaData.CreationDate;
DateTime? modifiedDate = pdf.MetaData.ModifiedDate;

// Get all metadata keys including custom properties
var allKeys = pdf.MetaData.Keys();
Imports IronPdf
Imports System

' Load a document containing sensitive metadata
Dim pdf As PdfDocument = PdfDocument.FromFile("internal-report.pdf")

' Access current metadata properties
Dim author As String = pdf.MetaData.Author
Dim title As String = pdf.MetaData.Title
Dim subject As String = pdf.MetaData.Subject
Dim keywords As String = pdf.MetaData.Keywords
Dim creator As String = pdf.MetaData.Creator
Dim producer As String = pdf.MetaData.Producer
Dim creationDate As DateTime? = pdf.MetaData.CreationDate
Dim modifiedDate As DateTime? = pdf.MetaData.ModifiedDate

' Get all metadata keys including custom properties
Dim allKeys = pdf.MetaData.Keys()
$vbLabelText   $csharpLabel

Eliminar los metadatos sensibles antes de la distribución:

Entrada

Un memorando interno que contiene metadatos incrustados, como nombres de autores, marcas de tiempo de creación y propiedades personalizadas que podrían revelar información confidencial de la organización.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/remove-metadata.cs
using IronPdf;
using System;

PdfDocument pdf = PdfDocument.FromFile("confidential-memo.pdf");

// Replace identifying metadata with generic values
pdf.MetaData.Author = "Organization Name";
pdf.MetaData.Creator = "Document System";
pdf.MetaData.Producer = "";
pdf.MetaData.Title = "Public Document";
pdf.MetaData.Subject = "";
pdf.MetaData.Keywords = "";

// Normalize dates to remove timing information
pdf.MetaData.CreationDate = DateTime.Now;
pdf.MetaData.ModifiedDate = DateTime.Now;

// Remove specific custom metadata keys
pdf.MetaData.RemoveMetaDataKey("OriginalFilename");
pdf.MetaData.RemoveMetaDataKey("LastSavedBy");
pdf.MetaData.RemoveMetaDataKey("Company");
pdf.MetaData.RemoveMetaDataKey("Manager");

// Remove custom properties added by applications
try
{
    pdf.MetaData.CustomProperties.Remove("SourcePath");
}
catch { }

pdf.SaveAs("confidential-memo-cleaned.pdf");
Imports IronPdf
Imports System

Dim pdf As PdfDocument = PdfDocument.FromFile("confidential-memo.pdf")

' Replace identifying metadata with generic values
pdf.MetaData.Author = "Organization Name"
pdf.MetaData.Creator = "Document System"
pdf.MetaData.Producer = ""
pdf.MetaData.Title = "Public Document"
pdf.MetaData.Subject = ""
pdf.MetaData.Keywords = ""

' Normalize dates to remove timing information
pdf.MetaData.CreationDate = DateTime.Now
pdf.MetaData.ModifiedDate = DateTime.Now

' Remove specific custom metadata keys
pdf.MetaData.RemoveMetaDataKey("OriginalFilename")
pdf.MetaData.RemoveMetaDataKey("LastSavedBy")
pdf.MetaData.RemoveMetaDataKey("Company")
pdf.MetaData.RemoveMetaDataKey("Manager")

' Remove custom properties added by applications
Try
    pdf.MetaData.CustomProperties.Remove("SourcePath")
Catch
End Try

pdf.SaveAs("confidential-memo-cleaned.pdf")
$vbLabelText   $csharpLabel

Este código sustituye los campos de metadatos identificativos por valores genéricos, normaliza las marcas de tiempo a la fecha actual y elimina las claves de metadatos personalizadas que las aplicaciones de gestión documental puedan haber añadido. El método RemoveMetaDataKey se dirige a propiedades como "OriginalFilename" y "LastSavedBy" que podrían exponer información interna o datos personales del responsable del tratamiento.

Muestra de resultado

La limpieza exhaustiva de metadatos en operaciones por lotes —por ejemplo, antes del envío masivo de facturas por FACe o de respuestas a solicitudes de información pública— requiere un enfoque sistemático:

using IronPdf;
using System;
using System.Collections.Generic;

public class MetadataCleaner
{
    private readonly string _defaultAuthor;
    private readonly string _defaultCreator;

    public MetadataCleaner(string organizationName)
    {
        _defaultAuthor = organizationName;
        _defaultCreator = $"{organizationName} Document System";
    }

    public void CleanMetadata(PdfDocument pdf)
    {
        // Replace standard metadata fields
        pdf.MetaData.Author = _defaultAuthor;
        pdf.MetaData.Creator = _defaultCreator;
        pdf.MetaData.Producer = "";
        pdf.MetaData.Subject = "";
        pdf.MetaData.Keywords = "";

        // Normalize timestamps
        DateTime now = DateTime.Now;
        pdf.MetaData.CreationDate = now;
        pdf.MetaData.ModifiedDate = now;

        // Get all keys and remove potentially sensitive ones
        List<string> keysToRemove = new List<string>();
        foreach (string key in pdf.MetaData.Keys())
        {
            // Keep only essential keys
            if (!IsEssentialKey(key))
            {
                keysToRemove.Add(key);
            }
        }

        foreach (string key in keysToRemove)
        {
            pdf.MetaData.RemoveMetaDataKey(key);
        }
    }

    private bool IsEssentialKey(string key)
    {
        // Keep only the basic display properties
        string[] essentialKeys = { "Title", "Author", "CreationDate", "ModifiedDate" };
        foreach (string essential in essentialKeys)
        {
            if (key.Equals(essential, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
        return false;
    }
}

// Usage
class Program
{
    static void Main()
    {
        MetadataCleaner cleaner = new MetadataCleaner("Acme Corporation");

        PdfDocument pdf = PdfDocument.FromFile("report.pdf");
        cleaner.CleanMetadata(pdf);
        pdf.SaveAs("report-clean.pdf");
    }
}
using IronPdf;
using System;
using System.Collections.Generic;

public class MetadataCleaner
{
    private readonly string _defaultAuthor;
    private readonly string _defaultCreator;

    public MetadataCleaner(string organizationName)
    {
        _defaultAuthor = organizationName;
        _defaultCreator = $"{organizationName} Document System";
    }

    public void CleanMetadata(PdfDocument pdf)
    {
        // Replace standard metadata fields
        pdf.MetaData.Author = _defaultAuthor;
        pdf.MetaData.Creator = _defaultCreator;
        pdf.MetaData.Producer = "";
        pdf.MetaData.Subject = "";
        pdf.MetaData.Keywords = "";

        // Normalize timestamps
        DateTime now = DateTime.Now;
        pdf.MetaData.CreationDate = now;
        pdf.MetaData.ModifiedDate = now;

        // Get all keys and remove potentially sensitive ones
        List<string> keysToRemove = new List<string>();
        foreach (string key in pdf.MetaData.Keys())
        {
            // Keep only essential keys
            if (!IsEssentialKey(key))
            {
                keysToRemove.Add(key);
            }
        }

        foreach (string key in keysToRemove)
        {
            pdf.MetaData.RemoveMetaDataKey(key);
        }
    }

    private bool IsEssentialKey(string key)
    {
        // Keep only the basic display properties
        string[] essentialKeys = { "Title", "Author", "CreationDate", "ModifiedDate" };
        foreach (string essential in essentialKeys)
        {
            if (key.Equals(essential, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
        return false;
    }
}

// Usage
class Program
{
    static void Main()
    {
        MetadataCleaner cleaner = new MetadataCleaner("Acme Corporation");

        PdfDocument pdf = PdfDocument.FromFile("report.pdf");
        cleaner.CleanMetadata(pdf);
        pdf.SaveAs("report-clean.pdf");
    }
}
Imports IronPdf
Imports System
Imports System.Collections.Generic

Public Class MetadataCleaner
    Private ReadOnly _defaultAuthor As String
    Private ReadOnly _defaultCreator As String

    Public Sub New(organizationName As String)
        _defaultAuthor = organizationName
        _defaultCreator = $"{organizationName} Document System"
    End Sub

    Public Sub CleanMetadata(pdf As PdfDocument)
        ' Replace standard metadata fields
        pdf.MetaData.Author = _defaultAuthor
        pdf.MetaData.Creator = _defaultCreator
        pdf.MetaData.Producer = ""
        pdf.MetaData.Subject = ""
        pdf.MetaData.Keywords = ""

        ' Normalize timestamps
        Dim now As DateTime = DateTime.Now
        pdf.MetaData.CreationDate = now
        pdf.MetaData.ModifiedDate = now

        ' Get all keys and remove potentially sensitive ones
        Dim keysToRemove As New List(Of String)()
        For Each key As String In pdf.MetaData.Keys()
            ' Keep only essential keys
            If Not IsEssentialKey(key) Then
                keysToRemove.Add(key)
            End If
        Next

        For Each key As String In keysToRemove
            pdf.MetaData.RemoveMetaDataKey(key)
        Next
    End Sub

    Private Function IsEssentialKey(key As String) As Boolean
        ' Keep only the basic display properties
        Dim essentialKeys As String() = {"Title", "Author", "CreationDate", "ModifiedDate"}
        For Each essential As String In essentialKeys
            If key.Equals(essential, StringComparison.OrdinalIgnoreCase) Then
                Return True
            End If
        Next
        Return False
    End Function
End Class

' Usage
Class Program
    Shared Sub Main()
        Dim cleaner As New MetadataCleaner("Acme Corporation")

        Dim pdf As PdfDocument = PdfDocument.FromFile("report.pdf")
        cleaner.CleanMetadata(pdf)
        pdf.SaveAs("report-clean.pdf")
    End Sub
End Class
$vbLabelText   $csharpLabel

¿Cómo puedo desinfectar un PDF para eliminar scripts incrustados y amenazas ocultas?

La desinfección de PDF aborda problemas de seguridad que van más allá del contenido visible y los metadatos. Los archivos PDF pueden contener código JavaScript, ejecutables incrustados, acciones de formulario que activan conexiones externas y otros elementos potencialmente maliciosos. En el contexto empresarial español, esta amenaza es especialmente relevante para los PDFs recibidos por correo electrónico como facturas o documentos contractuales: los ataques de phishing documentados contra empresas españolas han utilizado PDFs con JavaScript para simular facturas de proveedores conocidos y redirigir pagos. La clase Cleaner de IronPDF maneja la sanitización mediante la conversión del PDF a un formato intermedio y su reconversión posterior, eliminando JavaScript, objetos incrustados, acciones de formulario y anotaciones. Para obtener más información sobre los métodos de desinfección, consulte nuestra guía práctica de desinfección de PDF.

Entrada

Un documento PDF recibido de una fuente externa que puede contener JavaScript, objetos incrustados u otro contenido activo potencialmente malicioso.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/sanitize-pdf.cs
using IronPdf;

// Load a PDF that may contain active content
PdfDocument pdf = PdfDocument.FromFile("received-document.pdf");

// Sanitize using SVG conversion
// Faster processing, results in searchable text, slight layout variations possible
PdfDocument sanitizedSvg = Cleaner.SanitizeWithSvg(pdf);
sanitizedSvg.SaveAs("sanitized-svg.pdf");

// Sanitize using Bitmap conversion
// Slower processing, text becomes image (not searchable), exact visual reproduction
PdfDocument sanitizedBitmap = Cleaner.SanitizeWithBitmap(pdf);
sanitizedBitmap.SaveAs("sanitized-bitmap.pdf");
Imports IronPdf

' Load a PDF that may contain active content
Dim pdf As PdfDocument = PdfDocument.FromFile("received-document.pdf")

' Sanitize using SVG conversion
' Faster processing, results in searchable text, slight layout variations possible
Dim sanitizedSvg As PdfDocument = Cleaner.SanitizeWithSvg(pdf)
sanitizedSvg.SaveAs("sanitized-svg.pdf")

' Sanitize using Bitmap conversion
' Slower processing, text becomes image (not searchable), exact visual reproduction
Dim sanitizedBitmap As PdfDocument = Cleaner.SanitizeWithBitmap(pdf)
sanitizedBitmap.SaveAs("sanitized-bitmap.pdf")
$vbLabelText   $csharpLabel

Este código demuestra dos métodos de sanitización proporcionados por la clase Cleaner de IronPDF. SanitizeWithSvg convierte el PDF a través de un formato intermedio SVG, preservando el texto buscable mientras elimina el contenido activo. SanitizeWithBitmap convierte las páginas en imágenes primero, produciendo copias visuales exactas pero con el texto renderizado como gráficos no buscables.

Muestra de resultado

El método SVG es más rápido y conserva el texto como contenido que permite búsquedas, adecuado para documentos que deben permanecer indexados. El método de mapa de bits produce copias visuales exactas, pero convierte el texto en imágenes —útil cuando la máxima seguridad tiene prioridad sobre la capacidad de búsqueda. También puede aplicar opciones de renderizado durante la limpieza:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/sanitize-with-options.cs
using IronPdf;

// Load the potentially unsafe document
PdfDocument pdf = PdfDocument.FromFile("untrusted-source.pdf");

// Configure rendering options for sanitization
var renderOptions = new ChromePdfRenderOptions
{
    MarginTop = 10,
    MarginBottom = 10,
    MarginLeft = 10,
    MarginRight = 10
};

// Sanitize with custom options
PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf, renderOptions);
sanitized.SaveAs("untrusted-source-safe.pdf");
Imports IronPdf

' Load the potentially unsafe document
Dim pdf As PdfDocument = PdfDocument.FromFile("untrusted-source.pdf")

' Configure rendering options for sanitization
Dim renderOptions As New ChromePdfRenderOptions With {
    .MarginTop = 10,
    .MarginBottom = 10,
    .MarginLeft = 10,
    .MarginRight = 10
}

' Sanitize with custom options
Dim sanitized As PdfDocument = Cleaner.SanitizeWithSvg(pdf, renderOptions)
sanitized.SaveAs("untrusted-source-safe.pdf")
$vbLabelText   $csharpLabel

Los entornos de alta seguridad —flujos de recepción de facturas de proveedores en organizaciones sujetas al SII o al esquema VeriFactu— frecuentemente requieren combinar la sanitización con otras medidas de protección:

using IronPdf;
using System;

public class SecureDocumentProcessor
{
    public PdfDocument ProcessUntrustedDocument(string inputPath)
    {
        // Load the document
        PdfDocument original = PdfDocument.FromFile(inputPath);

        // Step 1: Sanitize to remove active content
        PdfDocument sanitized = Cleaner.SanitizeWithSvg(original);

        // Step 2: Clean metadata
        sanitized.MetaData.Author = "Processed Document";
        sanitized.MetaData.Creator = "Secure Processor";
        sanitized.MetaData.Producer = "";
        sanitized.MetaData.CreationDate = DateTime.Now;
        sanitized.MetaData.ModifiedDate = DateTime.Now;

        // Remove all custom metadata
        foreach (string key in sanitized.MetaData.Keys())
        {
            if (key != "Title" && key != "Author" && key != "CreationDate" && key != "ModifiedDate")
            {
                sanitized.MetaData.RemoveMetaDataKey(key);
            }
        }

        return sanitized;
    }
}

// Usage
class Program
{
    static void Main()
    {
        SecureDocumentProcessor processor = new SecureDocumentProcessor();
        PdfDocument safe = processor.ProcessUntrustedDocument("email-attachment.pdf");
        safe.SaveAs("email-attachment-safe.pdf");
    }
}
using IronPdf;
using System;

public class SecureDocumentProcessor
{
    public PdfDocument ProcessUntrustedDocument(string inputPath)
    {
        // Load the document
        PdfDocument original = PdfDocument.FromFile(inputPath);

        // Step 1: Sanitize to remove active content
        PdfDocument sanitized = Cleaner.SanitizeWithSvg(original);

        // Step 2: Clean metadata
        sanitized.MetaData.Author = "Processed Document";
        sanitized.MetaData.Creator = "Secure Processor";
        sanitized.MetaData.Producer = "";
        sanitized.MetaData.CreationDate = DateTime.Now;
        sanitized.MetaData.ModifiedDate = DateTime.Now;

        // Remove all custom metadata
        foreach (string key in sanitized.MetaData.Keys())
        {
            if (key != "Title" && key != "Author" && key != "CreationDate" && key != "ModifiedDate")
            {
                sanitized.MetaData.RemoveMetaDataKey(key);
            }
        }

        return sanitized;
    }
}

// Usage
class Program
{
    static void Main()
    {
        SecureDocumentProcessor processor = new SecureDocumentProcessor();
        PdfDocument safe = processor.ProcessUntrustedDocument("email-attachment.pdf");
        safe.SaveAs("email-attachment-safe.pdf");
    }
}
Imports IronPdf
Imports System

Public Class SecureDocumentProcessor
    Public Function ProcessUntrustedDocument(inputPath As String) As PdfDocument
        ' Load the document
        Dim original As PdfDocument = PdfDocument.FromFile(inputPath)

        ' Step 1: Sanitize to remove active content
        Dim sanitized As PdfDocument = Cleaner.SanitizeWithSvg(original)

        ' Step 2: Clean metadata
        sanitized.MetaData.Author = "Processed Document"
        sanitized.MetaData.Creator = "Secure Processor"
        sanitized.MetaData.Producer = ""
        sanitized.MetaData.CreationDate = DateTime.Now
        sanitized.MetaData.ModifiedDate = DateTime.Now

        ' Remove all custom metadata
        For Each key As String In sanitized.MetaData.Keys()
            If key <> "Title" AndAlso key <> "Author" AndAlso key <> "CreationDate" AndAlso key <> "ModifiedDate" Then
                sanitized.MetaData.RemoveMetaDataKey(key)
            End If
        Next

        Return sanitized
    End Function
End Class

' Usage
Module Program
    Sub Main()
        Dim processor As New SecureDocumentProcessor()
        Dim safe As PdfDocument = processor.ProcessUntrustedDocument("email-attachment.pdf")
        safe.SaveAs("email-attachment-safe.pdf")
    End Sub
End Module
$vbLabelText   $csharpLabel

¿Cómo buscar vulnerabilidades de seguridad en un PDF?

Antes de procesar o desinfectar los documentos, conviene evaluar qué amenazas potenciales contienen. El método Cleaner.ScanPdf de IronPDF examina documentos usando reglas YARA —definiciones de patrones comúnmente usadas en análisis de malware y detección de amenazas. El análisis identifica las características asociadas a los archivos PDF maliciosos, incluyendo los patrones de JavaScript incrustado frecuentes en campañas de phishing de facturación detectadas en el mercado español.

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/scan-vulnerabilities.cs
using IronPdf;

// Load the document to scan
PdfDocument pdf = PdfDocument.FromFile("suspicious-document.pdf");

// Scan using default YARA rules
CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

// Check the scan results
bool threatsDetected = scanResult.IsDetected;
int riskCount = scanResult.Risks.Count;

// Process identified risks
if (scanResult.IsDetected)
{
    foreach (var risk in scanResult.Risks)
    {
        // Handle each identified risk
    }

    // Sanitize the document before use
    PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf);
    sanitized.SaveAs("suspicious-document-safe.pdf");
}
Imports IronPdf

' Load the document to scan
Dim pdf As PdfDocument = PdfDocument.FromFile("suspicious-document.pdf")

' Scan using default YARA rules
Dim scanResult As CleanerScanResult = Cleaner.ScanPdf(pdf)

' Check the scan results
Dim threatsDetected As Boolean = scanResult.IsDetected
Dim riskCount As Integer = scanResult.Risks.Count

' Process identified risks
If scanResult.IsDetected Then
    For Each risk In scanResult.Risks
        ' Handle each identified risk
    Next

    ' Sanitize the document before use
    Dim sanitized As PdfDocument = Cleaner.SanitizeWithSvg(pdf)
    sanitized.SaveAs("suspicious-document-safe.pdf")
End If
$vbLabelText   $csharpLabel

Puede proporcionar archivos de reglas YARA personalizados para requisitos de detección especializados. Las organizaciones con sistemas de gestión de riesgos específicos o necesidades de cumplimiento del Esquema Nacional de Seguridad (ENS) suelen mantener sus propios conjuntos de reglas:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/scan-custom-yara.cs
using IronPdf;

PdfDocument pdf = PdfDocument.FromFile("incoming-document.pdf");

// Scan with custom YARA rules
string[] customYaraFiles = { "corporate-rules.yar", "industry-specific.yar" };
CleanerScanResult result = Cleaner.ScanPdf(pdf, customYaraFiles);

if (result.IsDetected)
{
    // Document triggered custom rules and requires review or sanitization
    PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf);
    sanitized.SaveAs("incoming-document-safe.pdf");
}
Imports IronPdf

Dim pdf As PdfDocument = PdfDocument.FromFile("incoming-document.pdf")

' Scan with custom YARA rules
Dim customYaraFiles As String() = {"corporate-rules.yar", "industry-specific.yar"}
Dim result As CleanerScanResult = Cleaner.ScanPdf(pdf, customYaraFiles)

If result.IsDetected Then
    ' Document triggered custom rules and requires review or sanitization
    Dim sanitized As PdfDocument = Cleaner.SanitizeWithSvg(pdf)
    sanitized.SaveAs("incoming-document-safe.pdf")
End If
$vbLabelText   $csharpLabel

La integración del escaneado en los flujos de admisión de documentos permite automatizar las decisiones de seguridad:

using IronPdf;
using System;
using System.IO;

public enum DocumentSafetyLevel
{
    Safe,
    Suspicious,
    Dangerous
}

public class DocumentSecurityGateway
{
    public DocumentSafetyLevel EvaluateDocument(string filePath)
    {
        PdfDocument pdf = PdfDocument.FromFile(filePath);
        CleanerScanResult scan = Cleaner.ScanPdf(pdf);

        if (!scan.IsDetected)
        {
            return DocumentSafetyLevel.Safe;
        }

        // Evaluate severity based on number of risks
        if (scan.Risks.Count > 5)
        {
            return DocumentSafetyLevel.Dangerous;
        }

        return DocumentSafetyLevel.Suspicious;
    }

    public PdfDocument ProcessIncomingDocument(string filePath, string outputDirectory)
    {
        DocumentSafetyLevel safety = EvaluateDocument(filePath);
        string fileName = Path.GetFileName(filePath);

        switch (safety)
        {
            case DocumentSafetyLevel.Safe:
                return PdfDocument.FromFile(filePath);

            case DocumentSafetyLevel.Suspicious:
                PdfDocument suspicious = PdfDocument.FromFile(filePath);
                return Cleaner.SanitizeWithSvg(suspicious);

            case DocumentSafetyLevel.Dangerous:
                throw new SecurityException($"Document {fileName} contains dangerous content");

            default:
                throw new InvalidOperationException("Unknown safety level");
        }
    }
}
using IronPdf;
using System;
using System.IO;

public enum DocumentSafetyLevel
{
    Safe,
    Suspicious,
    Dangerous
}

public class DocumentSecurityGateway
{
    public DocumentSafetyLevel EvaluateDocument(string filePath)
    {
        PdfDocument pdf = PdfDocument.FromFile(filePath);
        CleanerScanResult scan = Cleaner.ScanPdf(pdf);

        if (!scan.IsDetected)
        {
            return DocumentSafetyLevel.Safe;
        }

        // Evaluate severity based on number of risks
        if (scan.Risks.Count > 5)
        {
            return DocumentSafetyLevel.Dangerous;
        }

        return DocumentSafetyLevel.Suspicious;
    }

    public PdfDocument ProcessIncomingDocument(string filePath, string outputDirectory)
    {
        DocumentSafetyLevel safety = EvaluateDocument(filePath);
        string fileName = Path.GetFileName(filePath);

        switch (safety)
        {
            case DocumentSafetyLevel.Safe:
                return PdfDocument.FromFile(filePath);

            case DocumentSafetyLevel.Suspicious:
                PdfDocument suspicious = PdfDocument.FromFile(filePath);
                return Cleaner.SanitizeWithSvg(suspicious);

            case DocumentSafetyLevel.Dangerous:
                throw new SecurityException($"Document {fileName} contains dangerous content");

            default:
                throw new InvalidOperationException("Unknown safety level");
        }
    }
}
Imports IronPdf
Imports System
Imports System.IO

Public Enum DocumentSafetyLevel
    Safe
    Suspicious
    Dangerous
End Enum

Public Class DocumentSecurityGateway
    Public Function EvaluateDocument(filePath As String) As DocumentSafetyLevel
        Dim pdf As PdfDocument = PdfDocument.FromFile(filePath)
        Dim scan As CleanerScanResult = Cleaner.ScanPdf(pdf)

        If Not scan.IsDetected Then
            Return DocumentSafetyLevel.Safe
        End If

        ' Evaluate severity based on number of risks
        If scan.Risks.Count > 5 Then
            Return DocumentSafetyLevel.Dangerous
        End If

        Return DocumentSafetyLevel.Suspicious
    End Function

    Public Function ProcessIncomingDocument(filePath As String, outputDirectory As String) As PdfDocument
        Dim safety As DocumentSafetyLevel = EvaluateDocument(filePath)
        Dim fileName As String = Path.GetFileName(filePath)

        Select Case safety
            Case DocumentSafetyLevel.Safe
                Return PdfDocument.FromFile(filePath)

            Case DocumentSafetyLevel.Suspicious
                Dim suspicious As PdfDocument = PdfDocument.FromFile(filePath)
                Return Cleaner.SanitizeWithSvg(suspicious)

            Case DocumentSafetyLevel.Dangerous
                Throw New SecurityException($"Document {fileName} contains dangerous content")

            Case Else
                Throw New InvalidOperationException("Unknown safety level")
        End Select
    End Function
End Class
$vbLabelText   $csharpLabel

¿Cómo puedo crear una cadena completa de redacción y desinfección?

El procesamiento de documentos en producción bajo la LOPDGDD requiere la combinación de varias técnicas en un flujo de trabajo cohesivo. Un proceso completo para el mercado español podría: (1) escanear los documentos entrantes con reglas YARA, (2) desinfectar los que pasen el filtro inicial, (3) redactar identificadores personales españoles (DNI, NIE, NIF, IBAN, NSS) mediante los patrones de RedactorIdentificadoresEspanoles, (4) aplicar redacciones de regiones para firmas y fotografías, (5) eliminar metadatos de identificación personal y (6) producir un registro de auditoría conforme al art. 30 RGPD. El siguiente ejemplo muestra un enfoque integrado:

using IronPdf;
using IronSoftware.Drawing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

public class DocumentProcessingResult
{
    public string OriginalFile { get; set; }
    public string OutputFile { get; set; }
    public bool WasSanitized { get; set; }
    public int TextRedactionsApplied { get; set; }
    public int RegionRedactionsApplied { get; set; }
    public bool MetadataCleaned { get; set; }
    public List<string> SensitiveDataTypesFound { get; set; } = new List<string>();
    public DateTime ProcessedAt { get; set; }
    public bool Success { get; set; }
    public string ErrorMessage { get; set; }
}

public class ComprehensiveDocumentProcessor
{
    // Sensitive data patterns
    private readonly Dictionary<string, string> _sensitivePatterns = new Dictionary<string, string>
    {
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },
        { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" }
    };

    // Standard regions to redact (signature areas, photo locations)
    private readonly List<RectangleF> _standardRedactionRegions = new List<RectangleF>
    {
        new RectangleF(72, 72, 200, 50),    // Bottom left signature
        new RectangleF(350, 72, 200, 50)    // Bottom right signature
    };

    private readonly string _organizationName;

    public ComprehensiveDocumentProcessor(string organizationName)
    {
        _organizationName = organizationName;
    }

    public DocumentProcessingResult ProcessDocument(
        string inputPath,
        string outputPath,
        bool sanitize = true,
        bool redactPatterns = true,
        bool redactRegions = true,
        bool cleanMetadata = true,
        List<string> additionalTermsToRedact = null)
    {
        var result = new DocumentProcessingResult
        {
            OriginalFile = inputPath,
            OutputFile = outputPath,
            ProcessedAt = DateTime.Now
        };

        try
        {
            // Load the document
            PdfDocument pdf = PdfDocument.FromFile(inputPath);

            // Step 1: Security scan
            CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

            if (scanResult.IsDetected && scanResult.Risks.Count > 10)
            {
                throw new SecurityException("Document contains too many security risks to process");
            }

            // Step 2: Sanitization (if needed or requested)
            if (sanitize || scanResult.IsDetected)
            {
                pdf = Cleaner.SanitizeWithSvg(pdf);
                result.WasSanitized = true;
            }

            // Step 3: Pattern-based text redaction
            if (redactPatterns)
            {
                string fullText = pdf.ExtractAllText();
                HashSet<string> valuesToRedact = new HashSet<string>();

                foreach (var pattern in _sensitivePatterns)
                {
                    Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                    MatchCollection matches = regex.Matches(fullText);

                    if (matches.Count > 0)
                    {
                        result.SensitiveDataTypesFound.Add($"{pattern.Key} ({matches.Count})");
                        foreach (Match match in matches)
                        {
                            valuesToRedact.Add(match.Value);
                        }
                    }
                }

                // Apply redactions
                foreach (string value in valuesToRedact)
                {
                    pdf.RedactTextOnAllPages(value);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 4: Additional specific terms
            if (additionalTermsToRedact != null)
            {
                foreach (string term in additionalTermsToRedact)
                {
                    pdf.RedactTextOnAllPages(term);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 5: Region-based redaction
            if (redactRegions)
            {
                foreach (RectangleF region in _standardRedactionRegions)
                {
                    pdf.RedactRegionsOnAllPages(region);
                    result.RegionRedactionsApplied++;
                }
            }

            // Step 6: Metadata cleaning
            if (cleanMetadata)
            {
                pdf.MetaData.Author = _organizationName;
                pdf.MetaData.Creator = $"{_organizationName} Document Processor";
                pdf.MetaData.Producer = "";
                pdf.MetaData.Subject = "";
                pdf.MetaData.Keywords = "";
                pdf.MetaData.CreationDate = DateTime.Now;
                pdf.MetaData.ModifiedDate = DateTime.Now;
                result.MetadataCleaned = true;
            }

            // Step 7: Save the processed document
            pdf.SaveAs(outputPath);
            result.Success = true;
        }
        catch (Exception ex)
        {
            result.Success = false;
            result.ErrorMessage = ex.Message;
        }

        return result;
    }
}

// Usage example
class Program
{
    static void Main()
    {
        var processor = new ComprehensiveDocumentProcessor("Acme Corporation");

        // Process a single document with all protections
        var result = processor.ProcessDocument(
            inputPath: "customer-application.pdf",
            outputPath: "customer-application-redacted.pdf",
            sanitize: true,
            redactPatterns: true,
            redactRegions: true,
            cleanMetadata: true,
            additionalTermsToRedact: new List<string> { "Project Alpha", "Internal Use Only" }
        );

        // Batch process multiple documents
        string[] inputFiles = Directory.GetFiles("incoming", "*.pdf");
        foreach (string file in inputFiles)
        {
            string outputFile = Path.Combine("processed", Path.GetFileName(file));
            processor.ProcessDocument(file, outputFile);
        }
    }
}
using IronPdf;
using IronSoftware.Drawing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

public class DocumentProcessingResult
{
    public string OriginalFile { get; set; }
    public string OutputFile { get; set; }
    public bool WasSanitized { get; set; }
    public int TextRedactionsApplied { get; set; }
    public int RegionRedactionsApplied { get; set; }
    public bool MetadataCleaned { get; set; }
    public List<string> SensitiveDataTypesFound { get; set; } = new List<string>();
    public DateTime ProcessedAt { get; set; }
    public bool Success { get; set; }
    public string ErrorMessage { get; set; }
}

public class ComprehensiveDocumentProcessor
{
    // Sensitive data patterns
    private readonly Dictionary<string, string> _sensitivePatterns = new Dictionary<string, string>
    {
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },
        { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" }
    };

    // Standard regions to redact (signature areas, photo locations)
    private readonly List<RectangleF> _standardRedactionRegions = new List<RectangleF>
    {
        new RectangleF(72, 72, 200, 50),    // Bottom left signature
        new RectangleF(350, 72, 200, 50)    // Bottom right signature
    };

    private readonly string _organizationName;

    public ComprehensiveDocumentProcessor(string organizationName)
    {
        _organizationName = organizationName;
    }

    public DocumentProcessingResult ProcessDocument(
        string inputPath,
        string outputPath,
        bool sanitize = true,
        bool redactPatterns = true,
        bool redactRegions = true,
        bool cleanMetadata = true,
        List<string> additionalTermsToRedact = null)
    {
        var result = new DocumentProcessingResult
        {
            OriginalFile = inputPath,
            OutputFile = outputPath,
            ProcessedAt = DateTime.Now
        };

        try
        {
            // Load the document
            PdfDocument pdf = PdfDocument.FromFile(inputPath);

            // Step 1: Security scan
            CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

            if (scanResult.IsDetected && scanResult.Risks.Count > 10)
            {
                throw new SecurityException("Document contains too many security risks to process");
            }

            // Step 2: Sanitization (if needed or requested)
            if (sanitize || scanResult.IsDetected)
            {
                pdf = Cleaner.SanitizeWithSvg(pdf);
                result.WasSanitized = true;
            }

            // Step 3: Pattern-based text redaction
            if (redactPatterns)
            {
                string fullText = pdf.ExtractAllText();
                HashSet<string> valuesToRedact = new HashSet<string>();

                foreach (var pattern in _sensitivePatterns)
                {
                    Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                    MatchCollection matches = regex.Matches(fullText);

                    if (matches.Count > 0)
                    {
                        result.SensitiveDataTypesFound.Add($"{pattern.Key} ({matches.Count})");
                        foreach (Match match in matches)
                        {
                            valuesToRedact.Add(match.Value);
                        }
                    }
                }

                // Apply redactions
                foreach (string value in valuesToRedact)
                {
                    pdf.RedactTextOnAllPages(value);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 4: Additional specific terms
            if (additionalTermsToRedact != null)
            {
                foreach (string term in additionalTermsToRedact)
                {
                    pdf.RedactTextOnAllPages(term);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 5: Region-based redaction
            if (redactRegions)
            {
                foreach (RectangleF region in _standardRedactionRegions)
                {
                    pdf.RedactRegionsOnAllPages(region);
                    result.RegionRedactionsApplied++;
                }
            }

            // Step 6: Metadata cleaning
            if (cleanMetadata)
            {
                pdf.MetaData.Author = _organizationName;
                pdf.MetaData.Creator = $"{_organizationName} Document Processor";
                pdf.MetaData.Producer = "";
                pdf.MetaData.Subject = "";
                pdf.MetaData.Keywords = "";
                pdf.MetaData.CreationDate = DateTime.Now;
                pdf.MetaData.ModifiedDate = DateTime.Now;
                result.MetadataCleaned = true;
            }

            // Step 7: Save the processed document
            pdf.SaveAs(outputPath);
            result.Success = true;
        }
        catch (Exception ex)
        {
            result.Success = false;
            result.ErrorMessage = ex.Message;
        }

        return result;
    }
}

// Usage example
class Program
{
    static void Main()
    {
        var processor = new ComprehensiveDocumentProcessor("Acme Corporation");

        // Process a single document with all protections
        var result = processor.ProcessDocument(
            inputPath: "customer-application.pdf",
            outputPath: "customer-application-redacted.pdf",
            sanitize: true,
            redactPatterns: true,
            redactRegions: true,
            cleanMetadata: true,
            additionalTermsToRedact: new List<string> { "Project Alpha", "Internal Use Only" }
        );

        // Batch process multiple documents
        string[] inputFiles = Directory.GetFiles("incoming", "*.pdf");
        foreach (string file in inputFiles)
        {
            string outputFile = Path.Combine("processed", Path.GetFileName(file));
            processor.ProcessDocument(file, outputFile);
        }
    }
}
Imports IronPdf
Imports IronSoftware.Drawing
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.RegularExpressions

Public Class DocumentProcessingResult
    Public Property OriginalFile As String
    Public Property OutputFile As String
    Public Property WasSanitized As Boolean
    Public Property TextRedactionsApplied As Integer
    Public Property RegionRedactionsApplied As Integer
    Public Property MetadataCleaned As Boolean
    Public Property SensitiveDataTypesFound As List(Of String) = New List(Of String)()
    Public Property ProcessedAt As DateTime
    Public Property Success As Boolean
    Public Property ErrorMessage As String
End Class

Public Class ComprehensiveDocumentProcessor
    ' Sensitive data patterns
    Private ReadOnly _sensitivePatterns As Dictionary(Of String, String) = New Dictionary(Of String, String) From {
        {"SSN", "\b\d{3}-\d{2}-\d{4}\b"},
        {"Credit Card", "\b(?:\d{4}[-\s]?){3}\d{1,4}\b"},
        {"Email", "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"},
        {"Phone", "\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b"}
    }

    ' Standard regions to redact (signature areas, photo locations)
    Private ReadOnly _standardRedactionRegions As List(Of RectangleF) = New List(Of RectangleF) From {
        New RectangleF(72, 72, 200, 50),    ' Bottom left signature
        New RectangleF(350, 72, 200, 50)    ' Bottom right signature
    }

    Private ReadOnly _organizationName As String

    Public Sub New(organizationName As String)
        _organizationName = organizationName
    End Sub

    Public Function ProcessDocument(
        inputPath As String,
        outputPath As String,
        Optional sanitize As Boolean = True,
        Optional redactPatterns As Boolean = True,
        Optional redactRegions As Boolean = True,
        Optional cleanMetadata As Boolean = True,
        Optional additionalTermsToRedact As List(Of String) = Nothing) As DocumentProcessingResult

        Dim result As New DocumentProcessingResult With {
            .OriginalFile = inputPath,
            .OutputFile = outputPath,
            .ProcessedAt = DateTime.Now
        }

        Try
            ' Load the document
            Dim pdf As PdfDocument = PdfDocument.FromFile(inputPath)

            ' Step 1: Security scan
            Dim scanResult As CleanerScanResult = Cleaner.ScanPdf(pdf)

            If scanResult.IsDetected AndAlso scanResult.Risks.Count > 10 Then
                Throw New SecurityException("Document contains too many security risks to process")
            End If

            ' Step 2: Sanitization (if needed or requested)
            If sanitize OrElse scanResult.IsDetected Then
                pdf = Cleaner.SanitizeWithSvg(pdf)
                result.WasSanitized = True
            End If

            ' Step 3: Pattern-based text redaction
            If redactPatterns Then
                Dim fullText As String = pdf.ExtractAllText()
                Dim valuesToRedact As New HashSet(Of String)()

                For Each pattern In _sensitivePatterns
                    Dim regex As New Regex(pattern.Value, RegexOptions.IgnoreCase)
                    Dim matches As MatchCollection = regex.Matches(fullText)

                    If matches.Count > 0 Then
                        result.SensitiveDataTypesFound.Add($"{pattern.Key} ({matches.Count})")
                        For Each match As Match In matches
                            valuesToRedact.Add(match.Value)
                        Next
                    End If
                Next

                ' Apply redactions
                For Each value As String In valuesToRedact
                    pdf.RedactTextOnAllPages(value)
                    result.TextRedactionsApplied += 1
                Next
            End If

            ' Step 4: Additional specific terms
            If additionalTermsToRedact IsNot Nothing Then
                For Each term As String In additionalTermsToRedact
                    pdf.RedactTextOnAllPages(term)
                    result.TextRedactionsApplied += 1
                Next
            End If

            ' Step 5: Region-based redaction
            If redactRegions Then
                For Each region As RectangleF In _standardRedactionRegions
                    pdf.RedactRegionsOnAllPages(region)
                    result.RegionRedactionsApplied += 1
                Next
            End If

            ' Step 6: Metadata cleaning
            If cleanMetadata Then
                pdf.MetaData.Author = _organizationName
                pdf.MetaData.Creator = $"{_organizationName} Document Processor"
                pdf.MetaData.Producer = ""
                pdf.MetaData.Subject = ""
                pdf.MetaData.Keywords = ""
                pdf.MetaData.CreationDate = DateTime.Now
                pdf.MetaData.ModifiedDate = DateTime.Now
                result.MetadataCleaned = True
            End If

            ' Step 7: Save the processed document
            pdf.SaveAs(outputPath)
            result.Success = True
        Catch ex As Exception
            result.Success = False
            result.ErrorMessage = ex.Message
        End Try

        Return result
    End Function
End Class

' Usage example
Class Program
    Shared Sub Main()
        Dim processor As New ComprehensiveDocumentProcessor("Acme Corporation")

        ' Process a single document with all protections
        Dim result = processor.ProcessDocument(
            inputPath:="customer-application.pdf",
            outputPath:="customer-application-redacted.pdf",
            sanitize:=True,
            redactPatterns:=True,
            redactRegions:=True,
            cleanMetadata:=True,
            additionalTermsToRedact:=New List(Of String) From {"Project Alpha", "Internal Use Only"}
        )

        ' Batch process multiple documents
        Dim inputFiles As String() = Directory.GetFiles("incoming", "*.pdf")
        For Each file As String In inputFiles
            Dim outputFile As String = Path.Combine("processed", Path.GetFileName(file))
            processor.ProcessDocument(file, outputFile)
        Next
    End Sub
End Class
$vbLabelText   $csharpLabel

Entrada

Un formulario de solicitud que contiene varios tipos de datos confidenciales —DNI del solicitante, IBAN y bloques de firma manuscrita— que requieren una protección completa bajo la LOPDGDD.

Muestra de resultado

Para adaptar este pipeline al mercado español, sustituya el diccionario _sensitivePatterns por el PatronesEspanoles de la clase RedactorIdentificadoresEspanoles definida anteriormente. El resultado es un procesador de documentos LOPDGDD-compliant con auditoría trazable conforme al art. 30 RGPD.


Próximos pasos

La redacción documental conforme a la LOPDGDD requiere algo más que medidas superficiales. La redacción real elimina el contenido de la estructura del documento de forma permanente —la AEPD ha sancionado expresamente la ocultación visual como incumplimiento técnico del art. 25 RGPD. Los patrones regex para identificadores personales españoles (DNI, NIE, NIF, IBAN, NSS) automatizan el descubrimiento de datos sujetos al principio de minimización (art. 5.1.c LOPDGDD). La redacción por regiones gestiona firmas, fotografías de DNI y sellos empresariales que la concordancia de texto no puede abordar. La limpieza de metadatos elimina datos personales incrustados —nombre del autor, ruta del fichero— que podrían identificar al responsable del tratamiento. La sanitización elimina scripts incrustados relevantes para los flujos de recepción de facturas bajo VeriFactu y Facturae.

IronPDF ofrece todas estas funciones a través de una API coherente que se integra de forma natural con las prácticas de desarrollo de C# y .NET. El procesamiento es completamente on-premise —sin transmisión de datos personales a servicios externos— lo que satisface los requisitos de transferencia internacional de datos de la LOPDGDD cuando el tratamiento se realiza en el territorio de la UE. Los métodos que se muestran en esta guía se pueden aplicar a documentos individuales o a procesamiento por lotes de miles de expedientes. Tanto si está construyendo flujos de gestión de expedientes bajo LOPDGDD, preparando documentos para transmisión SII o Facturae redactando datos personales superfluos, o implementando controles técnicos para la renovación de certificados eIDAS y FNMT, estas técnicas forman la base del tratamiento documental responsable en el mercado español.

¿Listo para empezar a construir? Descarga IronPDF y pruébalo con una prueba gratuita de 30 días. La biblioteca incluye una licencia de desarrollo gratuita para evaluar en profundidad las funciones de redacción, extracción de texto y desinfección antes de adquirir una licencia de producción. Si tiene alguna pregunta sobre la implementación de flujos de trabajo LOPDGDD o la integración con sistemas de facturación electrónica VeriFactu/Facturae, póngase en contacto con nuestro equipo de asistencia técnica.

Preguntas Frecuentes

¿Qué obligaciones impone la LOPDGDD en materia de redacción de datos personales en PDF?

La LOPDGDD (arts. 5.1.b, 5.1.c, 25 y 32) exige minimizar los datos, protegerlos desde el diseño y aplicar medidas técnicas de seguridad. La AEPD ha sancionado explícitamente la ocultación visual de DNI y datos personales en PDF (expediente PS/00120/2023: 200.000 €) al no constituir eliminación real. IronPDF implementa redacción permanente que satisface estos requisitos.

¿Cómo redacto DNI, NIE, NIF y CIF en documentos PDF con C#?

IronPDF permite redactar identificadores españoles usando patrones regex: DNI (\b\d{8}[A-Z]\b), NIE (\b[XYZ]\d{7}[A-Z]\b), NIF empresa (\b[ABCDEFGHJNPQRSUVW]\d{7}[A-J0-9]\b). Extraiga el texto del PDF, localice coincidencias con Regex .NET y aplique RedactTextOnAllPages para cada valor detectado.

¿Cómo redacto el IBAN de una cuenta bancaria española en un PDF?

Use el patrón regex \bES\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}\b para detectar IBANs españoles con o sin espacios. Aplique IronPDF RedactTextOnAllPages sobre cada coincidencia para eliminar el IBAN de la estructura interna del documento.

¿Qué relación tiene VeriFactu con la redacción de datos personales?

Las facturas VeriFactu contienen NIF del cliente e IBAN. La LOPDGDD exige minimizar datos tras el plazo fiscal (4 años AEAT). La solución es redactar el PDF visible de la factura con IronPDF — manteniendo intacto el registro hash/huella del sistema de facturación — para cumplir simultáneamente VeriFactu y el derecho de supresión LOPDGDD (art. 17 RGPD).

¿Cuál es la diferencia entre redacción real y superposición visual según la AEPD?

La AEPD diferencia explícitamente ambas: la superposición visual (rectángulos opacos) deja los caracteres recuperables en los flujos de contenido del PDF. La redacción real elimina los datos de caracteres de la estructura interna. En el expediente PS/00120/2023, la AEPD sancionó con 200.000 € la entrega de PDFs con DNI 'tachados' visualmente pero recuperables. IronPDF implementa redacción real.

¿Puede IronPDF redactar fotografías de DNI y firmas manuscritas en PDF?

Sí, mediante redacción basada en regiones. Use RectangleF para definir las coordenadas del área (fotografía de DNI, bloque de firma) y aplique RedactRegionsOnAllPages. Las coordenadas se miden en puntos desde la esquina inferior izquierda; una página A4 mide 595x842 puntos.

¿Cómo elimino metadatos con datos personales de un PDF antes de compartirlo?

IronPDF expone MetaData.Author, Creator, Producer y claves personalizadas. Sustituya el autor por un valor genérico, normalice las marcas de tiempo y use RemoveMetaDataKey para eliminar propiedades como 'OriginalFilename' o 'LastSavedBy' que la AEPD considera datos personales cuando identifican a una persona física.

¿Es el procesamiento on-premise de IronPDF suficiente para cumplir las restricciones de transferencia LOPDGDD?

Sí. IronPDF procesa los documentos completamente en local, sin enviar datos a servicios externos. Esto satisface las restricciones de transferencia internacional de datos de la LOPDGDD cuando el tratamiento se realiza en territorio de la UE, cumpliendo el principio de minimización y los requisitos del Capítulo V del RGPD.

Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien ...

Leer más
¿Listo para empezar?
Nuget Descargas 19,014,616 | Versión: 2026.5 just released
Still Scrolling Icon

¿Aún desplazándote?

¿Quieres una prueba rápida? PM > Install-Package IronPdf
ejecutar una muestra Mira cómo tu HTML se convierte en PDF.