Technical Article

Delphi'de HotPDF ile PDF Metni Tam Hizalama

Tam hizalama, bir metin sütununun hem sol hem sağ kenarlarını hizalayan düzendir; basılı bir kitaptan veya resmi bir rapordan beklediğiniz görünümdür. Tanımlamak kolaydır ancak şaşırtıcı biçimde hata yapmak da kolaydır; çünkü "fazladan alan nereye gidecek" sorusunun yanıtı İngilizce için Japonca ile aynı değildir ve her satırı ölçmenin saf yolu hızlı bir sayfayı yavaşa çevirir. HotPDF, tek bir kutu-düzeni çağrısıyla betik-bilinçli hizalama sunar ve bu çağrının altında kendi başına anlamak değer bir ders niteliğindeki bir performans düzeltmesi yatar

Bu makale her ikisini de ele alır. Birincisi, kelime boşluklu betikler ile boşluksuz betikler için fazlalığın nasıl dağıtılacağına karar veren tipografik kural. İkincisi, çıktıda gözle görülür bir fark olmaksızın hizalamanın sayfa başı maliyetini yaklaşık seksen kat düşüren ölçüm değişikliği. Büyük hacimde belge üretiyor ve çıktının tek tip aralıklı bir metni sığdırmak için genişletilmiş görünmesi yerine gerçek bir dizgiye benzenmesini istiyorsanız her ikisi de önemlidir

Tam hizalamanın gerçekte neyi gerektirdiği

Doğal genişliğinde çizilen bir metin satırı neredeyse hiçbir zaman sütununun sağ kenarına ulaşmaz. Her zaman bir artık, boşluk; son glyph'in bittiği yer ile sütun sınırının oturduğu yer arasında bir fazlalık vardır. Sol hizalama bu fazlalığı sağda bırakır. Sağ hizalama onu sola taşır. Ortalama onu böler. Tam hizalama her iki kenar da kutuyu karşılayana kadar satırın kendisini genişleterek fazlalığı ortadan kaldırır ve bunu yapmanın tek dürüst yolu glyphleri içeriden birbirinden uzaklaştırmaktır

İyi hizalamayı kötüden ayıran kural fazlalığı nereye koyduğunuzdur. İngilizce ve Latin ailesinin geri kalanı gibi kelimeler arasına boşluk koyarak yazan bir betik, her kelimeler-arası boşlukta doğal dikişlere sahiptir. Bu boşlukları genişletmek göze görünmezdir çünkü okuyucular kelime aralıklarının değişkenliğini zaten kabul eder. Çince Han karakterleri, Japonca kana veya Korece Hangul gibi kelime boşluğu olmadan yazan bir betik böyle dikişlere sahip değildir. Orada fazlalığın bitişik glyphler arasına eşit biçimde yayılması gerekir; bu, Japon dizgicilerin kintou-waritsuke, eşit aralık dediği ilkedir. CJK satırına Latin tarzı kelime-boşluğu germe uygulamak veya bir CJK satırının boşluk içerdiği tek yere tüm fazlalığı sıkıştırmak, amatör çıktıyı işaret eden nehirler ve boşluklar üretir

HotPDF'in alanın nereye gideceğine nasıl karar verdiği

HotPDF bu kararı satır başına değil boşluk başına alır. Bir satırı hizalarken bitişik her glyph çiftini dolaşır ve aralarında esnek bir sınır olup olmadığını sorgular. Bir sınır, her iki taraftan biri boşluk veya sekme olduğunda, yani Latin durumunda; ya da her iki taraf da CJK-kırılabilir karakterler olduğunda, yani eşit aralık durumunda esnek olarak kabul edilir. Bu sınırları sayar, satırın fazlalığını eşit olarak aralarında böler ve her uygun boşluğa bu payı ekler

Sonuç doğal olarak ortaya çıkar. Bir İngilizce satır yalnızca kelime boşluklarında esnek sınırlara sahiptir; dolayısıyla tüm fazlalık orada toplanır ve kelimeler birbirinden uzaklaşırken her kelimenin içindeki harfler doğal aralıklarını korur. Bir Han veya kana satırı neredeyse her glyph çifti arasında esnek bir sınıra sahiptir; dolayısıyla fazlalık tüm satır boyunca eşit biçimde dağılır ve bu betiklerin gerektirdiği tam glyph-arası eşit aralık elde edilir. Dahili boşluk içermeyen tek uzun bir Latin kelimesi içeren satırın esnek sınırı yoktur; bu nedenle HotPDF onu harfi harfine yırtmak yerine doğal genişliğinde bırakır. Karar her sınıra yerel olduğundan aynı mantık, özel durum işlemleri gerektirmeksizin tek satırda karma Latin ve CJK çalışmalarını da yönetir

Bir sınır her yerde kasıtlı olarak dışarıda bırakılır. Bir satırın son glyphinden sonraki konum hiçbir zaman boşluk olarak işlenmez; çünkü orada germe yalnızca sağ tarafta bir artık yeniden ortaya çıkarır ki bu hizalamanın tam tersidir

Son satırın neden olduğu gibi bırakıldığı

Paragrafın son satırı özeldir ve onu yanlış yapmak en yaygın hizalama hatasıdır. Bir paragrafın son satırı genellikle kısadır, çoğu zaman yalnızca birkaç kelimedir; ve onu tam sütun genişliğine germek bu kelimeleri sayfaya seyrek, kopuk bir sıra halinde dağıtır. Doğru tipografi son satırı doğal genişliğinde, sola hizalı olarak bırakır

HotPDF sondaki satırı konuma göre algılar. Metni satırlara sararken, az önce ayırdığı satırın sağlanan dizenin sonuna ulaşıp ulaşmadığını bilir. Bu son satır düz sol hizalamayla yayılır ve doğal genişliğini korur. Ondan önceki her satır her iki kenara da hizalanır. Metne yazdığınız zorla satır sonları olduğu gibi korunur; böylece kasıtlı kısa bir satır hiçbir zaman gerilmez. Okuyucu, son satırı doğal biçimde biten temiz dikdörtgen bir metin bloğu görür; bu da gözün beklediği şeydir

Hizalamayı yavaşlatan ölçüm maliyeti

Bir satırı hizalamak için tam genişliğini bilmeniz ve fazladan alanı tam olarak yerleştirebilmek için her glyph'in ilerlemesini bilmeniz gerekir. İlk uygulama bu sayıları bariz yoldan elde etti. Tüm satırı tam bir Unicode genişlik sorgusuyla ölçtü, ardından her glyph'in ilerlemesini fark alarak kurtarmak için ön ek üstüne ön ek ölçtü. N glyph'li bir satır için bu, ölçüm motoruna N+1 çağrıdır ve her çağrı tam bir GDI gidiş-dönüşüdür; işletim sisteminden metni şekillendirmesini, ölçmesini ve yanıtı geri vermesini ister

Satır başına bu ucuz görünür. Sayfa genelinde değildir. Yaklaşık kırk beş satır ve seksen karakterden oluşan yoğun bir A4 sayfası gövde metni alın. Satır başına N+1 gidiş-dönüşte bu her satır için yaklaşık 81 gidiş-dönüş ve sayfa için yaklaşık 3.645 anlamına gelir; neredeyse tamamı motorun az önce baktığı metni yeniden ölçmek için harcanır. Binlerce sayfa üreten bir toplu işte bu ek yük düzenleme süresine hâkimdir ve her gidiş-dönüş işleminiz ile grafik alt sistemi arasındaki sınırı geçer

N artı bir yerine tek çağrı

Düzeltme, küçük görünüp büyük getiri sağlayan türden bir değişikliktir. GDI, bir dizenin toplam genişliğini ve her glyph'in konumunu tek bir sorguda zaten raporlayabilir. HotPDF bunu GetWideCharAdvances aracılığıyla sunar; bu, N+1 yerine tek bir çağrıda kerning dahil her glyph'in doğal ilerlemesiyle bir dizi doldurur ve toplam genişliği döndürür. Dahili olarak _HPDFEmitJustifiedWideLine olan hizalama rutini, tüm ilerleme değerlerini bir kez ister, fazlalığı hesaplar, esnek sınırlar arasında dağıtır ve satırı yayar

Aynı A4 sayfası için satır başına ölçüm yaklaşık 81 gidiş-dönüşten bire düşer; bu nedenle sayfa yaklaşık 3.645 gidiş-dönüşten yaklaşık 45'e, seksen katlık bir düşüşe yakın bir değere iner. Çıktı bayt bayt aynıdır; çünkü ölçümle ilgili hiçbir şey değişmemiştir, yalnızca kaç kez istendiği değişmiştir. Aynı GDI motoru, aynı yazı tipi metrikleri, aynı kerning aynı sayıları besler. Yalnızca gidiş-dönüş sayısı düştü. Bir ölçüm zaten doğru olduğunda, doğru optimizasyon onu yaklaşık olarak hesaplamak değil, tekrar tekrar sormayı bırakmaktır

Satırın sayfaya nasıl ulaştığı

Fazlalık tahsis edildiğinde HotPDF satırı ExtTextOut ve glyph başına ilerleme dizisi olan Dx dizisiyle yayar. Her giriş, bir glyph'in başlangıcından diğerine olan mesafedir; bu, söz konusu glyph'in doğal ilerlemesi artı kendisini esnek bir sınırın izlediği durumdaki fazlalık payıdır. Bu, PDF görüntüleme modeline doğrudan eşlenir. Konumlandırılmış metin, glyph çalışmalarını açık yatay ayarlamalarla iç içe geçiren bir dizi olan TJ operatörüyle yazılır ve Dx değerleri tam olarak bu ayarlamalar olur. Bu nedenle fazladan alan, dolgu karakterleriyle sahte olarak oluşturulmak yerine tam alt nokta konumlarında glyphler arasına yerleştirilir ve bu nedenle hizalanmış bir HotPDF satırı, aşağı akış bir araç onu geri okursa doğru biçimde ölçülür

Hizalanmış paragraflar için ExtTextOut'u kendiniz çağırmazsınız. Giriş noktası, bir Unicode dizesini bir kutuya sarar ve istediğiniz hizalamayı uygulayan WideTextOutBox'tır. Metni kutu genişliğine sığan satırlara böler, her satırı kutu yüksekliği boyunca yerleştirir ve dikey oda bitmeden önce sığdırmayı başardığı karakter sayısını döndürür. Hizalama, hizalama sabit listesiyle seçilir

type
  THPDFJustificationType = (jtLeft, jtCenter, jtRight, jtJustify);

İlk üçü kendi kendini açıklayan sol, ortalamalı ve sağ hizalamadır. Dördüncüsü olan jtJustify, burada açıklanan her iki kenar da tam hizalamadır ve WideTextOutBox'ın betik-bilinçli aralığı açmak için okuduğu değerdir

Uygulamada bir paragrafı hizalamak

Eksiksiz bir örnek bir belge oluşturur, bir yazı tipi ayarlar ve tam hizalamayla bir kutuya bir paragraf döker. Aynı kod, betik-bilinç API'nin altında yer aldığından bayrak değişikliği olmaksızın hem Latin hem de CJK metnini hizalar

uses
  HPDFDoc;

procedure JustifyParagraph;
var
  Pdf: THotPDF;
  Body: WideString;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'Justified.pdf';
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', 11);

    Body :=
      'Full justification spreads the slack on each filled line so both ' +
      'edges meet the column, while the last line keeps its natural width. ' +
      'For scripts with word gaps the space lands between words; for ' +
      'scripts without them it spreads evenly between glyphs.';

    // X, Y, LineSpacing, BoxWidth, BoxHeight, Text, Align
    Pdf.CurrentPage.WideTextOutBox(72, 72, 4, 380, 240, Body, jtJustify);

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Aynı bloğu sola hizalı, ortalanmış veya sağa hizalı çizmek için yalnızca son bağımsız değişkeni jtLeft, jtCenter veya jtRight olarak değiştirin. Sarmalama, satır yerleşimi ve dönüş değeri aynı kalır. Dört yolun tamamını yönlendiren ölçülen genişlik, eski bayt bazlı ölçümün Latin-1'in ötesindeki her şeyi yanlış boyutlandırdığı yerde bir WideString'i doğru biçimde ölçen Unicode-bilinçli genişlik sorgusu olan GetWideTextWidth'ten gelir; bu da kutunun CJK ve vekil çifti metnini doğru yerde sarmasını sağlayan şeydir

Hizalama, daha büyük bir metin şekillendirme yığınının bir katmanıdır. Bir satır glyphlerini yeniden sıralayan veya birleştiren betikler içerdiğinde, buradaki aralık kararları karmaşık-betik metin şekillendirme hakkındaki makalemizde açıklanan çalışmanın üstüne oturur; bir yazı tipi seçmek istediğiniz tipografik çeşitler taşıdığında ise OpenType GSUB stilistik alternatiflerin nasıl yönetileceğine bakın. Tüm bunlar, bu blogda ele alınan daha geniş metin, düzenleme ve belge API'leriyle birlikte glyph ikame sorgularını taşıyan Delphi ve C++Builder için HotPDF Component'te birlikte gelir