Teknisk artikkel

Ekte PDF-sladding i Delphi: Slett og rens

Ekte PDF-sladding i Delphi betyr å slette de underliggende bytene, ikke å male over dem. losLab PDF Library trekker denne grensen med to API-er: RedactRegion redigerer sidens innholdsstrøm slik at fjernet tekst, vektorer og bilder er fysisk borte, og SanitizeDocument tømmer metadata, skript og handlinger der sladdet informasjon også kan skjule seg. Hvis du gjør feil med en av delene, sender du en fil som ser sensurert ut på skjermen, men som gir hemmeligheten rett tilbake til alle som kopierer teksten eller åpner dokumentegenskapene

Denne feilmodusen er velkjent offentlig. Aviser, domstoler og offentlige etater har alle frigitt PDF-er med et svart rektangel lagt over et navn or et nummer, bare for at en leser kan markere teksten under, kopiere den og lime inn den angivelig sladdede strengen i full visning. Rektangelet var bare en tegning. Teksten var der fortsatt, plassert i innholdsstrømmen ett lag under malingen, helt intakt og fullt velgbar. losLab PDF Library, PDF-motoren for Delphi og C++Builder som eksponeres gjennom klassen TPDFlib, behandler dette skillet som selve poenget med sladding fremfor en ettertanke

Hvorfor er det å tegne en svart boks over PDF-tekst ikke ekte sladding?

losLab PDF Library tilbyr MaskRect for de tilfellene der tildekking er akkurat det du ønsker: et vannmerke, en visuell blackout, et korrekturstempel. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) legger til et rektangel i sidens innholdsstrøm og tegner det med re-operatoren for stikonstruksjon etterfulgt av fylloperatoren f (ISO 32000-1 §8.5). Hver byte som allerede var på siden — glyfene til navnet du dekket til, vektorlinjene, det innebygde bildet — forblir akkurat der det var, én instruksjon tidligere i den samme strømmen. En kompatibel visningsprogramvare tegner den svarte boksen sist, slik at den vinner visuelt. Tildekkingen endrer ingenting under

Hvem som helst kan hente ut det opprinnelige innholdet på tre måter: markere og kopiere teksten rett gjennom den ugjennomsiktige boksen, kjøre en tekstuttrekker over siden, eller slette det tildekkende rektangelet fra innholdsstrømmen. MaskRect er ærlig om dette i sitt eget navn — den maskerer, den fjerner ikke. Resultatet er un klistremerke over et ord du fremdeles kan lese ved å løfte klistremerket av

Hvordan RedactRegion sletter innhold på instruksjonsnivå

RedactRegion(Page, Left, Top, Width, Height, Options) fungerer ett lag under malingen. losLab PDF Library tolker hvert innholdslag på siden inn i en TPDFContentProgram, går gjennom instruksjonslisten med en CTM-sporingsanalysator (FindInstructionsInRect i innholdsmodellen), og markerer hver tekstvisnings-, stitegnings- og bilde-Do-operator der det tegnede området overlapper med rektangelet ditt. Disse instruksjonene fjernes med TPDFContentProgram.Delete, og det redigerte laget skrives tilbake med WritePageContentLayer. Bytene skyves ikke bak et deksel — de forlater strømmen. En tekstuttrekker som kjøres etterpå finner ingenting, fordi det ikke er noe å finne

To implementasjonsdetaljer er verdt å merke seg. RedactRegion tar et topp-venstre-rektangel i gjeldende origo og enheter og konverterer det internt til det bunn-venstre brukerrommet innholdsstrømmen bruker, slik at du angir området slik du ser det på siden. Når et rektangel dekker flere instruksjoner, sletter RedactRegion dem i synkende indeksrekkefølge, fordi å fjerne instruksjon 4 før instruksjon 7 ville forskyve alle senere indekser og ødelegge området. Send inn Options = 0, og losLab PDF Library stempler også en solid svart boks over det slettede området som en synlig markør — men denne markøren er kosmetisk, påført etter at det virkelige innholdet allerede er borte

I praksis vil du sjelden hardkode rektangelet. Du søker på siden etter den sensitive strengen og leser tilbake dens grenseboks (bounding box) — det samme sidesøket og elementopplistingen som dekkes i lokalisering av tekst og sideelementer i Delphi — og sender deretter rektangelet til 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;

Hvor skjuler det seg fremdeles sladdet informasjon i en PDF?

Sladding som stopper ved det synlige laget etterlater seg spor, fordi en PDF lagrer informasjon på steder en leser aldri ser. losLab PDF Library grupperer lekkasjene inn i navngitte kanaler. Document Information Dictionary og XMP-metadatastrømmen inneholder rutinemessig en Author-, Producer- eller Keywords-streng som navngir akkurat den personen eller saken du fjernet fra dokumentet. JavaScript på dokumentnivå og katalogens OpenAction kan kjøre ved åpning og hente data du trodde var borte. Hver merknad (annotation) har en valgfri /A-handling, og selve merknaden — et gult notat, et foreldet skjemafelt, en lenke — kan sitte direkte over det sladdede stedet med sin egen kopi av teksten. Ingenting av dette berøres av kryptering; en kryptert fil med en ren tekst XMP-pakke lekker fortsatt tittelen sin til indeksatorer, et avvik som er verdt å revidere kryptering og tillatelser for i seg selv. Forsvarlig rensing må sjekke hver av disse gjemmestedene, ikke just siden du så på

Rense dokumentet med SanitizeDocument

losLab PDF Library håndterer denne spredningen av lekkasjer med SanitizeDocument(Flags), et orkestreringskall som kjører et sett med uavhengige rensinger valgt av en bitmaske og returnerer antallet rensinger den utførte. Flaggene kan kombineres. SANITIZE_JAVASCRIPT fjerner alle JavaScript-pakker på dokumentnivå og eventuelle OpenAction-skript. SANITIZE_ACTIONS fjerner /A-handlingsordboken fra hver merknad, via den nye TPDFAnnots.RemoveAnnotAction, og tømmer katalogens OpenAction. SANITIZE_METADATA sletter Author, Subject, Keywords, Creator og Producer i både Document Information Dictionary (ISO 32000-1 §14.3.3) og XMP-metadatastrømmen (§14.3.2). SANITIZE_ANNOTATIONS sletter alle merknader i hele dokumentet, og SANITIZE_OPEN_ACTION fjerner katalogens /OpenAction-oppføring. SANITIZE_ALL er den bitvise unionen av alle fem

Indekskonvensjonene i disse rensingene er akkurat typen ting som i det stille kan ødelegge en manuelt skrevet løkke, noe som er mye av grunnen til at SanitizeDocument tilbys som et enkelt kall. GlobalJavaScriptPackageName er 0-basert, og RemoveGlobalJavaScript krymper listen underveis, slik at JavaScript-rensingen alltid leser pakkeindeks 0 og fjerner til antallet når null. DeleteAnnotation er derimot 1-basert. AnnotationCount rapporterer for den valgte siden, so merknadsrensingen velger hver side etter tur før tellingen. losLab PDF Library håndterer disse grensene riktig internt, slik at du bare sender inn et flaggsett og leser tilbake ett antall i stedet for å måtte gjenskape fem forskjellige iterasjonsregler

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;

Den komplette arbeidsflyten for sladding og rensing

Sett de to sammen, og den fulle prosedyren er kort. losLab PDF Library fjerner det synlige innholdet med RedactRegion, side for side, og tømmer deretter alle skjulte kanaler med SanitizeDocument(SANITIZE_ALL) før filen skrives. Siden sladding nesten alltid utføres på dokumenter som kommer utenfor din kontroll, er det verdt å laste dem inn via en herdet bane — den samme defensive holdningen som er beskrevet i sikker analysering av upålitelige PDF-er — slik at en feilformet inndata feiler rent i stedet for halvveis i sladdeprosessen

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;

Hvor innholdsstrøm-sladding stopper

RedactRegion opererer på innholdsstrømmen, som er akkurat der opprinnelig digital (born-digital) tekst lever, og akkurat der skannet tekst ikke lever. Når en side er et skannet bilde, er ordene piksler inni et bilde-XObject, ikke tekstoperatorer, og losLab PDF Library kan ikke skjære ut et navn fra midten av et rasterbilde på samme måte som det sletter en glyf. Dens bildehåndtering er bevisst konservativ: alle bilder der plasseringen overlapper med rektangelet fjernes i sin helhet, slik at en sladdingsboks på en skannet side tar med seg hele skanningen i stedet for bare en del av den. Å sladde et område inni et bilde krever rasterisering, maling og gjeninnbygging på pikselnivå, noe som er en helt annen operasjon enn sletting av innholdsstrømmen. Den samme forsiktigheten gjelder for et OCR-tekstlag over a skannet side: RedactRegion fjerner tekstoperatorene den kan se, men bildet under forblir uberørt med mindre dets bilde-XObject også overlapper med boksen

To finere detaljer sikrer ærlige resultater. Overlappingstesten anslår bredden på hver tekstkjøring (text run) ut fra tegnantall og skriftstørrelse i stedet for å måle nøyaktige glyf-mål, slik at et veldig kort ord skrevet med en proporsjonal skrifttype kan slippe unna et stramt tegnet rektangel — gi boksen en liten margin. Sletting er også instruksjons-granulært: RedactRegion fjerner hele tekstvisende operatorer, så et rektangel som bare klipper en del av en tekstkjøring, kan ta med seg tilstøtende tegn som delte samme operator. Ingen av disse begrensningene er en grunn til å mistro mekanismen, bare en grunn til å definere rektangelet nøye og kontrollere resultatet

Disiplinen som avslutter enhver sladdearbeidsflyt er verifisering, ikke tillit. Sladd regionen, rens dokumentet, lagre til en ny fil, åpne den på nytt, og prøv å hente ut både teksten du fjernet og metadataene du tømte; når begge kommer tomme tilbake, har sladdingen holdt. RedactRegion- og SanitizeDocument-API-ene som vises her leveres med losLab PDF Library for Delphi og C++Builder, sammen med den fullstendige referansen for innholdsmodell og rensing