Bir sıkıştırma özelliği yayınlandıktan bir hafta sonra iki yakınma gelir: taranan sözleşmenin harf biçimleri artık merdiven basamaklı ve tüylüdür ve kapak sayfasındaki saydam logo soluk bir halenin içinde durur. PDFiumPas her ikisine tek bir yerde yanıt verir. TPdf.OptimizeImages her görseli küçültmeden önce ölçer, sonra bir yeniden örnekleme çekirdeği seçer ve rengi alpha farkında biçimde biriktirir
Bu her zaman böyle değildi. v3.100.0 sürümünden önce aynı yöntem, ikili düzeyli olmayan her görseli sabit bir en yakın komşu adımıyla küçültüyordu; her iki yakınmayı da üreten algoritma tam olarak budur: çıktı pikseli başına bir kaynak pikseli noktasal örnekler ve tam saydam bir pikselin altındaki RGB değerini, bir okuyucunun onu göreceği varmış gibi ele alır. v3.100.0 sürümündeki yeniden yazım, o tek yolu beş çekirdekle, ölçüme dayalı bir seçim kuralıyla ve açık bir çalışma belleği bütçesiyle değiştirir
Küçültme taranan metni neden yırtık pırtık gösterir?
Çünkü noktasal örnekleme yanlış soruyu yanıtlar. 300 DPI bir tarama 150 DPI hedefine yeniden yönlendirildiğinde, her hedef piksel kaynak piksellerin ikiye ikilik bir bloğunu temsil eder ve en yakın komşu dördünden birini tutup geri kalanını atar. Hangisinin hayatta kalacağı yuvarlamaya bağlıdır; bu yüzden kaynakta düzgünce kenar yumuşatılmış bir çizgi kenarı, piksel başına bir yazı tura atışına dönüşür. Sonuç, glif kenarları boyunca klasik örtüşen merdivendir; buna ek olarak, atılan örneklerin deseni taşıdığı yarım ton bölgelerde moare oluşur. Bunun bir PDFde ekrandakinden daha fazla önemi vardır, çünkü hasar kalıcıdır. Bir görsel XObjecti örnek verisini /Width, /Height ve /BitsPerComponent ile birlikte taşır (ISO 32000-1 §8.9.5) ve yeniden örnekleme üçünü de dosyanın içinde yeniden yazar. Görüntüleyicideki kötü bir yakınlaştırma, yeniden çizebileceğiniz bir karedir ve PDFiumPas bunun için işleme önbelleği ve yakınlaştırma performansı içinde ayrı bir mekanizmaya sahiptir. Kötü bir küçültme ise müşteriye verdiğiniz yeni bir belgedir
PDFiumPas ayrıntıyı nasıl ölçer ve çekirdek seçer?
PDFiumPas kararı belge başına değil görsel başına verir. Çekirdek seçmeden önce, sınırlı bir örnekleme ızgarasından normalleştirilmiş bir parlaklık ayrıntı puanı hesaplar: yatay ve dikey adımlar (Width + 63) div 64 ve (Height + 63) div 64 değerleridir; böylece 12.000 piksellik bir tarama ile 300 piksellik bir küçük resim aşağı yukarı aynı 64e 64lük taramaya mal olur. Örneklenen her konumda, en fazla üç kanal boyunca sağdaki ve alttaki komşuyla mutlak farkı toplar, sonra örnek sayısı çarpı 255e böler. Puan 0 ile 1 arasına düşer; düz iş grafikleri sıfıra yakın durur ve yoğun fotoğrafımsı doku yukarı tırmanır
Seçim merdiveni bundan sonra sabit bir sırayla çalışır. ResampleFilter pirfAdaptive dışında bir değerse o filtre olduğu gibi kullanılır. Aksi takdirde: 1 bitlik içerik pirfBilevel alır; piccLineArt olan bir ContentClass pirfBox alır; 4 ya da daha büyük bir ölçek faktörü de pirfBox alır, çünkü o küçültmede bir alan ortalaması hem en ucuz hem de en doğru yanıttır; piccPhoto, 0,08 ya da daha yüksek bir ayrıntı puanı ya da 0,9 ya da daha yüksek bir PreferredQuality, üç loblu çekirdeğiyle pirfLanczos alır; 2 ya da daha büyük bir ölçek ya da 0,7 ya da daha yüksek bir kalite, yarıçap 2de pirfBicubic alır; geri kalan her şey pirfBilinear alır. TPdfImageOptimizeOptions.Default PreferredQuality değerini 0,85 yaptığı için, varsayılan bir çalıştırma ancak küçültme hafifse ve içerik düzse çift doğrusalya geri düşer
uses
PDFium;
procedure ShrinkScannedPdf(const InputFile, OutputFile: string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := InputFile;
// Varsayılanlar: TargetDpi 150, MinDpiRatio 1.5, PreserveBilevel True,
// MinDimension 8, pirfAdaptive, piccAuto, kalite 0.85, 64 MiB bütçe.
Options := TPdfImageOptimizeOptions.Default;
Options.TargetDpi := 150;
Options.MinDpiRatio := 1.5;
Options.ContentClass := piccAuto;
Options.PreferredQuality := 0.85;
if Pdf.OptimizeImages(Options, Report) and (Report.OptimizedCount > 0) then
Pdf.SaveAs(OutputFile);
finally
Pdf.Free;
end;
end;
Bir görsele ancak yatay ve dikey yerleştirme DPI değerlerinden büyüğü TargetDpi değerine bölündüğünde MinDpiRatio değerine ulaşırsa dokunulur. Bu koruma, 150 DPI hedefe yönelik 160 DPI bir fotoğrafın, bir kalite nesline mal olan yüzde altılık bir kazanç için yeniden kodlanmaması için vardır. Her iki eksende de MinDimension değerinin (varsayılan 8) altındaki görseller simge ya da kural çizgisi olarak atlanır
Saydam logolar neden beyaz bir saçak edinir?
Çünkü tam saydam bir pikselin altındaki renk gelişigüzeldir ve düz ağırlıklı bir ortalama onun oy kullanmasına izin verir. Bir tasarım aracından logo dışa aktardığınızda görünmez kenar boşluğu sıklıkla beyazdır ya da siyahtır ya da tuvalin rengi neyse odur; alpha kanalı onu gizler ve çekirdek izdüşümü üzerinden düz bir toplama, onu hemen görünür kenara geri karıştırır. PDFiumPas bundan, BGRA örneklerini önceden çarpılmış biçimde biriktirip ön çarpmayı yalnızca hedef pikselde geri alarak kaçınır
Soyut olarak, katkıda bulunan her örnek renk biriktiricisine channel * alpha * weight, bir alpha biriktiricisine alpha * weight ve ağırlık toplamına weight ekler. Hedef rengi bundan sonra ağırlık toplamına değil alpha biriktiricisine bölünür ve önemli olan adım budur: ağırlık toplamına bölmek rengi görünmez piksellere doğru sürüklerken, biriken alphaya bölmek görünür örneklerin gerçekte anlaştığı rengi yeniden kurar. Hedef alfası ayrı bir niceliktir: 255 * AlphaSum / WeightSum. Alpha içermeyen biçimler her zamanki gibi ağırlık toplamına bölünür; FPDFBitmap_BGRx hedefinin dolgu baytı sabit 255 olarak yazılır ve her kanal depolanmadan önce 0 ile 255 arasına kıstırılır. Bu alpha normalde görsel sözlüğündeki bir yumuşak maske girişinden (ISO 32000-1 §11.4) gelir; PDFium onu, yeniden örnekleyicinin aldığı BGRA arabelleğine zaten birleştirmiştir
// İç biriktirme döngüsünün biçimi, katkıda bulunan her kaynak örneği için
if SrcFormat = FPDFBitmap_BGRA then
Alpha := PByte(PAnsiChar(Pixel) + 3)^ / 255
else
Alpha := 1;
for Channel := 0 to Min(BytesPerPixel, 3) - 1 do
Accumulated[Channel] := Accumulated[Channel] +
PByte(PAnsiChar(Pixel) + Channel)^ * Alpha * Weight;
AlphaSum := AlphaSum + Alpha * Weight;
WeightSum := WeightSum + Weight;
// ... ve hedef pikselde, alpha toplamına karşı ön çarpmayı geri al
if SrcFormat = FPDFBitmap_BGRA then
begin
if Abs(AlphaSum) > 1E-12 then
ValueSum := Accumulated[Channel] / AlphaSum
else
ValueSum := 0;
end
else
ValueSum := Accumulated[Channel] / WeightSum;
1 bitlik çizgi sanatını gri bölgenin dışında tutmak
İkili düzeyli bir taramaya uygulanan her sürekli çekirdek gri üretir ve gri, faks tarzı bir görselin içermesine izin verilmeyen şeyin ta kendisidir. Bu yüzden PDFiumPas varsayılan olarak 1 bitlik görsellere dokunmaz: PreserveBilevel TPdfImageOptimizeOptions.Default içinde True değeridir ve bu tür görseller SkippedCount içine dokunulmadan iner. Onu False yaptığınızda, yumuşatıcı bir çekirdek yerine pirfBilevel yolu devreye girer. Her hedef pikseli kapsayan tam kaynak dikdörtgenini yürür, parlaklığı BGR bellek sırasındaki 0,114, 0,587 ve 0,299 ağırlıklarıyla ortalar ve sonucu 127,5 eşikinden düz 0 ya da 255 değerine eşikler. Ara hiçbir şey yazılamaz; bu yüzden kenarlar keskin kalır ve ince çizgilerin çevresinde gri hale oluşmaz; bir BGRA kaynağının alpha kanalı normal biçimde ortalanır ve bir BGRx hedefi sabit 255 değerini alır. Daha küçük bir belge yerine altındaki piksellere gereksiniminiz varsa, PDF belgelerinden görsel çıkarma ayrı yoldur
Bir görsel çalışma belleği bütçesini aştığında ne olur?
Tam olduğu gibi bırakılır ve sayılır. MaxWorkingBytes varsayılanı 64 MiB değeridir ve iki kez uygulanır. Hedef bit eşlem oluşturulmadan önce, genişlik çarpı yükseklik çarpı piksel başına bayt bütçeyi aşarsa PDFiumPas görseli reddeder. FPDFBitmap_CreateEx başarılı olduktan sonra, gerçek satır aralığı çarpı yüksekliği kullanarak yeniden denetler; çünkü satır dolgusu bir ayırmayı, saf çarpımın geçtiği bir sınırın ötesine itebilir. Her iki reddetme de hedefi yok eder ve hiçbir şey döndürmez. Bunun ima ettiği gerileme konusunda açık olun: bütçe aşan bir görsel daha düşük kalitede yeniden örneklenmez ve karolara bölünmez. Özgün belgede kalır, BudgetExceededCount ve SkippedCount ikisi de artar ve bu yüzden bir çalıştırma, bir belge yalnızca kısmen iyileştirilmişken başarı bildirebilir. Bu bilinçli bir güvenli başarısızlık davranışıdır, ancak raporun isteğe bağlı bir okuma olmadığı anlamına gelir. Ayrı bir hata biçimi de vardır: PDFiumun bit eşlem üretemediği görseller (CMYK, JPX, JBIG2 ya da maskeli kaynaklar gibi) bunun yerine FailedCount değerini artırır ve aynı şekilde dokunulmadan bırakılır
procedure OptimizeBatch(const Files: array of string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
I: Integer;
begin
Options := TPdfImageOptimizeOptions.Default;
Options.PreserveBilevel := False; // ikili düzeyli alan oylamasını kullan
Options.ContentClass := piccPhoto; // fotoğraf kümeleri için Lanczos zorla
Options.MaxWorkingBytes := 256 * 1024 * 1024; // büyük taramalar için pay
Pdf := TPdf.Create(nil);
try
for I := Low(Files) to High(Files) do
begin
Pdf.FileName := Files[I];
if not Pdf.OptimizeImages(Options, Report) then
begin
WriteLn('optimize failed: ', Report.ErrorMessage);
Continue;
end;
if Report.BudgetExceededCount > 0 then
WriteLn(Files[I], ': ', Report.BudgetExceededCount,
' image(s) over budget and kept at full size');
if Report.FailedCount > 0 then
WriteLn(Files[I], ': ', Report.FailedCount,
' image(s) could not be decoded to a bitmap');
if Report.OptimizedCount > 0 then
Pdf.SaveAs(ChangeFileExt(Files[I], '.opt.pdf'));
end;
finally
Pdf.Free;
end;
end;
Dosyayı göndermeden önce raporu okumak
TPdfImageOptimizeReport yalnızca günlüğe yazılmak için değil, tanı koydurmak için yapılmıştır. OptimizedCount, SkippedCount ve FailedCount yanında çekirdek başına bir sayaç sunar; böylece BoxFilterCount, BilinearFilterCount, BicubicFilterCount, LanczosFilterCount ve BilevelFilterCount, uyarlamalı kuralın derleminiz hakkında gerçekte ne sonuç çıkardığını söyler. Tümü kutu olan bir sonuç, küçültmelerin sert olduğunu ya da içeriğin çizgi sanatı olarak sınıflandırıldığını gösterir; çizgi sanatı olduğuna inandığınız bir belgede tümü Lanczos olan bir sonuç, ContentClass değerinin açıkça ayarlanması gerektiğine işarettir. AverageDetailScore, PreferredQuality ayarını yaparken 0,08 Lanczos eşiğiyle karşılaştıracağınız sayıdır ve PeakWorkingBytes, çalıştırmanın MaxWorkingBytes değerinden gerçekte ne kadarını kullandığını gösterir. Geçersiz seçenekler sessizce değil yüksek sesle başarısız olur: pozitif olmayan bir TargetDpi, 1in altındaki bir MinDpiRatio, 0 ile 1 aralığı dışında bir PreferredQuality ya da pozitif olmayan bir MaxWorkingBytes, herhangi bir sayfaya dokunulmadan EPdfError hatasını yükseltir. OptimizeImages yalnızca bellek içi belgeyi düzenler; değiştirilen her sayfa FPDFPage_GenerateContent ile işlenir ve bundan sonra SaveAs işlevini yine kendiniz çağırırsınız. Neyin değiştiğini gözle görmek için, PDF sayfalarını JPEG görsellere dönüştürme bölümünde anlatıldığı gibi önceki ve sonraki belgeleri bit eşlemlere işleyin ve tam yakınlaştırmada karşılaştırın
Uyarlamalı yeniden örnekleme, çalıştığında görünmez olan ve çalışmadığında destek biletleri üreten özelliklerdendir; ölçümün, alpha işlemenin ve bellek bütçesinin üç ayrı iyileştirme yerine birlikte hayata geçmesinin nedeni budur. Bunu bir Delphi, C++Builder ya da Lazarus ürünü için değerlendiriyorsanız, tam API yüzeyi ve lisanslama ayrıntıları PDFiumPas Delphi PDFium bileşeni sayfasında yer alır