Artikel Teknis

RtLTextOut di HotPDF: Teks PDF Kanan-ke-Kiri di Delphi

Kirim kalimat Arab يوضح ملف PDF هذا ke TextOut biasa, dan halaman yang kembali salah dalam dua hal sekaligus. Kata-katanya mengalir dari kiri ke kanan alih-alih dari kanan ke kiri, dan hurufnya berdiri terpisah dalam bentuk isolasi alih-alih menyambung menjadi kata yang utuh. Tidak ada yang error. Delphi tetap meng-compile, file tetap terbuka, dan seorang pemeriksa yang membaca bahasa Arab memberi tahu Anda bahwa hasilnya tidak terpakai. Perbaikannya adalah satu panggilan, bukan pergantian library: HotPDF mengalirkan teks kanan-ke-kiri melalui metode terpisah, RtLTextOut, yang menangani penataan ulang yang tidak dilakukan TextOut biasa. Halaman ini adalah referensi kerja untuk metode tersebut: signature dan parameternya, argumen charset yang memilih script, efek samping pada level dokumen, penyiapan font yang harus didahulukan, dan kegagalan yang benar-benar sampai ke support, masing-masing dengan perbaikannya

Signature dan parameter

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

X dan Y menambatkan run tersebut pada sistem koordinat milik halaman itu sendiri, diukur dari sudut kiri bawah dengan Y bertambah ke atas, titik asal yang sama yang dipakai setiap panggilan TextOut; RtLTextOut mengubah urutan glyph, bukan dari mana halaman itu diukur. angle memutar baseline persis seperti pada TextOut, jadi 0 menggambar garis horizontal. Text adalah string dalam urutan logis, urutan sebagaimana Anda mengetiknya, dan overload kedua menerima data UTF-16 yang sama sebagai buffer PWORD mentah dengan jumlah code unit yang eksplisit, bentuk yang dipakai ketika teksnya datang dari sebuah API alih-alih dari string Delphi. Pada versi Delphi lama yang mendahului resolusi overload untuk tipe-tipe ini, bentuk string-nya diekspos dengan nama RtLTextOutStr beserta daftar parameter yang identik

Pembagian tugas antara kedua panggilan output itu bersifat ketat. TextOut menggambar codepoint dalam urutan yang Anda berikan, yang benar untuk Latin, Sirilik, dan CJK serta salah untuk Arab dan Ibrani. RtLTextOut menata ulang setiap baris ke dalam urutan visual kanan-ke-kiri lebih dulu, baru menggambar, sambil menjaga kata Latin dan angka yang tersemat tetap terbaca dari kiri ke kanan di dalam baris tersebut. HotPDF sengaja memisahkan kedua metode itu alih-alih menebak arah dari karakternya, sehingga pilihan metode mana yang dipanggil adalah pilihan perilaku script mana yang Anda dapatkan; pakai RtLTextOut untuk run kanan-ke-kiri, TextOut untuk yang lain, dan jangan pernah mengalirkan yang satu lewat yang lain. Mengapa penataan ulang itu ada sama sekali, apa sebenarnya yang dilakukan Unicode Bidirectional Algorithm dan penyambungan kontekstual Arab, serta di mana shaping HotPDF berhenti adalah topik tulisan pendamping tentang shaping teks Arab dan RTL dengan HotPDF; semua yang di bawah ini adalah penyiapan praktisnya

Diagram cara RtLTextOut menata ulang sebuah baris campuran Arab dan Latin ke urutan visual kanan-ke-kiri sebelum menggambarnya ke dalam PDF
RtLTextOut menata ulang setiap baris ke urutan visual sebelum menggambar: run kanan-ke-kiri mempertahankan urutannya sementara kata Latin dan angka yang tersemat terbaca dari kiri ke kanan di dalam baris

Argumen charset yang menentukan script-nya

Yang memberi tahu RtLTextOut apakah ia sedang menata Arab atau Ibrani bukanlah metodenya, melainkan font-nya. SetFont menerima sebuah charset Windows sebagai argumen keempatnya, dan nilai itulah yang membawa aturan script ke dalam panggilan kanan-ke-kiri: 178 memilih Arab, 177 memilih Ibrani. Setel charset-nya, lalu gambar, dan kedua baris di bawah ini keluar dalam urutan baca yang benar tanpa konfigurasi tambahan apa pun

// Arab: charset 178 memberi tahu RtLTextOut untuk menerapkan aturan Arab
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');

// Ibrani: charset 177 mengalihkan aturannya ke Ibrani
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');

Satu detail urutan mudah terlewat: SetFont harus datang lebih dulu dan harus diulang setelah setiap AddPage, karena font yang aktif, termasuk charset-nya, tidak bertahan melewati pergantian halaman. Lupakan pengulangan itu dan halaman kedua akan jatuh kembali ke font apa pun yang sedang aktif, yang untuk bahasa Arab biasanya berarti kotak-kotak kosong

Ia tidak membalik teks yang sudah Anda balik

Satu kesalahan yang paling banyak menelan waktu debugging di sini adalah memberi RtLTextOut sebuah string yang sudah Anda balik sendiri. Orang sampai ke metode ini setelah percobaan pertama dengan TextOut biasa keluar terbalik, dan tambalan sementara yang umum adalah membalik karakternya di dalam kode sebelum menggambar. RtLTextOut sudah membalik secara internal, sehingga string yang sudah dibalik lebih dulu akan dibalik untuk kedua kalinya dan mendarat persis di tempatnya semula. Berikan teksnya dalam urutan logis, urutan sebagaimana Anda mengetik dan membacanya keras-keras, lalu biarkan panggilan itu yang menata ulang

Jebakannya lebih jahat daripada pembalikan biasa karena string yang terbalik dua kali bisa tampak benar untuk satu frasa uji yang seluruhnya berbahasa Arab, lalu rusak begitu sebuah baris membawa kata Latin atau angka. Di dalam baris kanan-ke-kiri, run tersemat seperti itu seharusnya terbaca dari kiri ke kanan, dan pembalikan manual merusak penyarangan tersebut sementara kasus Arab murni kebetulan selamat darinya. Jadi bug itu lolos dari smoke test pertama Anda dan muncul belakangan pada faktur sungguhan yang memuat nomor rekening. Singkirkan setiap pembalikan manual begitu Anda beralih ke RtLTextOut

Efek samping Direction yang layak diketahui

Memanggil RtLTextOut mengubah lebih dari sekadar baris yang sedang Anda gambar. Ia juga membalik preferensi arah baca dokumen menjadi kanan-ke-kiri, hal yang sama yang jika tidak begitu akan Anda setel sendiri lewat properti Direction. Setter tersebut menambahkan vpDirection ke ViewerPreferences dokumen, yang memberi tahu viewer bagaimana menyusun tampilan dua halaman berdampingan dan dari sisi mana tata letak halaman berhadapan dimulai. Ketika seluruh dokumennya berbahasa Arab atau Ibrani, inilah persis yang Anda inginkan, dan Anda mendapatkannya secara cuma-cuma

Hal ini layak diketahui justru karena ia tidak terlihat pada satu halaman. Jika dokumennya sebagian besar kiri-ke-kanan dengan satu blok kanan-ke-kiri, panggilan RtLTextOut pertama tetap akan menjungkirkan preferensi seluruh file, dan tidak ada apa pun dalam bukti satu halaman Anda yang menunjukkannya. Gejalanya muncul berminggu-minggu kemudian ketika seseorang mencetak buklet dupleks dan halaman berdampingannya keluar tercermin. Jika itu bukan yang Anda inginkan, setel Direction kembali secara eksplisit setelah run kanan-ke-kiri:

// RtLTextOut sudah menyetel arah dokumen ke RightToLeft;
// pulihkan kiri-ke-kanan jika dokumennya didominasi LTR
Pdf.Direction := LeftToRight;

Untuk dokumen yang memang benar-benar dibaca dari kanan ke kiri, biarkan saja. Intinya adalah mengetahui bahwa panggilan itu berefek ke seluruh dokumen sehingga kejutan buklet tadi tidak pernah terjadi

Daftarkan font yang Anda sertakan, bukan yang Anda harap sudah terpasang

Semua penataan ulang itu tidak ada artinya jika font-nya tidak punya glyph untuk digambar. Kegagalan klasiknya adalah laporan yang ter-render sempurna di mesin developer, tempat Arial Unicode MS kebetulan tersedia, lalu keluar sebagai barisan kotak kosong di server pelanggan tempat Windows diam-diam menggantinya dengan font yang sama sekali tidak punya cakupan Arab. Obatnya adalah berhenti memercayai font sistem yang terpasang dan mendaftarkan satu font yang Anda sertakan bersama aplikasi

// Sertakan font Arab yang sudah pasti dan daftarkan sebelum menggambar
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');

Dua batasan ikut menumpang bersama pendaftaran itu. Font yang dimasukkan lewat RegisterUnicodeTTF akan ditanamkan, dan penanganan Unicode tertanam milik HotPDF membutuhkan dokumen pada PDF 1.5 atau lebih baru; hal itu baru menggigit jika ada sesuatu di hilir yang bersikeras pada PDF 1.4, namun ketika itu terjadi kegagalannya senyap. Yang satu lagi bersifat legal alih-alih teknis: file TrueType membawa bit izin penanaman, dan sebuah typeface yang tampak baik-baik saja di layar bisa jadi dilisensikan dengan cara yang melarang penyertaannya di dalam dokumen pelanggan. Pastikan lisensinya sebelum Anda menanamkan, bukan setelah ada keluhan

Sebuah contoh console yang lengkap

Menyatukan semua bagiannya, berikut sebuah program mandiri yang menulis satu halaman dengan sebaris teks Arab, sebaris Ibrani, dan sebaris campuran yang membawa nama produk berhuruf Latin. Setiap blok menyetel charset-nya sendiri, lalu menggambar dalam urutan logis

program RtLTextOutDemo;

{$APPTYPE CONSOLE}

uses
  HPDFDoc;   // unit utama HotPDF

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

    // Judul berhuruf Latin melewati jalur TextOut biasa
    Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
    Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');

    // Arab: charset 178, urutan logis, RtLTextOut yang menata ulang
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    Pdf.CurrentPage.RtLTextOut(400, 720, 0,
      'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');

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

    // Baris campuran: kata Latin yang tersemat tetap terbaca kiri ke kanan
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    Pdf.CurrentPage.RtLTextOut(400, 640, 0,
      'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');

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

Jalankan lalu buka hasilnya. Baris Arab dan Ibrani terbaca dari kanan ke kiri, hurufnya menyambung di tempat script itu memang menyambungkannya, dan pada baris terakhir token HotPDF duduk dari kiri ke kanan di dalam run Arab. Penyarangan itu adalah hasil bidirectional yang benar, bukan bug, meskipun pemeriksa yang baru pertama kali melihatnya rutin melaporkannya sebagai bug; artikel shaping yang ditautkan di atas menjelaskan mengapa aturan Unicode menuntutnya dan bagaimana merumuskan kriteria penerimaan Anda supaya laporan itu tidak pernah masuk

Galat yang umum dan perbaikannya

Setiap kegagalan di bawah ini pernah muncul dalam thread support sungguhan, dan masing-masing berpangkal pada salah satu bagian di atas

  • Output terbaca terbalik atau berantakan pada baris campuran — string-nya dibalik dengan tangan sebelum panggilan, biasanya sisa akal-akalan dari percobaan dengan TextOut. Hapus setiap pembalikan manual dan berikan urutan logis; RtLTextOut membalik secara internal
  • Huruf tercetak terputus-putus dalam bentuk isolasi — teksnya melewati TextOut biasa, atau SetFont dipanggil tanpa charset kanan-ke-kiri. Gambar dengan RtLTextOut dan berikan 178 untuk Arab atau 177 untuk Ibrani sebagai argumen keempat SetFont
  • Kotak kosong di mesin pelanggan — Windows mengganti font dengan yang tidak punya cakupan Arab atau Ibrani. Berhenti menyebut font yang terpasang; daftarkan typeface yang Anda sertakan lewat RegisterUnicodeTTF lalu SetFont dengan nama itu
  • Halaman kedua ter-render dengan font yang salah — font yang aktif tidak bertahan melewati AddPage. Ulangi panggilan SetFont, termasuk charset-nya, setelah setiap pergantian halaman
  • Halaman berdampingan dupleks tercetak tercermin pada dokumen yang mayoritas LTR — panggilan RtLTextOut pertama menjungkirkan Direction dokumen sebagai efek samping. Setel Pdf.Direction := LeftToRight setelah run kanan-ke-kiri
  • Teks Unicode tertanam merosot diam-diam di hilir — ada sesuatu di dalam pipeline yang memaksa PDF 1.4, sementara penanganan Unicode tertanam milik HotPDF membutuhkan 1.5 atau lebih baru. Naikkan versi dokumen atau singkirkan batasan di hilir itu

Sebelum formatnya dirilis, lakukan verifikasi yang melampaui pengamatan mata: salin teksnya keluar dari viewer, jalankan pencarian di dalam dokumen, buka file-nya di mesin tanpa font pengembangan Anda, dan sodorkan satu dokumen sungguhan kepada pembaca penutur asli. Checklist verifikasi lengkap, peta cakupan per script, dan korpus string uji yang layak dibangun semuanya ada di artikel pendamping tentang shaping teks Arab dan RTL dengan HotPDF

Panggilan RtLTextOut, SetFont, dan RegisterUnicodeTTF yang ditampilkan di sini adalah bagian dari HotPDF Delphi Component untuk Delphi dan C++Builder