La redacción real de PDF en Delphi significa eliminar los bytes subyacentes, no pintar sobre ellos. losLab PDF Library traza esa línea con dos API: RedactRegion edita el flujo de contenido de la página para que el texto, vectores e imágenes eliminados desaparezcan físicamente, y SanitizeDocument limpia los metadatos, scripts y acciones donde también se oculta la información redactada. Si hace mal cualquiera de las dos partes, enviará un archivo que parece censurado en pantalla pero que devuelve el secreto a cualquiera que copie el texto o abra las Propiedades del documento
El modo de falla es de conocimiento público. Periódicos, tribunales y agencias gubernamentales han publicado archivos PDF con un rectángulo negro colocado sobre un nombre o un número, solo para que un lector seleccione el texto debajo, lo copie y pegue la cadena supuestamente redactada a la vista de todos. El rectángulo era un dibujo. El texto seguía allí, alojado en el flujo de contenido una capa por debajo de la pintura, completamente intacto y seleccionable. losLab PDF Library, el motor PDF de Delphi y C++Builder expuesto a través de la clase TPDFlib, trata esa distinción como el objetivo principal de la redacción en lugar de una idea tardía
¿Por qué dibujar un cuadro negro sobre el texto de un PDF no es una redacción real?
losLab PDF Library expone MaskRect para los casos en que cubrir es genuinamente lo que usted desea: una marca de agua, un oscurecimiento visual, un sello de prueba. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) agrega un rectángulo al flujo de contenido de la página y lo pinta con el operador de construcción de ruta re seguido del operador de relleno f (ISO 32000-1 §8.5). Cada byte que ya estaba en la página (los glifos del nombre que cubrió, los trazos vectoriales, la imagen incrustada) permanece exactamente donde estaba, una instrucción antes en el mismo flujo. Un visor compatible dibuja el cuadro negro al final, por lo que gana visualmente. La cubierta no cambia nada debajo
Cualquiera puede recuperar el contenido original de tres maneras: seleccionar y copiar el texto directamente a través del cuadro opaco, ejecutar un extractor de texto sobre la página o eliminar el rectángulo de cubierta del flujo de contenido. MaskRect es honesto al respecto en su propio nombre: enmascara, no elimina. El resultado es una calcomanía sobre una palabra que aún se puede leer levantando la calcomanía
Cómo RedactRegion elimina contenido al nivel de instrucción
RedactRegion(Page, Left, Top, Width, Height, Options) funciona una capa por debajo de la pintura. losLab PDF Library analiza cada capa de contenido de la página en un TPDFContentProgram, recorre la lista de instrucciones con un analizador de seguimiento de CTM (FindInstructionsInRect en el modelo de contenido) y marca cada operador de visualización de texto, pintura de ruta e imagen Do cuya área pintada se interseque con su rectángulo. Esas instrucciones se eliminan con TPDFContentProgram.Delete y la capa editada se escribe de nuevo con WritePageContentLayer. Los bytes no se deslizan detrás de una cubierta: salen del flujo. Un extractor de texto ejecutado posteriormente no encuentra nada, porque no hay nada que encontrar
Vale la pena incorporar dos detalles de implementación a su propio razonamiento. RedactRegion toma un rectángulo superior izquierdo en el origen y las unidades actuales y lo convierte internamente al espacio de usuario inferior izquierdo que utiliza el flujo de contenido, de modo que usted especifica la región tal como la ve en la página. Cuando un rectángulo cubre varias instrucciones, RedactRegion las elimina en orden descendente de índice, porque eliminar la instrucción 4 antes de la instrucción 7 desplazaría cada índice posterior y corrompería el rango. Si pasa Options = 0, losLab PDF Library también estampa un cuadro negro sólido sobre la región limpia como marcador visible, pero ese marcador es cosmético, aplicado después de que el contenido real ya se ha eliminado
En la práctica, rara vez se codifica el rectángulo de manera rígida. Se busca la cadena de caracteres confidencial en la página y se lee su cuadro delimitador (la misma búsqueda de página y enumeración de elementos analizada en localización de texto y elementos de página en Delphi) y luego se entrega ese rectángulo a RedactRegion:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', '') = 1 then
begin
// Delete everything painted inside a 220 x 14 pt box on page 1.
// Coordinates are top-left, in the current origin and units.
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // Options=0 stamps a black marker
PDF.SaveToFile('contract-redacted.pdf');
end;
finally
PDF.Free;
end;
end;
¿Dónde se sigue ocultando la información redactada en un PDF?
La redacción que se detiene en la capa visible deja un rastro, porque un PDF almacena información en lugares que un lector nunca examina. losLab PDF Library agrupa las filtraciones en canales que vale la pena mencionar. El Diccionario de información del documento (Document Information Dictionary) y el flujo de metadatos XMP suelen contener cadenas de caracteres como Author, Producer o Keywords que nombran a la misma persona o caso que usted eliminó del cuerpo. El JavaScript a nivel de documento y el OpenAction del catálogo pueden ejecutarse al abrir y acceder a datos que usted creía eliminados. Cada anotación lleva una acción /A opcional, y una anotación en sí (una nota adhesiva, un campo de formulario antiguo, un enlace) puede ubicarse directamente sobre el punto redactado con su propia copia del texto. Nada de esto se ve afectado por el cifrado: un archivo cifrado con un paquete XMP de texto plano sigue filtrando su título a cualquier indexador, una brecha que vale la pena auditar el cifrado y los permisos por cuenta propia. Una desinfección defendible debe visitar cada uno de estos lugares ocultos, no solo la página que estaba mirando
Limpieza del documento con SanitizeDocument
losLab PDF Library responde a esa propagación de filtraciones con SanitizeDocument(Flags), una llamada de orquestación que ejecuta un conjunto de limpiezas independientes seleccionadas mediante una máscara de bits y devuelve el número de limpiezas que aplicó. Las banderas son combinables. SANITIZE_JAVASCRIPT elimina cada paquete de JavaScript a nivel de documento y cualquier script OpenAction. SANITIZE_ACTIONS remueve el diccionario de acciones /A de cada anotación, a través del nuevo TPDFAnnots.RemoveAnnotAction, y limpia el OpenAction del catálogo. SANITIZE_METADATA borra Author, Subject, Keywords, Creator y Producer tanto en el Diccionario de información del documento (ISO 32000-1 §14.3.3) como en el flujo de metadatos XMP (§14.3.2). SANITIZE_ANNOTATIONS elimina cada anotación en todo el documento, y SANITIZE_OPEN_ACTION remueve la entrada /OpenAction del catálogo. SANITIZE_ALL es la unión bit a bit de las cinco
Las convenciones de índice dentro de estas limpiezas son exactamente el tipo de cosas que rompen silenciosamente un bucle implementado manualmente, lo cual es en gran parte la razón por la que SanitizeDocument existe como una sola llamada. GlobalJavaScriptPackageName se basa en 0 y RemoveGlobalJavaScript reduce la lista a medida que avanza, por lo que la limpieza de JavaScript siempre lee el índice de paquete 0 y lo elimina hasta que el conteo llegue a cero. DeleteAnnotation, por el contrario, se basa en 1. AnnotationCount informa sobre la página seleccionada actualmente, por lo que las limpiezas de anotaciones seleccionan cada página a su vez antes de realizar el conteo. losLab PDF Library maneja estos límites correctamente a nivel interno, por lo que usted pasa un conjunto de banderas y lee un conteo de vuelta en lugar de reproducir cinco reglas de iteración diferentes
var
Applied: Integer;
begin
// Strip metadata, scripts, and actions but keep the annotations:
Applied := PDF.SanitizeDocument(SANITIZE_METADATA or SANITIZE_JAVASCRIPT or
SANITIZE_ACTIONS or SANITIZE_OPEN_ACTION);
// Or clear every hidden channel, annotations included:
Applied := PDF.SanitizeDocument(SANITIZE_ALL);
end;
El flujo de trabajo completo de redacción y desinfección
Al unir ambos, el procedimiento completo es corto. losLab PDF Library elimina el contenido visible con RedactRegion, página por página, y luego limpia cada canal oculto con SanitizeDocument(SANITIZE_ALL) antes de escribir el archivo. Debido a que la redacción casi siempre se ejecuta en documentos que llegaron fuera de su control, vale la pena cargarlos a través de una ruta reforzada (la misma postura defensiva descrita en análisis seguro de archivos PDF no confiables) de modo que una entrada mal formada falle limpiamente en lugar de a la mitad del paso de redacción
var
PDF: TPDFlib;
Applied: Integer;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('incoming.pdf', '') = 1 then
begin
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // delete the sensitive line
Applied := PDF.SanitizeDocument(SANITIZE_ALL); // clear metadata, JS, actions, annots
PDF.SaveToFile('published.pdf');
end;
finally
PDF.Free;
end;
end;
Dónde se detiene la redacción del flujo de contenido
RedactRegion opera en el flujo de contenido, que es exactamente donde reside el texto digital nativo y exactamente donde el texto escaneado no lo hace. Cuando una página es una imagen escaneada, las palabras son píxeles dentro de un objeto de imagen (image XObject), no operadores de texto, y losLab PDF Library no puede extraer un nombre del medio de una trama de la manera en que elimina un glifo. Su manejo de imágenes es deliberadamente conservador: cualquier imagen cuya ubicación se interseque con el rectángulo se elimina por completo, por lo que un cuadro de redacción en una página escaneada se lleva todo el escaneo en lugar de solo una parte de él. Redactar una región dentro de una imagen significa rasterizar, pintar y volver a incrustar a nivel de píxel, lo cual es una operación diferente a la eliminación del flujo de contenido. La misma precaución se aplica a una capa de texto OCR sobre una página escaneada: RedactRegion elimina los operadores de texto que puede ver, pero la imagen debajo permanece intacta a menos que su objeto de imagen también se interseque con el cuadro
Dos puntos más precisos mantienen la integridad de los resultados. La prueba de intersección aproxima el ancho de cada bloque de texto a partir de su conteo de caracteres y tamaño de fuente en lugar de medir métricas de glifo exactas, por lo que una palabra muy corta establecida en una fuente proporcional puede pasar de largo por un rectángulo dibujado de forma ajustada: dé al cuadro un pequeño margen. La eliminación también es granular por instrucción: RedactRegion elimina operadores completos de visualización de texto, por lo que un rectángulo que corte solo parte de un bloque puede llevarse caracteres adyacentes que compartían el mismo operador. Ninguno de los límites es una razón para desconfiar del mecanismo, solo una razón para limitar el rectángulo cuidadosamente y verificar el resultado
La disciplina que cierra cada flujo de trabajo de redacción es la verificación, no la confianza. Redacte la región, desinfecte el documento, guarde en un archivo nuevo, vuelva a abrirlo e intente recuperar tanto el texto que eliminó como los metadatos que limpió: cuando ambos regresen vacíos, la redacción se habrá realizado correctamente. Las API RedactRegion y SanitizeDocument mostradas aquí se incluyen con losLab PDF Library para Delphi y C++Builder, junto con la referencia completa de desinfección y del modelo de contenido