PDF sayfa (page) boyutları bir sayfanın (page) oluşturulduğu sırada sabitlenir (fixed), bu nedenle bir resmi (image) yeniden boyutlandırdığınız (resize) gibi içeriği basitçe (simply) yerinde (in place) yeniden ölçeklendiremezsiniz. Küçültmeyi pratik (practical) hale getiren kitaplık modeli yakalama (capture) ve yeniden çizmektir (redraw): her sayfanın (page) içeriğini belgeden bir işleyiciye (handle) çıkarın (lift), orijinal (original) medya (media) boyutunda yeni (new) boş (blank) bir sayfa (page) oluşturun, ardından yakalanan içeriği daha küçük (reduced) bir sınırlayıcı kutu (bounding box) içinde geri (back) çizin (draw). Çevredeki boşluklar marj (margin) haline gelir. Örneğin bir A4 sayfasında %70 (70%) ölçekte (scale), genişliğin %15'i (15%) her iki tarafa, aynı oranda üst (top) ve alt (bottom) kısımlara düşer; bu da tam (exactly) olarak aşağıdaki (below) sınır (border) aritmetiğinin (arithmetic) ürettiği (produces) şeydir
CapturePage nasıl çalışır
CapturePage bir sayfa (page) numarası alır, o sayfanın (page) içeriğini (content) bellek içi (in-memory) bir yakalama nesnesine (capture object) yükseltir (promotes) ve sayfayı (page) belgenin sayfa (page) ağacından kaldırır (removes). Bu kaldırma işlemi kasıtlıdır (intentional) ve döngünün yinelenme indeksine (iteration index) bakılmaksızın her zaman sayfa (page) 1'i (1) seçmesinin (selects) nedeni budur: sayfa (page) 1 yakalanıp silindiğinde, sayfa (page) 2 olan (was) sayfa yeni (new) sayfa (page) 1 olur ve bu (so) böyle (on) devam eder. Sayfa seçiciyi (page selector) döngü sayacıyla (loop counter) birlikte artırırsanız (increment), her iki sayfadan (page) birini atlarsınız (skip) ve beklenen (expected) çıktının yarısını elde edersiniz (end up)
CapturePage'in döndürdüğü yakalama tutamacı bir sayfa referansı değildir; daha çok bir içerik anlık görüntüsüne benzer. Çizimden sonra da geçerli kalır; böylece aynı yakalamayı, ReleaseCapturedPage ile serbest bırakana ya da belge ortadan kalkana kadar birden fazla kez yerleştirebilirsiniz. DrawCapturedPage bu tutamacı artı bir hedef dikdörtgeni alır; dikdörtgen sol kenar, üst kenar, genişlik ve yükseklik olarak verilir ve varsayılan olarak tümü punto cinsindendir. Kütüphane, yakalanan içeriği bu dikdörtgene tam olarak sığacak şekilde ölçekler; en-boy oranını yalnızca dikdörtgeniniz tesadüfen özgün orantılarla eşleşiyorsa korur. Eşit ölçekleme için dikdörtgenin, sayfada ortalanmış, ölçek çarpanıyla çarpılmış özgün boyut olmasını istersiniz
Ortalama matematiği
%70'lik (70%) bir ölçek çarpanıyla (scale factor), her bir boyutun (dimension) kalan %30'u (30%) iki taraf (sides) arasında (between) eşit olarak paylaştırılır. Dolayısıyla (So) yatay içe doğru (horizontal inset) yerleşim pageWidth * (1.0 - 0.70) / 2'dir, bu da (which) genişliğin %15'idir (15%) ve dikey (vertical) iç yerleşim (inset), sayfa yüksekliğini (page height) kullanarak aynı formülü (formula) izler. Daha (Then) sonra (Then) DrawCapturedPage için (for) hedef dikdörtgeni (destination rectangle) (horizBorder, vertBorder) konumundan (at) başlar ve pageWidth - 2 * horizBorder boyutundan (by) pageHeight - 2 * vertBorder boyutuna yayılır (spans). Bu aritmetik, kütüphaneye (library) özgü değildir; sadece daha küçük bir dikdörtgeni daha (larger) büyük (larger) bir (one) dikdörtgenin (rectangle) içine simetrik (symmetrically) olarak yerleştirmenin (fitting) geometrisidir
Dikkat çeken bir şey: SetOrigin(1), koordinat başlangıç noktasını sol alt yerine sol üste koyar. DrawCapturedPage'e geçtiğiniz ikinci değer, dikdörtgenin üst kenarıdır ve ayarladığınız orijinden itibaren ölçülür. Örneklerin önce SetOrigin(1) çağırmasının nedeni budur: varsayılan sol alt orijinle aynı vertBorder, üst kenarı sayfanın dibine yakın bir yere koyar ve içeriğin çoğunu sayfanın dışına iter
C# örneği
Aşağıdaki kod, Pages.pdf'in her sayfasını yakala-ve-yeniden-çiz döngüsünden geçirir ve sonucu newpages.pdf dosyasına yazar. PDFL, DLL sürümüyle gelen PDFlibrary import sınıfının bir örneğidir ve new PDFlibrary("PDFlibDLL64.dll") ile oluşturulur; ActiveX sürümü aynı metotları PDFlibAX.PDFlibrary üzerinden açar
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++)
{
// Her zaman 1. sayfayı seçin: CapturePage sayfayı kaldırır, böylece 2. sayfa
// sonraki yinelemede 1. sayfa olur.
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);
// 0 = hiçbir şey yakalanmadı, örneğin tek sayfalık kaynak: tek sayfa kaldırılamaz
if (capturedPageId == 0)
throw new InvalidOperationException("CapturePage failed on source page " + i);
PDFL.NewPage();
PDFL.SetPageDimensions(pageWidth, pageHeight);
ret = PDFL.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
if (ret != 1)
throw new InvalidOperationException("DrawCapturedPage failed on source page " + i);
}
PDFL.SaveToFile("newpages.pdf");
}
Delphi örneği
Delphi sürümü, TPDFlib'i DLL import sınıfı üzerinden değil doğrudan kullanır, ama çağrı sırası aynıdır. Pratik bir fark, çıktı dosyası korumasıdır: File.Delete yerine FileExists artı DeleteFile; çünkü hedef, önceki bir çalıştırmadan görüntüleyicide hâlâ açık kalmışsa SaveToFile başarısız olur
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);
// 0 = hiçbir şey yakalanmadı, örneğin tek sayfalık kaynak: tek sayfa kaldırılamaz
if capturedPageId = 0 then
raise Exception.CreateFmt('CapturePage failed on source page %d', [i]);
PDFLib.NewPage();
PDFLib.SetPageDimensions(pageWidth, pageHeight);
ret := PDFLib.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
if ret <> 1 then
raise Exception.CreateFmt('DrawCapturedPage failed on source page %d', [i]);
end;
PDFLib.SaveToFile('newpages.pdf');
finally
PDFLib.Free;
end;
end;
Ölçek çarpanı (scale factor) aslında (actually) neyi kontrol (controls) eder (controls)
Buradaki 0.70 değeri, işlenen içeriğin her sayfa boyutunun %70'ini kapladığı anlamına gelir; dosyanın özgün bayt boyutunun %70'i olduğu anlamına değil. Bu işlemden sonraki dosya boyutu, özgün içeriğin karmaşıklığına bağlıdır; büyük resimler içeren bir sayfa orantılı olarak küçülmez, çünkü piksel verisi aynı çözünürlükte daha küçük bir alana yeniden çizilir. Amaç daha küçük bir dosyaysa bakılacak yer geometrik ölçekleme değil, sıkıştırılmadan çıkan sayfa içerik akışları için CompressContent ve resim yeniden sıkıştırmadır (Delphi sürümünde OptimizeImages). LinearizeFile başka bir araçtır: dosyayı sayfa-sayfa yükleme (Fast Web View) için yeniden sıralar; görüntüleyicilerin ağ üzerinden büyük dosyaları açmasına yardım eder ama küçültmez
%70 (70%) rakamı da kesin (hard) bir sınır (limit) değildir. 0.0 (0.0) ile 1.0 (1.0) arasındaki (between) herhangi (Any) bir değer (value) işe yarar ve 1.0'ın üzerindeki değerler (values), sayfa (page) boyutlarını da artırmadığınız (increase) sürece (unless) ortam kutusu (media box) kenarında (edge) kırpılan (clips) içeriği (content) orijinal sayfa (page) sınırının (boundary) ötesine (beyond) büyütür (enlarge). Karışık (Mixed-size) boyutlu (Mixed-size) belgeler (documents) doğal (naturally) olarak işlenir çünkü PageWidth ve PageHeight, kenarlık hesaplamasından (calculation) önce her sayfa için sorgulanır (queried), bu nedenle (so) tek (odd) sayfaların A4 ve çift (even) sayfaların A3 olduğu bir (a) belge, herhangi bir özel duruma (casing) gerek kalmadan her sayfa (page) boyutunda doğru (correctly) şekilde ortalanmış çıktı üretecektir (produce)
İşlerin nerede ters (wrong) gidebileceği (go)
Uygulamada (practice) iki başarısızlık (failure) modu (modes) ortaya çıkar (come up). Birincisi (first), önceki bir (a) çalıştırmadan (run) dolayı (from) PDF görüntüleyicide açık (open) bırakılan bir çıktı dosyasıdır: SaveToFile platforma bağlı (depending) olarak (depending) başarısız (fail) olacak (will) veya (or) sıfır bayt yazacaktır ve yeni (new) çıktı (output) asla inmeyecektir (lands). İşlevin (function) en üstündeki (top) dosya (file-delete) silme (file-delete) koruması (guard) geliştirme (development) için bunu (that) işler (handles), ancak bir üretim boru hattında (pipeline) geçici (temporary) bir (a) yola (path) yazmak (writing) ve (and) başarıyla yeniden adlandırmak (renaming) daha (safer) güvenlidir (safer)
İkincisi, sayfa sayısı uyumsuzluğudur. CapturePage, sayfaları işlerken belgeden kaldırdığı için döngüden önce PageCount()'ten okuduğunuz sayı, yineleme yapılacak doğru sınırdır. Döngünün içinde PageCount() çağırmak da işe yaramaz: her geçiş bir sayfa kaldırıp bir sayfa ekler; sayı yerinde sayar ve kaç kaynak sayfa kaldığı hakkında hiçbir şey söylemez. Örneklerdeki döngü değişkeni yalnızca kalan yineleme sayacı olarak görev yapar; sayfa seçmek için asla kullanılmaz, çünkü seçilecek sayfa, daha önce açıklanan nedenle daima 1'dir
Burada (here) gösterilen (shown) CapturePage, DrawCapturedPage ve SetPageDimensions dahil (including) sayfa (page) işleme (manipulation) çağrıları (calls), Delphi, C#, VB.NET ve C++ için losLab PDF Kütüphanesinin bir parçasıdır