Spajanje strani (page stitching) sestavi vsebino obstoječih strani na skupno platno, kar se razlikuje od spajanja datotek (file merging). Knjižnica losLab PDF Library implementira spajanje strani prek Form XObject in sestavljive predloge vodnih znakov za Delphi: metode StitchPageOverlay, StitchPagesSideBySide in StitchPagesVertically postavijo zajete strani v poljubnem merilu s polno vektorsko natančnostjo, razred TPDFlibCompositeWatermark pa z enim klicem naloži tekstovne in slikovne vodne znake čez obseg strani. Če dejansko potrebujete dodajanje celotnih strani iz več dokumentov v eno datoteko, to spada v družino spajanja, opisano v članku o hitrem spajanju PDF na ravni bajtov; ta članek pa govori o kombiniranju vsebine strani znotraj enega koordinatnega prostora
Zahteve, ki vodijo do tega, so vsakdanje in vztrajne. Šola želi dve strani izpita postaviti na en ležeči list, da bi za polovico zmanjšala porabo papirja. Pregledovalnik lokalizacije želi angleško stran in njen prevod vzporedno na eni strani, z možnostjo skupnega pomikanja. Pravna ekipa želi vsak osnutek označiti s tremi plastmi hkrati: logotipom v kotu, diagonalnim napisom DRAFT/ZAUPNO čez sredino in majhno vrstico v nogi, in sicer le na straneh od 1 do 3. Vsi trije primeri predstavljajo isto osnovno težavo, in sicer postavitev že oblikovane vsebine strani drugam in v drugi velikosti brez degradacije v bitno sliko
Zakaj se združevanje tokov vsebine PDF pokvari?
Naivno spajanje s kopiranjem toka /Contents izvorne strani na ciljno stran spodleti iz treh strukturnih razlogov. Prvič, koordinate trčijo: oba toka vsebine predvidevata svoje izhodišče v spodnjem levem kotu svojega MediaBox-a, zato se prilepljena vsebina znajde na vrhu obstoječe vsebine na napačnem mestu. Drugič, imena virov (resources) trčijo: vsaka stran razrešuje operaterje, kot sta /F1 Tf in /Im0 Do, glede na svoj slovar /Resources, dve strani pa rutinsko vežeta isto ime na različne pisave ali slike, zato se ena izmed njiju tiho upodobi z napačnim virom. Tretjič, surovo kopiranje toka vam sploh ne omogoča spreminjanja velikosti; najboljše, kar lahko storite, je le premik (translation)
Objekti Form XObject rešujejo vse tri težave hkrati in zato knjižnica losLab PDF Library na njih gradi svoje spajanje. ISO 32000-1 §8.10 opredeljuje Form XObject kot samostojen tok vsebine z lastnim slovarjem /Resources in lastnim koordinatnim prostorom, ki se postavi na stran s klicanjem njegovega imena pod katero koli aktivno matriko pretvorbe. Metoda CapturePage ovije celotno obstoječo stran, vsebino in vire, v objekt /Subtype /Form XObject, funkcije spajanja pa jo nato pozicionirajo z matriko cm: poljubno spreminjajo velikost, premikajo in rotirajo. Ker vir ostane vektorski, besedilo ostane besedilo, poti pa poti pri kateri koli stopnji povečave, kar je ista lastnost, zaradi katere so vektorska grafika in vzorci senčenja neodvisni od ločljivosti. Nič se ne rastrira, izoliran slovar virov pa pomeni, da se zajeta stran ne more boriti s ciljno stranjo za ime /F1
Kako združiti dve strani PDF na eno stran?
Metoda StitchPagesSideBySide(Page1, Page2, Gap, TargetHeight) je neposreden odgovor: zajame obe izvorni strani, ustvari novo, širšo stran in postavi oba zajema levo in desno z vmesnim razmikom (gutter), pri čemer se vsaka prilagodi višini TargetHeight. Funkcija vrne številko nove strani (1-bazirano) ali 0 ob neuspehu, obe izvorni strani pa se v okviru operacije zajameta in skrijeta. Primer izpita z dvema stranema na list je delo treh vrstic
var
Lib: TPDFlib;
NewPage: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('exam.pdf', '');
// Strani 1 in 2 na eni novi, širši strani z 18-točkovnim razmikom,
// obe prilagojeni na izhodno višino 595 točk (A4 ležeče)
NewPage := Lib.StitchPagesSideBySide(1, 2, 18, 595);
if NewPage > 0 then
Lib.SaveToFile('exam-2up.pdf');
finally
Lib.Free;
end;
end;
Za navpično sestavljanje metoda StitchPagesVertically(PageRanges, Gap, TargetWidth) zloži celoten obseg strani v eno visoko stran v obliki plakata. PageRanges sprejme standardno sintakso obsega, kot je '1-3,5'. Vsaka izvorna stran se spremeni tako, da njena širina ustreza TargetWidth, zato je prispevek k višini vsake strani njena lastna višina, pomnožena z njenim lastnim faktorjem merila, višina izhodne strani pa je vsota teh višin in enega razmika Gap med vsakim parom. To je oblika, ki jo želite za dokumente s stalnim pomikanjem ali dolga potrdila, sestavljena iz virov velikosti pisma (letter-size)
Kako prekrivati eno stran čez drugo?
Metoda StitchPageOverlay(SourcePage, TargetPage, Left, Top, Width, Height, Opacity) nariše eno obstoječo stran čez drugo obstoječo stran, namesto da bi ustvarila novo. Izvorna stran se zajame kot Form XObject in izriše na cilj v pravokotnik, ki ga določite, s prosojnostjo Opacity od 0 do 1 (1 pomeni neprosojno). Širina in višina sta prosta parametra, zato se postavitev raztegne, če se vaše razmerje razlikuje od lastnega razmerja stranic izvorne strani; posredujte proporcionalen par, ko želite nepopačeno sličico vira. Tipična uporaba je dokument, katerega odobritveni blok se vzdržuje kot lastna stran in se odtisne na naslovnico
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('report.pdf', '');
// Stran 5 vsebuje odobritveni blok; postavite ga na stran 1
// v polje 260 x 180 s 45-odstotno prosojnostjo. Stran 5 se porabi:
// zajame se in skrije, tako da strani 6..n postanejo 5..n-1
if Lib.StitchPageOverlay(5, 1, 300, 500, 260, 180, 0.45) = 1 then
Lib.SaveToFile('report-approved.pdf');
finally
Lib.Free;
end;
end;
Kaj se zgodi s številkami strani po zajemu?
Metoda CapturePage porabi izvorno stran: njena vsebina se premakne v XObject, zdaj prazna stran pa se skrije, zato se vsaka stran s številko nad izvorno takoj premakne za eno mesto navzdol. To je ena najpomembnejših semantik v celotnem API-ju za spajanje. Knjižnica losLab PDF Library ureja to aritmetiko znotraj vsakega klica spajanja, StitchPageOverlay prilagodi ciljni indeks, ko je cilj nad virom, StitchPagesSideBySide pa najprej zajame višje oštevilčeno stran, tako da je indeks drugega zajema še vedno veljaven. Vendar mora vaša lastna okoliška koda upoštevati ta premik med klici. Dve pravili ohranjata kodo za večkratno spajanje pravilno: zabeležite poljubne velikosti ali številke strani, ki jih boste potrebovali, pred prvim zajemom, saj se pozneje spremenijo. In ko sami v zanki zajemate več strani, izvajajte iteracijo v padajočem vrstnem redu strani, tako da skritje poznejše strani nikoli ne moti indeksa prejšnje strani, ki je še niste zajeli
Kako nanesti večplastni vodni znak na obseg strani?
Plast predlog vodnih znakov v enoti PDFlibWatermark.pas pretvori logiko "definiraj enkrat, uporabi na več straneh" v objekte. Razred TPDFlibWatermarkBase prinaša tisto, kar si delijo vsi vodni znaki: devettočkovni položaj Position (od wpTopLeft do wpBottomRight, plus wpCustom, ki uporablja surove koordinate X/Y), kot Angle v stopinjah, prosojnost Opacity od 0 do 1 in niz obsega strani PageRange, kjer prazno pomeni vse strani. TPDFlibTextWatermark doda besedilo, ID pisave (privzeto Helvetica), velikost in barvo RGB; TPDFlibImageWatermark doda ID slike iz prej dodane slike ter širino in višino postavitve. Razred TPDFlibCompositeWatermark drži seznam pod-vodnih znakov in jih nanaša rekurzivno, klic TPDFlib.ApplyWatermark pa je vstopna točka z enim klicem, ki upošteva lasten obseg strani predloge in vrne število označenih strani. Trikratni pravni žig izgleda takole
var
Lib: TPDFlib;
Logo: TPDFlibImageWatermark;
Diagonal, Footer: TPDFlibTextWatermark;
Stack: TPDFlibCompositeWatermark;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('draft.pdf', '');
Stack := TPDFlibCompositeWatermark.Create;
try
Logo := TPDFlibImageWatermark.Create;
Logo.ImageID := Lib.AddImageFromFile('logo.png', 0);
Logo.Position := wpTopRight;
Logo.Width := 90;
Logo.Height := 32;
Stack.Add(Logo);
Diagonal := TPDFlibTextWatermark.Create;
Diagonal.Text := 'CONFIDENTIAL';
Diagonal.Position := wpCenter;
Diagonal.Angle := 45;
Diagonal.FontSize := 60;
Diagonal.Opacity := 0.15;
Stack.Add(Diagonal);
Footer := TPDFlibTextWatermark.Create;
Footer.Text := 'Internal review copy';
Footer.Position := wpBottomCenter;
Footer.FontSize := 9;
Footer.Opacity := 0.6;
Stack.Add(Footer);
Stack.PageRange := '1-3'; // otroci pododedujejo ta obseg
Lib.ApplyWatermark(Stack);
Lib.SaveToFile('draft-marked.pdf');
finally
Stack.Free; // sestavljeni objekt (composite) sprosti svoje otroke
end;
finally
Lib.Free;
end;
end;
Vredno je izpostaviti dve podrobnosti sestavljanja
Dedovanje obsega je enosmerno in neporušitveno: otrok s prazno lastnostjo PageRange med izvajanjem metode Apply začasno pododeduje obseg sestavljenega znaka, medtem ko otrok, ki nastavi svoj obseg, tega ohrani, tako da lahko nogo (footer) pripnete na vsako stran, medtem ko diagonalni žig pokriva le del z osnutkom. Lastništvo pa sledi sestavljenemu objektu: destruktor TPDFlibCompositeWatermark.Destroy sprosti svoje otroke, tako da sprostite le sestavljeni objekt in nič drugega, kot v zgornjem primeru
Omejitve, ki jih je dobro poznati
Veljata dve stvarni omejitvi, obe pa izhajata iz načina delovanja mehanizma. Prvič, zajem ovije le tok vsebine in vire strani. Anotacije, povezave, obrazci in komentarji živijo kot ločeni objekti v slovarju strani in ne v toku vsebine, zato ne potujejo v XObject; spojen tloris prenaša vidno vsebino strani, ne pa interaktivne plasti svojih virov. Drugič, devettočkovno pozicioniranje besedila je le približek: brez celotnega pogona za postavitev knjižnica ne more poznati natančne izrisane širine niza, zato pri razreševanju položajev, kot je wpCenter, oceni polje z besedilom na 40% širine strani. To je dovolj natančno za centriranje kratke oznake, za slikovno točno postavitev dolgih nizov pa preklopite na wpCustom in sami izračunajte koordinate
Merilo tukaj ni praktična omejitev, saj zajem in spajanje manipulirata z referencami objektov in ne z vnovičnim kodiranjem vsebine strani; če so vaši viri veliki več sto megabajtov, velja pred spajanjem uporabiti isto disciplino, opisano v vodiču za spajanje in razdeljevanje velikih datotek z neposrednim dostopom. Funkcije spajanja in razredi predlog vodnih znakov, prikazani tukaj, se prinašajo v trenutni knjižnici losLab PDF Library za Delphi, C# in VB.NET, skupaj z nižjenivojskimi primitivi CapturePage, DrawCapturedPage in DrawCapturedPageMatrix, ko potrebujete postavitev, ki je trije vnaprej pripravljeni klici spajanja ne pokrivajo