Dimenzije strani PDF so določene ob ustvarjanju strani, zato vsebine ne morete preprosto pomanjšati na mestu, kot bi spremenili velikost slike. Model knjižnice, ki omogoča praktično pomanjševanje, je zajem in ponovni izris (capture-and-redraw): vsebino vsake strani prenesete iz dokumenta v ročico, ustvarite novo prazno stran v izvirni velikosti medija, nato pa zajeto vsebino znova izrišete v zmanjšanem omejitvenem okvirju. Okoliški prazen prostor postane rob. Pri 70-odstotnem merilu na strani A4 na primer 15 % širine pade na vsako stran in enak delež zgoraj in spodaj, kar je natanko tisto, kar ustvari spodnja aritmetika robov.
Kako deluje CapturePage
Metoda CapturePage sprejme številko strani, prenese vsebino te strani v zajeti objekt v pomnilniku in odstrani stran iz drevesa strani dokumenta. Ta odstranitev je namerna in je razlog, zakaj zanka vedno izbere stran 1, ne glede na indeks iteracije: ko je stran 1 zajeta in izbrisana, prejšnja stran 2 postane nova stran 1 in tako naprej. Če indeks strani povečujete skupaj s števcem zanke, boste preskočili vsako drugo stran in končali s polovico pričakovanega izhoda.
Zajeta ročica (capture handle), ki jo vrne CapturePage, ni referenca strani, temveč posnetek vsebine. Ostane veljavna, dokler ne pokličete DrawCapturedPage ali jo izrecno sprostite. DrawCapturedPage sprejme to ročico in ciljni pravokotnik, podan kot levi odmik, spodnji odmik, širina in višina, vse v točkah. Knjižnica prilagodi velikost zajete vsebine tako, da natančno ustreza temu pravokotniku, razmerje stranic pa ohrani le, če se vaš pravokotnik ujema z izvirnimi proporci. Za enakomerno pomanjševanje mora biti pravokotnik izvirne velikosti pomnožen s faktorjem merila in centriran na strani.
Aritmetika centriranja
Pri faktorju merila 70 % se preostalih 30 % vsake dimenzije enakomerno porazdeli med obe strani. Vodoravni rob je tako pageWidth * (1.0 - 0.70) / 2, kar predstavlja 15 % širine, navpični rob pa sledi enaki formuli z uporabo višine strani. Ciljni pravokotnik za DrawCapturedPage se nato začne na koordinatah (horizBorder, vertBorder) in obsega pageWidth - 2 * horizBorder v širino ter pageHeight - 2 * vertBorder v višino. Ta aritmetika ni specifična za knjižnico, temveč gre za geometrijo simetričnega umeščanja manjšega pravokotnika v večjega.
Ena stvar, ki jo je vredno omeniti: klic SetOrigin(1) postavi koordinatno izhodišče v zgornji levi kot namesto v spodnji levi kot. Vrednosti robov, ki jih posredujete metodi DrawCapturedPage, se merijo od izhodišča, ki ste ga nastavili. Če med nalaganjem in risanjem zamenjate načine izhodišča, bo centriranje napačno.
Primer v jeziku C#
private void ScalePages_Click(object sender, EventArgs e)
{
File.Delete("newpages.pdf");
double pageWidth, pageHeight, horizBorder, vertBorder;
double scaleFactor = 0.70;
int capturedPageId, ret;
PDFL.LoadFromFile("Pages.pdf", "");
PDFL.SetOrigin(1);
int numPages = PDFL.PageCount();
for (int i = 1; i <= numPages; i++)
{
// Always select page 1: CapturePage removes the page, so page 2
// becomes page 1 on the next iteration.
PDFL.SelectPage(1);
pageWidth = PDFL.PageWidth();
pageHeight = PDFL.PageHeight();
horizBorder = pageWidth * (1.0 - scaleFactor) / 2;
vertBorder = pageHeight * (1.0 - scaleFactor) / 2;
capturedPageId = PDFL.CapturePage(1);
PDFL.NewPage();
PDFL.SetPageDimensions(pageWidth, pageHeight);
ret = PDFL.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
}
PDFL.SaveToFile("newpages.pdf");
}
Primer v jeziku Delphi
procedure TForm1.ScalePagesClick(Sender: TObject);
var
PDFLib: TPDFlib;
pageWidth, pageHeight, horizBorder, vertBorder: Double;
scaleFactor: Double;
capturedPageId, ret, numPages, i: Integer;
begin
if FileExists('newpages.pdf') then
DeleteFile('newpages.pdf');
scaleFactor := 0.70;
PDFLib := TPDFlib.Create;
try
PDFLib.LoadFromFile('Pages.pdf', '');
PDFLib.SetOrigin(1);
numPages := PDFLib.PageCount();
for i := 1 to numPages do
begin
PDFLib.SelectPage(1);
pageWidth := PDFLib.PageWidth();
pageHeight := PDFLib.PageHeight();
horizBorder := pageWidth * (1.0 - scaleFactor) / 2;
vertBorder := pageHeight * (1.0 - scaleFactor) / 2;
capturedPageId := PDFLib.CapturePage(1);
PDFLib.NewPage();
PDFLib.SetPageDimensions(pageWidth, pageHeight);
ret := PDFLib.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
end;
PDFLib.SaveToFile('newpages.pdf');
finally
PDFLib.Free;
end;
end;
Kaj faktor merila dejansko nadzoruje
Vrednost 0,70 pomeni, da upodobljena vsebina zaseda 70 % vsake dimenzije strani, in ne, da je datoteka 70 % svoje prvotne velikosti v bajtih. Velikost datoteke po tej operaciji je odvisna od zapletenosti izvirne vsebine; stran z velikimi slikami se ne bo sorazmerno zmanjšala, saj se slikovni podatki znova izrišejo pri enaki ločljivosti na manjšem območju. Če je cilj stiskanje na ravni bajtov, je pravilen pristop klic LinearizeFile ali ponovno shranjevanje s stiskanjem tokov in ne geometrijsko pomanjševanje.
Vrednost 70 % prav tako ni stroga meja. Deluje katera koli vrednost med 0,0 in 1,0, vrednosti nad 1,0 pa povečajo vsebino čez prvotno mejo strani, kar jo obreže na robu medijskega okvirja (media box), razen če povečate tudi dimenzije strani. Dokumenti z mešanimi velikostmi strani se obdelajo samodejno, saj se PageWidth in PageHeight poizvedujeta za vsako stran pred izračunom robov, tako da bo dokument, kjer so lihe strani velikosti A4, sode pa A3, ustvaril pravilno centriran izpis na vsaki velikosti strani brez posebnega prilagajanja.
Kje lahko gre kaj narobe
V praksi se pojavita dva načina odpovedi. Prvi je izhodna datoteka, ki je iz prejšnjega zagona ostala odprta v pregledovalniku PDF: klic SaveToFile ne bo uspel ali pa bo zapisal nič bajtov, odvisno od platforme, nov izhod pa ne bo shranjen. Zaščita z izbrisom datoteke na začetku funkcije to rešuje med razvojem, v produkcijskem cevovodu pa je varneje pisati na začasno pot in jo ob uspehu preimenovati.
Drugi način je neskladje v številu strani. Ker CapturePage med obdelavo odstranjuje strani iz dokumenta, je število, ki ga preberete iz PageCount() pred zanko, pravilna meja za iteracijo. Klic PageCount() znotraj zanke bi vrnil padajoče število ob vsakem prehodu in zanko predčasno zaključil, s čimer bi zadnje strani ostale neobdelane. Spremenljivka zanke v primerih služi le kot števec preostalih iteracij; nikoli se ne uporablja za izbiro strani, saj je stran za izbiro vedno 1 iz prej pojasnjenega razloga.
Klici za manipulacijo strani, prikazani tukaj, vključno s CapturePage, DrawCapturedPage in SetPageDimensions, so del knjižnice losLab PDF Library za Delphi, C#, VB.NET in C++.