Puslapių sujungimas (stitching) sukomponuoja esamų puslapių turinį bendroje drobėje, o tai skiriasi nuo failų sujungimo (merging). „losLab PDF Library“ palaiko Form XObject puslapių sujungimą ir sujungiamus vandens ženklų šablonus Delphi aplinkoje: „StitchPageOverlay“, „StitchPagesSideBySide“ ir „StitchPagesVertically“ išdėsto užfiksuotus puslapius bet kokiu masteliu su visišku vektoriniu tikslumu, o „TPDFlibCompositeWatermark“ vienu iškvietimu uždeda tekstinius ir vaizdo vandens ženklus tam tikrame puslapių diapazone. Jei jums iš tikrųjų reikia pridėti ištisus puslapius iš kelių dokumentų į vieną failą, tai yra sujungimo (merge) šeima, aprašyta straipsnyje apie greitą baitų lygio PDF sujungimą; šis straipsnis yra apie puslapio turinio sujungimą vienoje koordinačių erdvėje
Prašymai, kurie atveda čia, yra kasdieniški ir pasikartojantys. Mokykla nori sutalpinti du egzamino puslapius viename gulsčiame lape, kad perpus sumažintų popieriaus sunaudojimą. Lokalizavimo tikrintojas nori matyti anglišką puslapį ir jo vertimą greta viename puslapyje, slenkant juos kartu. Teisininkų komanda nori, kad kiekvienas juodraštis būtų pažymėtas trimis sluoksniais vienu metu: logotipu kampe, įstrižu užrašu CONFIDENTIAL viduryje ir nedidele porštine eilute, tik nuo 1 iki 3 puslapio. Visos trys yra ta pati esminė problema — jau sumaketuoto puslapio turinio patalpinimas kitur ir kitu dydžiu, nepabloginant jo iki bitų žemėlapio
Kodėl sugenda PDF turinio srautų sujungimas?
Paprastas sujungimas, kai šaltinio puslapio „/Contents“ srautas tiesiog nukopijuojamas į tikslinį puslapį, nepavyksta dėl trijų struktūrinių priežasčių. Pirma, koordinačių susidūrimas: abu turinio srautai laiko savo koordinačių pradžia savo „MediaBox“ apatinį kairįjį kampą, zodžiu, įklijuotas turinys patenka ant esamo turinio netinkamoje vietoje. Antra, resursų pavadinimų susidūrimas: kiekvienas puslapis sprendžia operatorius, tokius kaip „/F1 Tf“ ir „/Im0 Do“, pagal savo „/Resources“ žodyną, o du puslapiai paprastai susieja tą patį pavadinimą su skirtingais šriftais ar vaizdais, todėl vienas iš jų tyliai atvaizduojamas su netinkamu resursu. Trečia, tiesioginis srauto kopijavimas nesuteikia jokio mastelio keitimo — geriausia, ką galite padaryti, tai tiesiog perkelti (translate)
Form XObjects išsprendžia visas tris problemas iš karto
Form XObjects išsprendžia visas tris problemas iš karto, todėl „losLab PDF Library“ puslapių sujungimą grindžia jais. ISO 32000-1 §8.10 apibrėžia formos XObject kaip savarankišką turinio srautą su savo „/Resources“ žodynu ir savo koordinačių erdve, patalpinamą puslapyje iškviečiant jo pavadinimą bet kurioje aktyvioje transformacijos matricoje. „CapturePage“ apgaubia visą esamą puslapį (turinį ir resursus) į „/Subtype /Form“ XObject, o sujungimo funkcijos nustato jo padėtį su „cm“ matrica: keičia mastelį, perkelia, suka, kaip norima. Kadangi šaltinis išlieka vektorinis, tekstas lieka tekstu, o keliai — keliais bet kokiu masteliu, o tai yra ta pati savybė, kuri padaro vektorinę grafiką ir šešėliavimo šablonus nepriklausomus nuo raiškos. Niekas nėra rasterizuojama, o izoliuotas resursų žodynas reiškia, kad užfiksuotas puslapis negali konfliktuoti su tiksliniu puslapiu dėl pavadinimo „/F1“
Kaip sujungti du PDF puslapius į vieną puslapį?
Tiesioginis atsakymas yra „StitchPagesSideBySide(Page1, Page2, Gap, TargetHeight)“: ši funkcija užfiksuoja abu šaltinio puslapius, sukuria naują platesnį puslapį ir išdėsto abu užfiksuotus vaizdus kairėje bei dešinėje su tarpeliu tarp jų, kiekvieną sumažinant ar padidinant pagal „TargetHeight“. Funkcija grąžina naujo puslapio numerį (pradedant nuo 1) arba 0 nesėkmės atveju, o abu šaltinio puslapiai užfiksuojami ir paslepiami atliekant šį veiksmą. Egzamino užduoties su dviem puslapiais lape atveju tai yra trijų eilučių darbas
var
Lib: TPDFlib;
NewPage: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('exam.pdf', '');
// Pages 1 and 2 on one new wider page with an 18pt gutter,
// both scaled to a 595pt output height (A4 landscape)
NewPage := Lib.StitchPagesSideBySide(1, 2, 18, 595);
if NewPage > 0 then
Lib.SaveToFile('exam-2up.pdf');
finally
Lib.Free;
end;
end;
Vertikaliam sujungimui „StitchPagesVertically(PageRanges, Gap, TargetWidth)“ sudeda visą puslapių diapazoną į vieną aukštą, plakato stiliaus puslapį. Parametras „PageRanges“ naudoja standartinę diapazono sintaksę, pvz., „'1-3,5'“. Kiekvienas šaltinio puslapis yra masteliuojamas taip, kad jo plotis atitiktų „TargetWidth“, todėl kiekvieno puslapio indėlis į bendrą aukštį yra jo paties aukštis, padaugintas iš jo paties mastelio koeficiento, o galutinis puslapio aukštis yra šių aukščių suma plius po vieną „Gap“ tarp kiekvienos poros. Tai yra formatas, kurio norite nuolatinio slinkimo peržiūros dokumentams arba ilgiems kvitams, sudarytiems iš laiško dydžio šaltinių
Kaip uždėti vieną puslapį ant kito?
Funkcija „StitchPageOverlay(SourcePage, TargetPage, Left, Top, Width, Height, Opacity)“ nupiešia vieną esamą puslapį ant kito esamo puslapio, užuot kūrusi naują. Šaltinio puslapis užfiksuojamas kaip formos XObject ir nupiešiamas ant tikslo jūsų nurodytame stačiakampyje su „Opacity“ reikšme nuo 0 iki 1 (1 reiškia nepermatomą). Plotis ir aukštis yra laisvi parametrai, zodžiu, uždėtas vaizdas išsitemps, jei jūsų santykis skirsis nuo paties šaltinio puslapio kraštinių santykio; perduokite proporcingą porą, kai norite neiškraipytos šaltinio miniatiūros. Įprastas naudojimo atvejis yra dokumentas, kurio patvirtinimo blokas palaikomas kaip atskiras puslapis ir uždedamas ant viršelio
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('report.pdf', '');
// Page 5 holds the approval block; place it on page 1
// in a 260 x 180 box at 45% opacity. Page 5 is consumed:
// it is captured and hidden, so pages 6..n become 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;
Kas nutinka puslapių numeriams po užfiksavimo?
„CapturePage“ sunaudoja šaltinio puslapį: jo turinys perkeliamas į XObject, o dabar jau tuščias puslapis yra paslepiamas, todėl kiekvienas puslapis, kurio numeris didesnis už šaltinį, iškart pasislenka vienu žemyn. Tai pati svarbiausia semantika visoje puslapių sujungimo API. „losLab PDF Library“ apdoroja aritmetiką kiekviename sujungimo iškvietime: „StitchPageOverlay“ sureguliuoja tikslo indeksą, kai tikslas yra virš šaltinio, o „StitchPagesSideBySide“ pirmiausia užfiksuoja didesnio numerio puslapį, kad antrojo užfiksavimo indeksas išliktų galiojantis, tačiau jūsų pačių kodas turi atsižvelgti į šį poslinkį tarp iškvietimų. Dvi taisyklės padeda išlaikyti teisingą kelis kartus sujungiantį kodą. Įrašykite bet kokius puslapių dydžius ar puslapių numerius, kurių jums prireiks, prieš pirmąjį užfiksavimą, nes vėliau jie pasikeis. O kai patys fiksuojate kelis puslapius cikle, iteruokite mažėjančia puslapių tvarka, kad vėlesnio puslapio paslėpimas niekada nesujauktų ankstesnio puslapio, kurio dar neužfiksavote, indekso
Kaip uždėti daugiasluoksnį vandens ženklą puslapių diapazonui?
Vandens ženklų šablonų sluoksnis faile „PDFlibWatermark.pas“ paverčia principą „apibrėžk vieną kartą, taikyk daugeliui puslapių“ objektais. „TPDFlibWatermarkBase“ perneša tai, kuo dalijasi visi vandens ženklai: devynių taškų padėtį „Position“ („wpTopLeft“ iki „wpBottomRight“ plius „wpCustom“, kuri naudoja neapdorotas X/Y koordinates), kampą „Angle“ laipsniais, nepermatomumą „Opacity“ nuo 0 iki 1 ir „PageRange“ eilutę, kur tuščia reiškia visus puslapius. „TPDFlibTextWatermark“ prideda tekstą, šrifto ID (numatytasis yra „Helvetica“), dydį ir RGB spalvą; „TPDFlibImageWatermark“ prideda vaizdo ID iš anksčiau pridėto vaizdo bei patalpinimo plotį ir aukštį. „TPDFlibCompositeWatermark“ laiko sub-vandens ženklų sąrašą ir taiko juos rekursyviai, o „TPDFlib.ApplyWatermark“ yra vieno iškvietimo pradžios taškas, kuris gerbia paties šablono puslapių diapazoną ir grąžina pažymėtų puslapių skaičių. Trijų sluoksnių teisinė žyma atrodo taip
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'; // children inherit this range
Lib.ApplyWatermark(Stack);
Lib.SaveToFile('draft-marked.pdf');
finally
Stack.Free; // composite frees its children
end;
finally
Lib.Free;
end;
end;
Verta detalizuoti dvi kompozicijos detales. Diapazono paveldėjimas yra vienpusis ir nesugriaunantis: vaikas su tuščia „PageRange“ laikinai paveldi kompozito diapazoną vykdant „Apply“, o vaikas, kuris nustato savo diapazoną, jį išlaiko, todėl galite prisegti porštinę eilutę prie kiekvieno puslapio, kol įstrižas spaudas dengia tik juodraščio dalį. Taip pat nuosavybės teisė seka paskui kompozitą: „TPDFlibCompositeWatermark.Destroy“ atlaisvina savo vaikus, todėl atlaisvinę kompozitą vieną kartą atlaisvinsite ir visa kita, kaip parodyta aukščiau pateiktame pavyzdyje
Ribos, kurias verta žinoti
Taikomos dvi tikros ribos, kurios išplaukia iš to, kaip veikia šis mechanizmas. Pirma, užfiksavimas apima tik puslapio turinio srautą ir resursus. Anotacijos, nuorodos, formos laukai ir komentarai gyvena kaip atskiri objektai puslapio žodyne, o ne turinio sraute, todėl jie nepersikelia į XObject; sujungtas išdėstymas perneša matomą puslapio turinį, bet ne interaktyvų jo šaltinių sluoksnį. Antra, devynių taškų teksto padėties nustatymas yra apytikslis: be pilno maketavimo variklio biblioteka negali žinoti tikslaus atvaizduojamo eilutės pločio, todėl sprendžiant tokias padėtis kaip „wpCenter“, ji įvertina teksto laukelį 40% puslapio pločio. Tai pakankamai tikslu centruojant trumpą etiketę, tačiau norint tikslaus taškų lygiu ilgų eilučių patalpinimo, pereikite prie „wpCustom“ ir patys apskaičiuokite koordinates
Mastelis čia nėra praktinis apribojimas, nes fiksavimas ir sujungimas manipuliuoja objektų nuorodomis, o ne iš naujo koduoja puslapio turinį; jei jūsų šaltiniai užima šimtus megabaitų, prieš jungiant taikoma ta pati tvarka, aprašyta didelio failo sujungimo ir skaidymo tiesiogine prieiga vadove. Čia parodytos sujungimo funkcijos ir vandens ženklų šablonų klasės tiekiamos dabartinėje „losLab PDF Library“ Delphi, C# ir VB.NET aplinkoms kartu su žemesnio lygio primityvais „CapturePage“, „DrawCapturedPage“ ir „DrawCapturedPageMatrix“, kai jums reikia maketo, kurio neapima trys paruošti sujungimo iškvietimai