En forespørsel lander på pulten din: ta en bunke allerede gjengitte kontoutskrifter, skjul kontonumrene, og send to sider per ark for å spare papir. Begge delene av den oppgaven er kirurgi i en innholdsstrøm på en PDF du ikke har laget, så du har ingen vennlig side-canvas å tegne på og ingen font manager å støtte deg på. Du redigerer objektgrafen i et lastet dokument direkte og legger rå tegningsoperatorer til en side som et annet verktøy har satt opp. HotPDF eksponerer akkurat to innganger for dette, og den farligste av dem er den som ser ufarlig ut
HotPDF er en innfødt VCL PDF-komponent for Delphi og C++Builder. Lastet-dokument-API-en i runde ni la til de første metodene som oppretter helt nytt innhold på en side du åpnet fra disk, i stedet for en du bygde fra bunnen av. To av dem er temaet her: RedactLoadedRect, som maler et ugjennomsiktig rektangel over et område, og StitchLoadedPage, som skalerer én side og tegner den på en annen. Begge virker ved å skrive ISO 32000-1 §8.5 content-stream-operatorer inn i sidens /Contents-strøm. Å forstå hva disse operatorene gjør, og like viktig hva de ikke gjør, er forskjellen mellom et fungerende verktøy og et databrudd
Legge operatører til en lastet side
Når du bygger en side med den vanlige HotPDF-API-en, eier komponenten innholdsstrømmen og serialiserer TextOut- og vektor-kallene dine for deg. En lastet side er annerledes: /Contents er et eksisterende strømobjekt, kanskje delt, kanskje en del av en content array, og du må spleise deg inn i det uten å ødelegge det som allerede ligger der. Runde ni introduserte tre små hjelpere som gjør dette trygt. NewIndirectStream allokerer et nytt indirekte THPDFStreamObject med en tom buffer og et /Length 0-felt; ResolveLoadedStream følger en indirekte referanse ned til den underliggende strømmen; og AppendLoadedStream skriver rå bytes til slutten av strømmen og skriver om /Length så det lagrede objektet holder seg velformet
Begge de offentlige metodene følger samme mønster. Finn sidens /Contents, løs den opp til en strøm, og hvis det ikke finnes en brukbar strøm, opprett en og fest den til dokumentet. Deretter appender du operatorene. Fordi de nye bytes-ene havner på slutten av strømmen, garanterer painter's model at de rendres oppå alt det opprinnelige oppsettet tegnet. Den rekkefølgen er hele mekanismen bak sensureringsrektangelet, og det er også grunnen til at rektangelet ikke er det de fleste tror det er
RedactLoadedRect: et ugjennomsiktig dekke, ikke en sletting
RedactLoadedRect tar en nullbasert sideindeks, fire koordinater i user space og tre fargekomponenter i området 0–1:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statement.pdf') > 0 then
begin
// Cover the account-number band on page 1 with solid black.
// Coordinates are PDF user space: origin bottom-left, points.
Pdf.RedactLoadedRect(0, 56, 690, 320, 706, 0, 0, 0);
Pdf.SaveLoadedDocument('statement-covered.pdf');
end;
finally
Pdf.Free;
end;
end;
Under panseret sender metoden tre operatorer inn i innholdsstrømmen: en fyllfarge satt i DeviceRGB (r g b rg), en rektangelbane (x y w h re) og en fylling (f). Bredde og høyde beregnes som X2 - X1 og Y2 - Y1, så du sender to motsatte hjørner og lar metoden regne ut utstrekningen. Sender du 0, 0, 0 som farge, får du en svart stripe; sender du 1, 1, 1, får du en hvit stripe som passer på en hvit side. Koordinatene er lastet sides eget user space, som betyr at origo er nederst til venstre og enhetene er points, og det betyr også at du trenger sidens /MediaBox for å plassere noe presist; GetLoadedPageBox med pbMediaBox gir deg det
Les dette to ganger: et fylt rektangel dekker innhold visuelt, det fjerner det ikke. Teksten, bildet eller vektorkunsten under rektangelet finnes fortsatt i PDF-en, fortsatt i objektgrafen, fortsatt mulig å hente ut for alle som kopierer siden, kjører en tekstuttrekker eller bare sletter rektangelet ditt fra innholdsstrømmen. Dette er visuell maskering, ikke sensurering i juridisk eller sikkerhetsmessig forstand. Hvis du skjuler virkelig sensitive data, som kontonumre, journaler, identiteter eller annet regulert innhold, er det å dekke det med en svart boks og sende filen en datalekkasje som venter på å bli oppdaget. Ekte sensurering krever at de underliggende innholdsobjektene slettes, ikke at man maler over dem
Metodenavnet sier "Redact", og det er en nyttig advarsel om hvordan resultatet kan misforstås, ikke et løfte om hva det sletter. Implementasjonen er ærlig om dette i sin egen kommentar: den kaller seg selv det "visuelle sensureringsprimitivet" og bemerker at innholds-fjernende sensurering trenger en content-stream-interpreter som går gjennom og skriver om de eksisterende operatorene. HotPDFs lastet-dokument-path gjør ikke det her. Så den trygge regelen er smal: bruk RedactLoadedRect for ikke-sensitive kosmetiske maskeringer, som å skjule et utkastvannmerke, tømme et område før et skjermbilde eller dekke en foreldet logo på en intern korrektur. I det øyeblikket det som ligger under boksen ville bety noe hvis det lekket, er denne metoden feil verktøy, og det riktige svaret er å regenerere dokumentet uten dataene eller bruke en ekte pipeline for fjerning av innhold
StitchLoadedPage: skaler, flytt, tegn
N-up-oppsett er den snillere oppgaven fordi ingenting skjules, bare flyttes. StitchLoadedPage tar en målsideindeks, en kildesideindeks, en X/Y-forskyvning og en skaleringsfaktor, og den tegner kildesiden på mål-siden i den posisjonen og størrelsen:
// Overlay page 2 (index 1) onto page 1 (index 0),
// scaled to 70% and nudged up-right.
Pdf.StitchLoadedPage(0, 1, 40, 380, 0.7);
// Convenience 2-up: source page on the right half of the target.
Pdf.StitchLoadedPageSideBySide(0, 1);
Operatorstrengen den appender er en standard transform-og-tegn-sekvens: q for å lagre grafikktilstanden, en cm-matrise som bærer skalaen på diagonalen og forskyvningen i translasjonsfeltene, /StitchSrc Do for å kalle et eksternt objekt og Q for å gjenopprette tilstanden. q/Q-paret er viktig, fordi det isolerer transformasjonen slik at den sammensydde siden ikke lekker koordinatsystemet sitt inn i noe som appenderes etterpå. Metoden vokter også mot de åpenbare feilene, indekser utenfor område, et mål som er lik kilden, en ikke-positiv skala som den klamper til 1.0, og den avslutter stille i stedet for å kaste, så sjekk inputene dine fordi en stille no-op ser identisk ut som suksess
StitchLoadedPageSideBySide er en tynn bekvemmelighet oppå den generelle metoden. Den leser målsidens media-box-bredde, halverer den, og kaller StitchLoadedPage med den halve bredden som X-forskyvning og en fast skala på 0.5, slik at kilden havner på høyre halvdel. Den hardkodede 0.5-en antar at kilde og mål har samme bredde; hvis de ikke har det, fyller kilden ikke halvparten pent, og du vil heller bruke den generelle StitchLoadedPage med en skala du selv regner ut fra begge media boxes
Den forenklede XObject-strategien og ISO-avveiningen
Her er det implementasjonen tar en bevisst snarvei du må kjenne til før du stoler på resultatet på tvers av visningsprogrammer. Et korrekt N-up-oppsett pakker kildesidens innhold inn i en Form XObject, et selvstendig tegnbart objekt som ISO 32000-1 §8.10.1 sier må bære /Type /XObject, /Subtype /Form og sin egen /BBox-klippeboks. HotPDFs runde ni stitch bygger ikke den wrapperen. I stedet registrerer den selve kilde-sideordboken direkte under målsidens /Resources /XObject med navnet StitchSrc, og tegner den så med Do. En sideordbok og en Form XObject deler nok av datamodellen sin, begge peker på en content stream og en ressurssordbok, til at mange lesere vil rendere resultatet
Men det er ikke en conforming Form XObject. Den mangler markøren /Subtype /Form og sin egen /BBox, noe som betyr at en streng konsument har full rett til å ignorere Do eller klippe den annerledes enn du forventer. TechnicalNotes for denne runden sier det rett ut: tilnærmingen fungerer hos de fleste lesere, men den er ikke en strengt ISO-kompatibel Form XObject, og full samsvar krever at du lager en ekte Form XObject-strøm som et eget steg. Så behandle stitch-utdataen som enhver annen ikke-konform konstruksjon: verifiser den i de visningsprogrammene kundene dine faktisk bruker, ikke bare i den på maskinen din, og hvis du trenger arkiv- eller validator-ren PDF, ikke stol på denne banen. Den samme disiplinen gjelder alt du bygger på den lastede objektgrafen, og derfor fortjener en PDF-preflight i Delphi sin plass i release-pipelinen hver gang du endrer dokumenter programmatisk
Hvor dette passer, og hvor det ikke gjør det
Begge metodene er content-stream-verktøy, så mentalmodellen er den samme som du bruker for direkte tegning. Hvis du har bygget sider fra bunnen av med komponenten, vil vektor- og fargeoperatorene bak disse kallene være kjente fra HotPDFs canvas-tegning i Delphi; forskjellen er bare at du her appender til en strøm noen andre har skrevet, ikke en du eier. Hold tre grenser i hodet:
- Sensurering er kosmetisk.
RedactLoadedRectmaler over innhold og sletter det aldri. For alt som er sensitivt må du regenerere kilden eller bruke ekte fjerning av innhold, en svart boks er ikke sikkerhet - Stitch er ikke-konform av design. Kildesiden refereres som en pseudo-XObject uten §8.10.1
/Subtype /Formog/BBox, så bekreft gjengivelsen i målviserne dine og unngå den der streng validering kreves - Koordinater er sidens user space. Origo er nederst til venstre, i points, styrt av sidens egen media box. Les boksen med
GetLoadedPageBoxfør du plasserer noe, fordi siden du lastet inn kanskje ikke er i størrelsen du antar
Brukt innenfor disse grensene dekker paret en reell arbeidsflyt: reorganiser sider for utskrift, maskér ikke-konfidensielle områder, og skriv resultatet tilbake med SaveLoadedDocument, alt uten full gjengivelse på nytt. API-et for lastede dokumenter som inkluderer disse stitch- og maskeringsprimitivene følger med HotPDF Component for Delphi og C++Builder, sammen med metodene for skjemafelt, annotasjoner og FDF fra samme runde