Le caviardage PDF réel dans Delphi consiste à supprimer les octets sous-jacents, et non à peindre par-dessus. La bibliothèque PDF losLab trace cette frontière avec deux API : RedactRegion modifie le flux de contenu de la page afin que le texte, les vecteurs et les images supprimés disparaissent physiquement, et SanitizeDocument nettoie les métadonnées, les scripts et les actions où des informations masquées se cachent également. Si vous vous trompez sur l'un de ces deux aspects, vous livrez un fichier qui semble censuré à l'écran mais qui révèle ses secrets à quiconque copie le texte ou ouvre les Propriétés du document
Ce mode de défaillance est de notoriété publique. Des journaux, des tribunaux et des organismes publics ont tous publié des PDF avec un rectangle noir superposé à un nom ou à un numéro, pour qu'un lecteur finisse par sélectionner le texte sous-jacent, le copier et coller la chaîne censément masquée au vu et au su de tous. Le rectangle n'était qu'un dessin. Le texte était toujours là, présent dans le flux de contenu une couche en dessous de la peinture, parfaitement intact et entièrement sélectionnable. La bibliothèque PDF losLab, le moteur PDF Delphi et C++Builder exposé via la classe TPDFlib, considère cette distinction comme le but même du caviardage plutôt que comme une réflexion après coup
Pourquoi dessiner un encadré noir sur du texte PDF ne constitue-t-il pas un véritable caviardage ?
La bibliothèque PDF losLab expose MaskRect pour les cas où le recouvrement est réellement ce que vous souhaitez : un filigrane, un masquage visuel, un tampon de preuve. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) ajoute un rectangle au flux de contenu de la page et le dessine avec l'opérateur de construction de chemin re suivi de l'opérateur de remplissage f (ISO 32000-1 §8.5). Chaque octet qui se trouvait déjà sur la page — les glyphes du nom que vous avez couvert, les tracés vectoriels, l'image intégrée — reste exactement là où il était, une instruction plus haut dans le même flux. Une liseuse conforme dessine l'encadré noir en dernier, de sorte qu'il l'emporte visuellement. Le masque ne change rien en dessous
N'importe qui peut récupérer le contenu d'origine de trois manières : sélectionner et copier le texte directement à travers l'encadré opaque, exécuter un extracteur de texte sur la page, ou supprimer le rectangle de couverture du flux de contenu. MaskRect est explicite à ce sujet de par son nom même — il masque, il ne supprime pas. Le résultat s'apparente à un autocollant sur un mot que l'on peut toujours lire en décollant l'autocollant
Comment RedactRegion supprime le contenu au niveau des instructions
RedactRegion(Page, Left, Top, Width, Height, Options) travaille une couche sous la peinture. La bibliothèque PDF losLab analyse chaque couche de contenu de la page dans un TPDFContentProgram, parcourt la liste d'instructions avec un analyseur de suivi CTM (FindInstructionsInRect dans le modèle de contenu), et marque chaque opérateur d'affichage de texte, de tracé de chemin et d'image Do dont la zone peinte intersecte votre rectangle. Ces instructions sont supprimées avec TPDFContentProgram.Delete, et la couche modifiée est réécrite avec WritePageContentLayer. Les octets ne glissent pas derrière un masque — ils quittent le flux. Un extracteur de texte exécuté par la suite ne trouve rien, car il n'y a plus rien à trouver
Deux détails d'implémentation méritent d'être pris en compte dans votre raisonnement. RedactRegion prend un rectangle en haut à gauche dans l'origine et les unités actuelles et le convertit en interne vers l'espace utilisateur en bas à gauche qu'utilise le flux de contenu, de sorte que vous spécifiez la région telle que vous la voyez sur la page. Lorsqu'un rectangle couvre plusieurs instructions, RedactRegion les supprime dans l'ordre décroissant des index, car la suppression de l'instruction 4 avant l'instruction 7 décalerait tous les index ultérieurs et corromprait la plage. Passez Options = 0 et la bibliothèque PDF losLab applique également un encadré noir plein sur la région effacée en guise de marqueur visuel — mais ce marqueur est cosmétique, appliqué une fois le contenu réel déjà supprimé
En pratique, vous codez rarement le rectangle en dur. Vous recherchez la chaîne sensible sur la page et lisez son rectangle englobant — la même recherche de page et énumération d'éléments abordée dans la localisation de texte et d'éléments de page dans Delphi — puis transmettez ce rectangle à 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;
Où les informations masquées se cachent-elles encore dans un PDF ?
Un caviardage qui s'arrête à la couche visible laisse des traces, car un PDF stocke des informations dans des endroits qu'un lecteur ne regarde jamais. La bibliothèque PDF losLab regroupe ces fuites dans des canaux qui méritent d'être nommés. Le dictionnaire d'informations sur le document (Document Information Dictionary) et le flux de métadonnées XMP contiennent régulièrement une chaîne Author, Producer ou Keywords qui nomme précisément la personne ou le dossier que vous avez retiré du corps du document. Le JavaScript au niveau du document et le OpenAction du catalogue peuvent s'exécuter à l'ouverture et accéder à des données que vous pensiez disparues. Chaque annotation comporte une action optionnelle /A, et une annotation elle-même — une note adhésive, un champ de formulaire obsolète, un lien — peut se situer directement sur la zone caviardée avec sa propre copie du texte. Rien de tout cela n'est affecté par le chiffrement ; un fichier chiffré contenant un paquet XMP en texte brut laisse toujours fuiter son titre auprès de n'importe quel indexeur, une lacune qui mérite à elle seule d'effectuer un audit du chiffrement et des autorisations. Une désinfection fiable doit inspecter chacun de ces lieux secrets, et pas seulement la page que vous visualisiez
Nettoyage du document avec SanitizeDocument
La bibliothèque PDF losLab remédie à cet éventail de fuites avec SanitizeDocument(Flags), un appel d'orchestration qui exécute un ensemble d'actions de nettoyage indépendantes sélectionnées par un masque de bits et renvoie le nombre de nettoyages appliqués. Les drapeaux sont combinables. SANITIZE_JAVASCRIPT supprime chaque paquet JavaScript au niveau du document ainsi que tout script OpenAction. SANITIZE_ACTIONS élimine le dictionnaire d'actions /A de chaque annotation, via le nouveau TPDFAnnots.RemoveAnnotAction, et efface l'OpenAction du catalogue. SANITIZE_METADATA vide les champs Author, Subject, Keywords, Creator et Producer dans le dictionnaire d'informations sur le document (ISO 32000-1 §14.3.3) ainsi que dans le flux de métadonnées XMP (§14.3.2). SANITIZE_ANNOTATIONS supprime toutes les annotations de l'ensemble du document, et SANITIZE_OPEN_ACTION supprime l'entrée /OpenAction du catalogue. SANITIZE_ALL représente l'union de ces cinq drapeaux
Les conventions d'indexation au sein de ces scripts de nettoyage sont précisément le genre de choses qui brisent silencieusement une boucle programmée manuellement, ce qui explique en grande partie pourquoi SanitizeDocument se présente sous la forme d'un appel unique. GlobalJavaScriptPackageName est basé sur 0, et RemoveGlobalJavaScript réduit la liste à mesure qu'elle avance, ainsi le nettoyage JavaScript lit toujours l'index de paquet 0 et le supprime jusqu'à ce que le nombre atteigne zéro. DeleteAnnotation, en revanche, est basé sur 1. AnnotationCount effectue son rapport par rapport à la page actuellement sélectionnée, ainsi les nettoyages d'annotations sélectionnent chaque page l'une après l'autre avant de compter. La bibliothèque PDF losLab gère correctement ces limites en interne, vous permettant de transmettre un ensemble de drapeaux et de récupérer un décompte plutôt que de reproduire temps de traitement different d'itération différentes
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;
Le flux de travail complet de caviardage et de désinfection
Associez les deux et la procédure complète devient rapide. La bibliothèque PDF losLab supprime le contenu visible avec RedactRegion, page par page, puis efface chaque canal caché avec SanitizeDocument(SANITIZE_ALL) avant l'écriture du fichier. Puisque le caviardage s'applique presque toujours à des documents provenant de tiers hors de votre contrôle, il convient de les charger via un chemin sécurisé — la même posture défensive décrite dans l'analyse sécurisée des fichiers PDF non fiables — afin qu'une entrée malformée échoue proprement plutôt qu'au milieu de la phase de caviardage
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;
Où s'arrête le caviardage au niveau du flux de contenu
RedactRegion opère sur le flux de contenu, là où réside le texte d'origine numérique et là où le texte numérisé est absent. Lorsqu'une page est une image numérisée, les mots sont des pixels au sein d'un XObject d'image, et non des opérateurs de texte, et la bibliothèque PDF losLab ne peut pas extraire un nom du milieu d'une trame comme elle supprime un glyphe. Sa gestion des images est délibérément prudente : toute image dont le placement intersecte le rectangle est supprimée entièrement, de sorte qu'une zone de caviardage sur une page numérisée emporte toute l'image numérisée plutôt qu'une simple partie. Caviarder une région à l'intérieur d'une image implique de pixeliser, de peindre et de réintégrer l'image au niveau des pixels, ce qui correspond à une opération distincte de la suppression dans le flux de contenu. La même prudence s'applique à une couche de texte OCR sur une page numérisée : RedactRegion supprime les opérateurs de texte qu'il détecte, mais l'image sous-jacente reste inchangée à moins que son XObject d'image n'intersecte également la zone
Deux subtilités garantissent la fiabilité des résultats. Le test d'intersection estime la largeur de chaque bloc de texte à partir de son nombre de caractères et de la taille de la police plutôt que de mesurer les dimensions exactes des glyphes. Ainsi, un mot très court dans une police proportionnelle peut échapper à un rectangle tracé de manière trop serrée — accordez une petite marge à la zone. La suppression s'effectue également au niveau des instructions : RedactRegion supprime des opérateurs d'affichage de texte entiers, de sorte qu'un rectangle qui ne coupe qu'une partie d'un bloc peut emporter des caractères adjacents qui partageaient le même opérateur. Aucune de ces limites ne doit inciter à se méfier du mécanisme, il convient simplement de délimiter soigneusement le rectangle et de vérifier le résultat
La rigueur qui conclut tout flux de travail de caviardage est la vérification, non la confiance. Caviardez la région, désinfectez le document, enregistrez dans un nouveau fichier, rouvrez-le et tentez de récupérer le texte supprimé ainsi que les métadonnées nettoyées ; lorsque les deux reviennent vides, le caviardage a fonctionné. Les API RedactRegion et SanitizeDocument présentées ici sont fournies avec la bibliothèque PDF losLab pour Delphi et C++Builder, accompagnées de la référence complète du modèle de contenu et de la désinfection