PDF puslapių matmenys yra fiksuoti puslapio kūrimo metu, todėl negalite tiesiog pakeisti turinio mastelio vietoje, kaip keistumėte paveikslėlio dydį. Bibliotekos modelis, dėl kurio sumažinimas tampa praktiškas, yra fiksavimas ir perpiešimas (capture-and-redraw): perkelkite kiekvieno puslapio turinį iš dokumento į rodyklę (handle), sukurkite naują tuščią puslapį pradinio laikmenos dydžio, tada nupieškite užfiksuotą turinį atgal sumažintame ribojančiame langelyje. Aplink esanti tuščia erdvė tampa parašte. Pavyzdžiui, esant 70% masteliui A4 formate, 15% pločio tenka kiekvienai pusei ir tokia pati dalis viršui bei apačiai, o tai tiksliai atitinka žemiau pateiktą paraščių aritmetiką
Kaip veikia CapturePage
CapturePage priima puslapio numerį, perkelia to puslapio turinį į atmintyje esantį fiksavimo objektą ir pašalina puslapį iš dokumento puslapių medžio. Šis pašalinimas yra tyčinis ir tai yra priežastis, kodėl ciklas visada pasirenka 1 puslapį nepriklausomai nuo iteracijos indekso: kai 1 puslapis užfiksuojamas ir ištrinamas, tai, kas buvo 2 puslapis, tampa naujuoju 1 puslapiu, ir taip toliau. Jei padidinsite puslapio parinkiklį kartu su ciklo skaitikliu, praleisite kas antrą puslapį ir gausite pusę laukiamo rezultato
Fiksavimo rodyklė (handle), kurią grąžina CapturePage, nėra nuoroda į puslapį; tai greičiau turinio momentinė nuotrauka. Ji išlieka galiojanti, kol iškviečiate DrawCapturedPage arba aiškiai ją atlaisvinate. DrawCapturedPage priima šią rodyklę ir paskirties stačiakampį, pateiktą kaip poslinkis kairėje, poslinkis apačioje, plotis ir aukštis, visi taškais (points). Biblioteka keičia užfiksuoto turinio mastelį, kad jis tiksliai tilptų į tą stačiakampį, išsaugodama kraštinių santykį tik tuo atveju, jei jūsų stačiakampis atsitiktinai atitinka pradines proporcijas. Tolygiam mastelio keitimui reikia, kad stačiakampis būtų pradinio dydžio, padauginto iš mastelio koeficiento, ir centruotas puslapyje
Centravimo matematika
Naudojant 70% mastelio koeficientą, likę 30% kiekvieno matmens padalijami po lygiai abiem pusėms. Taigi horizontalus intarpas yra pageWidth * (1.0 - 0.70) / 2, o tai yra 15% pločio, ir vertikalus intarpas naudoja tą pačią formulę su puslapio aukščiu. Tada DrawCapturedPage paskirties stačiakampis prasideda nuo (horizBorder, vertBorder) ir apima pageWidth - 2 * horizBorder ant pageHeight - 2 * vertBorder. Ši aritmetika nėra specifiška bibliotekai; tai tiesiog mažesnio stačiakampio simetriško patalpinimo didesniame geometrija
Vienas dalykas, kurį verta paminėti: SetOrigin(1) nustato koordinačių pradžią viršuje kairėje, o ne apačioje kairėje. Paraščių reikšmės, kurias perduodate DrawCapturedPage, matuojamos nuo to pradinio taško, kurį nustatėte, todėl, jei pakeisite pradžios taško režimus tarp įkėlimo ir piešimo, centravimas bus neteisingas
C# pavyzdys
Šis kodas apdoroja kiekvieną Pages.pdf puslapį per fiksavimo ir perpiešimo ciklą bei įrašo rezultatą į newpages.pdf. PDFL yra ActiveX/COM apvalkalo objektas, pridėtas į projektą iš PDFlibDLL64.dll
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");
}
Delphi pavyzdys
Delphi versija naudoja TPDFlib tiesiogiai, o ne per COM sluoksnį, tačiau iškvietimų seka yra identiška. Vienas praktinis skirtumas yra išvesties failo apsauga: FileExists ir DeleteFile vietoje File.Delete, nes SaveToFile nepavyks, jei paskirties vieta yra užrakinta ankstesnio paleidimo, kuris vis dar atidarytas peržiūros programoje
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;
Ką iš tikrųjų kontroliuoja mastelio koeficientas
Reikšmė 0.70 čia reiškia, kad atvaizduojamas turinys užima 70% kiekvieno puslapio matmens, o ne tai, kad failas sudaro 70% pradinio baitų dydžio. Failo dydis po šios operacijos priklauso nuo pradinio turinio sudėtingumo; puslapis su dideliais paveikslėliais nesumažės proporcingai, nes pikselių duomenys perpiešiami ta pačia raiška mažesniame plote. Jei tikslas yra glaudinimas baitų lygiu, teisingas metodas yra LinearizeFile arba perrašymas su srauto glaudinimu (stream compression), o ne geometrinis mastelio keitimas
70% skaičius taip pat nėra griežta riba. Tinka bet kokia reikšmė tarp 0.0 ir 1.0, o didesnės nei 1.0 reikšmės padidina turinį už pradinės puslapio ribos, kuri nukerpama ties laikmenos lauko (media box) kraštu, nebent taip pat padidinsite puslapio matmenis. Įvairaus dydžio dokumentai apdorojami natūraliai, nes prieš apskaičiuojant paraštes kiekvienam puslapiui užklausiama PageWidth ir PageHeight, todėl dokumentas, kurio nelyginiai puslapiai yra A4, o lyginiai - A3 formato, sukurs teisingai centruotą išvestį kiekvienam puslapio dydžiui be jokio specialaus apdorojimo
Kas gali nutikti ne taip
Praktikoje pasitaiko du gedimo režimai. Pirmasis – išvesties failas, paliktas atidarytas PDF peržiūros programoje nuo ankstesnio paleidimo: SaveToFile nepavyks arba įrašys nulį baitų, priklausomai nuo platformos, o nauja išvestis niekada nebus sukurta. Failo ištrynimo apsauga funkcijos viršuje išsprendžia tai kūrimo metu, tačiau gamybos aplinkoje saugiau įrašyti į laikiną kelią ir sėkmės atveju pervardyti
Antrasis – puslapių skaičiaus neatitikimas. Kadangi CapturePage pašalina puslapius iš dokumento, kai juos apdoroja, skaičius, kurį nuskaitote iš PageCount() prieš ciklą, yra teisinga riba iteracijai. PageCount() iškvietimas ciklo viduje grąžintų mažėjantį skaičių kiekvienu praėjimu ir anksti išeitų, palikdamas paskutinius puslapius neapdorotus. Ciklo kintamasis pavyzdžiuose tarnauja tik kaip likusių iteracijų skaitiklis; jis niekada nenaudojamas puslapiui pasirinkti, nes pasirinktinas puslapis visada yra 1 dėl anksčiau paaiškintos priežasties
Čia parodyti puslapių manipuliavimo iškvietimai, įskaitant CapturePage, DrawCapturedPage ir SetPageDimensions, yra losLab PDF bibliotekos, skirtos Delphi, C#, VB.NET ir C++, dalis