Dodavanje vodenog žiga ili logotipa na svaku stranicu izgleda kao petominutni posao dok rezultat ne proverite alatom za analizu veličine datoteke. Očigledan pristup prolazi kroz stranice i na svakoj ponovo gradi iste tekstualne ili slikovne objekte. Vizuelno radi, ali nepotrebno umnožava podatke. Dijagonalni vodeni žig „DRAFT“ nacrtan direktno na sto stranica znači sto kopija istih putanja i tekstualnih podataka u tokovima sadržaja, a sačuvana datoteka nosi svaku od njih
PDF za ovaj problem koristi Form XObject. On obavija sadržaj za višekratnu upotrebu, celu stranicu ili mali šablon, u jedan imenovani objekat koji se može iscrtati mnogo puta na različitim mestima. Sadržaj postoji jednom, a svaka stranica čuva kratku instrukciju da iscrta XObject N sa zadatom transformacijom. Vodeni žig na sto stranica zato dodaje jedan objekat sadržaja umesto stotinu. Vodeni žigovi, logotipi, šabloni brojeva stranica i pečati imaju isti oblik problema, a Form XObject je odgovarajući alat za sve njih
Jedan objekat je bolji od stotinu ponovnih crtanja
Ušteda je strukturna, a ne kozmetička. PDF stranica se renderuje izvršavanjem toka sadržaja, niza operatora za crtanje. Ako pečat ponovo crtate na svakoj stranici, ceo niz operatora dodaje se svakom toku i bajtovi se dupliraju onoliko puta koliko ima stranica. Form XObject premešta operatore u jedan tok sačuvan jednom u dokumentu. Pojedinačna stranica čuva samo malu referencu: postavlja matricu transformacije, poziva XObject i vraća stanje. Broj stranica više ne množi cenu grafike
Razlika je najveća kod složenog pečata. Vektorski znak sa stotinama segmenata putanje ili bitmap logotip skupi su za čuvanje. Kada se sačuvaju jednom i samo referenciraju, težak sadržaj plaća se jednom, dok je trošak po stranici nekoliko bajtova poziva. Vizuelni rezultat je isti kao pri direktnom crtanju; čitač ne vidi razliku, ali veličina datoteke vidi
Snimanje stranice u XObject
PDFium gradi objekat za višekratnu upotrebu iz postojeće stranice. Izvor može biti stranica otvorenog dokumenta, mali PDF od jedne stranice koji sadrži samo grafiku vodenog žiga ili određena stranica većeg dokumenta. CreateXObjectFromPage snima sadržaj izvorne stranice u ručicu za višekratnu upotrebu koja pripada odredišnom dokumentu na koji se pečat postavlja
var
Dest, Stamp: TPdf;
XObject: TPdfXObject;
begin
Dest := TPdf.Create(nil);
Stamp := TPdf.Create(nil);
try
Dest.FileName := 'Report.pdf';
Dest.Active := True;
Stamp.FileName := 'Watermark.pdf'; // one page of artwork
Stamp.Active := True;
if not (Dest.Active and Stamp.Active) then
raise Exception.Create('Could not open the input documents');
// Capture page 0 of the stamp document into a reusable handle that
// is owned by Dest. Source must be Active; the index is zero-based.
XObject := Dest.CreateXObjectFromPage(Stamp, 0);
if XObject = nil then
raise Exception.Create('Could not build the stamp XObject');
// ... place it, then free it before closing Stamp (see below) ...
Potpis je CreateXObjectFromPage(Source: TPdf; SourcePageIndex: Integer): TPdfXObject. Metoda izaziva izuzetak ako izvorni dokument nije Active, a vraća nil bez izuzetka kada PDFium ne može da napravi objekat, zato eksplicitna provera nije opciona. Vraćeni TPdfXObject pripada pozivaocu, a njegova dva pravila životnog veka opisana su u posebnoj sekciji ispod
Postavljanje pečata na stranicu
Snimljeni XObject sam po sebi ništa ne prikazuje. Kopiju umećete na trenutnu stranicu odredišnog dokumenta, izabranu svojstvom PageNumber zasnovanim na 1, pozivom InsertFormObjectFromXObject. Poziv vraća osnovni objekat stranice, FPDF_PAGEOBJECT, preko kojeg određujete položaj. Bez transformacije pečat završava u koordinatnom početku izvorne stranice, što retko odgovara željenom mestu
Pošto InsertFormObjectFromXObject umeće po jednu kopiju i svaki put vraća novi objekat stranice, isti XObject možete iscrtati više puta na jednoj stranici sa različitim transformacijama, dok se sadržaj u datoteci i dalje čuva jednom. Logotip u uglu i bledi vodeni žig preko cele stranice mogu poticati iz istog snimljenog objekta
var
PageObj: FPDF_PAGEOBJECT;
M: TPdfMatrix;
RawM: FS_MATRIX;
begin
// The current page of Dest receives one copy of the XObject.
PageObj := Dest.InsertFormObjectFromXObject(XObject);
if PageObj = nil then
raise Exception.Create('Insert failed on this page');
// Position it: move 200 units right, 500 up, at 70% scale.
M := TPdfMatrix.Create;
try
M.Scale(0.7, 0.7);
M.Translate(200, 500);
RawM := M.Handle;
if FPDFPageObj_SetMatrix(PageObj, RawM) = 0 then
raise Exception.Create('Cannot assign the stamp matrix');
finally
M.Free;
end;
Dest.UpdatePage; // commit this page's edits to its content stream
// if not Dest.SaveAs(...) then ... when every page is done.
end;
Dva detalja čine postupak bezbednim. Posle umetanja objekat stranice pripada stranici, a ne XObject-u; kasnije oslobađanje XObject-a ne poništava već postavljene kopije. Zato radi redosled kreiraj-postavi-oslobodi. Drugo, umetanje i pozicioniranje menjaju samo listu objekata stranice u memoriji. UpdatePage serijalizuje tu listu nazad u tok sadržaja, pa se izmena stranice bez tog poziva čuva kao da pečat nije ni postavljen
Pravilo životnog veka ručice
Dva ograničenja upravljaju XObject ručicom. Prvo, izvorni dokument mora biti aktivan u trenutku poziva CreateXObjectFromPage, jer snimanje čita sadržaj žive izvorne stranice. Drugo, ručica mora biti oslobođena pre zatvaranja izvorne stranice, a u praksi pre zatvaranja ili oslobađanja izvornog dokumenta iz kojeg je nastala
Razlog je to što XObject referencira strukturu koja je i dalje u vlasništvu izvornog dokumenta. Nije samostalna kopija koju možete zadržati nakon zatvaranja izvora. Ako prvo zatvorite izvor, ručica pokazuje na već uklonjen sadržaj, pa njeno kasnije oslobađanje ili korišćenje pristupa nevažećoj memoriji. Simptomi su tipični za viseću ručicu: access violation pri gašenju ili povremeno oštećenje koje zavisi od redosleda alokacije, sa stekom u kodu za čišćenje umesto na mestu uzroka. Ispravka je pravilan redosled: napravite XObject, umetnite ga na potrebne stranice, oslobodite XObject i tek zatim zatvorite izvorni dokument. Destruktor TPdfXObject oslobađa osnovnu PDFium ručicu; vaša odgovornost je da omotač oslobodite u pravom trenutku
Matrica i značenje njenih šest brojeva
Položaj je 2D afina transformacija koju PDF svuda koristi za sadržaj, prema ISO 32000-1, odeljak 8.3.4. Sastoji se od šest brojeva a, b, c, d, e, f, koje PDFium izlaže zapisom FS_MATRIX. Oni preslikavaju tačku iz prostora objekta u prostor stranice:
// x' = a*x + c*y + e
// y' = b*x + d*y + f
//
// a, d : horizontal and vertical scale
// b, c : the shear / rotation terms
// e, f : translation (where the origin lands on the page)
Šest vrednosti možete popuniti ručno, ali se pri ručnom sastavljanju najčešće greši u rotaciji jer ona istovremeno menja a, b, c, d. Omotač TPdfMatrix iz jedinice FPdfMatrix sastavlja uobičajene operacije i naknadno ih množi, pa se Translate, Scale i Rotate ulančavaju redosledom poziva. Dijagonalni vodeni žig koristi rotaciju pa translaciju radi ponovnog centriranja; logotip u uglu koristi skaliranje pa translaciju. Kada je matrica spremna, kopirajte njenu sirovu vrednost, svojstvo Handle tipa FS_MATRIX, u lokalnu promenljivu i prosledite je funkciji FPDFPageObj_SetMatrix. Import očekuje var parametar, pa svojstvo ne može direktno da se prosledi, a rezultat 0 označava neuspeh. Niži poziv FPDFPageObj_Transform prima šest Double vrednosti kada ne želite omotač
Pečatiranje svake stranice pravilnim redosledom
Potpun obrazac prati pravilo životnog veka: otvorite oba dokumenta, jednom snimite pečat, prođite kroz odredišne stranice postavljanjem svojstva PageNumber zasnovanog na 1, umetnite i pozicionirajte kopiju, potvrdite svaku stranicu pozivom UpdatePage, zatim oslobodite XObject, sačuvajte pomoću SaveAs i na kraju zatvorite izvorni dokument
procedure StampEveryPage(const ASource, AStamp, AOutput: string);
var
Dest, Stamp: TPdf;
XObject: TPdfXObject;
PageObj: FPDF_PAGEOBJECT;
M: TPdfMatrix;
RawM: FS_MATRIX;
I: Integer;
begin
Dest := TPdf.Create(nil);
Stamp := TPdf.Create(nil);
try
Dest.FileName := ASource;
Dest.Active := True;
Stamp.FileName := AStamp;
Stamp.Active := True;
if not (Dest.Active and Stamp.Active) then
raise Exception.Create('Could not open the input documents');
// 1. Capture the artwork once. Stamp is Active here.
XObject := Dest.CreateXObjectFromPage(Stamp, 0);
if XObject = nil then
raise Exception.Create('Could not capture the stamp page');
try
// 2. Place a copy on every page of Dest. PageNumber is 1-based.
for I := 1 to Dest.PageCount do
begin
Dest.PageNumber := I; // make page I current
PageObj := Dest.InsertFormObjectFromXObject(XObject);
if PageObj = nil then
Continue;
M := TPdfMatrix.Create;
try
M.Rotate(45); // diagonal watermark
M.Translate(150, 100); // nudge into position
RawM := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawM);
finally
M.Free;
end;
Dest.UpdatePage; // commit this page's edits
end;
finally
XObject.Free; // 3. free BEFORE Stamp closes
end;
// 4. Write the result while Dest is still open.
if not Dest.SaveAs(AOutput) then
raise Exception.Create('Could not save ' + AOutput);
finally
Stamp.Free; // source closes last
Dest.Free;
end;
end;
Oblik blokova try obezbeđuje pravilan rad. Unutrašnji finally oslobađa XObject pre nego što kontrola stigne do spoljnog finally koji oslobađa Stamp, pa se ručica uvek oslobađa dok je izvor živ, čak i ako izuzetak nastane usred petlje. Sa pravilnim ugnježđavanjem pravilo životnog veka sprovodi se samo
Pečatiranje je deo šireg skupa alata za izgradnju i uređivanje sadržaja stranice. Ako je pečat slika, tekst o pretvaranju slika u PDF pomoću PDFium-a pokazuje kako bitmapu prvo uneti u dokument. Ako uz vidljivi pečat želite da prenesete datoteku, tekst o PDF prilozima u Delphiju opisuje embedded-file stranu. Sve je deo PDFium Component za Delphi i C++Builder, zajedno sa API-jima za renderovanje, uređivanje i dokumente