Echte PDF-redactie in Delphi betekent het verwijderen van de onderliggende bytes, niet het overschilderen ervan. losLab PDF Library trekt die grens met twee API's: RedactRegion bewerkt de paginacontentstream zodat de verwijderde tekst, vectoren en afbeeldingen fysiek verdwenen zijn, en SanitizeDocument wist de metadata, scripts en acties waarin geredigeerde informatie zich ook kan verbergen. Als u een van beide helften verkeerd aanpakt, verzendt u een bestand dat er op het scherm gecensureerd uitziet, maar het geheim teruggeeft aan iedereen die de tekst kopieert of Documenteigenschappen opent
Deze fout is een bekend fenomeen. Kranten, rechtbanken en overheidsinstanties hebben allemaal PDF's vrijgegeven met een zwarte rechthoek over een naam of nummer, waarna een lezer simpelweg de onderliggende tekst kon selecteren, kopiëren en de zogenaamd geredigeerde tekst in het zicht kon plakken. De rechthoek was slechts een tekening. De tekst was er nog steeds, aanwezig in de contentstream één laag onder de verf, volledig intact en selecteerbaar. losLab PDF Library, de Delphi- en C++Builder-PDF-engine die beschikbaar is via de klasse TPDFlib, behandelt dit onderscheid als de essentie van redactie in plaats van als een bijzaak
Waarom is het tekenen van een zwart kader over PDF-tekst geen echte redactie?
losLab PDF Library biedt MaskRect voor gevallen waarin afdekken echt is wat u wilt: een watermerk, een visuele blackout, een proefstempel. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) voegt een rechthoek toe aan de paginacontentstream en tekent deze met de padconstructie-operator re, gevolgd door de vul-operator f (ISO 32000-1 §8.5). Elke byte die al op de pagina stond — de glyphs van the naam die u hebt afgedekt, de vectorlijnen, de ingebedde afbeelding — blijft precies waar deze was, één instructie eerder in dezelfde stream. Een compatibele viewer tekent de zwarte doos als laatste, waardoor deze visueel wint. De afdekking verandert niets aan de onderliggende inhoud
Iedereen kan de oorspronkelijke inhoud op drie manieren terughalen: selecteer en kopieer de tekst rechtstreeks door het ondoorzichtige kader, voer een tekstextractor uit over de pagina, of verwijder de afdekkende rechthoek uit de contentstream. MaskRect is hier eerlijk over in zijn eigen naam — het maskeert, het verwijdert niet. Het resultaat is een sticker over een woord dat u nog steeds kunt lezen door de sticker eraf te trekken
Hoe RedactRegion inhoud verwijdert op instructieniveau
RedactRegion(Page, Left, Top, Width, Height, Options) werkt één laag onder de verf. losLab PDF Library parseert elke inhoudslaag van de pagina in een TPDFContentProgram, doorloopt de instructielijst met een CTM-tracking-analyzer (FindInstructionsInRect in het inhoudsmodel) en markeert elke tekstweergave-, padteken- en afbeelding-Do-operator waarvan het getekende gebied uw rechthoek kruist. Die instructies worden verwijderd met TPDFContentProgram.Delete, en de bewerkte laag wordt teruggeschreven met WritePageContentLayer. De bytes schuiven niet achter een afdekking — ze verlaten de stream. Een tekstextractor die daarna wordt uitgevoerd, vindt niets, omdat er niets te vinden is
Twee implementatiedetails zijn de moeite waard om in uw achterhoofd te houden. RedactRegion neemt een rechthoek linksboven in de huidige oorsprong en eenheden en converteert deze intern naar de gebruikersruimte linksonder die de contentstream gebruikt, zodat u de regio specificeert zoals u deze op de pagina ziet. Wanneer een rechthoek meerdere instructies beslaat, verwijdert RedactRegion deze in aflopende indexvolgorde, omdat het verwijderen van instructie 4 vóór instructie 7 elke latere index zou verschuiven en het bereik zou corrumperen. Geef Options = 0 door en losLab PDF Library stempelt ook een effen zwart kader over het gewiste gebied als een visuele markering — maar die markering is cosmetisch en wordt pas toegepast nadat de echte inhoud al verdwenen is
In de praktijk codeert u de rechthoek zelden hard. U zoekt op de pagina naar de gevoelige tekst en leest de bounding box ervan uit — dezelfde paginazoekactie en elementenopsomming die worden behandeld in tekst en pagina-elementen lokaliseren in Delphi — en geeft die rechthoek vervolgens door aan 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;
Waar verbergt geredigeerde informatie zich nog steeds in een PDF?
Redactie die stopt bij de zichtbare laag laat sporen achter, omdat een PDF informatie opslaat op plaatsen waar een lezer nooit kijkt. losLab PDF Library groepeert de lekken in kanalen die de moeite waard zijn om te noemen. Het Document Information Dictionary en de XMP-metadatastream bevatten routinematig een Author-, Producer- of Keywords-string die de persoon of zaak noemt die u uit de hoofdtekst hebt verwijderd. JavaScript op documentniveau en de catalogus-OpenAction kunnen bij het openen worden uitgevoerd en gegevens bereiken waarvan u dacht dat ze weg waren. Elke annotatie bevat een optionele /A-actie, en een annotatie zelf — een notitie, een verouderd formulierveld, een link — kan direct over de geredigeerde plek liggen met zijn eigen kopie van de tekst. Niets hiervan wordt beïnvloed door encryptie; een gecodeerd bestand met een plaintext XMP-pakket lekt nog steeds zijn titel aan elke indexer, een hiaat dat het waard is om encryptie en machtigingen te auditeren. Defensieve opschoning moet elk van deze schuilplaatsen bezoeken, niet alleen de pagina waarnaar u keek
Het document reinigen met SanitizeDocument
losLab PDF Library beantwoordt die verspreiding van lekken met SanitizeDocument(Flags), een orchestratie-aanroep die een set onafhankelijke strips uitvoert die zijn geselecteerd door een bitmasker en het aantal toegepaste strips retourneert. De vlaggen zijn combineerbaar. SANITIZE_JAVASCRIPT verwijdert elk JavaScript-pakket op documentniveau en elk OpenAction-script. SANITIZE_ACTIONS verwijdert het /A-actiedictionary uit elke annotatie, via het nieuwe TPDFAnnots.RemoveAnnotAction, en wist de catalogus-OpenAction. SANITIZE_METADATA wist Auteur, Onderwerp, Trefwoorden, Maker en Producent in zowel het Document Information Dictionary (ISO 32000-1 §14.3.3) als de XMP-metadatastream (§14.3.2). SANITIZE_ANNOTATIONS verwijdert elke annotatie in het hele document, en SANITIZE_OPEN_ACTION verwijdert het catalogus-/OpenAction-item. SANITIZE_ALL is de bitsgewijze unie van alle vijf
De indexconventies binnen deze strips zijn precies het soort dingen dat stilletjes een handmatig geschreven lus breekt, wat de belangrijkste reden is waarom SanitizeDocument als één aanroep bestaat. GlobalJavaScriptPackageName is 0-gebaseerd, en RemoveGlobalJavaScript verkleint de lijst terwijl deze wordt doorlopen, dus de JavaScript-strip leest altijd package-index 0 en verwijdert totdat de teller nul bereikt. DeleteAnnotation is daarentegen 1-gebaseerd. AnnotationCount rapporteert over de momenteel geselecteerde pagina, dus de annotatiestrips selecteren achtereenvolgens elke pagina voordat ze tellen. losLab PDF Library regelt deze grenzen intern correct, zodat u één set vlaggen doorgeeft en één aantal terugleest in plaats van het reproduceren van vijf verschillende iteratieregels
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;
De volledige redactie- en opschoningswerkstroom
Voeg de twee samen en de volledige procedure is kort. losLab PDF Library verwijdert de zichtbare inhoud met RedactRegion, pagina voor pagina, en wist vervolgens elk verborgen kanaal met SanitizeDocument(SANITIZE_ALL) voordat het bestand wordt geschreven. Omdat redactie bijna altijd wordt uitgevoerd op documenten die buiten uw controle zijn binnengekomen, is het de moeite waard om ze via een beveiligd pad te laden — dezelfde defensieve houding die wordt beschreven in onvertrouwde PDF's veilig parseren — zodat een misvormde invoer clean faalt in plaats van halverwege de redactiegang
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;
Waar contentstream-redactie ophoudt
RedactRegion opereert op de contentstream, wat precies is waar born-digital tekst leeft en exact waar gescande tekst dat niet doet. Wanneer een pagina een gescande afbeelding is, de woorden pixels binnen een XObject-afbeelding, geen tekstoperatoren, en kan losLab PDF Library geen naam uit het midden van een raster snijden zoals het een glyph verwijdert. De beeldverwerking is bewust conservatief: elke afbeelding waarvan de plaatsing de rechthoek kruist, wordt in zijn geheel verwijderd, dus een redactiekader op een gescande pagina neemt de hele scan mee in plaats van een deel ervan. Het redigeren van een regio binnen een afbeelding betekent rasteren, schilderen en opnieuw inbedden op pixelverwerkingsniveau, wat een andere bewerking is dan verwijdering uit de contentstream. Dezelfde voorzichtigheid geldt voor een OCR-tekstlaag over een gescande pagina: RedactRegion verwijdert de tekstoperatoren die het kan zien, maar de onderliggende afbeelding blijft onaangetast, tenzij het XObject van de afbeelding ook de doos kruist
Twee fijnere punten houden de resultaten eerlijk. De kruisingstest schat de breedte van elke tekstreeks op basis van het aantal tekens en de lettergrootte in plaats van het meten van exacte glyph-metrieken, waardoor een zeer kort woord in een proportioneel lettertype langs een strak getekende rechthoek kan glippen — geef het kader een kleine marge. Verwijdering is ook instructie-granulair: RedactRegion verwijdert hele tekst-weergave-operatoren, dus een rechthoek die slechts een deel van een reeks aansnijdt, kan aangrenzende tekens meenemen die dezelfde operator deelden. Geen van beide limieten is een reden om het mechanisme te wantrouwen, alleen een reden om de rechthoek zorgvuldig af te bakenen en het resultaat te controleren
De discipline waarmee elke redactiewerkstroom wordt afgesloten, is verificatie, niet vertrouwen. Redigeer het gebied, schoont het document op, sla het op in een nieuw bestand, open het opnieuw en probeer zowel de tekst die u hebt verwijderd als de metadata die u hebt gewist terug te halen; wanneer beide leeg terugkomen, heeft de redactie standgehouden. De hier getoonde RedactRegion- en SanitizeDocument-API's worden geleverd met de losLab PDF Library voor Delphi en C++Builder, samen met het volledige inhoudsmodel en de opschoningsreferentie