A valódi PDF-redakció (szövegeltakarás/anonimizálás) Delphiben a háttérben lévő bájtok törlését jelenti, nem pedig azok lefestését. A losLab PDF Library két API-val húzza meg ezt a határvonalat: a RedactRegion szerkeszti az oldal tartalomfolyamát (content stream), így az eltávolított szövegek, vektorok és képek fizikailag eltűnnek, a SanitizeDocument pedig megtisztítja a metaadatokat, szkripteket és műveleteket, ahol az eltakart információk szintén rejtőzhetnek. Ha bármelyik félnél hibázik, olyan fájlt küld el, amely a képernyőn cenzúrázottnak tűnik, de visszaadja a titkot bárkinek, aki kimásolja a szöveget vagy megnyitja a Dokumentum tulajdonságait
A hibás működés módja köztudott. Újságok, bíróságok és kormányzati szervek mind adták már ki olyan PDF-eket, amelyekben fekete téglalap takarta a neveket vagy számokat, de az olvasók egyszerűen kijelöltek az alatta lévő szöveget, kimásolták azt, és beillesztették a feltételezhetően eltakart karakterláncot. A téglalap csupán egy rajz volt. A szöveg ott maradt a tartalomfolyamban egy réteggel a festék alatt, teljesen épen és kijelölhetően. A losLab PDF Library, a TPDFlib osztályon keresztül elérhető Delphi és C++Builder PDF-motor, ezt a különbséget a redakció lényegeként kezeli, nem pedig utólagos gondolatként
Miért nem valódi redakció egy fekete doboz rajzolása a PDF szövege fölé?
A losLab PDF Library elérhetővé teszi a MaskRect metódust azokra az esetekre, amikor a lefedés a valódi cél: vízjel, vizuális takarás, jóváhagyási bélyegző. A MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) hozzáfűz egy téglalapot az oldal tartalomfolyamához, és lefesti azt a re útvonal-konstrukciós operátorral, amelyet az f kitöltési operátor követ (ISO 32000-1 §8.5). Minden bájt, ami már az oldalon volt – a lefedett név glifái, a vektoros vonalak, a beágyazott kép – pontosan ott marad, ahol volt, egy utasítással korábban ugyanabban a folyamban. Egy szabványkövető megjelenítő a fekete dobozt rajzolja ki utoljára, így az vizuálisan győz. A lefedés semmit sem változtat meg alatta
Bárki visszanyerheti az eredeti tartalmat háromféleképpen: kijelöli és kimásolja a szöveget közvetlenül az átlátszatlan dobozon keresztül, szövegkivonót futtat az oldalon, vagy törli a lefedő téglalapot a tartalomfolyamból. A MaskRect őszinte ezzel kapcsolatban a saját nevében is – maszkol, nem pedig eltávolít. Az eredmény egy matrica egy szón, amelyet továbbra is elolvashat a matrica felhajtásával
Hogyan töröl tartalmat a RedactRegion az utasítások szintjén
A RedactRegion(Page, Left, Top, Width, Height, Options) egy réteggel a festék alatt dolgozik. A losLab PDF Library az oldal minden tartalomrétegét egy TPDFContentProgram-ba elemzi, végigmegy az utasításlistán egy CTM-követő elemzővel (FindInstructionsInRect a tartalommodellben), és megjelöl minden szövegmegjelenítő, útvonalfestő és Do képoperátort, amelynek festett területe metszi a téglalapját. Ezek az utasítások a TPDFContentProgram.Delete segítségével törlődnek, és a szerkesztett réteg a WritePageContentLayer segítségével íródik vissza. A bájtok nem csúsznak el egy takaró mögé – elhagyják a folyamot. A később futtatott szövegkivonó semmit sem talál, mert nincs mit találni
Két megvalósítási részletet érdemes észben tartani. A RedactRegion egy bal felső sarkú téglalapot vesz fel a jelenlegi kezdőpontban és mértékegységekben, és belsőleg átalakítja a tartalomfolyam által használt bal alsó felhasználói térré, így Ön úgy határozza meg a tartományt, ahogyan azt az oldalon látja. Ha egy téglalap több utasítást is lefed, a RedactRegion csökkenő indexsorrendben törli őket, mivel a 4. utasítás eltávolítása a 7. utasítás előtt eltolná az összes későbbi indexet, és elrontaná a tartományt. Passzoljon Options = 0 értéket, és a losLab PDF Library egy tömör fekete dobozt is bélyegez a törölt tartomány fölé vizuális jelzőként – de ez a jelző csupán kozmetikai jellegű, azután kerül alkalmazásra, hogy a valódi tartalom már eltűnt
A gyakorlatban ritkán kódolja fixen a téglalapot. Megkeresi az oldalon a kényes karakterláncot, és beolvassa annak befoglaló keretét (bounding box) – ugyanaz az oldalkeresés és elemfelsorolás, amelyet a szöveg és oldalelemek keresése Delphiben című cikk tárgyal –, majd átadja azt a téglalapot a RedactRegion metódusnak:
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;
Hol bújik meg még eltakart információ a PDF-ben?
A csak a látható rétegnél megálló redakció nyomot hagy maga után, mivel a PDF olyan helyeken is tárol információkat, ahová az olvasó soha nem néz. A losLab PDF Library megnevezésre érdemes csatornákba csoportosítja a szivárgásokokat. A Document Information Dictionary és az XMP metaadat-folyam rutinszerűen hordoz Author, Producer vagy Keywords karakterláncokat, amelyek pontosan azt a személyt vagy ügyet nevezik meg, amelyet Ön eltávolított a törzsből. A dokumentumszintű JavaScript és a katalógus OpenAction végrehajtható megnyitáskor, és elérhet olyan adatokat, amelyekről azt hitte, hogy már eltűntek. Minden megjegyzés tartalmazhat egy opcionális /A műveletet, és maga a megjegyzés – egy tapadós jegyzet, egy régi űrlapmező, egy hivatkozás – közvetlenül az eltakart helyen ülhet a szöveg saját másolatával. Ebből semmit sem érint a titkosítás; egy titkosított fájl sima szöveges XMP csomaggal továbbra is kiszivárogtatja a címét a keresőknek, ami egy olyan rés, amely önmagában is megérdemli a titkosítás és jogosultságok auditálását. A védhető szanálásnak ezen rejtekhelyek mindegyikét meg kell látogatnia, nem csak azt az oldalt, amelyet éppen nézett
A dokumentum megtisztítása a SanitizeDocument segítségével
A losLab PDF Library a szivárgások ezen skálájára a SanitizeDocument(Flags) metódussal válaszol, ami egy olyan vezérlőhívás, amely egy bitmaszk alapján kiválasztott független tisztítási folyamatokat futtat le, és visszaadja az alkalmazott lépések számát. A jelzők kombinálhatók. A SANITIZE_JAVASCRIPT eltávolít minden dokumentumszintű JavaScript csomagot és OpenAction szkriptet. A SANITIZE_ACTIONS eltávolítja a /A műveleti szótárt minden megjegyzésből az új TPDFAnnots.RemoveAnnotAction segítségével, és törli a katalógus OpenAction-t. A SANITIZE_METADATA kiüríti az Author, Subject, Keywords, Creator és Producer mezőket mind a Document Information Dictionary (ISO 32000-1 §14.3.3) szótárban, mind az XMP metaadat-folyamban (§14.3.2). A SANITIZE_ANNOTATIONS töröl minden megjegyzést a teljes dokumentumban, a SANITIZE_OPEN_ACTION pedig eltávolítja a katalógus /OpenAction bejegyzését. A SANITIZE_ALL mind az öt lépés bitenkénti uniója
Az ezeken a tisztítási folyamatokon belüli indexelési konvenciók pontosan azok a dolgok, amelyek csendben megtörik a kézzel írt ciklusokat, és ez a fő oka annak, hogy a SanitizeDocument egyetlen hívásként létezik. A GlobalJavaScriptPackageName 0-alapú, és a RemoveGlobalJavaScript zsugorítja a listát haladás közben, így a JavaScript tisztítás mindig a 0. indexű csomagot olvassa, és addig távolítja el, amíg a szám el nem éri a nullát. Ezzel szemben a DeleteAnnotation 1-alapú. Az AnnotationCount a jelenleg kiválasztott oldalra vonatkozik, így a megjegyzés-eltávolító sávok sorban kiválasztják az egyes oldalakat a számlálás előtt. A losLab PDF Library belsőleg helyesen kezeli ezeket a határokat, így Ön egyetlen jelzőkészletet ad át, és egyetlen számlálót olvas vissza, ahelyett, hogy öt különböző bejárási szabályt valósítana meg
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;
A teljes redakciós és szanálási munkafolyamat
Tegye össze a kettőt, és a teljes eljárás rövid lesz. A losLab PDF Library oldalról oldalra eltávolítja a látható tartalmat a RedactRegion segítségével, majd a fájl írása előtt tisztít minden rejtett csatornát a SanitizeDocument(SANITIZE_ALL) segítségével. Mivel a redakció szinte mindig olyan dokumentumokon fut, amelyek az Ön ellenőrzésén kívülről érkeztek, érdemes őket egy megerősített útvonalon keresztül betölteni – ugyanaz a védelmi felállás, amelyet a megbízhatatlan PDF-ek biztonságos elemzéséről szóló cikk ír le –, hogy a hibás bemenet tisztán hibával leálljon ahelyett, hogy a redakciós menet közepén omlana össze
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;
Ahol a tartalomfolyam-redakció megáll
A RedactRegion a tartalomfolyamon dolgozik, ami pontosan az a hely, ahol a digitálisan létrehozott szövegek élnek, és pontosan az, ahol a beszkennelt szövegek nem. Ha egy oldal egy szkennelt kép, a szavak képpontok (pixelek) egy kép XObject-en belül, nem pedig szöveges operátorok, és a losLab PDF Library nem tud kivágni egy nevet a raszter közepéből úgy, ahogyan egy glifet töröl. Képkezelése szándékosan konzervatív: minden olyan képet teljes egészében eltávolít, amelynek elhelyezkedése metszi a téglalapot, így egy szkennelt oldalon lévő redakciós doboz a teljes szkennelt képet magával viszi, nem pedig annak egy részét. Egy kép belsejében lévő terület eltakarása raszterezést, festést és képpontszintű újra-beágyazást igényel, ami más művelet, mint a tartalomfolyam-alapú törlés. Ugyanez az óvatosság vonatkozik a szkennelt oldal feletti OCR szövegrétegre is: a RedactRegion eltávolítja a látható szöveges operátorokat, de az alatta lévő kép érintetlen marad, hacsak a kép XObject-je szintén nem metszi a dobozt
Két finomabb részlet tartja tisztán az eredményeket. A metszéspont-teszt megbecsüli az egyes szövegfutások szélességét a karakterszámukból és a betűméretükből ahelyett, hogy pontos glifmetrikát mérne, így egy arányos betűtípussal szedett, nagyon rövid szó átcsúszhat egy szorosan meghúzott téglalapon – adjon a doboznak egy kis margót. A törlés szintén utasítás-szintű: a RedactRegion teljes szövegmegjelenítő operátorokat távolít el, így egy olyan téglalap, amely egy futásnak csak egy részét vágja le, magával viheti a szomszédos karaktereket is, amelyek ugyanazon az operátoron osztoztak vele. Egyik korlát sem ad okot a mechanizmus iránti bizalmatlanságra, csupán okot ad arra, hogy a téglalapot gondosan határoljuk be, és ellenőrizzük az eredményt
A fegyelem, amely minden redakciós munkafolyamatot lezár, az ellenőrzés, nem pedig a bizalom. Takarja le a tartományt, szanálja a dokumentumot, mentse új fájlba, nyissa meg újra, és próbálja meg visszanyerni mind az eltávolított szöveget, mind a kiürített metaadatokat; ha mindkettő üresen tér vissza, a redakció sikeres volt. Az itt bemutatott RedactRegion és SanitizeDocument API-k a Delphihez és C++Builderhez készült losLab PDF Library-vel együtt érkeznek, a teljes tartalommodell- és szanálási referenciával együtt