Tehnični članek

Pravo redigiranje PDF-jev v Delphi: Brisanje in čiščenje

Pravo redigiranje PDF-jev v Delphi pomeni brisanje osnovnih bajtov, ne pa barvanje čez njih. losLab PDF Library vleče to črto z dvema vmesnikoma API: RedactRegion uredi tok vsebine strani, tako da so odstranjeno besedilo, vektorji in slike fizično odstranjeni, SanitizeDocument pa počisti metapodatke, skripte in dejanja, kjer se prav tako skrivajo redigirani podatki. Če zgrešite katero koli polovico, boste poslali datoteko, ki je na zaslonu videti cenzurirana, vendar skrivnost vrne vsakomur, ki kopira besedilo ali odpre Lastnosti dokumenta

Način neuspeha je stvar javne evidence. Časopisi, sodišča in vladne agencije so že izdali PDF-je s črnim pravokotnikom čez ime ali številko, bralec pa je le izbral besedilo pod njim, ga kopiral in prilepil domnevno redigiran niz v jasen pogled. Pravokotnik je bil le risba. Besedilo je bilo še vedno tam, v toku vsebine eno plast pod barvo, popolnoma nedotaknjeno in popolnoma izbirljivo. losLab PDF Library, pogon PDF za Delphi in C++Builder, razkrit prek razreda TPDFlib, obravnava to razlikovanje kot bistvo redigiranja in ne kot naknadno misel

Zakaj risanje črnega polja čez besedilo PDF ni pravo redigiranje?

losLab PDF Library razkriva MaskRect za primere, ko je prekrivanje res tisto, kar želite: vodni znak, vizualna zatemnitev, dokazni žig. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) doda pravokotnik v tok vsebine strani in ga naslika z operatorjem za gradnjo poti re, ki mu sledi operator polnjenja f (ISO 32000-1 §8.5). Vsak bajt, ki je že bil na strani — glifi imena, ki ste ga prekrili, vektorske poteze, vdelana slika —, ostane natanko tam, kjer je bil, eno navodilo prej v istem toku. Skladen pregledovalnik nariše črno polje nazadnje, tako da vizualno zmaga. Pokrov ne spremeni ničesar pod njim

Vsakdo lahko pridobi prvotno vsebino na tri načine: izbere in kopira besedilo neposredno skozi neprosojno polje, zažene ekstraktor besedila čez stran ali izbriše prekrivni pravokotnik iz toka vsebine. MaskRect je pošten glede tega v svojem lastnem imenu — maskira, ne odstrani. Rezultat je nalepka čez besedo, ki jo še vedno lahko preberete tako, da nalepko dvignete

Kako RedactRegion izbriše vsebino na ravni navodil

RedactRegion(Page, Left, Top, Width, Height, Options) deluje eno plast pod barvo. losLab PDF Library razčleni vsako plast vsebine strani v TPDFContentProgram, prehodi seznam navodil z analizatorjem za sledenje CTM (FindInstructionsInRect v modelu vsebine) in označi vsak operator za prikaz besedila, slikanje poti in sliko Do, katerega naslikano območje se seka z vašim pravokotnikom. Ta navodila so odstranjena s TPDFContentProgram.Delete, urejena plast pa je zapisana nazaj z WritePageContentLayer. Bajti ne zdrsnejo za pokrov — zapustijo tok. Ekstraktor besedila, zagnan kasneje, ne najde ničesar, ker ni ničesar za najti

Dve podrobnosti izvedbe je vredno prenesti v lastno razmišljanje. RedactRegion vzeme pravokotnik zgoraj levo v trenutnem izhodišču in enotah ter ga interno pretvori v uporabniški prostor spodaj levo, ki ga uporablja tok vsebine, tako da določite regijo tako, kot jo vidite na strani. Ko pravokotnik pokriva več navodil, jih RedactRegion izbriše v padajočem vrstnem redu indeksov, ker bi odstranitev navodila 4 pred navodilom 7 premaknila vsak poznejši indeks in pokvarila obseg. Nastavite Options = 0 in losLab PDF Library prav tako vtisne polno črno polje čez očiščeno regijo kot viden marker — vendar je ta marker kozmetičen, uporabljen šele po tem, ko je prava vsebina déjà odstranjena

V praksi redko trdo kodirate pravokotnik. Strani preiščete za občutljiv niz in preberete njegov omejevalni okvir — isto iskanje strani in naštevanje elementov, zajeto v lociranju besedila in elementov strani v Delphi —, nato pa ta pravokotnik predate 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;

Kje v PDF se redigirani podatki še vedno skrivajo?

Redigiranje, ki se ustavi na vidni plasti, pušča sledi, saj PDF shranjuje informacije na mestih, kamor bralec nikoli ne pogleda. losLab PDF Library razvršča uhajanja v kanale, ki jih je vredno poimenovati. Slovar informacij o dokumentu in tok metapodatkov XMP rutinsko vsebujeta nize Avtor (Author), Izdelovalec (Producer) ali Ključne besede (Keywords), ki poimenujejo prav tisto osebo ali primer, ki ste ga odstranili iz telesa. JavaScript na ravni dokumenta in kataloški OpenAction se lahko izvedeta ob odpiranju in dosežeta podatke, za katere ste mislili, da so izginili. Vsaka opomba nosi neobvezno dejanje /A, sama opomba — lepljiva beležka, zastarelo polje obrazca, povezava — pa lahko sedi neposredno nad redigiranim mestom z lastno kopijo besedila. Šifriranje se ničesar od tega ne dotakne; šifrirana datoteka z navadnim paketom XMP še vedno razkriva svoj naslov vsakemu indeksatorju, ločeno od revizije šifriranja in dovoljenj. Zagovarljivo čiščenje mora obiskati vsako od teh skrivališč, ne le strani, ki ste jo gledali

Čiščenje dokumenta s SanitizeDocument

losLab PDF Library odgovarja na ta razpon uhajanj s SanitizeDocument(Flags), usklajevalnim klicem, ki zažene nabor neodvisnih odstranitev, izbranih z bitno masko, in vrne število uporabljenih odstranitev. Zastavice so združljive. SANITIZE_JAVASCRIPT odstrani vsak paket JavaScript na ravni dokumenta in katero koli skripto OpenAction. SANITIZE_ACTIONS odstrani slovar dejanj /A iz vsake opombe prek novega TPDFAnnots.RemoveAnnotAction in počisti kataloški OpenAction. SANITIZE_METADATA izbriše Avtorja, Zadevo, Ključne besede, Ustvarjalca in Izdelovalca tako v slovarju informacij o dokumentu (ISO 32000-1 §14.3.3) kot v toku metapodatkov XMP (§14.3.2). SANITIZE_ANNOTATIONS izbriše vsako opombo v celotnem dokumentu, SANITIZE_OPEN_ACTION pa odstrani kataloški vnos /OpenAction. SANITIZE_ALL je bitna unija vseh petih

Konvencije indeksov znotraj teh odstranitev so natanko tisto, kar tiho pokvari ročno napisano zanko, zato SanitizeDocument obstaja kot en sam klic. GlobalJavaScriptPackageName je 0-indeksiran, RemoveGlobalJavaScript pa med delovanjem krči seznam, tako da odstranitev JavaScripta vedno bere indeks paketa 0 in odstranjuje, dokler število ne doseže nič. DeleteAnnotation je po drugi organi 1-indeksiran. AnnotationCount poroča glede na trenutno izbrano stran, zato opombne odstranitve pred štetjem izberejo vsako stran po vrsti. losLab PDF Library te meje interno pravilno razreši, tako da predate en nabor zastavic in preberete eno število nazaj, namesto da bi poustvarjali pet različnih pravil iteracije

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;

Celoten potek dela redigiranja in čiščenja

Združite oboje in celoten postopek je kratek. losLab PDF Library odstrani vidno vsebino z RedactRegion stran za stranjo, nato pa pred zapisom datoteke počisti vsak skrit kanal s SanitizeDocument(SANITIZE_ALL). Ker redigiranje skoraj vedno teče na dokumentih, ki so prispeli izven vašega nadzora, jih je vredno naložiti skozi utrjeno pot — isto obrambno držo, opisano v varnem razčlenjevanju nezaupljivih PDF-jev —, tako da napačno oblikovan vhod ne uspe čisto in ne na polovici prehoda redigiranja

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;

Kje se redigiranje toka vsebine ustavi

When a page is a scanned image, the words are pixels inside an image XObject, not text operators, and losLab PDF Library cannot carve a name out of the middle of a raster the way it deletes a glyph. Njegovo ravnanje s slikami je namerno konzervativno: vsaka slika, katere postavitev se seka s pravokotnikom, se odstrani v celoti, tako da redigirno polje na skenirani strani vzame s seboj celoten sken in ne le njegovega dela. Redigiranje regije znotraj slike pomeni rastriranje, slikanje in ponovno vgrajevanje na ravni slikovnih pik, kar je drugačna operacija kot brisanje toka vsebine. Enako opozorilo velja za besedilno plast OCR nad skenirano stranjo: RedactRegion odstrani besedilne operatorje, ki jih vidi, slika spodaj pa ostane nedotaknjena, razen če se njen slikovni XObject prav tako seka s poljem

Dve natančnejši točki ohranjata rezultate poštene. Test presečišča približno oceni širino vsakega besedilnega teka na podlagi števila znakov in velikosti pisave, namesto da bi meril natančne metrike glifov, tako da lahko zelo kratka beseda v proporcionalni pisavi zdrsne mimo tesno narisanega pravokotnika — dajte polju majhen rob. Brisanje je prav tako zrnavo na ravni navodil: RedactRegion odstrani celotne operatorje za prikaz besedila, tako da lahko pravokotnik, ki obreže le del teka, vzame sosednje znake, ki so si delili isti operator z njim. Nobena omejitev ni razlog za nezaupanje mehanizmu, le razlog za skrbno omejitev pravokotnika in preverjanje rezultata

Disciplina, ki zaključi vsak potek dela redigiranja, je preverjanje, ne pa zaupanje. Redigirajte regijo, počistite dokument, shranite v novo datoteko, jo ponovno odprite in poskusite pridobiti nazaj tako besedilo, ki ste ga odstranili, kot metapodatke, ki ste jih počistili; ko se oboje vrne prazno, je redigiranje uspelo. API-ja RedactRegion in SanitizeDocument, prikazana tukaj, sta priložena knjižnici losLab PDF Library za Delphi in C++Builder, skupaj s polno referenco modela vsebine in čiščenja