Skutečná redakce PDF v Delphi znamená vymazání podkladových bajtů, nikoli jejich přemalování. losLab PDF Library tuto hranici definuje dvěma API: `RedactRegion` upravuje tok obsahu stránky tak, aby odstraněný text, vektory a obrázky byly fyzicky pryč, a `SanitizeDocument` maže metadata, skripty a akce, v nichž se redigované informace rovněž skrývají. Pokud uděláte chybu v kterékoli z těchto částí, odešlete soubor, který na obrazovce sice vypadá cenzurovaně, ale vrátí tajemství komukoli, kdo zkopíruje text nebo otevře vlastnosti dokumentu
Tento způsob selhání je věcí veřejného záznamu. Noviny, soudy a vládní agentury již mnohokrát vydaly PDF s černým obdélníkem položeným přes jméno nebo číslo, aby čtenář následně vybral text pod ním, zkopíroval jej a vložil údajně redigovaný řetězec na jasné světlo. Obdélník byl totiž pouhou kresbou. Text pod ním zůstal v toku obsahu o vrstvu níže, zcela neporušený a plně vybratelný. losLab PDF Library, engine pro Delphi a C++Builder zpřístupněný prostřednictvím třídy `TPDFlib`, považuje toto rozlišení za samotnou podstatu redakce, nikoli za dodatečnou myšlenku
Proč není nakreslení černého obdélníku přes text v PDF skutečnou redakcí?
losLab PDF Library vystavuje `MaskRect` pro případy, kdy je překrytí skutečně tím, co chcete: vodoznak, vizuální zatemnění, zkušební razítko. `MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue)` připojí obdélník k toku obsahu stránky a vykreslí jej pomocí operátoru konstrukce cesty `re` následovaného operátorem výplně `f` (ISO 32000-1 §8.5). Každý bajt, který na stránce již byl — znaky jména, které jste zakryli, vektorové tahy, vložený obrázek —, zůstává přesně tam, kde byl, o jednu instrukci dříve ve stejném toku. Kompatibilní prohlížeč vykreslí černý box jako poslední, takže vizuálně vyhraje. Překrytí pod ním nic nemění
Kdokoli může získat původní obsah třemi způsoby: vybrat a zkopírovat text přímo přes neprůhledný box, spustit nad stránkou extraktor textu nebo vymazat překrývající obdélník z toku obsahu. Metoda `MaskRect` je k tomu upřímná již ve svém názvu — maskuje, neodstraňuje. Výsledkem je nálepka přes slovo, které si stále můžete přečíst jejím odlepením
Jak RedactRegion maže obsah na úrovni instrukcí
`RedactRegion(Page, Left, Top, Width, Height, Options)` pracuje o vrstvu níže než vykreslování barvy. losLab PDF Library analyzuje každou vrstvu obsahu stránky do `TPDFContentProgram`, prochází seznam instrukcí pomocí analyzátoru sledujícího CTM (`FindInstructionsInRect` v modelu obsahu) a označí každý operátor pro zobrazení textu, vykreslení cesty a obrázek `Do`, jehož vykreslená oblast se protíná s vaším obdélníkem. Tyto instrukce jsou odstraněny pomocí `TPDFContentProgram.Delete` a upravená vrstva je zapsána zpět pomocí `WritePageContentLayer`. Bajty se neposouvají za kryt — opouštějí tok. Následně spuštěný extraktor textu nenajde nic, protože není co najít
Dva detaily implementace stojí za to vzít v úvahu. `RedactRegion` přijímá obdélník v aktuálním počátku a jednotkách (vlevo nahoře) a interně jej převádí do uživatelského prostoru (vlevo dole), který tok obsahu používá, takže oblast zadáváte tak, jak ji vidíte na stránce. Pokud obdélník pokrývá několik instrukcí, `RedactRegion` je maže v sestupném pořadí indexů, protože odstranění instrukce 4 před instrukcí 7 by posunulo každý další index a poškodilo rozsah. Pokud předáte `Options = 0`, losLab PDF Library také vykreslí plný černý obdélník přes vymazanou oblast jako viditelnou značku — tato značka je však kosmetická, aplikovaná až poté, co je skutečný obsah již pryč
V praxi obdélník málokdy kódujete napevno. Vyhledáte na stránce citlivý řetězec a přečtete jeho ohraničující rámeček (bounding box) — stejné vyhledávání na stránce a enumerace prvků, o kterých pojednává poznámka o vyhledávání textu a prvků stránek v Delphi — a poté tento obdélník předáte metodě `RedactRegion`:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', '') = 1 then
begin
// Vymazat vše vykreslené uvnitř pole 220 x 14 pt na stránce 1
// Souřadnice jsou vlevo nahoře, v aktuálním počátku a jednotkách
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // Options=0 vykreslí černou značku
PDF.SaveToFile('contract-redacted.pdf');
end;
finally
PDF.Free;
end;
end;
Kde se redigované informace v PDF stále skrývají?
Redakce, která se zastaví na viditelné vrstvě, zanechává stopu, protože PDF ukládá informace na místech, kam se čtenář nikdy nedívá. losLab PDF Library seskupuje tyto úniky do kanálů, které stojí za to vyjmenovat. Slovník s informacemi o dokumentu (Document Information Dictionary) a tok metadat XMP běžně nesou řetězce jako Author, Producer nebo Keywords, které uvádějí právě tu osobu nebo případ, které jste z těla odstranili. JavaScript na úrovni dokumentu a katalogová akce OpenAction se mohou spustit při otevření a dostat se k datům, o kterých jste si mysleli, že jsou pryč. Každá anotace nese volitelnou akci /A a anotace samotná — rychlá poznámka, staré formulářové pole, odkaz — může ležet přímo nad redigovaným místem se svou vlastní kopií textu. Ničeho z toho se šifrování nedotkne; šifrovaný soubor s prostým XMP balíčkem stále prozradí svůj název jakémukoli indexátoru, což je mezera, která sama o sobě stojí za audit šifrování a oprávnění. Obhajitelná sanitace musí navštívit každé z těchto úkrytů, nejen stránku, na kterou se díváte
Čištění dokumentu pomocí SanitizeDocument
losLab PDF Library reaguje na toto spektrum úniků metodou `SanitizeDocument(Flags)`, což je orchestrační volání, které spouští sadu nezávislých čištění vybraných bitovou maskou a vrací počet aplikovaných čištění. Příznaky (flags) lze kombinovat. `SANITIZE_JAVASCRIPT` odstraní každý balíček JavaScriptu na úrovni dokumentu a jakýkoli skript OpenAction. `SANITIZE_ACTIONS` odstraní slovník akcí /A z každé anotace prostřednictvím nové metody `TPDFAnnots.RemoveAnnotAction` a vymaže katalogovou akci OpenAction. `SANITIZE_METADATA` vymaže hodnoty Author, Subject, Keywords, Creator a Producer jak ve slovníku informací o dokumentu (ISO 32000-1 §14.3.3), tak v toku metadat XMP (§14.3.2). `SANITIZE_ANNOTATIONS` vymaže všechny anotace v celém dokumentu a `SANITIZE_OPEN_ACTION` odstraní katalogovou položku /OpenAction. `SANITIZE_ALL` je bitovým sjednocením všech pěti
Konvence indexování uvnitř těchto operací jsou přesně tím, co potichu naruší ručně psanou smyčku, což je hlavní důvod, proč `SanitizeDocument` existuje jako jediné volání. `GlobalJavaScriptPackageName` je indexován od 0 a `RemoveGlobalJavaScript` seznam za běhu zmenšuje, takže skript pro JavaScript vždy čte index balíčku 0 a odstraňuje jej, dokud počet nedosáhne nuly. `DeleteAnnotation` je naproti tomu indexován od 1. `AnnotationCount` se vztahuje k aktuálně vybrané stránce, takže skript pro anotace před počítáním vybere postupně každou stránku. losLab PDF Library tyto hranice interně ošetřuje správně, takže předáváte jednu sadu příznaků a čtete jeden výsledek, místo abyste museli reprodukovat pět různých pravidel iterace
var
Applied: Integer;
begin
// Odstranit metadata, skripty a akce, ale zachovat anotace:
Applied := PDF.SanitizeDocument(SANITIZE_METADATA or SANITIZE_JAVASCRIPT or
SANITIZE_ACTIONS or SANITIZE_OPEN_ACTION);
// Nebo vymazat každý skrytý kanál, včetně anotací:
Applied := PDF.SanitizeDocument(SANITIZE_ALL);
end;
Kompletní pracovní postup redakce a sanitace
Spojte tyto dvě operace a výsledný postup je krátký. losLab PDF Library odstraní viditelný obsah stránku po stránce pomocí `RedactRegion` a před zápisem souboru vyčistí každý skrytý kanál pomocí `SanitizeDocument(SANITIZE_ALL)`. Vzhledem k tomu, že redakce téměř vždy běží na dokumentech, které dorazily mimo vaši kontrolu, stojí za to je načítat přes zabezpečenou cestu — stejný obranný postoj, jaký je popsán v bezpečném zpracování nedůvěryhodných PDF —, aby poškozený vstup selhal čistě, a nikoli uprostřed redakčního průchodu
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); // vymazat citlivý řádek
Applied := PDF.SanitizeDocument(SANITIZE_ALL); // vymazat metadata, JS, akce, anotace
PDF.SaveToFile('published.pdf');
end;
finally
PDF.Free;
end;
end;
Kde redakce toku obsahu končí
`RedactRegion` pracuje na toku obsahu, což je přesně místo, kde žije digitálně vytvořený text a kde naopak naskenovaný text nežije. Pokud je stránka naskenovaným obrázkem, slova jsou pixely uvnitř objektu XObject obrázku, nikoli textovými operátory, a losLab PDF Library nemůže vyříznout jméno z prostředku rastru tak, jako maže znak. Její manipulace s obrázky je záměrně konzervativní: jakýkoli obrázek, jehož umístění protíná obdélník, je odstraněn celý, takže redakční box na naskenované stránce vezme s sebou celý sken, a nikoli pouze jeho část. Redigování oblasti uvnitř obrázku znamená rastrování, překreslení a opětovné vložení na úrovni pixelů, což je jiná operace než vymazání toku obsahu. Stejná opatrnost platí pro textovou vrstvu OCR nad naskenovanou stránkou: `RedactRegion` odstraní textové operátory, které vidí, ale obrázek pod nimi zůstane nedotčen, pokud se jeho objekt XObject obrázku rovněž neprotíná s obdélníkem
Dva jemnější body zajišťují poctivost výsledků. Test průniku odhaduje šířku každého textového bloku na základě počtu znaků a velikosti písma, namísto měření přesné metriky znaků, takže velmi krátké slovo v proporcionálním písmu může těsně nakresleným obdélníkem proklouznout — dejte boxu malou rezervu. Odstranění je také instrukčně granulární: `RedactRegion` odstraňuje celé operátory zobrazující text, takže obdélník, který ořízne pouze část bloku, může vzít s sebou i sousední znaky, které s ním sdílely stejný operátor. Ani jedno omezení není důvodem k nedůvěře v mechanismus, pouze důvodem k pečlivému ohraničení obdélníku a kontrole výsledku
Disciplína, která uzavírá každý redakční postup, je ověření, nikoli důvěra. Redigujte oblast, sanujte dokument, uložte do nového souboru, znovu jej otevřete a pokuste se získat zpět text, který jste odstranili, i metadata, která jste vyčistili; když se obojí vrátí prázdné, redakce se podařila. Zde popsaná API `RedactRegion` a `SanitizeDocument` se dodávají s knihovnou losLab PDF Library pro Delphi a C++Builder, spolu s kompletní referenční příručkou pro model obsahu a sanitaci