Egy kérés érkezik az asztalára: vegyen egy köteg már leképezett kivonatot, takarja le a számlaszámokat, és helyezzen el két oldalt laponként a papír megtakarítása érdekében. E feladat mindkét fele tartalomfolyam-sebészet (content-stream surgery) egy olyan PDF-fájlon, amelyet nem Ön hozott létre, így nincs barátságos oldalvászon, amelyre rajzolhatna, és nincs betűkészlet-kezelő sem, amelyre támaszkodhatna. Egy betöltött dokumentum objektumgráfját szerkeszti közvetlenül, nyers rajzolási operátorokat fűzve egy olyan oldalhoz, amelyet valamilyen más eszköz rendezett el. A HotPDF pontosan két belépési pontot biztosít erre, és a kettő közül a veszélyesebbik az, amelyik ártatlannak tűnik
A HotPDF egy natív VCL PDF-komponens Delphihez és C++Builderhez. A kilencedik verziójú (round-nine) betöltöttdokumentum-API-ja tartalmazza az első olyan metódusokat, amelyek vadonatúj tartalmat hoznak létre egy lemezről megnyitott oldalon ahelyett, hogy azt a semmiből építenék fel. Közülük kettő a jelen cikk témája: a RedactLoadedRect, amely egy átlátszatlan téglalapot fest egy területre, és a StitchLoadedPage, amely átméretez egy oldalt, és rárajzolja azt egy másikra. Mindkettő az ISO 32000-1 §8.5 tartalomfolyam-operátorainak (content-stream operators) a lap /Contents folyamába történő beírásával működik. Annak megértése, hogy mit tesznek ezek az operátorok — és ami még fontosabb, mit nem —, jelenti a különbséget a működő eszköz és az adatszivárgás között
Operátorok hozzáfűzése egy betöltött oldalhoz
Amikor oldalt épít a normál HotPDF API-val, a komponens birtokolja a tartalomfolyamot, és szerializálja Ön helyett a TextOut és a vektoros hívásokat. A betöltött oldal más: a /Contents kulcsa egy meglévő folyamobjektum (stream object), amely esetleg megosztott, vagy egy tartalomtömb része, és úgy kell belenyúlnia, hogy ne rontsa el azt, ami már ott van. A kilencedik verzió három kis segédfunkciót vezetett be, amelyek ezt biztonságossá teszik. A NewIndirectStream lefoglal egy új közvetett THPDFStreamObject objektumot üres pufferrel és egy /Length 0 bejegyzéssel; a ResolveLoadedStream követi a közvetett hivatkozást az alatta lévő folyamig; a AppendLoadedStream pedig nyers bájtokat ír a folyam végére, és átírja a /Length értékét, hogy a mentett objektum helyes formátumú maradjon
Mindkét nyilvános metódus ugyanazt a mintát követi. Megkeresi a lap /Contents kulcsát, feloldja azt folyammá (stream), és ha nincs használható folyam, létrehoz egyet és csatolja. Ezután hozzáfűzi az operátorokat. Mivel az új bájtok a folyam végére kerülnek, a festő modellje (painter's model) garantálja, hogy az eredeti elrendezés által rajzolt dolgok tetejére kerülnek leképezésre. Ez a sorrend a maszkoló téglalap mögötti teljes mechanizmus, és ez az oka annak is, hogy ez a téglalap nem az, aminek a legtöbb ember gondolná
RedactLoadedRect: átlátszatlan fedés, nem pedig törlés
A RedactLoadedRect egy nullaalapú oldalkeresőt, négy felhasználói térbeli koordinátát és három, a 0–1 tartományba eső színösszetevőt vár:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statement.pdf') > 0 then
begin
// Takarja le a számlaszám sávot az 1. oldalon teli feketével.
// A koordináták PDF felhasználói térben vannak: origó a bal alsó sarokban, pontok.
Pdf.RedactLoadedRect(0, 56, 690, 320, 706, 0, 0, 0);
Pdf.SaveLoadedDocument('statement-covered.pdf');
end;
finally
Pdf.Free;
end;
end;
A színfalak mögött a metódus három operátort bocsát ki a tartalomfolyamba: egy DeviceRGB-ben megadott kitöltési színt (r g b rg), a téglalap útvonalat (x y w h re) és egy kitöltést (f). A szélességet és magasságot a X2 - X1 és Y2 - Y1 értékekből származtatja, így Ön két szemközti sarkot ad át, és hagyja, hogy a metódus kiszámítsa a kiterjedést. Ha 0, 0, 0 értéket ad át színként, fekete sávot kap; ha 1, 1, 1 értéket ad át, fehéret kap, ami illeszkedik a fehér oldalhoz. A koordináták a betöltött oldal saját felhasználói terében vannak, ami azt jelenti, hogy az origó a bal alsó sarok és az egység a pont, valamint azt is jelenti, hogy a pontos elhelyezéshez szüksége van az oldal /MediaBox-ára; a GetLoadedPageBox a pbMediaBox opcióval megadja ezt
Olvassa el ezt kétszer: a kitöltött téglalap vizuálisan lefedi a tartalmat, de nem távolítja el azt. A téglalap alatti szöveg, kép vagy vektoros grafika továbbra is jelen van a PDF-ben, továbbra is az objektumgráfban található, és bárki kinyerheti, aki másolja az oldalt, szövegkinyerőt futtat, vagy egyszerűen törli a téglalapot a tartalomfolyamból. Ez csupán vizuális maszkolás, nem pedig jogi vagy biztonsági értelemben vett tartalomeltávolítás (redaction). Ha valóban érzékeny adatokat — számlaszámokat, orvosi leleteket, személyazonosságokat, bármilyen szabályozott adatot — rejt el, a fekete dobozzal való lefedés és a fájl továbbítása egy felfedezésre váró adatszivárgás. A valódi tartalommaszkolás (redaction) megköveteli az alatta lévő tartalom-objektumok törlését, nem pedig a lefestésüket
A metódus neve azt mondja: „Redact” (Maszkolás), és ez egy figyelmeztetés arra, hogy az eredményt hogyan lehet félreérteni, nem pedig ígéret arra vonatkozóan, hogy mit töröl. A megvalósítás őszinte ezzel kapcsolatban a saját megjegyzésében: „vizuális maszkoló primitívnek” (visual redaction primitive) nevezi magát, és megjegyzi, hogy a tartalom-eltávolító maszkoláshoz olyan tartalomfolyam-értelmezőre van szükség, amely végigjárja és átírja a meglévő operátorokat. A HotPDF betöltöttdokumentum-útvonala ezt itt nem teszi meg. Így a biztonságos szabály szűk: használja a RedactLoadedRect metódust nem érzékeny, kozmetikai maszkolásra — vázlat vízjel elrejtésére, egy terület letakarására képernyőkép készítése előtt, vagy egy elavult logó lefedésére egy belső ellenőrző példányon. Amint a doboz alatti dolog számítana, ha kiszivárogna, ez a metódus a rossz eszköz, és a helyes válasz a dokumentum újragenerálása az adatok nélkül, vagy egy valódi tartalom-eltávolítási folyamat használata
StitchLoadedPage: méretezés, eltolás, rajzolás
Az N-up elrendezés barátságosabb probléma, mert semmi sincs elrejtve, csak átrendezve. A StitchLoadedPage egy céloldal-indexet, egy forrásoldal-indexet, egy X/Y eltolást és egy skálatényezőt vár, és rárajzolja a forrásoldalt a céloldalra az adott pozícióban és méretben:
// Helyezze rá a 2. oldalt (index 1) az 1. oldalra (index 0),
// 70%-ra méretezve és jobbra-felfelé tolva.
Pdf.StitchLoadedPage(0, 1, 40, 380, 0.7);
// Kényelmi 2-up: a forrásoldal a céloldal jobb felén.
Pdf.StitchLoadedPageSideBySide(0, 1);
Az általa hozzáfűzött operátor-karakterlánc egy szabványos transzformációs-és-festési sorozat: q a grafikus állapot mentéséhez, egy cm mátrix, amely a skálát hordozza az átlón és az eltolást a transzlációs helyeken, /StitchSrc Do egy külső objektum meghívásához, és Q az állapot visszaállításához. A q/Q párnak nagy jelentősége van: elszigeteli a transzformációt, így a beillesztett oldal nem ereszti át a koordináta-rendszerét semmibe, amit utána fűznek hozzá. A metódus védelmet nyújt a nyilvánvaló hibák ellen is — indexek tartományon kívül, a cél megegyezik a forrással, nem pozitív skálatényező (amit 1.0-re korlátoz) —, és csendben lép ki ahelyett, hogy kivételt dobna, ezért ellenőrizze a bemeneteket, mert egy csendes no-op megegyezik a sikerrel
A StitchLoadedPageSideBySide egy vékony kényelmi réteg az általános metódus felett. Beolvassa a céloldal media-box szélességét, elfelezi azt, és meghívja a StitchLoadedPage metódust ezzel a fél szélességgel X eltolásként és rögzített 0.5 skálával, a forrást a jobb oldalra helyezve. Ez a kódolt 0.5 feltételezi, hogy a forrás és a cél szélessége megegyezik; ha nem, a forrás nem fogja tisztán kitölteni a felét, és az általános StitchLoadedPage-re lesz szüksége egy olyan skálával, amelyet Ön számít ki mindkét media-box alapján
Az egyszerűsített XObject stratégia és annak ISO kompromisszuma
Itt a megvalósítás egy szándékos rövidítést tesz, amelyet ismernie kell, mielőtt megbízna a kimenetben a különböző megjelenítőkön. A helyes N-up elrendezés a forrásoldal tartalmát egy Form XObject-be csomagolja — egy önálló rajzolható objektumba, amely az ISO 32000-1 §8.10.1 szerint köteles hordozni a /Type /XObject, /Subtype /Form értékeket, és a saját /BBox vágódobozát. A HotPDF kilencedik verziójú összefűzése nem építi fel ezt a burkolót. Ehelyett magát a forrás oldalszótárt (page dictionary) regisztrálja közvetlenül a cél /Resources /XObject alatt StitchSrc névvel, majd kirajzolja azt a Do segítségével. Az oldalszótár és a Form XObject elegendő mértékben osztozik a tartalommodelljén — mindkettő hivatkozik egy tartalomfolyam honlapjára és egy erőforrás-szótárra —, így sok olvasó le fogja képezni az eredményt
Ez azonban nem szabályos Form XObject. Hiányzik belőle a /Subtype /Form jelölő és a saját /BBox-a, ami azt jelenti, hogy a szigorú feldolgozók joggal hagyhatják figyelmen kívül a Do utasítást, vagy vágják le azt az elvárttól eltérően. A verzióhoz tartozó TechnicalNotes egyértelműen kimondja: a megközelítés „a legtöbb olvasó alatt leképeződik”, de „nem szigorúan ISO-kompatibilis Form XObject”, és a teljes megfelelőséghez egy valódi Form XObject folyam előállítása szükséges külön lépésben. Ezért kezelje az összefűzés kimenetét úgy, mint bármely nem-szabályos konstrukciót: ellenőrizze azt azokban a konkrét megjelenítőkben, amelyeket a vásárlói futtatnak, ne csak a saját gépén, és ha archiválásra vagy szigorúan ellenőrzött PDF-ekre van szüksége, ne támaszkodjon erre az útvonalra. Ugyanez a fegyelem érvényes mindenre, amit a betöltött objektumgráfra épít, ezért kap helyet a PDF preflight ellenőrzés Delphiben a kiadási munkafolyamatban, amikor programozottan módosít dokumentumokat
Hol alkalmazhatók ezek, és hol nem
Mindkét metódus tartalomfolyam-eszköz, így a mentális modell ugyanaz, mint amit a közvetlen rajzolásnál használ. Ha épített már oldalakat a semmiből a komponenssel, a hívások mögötti vektor- és színoperátorok ismerősek lesznek a HotPDF vászonrajzolás Delphiben cikkből; a különbség csak annyi, hogy itt egy olyan folyamhoz fűz hozzá, amelyet valaki más írt, nem pedig a sajátjához. Tartson szem előtt három határt:
- A maszkolás kozmetikai. A
RedactLoadedRectlefest egy tartalmat, de soha nem törli azt. Érzékeny adatok esetén generálja újra a forrást, vagy használjon valós tartalomeltávolítást — a fekete doboz nem jelent biztonságot - Az összefűzés kialakításából adódóan nem szabványos. A forrásoldalra pszeudo-XObject-ként hivatkozik a §8.10.1 szerinti
/Subtype /Formés/BBoxnélkül, ezért ellenőrizze a leképezést a célmegjelenítőkben, és kerülje el ott, ahol szigorú ellenőrzésre van szükség - A koordináták az oldal felhasználói terében vannak. Bal alsó origó, pontok, az oldal saját media box-a által vezérelve. Olvassa le a dobozt a
GetLoadedPageBoxsegítségével, mielőtt bármit elhelyezne, mert a betöltött oldal mérete eltérhet az Ön által feltételezetttől
Ezeken a határokon belül használva a páros egy valós munkafolyamatot fed le: oldalak átrendezése nyomtatáshoz, nem bizalmas területek maszkolása és az eredmény visszaírása a SaveLoadedDocument segítségével — mindezt teljes újra-leképezés nélkül. A betöltöttdokumentum-API, amely tartalmazza ezeket az összefűző és maszkoló primitíveket, a HotPDF Component csomaggal érkezik Delphihez és C++Builderhez, az ugyanabból a verzióból származó űrlapmező-, annotáció- és FDF-metódusokkal együtt