Îmbinarea paginilor (page stitching) compune conținutul paginilor existente pe o pânză comună, ceea ce reprezintă o operațiune diferită de îmbinarea fișierelor. losLab PDF Library implementează îmbinarea paginilor prin Form XObject și șabloane de watermark compozibile pentru Delphi: StitchPageOverlay, StitchPagesSideBySide și StitchPagesVertically plasează paginile capturate la orice scară cu fidelitate vectorială completă, iar TPDFlibCompositeWatermark suprapune watermark-uri de text și imagine peste un interval de pagini într-un singur apel. Dacă ceea ce aveți nevoie de fapt este să adăugați pagini întregi din mai multe documente într-un singur fișier, aceasta aparține familiei de îmbinare (merge), acoperită în articolul despre îmbinarea rapidă a PDF-urilor la nivel de octet; acest articol se referă la combinarea conținutului paginii în cadrul unui singur spațiu de coordonate
Cerințele care duc aici sunt obișnuite și persistente. O școală dorește ca două pagini de examen să fie impuse pe o singură foaie tip vedere (landscape) pentru a reduce consumul de hârtie la jumătate. Un recenzent de localizare dorește pagina în limba engleză și traducerea acesteia alăturate pe o singură pagină, derulându-se împreună. O echipă juridică dorește ca fiecare ciornă să fie ștampilată cu trei straturi deodată: un logo în colț, un text diagonal CONFIDENTIAL în mijloc și o linie mică de subsol, doar pe paginile 1 până la 3. Toate cele trei reprezintă aceeași problemă de bază, și anume plasarea conținutului paginii deja cules în altă parte, la o altă dimensiune, fără a-l degrada într-o imagine bitmap
De ce eșuează concatenarea fluxurilor de conținut PDF?
Îmbinarea simplistă, adică copierea fluxului /Contents al paginii sursă în pagina țintă, eșuează din trei motive structurale. În primul rând, coordonatele intră în conflict: ambele fluxuri de conținut își asumă propria origine în colțul din stânga-jos al propriului lor MediaBox, astfel încât conținutul lipit ajunge deasupra conținutului existent în locul greșit. În al doilea rând, numele de resurse intră în conflict: fiecare pagină rezolvă operatori precum /F1 Tf și /Im0 Do în raport cu propriul său dicționar /Resources, iar două pagini asociază frecvent același nume la fonturi sau imagini diferite, astfel încât una dintre ele se randează silențios cu resursa greșită. În al treilea rând, o copiere brută a fluxului nu vă oferă deloc scalare; cel mai bun lucru pe care îl puteți face este traducerea (deplasarea)
Obiectele Form XObject rezolvă toate cele trei probleme simultan
Obiectele Form XObject rezolvă toate cele trei probleme simultan, și de aceea losLab PDF Library își construiește îmbinarea pe baza lor. ISO 32000-1 §8.10 definește un form XObject ca fiind un flux de conținut autonom cu propriul său dicționar /Resources și propriul său spațiu de coordonate, plasat într-o pagină prin invocarea numelui său sub orice matrice de transformare curentă activă. CapturePage împachetează o întreagă pagină existentă, conținut plus resurse, într-un obiect XObject de tip /Subtype /Form, iar funcțiile de îmbinare îl poziționează apoi cu o matrice cm: scalare, translație, rotație, după bunul plac. Deoarece sursa rămâne vectorială, textul rămâne text, iar căile rămân căi la orice nivel de zoom, aceeași proprietate care face ca grafica vectorială și modelele de umbrire (shading patterns) să fie independente de rezoluție. Nimic nu este rasterizat, iar dicționarul izolat de resurse înseamnă că pagina capturată nu poate intra în conflict cu pagina țintă pentru numele /F1
Cum se combină două pagini PDF pe o singură pagină?
StitchPagesSideBySide(Page1, Page2, Gap, TargetHeight) este răspunsul direct: capturează ambele pagini sursă, creează o nouă pagină mai lată și plasează cele două capturi la stânga și la dreapta cu un spațiu de delimitare (gutter) între ele, fiecare scalată pentru a se potrivi cu TargetHeight. Funcția returnează noul număr de pagină (baza 1) sau 0 în caz de eșec, iar ambele pagini sursă sunt capturate și ascunse ca parte a operațiunii. Cazul examenului cu două pagini pe coală este o sarcină de trei linii de cod
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;
Pentru compoziția verticală, StitchPagesVertically(PageRanges, Gap, TargetWidth) stivuiește un întreg interval de pagini într-o singură pagină înaltă, în stil poster. PageRanges acceptă sintaxa standard pentru intervale, cum ar fi '1-3,5'. Fiecare pagină sursă este scalată astfel încât lățimea sa să se potrivească cu TargetWidth, astfel încât contribuția de înălțime a fiecărei pagini este propria sa înălțime înmulțită cu propriul său factor de scalare, iar înălțimea paginii de ieșire este suma acestora plus un Gap între fiecare pereche. Aceasta este forma pe care o doriți pentru documente de evaluare cu derulare continuă sau chitanțe lungi asamblate din surse de dimensiune letter
Cum se suprapune o pagină peste alta?
StitchPageOverlay(SourcePage, TargetPage, Left, Top, Width, Height, Opacity) desenează o pagină existentă peste o altă pagină existentă, în loc să creeze una nouă. Pagina sursă este capturată ca un form XObject și desenată pe țintă în dreptunghiul pe care îl furnizați, cu Opacity de la 0 la 1 (1 înseamnă opac). Lățimea și înălțimea sunt parametri liberi, astfel încât plasarea se întinde dacă raportul dvs. diferă de raportul de aspect propriu al paginii sursă; transmiteți o pereche proporțională când doriți o miniatură nedistorsionată a sursei. O utilizare tipică este un document al cărui bloc de aprobare este menținut ca o pagină proprie și aplicat pe copertă
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;
Ce se întâmplă cu numerele de pagină după o captură?
CapturePage consumă pagina sursă: conținutul acesteia este mutat în obiectul XObject, iar pagina acum goală este ascunsă, astfel încât fiecare pagină numerotată deasupra sursei se deplasează imediat în jos cu o unitate. Aceasta este cea mai importantă semantică din întregul API de îmbinare. losLab PDF Library gestionează aritmetica în cadrul fiecărui apel de îmbinare, StitchPageOverlay ajustează indexul țintă atunci când ținta se află deasupra sursei, iar StitchPagesSideBySide capturează mai întâi pagina cu numărul mai mare, astfel încât indexul celei de-a doua capturi să fie în continuare valabil, însă propriul dvs. cod extern trebuie să țină cont de această deplasare între apeluri. Două reguli mențin corect codul cu îmbinări multiple. Înregistrați orice dimensiuni de pagină sau numere de pagină de care veți avea nevoie înainte de prima captură, deoarece acestea se schimbă ulterior. Iar când capturați singur mai multe pagini într-o buclă, iterați în ordine descrescătoare a paginilor, astfel încât ascundea unei pagini ulterioare să nu perturbe niciodată indexul unei pagini anterioare pe care nu ați capturat-o încă
Cum se aplică un watermark pe mai multe straturi pe un interval de pagini?
Stratul șablonului de watermark din PDFlibWatermark.pas transformă conceptul „definește o dată, aplică pe mai multe pagini” în obiecte. TPDFlibWatermarkBase transportă ceea ce partajează fiecare watermark: o poziție Position în nouă puncte (de la wpTopLeft la wpBottomRight, plus wpCustom care folosește coordonate X/Y brute), un unghi Angle în grade, o opacitate Opacity de la 0 la 1 și un șir de caractere PageRange pentru intervalul de pagini, unde gol înseamnă toate paginile. TPDFlibTextWatermark adaugă text, un ID de font (implicit Helvetica), dimensiune și o culoare RGB; TPDFlibImageWatermark adaugă un ID de imagine dintr-o imagine adăugată anterior, plus o lățime și înălțime de plasare. TPDFlibCompositeWatermark conține o listă de sub-watermark-uri și le aplică în mod recursiv, iar TPDFlib.ApplyWatermark este punctul de intrare cu un singur apel care respectă propriul interval de pagini al șablonului și returnează numărul de pagini ștampilate. Ștampila legală pe trei straturi arată astfel
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;
Merită detaliate două aspecte ale compoziției. Moștenirea intervalului este unidirecțională și nedistructivă: un copil cu un PageRange gol moștenește temporar intervalul compozitului în timpul Apply, while a child that sets its own range keeps it, so you can pin the footer to every page while the diagonal stamp covers only the draft section. Iar dreptul de proprietate urmează compozitul: TPDFlibCompositeWatermark.Destroy își eliberează copiii, deci eliberați compozitul o singură dată și nimic altceva, ca în exemplul de mai sus
Limite care merită cunoscute
Se aplică două limitări oneste, ambele decurgând din modul în care funcționează mecanismul. În primul rând, o captură împachetează doar fluxul de conținut al paginii și resursele acesteia. Adnotările, linkurile, câmpurile de formular și comentariile există ca obiecte separate în dicționarul paginii, nu în fluxul de conținut, prin urmare nu călătoresc în obiectul XObject; o machetă îmbinată transportă conținutul vizibil al paginii, dar nu și stratul interactiv al surselor sale. În al doilea rând, poziționarea textului în nouă puncte este o aproximare: fără un motor complet de machetare, biblioteca nu poate cunoaște lățimea exactă randată a unui șir, așa că estimează caseta de text la 40% din lățimea paginii atunci când rezolvă poziții precum wpCenter. Acest lucru este suficient de precis pentru centrarea unei etichete scurte, dar pentru plasarea exactă la nivel de pixel a șirurilor lungi, comutați la wpCustom și calculați singur coordonatele
Scalarea nu reprezintă o constrângere practică aici, deoarece capturarea și îmbinarea manipulează referințe de obiecte în loc să re-codifice conținutul paginii; dacă sursele dvs. au sute de megabytes, se aplică aceeași disciplină descrisă în ghidul pentru îmbinarea și divizarea fișierelor mari cu acces direct înainte de a îmbina. Funcțiile de îmbinare și clasele de șabloane de watermark prezentate aici sunt incluse în versiunea curentă a losLab PDF Library pentru Delphi, C# și VB.NET, alături de primitivele de nivel inferior CapturePage, DrawCapturedPage și DrawCapturedPageMatrix când aveți nevoie de o machetă pe care cele trei apeluri de îmbinare predefinite nu o acoperă