Teknik Makale

HotPDF'te RtLTextOut: Delphi'de Sağdan Sola PDF Metni

Arapça cümleyi يوضح ملف PDF هذا düz TextOut'a gönderin ve geri dönen sayfa tek seferde iki şekilde yanlış olur. Sözcükler sağdan sola değil soldan sağa akar ve harfler bağlantılı sözcükler oluşturmak yerine ayrık hallerinde durur. Hiçbir şey hata vermez. Delphi derlenir, dosya açılır ve Arapça okuyan bir inceleyici çıktının kullanılamaz olduğunu söyler. Çözüm bir kütüphane değişimi değil, tek çağrıdır: HotPDF, sağdan sola metni düz TextOut'un yapmayacağı yeniden sıralamayı halleden ayrı bir metot olan RtLTextOut üzerinden geçirir. Bu sayfa o metodun çalışma referansıdır: imza ve parametreleri, betiği seçen charset argümanı, belge düzeyindeki yan etki, önce gelmesi gereken font kurulumu ve gerçekten desteğe ulaşan hatalar; her biri çözümüyle birlikte

İmza ve parametreler

procedure RtLTextOut(X, Y: Single; angle: Extended;
  Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
  Text: PWORD; TextLength: Integer); overload;

X ve Y, diziyi sayfanın kendi koordinat sisteminde çapalar; her TextOut çağrısının kullandığı, sol üst köşeden ölçülen ve Y'nin aşağıya doğru büyüdüğü aynı orijindir. RtLTextOut glif sırasını değiştirir, sayfanın ölçüm orijinini değil. X ayrıca çizilen dizinin sol ucudur, sağ kenarı değil: çağrı glifleri görsel sıraya dizer ve X'ten itibaren soldan sağa dizer; bu yüzden sağa hizalı bir satır, sağ kenar boşluğunun eksi satır genişliği kadarında başlar ve bu genişliği, çizimi yapacak SetFont'tan sonra CurrentPage.GetWideTextWidth ile ölçersiniz. angle taban çizgisini TextOut'taki gibi döndürür; 0 yatay bir satır çizer. Text, mantıksal düzendeki, yani yazacağınız düzendeki dizgedir; ikinci overload aynı UTF-16 verisini açık bir code-unit sayısıyla ham bir PWORD tamponu olarak alır ve metin bir Delphi dizesi yerine bir API'den geldiğinde kullanılacak form budur. Bu türler için overload çözümünden önceki eski Delphi sürümlerinde, dize formu aynı parametre listesiyle RtLTextOutStr adıyla açığa çıkarılır

İki çıktı çağrısı arasındaki iş bölümü katıdır. TextOut, kod noktalarını geçtiğiniz sırada çizer; bu Latin, Kiril ve CJK için doğrudur, Arapça ve İbranice için yanlış. RtLTextOut her satırı önce görsel sağdan sola düzene sokar, sonra çizer; satırın içindeki gömülü Latin sözcükleri ve rakamlar soldan sağa okunmaya devam eder. HotPDF, yönü karakterlerden tahmin etmek yerine iki metodu bilinçli olarak ayrı tutar; hangisini çağıracağınız seçimi, hangi betik davranışını elde edeceğinizin seçimidir. Sağdan sola diziler için RtLTextOut'u, geri kalan her şey için TextOut'u kullanın ve asla birini diğerine yönlendirmeyin. Yeniden sıralamanın neden var olduğunu, Unicode Çift Yönlü Algoritması ile Arapça bağlamsal birleşmenin gerçekte ne yaptığını ve HotPDF'in şekillendirmesinin bunları nasıl ele aldığını, HotPDF ile Arapça ve RTL metin şekillendirme üzerine tamamlayıcı yazı ele alır; aşağıdaki her şey pratik kurulumdur

RtLTextOut'un karışık bir Arapça ve Latin satırını PDF'e çizmeden önce görsel sağdan sola sıraya nasıl yeniden dizdiğinin diyagramı
RtLTextOut her satırı çizmeden önce görsel sıraya yeniden dizer: sağdan sola diziler sırasını korurken gömülü Latin sözcükleri ve rakamlar satırın içinde soldan sağa okunur

Charset argümanı betiği belirler

RtLTextOut'a Arapça mı yoksa İbranice mi yerleştirdiğini söyleyen şey metot değil, yazı tipidir. SetFont, dördüncü argümanı olarak bir Windows charset alır ve o değer, betik kurallarını sağdan sola çağrıya taşır: 178 Arapçayı, 177 İbraniceyi seçer. Charset'i ayarlayın, sonra çizin ve aşağıdaki iki satır herhangi bir ek yapılandırma olmadan doğru okuma sırasında çıkar

// Arapça: charset 178, RtLTextOut'un Arapça kurallarını uygulamasını sağlar
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 100, 0, 'يوضح ملف PDF هذا');

// İbranice: charset 177 kuralları İbranice'ye çevirir
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 140, 0, 'קובץ PDF זה');

Gözden kaçırması kolay bir sıralama ayrıntısı: SetFont önce gelmeli ve her AddPage'den sonra tekrarlanmalıdır, çünkü charset dâhil geçerli yazı tipi, bir sayfa geçişinden sağ çıkmaz. Tekrarı unutun ve ikinci sayfa etkin olan yazı tipine geri döner ki bu Arapça için genellikle boş kutular anlamına gelir

Zaten tersine çevirdiğiniz metni tersine çevirmez

Burada en çok hata ayıklama zamanını yutan tek hata, RtLTextOut'a elle önceden çevirdiğiniz bir dize beslemektir. İnsanlar bu metoda, sade TextOut ile ilk denemeleri ters çıktıktan sonra ulaşır ve yaygın bir geçici çözüm, çizmeden önce karakterleri kodda tersine çevirmektir. RtLTextOut kendi başına dahili olarak tersine çevirir, bu yüzden önceden tersine çevrilmiş bir dize ikinci kez tersine çevrilir ve tam olarak başladığı yere geri döner. Metni mantıksal sırada, yazacağınız ve yüksek sesle okuyacağınız sırada geçin ve yeniden sıralamayı çağrının yapmasına izin verin

Tuzak sade bir ters çevirmeden daha sinsidir, çünkü iki kez tersine çevrilmiş bir dize, tamamı Arapça bir test ifadesi için doğru görünebilir ve sonra bir satır Latin bir sözcük veya bir sayı taşıdığı anda bozulur. Sağdan sola bir satırın içinde o gömülü dizilerin soldan sağa okunması gerekir ve elle ters çevirme, saf Arapça durum bunu tesadüfen atlatırken bu yuvalamayı mahveder. Böylece hata, ilk duman testinizden geçer ve daha sonra içinde bir hesap numarası olan gerçek bir faturada ortaya çıkar. RtLTextOut'a geçtiğiniz an her elle ters çevirmeyi kaldırın

Bilmeye değer Direction yan etkisi

RtLTextOut'u çağırmak, çizdiğiniz satırdan daha fazlasını değiştirir. Ayrıca belgenin okuma yönü tercihini sağdan sola çevirir; bu, aksi halde Direction özelliğiyle kendiniz ayarlayacağınız şeydir. O setter, belgenin ViewerPreferences'ine vpDirection ekler; bu da görüntüleyiciye iki sayfalı yaylımları nasıl düzenleyeceğini ve karşılıklı sayfa düzeninin hangi taraftan başlayacağını söyler. Tüm belge Arapça ya da İbraniceyse tam olarak istediğiniz budur ve hiçbir ek iş yapmadan elde edersiniz

Bunun bilinmeye değer olmasının nedeni tam olarak tek bir sayfada görünmez olmasıdır. Belge çoğunlukla soldan sağaysa ve tek bir sağdan sola blok içeriyorsa, ilk RtLTextOut çağrısı yine de tüm dosyanın tercihini yatıracaktır ve tek sayfalık kanıtınızdaki hiçbir şey bunu göstermez. Belirti, biri dupleks bir kitapçık yazdırdığında ve yayılmalar aynalanmış çıktığında haftalar sonra ortaya çıkar. İstediğiniz bu değilse, sağdan sola diziden sonra Direction'ı açıkça geri ayarlayın:

// RtLTextOut belge yönünü zaten SağdanSola ayarladı;
// belge ağırlıklı soldan sağa ise soldan sağa geri dön
Pdf.Direction := LeftToRight;

Gerçekten sağdan sola okunan bir belge için, ona dokunmayın. Amaç, çağrının belge genelinde bir etkisi olduğunu bilmektir, böylece kitapçık sürprizi asla gerçekleşmez

Kurulu olmasını umduğunuz değil, gönderdiğiniz yazı tipini kaydedin

Yazı tipinin çizecek glifi yoksa, yeniden sıralamanın hiçbiri önemli değildir. Klasik başarısızlık, Arial Unicode MS'in var olduğu geliştiricinin makinesinde kusursuzca işlenen ve Windows'un sessizce hiç Arapça kapsaması olmayan bir yazı tipiyle değiştirdiği bir müşterinin sunucusunda boş kutular halinde çıkan bir rapordur. Çözüm, kurulu sistem yazı tiplerine güvenmeyi bırakıp uygulamayla birlikte gönderdiğiniz bir tanesini kaydetmektir

// Bilinen bir Arapça yazı tipini dağıt ve çizimden önce kaydet
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 100, 0, 'يوضح ملف PDF هذا');

Kayıtla birlikte iki sınır gelir. RegisterUnicodeTTF aracılığıyla getirilen bir yazı tipi gömülür ve HotPDF'in gömülü Unicode işleyişi, belgenin PDF 1.5 veya sonrası olmasına ihtiyaç duyar; bu yalnızca aşağı akıştaki bir şey PDF 1.4'te ısrar ederse ısırır, ama ısırdığında başarısızlık sessizdir. Diğeri teknik değil hukukidir: TrueType dosyaları gömme-izni bitleri taşır ve ekranda gayet iyi görünen bir yazı yüzü, onu müşteri belgelerinin içinde göndermeyi yasaklayan bir şekilde lisanslanmış olabilir. Bir şikayetten sonra değil, gömmeden önce lisansı doğrulayın

Eksiksiz bir konsol örneği

Parçaları bir araya getirerek, işte bir Arapça satır, bir İbranice satır ve bir Latin ürün adı taşıyan karışık bir satırla tek bir sayfa yazan bağımsız bir program. Her blok kendi charset'ini ayarlar, sonra mantıksal sırada çizer

program RtLTextOutDemo;

{$APPTYPE CONSOLE}

uses
  HPDFDoc;   // HotPDF ana birimi

const
  RightMargin = 545;   // A4 595 pt genişliğindedir: 50 pt sağ kenar boşluğu

// RtLTextOut, yeniden dizilen diziyi X'ten itibaren soldan sağa dizler,
// bu yüzden sağa hizalı satır, kenar boşluğu eksi ölçülen genişlikten başlar
procedure RightAlignedRtL(Page: THPDFPage; Y: Single; const S: WideString);
begin
  Page.RtLTextOut(RightMargin - Page.GetWideTextWidth(S), Y, 0, S);
end;

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'RtLTextOut.pdf';
    Pdf.BeginDoc;

    // Latince bir başlık sıradan TextOut yolundan geçer
    Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
    Pdf.CurrentPage.TextOut(40, 60, 0, 'Right-to-left text with HotPDF');

    // Arapça: charset 178, mantıksal sıra, sıralamayı RtLTextOut yapar
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    RightAlignedRtL(Pdf.CurrentPage, 100,
      'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');

    // İbranice: charset 177
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
    RightAlignedRtL(Pdf.CurrentPage, 140,
      'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');

    // Karışık satır: gömülü Latince sözcük hâlâ soldan sağa okunur
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    RightAlignedRtL(Pdf.CurrentPage, 180,
      'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');

    Pdf.EndDoc;
    Writeln('Wrote RtLTextOut.pdf');
  finally
    Pdf.Free;
  end;
end.

Çalıştırın ve sonucu açın. Arapça ve İbranice satırlar sağdan sola okunur, betiğin birleştirdiği yerlerde harfler birleşir ve son satırda HotPDF belirteci Arapça dizinin içinde soldan sağa oturur. O yuvalama, ilk kez gözden geçirenler sıklıkla onu bir hata olarak bildirse de, bir hata değil doğru çift yönlü sonuçtur; yukarıda bağlanan şekillendirme yazısı, Unicode kurallarının bunu neden gerektirdiğini ve raporun asla açılmaması için kabul kriterlerinizi nasıl ifade edeceğinizi açıklar

Yaygın hatalar ve çözümleri

Aşağıdaki her başarısızlık gerçek bir destek başlığında ortaya çıkmıştır ve her biri yukarıdaki bölümlerden birine dayanır

  • Çıktı ters okunuyor veya karışık satırlarda bozuluyor — dize çağrıdan önce elle tersine çevrilmişti, genellikle bir TextOut denemesinden kalan bir geçici çözüm. Her elle ters çevirmeyi silin ve mantıksal sırayı geçin; RtLTextOut dahili olarak tersine çevirir
  • Harfler izole biçimlerde bağlantısız yazdırılıyor — metin sade TextOut'tan geçti veya SetFont sağdan sola bir charset olmadan çağrıldı. RtLTextOut ile çizin ve dördüncü SetFont argümanı olarak Arapça için 178, İbranice için 177 geçin
  • Müşterinin makinesinde boş kutular — Windows, Arapça veya İbranice kapsaması olmayan bir yazı tipiyle değiştirdi. Kurulu yazı tiplerini adlandırmayı bırakın; RegisterUnicodeTTF ile gönderdiğiniz bir yazı yüzünü kaydedin ve SetFont'u o adla çağırın
  • İkinci sayfa yanlış yazı tipinde işleniyor — geçerli yazı tipi AddPage'den sağ çıkmaz. Her sayfa geçişinden sonra charset dâhil SetFont çağrısını tekrarlayın
  • Çoğunlukla LTR bir belgede dupleks yayılmalar aynalanmış yazdırılıyor — ilk RtLTextOut çağrısı bir yan etki olarak belgenin Direction'ını çevirdi. Sağdan sola diziden sonra Pdf.Direction := LeftToRight ayarlayın
  • Gömülü Unicode metni aşağı akışta sessizce bozuluyor — hattaki bir şey PDF 1.4'ü zorluyor ve HotPDF'in gömülü Unicode işleyişi 1.5 veya sonrasına ihtiyaç duyuyor. Belge sürümünü yükseltin veya aşağı akış kısıtlamasını kaldırın

Format gönderilmeden önce, göz kararından öteye doğrulayın: metni görüntüleyiciden geri kopyalayın, belge içi aramayı çalıştırın, dosyayı geliştirme yazı tipleriniz olmayan bir makinede açın ve gerçek bir belgeyi anadili konuşan bir okuyucunun önüne koyun. Tam doğrulama denetim listesi, betik başına kapsama haritası ve oluşturmaya değer test-dizesi külliyatı, HotPDF ile Arapça ve RTL metin şekillendirme üzerine tamamlayıcı yazıda yer alır

Burada gösterilen RtLTextOut, SetFont ve RegisterUnicodeTTF çağrıları, Delphi ve C++Builder için HotPDF Bileşeninin bir parçasıdır