Sidsammanfogning komponerar innehållet på befintliga sidor på en gemensam rityta, vilket är en annan operation än att slå samman filer. losLab PDF Library implementerar Form XObject-sidsammanfogning och sammansättningsbara vattenstämpelmallar för Delphi: StitchPageOverlay, StitchPagesSideBySide och StitchPagesVertically placerar fångade sidor i valfri skala med full vektorprecision, och TPDFlibCompositeWatermark staplar text- och bildvattenstämplar över ett sidintervall i ett enda anrop. Om det du faktiskt behöver är att lägga till hela sidor från flera dokument till en enda fil, handlar det om sammanfogning, vilket behandlas i artikeln om snabb PDF-sammanfogning på bytesnivå; den här artikeln handlar om att kombinera sidinnehåll inom ett koordinatutrymme
Förfrågningarna som leder hit är vardagliga men envisa. En skola vill ha två provsidor placerade på ett liggande ark för att halvera pappersförbrukningen. En lokaliseringsgranskare vill ha den engelska sidan och dess översättning sida vid sida på en enda sida, rullade tillsammans. En juridisk avdelning vill ha varje utkast stämplat med tre skikt samtidigt: en logotyp i hörnet, en diagonal CONFIDENTIAL över mitten och en liten sidfotsrad, endast på sidorna 1 till 3. Alla tre är samma underliggande problem, nämligen att placera redan typsatt sidinnehåll någon annanstans i en annan storlek utan att göra om det till en bitmapp
Varför bryts sammanfogning av PDF-innehållsströmmar?
Enkel sammanfogning, där man kopierar källsidans /Contents-ström till målsidan, misslyckas av tre strukturella skäl. För det första krockar koordinaterna: båda innehållsströmmarna antar sitt eget ursprung längst ner till vinkel i sin egen MediaBox, så det inklistrade innehållet hamnar ovanpå det befintliga innehållet på fel ställe. För det andra krockar resursnamnen: varje sida löser operatorer som /F1 Tf och /Im0 Do mot sin egen /Resources-ordlista, och två sidor binder rutinmässigt samma namn till olika typsnitt eller bilder, så att en av dem tyst renderas med fel resurs. För det tredje ger en rå kopiering av strömmen ingen skalning alls; det bästa du kan göra är att flytta
Hur kombinerar du två PDF-sidor på en enda sida?
StitchPagesSideBySide(Page1, Page2, Gap, TargetHeight) är det direkta svaret: funktionen fångar båda källsidorna, skapar en ny bredare sida och placerar de två fångade sidorna till vänster och höger med en marginal emellan, där varje sida skalas för att passa TargetHeight. Funktionen returnerar det nya sidnumret (1-baserat) eller 0 vid misslyckande, och båda källsidorna fångas och döljs som en del av operationen. Fallet med två provsidor per ark är ett jobb på tre rader
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;
För vertikal sammansättning staplar StitchPagesVertically(PageRanges, Gap, TargetWidth) ett helt sidintervall på en enda hög sida i affischstil. PageRanges tar standardintervallssyntaxen såsom '1-3,5'. Varje källsida skalas så att dess bredd matchar TargetWidth, vilket innebär att höjdbidraget från varje sida är dess egen höjd multiplicerad med dess egen skalfaktor, och den resulterande sidhöjden är summan av dessa plus ett Gap (mellanrum) mellan varje par. Detta är den form du vill ha för dokument med kontinuerlig rullning vid granskning eller långa kvitton sammanställda från källor i Letter-storlek
Hur lägger du en sida som ett överlägg på en annan?
StitchPageOverlay(SourcePage, TargetPage, Left, Top, Width, Height, Opacity) ritar en befintlig sida på en annan befintlig sida, snarare än att skapa en ny. Källsidan fångas som ett form XObject och ritas på målsidan i den rektangel du anger, med en opacitet (Opacity) från 0 till 1 (där 1 är helt opakt). Bredd och höjd är fria parametrar, så placeringen sträcks ut om dina proportioner skiljer sig från källsidans egna bildförhållande; skicka ett proportionellt par om du vill ha en oförvrängd miniatyrbild av källan. En typisk användning är ett dokument där godkännandeblocket sparas som en egen sida och stämplas på omslaget
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;
Vad händer med sidnumren efter en fångst?
CapturePage förbrukar källsidan: dess innehåll flyttas till XObject och den nu tomma sidan döljs, så att alla sidor med ett högre nummer än källsidan omedelbart skiftar nedåt med ett steg. Detta är den enskilt viktigaste semantiken i hela sidsammanfognings-API:et. losLab PDF Library hanterar aritmetiken inuti varje sammanfogningsanrop, StitchPageOverlay justerar målindexet när målet ligger ovanför källan och StitchPagesSideBySide fångar sidan med det högre numret först så att indexet för den andra fångsten fortfarande är giltigt, men din egen omgivande kod måste ta hänsyn till skiftningen mellan anropen. Två regler håller kod med flera sammanfogningar korrekt: registrera alla sidstorlekar eller sidnummer du kommer att behöva innan den första fångsten, eftersom de ändras efteråt. Och när du själv fångar flera sidor i en loop, iterera i fallande sidordning så att döljandet av en senare sida aldrig stör indexet för en tidigare som du ännu inte har fångat
Hur tillämpar du en vattenstämpel med flera skikt på ett sidintervall?
Mallen för vattenstämplar i PDFlibWatermark.pas förvandlar "definiera en gång, tillämpa på många sidor" till objekt. TPDFlibWatermarkBase bär på det som alla vattenstämplar delar: en niopunkts position (Position, från wpTopLeft till wpBottomRight, samt wpCustom som använder råa X/Y-koordinater), en vinkel (Angle) i grader, en opacitet (Opacity) från 0 till 1, samt en sidintervallssträng (PageRange) där tomt innebär alla sidor. TPDFlibTextWatermark lägger till text, ett typsnitts-ID (med Helvetica som standard), storlek och en RGB-färg; TPDFlibImageWatermark lägger till ett bild-ID från en tidigare tillagd bild plus en placeringsbredd och höjd. TPDFlibCompositeWatermark innehåller en lista med delvattenstämplar och tillämpar dem rekursivt, och TPDFlib.ApplyWatermark är ingångspunkten med ett enda anrop som respekterar mallens eget sidintervall och returnerar antalet stämplade sidor. Den juridiska stämpeln med tre skikt ser ut så här
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;
Två kompositionsdetaljer är värda att förtydliga. Intervallsarv är envägs och icke-förstörande: ett underordnat objekt med ett tomt PageRange ärver tillfälligt sammansättningens intervall under Apply, medan ett underordnat objekt som ställer in sitt eget intervall behåller det, så att du kan fästa sidfoten på varje sida medan den diagonala stämpeln endast täcker utkastsektionen. Och äganderätten följer sammansättningen: TPDFlibCompositeWatermark.Destroy frigör sina underordnade objekt, så du frigör sammansättningen en gång och inget annat, som i exemplet ovan
Begränsningar värda att känna till
Två ärliga begränsningar gäller, och båda följer av hur mekanismen fungerar. För det första är en fångst endast sidans innehållsström och resurser. Kommentarer (annotations), länkar, formulärfält och kommentarer lever som separata objekt i sidordlistan, inte i innehållsströmmen, så de följer inte med in i XObject; en sammanfogad layout bär det synliga sidinnehållet men inte det interaktiva skiktet från källorna. För det andra är niopunkters textpositionering en uppskattning: utan en fullständig layoutmotor kan biblioteket inte veta den exakta renderade bredden på en sträng, så det uppskattar textrutan till 40 % av sidbredden när positioner som wpCenter löses upp. Detta är tillräckligt exakt för att centrera en kort etikett, men för pixel-exakt placering av långa strängar bör du växla till wpCustom och beräkna koordinaterna själv
Skala är inte en praktisk begränsning här, eftersom fångst och sammanfogning manipulerar objektreferenser snarare än att koda om sidinnehåll; om dina källor är på hundratals megabyte gäller samma disciplin som beskrivs i guiden till sammanslagning och uppdelning av stora filer med direktåtkomst innan du sammanfogar. Sammanfogningsfunktionerna och klasserna för vattenstämpelmallar som visas här ingår i den aktuella versionen av losLab PDF Library för Delphi, C# och VB.NET, tillsammans med de lägre primitiverna CapturePage, DrawCapturedPage och DrawCapturedPageMatrix när du behöver en layout som de tre förbyggda sammanfogningsanropen inte täcker