HotPDF, HTML tablolarını HTML5 paged-media profili üzerinden çizer: rowspan ve colspan için gerçek bir doluluk ızgarası, karakter sayma tahmini yerine ölçülmüş satır yükseklikleri ve her devam sayfasında yinelenen başlık satırları kullanır. İki durumda başlık yinelemeyi reddeder ve bunları baştan bilmek, sonra mükerrer bir hücreyi ayıklamaktan daha ucuzdur
Bunu dayatan belge sınıfı, her raporlama ekibinin er ya da son çıkardığı belgedir: kaynağın gerçeği HTML olan, tablonun dört sayfa boyunca aktığı ve başlığın her sayfada okunabilir olması gereken bir fatura ya da uyumluluk raporu. Gerçek bir tablo yerleşiminden aşağısı, okuyucunun hemen fark ettiği iki hatayı üretir: birinci sayfada bir kez görünen başlık ve karakter sayısından tahmin edilmiş satır yükseklikleri
Tablo yeteneği HTML renderer'a neden taşındı?
Çünkü alternatif zengin metni kaybeder ve içerik baştan zaten HTML'dir, onun nedeni de zengin metindir. Bariz plan yeniden kullanım gibi görünür: HotPDF'in kuralı dolu bir ızgarası olan layout DOM tablo nesnesi zaten vardır; HTML çözümleyicisini ona köprüleyin ve kapsamı bedavaya alın. Sorun, o tablo nesnesinin neyle çizdiğidir. Hücreleri metin ve stil taşır, çizim yolu düz metin çıktısı üretir; böylece HTML'in font ve renk ötesinde gerçekten içerdiği her şey, linkler, üst simgeler, satır içi boyut değişimleri, run başına renk, sayfaya ulaştığında kaybolmuştur
Gerçek belgelerle temasta hayatta kalan yön, tersidir. Tablo motorunun yeteneklerini (doluluk ızgarası, gerçek ölçüm, başlık yinelemesi ve sütun ağırlıklandırması) HTML renderer'a taşıyın ve zengin metin render'ını zaten çalıştığı yerde bırakın. Bu, köprüden daha büyük bir değişikliktir ve tablo hücresi içindeki bir hyperlink'i hyperlink yapan da bu değişikliktir
Union-find olmadan rowspan
Kapsayan hücreler atomik satır grupları oluşturur ama bu gruplar üzerindeki kapanış, genel bir disjoint-set yapısına ihtiyaç duymaz; çünkü doluluk her zaman bitişik bir aralıktır. K satırında başlayan rowspan="3" hücresi, K'dan K+2'ye kadarki satırları kaplar, başka hiçbir şeyi; böylece grup bilgisi satır başı bir bitiş işaretine indirgenir
Algoritma iki satırlık bir niyettir. K'da başlayıp E'de biten kapsayan bir hücre yerleştirirken GroupEnd[K] := Max(GroupEnd[K], E) kaydedin. Sonra satırları bir kez ters yönde yürüyüp G[R] := G[G[R]] uygulayın; bu, her satır bitişini üst üste binen span'lar üzerinden geriye yayar ve geçişli kapanışı tek geçişte verir. Elde ettiğiniz şey, her satır için onunla aynı sayfada kalmak zorunda olan son satırdır; sayfalama adımının bir kırılımın nereye düşebileceğine karar vermek için ihtiyacı olan da tam olarak budur
Yüksekliği dağıtmak öbür yarısıdır. Kapsayan bir hücre, kapladığı satırların şu an sağladığından daha fazla dikey alana ihtiyaç duyduğunda fazlalık, satırlara eşit yayılmaz; span'ın son satırına gider. Sıradan satır yükseklikleri belirlendikten sonra kapsayan hücreleri işleyin, ardından her span'ın son satırını tamamlayın. Fazlalığı eşit dağıtmak daha adil görünür ve bariz biçimde yanlış çıktı üretir: yalnızca kısa tek satırlık hücreler içeren satırlar, üç satır yukarıdaki alakasız bir hücre tesadüfen uzun olduğu için şişer
var
Pdf: THotPDF;
Importer: THPDFHTMLImporter;
Stats: THPDFHTMLImportStatistics;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'audit-report.pdf';
Pdf.BeginDoc;
Importer := THPDFHTMLImporter.Create(Pdf);
try
Importer.Margin := 48;
Importer.BaseFontName := 'Arial';
Importer.BaseFontSize := 10;
Importer.MaxDOMNodes := 200000;
Importer.MaxLayoutOperations := 2000000;
if Importer.RenderHTML5(SourceHtml, PrintStyleSheet) then
begin
Stats := Importer.Statistics;
Writeln('tables ', Stats.TableCount,
' page breaks ', Stats.PageBreakCount);
end;
finally
Importer.Free;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
RenderHTML5, ikinci argüman olarak isteğe bağlı bir author style sheet alır; baskı kuralları oraya aittir. Ekran style sheet'ini dışarıda tutun. Profil sürümlüdür ve HTML5ProfileMilestones, güncel derlemenin hangi yetenek gruplarını uyguladığını bildirir: himParserCascade, himPagedLayout, himTablesForms ve himBoundedResources; böylece bir uygulama, boşluğu üretimde keşfetmek yerine bilinçli biçimde düşük moda geçebilir
Ölçüm, çizimle tam olarak örtüşmek zorundadır
Satır yüksekliği, ancak kıvrılan satırları ölçen kod, onları çizen kodla aynı kuralla kıvırdığında doğrudur. Bariz gibi görünür ve kenarlıkları içeriğiyle hizalanmayan tabloların tek en yaygın kaynağıdır. HotPDF açgözlü bir satır sayacıyla ölçer ve o sayacın, zengin metin çıktı yolunun kıvırma semantiğiyle üç özgül noktada eşleşmesi gerekir: yalnızca boşluklarda kırar, hiçbir zaman bir kelimeyi bölmez ve sütundan geniş bir kelime kendi başına bir satır alır
İkinci gereklilik fonttur. Ölçüm, hücrenin kendi fontuyla çalışmalıdır: genişlik işlevini çağırmadan önce SetFont ile gerçek ad, stil seti ve boyut ayarlanmış olarak; aktif tesadüf eden hangi fontla değil. Bold metin, aynı boyutta regular'dan rutin olarak yüzde ondan fazla geniştir ve üç satırlık hücreyi dört satırlık hücreye çevirmeye yeter. Başlık hücreleri bold, gövde hücreleri bold olmayan bir tablo tek fontla ölçülürse, tam olarak okuyucunun ilk baktığı satırlarda yanlış olur
Bunu doğru yapmak, testte neyi doğrulayabileceğinizi değiştirir. Hassas ölçümün gözlenebilir etkisi satır aralığıdır, glif sayıları değil: tek satırlık bir satır yaklaşık 20 punto yüksekliğindedir, aynı içerik için karakter sayma tahmini ise iki satır ve kabaca 35 öngörür. Satırlar arasındaki dikey mesafeye doğrulama kurun. Ve PDF kullanıcı uzayında Y'nin yukarı doğru arttığını unutmayın; gövde satırının üstünde duran bir başlık, başlık Y değerinin daha büyük olduğu anlamına gelir, bu da ekran koordinatı içgüdüsünün yazacağıyla terstir
HotPDF bir başlığı yinelemeyi ne zaman reddeder?
İki durumda; her ikisinde de devam edilseydi bariz biçimde yanlış çıktı oluşurdu. Birincisi, başlıktan geçip gövde satırlarına uzanan kapsayan bir hücre içeren başlık bloğudur. Başlığı yinelemek, o hücre içeriğini artık ait olmadığı konumda ikinci kez çizerdi; bu yüzden başlık bir kez çizilir ve tablo onsuz devam eder. İkincisi, kullanılabilir sayfa yüksekliğinin yüzde 90'ından uzun bir başlıktır; yineleme veriye neredeyse hiç yer bırakmazdı ve tablo ilerleme kaydetmezdi
Her iki red de tasarım gereği kasıtlı ve sessizdir; çünkü alternatif daha kötüdür. Başlığınız yinelenmiyorsa ve yinelenmesini bekliyorsanız, motordan kuşkulanmadan önce markup'ta thead sınırını geçen bir rowspan olup olmadığına bakın. Sürprizlerin çoğunu tek başına o markup kalıbı açıklar
// Sütun ağırlıkları markup'tan gelir; onları kontrol edecek yer baskı
// style sheet'idir. Genişlikler piksel değil ağırlık olarak ele alınır
const
PrintStyleSheet =
'table { width: 100%; }' +
'thead th { font-weight: bold; background: #eee; }' +
'td.amount { text-align: right; }';
// Gövdeye taşan rowspan taşıyan bir başlık satırı, başlık yinelemesini
// bastırır. Span'ları tek bölüm içinde tutun:
// <thead><tr><th rowspan="2">Item</th>...</tr></thead> tamam
// <tr><th rowspan="3">Item</th>... tbody'ye taşıyor, yineleme yok
Sütun genişlikleri mutlak ölçü yerine ağırlık gibi davranır; içerik yazarın tahminiyle örtüşmediğinde tabloyu kullanılabilir tutan davranış budur. Yüzde 30 ilan edilmiş bir sütun, kullanılabilir genişliğin kabaca yüzde 30'unu alır ama dağıtım, her sütunun gerçekten ihtiyaç duyduğu asgari genişliğe saygı duyar; böylece bölünemeyen uzun bir token taşıyan dar bir sütun, tablo kutusunu sessizce taşmaz
Bunlar bir belge işlem hattında nereye oturur?
Tablo işi, daha geniş paged-media profilinin içinde durur ve HTML5 paged-media içe aktarım yolu makalesinde anlatılan sayfalama kuralları, kaynak bütçeleri ve CSS idaresi, tablo içeren belgeler için de değişmeden geçerlidir. Veriniz HTML olarak başlamıyorsa, tabloları doğrudan PDF'e kurma makalesindeki doğrudan inşa yolu çözümleme katmanını tümüyle atlar ve aynı ızgara davranışını bir API üzerinden verir. Satır yüksekliği sonuç olarak satırların nerede kırıldığına bağlı olduğu için, metin iki yana hizalama ve satır kırma makalesindeki ölçüm tartışması, yoğun tabular çıktı ayarlayan herkesin yardımcı metnidir
Buradaki yeniden kullanılabilir ders aslında tablolarla ilgili değil. Yeni bir alt sistemin, eski bir alt sistemin zaten sahip olduğu bir yeteneğe ihtiyacı olduğunda, ikisinden hangisinin yeniden yazılması en zor olan şeye sahip olduğunu sorun. Izgara aritmetiği birkaç düzine satırdır ve kolay taşınır. Satır içi linkler, üst simgeler ve run başına stil taşıyan zengin metin render'ı değildir; bu yüzden ızgara taşındı, metin yerinde kaldı. HotPDF her iki yolu da HotPDF Delphi PDF bileşeni kapsamında sunar; böylece HTML girdisi ile doğrudan inşa arasındaki seçim, kütüphane değil proje kararıdır