PDFium Bileşeni'nin FPDF_RenderPageBitmap fonksiyonu, PDFium'un sayfanın kendi /Rotate girdisinde zaten taşıdığı döndürmenin her zaman üzerine eklediği bir döndürme argümanı kabul eder; bu yüzden bir sayfanın saklanan döndürmesini okuyup aynı değeri render çağrısına geri beslemek sayfayı iki kez döndürür. Aynı hata fit-zoom matematiğinde de ortaya çıkar: bir küçük resmi sayfanın döndürülmemiş genişlik ve yüksekliğinden boyutlandırmak, /Rotate 90 veya 270 derece olduğunda yanlış en-boy oranı üretir, çünkü render edilmiş bitmap genişlik ve yükseklik değiştirilmiş olarak çıkar
Bu hata, neye bakacağınızı bildiğinizde fark edilmesi kolaydır ve o zamana kadar gözden kaçırılması kolaydır. Karışık dikey ve yatay orijinallerden oluşan bir toplu fatura yığını gelir, biri arşivlemeden önce Acrobat'ta yarısını 90 derecelik bir döndürmeyle düzeltir ve PDFium üzerine kurulu bir Delphi görüntüleyicideki küçük resim şeridi, o belirli sayfaları yan yatık, ters veya yanlış yönlendirme için şekillendirilmiş bir kutuya sıkıştırılmış olarak render eder. Hiçbir şey bir istisna fırlatmaz. Hiçbir şey bir hata günlüğe kaydetmez. Pikseller basitçe yanlıştır ve yalnızca birinin sonradan döndürdüğü sayfa alt kümesi için -tam olarak döndürülmemiş bir test PDF'ine karşı tam bir QA geçişinden sağ çıkıp gerçek bir belgenin 47. sayfasında üretimde ortaya çıkan türden bir hata
PDFium sayfayı neden iki kez döndürüyor?
PDFium, render'lanıcıya ne geçirilirse geçirilsin, bir sayfanın kendi /Rotate değerini bir bitmap render ettiği her seferinde otomatik olarak uygular. FPDF_RenderPageBitmap döndürme parametresi -PDFiumPas'ta TPdf.RenderPage, TPdf.RenderTile ve TPdf.RenderPageThumbnail üzerinde TRotation değerleri ro0, ro90, ro180 ve ro270 olarak sunulur- bir sayfanın sonunda hangi açıda olması gerektiğini ayarlamaz; döndürme parametresi, sayfa sözlüğünün zaten belirttiği şeyin üzerine ne kadar ekstra döndürme katmanlanacağını ayarlar; bu yüzden bu metotların her biri onu ro0'a varsayılan yapar
TPdf.PageRotation, aynı /Rotate değerini FPDFPage_GetRotation üzerinden okur ve uygulama kodu genellikle bunu render etmeyle hiçbir ilgisi olmayan nedenlerle ihtiyaç duyar, örneğin bir açıklamayı sayfa uzayında nasıl yerleştireceğine karar vermek gibi. Tuzak tek bir satırdır: PageRotation'ı RenderPage'in Rotation argümanına geçirmek, çağrının sayfayı dikey konuma normalleştirmesini beklemek. /Rotate 90 ile zaten kaydedilmiş bir sayfa, PDFium dahil uyumlu herhangi bir görüntüleyicide doğru şekilde döndürülmüş olarak görüntülenir; bunun üzerine tekrar ro90 ekleyin ve sayfa amaçlanan 90 yerine 180 dereceye döner, hiç döndürmesi olmayan bir sayfa ise hiçbir nedeni olmadan istenmeyen bir çeyrek dönüş alır
// Wrong: PageRotation already reflects /Rotate, and PDFium applies
// it automatically on every render -- passing it again as Rotation
// doubles the angle
Bitmap := Pdf.RenderPage(0, 0, TargetW, TargetH, Pdf.PageRotation, []);
// Right: leave Rotation at its ro0 default and let PDFium apply the
// page's own /Rotate exactly once
Bitmap := Pdf.RenderPage(0, 0, TargetW, TargetH, ro0, []);
Rotation parametresi gerçekte ne için?
Rotation parametresi, API'deki yerini gerçekten farklı bir iş için kazanır: bir sayfanın saklanan yönelimiyle hiçbir ilgisi olmayan, bir döndürme-görünüm araç çubuğu düğmesinin altta yatan dosyaya dokunmadan uyguladığı türden salt görünüm döndürmesi eklemek. TPdfView, tam olarak bu nedenle iki kavramı iki ayrı özellik olarak tutar. TPdfView.PageRotation, sayfanın kendi /Rotate'ını yansıtır ve FPDFPage_SetRotation aracılığıyla belgeye yeni bir değer yazabilir; TPdfView.Rotation, varsayılan olarak ro0 olan ve dosyaya asla dokunmayan geçici, salt görünüm bir özelliktir. İlk özelliği okuyup ikincisine yazmak, hatanın tamamı tek bir cümlede
// View-only: rotates what the user sees, changes nothing in the file
procedure TViewerForm.RotateViewClick(Sender: TObject);
begin
case PdfView.Rotation of
ro0: PdfView.Rotation := ro90;
ro90: PdfView.Rotation := ro180;
ro180: PdfView.Rotation := ro270;
ro270: PdfView.Rotation := ro0;
end;
end;
// Persistent: rewrites the page's own /Rotate entry in the document
procedure TViewerForm.RotatePageClick(Sender: TObject);
begin
case PdfView.PageRotation of
ro0: PdfView.PageRotation := ro90;
ro90: PdfView.PageRotation := ro180;
ro180: PdfView.PageRotation := ro270;
ro270: PdfView.PageRotation := ro0;
end;
end;
Fit-zoom boyutlandırma neden aynı şekilde bozulur?
Fit-zoom boyutlandırma, ayna görüntüsü bir nedenle bozulur: hesaplama yanlış açı yerine yanlış sayı çiftinden başlar. Bir küçük resim kutusunu boyutlandırmanın tipik bir yolu, PDFium'dan bir sayfanın genişliğini ve yüksekliğini ister, bu en-boy oranını mevcut kutuyla karşılaştırır ve içine sığan en büyük dikdörtgeni hesaplar -bu, döndürülmemiş bir sayfa için sorunsuz çalışır. Aynı hesaplama, genişlik ve yükseklik, sayfanın içsel, döndürülmemiş boyutunu bildiren bir çağrıdan geldiğinde bir /Rotate 90 veya /Rotate 270 sayfası için sessizce başarısız olur: /Rotate 90 taşıyan bir A4 dikey sayfa, PDFium onu döndürme etkiye girdikten sonra doğru şekilde yaklaşık 842 x 595 puan olarak render etse bile, hâlâ yaklaşık 595 x 842 puan bildirir ve döndürülmemiş çiftten hesaplanan bir sığdırma kutusu tamamen yanlış yönelim için şekillenmiş olur
FPDF_GetPageSizeByIndex, tasarım gereği bu içsel, döndürülmemiş boyutu bildiren bir çağrının somut bir örneğidir; bu, her sayfayı yüklemeden sayfa boyutlarını taramak için kullanışlı ve bunu hesaba katmayı unutan fit-zoom matematiği için risklidir. Düzeltme, sorunu adlandırmaktan doğrudan gelir: fit aritmetiğini yapmadan önce sayfanın döndürmesini kontrol edin, o döndürme 90 veya 270 derece olduğunda genişlik ve yüksekliği değiştirin, değiştirilmiş çiftten sığdırma kutusunu hesaplayın ve gerçek render çağrısına yine de ro0 geçirin, çünkü gerçek döndürmeyi uygulayan hâlâ PDFium'dur
Sığdırma matematiğini yeniden icat etmeden küçük resimleri doğru yapmak
TPdf.RenderPageThumbnail bu düzeltmeyi zaten taşır; bu yüzden doğru bir küçük resme giden en kısa yol, sığdırma-ve-döndürme mantığını elle yeniden birleştirmek yerine onu çağırmaktır. 1 tabanlı bir sayfa indeksi ile maksimum bir genişlik ve yükseklik verildiğinde, RenderPageThumbnail bir sığdırma kutusu hesaplar, bunu dahili olarak 90 veya 270'lik bir /Rotate için düzeltir ve belgenin mevcut sayfasını rahatsız etmeden veya bir OnPageChange olayı tetiklemeden çağırana ait bir bitmap döndürür -bu, aynı TPdf örneğinde canlı bir görüntüleyicinin yanında kurulan bir küçük resim şeridi için önemlidir
// PageW, PageH are a page's own (unrotated) dimensions in points, for
// example from FPDF_GetPageSizeByIndex, which reports size before
// /Rotate is applied
function FitBox(PageW, PageH: Double; Rotation: TRotation;
MaxW, MaxH: Integer; out FitW, FitH: Integer): Boolean;
var
PgW, PgH, Swap: Integer;
begin
PgW := Round(PageW);
PgH := Round(PageH);
if PgW < 1 then PgW := 1;
if PgH < 1 then PgH := 1;
if Rotation in [ro90, ro270] then
begin
Swap := PgW;
PgW := PgH;
PgH := Swap;
end;
Result := (MaxW > 0) and (MaxH > 0);
if not Result then
Exit;
if PgW * MaxH > PgH * MaxW then
begin
FitW := MaxW;
FitH := (MaxW * PgH) div PgW;
end
else
begin
FitH := MaxH;
FitW := (MaxH * PgW) div PgH;
end;
end;
FitBox yardımcısı, RenderPageThumbnail yalnızca tek bitmap durumunu kapsadığından, yine de yanınızda tutmaya değer. Bağımsız kutulara karşı birkaç sayfayı düzenleyen özel bir küçük resim ızgarası, bir baskı önizleme şeridi veya bir sayfa seçici iletişim kutusu, her karo için mutlaka yeni bir bitmap istemeden aynı döndürme-farkında sığdırma matematiğine ihtiyaç duyar ve TPdfView'in kendi sayfaya-sığdır ve genişliğe-sığdır yakınlaştırma modları, görünümün mevcut döndürmesine göre yakınlaştırma oranı hesaplaması için bir sayfanın genişliği ile yüksekliği arasında seçim yaparak, bunu mevcut istemci alanıyla karşılaştırmadan önce dahili olarak aynı fikre dayanır. Bu tür bir görüntüleyicide yakınlaştırma ve kaydırma performansı listedeki bir sonraki sorunsa, PDFium tabanlı bir Delphi görüntüleyicide render önbelleğe alma ve pürüzsüz yakınlaştırma üzerine tamamlayıcı makale, doğru boyutlandırmanın bıraktığı yerden tam olarak devam eder
Bir müşteriden önce çift döndürmeyi fark etmek
Çift döndürmenin güvenilir bir görsel imzası vardır: girişte 90 derece döndürülmüş bir sayfa, belgenin geri kalanına göre 90 değil 180 döndürülmüş görünür, çünkü ekstra ro90, sayfanın kendi ro90'ının yerine geçmek yerine üzerine yığılmıştır. Yalnızca /Rotate 0 sayfalarından oluşturulmuş bir test yapısı bunu asla yakalamaz, çünkü ro0'a ro0 eklemek hâlâ ro0'dır ve hata görünmez kalır; bir yapı, bir küçük resim veya fit-zoom kod yoluna güvenilmeden önce en az bir /Rotate 90 ile ve bir /Rotate 270 ile kaydedilmiş bir sayfaya ihtiyaç duyar
PDFium Bileşeni ile PDF sayfalarını JPEG'e render etme makalesinde ele alınan temel sayfa-bitmap boru hattı, herhangi bir özel durum kodu olmadan döndürülmüş sayfaları zaten doğru şekilde render eder, tam olarak Rotation'ı ro0 varsayılanında bıraktığı ve PDFium'un /Rotate'ı kendi başına uygulamasına izin verdiği için. Çift döndürme hatası, yalnızca uygulama kodu PageRotation'ı geri okuyup onu ait olmadığı bir yere beslemeye başladığında ortaya çıkar
Burada açıklanan döndürme-farkında render çağrıları ve küçük resim boyutlandırması, aynı TPdf ve TPdfView sınıfları üzerine kurulu render, görüntüleme ve metin çıkarma API'lerinin geri kalanının yanında, Delphi ve C++Builder için PDFium Bileşeni'nin bir parçasıdır