Na mizo vam pristane zahteva: v paketu že izrisanih izpiskov prebarvajte številke računov in jih nato po dve strani na list pošljite naprej, da prihranite papir. Obe polovici tega opravila sta kirurški poseg na content streamu PDF-ja, ki ga niste ustvarili sami, zato ni prijaznega platna strani, na katerega bi risali, niti upravljalnika pisav, na katerega bi se naslonili. Neposredno urejate graf objektov naloženega dokumenta in na stran, ki jo je razporedilo neko drugo orodje, dodajate surove operatorje risanja. HotPDF za to izpostavi natanko dve vstopni točki, bolj nevarna od obeh pa je tista, ki je videti nenevarna
HotPDF je izvorna komponenta VCL PDF za Delphi in C++Builder. Njegov API za naložene dokumente iz devetega kroga je prinesel prve metode, ki ustvarijo povsem novo vsebino na strani, odprti z diska, namesto na strani, ki ste jo zgradili iz nič. Tukaj sta pomembni dve: RedactLoadedRect, ki čez območje nariše neprozoren pravokotnik, in StitchLoadedPage, ki eno stran pomanjša in jo nariše na drugo. Obe delujeta tako, da v tok /Contents strani zapisujeta operatorje content streama po ISO 32000-1 §8.5. Razumevanje, kaj ti operatorji počnejo in prav tako pomembno česa ne počnejo, je razlika med uporabnim orodjem in uhajanjem podatkov
Dodajanje operatorjev na naloženo stran
Ko stran gradite z običajnim API-jem HotPDF, komponenta obvladuje content stream in namesto vas serializira klice TextOut ter vektorske klice. Naložena stran je drugačna: njen /Contents je obstoječi objekt toka, morda v skupni rabi, morda del polja vsebin, vanj pa morate poseči, ne da bi pokvarili tisto, kar je tam že bilo. Deveti krog je uvedel tri majhne pomočnike, ki to naredijo varno. NewIndirectStream ustvari svež posredni THPDFStreamObject s praznim medpomnilnikom in vnosom /Length 0, ResolveLoadedStream sledi posredni referenci navzdol do osnovnega toka, AppendLoadedStream pa zapiše surove bajte na konec toka in nato ponovno izračuna /Length, da shranjeni objekt ostane pravilno oblikovan
Vzorec, ki mu sledita obe javni metodi, je enak. Poiščeta /Contents strani, ga razrešita v tok, in če uporabnega toka ni, ga ustvarita in pripneta. Nato dodata operatorje. Ker novi bajti gredo na konec toka, slikarski model zagotovi, da se izrišejo nad vsem, kar je narisala prvotna postavitev. Prav ta vrstni red je celoten mehanizem pravokotnika za redakcijo in prav zaradi tega ta pravokotnik ni to, kar večina ljudi domneva, da je
RedactLoadedRect: neprozorno prekrivanje, ne brisanje
RedactLoadedRect prejme ničelni indeks strani, štiri koordinate uporabniškega prostora in tri barvne komponente v območju od 0 do 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;
Pod pokrovom metoda v content stream zapiše tri operatorje: nastavitev barve polnila v DeviceRGB (r g b rg), pot pravokotnika (x y w h re) in polnjenje (f). Širina in višina se izpeljeta kot X2 - X1 in Y2 - Y1, zato podate dva nasprotna kota, metoda pa sama izračuna razpon. Če za barvo podate 0, 0, 0, dobite črno prečko, če podate 1, 1, 1, pa belo, ki se ujema z belo stranjo. Koordinate pripadajo uporabniškemu prostoru same naložene strani, kar pomeni, da je izhodišče v spodnjem levem kotu in so enote točke. To pomeni tudi, da za natančno postavljanje potrebujete /MediaBox strani, GetLoadedPageBox skupaj z pbMediaBox pa vam to zagotovi
To preberite dvakrat: zapolnjen pravokotnik vsebino vizualno prekrije, ne odstrani je. Besedilo, slika ali vektorska grafika pod pravokotnikom je v PDF-ju še vedno prisotna, še vedno je v grafu objektov, še vedno jo lahko izlušči vsakdo, ki kopira stran, zažene izvlečnik besedila ali preprosto izbriše vaš pravokotnik iz content streama. To je vizualno maskiranje, ne redakcija v pravnem ali varnostnem smislu. Če skrivate res občutljive podatke, kot so številke računov, zdravstveni podatki, identitete ali karkoli reguliranega, je pošiljanje take datoteke s črno škatlo povrhu le še čakajoče uhajanje podatkov. Prava redakcija zahteva brisanje osnovnih objektov vsebine, ne pa prebarvanja čeznje
Ime metode pravi "Redact", kar je uporabno opozorilo, kako bodo ljudje rezultat narobe razumeli, ni pa obljuba o tem, kaj bo izbrisano. Izvedba je glede tega iskrena že v lastnem komentarju: imenuje se "visual redaction primitive" in opozori, da redakcija z odstranitvijo vsebine zahteva interpreter content streama, ki prehodi in prepiše obstoječe operatorje. Pot za naložene dokumente v HotPDF tega tukaj ne počne. Zato je varno pravilo ozko: RedactLoadedRect uporabite za neobčutljivo kozmetično maskiranje, denimo za skrivanje osnutkovnega vodnega žiga, prekrivanje območja pred posnetkom zaslona ali prekritje zastarelega logotipa na interni korekturi. V trenutku, ko bi to, kar je pod pravokotnikom, predstavljalo težavo ob razkritju, je ta metoda napačno orodje. Pravilen odgovor je, da dokument ponovno ustvarite brez teh podatkov ali uporabite pravo pot za odstranjevanje vsebine
StitchLoadedPage: pomanjšaj, premakni, nariši
N-up postavitev je prijaznejši problem, ker se nič ne skriva, vsebina se samo prerazporedi. StitchLoadedPage prejme indeks ciljne strani, indeks izvorne strani, odmik X/Y in faktor merila ter na ciljno stran nariše izvorno stran na tem mestu in v tej velikosti:
// 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);
Niz operatorjev, ki ga doda, je standardno zaporedje transformacije in izrisa: q za shranjevanje grafičnega stanja, matrika cm, ki nosi merilo na diagonali in odmik v translacijskih mestih, /StitchSrc Do za klic zunanjega objekta in Q za obnovitev stanja. Par q/Q je pomemben, ker izolira transformacijo, da prišita stran svojega koordinatnega sistema ne prenese na karkoli, kar bi dodali kasneje. Metoda varuje tudi pred očitnimi napakami, kot so indeksi zunaj obsega, primer, ko je cilj enak izvoru, in nepozitivno merilo, ki ga omeji na 1.0, ter tiho konča brez izjeme, zato preverite vhodne podatke, ker je tih no-op videti popolnoma enako kot uspeh
StitchLoadedPageSideBySide je tanek priročen ovoj okoli splošne metode. Prebere širino media boxa cilja, jo razpolovi in pokliče StitchLoadedPage s to polovično širino kot odmikom X in fiksnim merilom 0.5, tako da izvor postavi na desno polovico. Ta trdo kodirana vrednost 0.5 predpostavlja, da imata izvor in cilj enako širino. Če je nimata, izvor ne bo lepo zapolnil svoje polovice, zato boste raje uporabili splošni StitchLoadedPage z merilom, ki ga sami izračunate iz obeh media boxov
Poenostavljena strategija XObject in njen kompromis glede ISO
Tu implementacija naredi namensko bližnjico, ki jo morate poznati, preden izhodu zaupate v različnih pregledovalnikih. Pravilna postavitev N-up izvorno vsebino strani ovije v Form XObject, torej v samozadosten risljiv objekt, za katerega ISO 32000-1 §8.10.1 zahteva /Type /XObject, /Subtype /Form in lasten obrezovalni okvir /BBox. Stitch v devetem krogu tega ovoja ne zgradi. Namesto tega neposredno registrira slovar izvorne strani pod ciljno /Resources /XObject z imenom StitchSrc, nato pa ga nariše z Do. Slovar strani in Form XObject si delita dovolj vsebinskega modela, saj oba kažeta na content stream in slovar virov, zato bo veliko bralnikov rezultat vseeno izrisalo
Vendar to ni skladen Form XObject. Manjkata mu oznaka /Subtype /Form in lasten /BBox, zato ima strog porabnik vso pravico, da Do prezre ali ga obreže drugače, kot pričakujete. TechnicalNotes za ta krog to povesta naravnost: pristop se "izriše v večini bralnikov", ni pa "strogo ISO skladen Form XObject", popolna skladnost pa zahteva dodatni korak, ki sintetizira pravi tok Form XObject. Izhod stitch zato obravnavajte enako kot vsak drug neskladen konstrukt: preverite ga v konkretnih pregledovalnikih, ki jih uporabljajo vaše stranke, ne samo v enem na svojem računalniku. Če potrebujete arhivske ali strogo validirane PDF-je, se na to pot ne zanašajte. Enaka disciplina velja za vse, kar zgradite nad naloženim grafom objektov, zato preflight preverjanje PDF-jev v Delphiju upravičeno sodi v izdajni cevovod vedno, ko dokumente spreminjate programsko
Kam to sodi in kam ne
Obe metodi sta orodji content streama, zato je miselni model enak tistemu pri neposrednem risanju. Če ste s komponento že gradili strani iz nič, vam bodo vektorski in barvni operatorji za temi klici znani iz članka risanje na platno HotPDF v Delphiju. Razlika je samo v tem, da tukaj dodajate na tok, ki ga je ustvaril nekdo drug, namesto na svojega. V glavi ohranite tri meje:
- Redakcija je kozmetična.
RedactLoadedRectprebarva vsebino in je nikoli ne izbriše. Za karkoli občutljivega ponovno ustvarite izvor ali uporabite pravo odstranjevanje vsebine. Črna škatla ni varnost - Stitch je namenoma neskladen. Izvorna stran je referencirana kot psevdo-XObject brez zahtevanih polj
/Subtype /Formin/BBox, zato izris preverite v ciljnih pregledovalnikih in se tej poti izognite tam, kjer je zahtevana stroga validacija - Koordinate so uporabniški prostor strani. Izhodišče je v spodnjem levem kotu, enote so točke, vse pa določa lastni media box strani. Preden karkoli postavite, preberite polje z
GetLoadedPageBox, saj naložena stran morda ni velikosti, ki ste jo predvidevali
Če ostanete znotraj teh omejitev, par pokrije resničen potek dela: prerazporeditev strani za tisk, maskiranje nezaupnih območij in zapis rezultata nazaj z SaveLoadedDocument brez polnega ponovnega izrisa. API za naložene dokumente, ki vključuje te primitive za stitch in maskiranje, je del paketa HotPDF Component za Delphi in C++Builder, skupaj z metodami za polja obrazcev, anotacije in FDF iz istega kroga