Neko nacrta crni kvadrat preko imena, ništa ne spljošti, pošalje fajl, i recenzent selektuje pravougaonik i nalepi ime u mejl. PDFiumPas odgovara na to redakcijom na nivou operatora: SaveAsRedacted briše samo Unicode skalarne čije su kutije znakova dotakle pravougaonik redakcije, ponovo gradi preživele iz originalnog fonta, veličine, matrice, render režima i boje, i iseče ose poravnate putanje i slike umesto da ih ispusti cele
Zašto naslikani pravougaonik nije redakcija
Operacija crtanja dodata preko toka sadržaja ne krije ništa, jer operatori koji prikazuju tekst ispod nje su i dalje u toku i dalje se mapiraju na kodne tačke. ISO 32000-1 §9.4 definiše tekst objekt kao niz operatora pozicioniranja i prikazivanja unutar BT i ET; pun pravougaonik nacrtan posle je jednostavno još jedan operator u istom toku. Izvlačenje šeta operatore, ne piksele, pa pokriveni string dolazi nazad netaknut. Prava redakcija mora ukloniti operand, ne zamračiti izlaz
Očigledna bezbedna implementacija je brutalna: nađi svaki objekat stranice čiji bounding box seče pravougaonik redakcije i obriši ceo objekat. To su ranija PDFiumPas izdanja radila, i ispravno je ali skupo. Jedan Tj može nositi ceo red tabele, pa zamračivanje jednog broja računa odnelo je datum, opis i iznos sa sobom. Pravougaonsko punjenje koje slučajno jeste punoširina traka tabele nestalo je preko cele stranice. Logo fakture nestao je jer je redakcija odsekla jedan njegov ugao. Verzija 3.101.0 pomera odluku jedan nivo niže, sa objekta stranice na operand
Šta redakcija na nivou operatora stvarno briše?
PDFiumPas briše Unicode skalarne, ne tekst objekte. Tokom SaveAsRedacted komponenta gradi preslikavanje znak-u-objekat-stranice iz učitane tekstualne stranice, pa za svaki znak u vlasništvu objekta pod testom čita kutiju znaka i seče tu kutiju protiv svakog pravougaonika redakcije. Znaci koji dotaknu pravougaonik se označavaju za uklanjanje; ostali se označavaju kao preživeli. Ako se ništa ne seče, objekat se potpuno ostavlja na miru. Ako se svaki znak seče, objekat se uklanja ceo, baš kao pre. Samo mešani slučaj okida razdvajanje
Svaki preživeli se zatim ponovo emituje kao svoj tekst objekt izgrađen iz originalnog rukovanca fonta, originalne veličine fonta, tekst matrice po znaku, originalnog režima renderovanja teksta, i fill i stroke stanja roditeljskog objekta uključujući širinu poteza, spajanje linija, kraj linije i niz crtica. Ponovna upotreba rukovanca fonta umesto rešavanja novog je ono što činke metrički identičnima, i ponovna upotreba matrice po znaku je ono što drži kerning i razmak rečeva na mestu bez ponovnog pokretanja rasporeda. Cena je broj objekata: jedan zadržani znak postaje jedan tekst objekt, po čemu TPdfRedactionOptions.MaxSplitObjects postoji kao kruta granica na generisane fragmente
procedure RedactDocument(const SourcePdf, TargetPdf: string);
var
Pdf: TPdf;
Options: TPdfRedactionOptions;
Report: TPdfRedactionReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := SourcePdf; // fajl već nosi /Redact anotacije
Pdf.Active := True;
Options := TPdfRedactionOptions.Default;
Options.PreservePartialObjects := True; // razdvajanje na nivou operatora (podrazumevano)
Options.RemoveIntersectingAnnotations := True;
Options.MaxSplitObjects := 20000; // granica na generisane fragmente
if not Pdf.SaveAsRedacted(TargetPdf, Options, Report) then
raise Exception.Create(Report.ErrorMessage); // padni zatvoreno, nemoj isporučiti
finally
Pdf.Free;
end;
end;
Pravougaonici se iseču, rotirana geometrija ne
Putanje se razdvajaju tek kada PDFiumPas može dokazati da je putanja ose poravnat pravougaonik. Dokaz je namerno uzan: matrica objekta mora imati oba smična člana ispod 0.0001, putanja se mora sastojati od četiri do šest segmenata koja počinju MOVETO i nastavljaju samo LINETO, i transformisane tačke moraju sleteti na sva četiri ugla granica objekta unutar tolerancije 0.01. Putanja koja prođe tu proveru se smanjuje uzastopnim oduzimanjem pravougaonika, svaki pravougaonik redakcije reže skup preživelih u leve, desne, donje i gornje trake, i svaka nastala traka se ponovo stvara sa originalnim režimom punjenja, stroke flagom i stanjem bojanja. Krive, trouglovi, iseci oblici i išta rotirano pada tu proveru i ceo objekat se uklanja
Slike slede ISO 32000-1 §8.9, gde uzorci slike zauzimaju jedinični kvadrat preslikan kroz trenutnu matricu transformacije. PDFiumPas obrće to preslikavanje da bi svaki preživeli fragment u prostoru stranice vratio u normalizovane koordinate slike, sabija ih u jedinični interval, pa pretvara u piksel indekse zaokruživanjem unutra: leve i gornje ivice idu kroz Ceil, desne i donje kroz Floor. Taj smer biti. Zaokruživanje napolje bi dozvolilo parcijalnoj koloni izvornih piksela sa redakcione strane da preživi na ivici fragmenta. Celi broj piksel granice se zatim vraćaju u normalizovane koordinate i koriste za izvođenje matrice fragmenta, pa isečena bitmapa sleće baš na piksel granicu na kojoj je odsečena. Samo iseckanje je kopija redova svesna koraka preko Gray, BGR, BGRx i BGRA formata. Kao kod putanja, rotirana ili nakošena slika, ili jedna čija matrica ima degenerisan član razmere, uklanja se u celosti
// Posle uspešnog SaveAsRedacted poziva
Writeln(Format('applied %d redaction(s) on %d page(s)',
[Report.RedactionCount, Report.RedactedPageCount]));
Writeln(Format('scanned %d object(s), removed %d',
[Report.ScannedObjectCount, Report.RemovedObjectCount]));
Writeln(Format('split text/path/image: %d / %d / %d',
[Report.SplitTextObjectCount, Report.SplitPathObjectCount,
Report.SplitImageObjectCount]));
Writeln(Format('preserved %d fragment(s)', [Report.PreservedFragmentCount]));
Writeln(Format('pruned %d resource name(s), swept %d object(s)',
[Report.ResourcePruneReport.RemovedNameCount,
Report.ResourcePruneReport.RemovedObjectCount]));
if Report.PreservedFragmentCount = 0 then
// ništa se nije moglo razdvojiti: svaki objekat koji je sekao ispušten je ceo
LogWholeObjectFallback(SourcePdf);
Zašto PDFiumPas pada zatvoreno na nemapirane znakove?
Zato što se glif bez reproduktivnog Unicode skalara ne može iskreno ponovo izgraditi. Rekonstrukcija preživelog znači pozivanje API-ja za postavljanje teksta sa stringom, a to zahteva stabilnu kodnu tačku za svaki zadržani znak. Simbolički podskup fontovi sa pokvarenim ili odsutnim ToUnicode podacima mogu dati prazno preslikavanje, a ponovno kodiranje nagađanjem bi dalo izlaz koji izgleda ispravno na ekranu dok ispod nosi drugi znak. PDFiumPas odbija: provera zadržanih znakova diže izuzetak, izuzetak se hvata unutar SaveAsRedacted, TPdfRedactionReport.Succeeded se vraća kao False sa porukom u ErrorMessage, i funkcija vraća False. Isto pravilo važi za budžet razdvajanja, koji diže izuzetak umesto da ušutke skraćuje skup fragmenata. Kada dokument ima fontove kojima ne verujete i želite determinističko staro ponašanje, postavite Options.PreservePartialObjects := False i svaki objekat koji seče nestaje ceo
Šišanje resursa kroz deljene opsege
Razdvajanje objekata ostavlja siročiđa iza sebe, a njihovo šišanje nije tako jednostavno kao poređenje rečnika /Resources nivoa stranice. ISO 32000-1 §7.8.3 dozvoljava da isti rečnik resursa bude referenciran od strane više stranica, Form XObjects, uzoraka i izglednih tokova anotacija istovremeno. Brisanje imena fonta zato što je jedna stranica prestala da ga koristi slomiće drugu stranicu koja ga i dalje koristi. PruneUnusedPdfResources zato radi po opsegu: rešava /Contents bilo da je direktan niz, indirektna referenca na niz ili jedan tok, pa sakuplja korišćenje resursa od operatora koji stvarno imenuju resurse — Tf za fontove, Do za XObjects, gs za grafičko stanje, CS, cs, SCN i scn za prostore boja i uzorke, sh za senčenja, BDC i DP za svojstva označenog sadržaja, plus /CS unos ugrađenih slika. Kada jedan rečnik deli više opsega, skupovi korišćenih imena se ujedinjavaju po kategoriji pre nego što se bilo šta ukloni
Ispuštaju se samo imena potvrđeno nereferencirana u svakom opsegu koji ukazuje na rečnik. Opseg koji se ne može pouzdano raščlaniti ostavlja se netaknut, što je konzervativan smer: nešišan fajl je samo veći, pogrešno šišan je oštećen. Preživeli rečnici se upisuju nazad kao redak inkrementalno ažuriranje koje nosi tačne brojeve generacije, a preprava dostižnosti zatim počisti objekte koji su postali nedostižni čim su imena nestala. TPdfResourcePruneReport izveštava ScannedScopeCount, UpdatedScopeCount, RemovedNameCount, RemovedObjectCount, brojeve bajtova i flag Succeeded. SaveAsRedacted pokreće ovaj korak automatski nad sanitizovanim izlazom, pa ga putanja redakcije već uključuje, ali je funkcija izvezena na nivou toka za pipeline-ove koji je žele odvojeno
uses
FPdfCompress;
procedure PruneResourceNames(const SourcePdf, TargetPdf: string);
var
Source, Dest: TFileStream;
Report: TPdfResourcePruneReport;
begin
Source := TFileStream.Create(SourcePdf, fmOpenRead or fmShareDenyWrite);
try
Dest := TFileStream.Create(TargetPdf, fmCreate);
try
// AllowSignedDocument ostaje False: inkrementalna preprava bi
// poništila bajt opsege koje potpis pokriva
PruneUnusedPdfResources(Source, Dest, Report);
if not Report.Succeeded then
raise Exception.Create(Report.ErrorMessage);
Writeln(Format('%d name(s) removed from %d scope(s), %d -> %d bytes',
[Report.RemovedNameCount, Report.UpdatedScopeCount,
Report.SourceByteCount, Report.OutputByteCount]));
finally
Dest.Free;
end;
finally
Source.Free;
end;
end;
Uklapanje u dokument pipeline
Putanja redakcije nikada ne mutira dokument koji ste učitali. SaveAsRedacted beleži izolovani snimak, primenjuje /Redact anotacije na njemu, skida priloge, pokreće prolaz sanitizacije koji uklanja open action, akcije kataloga, stabla imena, pridružene fajlove, AcroForm i metapodatke, šiša resurse, i tek onda upisuje izlazni tok. Ponovno otvaranje tog izlaza kao nezavisnog dokumenta i ponovno izvlačenje teksta je korak provere vredan čuvanja u vašem test skupu, jer je to jedina provera koja odgovara na prvobitno pitanje — može li čitalac još doći do stringa. Jedna posledica koju treba planirati: razdvajanje zamenjuje objekte stranice, pa je svaki FPDF_PAGEOBJECT rukovanac koji ste držali mrtav posle toga, ista zamka životnog veka opisana u zastarelim rukovaocima objekata stranice posle transformacije
Dva susedna komada čine tok rada kompletanim. Odlučivanje gde pravougaoni redakcije idu obično polazi iz izvučene geometrije, i model blokova i redosleda čitanja u strukturiranim tekst blokovima i redosledu čitanja je bolji izvor kandidatskih kutija nego sirovi znakovni nizovi. Služenje rezultata recenzentu pripada pravilima očvršćavanja u izgradnji sigurnog PDF pregleda, gde popunjavanje formulara i JavaScript ostaju isključeni po podrazumevanom. Zajedno pokrivaju petlju koju većina compliance tokova treba: lociraj, redaktuj na nivou operatora, proveri ponovnim otvaranjem, pregledaj sigurno. Cela API površina, probno preuzimanje i licencni uslovi za komponentu žive na stranici proizvoda PDFium Delphi Component