Artikel Teknis

Font dan Teks PDF: Mengapa Glyph Berubah Jadi Kotak

PDF yang terlihat sempurna di mesin Anda dan dirender sebagai deretan kotak kosong di mesin orang lain adalah cacat font paling umum dalam perangkat lunak dokumen, dan itu hampir tidak pernah berarti teksnya salah. Karakternya utuh, encodingnya baik, glyphnya hanya tidak ada. Yang berubah antara dua mesin adalah font apa yang terinstal di sistem operasi, dan kesenjangan antara file portabel dan file yang rapuh adalah satu keputusan yang dibuat saat halaman ditulis: apakah font ikut pergi di dalam PDF atau diasumsikan hadir di ujung tujuan

Memahami mengapa hal itu terjadi, dan mengapa kegagalan terpisah menghasilkan teks yang terlihat dapat dicari namun saat disalin keluar menjadi sampah, berarti melihat bagaimana PDF menyimpan teks. PDF tidak menyimpan kalimat. PDF menyimpan kode glyph ditambah program font ditambah tabel yang memetakan satu ke yang lain, dan setiap bug rendering atau ekstraksi ada dalam celah antara ketiganya. Berikut adalah panduan tentang mesin tersebut, berdasarkan ISO 32000, dengan pemanggilan Delphi yang mengontrolnya di mana penting

Karakter, kode, dan glyph adalah tiga hal yang berbeda

Kosakata ini membingungkan orang karena percakapan sehari-hari menciutkan tiga ide berbeda menjadi kata "huruf." Karakter adalah unit penulisan abstrak, gagasan tentang huruf kapital A, diidentifikasi dalam Unicode sebagai U+0041. Glyph adalah bentuk yang digambar, garis dan batang yang digunakan font tertentu untuk menggambarkan karakter tersebut. Di antara keduanya ada kode: byte atau byte-byte dalam content stream yang memberitahu penampil glyph mana dalam font saat ini yang harus dilukis

PDF bekerja dalam kode. Saat content stream menampilkan sebuah string, byte tersebut adalah indeks ke dalam font yang aktif, bukan Unicode. Encoding font memutuskan bahwa kode 65 berarti "gambar glyph yang diarsipkan di bawah 65," dan tidak ada dalam operasi tersebut yang tahu hasilnya terlihat seperti huruf A bagi manusia. Itulah yang membuat PDF dirender secara identik di mana pun ia dapat menemukan glyphnya, dan itulah juga mengapa ekstraksi adalah masalah yang terpisah dari tampilan: menggambar hanya membutuhkan kode-ke-glyph, membaca membutuhkan kode-ke-Unicode, dan itu adalah dua tabel berbeda yang dapat tidak sependapat atau hilang secara independen

Jenis font yang sebenarnya akan Anda temui

ISO 32000 mendefinisikan beberapa tipe kamus font, dan dalam praktiknya dokumen yang Anda terima atau hasilkan menggunakan salah satu dari tiga. Mengetahui mana yang Anda lihat menjelaskan sebagian besar dari apa yang bisa salah

Type 1 adalah format garis Adobe PostScript asli, dibangun dari kurva Bezier kubik. Empat belas font standar yang harus disediakan setiap pembaca yang sesuai, keluarga Helvetica, Times, Courier, Symbol, dan ZapfDingbats, adalah Type 1, dan kamus font yang menamai salah satunya secara hukum dapat menghilangkan program font. Itulah satu-satunya kasus di mana membiarkan font tidak tertanam aman berdasarkan spesifikasi dan bukan hanya keberuntungan. Untuk wajah Type 1 lainnya program harus tertanam atau penampil mensubstitusinya, biasanya dengan font yang serupa secara metrik namun berbeda secara visual

TrueType menggunakan kurva kuadratik dan berasal dari dunia Apple dan Microsoft. Inilah yang biasanya menjadi font sistem, dan yang paling sering Anda tanamkan. Font TrueType sederhana dalam PDF terbatas pada kode satu byte, sehingga satu font tersebut dapat mengalamatkan paling banyak 256 glyph sekaligus. Batasan tersebut adalah alasan struktural mengapa aksara CJK dan aksara besar lainnya tidak dapat menggunakan font sederhana

Type 0, font komposit atau keyed-CID, adalah jawaban untuk keterbatasan tersebut. Font ini menggunakan kode multi-byte dan CMap untuk merutekannya melalui CIDFont turunan, yang garisnya sendiri adalah TrueType atau CFF/Type 1. Ini adalah satu-satunya tipe font yang dapat membawa ribuan glyph, sehingga PDF mana pun yang berisi bahasa Tionghoa, Jepang, Korea, atau campuran multibahasa yang luas menggunakan Type 0 apakah penulisnya memikirkannya atau tidak. Harga yang harus dibayar adalah kerumitan: lebih banyak komponen bergerak, lebih banyak yang harus benar baik untuk rendering maupun ekstraksi

One TrueType font rendered at 12, 18, 24, and 36 points in a PDF, showing that a single embedded outline scales to any size

Satu detail di balik gambar tersebut menentukan ukuran file. Font adalah pustaka garis, bukan bitmap berukuran tetap, sehingga program yang sama yang tertanam melayani setiap ukuran titik di halaman. Penskalaan adalah transformasi yang diterapkan pada saat penggambaran, itulah mengapa judul dan teks isinya berbagi satu wajah yang tertanam dan mengapa biaya embedding adalah per font, bukan per ukuran

Embedding adalah perbedaan antara portabel dan rapuh

Embedding berarti program font, data garis yang sebenarnya, ditulis ke dalam PDF sebagai stream. Pembaca di mesin yang belum pernah mengenal font Anda membaca garis tersebut langsung dari file dan menggambar glyph yang tepat. Lewati embedding dan Anda bertaruh bahwa tujuannya memiliki font dengan nama yang sama; ketika tidak, penampil mundur ke pengganti. Untuk empat belas standar substitusi tersebut didefinisikan dan tidak berbahaya. Untuk semua yang lain, hasilnya berkisar dari hampir cocok dalam jenis huruf yang berbeda hingga hasil kotak kosong ketika tidak ada pengganti yang mencakup aksara tersebut

Dengan HotPDF kontrolnya adalah satu properti, yang diatur sebelum dokumen dibuka. FontEmbedding memberitahu pustaka untuk mengemas wajah yang digambar ke dalam file:

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'report.pdf';
    Pdf.Compression := cmFlateDecode;
    Pdf.FontEmbedding := True;          // outlines travel inside the file
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Calibri', [], 11);
    Pdf.CurrentPage.TextOut(72, 760, 0, 'This renders the same on a machine without Calibri.');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Urutannya bukan kosmetik. BeginDoc adalah tempat HotPDF mengkomit struktur dokumen, sehingga FontEmbedding harus bernilai true sebelum pemanggilan tersebut. Tetapkan setelahnya dan tidak ada error, tidak ada peringatan, hanya file yang diam-diam keluar tanpa font-nya. Itu adalah jenis bug terburuk: melewati semua pengujian di mesin pengembang, di mana font kebetulan terinstal, dan hanya muncul di mesin pelanggan, di mana tidak

Embedding juga merupakan tempat di mana lisensi bertemu rekayasa. Program font membawa flag yang mendeskripsikan apakah boleh tertanam secara bebas, hanya untuk pratinjau, atau sama sekali tidak. Menghormati flag-flag tersebut adalah tanggung jawab Anda, bukan renderer, dan "berhasil" tidak sama dengan "diizinkan."

Subsetting: tanamkan hanya glyph yang Anda gunakan

Embedding penuh menulis seluruh program font ke dalam file. Wajah TrueType CJK besar bisa mencapai beberapa megabyte, dan menanamkannya secara keseluruhan untuk menampilkan selusin karakter adalah pemborosan yang berganda di seluruh dokumen multi-halaman. Subsetting memecahkan ini dengan menulis hanya glyph yang direferensikan dokumen, kemudian mengganti nama font dengan tag enam huruf dan tanda plus, bentuk ABCDEF+Calibri dalam daftar font PDF yang disubset, sehingga pembaca tidak pernah mengacaukan wajah parsial dengan font sistem penuh dengan nama yang sama

Untuk sebagian besar dokumen yang dihasilkan, subsetting adalah default yang tepat. Ini menjaga ukuran file proporsional terhadap konten daripada terhadap font sumber, yang paling penting untuk font multibahasa besar yang sinon akan mendominasi file. Satu peringatan adalah bahwa subset hanya berisi apa yang digunakan pada saat pembuatan. Jika proses hilir mencoba menambahkan teks ke font yang disubset nanti, glyph yang dibutuhkannya mungkin tidak ada dalam file, kendala nyata pada pengeditan bertahap PDF orang lain

Font Unicode dan masalah kotak CJK

Ketika teksnya bukan Latin biasa, jalur font-sederhana kehabisan cara, dan solusinya adalah mendaftarkan font yang mampu Unicode secara eksplisit dan membiarkan HotPDF membangun font Type 0 darinya. RegisterUnicodeTTF memuat file TrueType berdasarkan path; setelah itu nama yang terdaftar dapat digunakan di SetFont seperti font lainnya:

Pdf.FontEmbedding := True;
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansCJKsc-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('NotoSansCJKsc-Regular', [], 14);
Pdf.CurrentPage.TextOut(72, 720, 0, '你好,世界 こんにちは 안녕하세요');
Pdf.EndDoc;

Dua hal yang membuat atau menghancurkan ini. Font harus mencakup aksara dalam string: TrueType Latin-only tidak akan menumbuhkan glyph Tionghoa karena Anda memintanya, dan hasilnya adalah kotak kosong lagi, kali ini karena glyph tersebut memang tidak ada dalam wajah tersebut. Dan embedding harus tetap aktif, karena font Type 0 yang dirakit dari TTF yang terdaftar tidak berarti apa-apa bagi pembaca yang tidak dapat menemukan garisnya. Untuk konten campuran, pilihan tahan lama adalah wajah dengan cakupan luas, keluarga Noto dan Arial Unicode MS menjadi jawaban biasa, tertanam dan disubset

Aksara kanan-ke-kiri dan aksara kompleks menambahkan lapisan shaping di atas cakupan. HotPDF mengekspos RtLTextOut untuk bahasa Arab dan Ibrani, yang menangani pengurutan ulang direktif sehingga Anda meneruskan urutan logis dan membiarkan pustaka menata letaknya. Mendapatkan bahasa Arab yang benar adalah cakupan ditambah shaping ditambah arah, tiga hal terpisah, dan sebuah kotak di sana dapat berarti salah satunya gagal

Tabel ToUnicode: tempat di mana salin-tempel hidup

Semua di atas berkaitan dengan penggambaran. Ekstraksi adalah gambar cermin dan gagal karena alasannya sendiri. Penampil merender halaman menggunakan pemetaan kode-ke-glyph milik font, tetapi saat pengguna memilih teks dan menyalinnya, penampil perlu mengubah kode yang sama tersebut kembali menjadi Unicode. Pemetaan terbalik itulah CMap ToUnicode, stream opsional yang dilampirkan pada font

Ketika ada dan benar, teks yang disalin keluar sebagai karakter yang tepat. Ketika tidak ada atau salah, atau font disubset dengan kode glyph khusus dan tidak ada ToUnicode yang ditulis, halaman terlihat sempurna dan papan klip terisi sampah: kode glyph dibaca seolah-olah merupakan Unicode, yang untuk subset yang dikodekan khusus memang bukan. Inilah mengapa dokumen yang dipindai dengan lapisan teks OCR dapat dicari sementara PDF yang lahir secara digital dari generator yang ceroboh tidak. Rendering dan ekstraksi menggunakan tabel yang berbeda, sehingga file dapat memenuhi satu dan gagal yang lain. Jika ekstraksi penting untuk output Anda, perlakukan peta ToUnicode yang benar sebagai persyaratan, dan verifikasi dengan menyalin teks keluar dari sampel daripada mempercayai bahwa peta tersebut ada

Cara mendiagnosis bug font dengan cepat

Mode kegagalan memberitahu Anda di mana harus melihat. Kotak kosong di mesin lain hampir selalu berarti font yang tidak tertanam, jadi periksa embedding terlebih dahulu dan cakupan glyph kedua. Kotak yang muncul bahkan di mesin Anda sendiri menunjukkan cakupan: font tidak mengandung aksara tersebut, terlepas dari embedding. Teks yang dirender dengan benar tetapi saat disalin keluar menjadi omong kosong adalah masalah ToUnicode, bukan masalah rendering, dan mengotak-atik font atau embedding tidak akan memperbaikinya karena penggambarannya tidak pernah rusak. Untuk membaca file yang telah selesai, buka di Acrobat dan lihat Document Properties, Fonts: entri yang sehat menunjukkan tipe, mengatakan Embedded atau Embedded Subset, dan menamai encoding. Font yang seharusnya tertanam dan tidak akan mengumumkan dirinya di sana sebelum pelanggan melakukannya

Tidak satu pun dari ini yang eksotis begitu pemisahan antara karakter, kode, dan glyph menjadi jelas. Tanamkan font yang Anda gambar, subset yang besar, jangkau wajah Unicode dan RegisterUnicodeTTF begitu teks meninggalkan Latin, dan pertahankan peta ToUnicode yang benar jika ada yang akan mengekstrak teksnya. Lakukan itu dengan benar dan kotak-kotak tersebut berhenti muncul. Untuk mekanika di sekitarnya, anatomi PDF minimal menunjukkan di mana kamus font berada dalam pohon objek, dan penelusuran struktur dokumen mencakup bagaimana sumber daya dibagikan di seluruh halaman

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